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
Understand how the app, backend, and FastSpring coordinate the subscription flow
Configure FastSpring
Set up your subscription product, checkout, keys, and webhooks
Connect your backend
Handle user identification, signing requests, and subscription updates
Connect your React Native app
Implement authentication, checkout launch, deep links, and polling
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.
- Sign in to your FastSpring account, or create an account if you’re new.
- Go to Catalog > Subscription Plans.
- Create your subscription plan and set a Product Path (for example,
bronze-monthly). - Configure your pricing, billing interval, and any trial settings.
- 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.
- Go to Checkouts > Embedded Checkouts.
- On the applicable checkout, click Products. The Homepage Products modal opens.
- In the Main Products field, add your subscription ID (for example,
bronze-monthly). - 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.
-
Go to Developer Tools > Store Builder Library.
-
Copy your Access Key (used later in
data-access-keyon the web page). -
Generate a 2048-bit RSA key pair in your terminal:
Private key (server only)openssl genrsa -out privatekey.pem 2048Note: 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>" -
Upload your
publiccert.pemfile using the File Upload section. -
Click Save.
4. Configure your webhook
Set up a webhook so FastSpring can notify your backend when subscription events occur.
Create webhook
- Go to Developer Tools > Webhooks > Configuration.
- Click Add Webhook (top right).
- Enter an internal Title for the webhook.
- 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.
- Click Add.
Add a URL endpoint and select events
- Open your webhook and click Add URL Endpoint.
- Enter your backend URL where FastSpring should send JSON webhook data.
- (Optional) Add an HMAC SHA256 Secret if your backend validates signatures.
- Select subscription lifecycle events such as:
subscription.activatedsubscription.charge.completedsubscription.charge.failedsubscription.canceledsubscription.deactivatedreturn.created
- 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 ID | Backend subscription ID | Client value |
|---|---|---|
bronze-monthly | bronze-monthly | bronze-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:
- Identify users using a stable identifier (for example, a device ID).
- Generate signed checkout URLs using your private RSA key.
- Process FastSpring webhook events via your preferred middleware (for example, AWS).
- Store the latest subscription state and return it to the app through API calls.
Webhook events to listen for
FastSpring may send:
order.completedsubscription.activatedsubscription.charge.completedsubscription.charge.failedsubscription.canceledsubscription.deactivatedreturn.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
tokenanduserId. - 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
| State | Meaning | Access granted? |
|---|---|---|
| active | Subscription is paid and current | Yes |
| trial | user is within a trial period | Yes |
| overdue | Temporary access allowed during payment issues | Yes |
| canceled | Subscription canceled; may have remaining time | No |
| deactivated | Subscription fully ended | No |
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-completeWhat happens
- The user taps Buy or Subscribe.
- The app calls
/encodewith the subscription ID. - The backend generates a secure, signed payload.
- The backend returns
securePayloadandsecureKey. - 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-completeiOS 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:
activetrialoverdue
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.
| Step | Expected 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.
| State | Expected app behavior |
|---|---|
active | Premium content is unlocked |
trial | Premium content is unlocked during the trial period |
overdue | Premium content remains unlocked during grace period |
canceled | Premium content is removed; offer screen appears |
deactivated | Premium 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)
Updated about 12 hours ago