Skip to main content
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: Use the ZeroSettleKit SDK alongside RevenueCat in your app. This gives you full control over the purchase flow.

Configuration

When using RevenueCat, disable ZeroSettle’s StoreKit/Play Billing listener to avoid conflicts:
import ZeroSettleKit
import RevenueCat

@main
struct 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.

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 product catalog
let 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 ... }

Hybrid Paywall

Show both ZeroSettle (web checkout) and RevenueCat (StoreKit/Play Billing) purchase options:
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)
        }
    }
}

Option 2: RevenueCat Paywall Custom Button

If you use RevenueCat’s paywall builder, you can add a custom checkout button that links to ZeroSettle’s hosted checkout:
  1. In your RevenueCat paywall template, add a purchase button with custom checkout
  2. Set the URL to your ZeroSettle checkout link
  3. 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.