Quickstart

Choose your CashSDK platform, connect the right billing provider and verify access. Native quickstart for iOS, macOS and Android.

Start with Choose your platform. For web, direct macOS with Stripe, Chrome extensions or standalone Stripe, follow the matching guide. Stripe-only integrations need no client SDK or native store setup.

The steps below cover the native iOS, Mac App Store and Android SDKs. CashSDK does not depend on any other merchant application: create your own workspace and app, connect your store accounts, and verify a Sandbox purchase. Store-account preparation and review can take longer than the SDK integration itself.

Before you start#

  • iOS: use your app's bundle ID, numeric App Store App ID and App Store Connect credentials. Configure both Sandbox and Production notification URLs from CashSDK.
  • macOS: a Mac build that shares the iOS bundle ID (universal purchase) only needs macOS ticked as a platform. A Mac app with its own App Store Connect record, including a Mac Catalyst build with the default maccatalyst. bundle ID, also needs its Mac bundle ID, Mac Apple App ID and the same notification URL. See macOS / Catalyst.
  • Android: use your app's package name and its authorized Play service account. Configure the RTDN topic CashSDK gives this app, not a topic from another project.
  • Both: select both platforms on one CashSDK app. Keep the iOS bundle ID and Android package name separate; map their store products to shared access tiers.

Use Quickstart and Store Connect in the dashboard to see the requirements for the platforms you selected. A real store test requires a suitable tester account and build; a passing local SDK unit test is not proof that store billing is connected.

For one-input-at-a-time store setup, use the dashboard walkthrough. It includes separate iOS and Android paths, screenshots, exact permission names, notification tests, and stable step links for coding agents. Back, Next and Generate Prompt remain at the bottom of the dashboard flow.

1. Get your API keys#

Every app has two kinds of keys:

KeyPrefixUsed by
Publishablecsk_pk_…Client SDKs (safe to ship)
Secretcsk_sk_test_… / csk_sk_live_…Backend REST API reads and authorized server operations

Find them under Developers → API keys in the dashboard, or read more in Authentication.

There is one publishable key and it works in every store environment, so use the same value in every build you ship, TestFlight included. Only your secret key comes in a test and a live version.

For agent-led management, use a scoped MCP token or the short-lived setup token from Generate prompt. Neither belongs in the mobile app.

2. Install the SDK#

swift
// In Xcode: File → Add Packages…
// https://github.com/cashsdk/cashsdk-ios
// Version: from 2.6.0 (up to next major).
import CashSDK
kotlin
// settings.gradle.kts
dependencyResolutionManagement {
  repositories {
    google()
    mavenCentral()
    // CashSDK 1.4.0 is served from GitHub until Maven Central carries it.
    maven("https://raw.githubusercontent.com/CashSDK/cashsdk-android/maven-1.4.0")
  }
}

// build.gradle.kts
dependencies {
  implementation("com.cashsdk:cashsdk-android:1.4.0")
  implementation("com.android.billingclient:billing-ktx:9.1.0")
}

Follow the complete iOS or Android build requirements. The versions above are the current releases: iOS 2.6.0 and Android 1.4.0. Android 1.4.0 is not on Maven Central yet, which is why the extra maven(...) repository is there; that line can be dropped once Maven Central serves 1.4.0.

3. Configure on launch#

Initialize CashSDK once, as early as possible, with your publishable key.

swift
import CashSDK

CashSDK.configure(publishableKey: "csk_pk_…")
kotlin
import com.cashsdk.CashSDK

// In Application.onCreate()
CashSDK.configure(context = this, publishableKey = "csk_pk_…")

Then identify the signed-in customer, on every launch:

Obtain a fresh signed user token from your authenticated backend. The user ID must match its signed subject. Keep the app's user-token signing secret on that backend; the publishable key cannot sign user tokens. Fetch a new token when the session changes or the old token expires. On iOS, set CashSDK.shared.userTokenProvider and the SDK asks your backend for one when it needs it; on Android, call identify again with the new token. Use opaque IDs rather than zero-padded numeric IDs.

swift
CashSDK.shared.identify(userId: "8841", userToken: tokenFromYourBackend)
kotlin
CashSDK.shared.identify(userId = "8841", userToken = tokenFromYourBackend)

4. Create a product#

Model a subscription in the dashboard under Catalog, then confirm it from the terminal with the CLI:

bash
cashsdk catalog          # products + entitlements
cashsdk catalog sync     # pull the latest from the store

Create an entitlement such as pro, then map each access product to it in Products. Map each Play base plan separately. Consumables credit balances and need no entitlement mapping. See Catalog for the exact create/update routes and the unreleased access-readiness protections.

5. Show a paywall and purchase#

Present your paywall and complete a purchase with a product identifier. CashSDK handles the native store interaction and verifies the transaction server-side.

If you render your own purchase screen, choose My app’s own paywall in Quickstart → Paywall or App settings → Paywall, then save. CashSDK still verifies purchases and access or credits; you do not need to publish an unused CashSDK campaign. This does not pause existing campaigns. You can switch back at any time. Agents can set the same choice with update_app_settings({ usesOwnPaywall: true }). This setting is part of the unreleased platform update; deploy its migration and API first.

swift
switch try await CashSDK.shared.purchase("pro_monthly") {
case .success(let entitlements):
  if entitlements.isActive("pro") { unlockPro() }
case .pending:      showPendingNotice()   // Ask-to-Buy / SCA
case .userCancelled: break
}
kotlin
// Play Billing launches its flow from an Activity.
val entitlements = CashSDK.shared.purchase(activity, "pro_monthly")

if (entitlements.isActive("pro")) {
  unlockPro()
}

Replace pro_monthly with the exact identifier of your product on that store. On Android, call from a coroutine and handle CashSDKError.PurchasePending and CashSDKError.PurchaseCancelled separately from recoverable errors. For consumables or lifetime products, pass com.cashsdk.model.PurchaseKind.PRODUCT instead of the subscription default.

Then gate features on the entitlement, a synchronous, offline-valid read:

swift
if CashSDK.shared.entitlements.isActive("pro") { unlockPro() }
kotlin
if (CashSDK.shared.entitlements.isActive("pro")) unlockPro()

If purchase throws, the transaction is deliberately left unfinished and the store will redeliver it. Never tell the customer they were not charged. Show a retry instead.

6. Check access from your backend (optional)#

The SDK already sends purchases for server verification. Your backend can independently read the resulting access with its own secret key:

bash
curl https://api.cashsdk.com/v1/customers/8841/entitlements \
  -H "Authorization: Bearer $CASHSDK_SECRET_KEY"

Use a test secret key for Sandbox and a live secret key for Production. Do not compare a Sandbox purchase with a Production read. Before shipping, verify purchase, restore, pending, cancellation and account switching on each selected platform, and confirm store notifications reach the same CashSDK app.

Next steps#