S Subster

Quick start

You will connect the library or your own server and get our first answer about a customer's access — from half an hour to a day, depending on whether you take our library.

Hand the task to an AI assistant

Copy the prompt, keep your platform in it and fill in the project ID.

I am integrating Subster into a mobile app. Subster takes subscription payments
on a web page, outside the app store, and opens the paid access inside the app.

Platform: iOS / Android / server-side code in <language>
API address: https://api.subster.ai
Project ID: <PROJECT_ID>

What the app has to do:
1. get the buyer id (guid) from the link that returns the user to the app
   after payment;
2. ask Subster for that buyer's entitlement;
3. open the paid flow if at least one of the returned grants has
   status "active".

Take every endpoint and field name from the Subster documentation: its MCP
server is https://api.subster.ai/mcp, or use the pages I attach. If a field or
an endpoint is not there, ask me — do not invent names or addresses.

How to connect the MCP server to the assistant — “MCP server”.

Before you start

The values are in the cabinet: Project settings → App connection → For developers.

If the cabinet opened in the wrong language, switch it in “Personal settings” → “Personalization”: “Account and security”.

A snippet for your platform

Copy the code from this page: the example on the “For developers” tab in the cabinet is outdated for now and does not build.

iOS

In Xcode: File → Add Package Dependencies, address https://github.com/web2web-dev/web2app-ios-sdk.git, version 0.8.1, product Web2AppSDK. iOS 14 or later is required.

import Web2AppSDK

// once at launch — baseUrl is a URL, not a String
Web2App.configure(projectId: "<PROJECT_ID>", baseUrl: URL(string: "https://api.subster.ai")!)

// identify the buyer: pass the one-time code from the return link,
// or nil on first launch without a link — then the library tries the device fingerprint
Web2App.identify(deepLinkValue: code) { result in
    // .success(guid); .failure(.needsEmailFallback) — nothing matched:
    // ask for an email and call Web2App.requestEmailRecovery(email) { _ in }
}

// check access
Web2App.entitlement { grant in
    let isPaid = grant?.isActive ?? false
}

Android

Android 7.0 or later is required. The library comes from JitPack:

// settings.gradle.kts
dependencyResolutionManagement {
    repositories {
        google()
        mavenCentral()
        maven { url = uri("https://jitpack.io") }
    }
}

// build.gradle.kts of the app module
dependencies {
    implementation("com.github.web2web-dev:web2app-android-sdk:0.7.2")
}
import app.web2app.sdk.Web2AppSdk

// once at launch — needs a context, baseUrl is a String
Web2AppSdk.configure(context, "<PROJECT_ID>", "https://api.subster.ai")

// first launch after install from Google Play: the library reads Install Referrer itself
Web2AppSdk.identify(
    onResult = { result -> result.onSuccess { guid -> /* buyer identified */ } },
    onNeedEmail = { /* nothing matched: ask for an email, then Web2AppSdk.requestEmailRecovery(email) { } */ },
)

// check access
Web2AppSdk.entitlement { grant -> val isPaid = grant?.isActive == true }

Without a code from a link, the library recognises the customer itself: on iOS by the device fingerprint, on Android by the data Google Play passes to the app at install (Install Referrer). How it works — “How we recognise the customer”.

If the app was opened by a return link, its code is passed through another method — “Return to the app”, step 3.

The library answers by the newest grant and ignores the rest. If a customer can have more than one purchase — say, a subscription and a one-time add-on — decide by the whole list of grants (grants): ready-made code in “Recipes”, section “Check access from the app without the library”.

Your own server

No library needed: the app exchanges the code from the return link for a buyer identifier, and your server requests the entitlement with a key.

# 1. in the app: exchange the one-time code from the return link for a buyer id
curl "https://api.subster.ai/public/handoff/resolve?code=<CODE>"
# { "success": true, "data": { "guid": "abc123def456", "projectId": "…" } }

# 2. on your server: check that buyer's access
curl "https://api.subster.ai/s2s/v1/entitlement?guid=abc123def456" \
  -H "Authorization: Bearer sk_live_…"
# { "guid": "abc123def456", "testMode": false, "grants": [{ "level": "premium", "status": "active", … }] }

Exchange the code from the device, not from the server: the exchange accepts ten requests a minute from one IP address, and the server will hit that limit as soon as customers start coming in a stream.

The first access answer

  1. Open https://api.subster.ai/public/entitlement?guid=abc123def456 in a browser — any identifier of 8 to 64 characters will do.
  2. If you got { "guid": "abc123def456", "testMode": false, "grants": [] }, the address is right and this customer has no purchases.
  3. Test mode or a test card will show an active grant before a real payment — “Troubleshooting”, section “Check without waiting for a real payment”.

The whole path

  1. Payment. The customer pays on the paywall, outside the app store. From an ad they reach it through your funnel, nothing is needed from you; from the app, by a link your server gets with a POST /s2s/v1/users request: “Return to the app”, section “Selling with a button inside the app”.
  2. Return. The post-payment screen and the email lead the customer to the app with a return link carrying a one-time code, and if the app is not installed, to the store first: “Return to the app”.
  3. Identification. The app exchanges the code for a buyer identifier, and after install recognises the customer in other ways: “How we recognise the customer”.
  4. Access check. Access is open if at least one grant has the status active: “Check the entitlement”.
  5. The rest of the subscription. Your server learns about renewals, cancellations and refunds from our events, the app by requesting the entitlement again: “Webhooks”.

Next