Tapll reads revenue from two webhooks: Apple’s App Store Server Notifications and RevenueCat. Both describe the same purchases, so only one of them counts at a time.
Which source counts
| Connected | What counts |
|---|---|
| App Store Server Notifications | Apple’s notifications. |
| RevenueCat only | RevenueCat’s webhooks. |
| Both | Apple’s notifications; RevenueCat events are stored but not counted, and the panel says so. |
Apple signs every notification and needs no third party, so it wins when both are connected.
App Store Server Notifications
In Settings → App Store Server Notifications, create the notification URL and paste it into App Store Connect as the Production server URL. Apple accepts only one URL per app, so if your backend already receives notifications, enter its URL as the forward target: Tapll answers Apple first and forwards every notification unchanged, with retries and a log of each attempt.
Apple does not send a USD price, so revenue from notifications is shown per currency as charged.
RevenueCat
Create a webhook in RevenueCat pointing at the URL from Settings → RevenueCat. Optionally add a v2 secret key (sk_…): Tapll checks it against RevenueCat before saving it, uses it to import subscriptions from before the webhook existed, and to look up purchasers.
Link purchases to people
With StoreKit 2, pass Tapll’s install id as the appAccountToken on every purchase:
let result = try await product.purchase(
options: Tapll.installUUID.map { [.appAccountToken($0)] } ?? []
)
With RevenueCat, set the subscriber attribute after Purchases.configure, and call Tapll.identify(userId:) with the same id you pass to Purchases.logIn:
if let id = Tapll.installId {
Purchases.shared.attribution.setAttributes(["tapll_install_id": id])
}
The purchase token is not retroactive Purchases made before
appAccountTokenships can never be linked to a person. Ship it before the first release that sells anything, even if you connect revenue later.
Revenue totals do not depend on any of this: a purchase Tapll cannot link still counts, and the Revenue view shows how many subscriptions are unlinked.
Stripe and Paddle (websites)
Payments on a website come from Stripe or Paddle (Billing). They are separate sources from the two above, and they never mix: Tapll shows website money in its own block, never added to in-app revenue or to the other web provider.
In Settings, open the Stripe or Paddle section and create the webhook URL. In Stripe, add it under Developers → Webhooks and bring the endpoint’s signing secret back to Tapll. In Paddle, add it under Developer tools → Notifications as a webhook destination and bring back the destination’s secret key. Tapll stores the secret but cannot check it until the first delivery arrives; the section says when it has.
To tie a payment to the visit that made it, pass the visit’s id into checkout. With Stripe, set it on the Checkout Session and on the subscription, because a Checkout Session’s metadata is not copied onto the subscription it creates:
stripe.checkout.sessions.create({
mode: 'subscription',
// ...line_items, success_url, cancel_url
metadata: { tapll_session_id: tapllSessionId, tapll_user_id: userId },
subscription_data: {
metadata: { tapll_session_id: tapllSessionId, tapll_user_id: userId },
},
});
With Paddle.js, customData is copied onto the transaction and the subscription:
Paddle.Checkout.open({
items: [{ priceId: 'pri_…', quantity: 1 }],
customData: {
tapll_session_id: tapll.sessionId() ?? '',
tapll_user_id: userId,
},
});
Here tapllSessionId is tapll.sessionId() read in the browser and sent to your server. Tapll keeps no email, name or address from either provider; the customer is stored as a keyed hash. A payment with no visit attached still counts as revenue, it just has no channel.
Strict anonymous projects
Tapll.installUUID is nil in strict anonymous mode, so purchases are counted but never linked to a person. Do not set appAccountToken from Tapll.