ZeroSettle works alongside RevenueCat. You can use RevenueCat for StoreKit subscription management while routing eligible purchases through ZeroSettle’s web checkout for lower fees.
There are two integration approaches:
Option 1: SDK Integration (Recommended)
Use the ZeroSettleIAP SDK alongside RevenueCat in your app. This gives you full control over the purchase flow.
Configuration
When using RevenueCat, disable ZeroSettle’s StoreKit listener to avoid conflicts:
import ZeroSettleIAP
import RevenueCat
@main
struct YourApp: App {
init() {
// Configure RevenueCat first
Purchases.configure(withAPIKey: "your_revenuecat_key")
// Configure ZeroSettle with StoreKit sync disabled
ZeroSettleIAP.shared.configure(.init(
publishableKey: "your_zerosettle_key",
syncStoreKitTransactions: false // RevenueCat manages StoreKit
))
}
var body: some Scene {
WindowGroup {
ContentView()
.zeroSettleIAPHandler()
}
}
}
Always set syncStoreKitTransactions: false when using RevenueCat. Both SDKs listening for StoreKit transactions will cause conflicts.
User Identity
Pass RevenueCat’s appUserID as the userId when making ZeroSettle purchases. This links web checkout purchases to the correct RevenueCat customer:
let rcUserId = Purchases.shared.appUserID
// Fetch ZeroSettle products
let products = try await ZeroSettleIAP.shared.fetchProducts(userId: rcUserId)
// Purchase via web checkout
try await ZeroSettleIAP.shared.purchase(
productId: product.id,
userId: rcUserId
)
Hybrid Paywall
Show both ZeroSettle (web checkout) and RevenueCat (StoreKit) purchase options:
struct PaywallView: View {
@State private var showPaymentSheet = false
let zsProduct: ZSProduct // From ZeroSettleIAP
let rcPackage: Package // From RevenueCat
var body: some View {
VStack(spacing: 16) {
// Web checkout — lower fees
Button("Subscribe — \(zsProduct.webPrice.formatted)") {
showPaymentSheet = true
}
.zsPaymentSheet(
isPresented: $showPaymentSheet,
product: zsProduct,
userId: Purchases.shared.appUserID
) { result in
if case .success = result {
// Refresh RC customer info to pick up the webhook
try? await Purchases.shared.customerInfo()
}
}
// StoreKit fallback via RevenueCat
Button("Subscribe via App Store — \(rcPackage.localizedPriceString)") {
Task {
try? await Purchases.shared.purchase(package: rcPackage)
}
}
.foregroundStyle(.secondary)
}
}
}
Webhook Sync
When a user purchases via ZeroSettle web checkout, you need to sync the entitlement to RevenueCat. Configure a webhook in your ZeroSettle dashboard that posts to RevenueCat’s REST API:
- Go to your ZeroSettle dashboard
- Add a webhook endpoint pointing to your backend
- Your backend receives the purchase event and calls RevenueCat’s Grant Entitlement API
This ensures RevenueCat’s CustomerInfo stays in sync with web checkout purchases.
If you use RevenueCat’s paywall builder, you can add a custom checkout button that links to ZeroSettle’s hosted checkout:
- In your RevenueCat paywall template, add a purchase button with custom checkout
- Set the URL to your ZeroSettle checkout link
- Configure a URL scheme in your app to handle the callback
Handle the Callback
@main
struct YourApp: App {
var body: some Scene {
WindowGroup {
ContentView()
.onOpenURL { url in
if url.scheme == "yourapp" {
let status = url.lastPathComponent
switch status {
case "success":
// Refresh RevenueCat customer info
Task {
try? await Purchases.shared.customerInfo()
}
case "cancelled":
break
default:
break
}
}
}
}
}
}
The SDK integration (Option 1) provides a better user experience since the payment sheet stays inside your app. The custom button approach is useful if you want to use RevenueCat’s paywall builder without writing custom UI code.