- Client-side — the SDK receives real-time updates via delegate callbacks
- Server-side — your backend queries the Entitlements API to verify purchase status
How It Works
User completes checkout
The user pays via Apple Pay or card through the ZeroSettle checkout sheet. The SDK creates a PaymentIntent on ZeroSettle’s backend.
Stripe sends a webhook to ZeroSettle
Stripe fires
payment_intent.succeeded (or checkout.session.completed for session-based flows). ZeroSettle receives this at its webhook endpoint with signature verification.ZeroSettle creates Transaction + Entitlement
The webhook handler creates an immutable
Transaction record (the payment ledger entry) and an Entitlement (the user’s access grant). For subscriptions, it also creates the Stripe subscription and tracks renewal state.SDK updates client-side
The SDK polls for transaction completion and updates the local entitlement cache. Your delegate callback fires with the updated entitlements.
Server-Side Verification
For server-side purchase verification (e.g., unlocking content from your API, validating access in a game server, or gating API features), query the Entitlements API.Query by User ID
Query by Email
For anonymous users who purchased without a user account:Response Format
Server-Side Verification Examples
All API endpoints authenticate with your publishable key (
zs_pk_live_... or zs_pk_test_...) via the X-ZeroSettle-Key header — the same key you use in the SDK. Use zs_pk_test_... for sandbox queries and zs_pk_live_... for production.Transaction History
For a full history of all transactions (including failed, refunded, and expired), use the transaction history endpoint:limit parameter defaults to 50 (max 100).
Client-Side Updates
The SDK provides real-time delegate callbacks when entitlements change. These fire after purchases, restores, refunds, and subscription renewals.Delegate Callbacks
Entitlement updates are triggered by SDK operations (purchases, restores). They are not pushed from the backend in real-time via WebSocket or push notification. For server-authoritative access control, use the Entitlements API on your backend.
Reactive UI Observation
You can also observe entitlements reactively in your UI framework:Stripe Webhook Events
ZeroSettle processes the following Stripe webhook events automatically. You do not need to handle these yourself.Payment Events
Payment Events
| Event | What ZeroSettle Does |
|---|---|
payment_intent.succeeded | Marks transaction as succeeded, creates Identity + Entitlement. For subscriptions, creates the Stripe subscription and handles plan upgrades (cancels old subscription with proration). |
payment_intent.processing | Moves transaction from pending to processing, freeing the deduplication slot so the user can retry if needed. |
payment_intent.payment_failed | Marks the pending transaction as failed. |
payment_intent.canceled | Marks the pending transaction as failed to free the dedup slot. |
Checkout Session Events
Checkout Session Events
| Event | What ZeroSettle Does |
|---|---|
checkout.session.completed | Routes to IAP checkout handler if payment_status=paid. Creates Transaction + Entitlement. |
checkout.session.expired | Marks pending transaction as failed when the checkout session expires without payment. |
Subscription Lifecycle Events
Subscription Lifecycle Events
| Event | What ZeroSettle Does |
|---|---|
customer.subscription.updated | Handles cancellation (cancel_at_period_end), pause/resume, and status changes. Entitlement stays active until period end on cancellation. |
customer.subscription.deleted | Deactivates the entitlement when the subscription is fully terminated. |
customer.subscription.paused | Marks entitlement as paused. Access is suspended until resumed. |
customer.subscription.resumed | Reactivates a paused entitlement. |
customer.subscription.trial_will_end | Logs upcoming trial expiration (~3 days before). Used for Switch & Save migrations with aligned trial periods. |
Invoice Events
Invoice Events
| Event | What ZeroSettle Does |
|---|---|
invoice.paid | Creates Transaction records for subscription renewals and proration charges. Extends entitlement expiry on renewal. Skips initial subscription_create invoices (handled by checkout flow). |
invoice.payment_failed | Logs renewal failure context. Entitlement status is handled by customer.subscription.updated (moves to past_due). |
invoice.voided | Handles voided invoices for paused subscriptions. |
Refund & Dispute Events
Refund & Dispute Events
| Event | What ZeroSettle Does |
|---|---|
charge.refunded | Marks transaction as refunded, deactivates all linked entitlements. |
charge.dispute.created | Marks transaction as disputed, revokes all linked entitlements. ZeroSettle handles evidence submission as MoR. |
charge.dispute.updated | Logs dispute status changes. |
charge.dispute.closed | Records dispute outcome (won/lost). |
charge.dispute.funds_withdrawn | Logs fund withdrawal. |
charge.dispute.funds_reinstated | Re-activates entitlements if dispute is won and funds are returned. |
Setup & Account Events
Setup & Account Events
| Event | What ZeroSettle Does |
|---|---|
setup_intent.succeeded | Confirms trial subscription card setup. Finalizes the trial transaction. |
setup_intent.setup_failed | Marks trial transaction as failed if card setup fails. |
account.application.deauthorized | Clears stale Stripe references when a connected account revokes platform access. |
BYOS (Bring Your Own Stripe)
If you use BYOS mode with your own Stripe account connected via OAuth, ZeroSettle still receives and processes all webhook events through the same endpoint. Stripe routes Connect webhooks to the platform automatically. You do not need to configure a separate webhook endpoint on your connected account.App Store Server Notifications
ZeroSettle also handles Apple’s App Store Server Notifications V2 (ASSN v2) for StoreKit subscription lifecycle events. If your app uses StoreKit purchases alongside web checkout, these notifications keep entitlements in sync automatically.Setting Up ASSN v2
Configure the webhook URL in App Store Connect:Open App Store Connect
Navigate to your app in App Store Connect.
Handled Apple Notifications
ZeroSettle processes the following ASSN v2 notification types:| Notification | What ZeroSettle Does |
|---|---|
REFUND | Deactivates entitlement, marks transaction as refunded |
REVOKE | Deactivates entitlement (family sharing revocation) |
DID_RENEW | Extends entitlement expiry to new period end. Applies pending product changes from plan switches. |
EXPIRED | Marks entitlement as expired and deactivates access |
DID_FAIL_TO_RENEW | Marks entitlement as grace period or past due depending on subtype |
DID_CHANGE_RENEWAL_STATUS | Handles auto-renew toggle (marks cancelled or reactivated) |
DID_CHANGE_RENEWAL_PREF | Records pending product change for next renewal |
GRACE_PERIOD_EXPIRED | Deactivates access after billing grace period ends |
RENEWAL_EXTENDED | Extends entitlement expiry when Apple grants an extension |
SUBSCRIBED | Creates transaction and entitlement for new subscriptions |
ONE_TIME_CHARGE | Creates transaction and entitlement for one-time purchases |
OFFER_REDEEMED | Records offer metadata on the entitlement |
REFUND_REVERSED | Re-activates entitlement after a refund reversal |
REFUND_DECLINED | Logged only (refund request was declined by Apple) |
All Apple notifications are verified using JWS signature verification against Apple’s Root CA certificate chain before processing. Duplicate notifications are automatically skipped via idempotency checks on the
notificationUUID.Notification Forwarding
If your app needs to receive ASSN v2 notifications on your own server (e.g., for analytics or custom logic), you can configure notification forwarding in the ZeroSettle dashboard. When enabled, ZeroSettle processes the notification first, then forwards the original signed payload to your URL.Best Practices
Use server-side verification for access control
Use server-side verification for access control
For content gating on your backend (API access, downloadable content, server-side features), always verify entitlements via the REST API rather than trusting client-side state. The SDK cache is convenient for UI but should not be the source of truth for authorization decisions.
Call restoreEntitlements() on app launch
Call restoreEntitlements() on app launch
The SDK’s entitlement cache is in-memory only and is cleared when the app process terminates. Call
restoreEntitlements() on every app launch to rehydrate the user’s entitlements from the server.Handle the empty entitlements case
Handle the empty entitlements case
Entitlements will be empty if no purchases have been made. Always handle this case gracefully in your UI and server-side logic.
Don't build your own Stripe webhook handler
Don't build your own Stripe webhook handler
ZeroSettle handles all Stripe webhook processing, including signature verification, idempotency, and retry logic. Building a parallel webhook handler will cause duplicate entitlement creation and state conflicts.

