How payments and subscriptions work
AnythinkPay takes Stripe and Apple payments through one plan, subscription and entitlement model, so your app checks access one way for both.
Last updated
AnythinkPay is the payments and subscriptions system built into every project. It takes one-off payments and runs recurring subscriptions through Stripe (web, Android and non-digital purchases) and Apple in-app purchases (iOS digital goods). Both end up as the same subscription record, so your app asks one question, "does this user have access?", whichever provider they paid through.
This page is the mental model. To take a first payment, start with Sell a subscription in 15 minutes.
The moving parts
| Part | What it is |
|---|---|
| Stripe Connect account | Your own Stripe account, connected to your project. Payments for what you sell are paid out to it. See Take payments with Stripe. |
| Apple in-app purchases | Your App Store Connect app, connected to your project so Anythink can verify StoreKit 2 transactions and receive App Store Server Notifications. See Verify Apple in-app purchases. |
| Plan | A product you define once: name, price, billing interval, trial length, an optional Apple product id and a tier rank for comparing plans. Plans live in your project, under AnythinkPay › Plans. |
| Subscription | One subscriber's purchase: a status, the current period, the provider (stripe or apple) and the users it is linked to. |
| Payment | A single charge, either a one-off payment or a subscription's recurring invoice, with its amount, status and fees. |
| Subscription event | An append-only history entry for a subscription, such as created, renewed, cancelled, refunded or plan changed. It powers the timeline on a subscription's page. |
| Offer and code | A referral programme or promo code. A code carries a reward, such as bonus trial days or account credit, and rules for who can redeem it. Under Growth. |
| Balance | The credit or points a user has earned from offers, adjustable by an administrator. |
| Entitlement | The answer to "can this user use the paid feature right now?", read with one call that covers Stripe, Apple and the app's free trial. |
A subscription copies its price, interval and trial length from a plan when you create it in the dashboard. Apple subscriptions are matched to a plan by the plan's Apple product ID.
One model, two providers
From your app's point of view a Stripe subscriber and an Apple subscriber look the same: a subscription with a provider, a status and a current_period_ends_at. One rule turns the status into access.
| Status | Access |
|---|---|
active, trialing, grace_period |
Yes |
canceled (Stripe) or cancelled (Apple), meaning auto-renew is off |
Yes, until current_period_ends_at passes |
past_due or in_billing_retry, meaning a renewal payment failed |
Yes, until current_period_ends_at passes |
revoked, expired, unpaid |
No |
Anything else, including incomplete and superseded |
No. An unrecognised status never grants access. |
You don't re-implement this table. Call GET /org/{project_id}/integrations/anythinkpay/subscriptions/me/entitlement and read has_access. The same rule decides it for both providers. See Offer codes, free trials and entitlements.
The lifecycle of a Stripe subscription
- You connect Stripe and create a plan. See Sell a subscription in 15 minutes.
- A subscription is created for a subscriber, and they complete Stripe Checkout. The subscription is
incompleteuntil they pay. - Stripe tells Anythink the subscription is
trialingoractive. Anythink updates the subscription, records its history and raises Subscription Activated in your project. - Each renewal, Stripe reports the paid invoice. Anythink records a payment against the subscription.
- If a renewal fails the status becomes
past_due, and the subscriber keeps access until the period ends. If Stripe gives up it becomesunpaid. - If the subscriber cancels, auto-renew switches off and they keep access until the period ends. When Stripe ends the subscription its status becomes
canceled, and Subscription Expired is raised.
The lifecycle of an Apple subscription
- The subscriber buys in your iOS app, and StoreKit 2 returns a signed transaction.
- Your app sends it to
POST .../subscriptions/apple/verify. Anythink verifies it, links it to the signed-in user and returns the subscription withhas_access. - Apple sends App Store Server Notifications to the URL you registered once. Renewals, refunds, expiries, billing retries and plan changes update the status and add subscription events.
Card numbers stay with Stripe and never reach Anythink. Apple's private key is stored encrypted and never read back.
Where the money goes
For Stripe, payments are paid out to your connected Stripe account, minus Stripe's processing fee and Anythink's platform fee.
To see the fees that apply to your project and try an amount, open Settings › Payments and choose the Fees & Calculator tab. This page doesn't repeat them, because that tab always shows the current figures.
Apple's payouts and fees are handled between you and Apple.
Webhooks into Anythink
You don't build a webhook endpoint for payments. Provider events arrive at a payments service that Anythink runs.
- Stripe sends events for your connected account. Anythink verifies each one, then updates the matching payment, subscription or saved payment method. Events it doesn't act on are acknowledged and ignored.
- Apple sends App Store Server Notifications V2 to a URL you register once in App Store Connect. Anythink shows the URL on the Apple tab of Settings › Payments.
For the list of Stripe events, the Apple setup steps and the workflow events they raise, see Webhooks.
Events your workflows get
After Anythink records a Stripe change, it raises a matching event in your project and delivers it with a signature your project checks. Start a workflow from one with an Event trigger. See Triggers and scheduling.
| Event | Raised when | Trigger data |
|---|---|---|
| Payment Succeeded, Payment Failed | A one-off Stripe payment (a web checkout or a mobile payment) completes or fails | The payment |
| Subscription Created | A Stripe subscription is created and is waiting for its first payment | The subscription |
| Subscription Activated | Stripe reports a subscription as active or trialing, on first activation and on later updates |
The subscription |
| Subscription Expired | Stripe reports a subscription as canceled, unpaid or incomplete_expired |
The subscription |
| Payment Method Captured, Payment Method Capture Failed, Payment Method Removed | A subscriber's saved card is added, fails to be saved or is removed | The payment method |
For these events $anythink.trigger.id is 0, so read everything from $anythink.trigger.data. Field names are snake_case. A payment has id, type, provider, amount, currency, status, description, reference, paid_at, metadata and payable_id, the id of the Anythink user it is attributed to. A subscription has id, name, type, amount, currency, billing_interval, status, provider, current_period_ends_at, trial_ends_at and subscriber_id, the id of the subscriber.
If your project can't be reached when an event is delivered, Anythink retries up to five times, waiting 30 seconds, then 1, 2, 4 and 8 minutes.
Limit: Events come from Stripe only. Apple notifications update subscriptions and their history, but they don't start workflows.
Limit: A renewal's invoice is recorded as a payment, and it shows in AnythinkPay › Payments, but it doesn't raise Payment Succeeded. To react to renewals, use Subscription Activated.
Limit: Subscription Expired fires when access ends, not when the subscriber turns off renewal. Until the period ends the status is still active.
Payments recipes has workflows for the common cases.
What your app calls
Your app calls AnythinkPay through your project's API, as the signed-in user, never straight to the payments service. Every call below needs only the anythink_subscription_plans:read permission, so a subscriber's own token works and your app never needs an administrator key or an API key.
| Call | What it does |
|---|---|
GET .../subscriptions/me/entitlement |
Returns has_access and the details behind it |
GET .../subscription-plans |
Lists your plans for a paywall |
GET .../payment-options?platform=ios&storefront=GBR |
Says which provider to offer on a platform |
POST .../subscriptions/apple/verify |
Verifies an Apple purchase and links it to the user |
GET .../offers/me/code, .../offers/me/codes, .../offers/me/referrals |
The user's personal referral codes and stats |
POST .../offers/me/redeem/{slug} |
Redeems a code as the user |
GET .../balances/me |
The user's credit or points balance |
Every path starts /org/{project_id}/integrations/anythinkpay. Give the role your subscribers use the permission first. Payments recipes shows how.
What the entitlement call returns
| Field | Meaning |
|---|---|
has_access |
The only field you need to gate a feature |
provider, product_id, status |
The subscription behind the answer. product_id is the subscription's name, which matches a plan's name. |
subscription_id |
The subscription's id |
expires_at |
When the current period, or the free trial, ends |
auto_cancel_at, cancelled_at |
Set when the subscriber has turned off renewal |
is_trial, trial_ends_at, trial_days_remaining |
Set while the user is on the app's free trial |
trial_length_days, trial_base_days, trial_bonus_days |
The trial's length, split into the project's setting and days added by promo codes |
pending_product_id, pending_at |
An Apple plan change that takes effect on the next renewal |
If a user has several subscriptions, the call returns the strongest one: one that grants access first, then the one that runs longest. A user who has never subscribed is resolved from the app's free trial, which needs no call to the payments service.
The app's free trial
Separate from a plan's own trial, you can give every user who has never subscribed one free window of access. Turn it on under Growth › Engagement and set its length in days. It starts the first time that user calls the entitlement endpoint, runs once per user and never restarts. A promo code with a trial reward adds days to it. Payments recipes has a worked example.
Limits
Limit: Apple in-app purchases are iOS only. Android and web go through Stripe checkout.
Limit: There is no team or per-seat billing. Every subscription has one payer. You can share read access to a subscription with other users, but that doesn't change who is billed.
Limit: Cancel, Resume and Resync work on Stripe subscriptions only. Apple subscribers manage their subscription in the App Store, and an Apple subscription changes only when a notification arrives.
Limit: Apple subscriptions don't raise workflow events, and Stripe renewals don't raise Payment Succeeded. See the limits under Events your workflows get.
Note: Plan creation and listing work even when the payments service is down, because plans live in your project. Anything that touches a live subscription, payment or offer needs the payments service. When it is unreachable, the entitlement call for a user who has subscribed, and Apple verification, return 502.