Unity/UGS and FastSpring checkout integration on iOS with Steer Safe™

Implement a secure FastSpring checkout flow in your iOS game using Unity/UGI and Steer Safe™.

Overview

Enable an in-game purchase flow where your iOS game opens a secure FastSpring checkout inside the Safari app (i.e., "steering"). FastSpring notifies your Unity Gaming Services (UGS) backend, which grants an entitlement in your economy service, and the player sees their updated in-game currency balance.

Engine compatibility: The Unity engine is used as an example throughout this guide. However, the pattern is engine-agnostic, and you can refer to this guide to help plan similar mobile game integrations. Replace the authentication, economy, and deep link APIs with your own. The redirect step and secure-payload flow stay the same.

How it works

Here’s the end-to-end checkout flow you’ll implement and the parts you’ll configure: FastSpring (product, embedded checkout, public certificate, webhook), your backend (/encode and the webhook handler), the web checkout page (initializes the secure session and redirects back), and the game client (opens the URL, handles the deep link, and polls the economy).

Runtime flow (in order)

  1. Request: The client calls your backend /encode with product (FastSpring product ID).
  2. Signed URL: The backend returns a checkout URL with product, securePayload, and returnUrl.
  3. Open: The client opens the URL (for example, Unity Application.OpenURL or an in-app web view).
  4. Checkout and webhook: FastSpring completes checkout and posts to the order.completed webhook.
  5. Grant: The backend grants the mapped entitlement in your economy service.
  6. Return and sync: The web page redirects to returnUrl; the client handles the deep link and polls the economy until the player's balance updates.

Prerequisites

Before you begin, verify that your environment meets the recommended baseline requirements. Additionally, ensure that each required service exists in an environment you control and that you have the necessary credentials and permissions to configure settings, deploy changes, and set up webhooks/keys.

Compatibility baseline

Use this table to verify the recommended versions and platform capabilities for this integration.

ComponentRecommended baselineNotes
Unity2022.3 LTS or laterTargets .NET Standard 2.1.
.NET profile.NET Standard 2.1Required by Unity 2022 LTS.
Web pageModern browsersSupports FastSpring Store Builder script.
BackendPublic HTTPS runtime (e.g., Node.js 18+)Public endpoints, secure secret storage, reachable by FastSpring webhooks.
FastSpringAccount with Embedded CheckoutAccess to Store Builder and webhooks.

Services checklist

Confirm each service exists in your environment and that you can sign in and change settings:

  • Player authentication: You can create/read tokens and configure client apps (e.g., Unity Authentication, Firebase Auth)
  • Economy/entitlement service: You can create entitlements and grant/read balances (e.g., UGS Economy, PlayFab Economy)
  • FastSpring product in embedded checkout: The product/ product ID you will sell exists and is included in the embedded checkout
  • Backend for signing + webhook: You can deploy /encode, set secrets, and receive FastSpring order.completed webhooks
  • Web checkout page: You can host a page that initializes the secure session and redirects back to the app
  • Cloud Code or equivalent (optional): You can run entitlement-grant logic off the critical path if desired
Engine and service mapping (for non-Unity stacks)

Use this mapping to translate the Unity examples to your stack.

AreaUnity/UGS exampleAlternatives
Client engineUnityUnreal, Godot, custom engine
Player authUnity AuthenticationFirebase Auth, PlayFab Auth, custom
Economy/entitlementsUGS EconomyPlayFab Economy, custom
Server runtimeAWS + Node.jsGCP Cloud Run, Azure Functions, any HTTPS stack
Web checkout pageStatic site with JSAny static host with client-side JS

Configure FastSpring

Sign in to your FastSpring account, or create an account if you’re new.

In the FastSpring app, complete the following setup: create a product, enable an embedded checkout, upload your public certificate for secure payloads, and configure the order.completed webhook to notify your backend. The sections below walk you through each step.

Create your product

  1. Go to Catalog > One-Time Products.
  2. Create your product and set a Product Path, also known as the product ID (this will be your product value).
  3. Configure pricing and any required tax settings.
  4. Save the product to your catalog.

Add the product to your embedded 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, type the product name or ID that you want to add. Repeat this step for each product.
  4. Click Save and confirm the product appears in your embedded checkout.

Generate a private key and upload its public certificate

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
    

    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.

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

  5. Click Save.

Configure your webhook

Set up a webhook so FastSpring can notify your backend when a checkout is completed. First, create the webhook record, then attach a URL endpoint and select the event you want to receive notifications for.

Create a 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 URL and event

After creating a webhook, follow the steps below to apply URLs and events to it. This enables you to send specific webhook events to multiple URLs and endpoints.

  1. Go to Developer Tools > Webhooks > Configuration. Click Add URL Endpoint on the webhook you wish to edit.
  2. In the URL field, enter the external URL or endpoint where you want FastSpring to post the JSON data. We recommend using HTTPS endpoints to encrypt data.
  3. In the HMAC SHA256 Secret field, optionally enter a secret phrase for additional security.

    Note: If you set an HMAC secret, verify the webhook signature on your backend before processing the request.

  4. In the Events section, select order.completed.
  5. Click Add.

Map your product

Use a single product ID (per product) across all systems, so your backend can grant the correct entitlement without lookups.

FastSpring product IDEntitlement ID (economy)Client productId
coinpack-150coinpack-150coinpack-150

Warning: An exact, case-sensitive match is required across all systems. Keep IDs stable (avoid renaming after launch). Any mismatch prevents the grant from being applied after purchase.

Build the backend (AWS Serverless TypeScript example)

Build the backend that signs FastSpring secure payloads and grants entitlements after checkout. The example uses AWS, the Serverless Framework, and Node.js/TypeScript. Any stack works if it exposes a POST /encode endpoint and handles the FastSpring order.completed webhook.

What you'll build

At a high level, the backend has two primary responsibilities: generating a signed checkout link that the client can open, and granting the player’s entitlement once FastSpring confirms payment.

  • POST /encode
    Purpose: Verify the player and return a signed checkout URL.
    Input: Authorization token and { "product": "<FastSpring product ID>" }.
    Output: URL with product, securePayload, and returnUrl.

  • Webhook handler (order.completed)
    Purpose: Receive FastSpring’s webhook and grant the mapped entitlement.
    Input: order.completed payload from FastSpring.
    Action: Grant the exact mapped product to the player, and process each webhook only once (ignore duplicates/retries).

Environment configuration

Use this table to collect the secrets/IDs you need, regardless of stack.

KeyWhat it isSourceUsed by
UGS_PROJECT_IDUnity project IDUnity DashboardWebhook grant call
UGS_ENV_IDUnity environment IDUnity DashboardWebhook grant call
UGS_SERVICE_ACCOUNT_SECRETServer-to-server token/secretUnity servicesExchanging/authorizing server calls
PEM_PRIVATE_KEYFastSpring signing private keyYour PKI/keygen/encode signing

Note: If you’re not using UGS, replace the UGS_* items with your economy provider’s equivalent credentials (for example, PlayFab title secret, custom service token).

If you’re using the Serverless Framework (this guide’s example sets provider: aws), add references to these values in your serverless.yml and load the actual secrets from your secrets manager via environment variables.

# serverless.yml (excerpt)
UGS_PROJECT_ID: "<YOUR_UGS_PROJECT_ID>"
UGS_ENV_ID: "<YOUR_UGS_ENV_ID>"
UGS_SERVICE_ACCOUNT_SECRET: "<YOUR_UGS_SERVICE_ACCOUNT_SECRET>"
PEM_PRIVATE_KEY: "<YOUR_PEM_PRIVATE_KEY>"

Note: PEM_PRIVATE_KEY is the value you need to generate and store in order to have a FastSpring secure payload.

Endpoints

Use this map to see where each endpoint lives, who calls it, and when it runs.

NameLives inCalled byWhenPurpose
POST /encodeYour backend (API Gateway, Cloud Run, etc.)Game clientWhen player initiates purchaseVerify player; sign FastSpring secure payload; return signed checkout URL (product,securePayload, returnUrl).
order.completed webhookYour backend (public HTTPS route)FastSpringAfter successful checkoutReceive event; validate; grant mapped product to player; return success.
UGS VirtualPurchase (HTTP call)Unity Cloud Code APIWebhook handlerInside webhook grant stepGrant the entitlement in UGS using the mapped product ID (must equal the FastSpring product ID).

AWS /encode example (your backend)

The game client calls this endpoint to obtain a signed checkout URL. The backend verifies the player and signs the FastSpring secure payload with the private key.

Inputs: Authorization header and JSON body { "product": "<FastSpring product ID>" }
Output: URL containing product, securePayload, and returnUrl.

curl https://xxxxxxxxx.execute-api.us-east-1.amazonaws.com/dev/encode \
  --request POST \
  --header 'Authorization: <player_access_token_or_exchange_token>' \
  --header 'Content-Type: application/json' \
  --data '{
    "product": "<your_product_ID>"
  }'

Economy grant (UGS VirtualPurchase or provider equivalent)

After you validate FastSpring's order.completed webhook, grant the purchase to the player using one of the following approaches:

  • UGS Virtual Purchase (example below): Call Unity Cloud Code with the mapped product ID (must equal the FastSpring product ID) and the player identity.
  • Your provider’s grant API (non-UGS): Call your economy service’s server-to-server grant endpoint with the same mapped product ID and player identity. Authenticate with your service token/secret and ensure you process each webhook only once (ignore retries/duplicates).
# UGS VirtualPurchase example (grant from your webhook handler)
curl https://cloud-code.services.api.unity.com/v1/projects/<YOUR_UGS_PROJECT_ID>/scripts/VirtualPurchase \
    --request POST \
    --header 'Authorization: <exchange_token>' \
    --header 'Content-Type: application/json' \
    --data '{
    "params": {
        "playerId": "<target_player_id>",
        "productId": "<purchased_item_id>"
    }
    }'

Where:

  • <YOUR_UGS_PROJECT_ID> is your Unity project ID.
  • <exchange_token> is obtained via Unity's token exchange API.
  • <target_player_id> is the player to grant currency/items.
  • <purchased_item_id> is the UGS Virtual Purchase ID and must match the FastSpring product ID (for example, coinpack-150).

Note: FastSpring may retry webhooks. Deduplicate by event ID or order reference and process each webhook only once.

Deployment

Deploy now so your /encode endpoint and webhook URL are publicly reachable (this is required to run the full flow).

What goes live

  • POST /encode (client → signed checkout URL)
  • order.completed webhook (FastSpring → your backend)

How to deploy

  • In UGS: Deploy the VirtualPurchase script in the Unity Cloud Dashboard.

  • In AWS: Deploy after configuring your AWS CLI and Serverless Framework:

    npm run deploy
    # or
    serverless deploy
    

Verify

  • Call /encode (see “Client request” curl) and confirm you receive a signed checkout URL with product, securePayload, and returnUrl.
  • Complete a test checkout and confirm your webhook endpoint receives order.completed.

Build the web checkout page

Build a small web page that runs in the browser, reads parameters from your backend’s signed URL, initializes a secure FastSpring session, and returns to the game via your deep link.

Expected URL parameters

Your signed URL must include these query parameters; the web page reads them on load.

ParameterRequiredDescription
productYesProduct identifier, for example coinpack-150
securePayloadYesBase64-encoded and URL-escaped encrypted payload
secureKeyYesBase64-encoded and URL-escaped encryption key
returnUrlYesApp URL/deep link to redirect to after checkout (for example, fastspring://shop-return)

Initialize the secure session

After parsing the query string, decode the secure values and initialize the FastSpring session.

const decodedPayload = decodeURIComponent(securePayload);
const decodedKey = decodeURIComponent(secureKey);

fastspring.builder.secure(decodedPayload, decodedKey);

Embed attributes (HTML)

In your HTML container, set these attributes from your FastSpring account.

<div
  data-storefront="https://<your-fs-checkout-url>"
  data-access-key="ACCESS_KEY">
</div>

Note: Set both attributes from your FastSpring configuration (mind the hyphen in data-access-key). After checkout success or cancellation, redirect to returnUrl so the client can resume and update the state.

Configure the client (Unity example)

Unity is shown for example, but the same client flow applies in any engine. Swap in your authentication, economy, and deep link APIs; the remaining steps are identical: request a signed checkout URL from your backend, open it, return via deep link, and poll the economy until the balance updates. The client never signs or encrypts; the backend returns securePayload and secureKey, and the web checkout page initializes the secure session.

Update client config (Unity example)

In Unity, set these fields in the StoreConfig ScriptableObject. If you are not using Unity, create an equivalent client configuration (for example, a class or JSON settings file) and include the same three values. Field names can differ as long as your code reads the correct values and passes them to the appropriate calls.

FieldPurposeExample
storeBaseUrlOrigin of your checkout web page (no trailing slash)https://checkout.example.com
returnUrlApp deep link to return after checkout (must be registered in the OS)fastspring://shop-return
purchaseCreatorEndpointBackend endpoint that returns a signed checkout URLhttps://api.example.com/encode

Handle the deep link and poll for balance

Deep links are hyperlinks that take a user to a specific location within an application rather than a website. Visit Unity's Deep linking documentation to learn more.

After checkout, your web page should redirect to the deep link, for example:

fastspring://shop-return?amount=150

On return, sign in if needed, poll your economy until the balance updates, and then display a confirmation message to the player.

Tip: Polling is needed because the webhook path (FastSpring → backend → economy) can take a few seconds. There’s no real-time push from the economy service to the client.

Note: If you also pass amount in the deep link, treat it as display-only. Use a read from your economy service as the source of truth for the player’s balance.

Economy integration

We use Unity Gaming Services (UGS) Economy in this example. Any provider works as long as it supports player authentication, granting the mapped product, and reading the player’s balance for polling.

Set up economy (UGS example)

  • Enable Authentication and Economy in your Unity dashboard.
  • Use the COINS currency (or update your code to your currency ID).
  • Keep the product → amount mapping on the backend; do not trust amounts from the client.

Show a thank you message after the balance updates

Note: The example below shows a Unity helper method. If you use another engine, display an in-app notification or dialog after you read the updated balance from your economy service.

EconomyManager.Instance.ShowThanksYouPopup(delta); // delta is the granted coin amount

Request and open a signed checkout

Use this engine-agnostic sequence to start a secure FastSpring checkout. The client posts product (FastSpring product ID) to your backend at POST /encode. The backend creates securePayload and secureKey (per FastSpring secure payloads) and returns a signed checkout URL. The client opens that URL to launch checkout, then handles the deep link to return to the app.

Responsibilities

ActorWhat it does
ClientEnsure the player is signed in, send product (FastSpring product ID) to /encode, open the returned URL, and handle the deep link on return.
BackendVerify player identity, create securePayload and secureKey (per FastSpring secure payloads), return a signed checkout URL, handle the order.completed webhook, grant the entitlement.

Client request (placeholders)

POST https://api.example.com/encode
Authorization: <player_access_token_or_exchange_token>
Content-Type: application/json

{ "product": "<your_product_id>" }

Backend response (open this URL) (placeholders)

Use angle-bracket placeholders and replace them with your real values.

https://<your-fs-checkout-url>?
product=<your_product_id>&
securePayload=<your_securePayload>&
returnUrl=<your_deep_link>

Placeholder legend

  • <your-fs-checkout-url>: your FastSpring store embedded checkout URL.
  • <your_product_id>: the mapped FastSpring product ID (for example, coinpack-150).
  • <your_securePayload>: URL-escaped securePayload returned by /encode.
  • <your_deep_link>: your return URL (for example, fastspring://shop-return).

Open the URL: Use your platform’s system browser method (for example, Unity Application.OpenURL) or an in-app web view to launch checkout.

Note: The private key lives on the backend. The client never signs or stores it.