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)
- Request: The client calls your backend
/encode
withproduct
(FastSpring product ID). - Signed URL: The backend returns a checkout URL with
product
,securePayload
, andreturnUrl
. - Open: The client opens the URL (for example, Unity
Application.OpenURL
or an in-app web view). - Checkout and webhook: FastSpring completes checkout and posts to the
order.completed
webhook. - Grant: The backend grants the mapped entitlement in your economy service.
- 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.
Component | Recommended baseline | Notes |
---|---|---|
Unity | 2022.3 LTS or later | Targets .NET Standard 2.1. |
.NET profile | .NET Standard 2.1 | Required by Unity 2022 LTS. |
Web page | Modern browsers | Supports FastSpring Store Builder script. |
Backend | Public HTTPS runtime (e.g., Node.js 18+) | Public endpoints, secure secret storage, reachable by FastSpring webhooks. |
FastSpring | Account with Embedded Checkout | Access 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 FastSpringorder.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.
Area | Unity/UGS example | Alternatives |
---|---|---|
Client engine | Unity | Unreal, Godot, custom engine |
Player auth | Unity Authentication | Firebase Auth, PlayFab Auth, custom |
Economy/entitlements | UGS Economy | PlayFab Economy, custom |
Server runtime | AWS + Node.js | GCP Cloud Run, Azure Functions, any HTTPS stack |
Web checkout page | Static site with JS | Any 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
- Go to Catalog > One-Time Products.
- Create your product and set a Product Path, also known as the product ID (this will be your
product
value). - Configure pricing and any required tax settings.
- Save the product to your catalog.
Add the product to your embedded checkout
- Go to Checkouts > Embedded Checkouts.
- On the applicable checkout, click Products. The Homepage Products modal opens.
- In the Main Products field, type the product name or ID that you want to add. Repeat this step for each product.
- 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.
-
Go to Developer Tools > Store Builder Library.
-
Copy your Access Key (used later in
data-access-key
on the web page). -
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>"
-
Upload your
publiccert.pem
file using the File Upload section.Note: Keep
privatekey.pem
on your backend only (for creatingsecurePayload
andsecureKey
). Do not embed it in client or front-end code. -
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
- 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 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.
- Go to Developer Tools > Webhooks > Configuration. Click Add URL Endpoint on the webhook you wish to edit.
- 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.
- 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.
- In the Events section, select order.completed.
- 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 ID | Entitlement ID (economy) | Client productId |
---|---|---|
coinpack-150 | coinpack-150 | coinpack-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 withproduct
,securePayload
, andreturnUrl
. -
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.
Key | What it is | Source | Used by |
---|---|---|---|
UGS_PROJECT_ID | Unity project ID | Unity Dashboard | Webhook grant call |
UGS_ENV_ID | Unity environment ID | Unity Dashboard | Webhook grant call |
UGS_SERVICE_ACCOUNT_SECRET | Server-to-server token/secret | Unity services | Exchanging/authorizing server calls |
PEM_PRIVATE_KEY | FastSpring signing private key | Your 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.
Name | Lives in | Called by | When | Purpose |
---|---|---|---|---|
POST /encode | Your backend (API Gateway, Cloud Run, etc.) | Game client | When player initiates purchase | Verify player; sign FastSpring secure payload; return signed checkout URL (product ,securePayload , returnUrl ). |
order.completed webhook | Your backend (public HTTPS route) | FastSpring | After successful checkout | Receive event; validate; grant mapped product to player; return success. |
UGS VirtualPurchase (HTTP call) | Unity Cloud Code API | Webhook handler | Inside webhook grant step | Grant the entitlement in UGS using the mapped product ID (must equal the FastSpring product ID). |
AWS /encode
example (your backend)
/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 withproduct
,securePayload
, andreturnUrl
. - 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.
Parameter | Required | Description |
---|---|---|
product | Yes | Product identifier, for example coinpack-150 |
securePayload | Yes | Base64-encoded and URL-escaped encrypted payload |
secureKey | Yes | Base64-encoded and URL-escaped encryption key |
returnUrl | Yes | App 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 toreturnUrl
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.
Field | Purpose | Example |
---|---|---|
storeBaseUrl | Origin of your checkout web page (no trailing slash) | https://checkout.example.com |
returnUrl | App deep link to return after checkout (must be registered in the OS) | fastspring://shop-return |
purchaseCreatorEndpoint | Backend endpoint that returns a signed checkout URL | https://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
Actor | What it does |
---|---|
Client | Ensure the player is signed in, send product (FastSpring product ID) to /encode , open the returned URL, and handle the deep link on return. |
Backend | Verify 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-escapedsecurePayload
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.
Updated about 9 hours ago