Apple App Store native IAP
Connect Apple's App Store Server Notifications V2 and App Store Server API directly, without RevenueCat in the middle. iOS-only.
When to use Apple native
The trade-offs versus the RevenueCat option.
Pick Apple native if
Pick RevenueCat instead if
What you'll need from App Store Connect
Five values plus one file. Apple uses these to sign the JWT we send on every API call.
Bundle ID
Reverse-DNS identifier of your app, e.g. com.acme.app. Find under: App Store Connect → My Apps → your app → App Information.
App Apple ID
The 10-digit numeric ID Apple assigns to your app. Same page as Bundle ID, labeled 'Apple ID'.
Issuer ID
UUID for your App Store Connect API issuer. Find under: Users and Access → Integrations → App Store Connect API → Active.
Key ID
10-character identifier of the In-App Purchase key. Generated in the next step.
Private key (.p8 file)
Generated in the next step. Apple only lets you download it once, store it somewhere safe before uploading to us.
Environment
Production for App Store traffic; Sandbox for TestFlight and StoreKit testing. You can connect a separate sandbox app on Affiliateo for testing if needed.
Step 1: Generate the .p8 key in App Store Connect
AuthKey_XXXXXXXXXX.p8 file. Apple only lets you download this once. Save a backup before you leave the page.Step 2: Connect on Affiliateo
We store the .p8 encrypted at rest with AES-256-GCM. We never log it, never expose it back through the UI, and only use it to sign JWTs for Apple's Server API. Rotate it in App Store Connect → Affiliateo any time.
Step 3: Paste the Server URL into App Store Connect
This is the manual step Apple doesn't let us automate.
If you already use the Server URL for your own backend: Apple only allows one URL per environment per app. You have two options: (a) switch the URL to ours entirely (greenfield apps), or (b) keep your URL and have your own server forward the raw { signedPayload } body to our endpoint. Option (b) is a ~20-line proxy.
Step 4: Pass appAccountToken at purchase time
Our SDK auto-mints a stable UUID per (app, affiliate) at identify time and registers it with our backend. Your purchase code reads it from the SDK and hands it to whichever IAP library you use. Apple stamps the UUID onto every transaction in the chain (initial purchase, every renewal, every refund), and our webhook resolves it back to the affiliate.
Pass appAccountToken to StoreKit 2 at purchase time
import StoreKit
import Affiliateo
@MainActor
class PurchaseManager: ObservableObject {
@EnvironmentObject var affiliateo: AffiliateoManager
func buy(_ product: Product) async throws {
let token = affiliateo.state.appAccountToken
let opts: Set<Product.PurchaseOption> = token.map { [.appAccountToken($0)] } ?? []
let result = try await product.purchase(options: opts)
// handle result.success / userCancelled / pending
}
}Requires iOS 15+. On older iOS versions appAccountToken is nil; the purchase still works but is booked as organic on our side.
How attribution works under the hood
What happens between a user clicking an affiliate link and a renewal showing up on the affiliate's earnings.
/api/v1/mobile/identify with their device fingerprint. We match the fingerprint against recent clicks and return a ref_code if matched./api/v1/mobile/apple-token to bind it to the visitor row.appAccountToken from the SDK and passes it to StoreKit. Apple stamps it onto the transaction record.SUBSCRIBED or ONE_TIME_CHARGE notification. We verify the signature (JWS chain pinned to Apple Root CA G3), decode the inner transaction, look up appAccountToken → visitor → ref_code → affiliate, and record the conversion.DID_RENEW carrying the same token. We record it as a renewal conversion under the same affiliate.REFUND (or REVOKE for Family Sharing revocations). We insert a negative-amount conversion, decrement the affiliate's totals, and claw back the commission from their wallet if it was already paid out.Which Apple events we handle
From App Store Server Notifications V2. Anything not listed gets logged for audit but doesn't change conversion state.
SUBSCRIBEDsubtype INITIAL_BUY or RESUBSCRIBE → records as new subscriptionONE_TIME_CHARGEiOS 17.4+, production from May 27 2025 → records as one-time purchaseDID_RENEWrecords as renewal under the original affiliateREFUNDrecords refund + decrements affiliate counters + claws back paid commissionREVOKEFamily Sharing revocation, treated the same as REFUNDOFFER_REDEEMED (INITIAL_BUY / RESUBSCRIBE)treated as a new subscriptionKnown limitations
Things to be aware of before going live.
iOS 15+ required for first-party attribution
appAccountToken only exists in StoreKit 2 (iOS 15+). Older devices can still purchase, but the transaction has no token, so we book it as organic.
Pre-existing purchases stay organic
Customers who connected Apple to an already-running app: only purchases made AFTER you ship the SDK with appAccountToken support are attributable. Historical transactions don't carry the token.
Apple's commission isn't in the payload
Apple charges 15-30% off the top. The price we receive is what the customer paid, not what you net. If you set affiliate commission as a percentage, it's computed on the gross.
Multi-currency aggregate stats
We store each conversion in its native currency (Apple sends ISO 4217 + milliunits). Cross-currency aggregate revenue stats on the affiliate level may mix currencies until our Phase 1.5 FX layer ships.
Sandbox notifications deliver once, no retries
A handler bug in sandbox silently drops the event. Use the Sandbox environment for end-to-end testing; production retries 5 times over 72h.
Troubleshooting
"Credential validation failed" on connect
Apple rejected the JWT we built from your .p8 and Issuer/Key IDs. Double-check the Issuer ID (UUID from App Store Connect → Users and Access → Integrations), the Key ID (10 chars next to your downloaded key), and that the .p8 file matches that exact Key ID. The .p8 is downloadable only once, if you lost it, generate a new key and reconnect.
Connected but "Last event" never updates
Apple isn't delivering notifications to our URL. Most common cause: you didn't paste our URL into App Store Connect, or you pasted it as V1 instead of V2. Go to App Store Connect → App Information → App Store Server Notifications, verify the Server URL matches what Affiliateo shows on the connected card, and that the Version is V2.
Purchases happen but attribution shows organic
Your purchase code isn't passing the SDK's appAccountToken to StoreKit. Check that you're reading it from the SDK after identify completes (use the isLoading flag or await Affiliateo.ready), and that you're running iOS 15+.
"Credentials invalid" banner appears later
Our daily reconciliation cron tried to call Apple's Server API and got 401. Likely the key was rotated or revoked in App Store Connect. Generate a new key, download the new .p8, and reconnect.
Want to switch to RevenueCat (or vice versa)
Disconnect Apple in the edit dialog first. The app goes back to pending_provider and you can connect RevenueCat from there. The two are mutually exclusive: RevenueCat covers iOS already, so stacking them would double-count purchases.
Need help connecting Apple? See the main mobile affiliate guide for the broader overview, or contact support.