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/iap payment flow #8053

Merged
merged 38 commits into from
Dec 30, 2022
Merged

Feature/iap payment flow #8053

merged 38 commits into from
Dec 30, 2022

Conversation

JorgeMucientes
Copy link
Contributor

@JorgeMucientes JorgeMucientes commented Dec 16, 2022

Second part of: #7925

Description

Provides integration with In App Purchases (IAP) to purchase the eCommerce plan when creating a new store.

The flow works like this:

  1. When user kicks off the store creation flow we check for IAP eligibility. That is done with this PR changes
  2. If IAP is enabled and user is eligible the native store creation flow is triggered. If not, a dialogue with an error is shown.
  3. User provides all the store details and lands on the purchase eCommerce plan screen
  4. When clicking on "Create store for $69.99/month" a new site is created and then the IAP purchase flow is triggered.
  5. After IAP is purchased successfully user is taken to the next step. In this step we wait for the new site to migrate to Atomic site and Jetpack and WooCommerce plugin to get installed.

Testing instructions

Follow these instructions: PdfdoF-1Ej-p2 (Android section) to be able to use IAP. A Google Play US account is required for this.

  • Check that purchasing eCommerce plan creates a new site. We are still investigating a potential bug on backed side pdfdoF-20N-p2#comment-3131, so it is possible that once the site is created and the plan purchased successfully, the site doesn't have the eCommerce plan.
  • Check that any error during IAP purchase flow is tracked and hides the loading spinner from the main button

Images/gif

Android_IAP_demot.mp4

@JorgeMucientes JorgeMucientes force-pushed the feature/iap-payment-flow branch from 113f892 to f270124 Compare December 16, 2022 20:07
@wpmobilebot
Copy link
Collaborator

wpmobilebot commented Dec 16, 2022

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

@JorgeMucientes JorgeMucientes removed the status: do not merge Dependent on another PR, ready for review but not ready for merge. label Dec 23, 2022
init {
loadPlan()
trackStep(VALUE_STEP_PLAN_PURCHASE)
}

fun setIAPActivityWrapper(wrapper: IAPActivityWrapper) {
iapActivityWrapper = wrapper
Copy link
Contributor

Choose a reason for hiding this comment

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

Won't it be a memory leak when the activity is recreated?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point! I've completely missed that. Fixed :)

Copy link
Contributor

@kidinov kidinov Dec 27, 2022

Choose a reason for hiding this comment

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

The way you've done probably will work, just wondering why just not pass it as a method param

fun onConfirmClicked(wrapper: IAPActivityWrapper) {

Seems that it will be the more straightforward approach

Copy link
Contributor Author

@JorgeMucientes JorgeMucientes Dec 27, 2022

Choose a reason for hiding this comment

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

I remember attempting that approach first. I had some code like:

val activity = LocalContext.current.getActivity()

fun Context.getActivity(): AppCompatActivity? = when (this) {
    is AppCompatActivity -> this
    is ContextWrapper -> baseContext.getActivity()
    else -> null
}

In one of the UI @Composable functions. But somehow I kept getting crashes because the retrieved activity was not AppCompatActivity. I'll revisit this again, to double check if I can find the way to make it work, as I agree that passing that as parameter is way more straightforward.

Copy link
Contributor

Choose a reason for hiding this comment

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

That's weird. I briefly tried that and it worked fine for

Pass_activity_as_method_param.patch

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think in my previous attempts I was trying to directly cast LocalContext.current as AppCompatActivity resulting in a crash because LocalContext.current is actually a FragmentContextWrapper. The key here is in the line is ContextWrapper -> baseContext.getActivity() which I don't think I had in my original implementation.
In any case, with the latest changes this is solved in a more explicit manner.

@kidinov kidinov self-requested a review December 26, 2022 09:05
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! I left some comments though, please take a look.

Also I pushed the fix of the functional tests inside of the IAP module

@codecov-commenter
Copy link

codecov-commenter commented Dec 26, 2022

Codecov Report

Base: 43.02% // Head: 43.17% // Increases project coverage by +0.15% 🎉

Coverage data is based on head (a5751a8) compared to base (2f9b61b).
Patch coverage: 67.30% of modified lines in pull request are covered.

Additional details and impacted files
@@             Coverage Diff              @@
##              trunk    #8053      +/-   ##
============================================
+ Coverage     43.02%   43.17%   +0.15%     
- Complexity     3522     3537      +15     
============================================
  Files           679      681       +2     
  Lines         36956    37015      +59     
  Branches       4790     4798       +8     
============================================
+ Hits          15901    15983      +82     
+ Misses        19629    19602      -27     
- Partials       1426     1430       +4     
Impacted Files Coverage Δ
...pshowcase/purchase/IAPShowcasePurchaseViewModel.kt 0.00% <0.00%> (ø)
...android/ui/login/storecreation/iap/IsIAPEnabled.kt 0.00% <0.00%> (ø)
...al/planpurchase/IAPPurchaseWPComPlanActionsImpl.kt 0.00% <0.00%> (ø)
...erce/android/iap/pub/IAPSitePurchasePlanFactory.kt 0.00% <0.00%> (ø)
...oid/ui/login/storecreation/plans/PlansViewModel.kt 81.48% <65.75%> (-9.75%) ⬇️
...e/android/util/SiteIndependentCurrencyFormatter.kt 75.00% <75.00%> (ø)
...om/woocommerce/android/analytics/AnalyticsEvent.kt 100.00% <100.00%> (ø)
...login/storecreation/iap/IapEligibilityViewModel.kt 87.80% <100.00%> (+87.80%) ⬆️
.../com/woocommerce/android/util/CurrencyFormatter.kt 58.46% <100.00%> (-0.11%) ⬇️
...m/woocommerce/android/viewmodel/ScopedViewModel.kt 100.00% <100.00%> (ø)
... and 9 more

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.

val reason = if (errorType is IAPError.RemoteCommunication.Server) {
errorType.reason
} else ""
WooLog.i(WooLog.T.IAP, "Error processing eCommerce plan: $errorType reason: $reason")
Copy link
Contributor

@kidinov kidinov Dec 27, 2022

Choose a reason for hiding this comment

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

np: probably this logging here is redundant, we log what we send to the tracks anyway:

2022-12-27 11:07:09.460  5998-5998  WooCommerce-IAP         com.woocommerce.android              I  Error processing eCommerce plan: UserCancelled reason: 
2022-12-27 11:07:09.462  5998-5998  WooCommerce-UTILS       com.woocommerce.android              I  🔵 Tracked: site_creation_iap_purchase_error, Properties: {"error_type":"UserCancelled","error_description":"","is_debug":true}

@kidinov kidinov self-requested a review December 27, 2022 07:09
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!

I left a couple of nonblocking comments, feel free to proceed without addressing them

@JorgeMucientes
Copy link
Contributor Author

Thank you for the careful review @kidinov! 🙏🏼
I'm AFK today, but I'll revisit this PR tomorrow to double check what I mentioned earlier about trying to pass the activityWrapper as parameter from compose code. So, let's not merge this yet.

@JorgeMucientes
Copy link
Contributor Author

Hey @kidinov! I think the PR is ready for the last round review! I was finally able to get rid of the lifecycle callbacks in PlanViewModel and properly pass the IAPActivityWrapper when the purchase button is clicked (I could resolve the issues I encountered on my first attempt), you can review the changes here

I've also created an issue to address the error handling when fetching IAP product details fails

Once again thanks for the careful PR review 🙏🏼

@JorgeMucientes
Copy link
Contributor Author

I'll be AFK the next week. Since this PR was already approved by @kidinov and the only changes I've added were minor improvement after the latest suggestions I'll proceed with merging.

Once again thank you for the detailed review and the great suggestions.

@JorgeMucientes JorgeMucientes merged commit 9b57349 into trunk Dec 30, 2022
@JorgeMucientes JorgeMucientes deleted the feature/iap-payment-flow branch December 30, 2022 16:15
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
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants