React Native FastSpring integration (iOS & Android) with Steer Safe™

Build a secure subscription flow using FastSpring checkout, backend authentication, deep link returns, and Steer Safe browser steering.

Build a secure subscription flow in your React Native app using FastSpring checkout, deep link returns, and backend-verified subscription updates. This guide shows how users authenticate, start a purchase, return from checkout, and receive updated subscription access from your backend.

The sections below outline the full subscription flow, explain how FastSpring fits into your system architecture, and provide the configuration and implementation details needed to support in-app purchases.

How it works

This section provides a high-level view of how FastSpring, your backend, and your React Native app work together during the subscription lifecycle. Reviewing this flow first will help you understand how each stage of the implementation fits into the overall user experience.

sequenceDiagram
    participant User
    participant RNApp as React Native App
    participant Backend
    participant FastSpring
    participant SubDB as Subscription Store

    user->>RNApp: Open app
    RNApp->>Backend: POST /login (deviceId)
    Backend-->>RNApp: token, userId

    RNApp->>Backend: POST /getSubscription
    Backend-->>RNApp: subscription state

    user->>RNApp: Tap "Subscribe"
    RNApp->>Backend: POST /encode (subscriptionId)
    Backend-->>RNApp: securePayload, secureKey, returnUrl

    RNApp->>FastSpring: Open checkout in Safari/Chrome

    FastSpring->>Backend: subscription webhooks
    Backend->>SubDB: Update subscription status

    RNApp->>Backend: Poll for subscription updates
    Backend-->>RNApp: Updated subscription state
    RNApp->>User: Show premium content

Actor key

Each part of the system plays a distinct role in the purchase and subscription lifecycle.

React Native app

Handles user authentication, starts checkout, receives deep link returns, and polls subscription status through your backend.

Backend

Identifies the user, signs secure checkout requests, processes FastSpring webhook events, and stores subscription state.

FastSpring

Renders the checkout experience, processes billing, and emits subscription events to your backend.

Subscription store

Stores the authoritative subscription status returned to your app through /getSubscription.


Stage 1: Configure FastSpring

Set up your FastSpring store so it can generate secure payloads, render your subscription in checkout, and notify your backend when subscription events occur.

1. Create your subscription

Create a subscription plan that matches the product you want to sell in your app.

  1. Sign in to your FastSpring account, or create an account if you’re new.
  2. Go to Catalog > Subscription Plans.
  3. Create your subscription plan and set a Product Path (for example, bronze-monthly).
  4. Configure your pricing, billing interval, and any trial settings.
  5. Click Save and confirm the subscription appears in your catalog.
2. Add the subscription to your checkout

Attach your subscription to an Embedded Checkout so FastSpring can display it when the user opens checkout.

  1. Go to Checkouts > Embedded Checkouts.
  2. On the applicable checkout, click Products. The Homepage Products modal opens.
  3. In the Main Products field, add your subscription ID (for example, bronze-monthly).
  4. Click Save and confirm the product appears in your embedded checkout.
3. Generate and upload your keys

Before you can pass a secure payload to your store checkout, complete a one-time encryption setup in your FastSpring account.

  1. Go to Developer Tools > Store Builder Library.

  2. Copy your Access Key (used later in data-access-key on the web page).

  3. Generate a 2048-bit RSA key pair in your terminal:

    Private key (server only)

    openssl genrsa -out privatekey.pem 2048

    Note: Keep privatekey.pem on your backend only (for creating securePayload and secureKey). Do not embed it in client or front-end code.

    Public certificate (upload to FastSpring)

    Replace <your_store_name> with your FastSpring store name (for example, "/CN=fastspring").

    openssl req -new -x509 -key privatekey.pem -out publiccert.pem -days 3650 -subj "/CN=<your-store-name>"
  4. Upload your publiccert.pem file using the File Upload section.

  5. Click Save.

4. Configure your webhook

Set up a webhook so FastSpring can notify your backend when subscription events occur.

Create webhook

  1. Go to Developer Tools > Webhooks > Configuration.
  2. Click Add Webhook (top right).
  3. Enter an internal Title for the webhook.
  4. Select the Get webhooks from dropdown and choose whether you would like FastSpring to send you webhook events for Live Orders, Test Orders, or Live and Test Orders.
  5. Click Add.

Add a URL endpoint and select events

  1. Open your webhook and click Add URL Endpoint.
  2. Enter your backend URL where FastSpring should send JSON webhook data.
  3. (Optional) Add an HMAC SHA256 Secret if your backend validates signatures.
  4. Select subscription lifecycle events such as:
    • subscription.activated
    • subscription.charge.completed
    • subscription.charge.failed
    • subscription.canceled
    • subscription.deactivated
    • return.created
  5. Click Add.
5. Map your subscription IDs

Use the same subscription ID across your app, backend, and FastSpring to ensure the correct plan is purchased and validated.

FastSpring subscription IDBackend subscription IDClient value
bronze-monthlybronze-monthlybronze-monthly

Tip: Use exact, case-sensitive matches across all systems.


Stage 2: Connect your backend

Set up your backend so it can authenticate users, generate secure checkout URLs, process FastSpring subscription events, and return subscription status to your app.

Your backend acts as the source of truth for subscription access. The React Native app never interprets webhooks directly; it only reads the state returned by your API.

Backend responsibilities

Your backend should:

  1. Identify users using a stable identifier (for example, a device ID).
  2. Generate signed checkout URLs using your private RSA key.
  3. Process FastSpring webhook events via your preferred middleware (for example, AWS).
  4. Store the latest subscription state and return it to the app through API calls.

Webhook events to listen for

FastSpring may send:

  • order.completed
  • subscription.activated
  • subscription.charge.completed
  • subscription.charge.failed
  • subscription.canceled
  • subscription.deactivated
  • return.created

Your backend should update the subscription state accordingly.

Data your backend returns to the app

  • A signed checkout URL when the user initiates a subscription purchase
  • The latest subscription state whenever the app requests it

Stage 3: Connect your React Native app

Your React Native app communicates only with your backend. The app does not parse FastSpring webhook events directly. Instead, it authenticates the user, initiates checkout, receives the return deep link, and retrieves subscription access from your backend.

This section explains the client-side responsibilities and shows how the app exchanges data with your backend using the required endpoints.

3.1 Authenticate the user session

The first step in the subscription lifecycle is identifying the user. Your backend uses this identifier to associate purchases and maintain subscription access across app sessions.

Request

POST /login
Content-Type: application/json

{
  "deviceId": "unique-device-identifier"
}

Response

{
  "success": true,
  "login": {
    "token": "user-auth-token",
    "userId": "user_123456"
  }
}

What happens

  • The app generates or retrieves a persistent device identifier.
  • The device ID is sent to your backend.
  • The backend returns a token and userId.
  • The app stores these values and uses the token for all authenticated calls.

The token allows your backend to associate subscription activity with the correct user.

3.2 Retrieve subscription state

The React Native app checks the subscription state by calling your backend. This ensures that access always reflects the latest value processed by the webhook.

Request

POST /getSubscription
Content-Type: application/json 
Authorization: user-auth-token  

{
  "subscription": "bronze-monthly"
}

Response

{
  "success": true,
  "subscription": {
    "subscriptionId": "bronze-monthly",
    "state": "active"
  }
}

Subscription states

StateMeaningAccess granted?
activeSubscription is paid and currentYes
trialuser is within a trial periodYes
overdueTemporary access allowed during payment issuesYes
canceledSubscription canceled; may have remaining timeNo
deactivatedSubscription fully endedNo

What happens

  • The app asks for the user’s subscription status.
  • The backend returns the latest known state.
  • The UI updates (premium vs. offer screen) based on the state.

This endpoint is the primary way the app determines subscription access.

3.3 Purchase a subscription

When the user chooses to subscribe, the app requests a secure checkout URL from your backend. Your backend signs the request, returns the encoded values, and the app opens the FastSpring checkout in the device's browser.

Request

POST /encode
Content-Type: application/json
Authorization: user-auth-token

{
  "subscription": "bronze-monthly"
}

Response

{
  "success": true,
  "encode": {
    "securePayload": "base64-encoded-payload",
    "secureKey": "base64-encoded-key"
  }
}

Generated checkout URL

https://<your-fs-checkout-url>
  ?product=bronze-monthly
  &securePayload=<payload>
  &secureKey=<key>
  &returnUrl=fsreactapp://purchase-complete

What happens

  • The user taps Buy or Subscribe.
  • The app calls /encode with the subscription ID.
  • The backend generates a secure, signed payload.
  • The backend returns securePayload and secureKey.
  • The app builds the FastSpring checkout URL.
  • Safari (iOS) or Chrome (Android) opens with the checkout page.

This step initiates the purchase flow for the user.

3.4 Handle purchase completion

After completing a purchase, FastSpring redirects the user back to your app using the custom deep link scheme you configure. The app then verifies the purchase by retrieving the updated subscription state from your backend.

The following examples illustrate a typical deep link configuration. Implement these in the appropriate project files for your app.

Deep link example

fsreactapp://purchase-complete

iOS configuration example

<key>CFBundleURLTypes</key>
<array>
  <dict>
    <key>CFBundleURLSchemes</key>
    <array>
      <string>fsreactapp</string>
    </array>
  </dict>
</array>

Android configuration example

<intent-filter>
  <action android:name= "android.intent.action.VIEW"/>
  <category android:name= "android.intent.category.DEFAULT"/>
  <category android:name= "android.intent.category.BROWSABLE"/>
  <data android:scheme="fsreactapp"/>
</intent-filter>

What happens

  • The user completes checkout in Safari or Chrome.
  • FastSpring redirects to your custom deep link.
  • The browser dismisses automatically.
  • Your app receives the deep link event.
  • The app immediately calls /getSubscription.
  • The backend returns the updated subscription state.
  • The UI updates to show premium content.
  • (Optional) A success confirmation is shown to the user.

This keeps the app's access logic aligned with the backend's latest subscription state.

3.5 Poll for subscription updates

Use polling to keep your app in sync with the user’s subscription status, even when webhook delivery or deep linking is delayed.

Why polling is needed

After a user completes checkout, several processes must finish before the new subscription state is available:

  • FastSpring must send subscription lifecycle webhooks
  • Your backend must process those events and update its subscription store
  • The updated value must be available through /getSubscription

Because each of these steps may take a few seconds, the React Native app should poll for subscription updates on a recurring interval. Polling also ensures the app stays accurate in environments where deep linking may not fire consistently, such as certain iOS simulator configurations.

Recommended behavior

  • Poll every 30 seconds
  • Pause polling while checkout is open
  • Pause polling if the user is not authenticated
  • Resume polling when the app returns to the foreground
  • Unlock premium access when the backend returns:
    • active
    • trial
    • overdue

Polling works together with the deep link return flow, so the app can detect subscription changes as soon as your backend confirms them.


Stage 4: Test your setup

Use this section to confirm that authentication, checkout, subscription updates, and polling all work together.

4.1 Test the basic flow

Use this table to validate the end-to-end subscription purchase and activation flow.

StepExpected result
Launch the app
user authenticates automatically
Backend returns a valid token and userId
Check subscription
App calls /getSubscription
Backend returns a non-access state; offer screen is shown
Start purchase
App calls /encode
Backend returns securePayload and secureKey
Open checkout
App launches FastSpring checkout
Safari or Chrome loads your subscription page
Complete purchase
user finishes checkout
FastSpring sends subscription lifecycle webhooks to your backend
Return to app
FastSpring triggers your deep link
App resumes and calls /getSubscription
Confirm access
App refreshes state
Backend returns active, trial, or overdue; premium unlocks

4.2 Test subscription states

Verify that the app responds correctly to changes in subscription status. Update subscription state in FastSpring or through your internal tools, then confirm the app reflects the update when it calls /getSubscription.

StateExpected app behavior
activePremium content is unlocked
trialPremium content is unlocked during the trial period
overduePremium content remains unlocked during grace period
canceledPremium content is removed; offer screen appears
deactivatedPremium content is removed; offer screen appears

4.3 Validate polling behavior

Polling ensures the app stays up-to-date even when:

  • Webhooks are delayed
  • A deep link is not triggered
  • The subscription changes while the app is already running

Confirm that:

  • Polling runs every 30 seconds
  • Polling pauses while checkout is open
  • Polling resumes when the app returns to the foreground
  • UI updates when the backend returns an access-granted state (active, trial, overdue)