How It Works
- User initiates purchase via
.checkoutSheet()(iOS) or the platform-specific purchase API - SDK opens Stripe checkout in Safari
- User completes payment (or cancels)
- Stripe redirects to
https://api.zerosettle.io/checkout/callback?... - iOS opens your app via universal link
- SDK parses the callback and updates entitlements
Step 1: Add Associated Domains Entitlement
In Xcode, add the Associated Domains capability to your app target:- Select your app target in Xcode
- Go to Signing & Capabilities
- Click + Capability
- Select Associated Domains
- Add:
applinks:api.zerosettle.io
Developer Mode
During development, add?mode=developer to bypass Apple’s AASA caching:
| Mode | Behavior |
|---|---|
| Normal | iOS caches the AASA file and refreshes infrequently. Changes can take hours to propagate. |
| Developer | iOS fetches the AASA file directly on each app install, bypassing CDN caching. |
Use
?mode=developer during development for faster iteration. Remove it before submitting to the App Store—developer mode only works on devices registered in your Apple Developer account.Step 2: Register Your App ID
Contact ZeroSettle support to register your app’s bundle ID and team ID for universal link handling. You’ll need to provide:| Field | Example | Where to Find |
|---|---|---|
| Team ID | YUM5K9J52M | Apple Developer Portal → Membership |
| Bundle ID | com.yourcompany.yourapp | Xcode → Target → General |
apple-app-site-association file hosted at https://api.zerosettle.io/.well-known/apple-app-site-association.
Step 3: Handle the Callback
SwiftUI
Use the.zeroSettleHandler() view modifier on your root view (or handle deep links in your Android Activity):
onOpenURL and onContinueUserActivity for you on iOS. On Android, handleDeepLink parses the callback URI, and onResume lets the SDK poll for transaction status when the user returns from the browser.
UIKit (SceneDelegate)
UIKit (AppDelegate - iOS 12 and earlier)
Step 4: Handle Checkout Results
Use the delegate to respond to checkout completion:Callback URL Format
The callback URL contains these parameters:| Parameter | Description |
|---|---|
transaction_id | Unique transaction identifier |
product_id | The product that was purchased |
status | success or cancelled |
You don’t need to parse this URL yourself. The SDK’s
handleUniversalLink(_:) method handles parsing and verification.Testing Universal Links
Simulator Limitations
Universal links don’t work reliably in the iOS Simulator. Test on a physical device.Testing Checklist
- Install a development build on a physical device
- Ensure the device has internet access
- Start a purchase flow
- Complete checkout in Safari
- Verify you’re redirected back to the app
Debugging Tips
If the redirect isn’t working:- Check Associated Domains - Verify the entitlement is correctly added in Xcode
- Verify Registration - Confirm your app ID is registered with ZeroSettle
- Check AASA - Visit
https://api.zerosettle.io/.well-known/apple-app-site-associationand verify your app ID is listed - Reinstall the App - iOS caches AASA; reinstalling forces a refresh
- Check Console Logs - Look for
swcdprocess logs in Console.app
Manual Testing
You can test the link handler directly:Fallback Handling
If the universal link fails to open your app, users will see the callback page in Safari. This page displays a success/cancel message and provides a button to manually open the app. To handle this case, you can also:- Poll for transaction status - The SDK stores a pending transaction ID that can be verified on next app launch
- Restore entitlements - Call
restoreEntitlements(userId:)on app launch to sync any missed purchases
Android Deep Links
On Android, checkout callbacks arrive via Android App Links (verified deep links). This section covers the equivalent setup for Android apps.Deep link handling is only needed for Custom Tab and external browser checkout modes. The WebView mode (default) handles callbacks internally — no deep link setup required.
Step 1: Add Intent Filter
In yourAndroidManifest.xml, add a deep link intent filter to your main Activity:
android:autoVerify="true" attribute tells Android to verify the domain via a Digital Asset Links file hosted on api.zerosettle.io.
Step 2: Handle the Callback
In your Activity, handle the deep link inonNewIntent:
Step 3: Call onResume
On Android, Custom Tabs have no dismiss callback, so the SDK detects the user’s return viaonResume. Call ZeroSettle.onResume() from your Activity:
Troubleshooting
Universal link opens Safari instead of my app
Universal link opens Safari instead of my app
- Reinstall the app to refresh the AASA cache
- Verify your bundle ID and team ID are registered with ZeroSettle
- Check that Associated Domains is enabled in your provisioning profile
handleUniversalLink returns false
handleUniversalLink returns false
- The URL may not be a ZeroSettle callback URL
- Check that the URL host is
api.zerosettle.io - Verify the path starts with
/checkout/callback
Delegate methods not called
Delegate methods not called
- Ensure
ZeroSettle.shared.delegateis set before the callback - Verify the SDK is configured with
configure() - Check that
handleUniversalLinkis being called fromonOpenURLor scene delegate

