Skip to main content

Overview

When RevenueCat is your purchase SDK, RevenueCat — not AppDNA — owns every store transaction. AppDNA learns about purchases, renewals, cancellations and refunds from RevenueCat’s webhook. Each RevenueCat event is converted into AppDNA’s own canonical events (purchase_completed, subscription_renewed, …) with source: "revenuecat", so they appear in revenue, lifetime value, attribution, audiences and dashboards exactly like purchases made through AppDNA’s own billing.
RevenueCat event mapping is enabled at release. Authenticated RevenueCat deliveries are counted as soon as the integration is connected.
Provider events count for revenue, not for activity. RevenueCat events count for revenue, lifetime revenue and attribution. They never count as sessions, active users, retention or billable MAU — those come only from the AppDNA SDK running in your app. RevenueCat purchases and renewals do count toward MTPU (monthly tracked paying users), once per user after de-duplication with SDK purchases.

Configure the SDK for RevenueCat

Set billingProvider to revenueCat in your AppDNA configuration. With that setting:
  • AppDNA never finishes (iOS) or acknowledges (Android) a store transaction — RevenueCat does.
  • RevenueCat is not linked into AppDNA itself (on Android adding it to your app does not change that; for iOS see when a provider counts as linked), so in every published build tapping a plan on an AppDNA paywall does not open a store sheet. The SDK emits purchase_failed with error_type: "providerNotAvailable" and calls your paywall delegate’s onPaywallPurchaseFailed with errorType: "providerNotAvailable" and the tapped productId.
  • For the same reason, AppDNA.billing.restorePurchases() throws providerNotAvailable in every published build: restore through RevenueCat (Purchases.restorePurchases).
  • The SDK does not emit device-side subscription_renewed, subscription_canceled or subscription_renewal_failed events: the webhook is the single source for them.
Recipe — buy from an AppDNA paywall with RevenueCat: in onPaywallPurchaseFailed, when errorType is providerNotAvailable, start the purchase for productId with RevenueCat (Purchases.purchase(...)), then dismiss the paywall. The purchase then reaches AppDNA through this webhook. Use RevenueCat’s CustomerInfo as the source of truth for entitlements; the SDK’s own entitlement reads come from the store and can differ (for example, RevenueCat promotional grants).

Identity

Call RevenueCat’s Purchases.logIn with the same user id you pass to AppDNA.identify. AppDNA matches each RevenueCat event to the AppDNA user whose identify id equals RevenueCat’s app user id (or one of its aliases). Events from an anonymous RevenueCat user are still stored and counted, and are linked to the user once a later event carries an identified app user id.

Do not track purchases yourself

Once the webhook is connected, do not track purchase or subscription events yourself with AppDNA.track (purchase_completed, subscription_started, …). The webhook is the purchase source; a host-tracked copy that does not carry RevenueCat’s transaction id would be counted a second time. For UI analytics around your own purchase screens, use custom event names (for example checkout_sheet_opened).

Connect

  1. Open Integrations in the dashboard, choose RevenueCat → Connect, enter your RevenueCat Secret API Key and press Test & Connect (admin role).
  2. The dialog shows the Webhook URL. If you are the organisation owner, it also shows the Authorization header value once, with Copy. An admin who is not the owner sees “Ask your organisation owner to reveal the webhook credential on this integration’s page”.
  3. In RevenueCat → Project settings → Integrations → Webhooks, add a webhook:
    • Webhook URL: the URL from AppDNA.
    • Authorization header value: the value from AppDNA (it has the form Bearer <secret>; AppDNA also accepts the secret without the Bearer prefix).
    • Environment: send both sandbox and production events (AppDNA separates them, below).
  4. Press Send test event in RevenueCat. AppDNA answers 200 — the test event is stored but never counted.

Reveal and rotate (owner only)

On the integration’s page the Authorization value is masked. Only the organisation owner can Reveal and Copy it, or Rotate it. Rotating invalidates the old value immediately: RevenueCat deliveries fail with 401 until you paste the new value into RevenueCat. RevenueCat retries a failed delivery up to five times, 5, 10, 20, 40 and 80 minutes apart (about 2 hours 35 minutes in all), and then stops, so deliveries made during the paste window are not lost. Disconnecting and reconnecting the integration keeps the existing value.

Optional: webhook signing secret

RevenueCat can also sign each delivery (header X-RevenueCat-Webhook-Signature). To require signatures:
  1. In RevenueCat → Webhooks, enable signing and copy the signing secret (RevenueCat shows it once).
  2. On the AppDNA integration page, paste it into Webhook signing secret (optional) and save (owner only).
Once a signing secret is set, both the Authorization value and the signature must be valid, and a signature older than 5 minutes is rejected. If you set the secret in AppDNA but do not enable signing in RevenueCat, every delivery is rejected. Remove turns signature verification off again.

Sandbox and production

RevenueCat marks every event SANDBOX or PRODUCTION. Sandbox events are written to AppDNA’s sandbox dataset and never reach production revenue, dashboards or subscription analytics.

Event mapping

Every published event carries environment, source: "revenuecat" and AppDNA’s bookkeeping keys (provider, rc_event_type, provider_placeholder_ids and _appdna_origin, and rc_event_id / provider_stable_id when known). Every event except subscription_transferred also carries is_trial, plus, when RevenueCat sends them, product_id (for Google Play, the subscription id before the : of RevenueCat’s <subscription>:<base plan>), store_product_id (the full RevenueCat product id), transaction_id, original_transaction_id, store, price (RevenueCat’s price_in_purchased_currency), currency, period_type, country_code and entitlement_ids; subscription_transferred adds only store. Only the three events that move money — purchase_completed, subscription_renewed and purchase_refunded — carry revenue_usd and count as revenue; on subscription_started, subscription_canceled, subscription_expired and subscription_renewal_failed the price is informational and is never counted. Retries of the same RevenueCat event are deduplicated, and a purchase that both RevenueCat and another source report is counted once. Renewals of a subscription RevenueCat reports are counted from RevenueCat only: the AppDNA SDK’s own subscription_renewed for that subscription is kept as an event but adds no revenue, even when its transaction id differs.

Troubleshooting