Use ZeroSettle alongside RevenueCat for web checkout
ZeroSettle works alongside RevenueCat. You can use RevenueCat for StoreKit and Play Billing subscription management while routing eligible purchases through ZeroSettle’s web checkout for lower fees.There are two integration approaches:
When using RevenueCat, disable ZeroSettle’s StoreKit/Play Billing listener to avoid conflicts:
Copy
import ZeroSettleKitimport RevenueCat@mainstruct YourApp: App { init() { // Configure RevenueCat first Purchases.configure(withAPIKey: "your_revenuecat_key") // Configure ZeroSettle with StoreKit sync disabled ZeroSettle.shared.configure(.init( publishableKey: "your_zerosettle_key", syncStoreKitTransactions: false // RevenueCat manages StoreKit )) } var body: some Scene { WindowGroup { ContentView() .zeroSettleHandler() } }}
Always set syncStoreKitTransactions: false (iOS) or syncPlayStoreTransactions: false (Android) when using RevenueCat. Both SDKs listening for store transactions will cause conflicts.
Pass RevenueCat’s appUserID as the userId when making ZeroSettle purchases. This links web checkout purchases to the correct RevenueCat customer:
Copy
let rcUserId = Purchases.shared.appUserID// Fetch ZeroSettle product cataloglet catalog = try await ZeroSettle.shared.fetchProducts(userId: rcUserId)// Purchase via web checkout using .checkoutSheet() in your SwiftUI view:// .checkoutSheet(item: $selectedProduct, userId: rcUserId) { result in ... }
Show both ZeroSettle (web checkout) and RevenueCat (StoreKit/Play Billing) purchase options:
Copy
struct PaywallView: View { @State private var selectedProduct: ZSProduct? let zsProduct: ZSProduct // From ZeroSettleKit let rcPackage: Package // From RevenueCat var body: some View { VStack(spacing: 16) { // Web checkout — lower fees Button("Subscribe — \(zsProduct.webPrice.formatted)") { selectedProduct = zsProduct } .checkoutSheet( item: $selectedProduct, 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) } }}
@mainstruct 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.