How do I restore purchases?

Call the SDK's restorePurchases() from a Restore purchases button, the same code you use with RevenueCat. The SDK posts the device's store receipts to POST /v1/receipts, and RevenueDot answers with the updated customer info. If the purchase already belongs to another user, the project's transfer behaviour decides what happens: by default the purchase moves to the user who restored it, and RevenueDot sends a TRANSFER webhook.

Restore or sync#

Call When to use it What the user sees
restorePurchases() The user taps Restore purchases On iOS, it may ask the user to sign in to their Apple account
syncPurchases() Silently, for example once after a migration, so RevenueDot learns about purchases it has not seen Nothing
Swift
// iOS
let customerInfo = try await Purchases.shared.restorePurchases()
Kotlin
// Android
Purchases.sharedInstance.restorePurchasesWith(
  onError = { error -> /* show error.message */ },
  onSuccess = { customerInfo -> /* check customerInfo.entitlements */ },
)
TypeScript
// React Native
const customerInfo = await Purchases.restorePurchases();

RevenueCat recommends calling restore only from a button, not on every launch (RevenueCat docs). The same advice applies here.

Who owns a restored purchase#

RevenueDot follows the project setting transfer_behavior. The four values match RevenueCat's options (RevenueCat docs).

transfer_behavior When another known user already owns the purchase
transfer (default) The purchase moves to the user who restored it. RevenueDot sends a TRANSFER event
transfer_if_no_active The purchase moves only if the current owner has no active subscription. Otherwise the restore fails with 7102
keep The purchase stays with its owner. The restore fails with HTTP 400, code 7102, "The receipt is already in use by another subscriber."
share The two users are merged into one customer, so both IDs share the purchase

Two cases never depend on the setting:

  • If the current owner is anonymous ($RCAnonymousID:...), it is always merged into the user who restored.
  • If the user restoring is anonymous and the owner is a known user, the anonymous ID is merged into the owner.

Change the setting on the dashboard's project settings, or with the API:

Shell
curl -s -X POST https://revenuedot.example.com/v2/projects/$PROJECT_ID \
  -H "Authorization: Bearer $SECRET_KEY" -H "Content-Type: application/json" \
  -d '{"transfer_behavior":"transfer_if_no_active","sandbox_transfer_behavior":"transfer"}'

sandbox_transfer_behavior applies to sandbox purchases. Leave it null to use transfer_behavior for both. More background is in Who owns a restored purchase.

The TRANSFER webhook#

When a subscription moves, your webhook receives an event like this one, captured from a local run:

JSON
{
  "api_version": "1.0",
  "event": {
    "id": "90CB2D5B-DDD2-4F25-9430-4E5290CC493A",
    "type": "TRANSFER",
    "store": "TEST_STORE",
    "app_id": "appvnrm0a5h",
    "environment": "SANDBOX",
    "transferred_from": ["alice"],
    "transferred_to": ["bob"],
    "event_timestamp_ms": 1790800924235,
    "subscriber_attributes": {}
  }
}

Use it to move access in your own database from the transferred_from IDs to the transferred_to IDs.

If the restore fails with 7102#

  1. The project uses keep, or transfer_if_no_active while the owner still has an active subscription.
  2. Tell the user the purchase belongs to another account, and ask them to sign in with that account.
  3. If the rule is too strict for your app, switch to transfer.

If the restore finds nothing#

  1. Check that the user is signed in to the same Apple or Google account that bought.
  2. Check that the purchase is for this app's bundle ID or package name.
  3. Check the server log for a 5xx on /v1/receipts. See Why does RevenueDot answer 4xx or 5xx to a receipt?
Edit this page on GitHub ↗ View as Markdown Last updated