Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/8182 iap single subcription requirement #8206

Merged
merged 18 commits into from
Feb 1, 2023

Conversation

JorgeMucientes
Copy link
Contributor

@JorgeMucientes JorgeMucientes commented Jan 17, 2023

Closes: #8182 and #8148

Description

Adds a check before triggering store creation flow, to verify that the user doesn't have an active IAP subscription to WooCommerce.

Testing instructions

  • Trigger store creation flow (compile vanila debug flavour for this, and have a Google Play account located in the US)
  • Complete the flow creating a new site and getting an active subscription for IAP
  • Trigger store creation flow again and see how an error dialog is shown stating that you can't have more than 1 active subscription linked to your current Google account.

You are welcome to provide any feedback for a better error message.

  • I have considered if this change warrants user-facing release notes and have added them to RELEASE-NOTES.txt if necessary.

@wpmobilebot
Copy link
Collaborator

wpmobilebot commented Jan 17, 2023

You can test the changes on this Pull Request by downloading an installable build, or scanning this QR code:

@JorgeMucientes JorgeMucientes added type: enhancement A request for an enhancement. feature: store creation Store creation flow labels Jan 18, 2023
@JorgeMucientes JorgeMucientes marked this pull request as ready for review January 18, 2023 09:14
@peril-woocommerce
Copy link

peril-woocommerce bot commented Jan 18, 2023

Warnings
⚠️ PR is not assigned to a milestone.

Generated by 🚫 dangerJS

@JorgeMucientes JorgeMucientes added this to the 12.0 milestone Jan 18, 2023
@kidinov kidinov self-assigned this Jan 19, 2023
is IAPError.Billing.ServiceUnavailable,
is IAPError.Billing.ItemNotOwned,
is IAPError.Billing.UserCancelled,
is IAPError.RemoteCommunication.Network -> R.string.store_creation_iap_eligibility_check_generic_error
Copy link
Contributor

@kidinov kidinov Jan 19, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it make sense to use offline_error string for the RemoteCommunication.Network error? To be a bit more specific?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I'm not sure about displaying any non-generic message here.
From what I've seen in the code is that IAPError.RemoteCommunication.Network is returned when BaseRequest.GenericErrorType.TIMEOUT is returned. But that doesn't necessarily mean the user is offline. In fact there is another type of error for that BaseRequest.GenericErrorType.NO_CONNECTION, which is mapped to CreateOrderErrorType.API_ERROR

    private fun WPComGsonRequest.WPComGsonNetworkError.toCreateOrderError() =
        when (type) {
            BaseRequest.GenericErrorType.TIMEOUT -> CreateOrderErrorType.TIMEOUT
            BaseRequest.GenericErrorType.NO_CONNECTION,
            BaseRequest.GenericErrorType.SERVER_ERROR,
            BaseRequest.GenericErrorType.INVALID_SSL_CERTIFICATE,
            BaseRequest.GenericErrorType.NETWORK_ERROR -> CreateOrderErrorType.API_ERROR
            BaseRequest.GenericErrorType.PARSE_ERROR,
            BaseRequest.GenericErrorType.NOT_FOUND,
            BaseRequest.GenericErrorType.CENSORED,
            BaseRequest.GenericErrorType.INVALID_RESPONSE -> CreateOrderErrorType.INVALID_RESPONSE
            BaseRequest.GenericErrorType.HTTP_AUTH_ERROR,
            BaseRequest.GenericErrorType.AUTHORIZATION_REQUIRED,
            BaseRequest.GenericErrorType.NOT_AUTHENTICATED -> CreateOrderErrorType.AUTH_ERROR
            BaseRequest.GenericErrorType.UNKNOWN -> CreateOrderErrorType.GENERIC_ERROR
            null -> CreateOrderErrorType.GENERIC_ERROR
        }

I wonder if ``BaseRequest.GenericErrorType.NO_CONNECTION, should be mapped instead to CreateOrderErrorType.TIMEOUT ? Wdyt?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just judging by Volley's naming of the errors:

NoConnectionError
Error indicating that no connection could be established when performing a Volley request.

It might refer to the case when a TCP handshake could not be established. This can happen when the device is offline, some package loss, or any connection-related issue

TimeoutError
Indicates that the connection or the socket timed out.

The request was sent, but the response didn't come back during the timeout period. The reason can be exactly the same as the first error + the backend may just didn't answer on time

But that doesn't necessarily mean the user is offline.

True, but I guess the probability that something with a connection is high in these cases

I wonder if ``BaseRequest.GenericErrorType.NO_CONNECTION, should be mapped instead to CreateOrderErrorType.TIMEOUT? Wdyt?

Yeah, seems to be a bug in the implementation there. I'll create for myself a ticket to fix that

We might use less assertive message

<string name="error_generic_network">A network error occurred. Please check your connection and try again.</string>

in this case, wdyt?

Copy link
Contributor

@kidinov kidinov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for this improvement! It looks and works well.

I left a comment which I think is worth thinking about. Please let me know your ideas regarding this

is WPComIsPurchasedResult.Success -> {
when (result.purchaseStatus) {
PurchaseStatus.PURCHASED_AND_ACKNOWLEDGED,
PurchaseStatus.PURCHASED -> onUserNotEligibleForIAP(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking about this case. So the item is purchased but not acknowledged by our backend, which means that the WP plan is not given to a user as well. We could call purchaseWPComPlan and it internally will call the backend and the backend should acknowledge the purchase on the GP side, but at this moment we actually not sure if this is the same site a user wanted to purchase a subscription to at the begging 🤔

On other hand, it's the user's decision and we should hope that they know what they are doing, so maybe we still should purchaseWPComPlan in this case? I am not saying to call it here, but let a user go further, and then when the website is picked use an existing GP subscription in order to give a WP plan to the picked site

Another option would be to at least show to a user that yes we have your purchase, please contact support so they will link the GP subscription to a site that needs an upgrade

Wdyt?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @kidinov I'm not sure we can do what you suggest. First of all I'd like to ask under which circumstance the purchase can be placed but not acknowledged?

Regarding this:

We could call purchaseWPComPlan and it internally will call the backend and the backend should acknowledge the purchase on the GP side, but at this moment we actually not sure if this is the same site a user wanted to purchase a subscription to at the begging

We do not have a siteId available at this point and we have no clue about the site that has the active IAP WooCommerce subscription, as this subscription is tied to Google account. Keep in mind these checks run when user clicks on "create a new store", so the user hasn't even entered a site name or anything yet.

I agree we could redirect them to support to troubleshoot the problem. I'll submit a curated message for that scenario with the appropriate CTA to create a Zendesk ticket

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First of all I'd like to ask under which circumstance the purchase can be placed but not acknowledged?

The IAP becomes purchased after the user from the app makes a purchase. It gets acknowledged by the backend after we do the create_order call. So any network issue between the mobile and the backend, or between the backend and google play will lead to the purchased and not acknowledged state. Also, any error on the backend or the user kills the app at a specific moment will lead to the same result. So there are many places where it can go wrong.

Having said that, after some time not acknowledged purchase will get refunded, so it's not the end of the world

We do not have a siteId available at this point and we have no clue about the site that has the active IAP WooCommerce subscription, as this subscription is tied to Google account.

Yep, that's why I mentioned:

I am not saying to call it here, but let a user go further, and then when the website is picked use an existing GP subscription in order to give a WP plan to the picked site

So you could just let a user go further, and let them click the "purchase" button and then the library will do the rest of the work, but thinking about this now, it would feel weird for a user, to buy the same thing for the second time. It's not like the "try again" button, so I guess it is not something that people will do, and saying that to talk to support is probably the best approach in this case

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for clarifying the scenarios when purchase is done but couldn't be acknoledged.

but thinking about this now, it would feel weird for a user, to buy the same thing for the second time

I agree with that. The user would be taken through a flow that expects a new site at all times. So it would be super confusing for them to have to enter all the same information again (in some cases they wouldn't be able to do this as the domain for example would be already assigned to their previous site), and then be requested to pay again for something they had already paid. Offer a "Retry" option for this case would require an entirely different flow where we request the site address the site they have purchased an eCommerce subscription for but is not acknowledged.

Once more question on this. Do HE have an straightforward way to check if users have an active IAP eCommerce, and if they do, link the subscription to their site?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding this for reference @kidinov
If the purchase fails to be acknowledged it won't matter if we retry to confirm the purchase as the associated purchaseToken expires.
Full context on this discussion p1674662344895449/1673441948.587159-slack-C025A8VV728

@@ -2766,7 +2766,9 @@
<string name="store_creation_iap_eligibility_loading_title">We are getting ready for your store creation</string>
<string name="store_creation_iap_eligibility_loading_subtitle">Please remain connected.</string>
<string name="store_creation_iap_eligibility_check_error_title">Store Creation Unavailable </string>
<string name="store_creation_iap_eligibility_check_error_description">Store creation from the mobile app is currently unavailable for your country.</string>
<string name="store_creation_iap_eligibility_check_error_not_available_for_country">Store creation from the mobile app is currently unavailable for your country.</string>
<string name="store_creation_iap_eligibility_check_error_existing_subscription">You have an active WooCommerce subscription linked to your Google account. Only one WooCommerce subscription per Google account is allowed.</string>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if we will give an option for what to do next to a user, something like:

Currently you are only able to purchase one eCommerce plan from the mobile app. For additional options, please visit our website

Copy link
Contributor Author

@JorgeMucientes JorgeMucientes Jan 19, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, makes sense. I've updated the message. However we can't provide a link to the actual store creation flow as that would not be compliant with Google Play terms.

@codecov-commenter
Copy link

codecov-commenter commented Jan 19, 2023

Codecov Report

Base: 43.45% // Head: 43.50% // Increases project coverage by +0.05% 🎉

Coverage data is based on head (1d3c232) compared to base (9ec076a).
Patch coverage: 60.65% of modified lines in pull request are covered.

Additional details and impacted files
@@             Coverage Diff              @@
##              trunk    #8206      +/-   ##
============================================
+ Coverage     43.45%   43.50%   +0.05%     
- Complexity     3532     3535       +3     
============================================
  Files           712      711       -1     
  Lines         37933    37958      +25     
  Branches       5010     5018       +8     
============================================
+ Hits          16482    16513      +31     
+ Misses        19980    19962      -18     
- Partials       1471     1483      +12     
Impacted Files Coverage Δ
...login/storecreation/iap/IapMobilePayApiProvider.kt 0.00% <0.00%> (ø)
...login/storecreation/iap/IapEligibilityViewModel.kt 70.45% <62.71%> (-17.36%) ⬇️
...ce/android/iap/pub/model/WPComIsPurchasedResult.kt 100.00% <0.00%> (+100.00%) ⬆️

Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here.

☔ View full report at Codecov.
📢 Do you have feedback about the report comment? Let us know in this issue.

@JorgeMucientes JorgeMucientes removed this from the 12.0 milestone Jan 19, 2023
@JorgeMucientes
Copy link
Contributor Author

Hey @kidinov thank you for the review and all the great feedback. I'll summarize the latest changes applied after you last comments.

  • From this comments I've refactored error codes in this commit as suggested, providing a specific error message for them. This also required updating mapping inside IapMobilePayApiProvider where I've also taken the opportunity to remove duplicated code: commit
  • Added a curated error message for the case where purchase has not been acknowledged, providing a CTA to open support:
contectSuppportIAP.mp4

Ready for another round of review.

Copy link
Contributor

@kidinov kidinov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

Before merging, maybe we should integrate this fluxc change though?

…ment

# Conflicts:
#	WooCommerce/src/debug/kotlin/com.woocommerce.android/iapshowcase/purchase/IAPShowcaseMobilePayAPIProvider.kt
#	build.gradle
@JorgeMucientes JorgeMucientes merged commit ab19239 into trunk Feb 1, 2023
@JorgeMucientes JorgeMucientes deleted the feature/8182-iap-single-subcription-requirement branch February 1, 2023 15:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature: store creation Store creation flow type: enhancement A request for an enhancement.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Disable store creation for users that have already subscribed to eCommerce plan using IAP
4 participants