Skip to content
This repository has been archived by the owner on Mar 22, 2024. It is now read-only.

Update SwiftUI doc to reflect mock products for previews #276

Merged
merged 4 commits into from
Aug 15, 2023

Conversation

codykerns
Copy link
Member

Motivation / Description

We added support for SwiftUI previews with some mock types a couple of months ago: RevenueCat/purchases-ios#2711

Changes introduced

Refactor existing SwiftUI 'app lifecycle' doc to be more generic about SwiftUI usage

Jira ticket (if any)

Additional comments

Copy link
Contributor

@NachoSoto NachoSoto left a comment

Choose a reason for hiding this comment

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

Yesss 😍


The Purchases SDK includes a debug overlay that allows developers to see various configuration details while running an app. You can read more about the debug overlay in our [Debugging](https://www.revenuecat.com/docs/debugging#debug-ui) guide.

# SwiftUI Previews
Copy link
Contributor

Choose a reason for hiding this comment

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

Awesome 👍🏻

@RCGitBot
Copy link
Contributor

Previews

temp/how-grace-periods-work.md

See contents

If a customer is unable to complete their purchase due to an invalid or expired payment method, supported stores offer an optional grace period. Grace periods allow the customer to retain access to their subscription purchase for a short period of time while the store attempts to renew the subscription. This prevents disruption for paid features, and can improve the user experience for your app.

Grace Periods are optional and customizable on certain platforms.

Store Required? Duration
App Store Optional Customizable
Google Play Optional Customizable
Stripe Optional Customizable
Amazon ❌ Not supported N/A

Encountering Billing Issues

As mentioned, billing issues occur when a user is unable to complete a subscription purchase due to an invalid or expired payment method. When this occurs, RevenueCat sends a BILLING_ISSUE event to webhooks, integrations, and the customer history page.

RevenueCat will only send one billing issue event -- additional payment failures won't trigger additional billing issue events, unless a renewal is successful between payment failures or the subscription ends and is restarted.

SDK Prompt

Starting in iOS 16.4+, a system-sheet will automatically be displayed if a user encounters a billing issue, with a prompt for the customer to update their payment method. You can test this behavior by following Apple's instructions.

  1. Make a sandbox purchase on a real device using iOS 16.4+
  2. Once the purchase is completed, background or close the app
  3. Disable renewals in Settings -> App Store -> Sandbox Account -> Manage
  4. Wait a few minutes (depending on the product duration) and allow the subscription to attempt renewal. Renewal will fail.
  5. Relaunch or reopen your app, and see the billing issue prompt

Entering a Grace Period

When a subscription enters a grace period, RevenueCat detects the change automatically. Users will retain access to their subscriptions, but we'll immediately send events indicating the subscription has been cancelled. These subscriptions are considered cancelled because they are now past due, but will not be considered expired until the end of their grace period. During this time, a subscription may convert to paid through additional billing attempts from the store or by the customer updating their billing information.

API, Events, and Webhooks

To detect grace periods in webhook events, watch for the value of grace_period_expiration_at_ms. This property is only valid for BILLING_ISSUE events.

To detect grace periods in the GET /subscriber endpoint, watch for the value of grace_period_expires_date on a subscription object and compare it to the current date. This property will be null if the subscription is not in a grace period.

Once a user corrects their payment method, RevenueCat will send a renewal event. This will reset the grace_period_expires_date property to null in the GET /subscriber endpoint.

Dashboard

Customers who enter into a grace period will have events added to their Customer History.

[block:image]
{
"images": [
{
"image": [
"https://files.readme.io/4265860-Screen_Shot_2022-09-15_at_2.46.12_PM.png",
"Screen Shot 2022-09-15 at 2.46.12 PM.png",
1244
],
"align": "center",
"sizing": "80",
"border": true
}
]
}
[/block]

Additionally, subscriptions that are currently in a grace period will still be considered "active," since the customer retains access to their entitlement throughout their grace period. Distinct customers who are currently in a grace period can be counted through Customer Lists using the "Billing Issue Trial" and "Billing Issue" statuses.

temp/restoring-purchases.md

See contents

Restoring purchases is a mechanism by which your user can restore their in-app purchases, reactivating any content that had previously been purchased from the same store account (Apple, Google, or Amazon).

It is recommended that all apps have some way for users to trigger the restorePurchases method, even if you require all customers to create accounts.

Purchases.shared.restorePurchases { customerInfo, error in
    //... check customerInfo to see if entitlement is now active
}
[[RCPurchases sharedPurchases] restorePurchasesWithCompletion:^(RCCustomerInfo *customerInfo, NSError *error) {
    //... check customerInfo to see if entitlement is now active
}];
Purchases.sharedInstance.restorePurchasesWith() { customerInfo ->
	//... check customerInfo to see if entitlement is now active
}
Purchases.getSharedInstance().restorePurchases(new ReceiveCustomerInfoCallback() {
	@Override
	public void onError(@NonNull PurchasesError error) {
    
  }

	@Override
	public void onReceived(@NonNull CustomerInfo customerInfo) {
    
  }
});
try {
  CustomerInfo customerInfo = await Purchases.restorePurchases();
  // ... check restored purchaserInfo to see if entitlement is now active
} on PlatformException catch (e) {
  // Error restoring purchases
}
try {
  const restore = await Purchases.restorePurchases();
  // ... check restored purchaserInfo to see if entitlement is now active
} catch (e) {

}
Purchases.restorePurchases(
  info => {
    //... check customerInfo to see if entitlement is now active
  },
  error => {
    // Error restoring purchases
  }
);
var purchases = GetComponent<Purchases>();
purchases.RestorePurchases((info, error) =>
{
    //... check purchaserInfo to see if entitlement is now active
});

The restorePurchases method should not be triggered programmatically, since it may cause OS level sign-in prompts to appear, and should only be called from some user interaction (e.g. tapping a "Restore" button.)

🚧 Programmatically trigger a restore, by calling syncPurchases instead

If you are trying to restore a purchase programmatically, use syncPurchases instead. This will not cause OS level sign-in prompts to appear.

Transferring purchases seen on multiple App User IDs

If an App User ID tries to restore transactions that are already associated with a different identified App User ID in RevenueCat, you can configure how RevenueCat should respond by changing the dropdown in Project Settings > General in the dashboard:

Note that the behavior set here will affect all apps under the project. Also note that Share between App User IDs (legacy) will only be available for legacy projects with this behavior already enabled, not new projects.

📘 Transfer behavior also applies to making purchases

The configured behavior will also apply if an identified App User ID makes a new purchase and the device receipt is already associated with a different identified App User ID in RevenueCat.

📘 Transfer behavior only applies to purchases associated with an identified App User ID

The behavior selected in the dropdown only applies to purchases currently associated with an identified App User IDs. If the purchase is currently associated with an anonymous App User ID, that App User ID will be aliased with the new App User ID instead (ie. the purchase is shared between the App User IDs).

Transfer to new App User ID

Default ✅

The default behavior is to transfer purchases between identified App User IDs if needed. This ensures that the customer restoring gets access to any purchased content, but only one customer at a time can have access. For example, if UserA buys a subscription, then UserB logs into your app on the same device and restores transactions, UserB would now have access to the subscription and it would be revoked from UserA.

If an identified App User ID restores and the owner of the receipt is anonymous, the anonymous identifiers will be merged (aliased) into the same customer profile in RevenueCat and treated as the same customer going forward. If an anonymous ID restores and the owner of the receipt is an identified App User ID, we will resort to the specified restore behavior and transfer the receipt to the anonymous user. And finally, if an anonymous ID restores and the owner of the receipt is also anonymous, the anonymous identifiers will be merged (aliased).

Google Play on Android

Due to platform limitations, purchases will be transferred as soon as you call configure if a user's purchases are already associated with another app user ID.

This may cause unexpected transfers of purchases between app user IDs, especially for apps with optional logins or users with multiple devices. To prevent this behavior, you should wait to call configure until you have the appropriate app user ID for your customer.

Transfer if there are no active subscriptions

This behavior will transfer the purchases to the new App User ID unless they contain an active subscription. This behavior is especially beneficial if you have strict business logic that associate subscriptions with a given App User ID, but you still want churned subscribers who might later return to your app with a new App User ID to start a new subscription. This is especially relevant on iOS, where a receipt contains all purchases associated with a given Apple ID, so in the case of using the behavior "Keep with original App User ID", the customer would be able to start a new subscription on the store, but they would not be able to gain access because RevenueCat would associate that new subscription with the original App User ID (since it is on the same Apple receipt).

Note that the presence of one-time (non-subscription) purchases does not prevent transfers from happening.

Keep with original App User ID

Use with caution 🚧

Returns an error if the App User ID attempting to restore purchase or make a new purchase is different from the original App User ID that made the purchase. This requires customers to sign in with their original App User ID, and is only allowed for apps that require every customer to create an account before purchasing.

Share between App User IDs (legacy)

Legacy ✅

The legacy behavior is to merge (alias) any App User IDs that restore the same underlying subscription and treat them as the same subscriber moving forward. This applies to both anonymous and identified App user IDs. You can continue to use this legacy behavior as long as you'd like, but you cannot re-enable the alias behavior if you switch to Transfer Purchases or Block Restores.

Example usage

My app... Restore Behavior
Does not have any login mechanism and only relies on RevenueCat anonymous App User IDs. Transfer to new App User ID. Required to allow customers to restore transactions after uninstalling / reinstalling your app.
Has an optional login mechanism and / or allows customers to purchase before creating an account in my app. Transfer to new App User ID. Required to make sure customers that purchase without an account can restore transactions.
Requires all customers to create an account before purchasing. Transfer to new App User ID. Recommended to help customers restore transactions even if they forget previous account information.
Requires all customers to create an account before purchasing, and has strict business logic that requires purchases to only ever be associated with a single App User ID. Keep with original App User ID. Will make sure that transactions never get transferred between accounts. Your support team should be prepared to guide customers through an account recovery process if needed.

Syncing purchases without user interaction

syncPurchases is a method we provide in our SDK which allows you to programmatically trigger a restore. This method, much like restorePurchases, reactivates any content that had previously been purchased from the same store account (Apple, Google, or Amazon).

Considerations

  • syncPurchases is typically used for migrating subscriptions
  • Since this method simulates restoring a purchase, there is a risk of transferring or aliasing an anonymous user

Restoring Purchases for Consumables and Non-renewing Subscriptions

Consumables and non-renewing subscriptions can only be restored by using an acount system with custom App User IDs. This is due to these types of in-app purchases not showing up on the underlying store receipt after the transaction is finished.

By logging in your users with a custom App User ID, RevenueCat can continue to provide transaction details in a user's CustomerInfo for their previous consumable and non-renewing subscription purchases.

Next Steps

  • Make sure all purchases are being linked to the correct user Id
  • If you're ready to test, start with our guides on sandbox testing

@codykerns codykerns merged commit 45220eb into main Aug 15, 2023
@codykerns codykerns deleted the cody/swiftui_updates branch August 15, 2023 20:24
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants