How It Works
- User initiates purchase with
ZeroSettleIAP.shared.purchase() - 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. We need:| 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.zeroSettleIAPHandler() view modifier on your root view:
onOpenURL and onContinueUserActivity for you.
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 universal 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
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
ZeroSettleIAP.shared.delegateis set before the callback - Verify the SDK is configured with
configure() - Check that
handleUniversalLinkis being called fromonOpenURLor scene delegate
