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

[VIO-104] feat: Implement IAP #991

Merged
merged 11 commits into from
Jan 16, 2024
Merged

[VIO-104] feat: Implement IAP #991

merged 11 commits into from
Jan 16, 2024

Conversation

zatteo
Copy link
Contributor

@zatteo zatteo commented Oct 25, 2023

This PR adds support for IAP by:

  • Implementing the ClouderyOffer view that is responsible for displaying the Cloudery webpage with premium plans description
  • Opening this ClouderyOffer view when the OAuthClientLimitExceeded popup's corresponding button is clicked
  • Opening this ClouderyOffer view when any cozy-app redirects to its url (not tested yet, this will be done soon)
  • Intercepting premium plans redirection and trigger IAP process
    • Providing productID and cozy's UUID in the IAP process so the Cloudery can link the IAP Receipt to the corresponding Cozy
  • Adding rudimentary overlay over the ClouderyOffer view during the IAP process (this will be replaced by a designed loading spinner)

Warning
This PR contains a temporary commit to make the IAP button visible in the OAuthClientLimitExceeded popup for testing purpose. The final behavior will be implemented in the following days

@Ldoppea Ldoppea force-pushed the feat/iap branch 2 times, most recently from a3dfa23 to d1da5f9 Compare November 13, 2023 15:04
@Ldoppea Ldoppea force-pushed the feat/iap branch 5 times, most recently from 05e4821 to 8bfac6a Compare November 27, 2023 10:27
@Ldoppea Ldoppea changed the title Feat/iap feat: Implement IAP Dec 6, 2023
@Ldoppea
Copy link
Member

Ldoppea commented Dec 6, 2023

@zatteo @Crash-- I cleaned all current-progress commits and I added a description to the PR.

Current progress can be reviewed and I think it can be merged (i still have to fix the CI error).

The following steps will consist to:

  • Implement the "loading" UI that is displayed in the app during the IAP process (iOS sometimes shows the app between IAP steps)
  • Add cozy's domain next to the UUID on the IAP params (this will be needed by @taratatach for the Android's part)
  • Handle the refresh notification
  • Some UI refinements (i.e. status bar and navigation bar colors)

They may be done in another PR

@Ldoppea Ldoppea marked this pull request as ready for review December 7, 2023 15:46
@Ldoppea Ldoppea force-pushed the feat/iap branch 5 times, most recently from 5dbfea9 to eef4e9d Compare December 7, 2023 16:19
@zatteo
Copy link
Contributor Author

zatteo commented Dec 8, 2023

J'approuve

Copy link
Contributor

Choose a reason for hiding this comment

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

Can you explain the reasoning behind this event emiter implementation?

Copy link
Member

Choose a reason for hiding this comment

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

This is the same one we implemented for some other dialogs (ex: the one for OauthClientsLimitService)

The goal is that from anywhere in the code we can trigger this event to display the corresponding dialog.

Here we have two places that can trigger the ClouderyOffer dialog: the OauthClientsLimitService's navigation interception (in onShouldStartLoadWithRequest) and the ReloadInterceptorWebView's one

The ClouderyOffer dialog is declared somewhere in the app, and listen this event in order to chose when it should be displayed.

Another approach would be to create a root Context, but there are already a lot of root provider and I find this method easier to handle (one time event, no global rerender, no risk to forget declaring the context or to call the openDialog method outside of the context)

@@ -25,6 +25,9 @@
"title": "Déconnexion"
},
"screens": {
"clouderyOffer": {
"backButton": "RETOUR À MON COZY"
Copy link
Contributor

Choose a reason for hiding this comment

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

OUAIS VIVE LES MAJ!

Copy link
Member

Choose a reason for hiding this comment

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

@Ldoppea Ldoppea changed the base branch from master to refactor/http_server_ts December 13, 2023 18:19
Base automatically changed from refactor/http_server_ts to master December 19, 2023 17:58
@Ldoppea Ldoppea changed the base branch from master to feat/rework_set_flagship_ui December 19, 2023 19:32
@Ldoppea Ldoppea force-pushed the feat/rework_set_flagship_ui branch from 902a997 to 81fd6c5 Compare December 19, 2023 19:38
@Ldoppea Ldoppea force-pushed the feat/rework_set_flagship_ui branch from 81fd6c5 to 6b73311 Compare December 20, 2023 14:49
@Ldoppea Ldoppea changed the title feat: Implement IAP [VIO-104] feat: Implement IAP Dec 27, 2023

export const CLOUDERY_OFFER = 'CLOUDERY_OFFER'

const START_IAP_REDIRECT_URL = 'https://iapflagship'
Copy link
Contributor

Choose a reason for hiding this comment

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

Those strings look more like config than code, maybe we should put them somewhere else

</View>
)
}

const styles = StyleSheet.create({
Copy link
Contributor

Choose a reason for hiding this comment

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

starting to have a lot of styles LoC in that file maybe we can extract it. Also it seems some of the value are magic numbers at first glance

@@ -94,6 +99,13 @@ const isStartIapUrl = (url: string): boolean => {
return url.startsWith(START_IAP_URL)
}

const isOsStoreUrl = (url: string): boolean => {
return (
url.startsWith('https://apps.apple.com/account/subscriptions') ||
Copy link
Contributor

Choose a reason for hiding this comment

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

same as earlier, I think those kinds of strings shouldn't be hardcoded into a "service" file. Maybe we can have "config" folders into a "domain" if we really want that kind of info to live near the code

Copy link
Contributor

@acezard acezard left a comment

Choose a reason for hiding this comment

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

Left some comments but they are non blocking 👍

@Ldoppea Ldoppea force-pushed the feat/rework_set_flagship_ui branch from 4bb373b to 7b2a6c9 Compare January 4, 2024 16:37
Base automatically changed from feat/rework_set_flagship_ui to master January 5, 2024 10:34
This package is needed by the following commits to implement
InAppPurchase scenario
zatteo and others added 10 commits January 16, 2024 17:40
react-native-iap suggests minSdkVersion = 24 but it seems to work
also with minSdkVersion = 21
(hyochan/react-native-iap#2423
and
hyochan/react-native-iap#2528).
So I decided to try without changing minSdKVersion.

Updating kotlinVersion is also asked and does not break the build.

I also decided to try without setting androidXAnnotation and
AndroidXBrowser as suggested.
For now we want to add IAP support only for Cozy's app

Adding react-native-iap package in package.json is enough to add the
"in app purchase" flag to the store. This is because the react-native
auto linking feature will automatically add the BILLING permission to
Android

To fix this and in order to have a generic solution, this commit adds
the ability to set different packages configurations for each brand

This is done in `white_label/config.json` in the `package` field.
Either add the specified package version to fix it, or add `remove` to
remove it
The ClouderyOffer view is responsible to show the Cloudery web page
that will allow the user to buy premium plans

We want to open it when intercepting "View offers" redirection in
`OauthLimitExceeded` view
We want to open the ClouderyOffer view when intercepting "View offers"
redirection in cozy-apps (i.e. paywalls modals)
We do nothing for the moment because :
- cloudery is not ready
- IAP is not ready and will be implemented in future commits
In the ClouderyOffer view, when the user click on a plan, we want to
trigger the corresponding in-app purchase

The IAP request is done by injecting the corresponding productId and
the cozy's UUID that will be used by the Cloudery in order to link the
IAP receipt with the corresponding Cozy instance

During the IAP process, the ClouderyOffer view is hidden behind an
overlay. For now this overlay is filled with primaryColor, but it
should be replaced by a loading spinner later
If the user doesn't want to buy a premium plan, they should be able to
manually close the ClouderyOffer view

The close button's UI is the same as for CLISK launcher
Because the ClouderyOffer view's URL is provided by the cozy-app, we
want to ensure it is legit by comparing it with the Cozy instance's URL
and UUID

Requires `cozy-intent` to be `>=43.4.1`

Related PR: cozy/cozy-client#1418
The app cannot unsubscribe a plan by itself. The only thing we can do
it to redirect the user to the OS Store's subscriptions page

The Cloudery is expected to do this when the user clicks on the
"Manage plan" button, then the app will intercept the corresponding
link and open the OS page
@Ldoppea Ldoppea merged commit d5a950e into master Jan 16, 2024
1 check passed
@Ldoppea Ldoppea deleted the feat/iap branch January 16, 2024 16:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants