A Swift SDK that lets iOS developers offer web-based checkout as an alternative to In-App Purchase — keeping more revenue while staying fully compliant with App Store guidelines.
5% + 50¢ per transaction. Instant Stripe payouts. We handle tax, compliance, and liability as your Merchant of Record.
On a $9.99 sale through the App Store, you keep ~$6.99. With ZeroSettle, you keep ~$8.99.
ZeroSettle acts as your Merchant of Record — we're the legal seller, so we handle sales tax remittance, chargebacks, refunds, and regulatory compliance. You focus on building your app.
- Web checkout with Apple Pay — Stripe-powered payment sheet that feels native
- StoreKit 2 integration — offer both web and App Store pricing on the same paywall
- Entitlement management — unified subscription state across both purchase sources
- Server-controlled checkout — embedded sheet, in-app Safari, or external Safari
- Tax compliance — US sales tax, EU VAT, and AU GST handled automatically
- Promotional pricing — percent off, fixed amount, and free trial support
ZeroSettleKit ships two independent products you can import separately:
| Product | Import | Purpose |
|---|---|---|
| ZeroSettleKit | import ZeroSettleKit |
Merchant of Record web checkout for subscriptions and one-time purchases |
| ZeroSettleEscrow | import ZeroSettleEscrow |
Skill-based competitive gaming with on-chain Solana escrow |
This README covers ZeroSettleKit. For Escrow documentation, see docs.zerosettle.io.
Add ZeroSettleKit to your project in Xcode:
- File > Add Package Dependencies...
- Enter:
https://github.com/zerosettle/ZeroSettleKit - Add
ZeroSettleKitto your target
Or in Package.swift:
dependencies: [
.package(url: "https://github.com/zerosettle/ZeroSettleKit", from: "1.0.0")
],
targets: [
.target(
name: "YourApp",
dependencies: [
.product(name: "ZeroSettleKit", package: "ZeroSettleKit")
]
)
]Call configure early in your app lifecycle — typically in your App init or AppDelegate:
import ZeroSettleKit
ZeroSettle.shared.configure(.init(
publishableKey: "pk_live_your_key",
environment: .production,
syncStoreKitTransactions: true
))Add the universal link handler to your root view so checkout callbacks are processed:
@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
ContentView()
.zeroSettleHandler()
}
}
}Products are defined in the ZeroSettle Dashboard and include both web pricing and App Store pricing:
let products = try await ZeroSettle.shared.fetchProducts(userId: user.id)
for product in products {
print("\(product.displayName): \(product.webPrice.formatted)")
}Use the built-in payment sheet for an embedded checkout experience with Apple Pay:
.zsPaymentSheet(
isPresented: $showCheckout,
product: product,
userId: user.id
) { result in
switch result {
case .success(let transaction):
// Entitlements update automatically
unlockContent(transaction.productId)
case .failure(let error):
showError(error)
}
}Or trigger a Safari-based checkout directly:
try await ZeroSettle.shared.purchase(
productId: "pro_monthly",
userId: user.id
)Entitlements unify purchases from both web checkout and StoreKit into a single state:
let entitlements = try await ZeroSettle.shared.restoreEntitlements(userId: user.id)
let isPro = entitlements.contains { $0.productId == "pro_monthly" && $0.isActive }Call restoreEntitlements on every app launch to ensure cross-device sync.
The checkout experience is controlled server-side via your dashboard:
| Mode | Description |
|---|---|
| Embedded Sheet | Native bottom sheet with WKWebView — Apple Pay and card support |
| In-App Safari | SFSafariViewController within your app |
| External Safari | Opens the user's default browser, returns via universal link |
The SDK reads this configuration automatically — no client-side changes needed to switch modes.
ZeroSettleKit works with any authentication system:
| Approach | When to use | userId value |
|---|---|---|
| Custom Auth | You have your own auth system | Your stable user ID |
| RevenueCat | Migrating from RevenueCat | Purchases.shared.appUserID |
| Anonymous | No auth system | Empty string (email collected at checkout) |
Use a stable, non-email identifier. See User Identity for details.
Implement ZeroSettleDelegate to observe checkout and entitlement events:
extension AppState: ZeroSettleDelegate {
func zeroSettleCheckoutDidComplete(transaction: ZSTransaction) {
// Purchase succeeded — entitlements are already updated
}
func zeroSettleCheckoutDidCancel(productId: String) {
// User dismissed checkout
}
func zeroSettleCheckoutDidFail(productId: String, error: Error) {
// Handle error
}
func zeroSettleEntitlementsDidUpdate(_ entitlements: [Entitlement]) {
// Entitlement state changed
}
}Offer both web checkout (lower fees) and native StoreKit on the same paywall:
// Web checkout — 5% + 50¢
try await ZeroSettle.shared.purchase(
productId: "pro_monthly",
userId: user.id
)
// StoreKit fallback — standard App Store pricing
let transaction = try await ZeroSettle.shared.purchaseViaStoreKit(
productId: "pro_monthly",
userId: user.id
)Both paths feed into the same entitlement system. See StoreKit Integration for hybrid paywall patterns.
Web checkout callbacks require universal links. Add an Apple App Site Association file to your domain and configure your entitlements. See Universal Links Setup for the full walkthrough.
Note: Universal links do not work in the iOS Simulator — test on a physical device.
| Type | Description |
|---|---|
ZeroSettle |
Main SDK singleton — configure, fetch, purchase, restore |
Product |
A purchasable item with web and App Store pricing |
Entitlement |
An active access right with source tracking (.webCheckout or .storeKit) |
ZSTransaction |
Result of a completed purchase |
ZSPaymentSheet |
SwiftUI payment sheet component |
RemoteConfig |
Server-controlled checkout mode and migration campaigns |
Promotion |
Active promotional pricing on a product |
- iOS 17.0+
- Swift 5.9+
- Xcode 15.0+
- No third-party dependencies (for ZeroSettleKit)
- Call
restoreEntitlementson every app launch for cross-device sync - Validate entitlements server-side for sensitive features
- Provide a visible "Restore Purchases" button (App Store requirement)
- Use sandbox keys during development — switch to live keys for production
- Preload products on paywall screens for instant checkout
- Never trust client-side entitlements alone for critical access control
See Best Practices for the full guide.
Full guides, API reference, and integration walkthroughs are available at docs.zerosettle.io.
- Quickstart
- Payment Sheet
- User Identity
- Subscription State
- StoreKit Integration
- RevenueCat Integration
- Customer Portal
- Best Practices
MIT License — see LICENSE for details.