# Mobile App Marketing Stack: Subscription Data, Tools and Release Tests

A subscription app’s marketing stack should establish who has access, what they paid, how they use the product and which action could help them next. Start with verified store or billing state and joinable app identities. Add product analytics, attribution and lifecycle tools for specific decisions. Keep entitlement, revenue, renewal intent and campaign credit separate so one misleading status cannot drive the whole customer experience.

Canonical URL: https://www.martechdevs.com/blog/mobile-app-marketing-stack

By [Jafrin Akter Alo](https://www.martechdevs.com/authors/jafrin-akter-alo)

Data Engineer and Co-founder at [Martechdevs](https://www.martechdevs.com) and [Growth and Analytics Partners](https://www.growthandanalytics.com)

[LinkedIn](https://linkedin.com/in/jafrinalo)

Last updated: 2026-10-25

Next step: [B2B SaaS Martech Stack: Connect Product, Sales and Billing](https://www.martechdevs.com/blog/b2b-saas-martech-stack)

A customer can turn off auto-renewal and still have paid access. Another can enter a billing grace period without becoming a lost subscriber. A third can restore a purchase on a new device without generating another sale. If every tool receives only `is_subscriber: true` or `false`, those differences disappear.

The [industry stack guide](https://www.martechdevs.com/blog/martech-stack-by-industry) explains how the business model changes the architecture. This guide focuses on subscription apps and the joins between purchase handling, customer access, product behavior and growth.

## Which jobs should the app stack perform?

Give each layer a defined question and owner. Start with the layers needed for a working purchase and customer experience; add analysis and activation capacity when a recurring decision justifies it.

| Job | Governing evidence | Implementation choice | Acceptance question |
| --- | --- | --- | --- |
| Grant product access | Verified subscription or purchase state and product mapping | Direct store integration or a subscription service | Does the correct account receive the promised access? |
| Record money | Validated transactions and adjustments | Billing ledger plus store or processor reports | Do purchases and refunds reconcile by currency and period? |
| Understand product value | Deliberate first-party events | Existing event setup or product analytics | Can the team explain activation and retention behavior? |
| Evaluate acquisition | Spend, eligible attribution signals and commercial outcomes | Direct reports or an MMP | What decision does the attribution evidence support? |
| Help customers progress | Current eligibility, product state and channel permissions | In-app, push or email workflows | Is the message still relevant when it is sent? |
| Join recurring questions | Stable identities and maintained cross-source models | A warehouse when the need warrants it | Can an owner explain the result and its gaps? |

The important boundary is between an operating fact and a report about it. An attribution tool can credit a campaign for a renewal; it should not decide whether the renewal was valid. A messaging tool can react to a billing issue; it should not become the only place the app records access.

Use [product analytics and app tracking implementation](https://www.martechdevs.com/product-analytics) to connect those facts to the decisions your team actually makes. The deliverable should include the exception cases and operating owner, not just an SDK appearing in the build.

## Where should subscription truth come from?

Use verified store or billing information, either directly or through a subscription service that maintains it. Treat notifications as inputs to a state and transaction model, not as commands to blindly toggle a customer’s access.

### What do Apple’s subscription notifications mean?

Apple’s V2 notifications distinguish `SUBSCRIBED`, `DID_RENEW`, renewal-status changes, billing failures, grace-period expiry and refunds. Subtypes matter: `AUTO_RENEW_DISABLED` describes a renewal choice, while `BILLING_RECOVERY` describes a recovered renewal. `REFUND_REVERSED` differs from `REFUND`, and Family Sharing can produce access changes. Preserve type, subtype and the transaction context. [Apple notification types](https://developer.apple.com/documentation/appstoreservernotifications/notificationtype)

Verify signed data before accepting its contents. Apple’s server library provides signed-notification verification with the configured root certificates, environment and app identity. Production and sandbox configuration must remain distinct. A JSON payload that contains a plausible transaction ID is not verified evidence. [Apple’s server library](https://github.com/apple/app-store-server-library-python)

For current access and history, Apple provides subscription-status and transaction-history APIs. Billing Grace Period is a configured way to retain access during recovery; a failed renewal alone does not identify the complete access decision. [Apple subscription state and recovery](https://developer.apple.com/app-store/subscriptions/)

### What does Google Play send through RTDN?

Google’s real-time developer notification tells you that purchase state changed. Google requires a Play Developer API call for the complete status and recommends checking the notification’s `messageId` for duplicates. Retain the purchase reference and package context needed to retrieve and reconcile the actual resource. [Google RTDN reference](https://developer.android.com/google/play/billing/rtdn-reference)

For auto-renewing Play subscriptions, voluntary cancellation can leave access valid until the returned expiry. Grace period retains entitlement; account hold removes it. Recovery and expiry require their corresponding state handling. Do not turn a cancellation notification into immediate revocation without checking the applicable state and expiry. [Google subscription lifecycle](https://developer.android.com/google/play/billing/lifecycle/subscriptions)

A later-arriving notification can describe an earlier event. Refresh authoritative state and preserve provenance instead of letting network arrival order decide which customer fact wins. Keep an exception path for a failed refresh; missing information is not a newly verified expiration.

## Should you use RevenueCat or build the subscription layer yourself?

Use a service when its supported stores, identity model and operating behavior fit, and when maintaining those integrations yourself would distract from the product. Build directly when you need control the service does not provide and can own validation, lifecycle handling, recovery and ongoing platform changes.

RevenueCat entitlements map purchased products to an access level. Product attachment matters: a product can exist without granting the entitlement your app checks. Verify the mapping for every relevant product, tier and platform. [RevenueCat entitlements](https://www.revenuecat.com/docs/getting-started/entitlements)

| Choice | Can suit | Work that remains yours |
| --- | --- | --- |
| Direct store integration | A team with the engineering capacity and a clear need for control | Verification, state reconciliation, identity, monitoring and platform maintenance |
| RevenueCat or another evaluated subscription service | A team wanting maintained purchase and entitlement infrastructure | Product mapping, app integration, identity decisions, permissions and customer tests |
| Existing service plus a warehouse | Repeated questions joining billing, behavior and acquisition | Canonical models, freshness, reconciliation and downstream action rules |

RevenueCat’s Pro pricing is free up to the stated USD 2,500 monthly tracked revenue threshold, then 1% of tracked revenue; custom arrangements are available. Check the exact account terms around the threshold rather than treating every quote as the same package. [RevenueCat pricing](https://www.revenuecat.com/pricing/)

Monthly Tracked Revenue is distinct from MRR. It includes purchases and renewals, including non-subscription products, before store commission and taxes. RevenueCat’s example bills USD 26 for USD 2,600 of MTR: the percentage applies to the tracked amount, not only the USD 100 above the threshold. [RevenueCat billing basis](https://www.revenuecat.com/docs/welcome/set-up-revenuecat/account-management)

For an illustrative USD 12,000 MTR cycle under that 1% basis, the service line is USD 120. It is not the full app stack cost: messaging, analytics, attribution, infrastructure, store or processor deductions and internal work remain separate. Compare the whole operating scope before deciding that building directly is cheaper.

RevenueCat lists integrations with analytics and lifecycle tools including Amplitude, Mixpanel, PostHog, Braze, Customer.io and OneSignal. An integration being listed does not prove that its event mapping, identity and feature scope fit your workflow. Test the actual destination behavior. [Supported integrations](https://www.revenuecat.com/docs/integrations/third-party-integrations)

## Which identity rules prevent access and messaging mistakes?

Keep person identity, store purchase identity, device destination and attribution identity distinct. Join them only through a supported, verified relationship.

A stable application account ID can connect the signed-in customer to analytics and lifecycle systems. A push token is a delivery destination, not the person’s primary identifier. An email address can change or be shared. A store purchase reference identifies a purchase relationship, not every human who might receive access through it.

RevenueCat creates an anonymous App User ID when one is not supplied; reinstalling can create a new one. Login, aliases and restores depend on the identity and restore behavior selected. RevenueCat recommends non-guessable custom IDs and advises against email or advertising identifiers as App User IDs. [RevenueCat customer identity](https://www.revenuecat.com/docs/customers/identifying-customers)

Use the [identity-merge guide](https://www.martechdevs.com/blog/identity-merge) for the analytics side. Do not assume that merging two analytics profiles transfers a store subscription, or that a restored purchase creates a second paying customer.

| Scenario | Relationship to verify | Failure to prevent |
| --- | --- | --- |
| Anonymous customer signs in | Intended anonymous activity maps to the correct application account | One shared ID joins unrelated customers |
| Existing customer reinstalls | Supported restore and account mapping recover the intended access | New anonymous profile is counted as another subscriber |
| Two app accounts use one device | Logout and account switching update the right identities and destinations | The next person sees the previous person’s state |
| One account uses several devices | Same account, separate delivery destinations | Person-level audience and device billing counts are confused |
| Family or shared access | Store-supported access relationship remains distinct from the payer | Every beneficiary is treated as a separate purchase |

Record the rule before choosing the SDK calls. Test with accounts that already have purchases as well as fresh installations. The clean new-user path rarely exposes the account-switching and restore problems that support later has to resolve.

## Which events should the first implementation include?

Start with the events needed to explain one useful journey: arrival, onboarding, first value, purchase intent, verified purchase or trial, renewal state and relevant exceptions. Give each event a producer and a meaning.

| Event family | Authoritative producer | What it should establish |
| --- | --- | --- |
| First open and onboarding | Approved application instrumentation | The measured app action occurred |
| Core value action | Product system or app event contract | The customer completed a meaningful task |
| Paywall view | Application or paywall implementation | An offer was shown, not purchased |
| Trial or purchase outcome | Verified store/billing integration | The relevant product and purchase state exist |
| Renewal, billing issue and expiry | Verified subscription state pipeline | The state changed with its correct effective context |
| Refund or reversal | Verified monetary adjustment source | A specific financial adjustment occurred |

Use [event names that survive app releases](https://www.martechdevs.com/blog/event-naming-convention). A paywall click, a completed purchase and a successful renewal should not share one generic conversion name. Keep product, offer, currency, environment and source references with the appropriate records.

Choose one canonical producer for each monetary fact. If the app SDK, subscription service and backend all emit the same purchase into analytics, three green integrations can produce three times the revenue. Deduplicate transport retries separately from reconciling different producers that describe the same commercial transaction.

RevenueCat recommends idempotent webhook processing using its event ID and supports separate sandbox/production filtering. It also recommends refreshing customer information through its API when syncing state. Retries are finite, so a receiver needs monitoring and recovery rather than an assumption that every missed delivery will return forever. [RevenueCat webhooks](https://www.revenuecat.com/docs/integrations/webhooks)

Authenticate the actual delivery mechanism, record receipt durably and process it through the appropriate state or ledger path. Acknowledge only according to the receiver design you can recover. A transport success is not proof that analytics, entitlements and messaging have all consumed the update.

## How should lifecycle messaging use subscription state?

Use current customer need and channel eligibility, with a final check close to dispatch. Renewal intent, current access and a successful payment should be separate inputs to the decision.

| Verified situation | Useful customer action | Incorrect shortcut |
| --- | --- | --- |
| Trial is active; first value is missing | Help complete the relevant task | Repeatedly push payment before the customer can use the product |
| Auto-renewal is disabled; access remains valid | Respect the choice and clarify remaining access where useful | Declare the customer expired immediately |
| Payment recovery is in progress with valid grace access | Provide appropriate payment-recovery help | Block access simply because a payment attempt failed |
| Access has expired after authoritative confirmation | Explain the current state and permitted next step | Continue a paid-customer journey using stale eligibility |
| A refund or access reversal is confirmed | Update the relevant monetary and access records | Treat every adjustment as a new purchase |

Those are workflow recommendations, not identical state names across stores. Apply the store or service’s actual entitlement rules, then decide which communication is appropriate. Do not infer that a marketing subscription grants permission for every transactional or promotional purpose.

Push delivery depends on app and operating-system eligibility. In-app content requires the customer to open the relevant experience. Email requires a usable address and the applicable permission and suppression handling. Store recovery notices can already be doing part of the job; coordinate your own messaging instead of creating several competing reminders.

Compare [Braze and Customer.io](https://www.martechdevs.com/blog/braze-vs-customer-io) or [OneSignal’s pricing and billing units](https://www.martechdevs.com/blog/onesignal-pricing) only after defining those requirements. A lower per-user headline is not useful if it uses a different population or omits the workflow you need.

Do not assume a server update instantly refreshes a device screen. RevenueCat’s `CustomerInfo` is cached, and updates reach the client through outbound requests rather than being pushed automatically from its backend. Test the refresh behavior at premium-content entry and after purchase or restore. Keep the supported offline behavior explicit. [CustomerInfo and caching](https://www.revenuecat.com/docs/customers/customer-info)

## When does an MMP or a warehouse earn its place?

Add an MMP when its maintained acquisition integrations, links, exports and operating support solve a problem your current setup cannot handle economically. Add a warehouse when important recurring questions need several sources and retained history. Neither should be a default purchase just because the app has launched.

Check [what AppsFlyer charges per billed conversion](https://www.martechdevs.com/blog/appsflyer-pricing) before adding its subscription and usage assumptions to the stack budget.

The [mobile attribution guide](https://www.martechdevs.com/blog/mobile-app-attribution) covers SKAdNetwork, AdAttributionKit, reporting delay and missing detail. Attribution credit does not establish incremental impact, and aggregate reports do not provide a verified person-level join just because your product database has an account ID.

Apple requires ATT permission for the cross-company tracking it defines; without authorization, the advertising identifier is zeroed. Hashed emails or fingerprinting do not provide a way around that boundary. Record the applicable OS, regional prompt behavior and permission outcome, then verify each SDK’s actual collection and forwarding. [Apple privacy and data use](https://developer.apple.com/app-store/user-privacy-and-data-use/)

Use [the MMP comparison](https://www.martechdevs.com/blog/appsflyer-vs-adjust) to evaluate the required acquisition paths. A campaign report should identify its attribution method, window and maturity. Keep unavailable attribution as unavailable rather than assigning it to organic traffic or reconstructing a forbidden identity.

A warehouse can join permitted customer activity, validated financial records, message exposure and acquisition cost for a recurring cohort question. Use [cohort retention definitions](https://www.martechdevs.com/blog/cohort-retention-analysis) so install retention, product retention and paid retention do not become interchangeable labels.

For health or children’s apps, evaluate the data sent to each destination before enabling general SDK forwarding. Use the [health-data analytics guide](https://www.martechdevs.com/blog/hipaa-compliant-analytics) for health-specific decisions, and use the [COPPA review for kids’ and education apps](https://www.martechdevs.com/blog/coppa-compliant-analytics) to check the child-data route, SDK collection and store-policy boundaries. An SDK’s technical ability to accept a field is not permission to transmit it.

## How do you reconcile revenue without mixing environments or currencies?

Reconcile canonical purchases and adjustments over the same reporting period, then compare them with the applicable store or processor report. Keep currency, source and environment visible, and distinguish customer charges from net proceeds after deductions.

This original Python helper checks a normalized ledger export for a period you have already selected. Each entry needs a stable commercial `entry_id` within its store and app namespace. A refund or reversal needs its own canonical adjustment entry ID linked to the original purchase; reusing the purchase ID would correctly fail the duplicate-entry check. This helper supports only charges and negative refund entries, and rejects other kinds. Model reversals explicitly in your ledger rather than passing an unsupported kind to this example. Retries of the same entry must not be counted again. Do not substitute a webhook delivery ID for the underlying commercial entry. These are example fields, not a raw Apple, Google or RevenueCat payload schema.

```python
import re

def production_totals(entries):
    seen = set()
    totals = {}
    for row in entries:
        if row["environment"] not in ("production", "sandbox"):
            raise ValueError("unknown environment")
        if row["environment"] == "sandbox":
            continue
        keys = (row["store"], row["app_id"], row["entry_id"])
        if any(not isinstance(x, str) or not x.strip() for x in keys):
            raise ValueError("store, app and entry IDs are required")
        if keys in seen:
            raise ValueError("duplicate commercial entry")
        seen.add(keys)
        currency = row["currency"]
        if not isinstance(currency, str) or not re.fullmatch(r"[A-Z]{3}", currency):
            raise ValueError("use the agreed three-letter currency code")
        amount = row["amount_minor"]
        if type(amount) is not int:
            raise ValueError("amount_minor must be an integer")
        if row["kind"] not in ("charge", "refund"):
            raise ValueError("unsupported ledger entry kind")
        if row["kind"] == "charge" and amount < 0:
            raise ValueError("charge amount cannot be negative")
        if row["kind"] == "refund" and amount > 0:
            raise ValueError("refund amount cannot be positive")
        totals[currency] = totals.get(currency, 0) + amount
    return totals
```

The helper fails on duplicate commercial entries instead of silently choosing one. It does not validate store signatures, resolve identity, discover missing transactions, convert currency or calculate recognized accounting revenue. An empty result is not proof that the app earned no revenue. Its three-letter check is structural; the upstream contract must establish the valid currency and its minor-unit scale.

For a synthetic production export, two USD charges of 999 minor units and one refund of −999 produce USD 999 minor units, or USD 9.99. A separate EUR charge of 899 remains EUR 899 minor units. A sandbox charge is excluded. Adding USD and EUR integers into one total would be meaningless without a defined exchange-rate policy.

| Reconciliation question | Evidence to compare | Reason totals can differ |
| --- | --- | --- |
| Did every commercial entry arrive? | Store or processor population against canonical entry keys | Missing imports, duplicate producers or late adjustments |
| Is the period aligned? | Effective event time, reporting window and timezone | Delivery occurred later than the underlying transaction |
| Are we comparing the same money? | Gross charges, refunds, tax, commission and proceeds definitions | A customer charge is not the payout received |
| Is the population aligned? | App, store, environment and currency | Sandbox activity or another app entered the report |
| Does access agree with the purchase history? | Current entitlement and relevant transactions | Restore, family access, grace or promotional access differs from a new charge |

A reconciled total still needs record-level coverage. Two missing charges and two duplicates can cancel numerically. Keep the exceptions visible and prove the selected population before using the result for acquisition payback or retention decisions.

## What should one trial-to-renewal test prove?

A useful end-to-end fixture follows one account through product behavior, purchase state, access and messaging, then includes a failure and recovery. The following is a hypothetical Apple subscription path with a seven-day trial; it is not a reported customer result or a universal offer configuration.

| Stage | State to verify | Analytics and messaging consequence |
| --- | --- | --- |
| Install and tracking refusal | Permitted app identity exists; no unauthorized cross-company tracking | Record eligible product activity without inventing campaign identity |
| Signup and trial | Correct account receives the intended trial entitlement | Begin useful onboarding once its eligibility is established |
| Paid renewal after trial | Verified renewal and commercial record | Record the paid outcome once and stop obsolete trial messaging |
| Later billing issue with grace | Current access and recovery state reflect the configured grace policy | Offer relevant recovery help without immediately treating access as expired |
| Successful recovery | Verified recovered renewal and refreshed entitlement | Stop unresolved-payment reminders and reconcile the money |

Apple distinguishes grace-enabled billing failure from recovery through its type/subtype model. Use those distinctions in the fixture, but confirm current state before the customer action. A delayed earlier failure notification must not restart a reminder after recovery. [Apple lifecycle notifications](https://developer.apple.com/documentation/appstoreservernotifications/notificationtype)

Then run a separate Android fixture using Play’s actual states and purchase references. Do not relabel the Apple test and assume it proves Google behavior. Add reinstalls, account switching, refund reversal and repeated delivery to the release suite.

## What changes for web purchases or apps without subscriptions?

For web purchases, connect the verified web billing account to the intended app identity and entitlement. RevenueCat Web supports its own billing engine, Stripe Billing and Paddle Billing, with different ownership of billing functions. Confirm the selected integration and current app-store rules before designing external purchase links or an in-app checkout route. Technical integration support is not blanket policy approval. [RevenueCat Web](https://www.revenuecat.com/docs/web/overview)

For a one-off purchase app, replace recurring-renewal analysis with the actual product model. A consumable purchase, a permanent unlock and a temporary pass have different access and restore requirements. Keep the verified purchase, refund and fulfillment relationship explicit. Do not apply subscription expiration logic to a permanent unlock merely because the analytics tool uses the same event name.

For an ad-funded app, revenue comes from the relevant advertising or mediation records and their eventual adjustments and payouts. Product events still explain engagement, but an impression event is not automatically a settled amount received. Reconcile the provider’s revenue definition and timing before comparing cohort acquisition cost with monetization.

Mixed models need both paths. A subscriber might also see ads or buy a one-off item. Name the revenue components and the customer treatment they permit, rather than forcing every record into one subscription flag. A maintained warehouse becomes useful when these combined questions recur and an owner can operate the model.

## Which release checks should pass before growth spend increases?

Prove access, money, identity and messaging independently, then verify the complete customer path. Use the actual store sandbox and supported test tools for the native integration; a local ledger calculation cannot prove a purchase implementation works.

![Identify store or billing evidence and its environment. Unverified source or unresolved account holds the action until repaired. Verified evidence refreshes entitlement and canonical monetary state; stale or unreconciled records return to repair and source verification. Only a remaining relevant, permitted action proceeds; otherwise access remains correct and unnecessary messaging is suppressed.](https://www.martechdevs.com/assets/blog/mobile-app-marketing-stack.svg)

Diagram connections:

- Receive store or billing evidence and identify its environment to Verified source and intended account resolve?. 
- Verified source and intended account resolve? to Hold the action and resolve verification or identity: No. 
- Hold the action and resolve verification or identity to Receive store or billing evidence and identify its environment. 
- Verified source and intended account resolve? to Refresh entitlement and canonical monetary state: Yes. 
- Refresh entitlement and canonical monetary state to State is current and required records reconcile?. 
- State is current and required records reconcile? to Repair the gap and recheck authoritative state: No. 
- Repair the gap and recheck authoritative state to Receive store or billing evidence and identify its environment. 
- State is current and required records reconcile? to A relevant permitted customer action remains?: Yes. 
- A relevant permitted customer action remains? to Keep product access correct and suppress unnecessary messaging: No. 
- A relevant permitted customer action remains? to Perform the owned action and monitor its outcome: Yes

## Checklist

- [ ] Product and entitlement mappings cover every intended store and tier.
- [ ] Verified notifications and authoritative state are separated from raw transport events.
- [ ] Production and sandbox records remain distinct throughout reporting and activation.
- [ ] Cancellation, grace, hold, expiry, refund and recovery have explicit acceptance cases.
- [ ] Login, reinstall, restore and account switching preserve the correct identity and access.
- [ ] Each monetary fact has one canonical producer and a duplicate-control rule.
- [ ] Ledger reconciliation preserves currency, period, coverage and adjustment definitions.
- [ ] SDK behavior respects the relevant tracking and data-sharing decisions.
- [ ] Messaging rechecks current need, channel eligibility and ownership near dispatch.
- [ ] Missing deliveries, stale state and failed refreshes have an operator and recovery path.

Start with the [$99 martech audit](https://www.martechdevs.com/pricing) when subscription, analytics and messaging totals cannot be explained together. For an implementation, [request a detailed product analytics and app integration scope](https://www.martechdevs.com/product-analytics#offer) with the stores, billing route, identity model and first customer journey. That scope makes the access, revenue and activation tests concrete.

## Frequently asked questions

### Can Firebase alone cover the first version of the app?

It may cover the initial product-event and diagnostic needs if those capabilities fit your questions. [Firebase Analytics](https://firebase.google.com/docs/analytics) measures app usage and engagement. It does not remove the requirement to establish verified purchase state, entitlement and current customer eligibility. Start with the required workflow and evidence, then add another platform when it solves a recurring gap rather than duplicating the same events elsewhere.

### Does canceling auto-renewal mean the customer has churned?

It can mean the customer intends not to renew, while access and the paid relationship continue through the valid period. Keep renewal intent, current entitlement and paid retention separate in reports. A later recovery or reversal can change the customer action again. Define which state your churn metric measures before comparing it with a vendor’s cancellation chart.

### Does restoring a purchase create another revenue event?

Restoring access is not by itself evidence of another charge. Reconcile the store purchase relationship and the application identity, then preserve the commercial entry’s uniqueness. A new installation, alias or device can change the way a customer appears in analytics without creating a new sale. Test the restore path against both access and the monetary ledger.

### Can we remove app access whenever the subscription API is unavailable?

An unavailable response is not proof of an expired entitlement. Use the supported cache and offline behavior of the selected implementation, with explicit limits and recovery handling. Keep new marketing actions on hold when their required state is uncertain. Test the outage and refresh paths so temporary connectivity problems do not silently become an invented cancellation or an unlimited access grant.

### Should analytics revenue equal the store payout every month?

Only if the definitions and populations are aligned, which a raw comparison often fails to establish. Customer charges, refunds, taxes, commissions, currency conversion and settlement timing can produce different amounts. Reconcile commercial entries first, then bridge the relevant deductions and timing to proceeds. Preserve unresolved differences rather than forcing an analytics total to equal a payout by adjustment.

## Sources

1. [Apple: App Store notification types](https://developer.apple.com/documentation/appstoreservernotifications/notificationtype).

2. [Apple: App Store Server Library for Python](https://github.com/apple/app-store-server-library-python).

3. [Apple: Subscription status and billing recovery](https://developer.apple.com/app-store/subscriptions/).

4. [Google Play: Real-time developer notifications](https://developer.android.com/google/play/billing/rtdn-reference).

5. [Google Play: Subscription lifecycle](https://developer.android.com/google/play/billing/lifecycle/subscriptions).

6. [RevenueCat: Entitlement mapping](https://www.revenuecat.com/docs/getting-started/entitlements).

7. [RevenueCat: Pricing](https://www.revenuecat.com/pricing/).

8. [RevenueCat: Monthly Tracked Revenue and billing](https://www.revenuecat.com/docs/welcome/set-up-revenuecat/account-management).

9. [RevenueCat: Supported third-party integrations](https://www.revenuecat.com/docs/integrations/third-party-integrations).

10. [RevenueCat: Customer identity](https://www.revenuecat.com/docs/customers/identifying-customers).

11. [RevenueCat: Webhook delivery and state synchronization](https://www.revenuecat.com/docs/integrations/webhooks).

12. [RevenueCat: CustomerInfo and caching](https://www.revenuecat.com/docs/customers/customer-info).

13. [Apple: Privacy and tracking requirements](https://developer.apple.com/app-store/user-privacy-and-data-use/).

14. [RevenueCat: Web billing integrations](https://www.revenuecat.com/docs/web/overview).

15. [Google: Analytics for Firebase](https://firebase.google.com/docs/analytics).
