Unity/UGS and FastSpring checkout integration (iOS & Android) with Steer Safe™
Build a secure in-game purchase flow using FastSpring, Unity UGS, and deep link steering to handle checkout, webhooks, and post-purchase updates.
Enable a secure, cross-platform in-game purchase flow where your mobile game (iOS or Android) launches a FastSpring checkout in a browser or in-app web context. After purchase, FastSpring notifies your backend via webhook, your backend grants the corresponding entitlement in your economy service, and the player’s balance updates inside the game.
Engine compatibility: The Unity engine is used in examples, but this pattern is engine-agnostic. Replace the authentication, economy, and deep link APIs with equivalents in your stack. The secure payload and redirect flow remain the same.
How it works
The diagram below shows the complete end-to-end flow of a FastSpring checkout from the player's perspective. It outlines which system performs each step and how data moves between the client, backend, FastSpring, and your economy service.
sequenceDiagram
participant Player
participant Client
participant Backend
participant FastSpring
participant Economy
Player->>Client: Initiate purchase
Client->>Backend: POST /encode (product)
Backend-->>Client: Signed checkout URL (securePayload + secureKey)
Client->>FastSpring: Opens checkout
FastSpring->>Backend: order.completed webhook
Backend->>Economy: Grant entitlement
Client->>Economy: Poll for updated balance
Actor key
Each part of the system plays a specific role in this process. Use this table as a quick reference while following the setup stages below.
Client
Game client (Unity or another engine) that requests the checkout URL and handles deep links.
Backend
Your secure API service that signs payloads and handles FastSpring webhook events.
FastSpring
Processes the checkout, payments, and sends the order.completed webhook.
Economy
Your entitlement or currency system (UGS, PlayFab, or custom) that grants items after purchase.
Stage 1: Environment setup
Confirm your environment and services can communicate with FastSpring before you start configuring products or code.
Compatibility baseline
Recommended versions and capabilities for each component:
| Component | Recommended baseline | Notes |
|---|---|---|
| Unity | 2022.3 LTS or later | Targets .NET Standard 2.1 |
| .NET profile | .NET Standard 2.1 | Required for Unity 2022 LTS |
| Web page | Modern browsers | Supports FastSpring Store Builder |
| Backend | Public HTTPS runtime (e.g., Node.js 18+) | Must be reachable by FastSpring webhooks |
| FastSpring | Account with Embedded Checkout | Required for Store Builder and webhooks |
Note: Android integrations using Chrome Custom Tabs require
androidx.browser:browser:1.5.0.
Services checklist
Confirm each of the following is available and configured:
- Player authentication (Unity Authentication, Firebase Auth, or equivalent)
- Economy or entitlement service (UGS Economy, PlayFab, or custom)
- FastSpring product added to Embedded Checkout
- Backend that can sign payloads and receive webhooks
- Hosted web page that initializes FastSpring checkout
- Optional: Cloud function (e.g., Unity Cloud Code) for async grant handling
Stage 2: Configure FastSpring
Configure your FastSpring store so it can generate secure payloads, run checkout, and send order webhooks to your backend.
1. Create your product
- Sign in to your FastSpring account, or create an account if you’re new.
- Go to Catalog > One-Time Products.
- Create your product and set a Product Path, also known as the product ID (this will be your
productvalue). - Configure pricing and any required tax settings.
- Click Save and confirm the product appears in your catalog.
2. 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.
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 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 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.
5. Map your product IDs
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.
Stage 3: Build the backend
Implement a backend that signs secure payloads, returns signed checkout URLs, and grants entitlements on order.completed. Examples use AWS, Serverless Framework, and Node.js/TypeScript, but any stack works if it exposes POST /encode and handles webhooks.
What you'll build
-
POST /encodeVerifies the player and returns a signed checkout URL.- Input:
Authorizationtoken and{ "product": "<FastSpring product ID>" } - Output: URL with
product,securePayload, andreturnUrl
- Input:
-
Webhook handler (
order.completed) Receives FastSpring’s webhook and grants the mapped entitlement.- Input:
order.completedpayload from FastSpring - Action: Grant the product and process each webhook only once (ignore retries).
- Input:
Environment variables
Use this table to collect the secrets and IDs your backend needs:
| Key | Description | 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 or secret | Unity Services | Authorize webhook calls |
PEM_PRIVATE_KEY | FastSpring private key | Your PKI or 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 (example sets provider: aws), reference these values in serverless.yml and load actual secrets from your secrets manager:
# 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_KEYis the value you generated and uploaded in Stage 2 for secure payloads.
/encode example
The client calls /encode to obtain a signed checkout URL. Your backend verifies the player, signs the payload with your private key, and returns 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
After validating the 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>"
}
}'Placeholder legend
<YOUR_UGS_PROJECT_ID>Unity project ID<exchange_token>obtained via Unity’s token exchange API<target_player_id>the player to grant currency/items<purchased_item_id>UGS Virtual Purchase ID (must match the FastSpring product ID, for examplecoinpack-150)
Note: FastSpring may retry webhooks. Deduplicate by event ID or order reference and process each webhook only once.
Deploy and verify
What goes live
POST /encode(client requests signed checkout URLs)- Webhook endpoint (receives and handles
order.completedevents)
How to deploy
- In UGS: deploy the
VirtualPurchasescript in the Unity Cloud Dashboard. - In AWS: deploy after configuring the AWS CLI and Serverless Framework:
npm run deploy
# or
serverless deployVerify
- Call
/encodeand confirm the response containsproduct,securePayload, andreturnUrl. - Complete a test checkout and confirm your webhook endpoint receives
order.completed.
Stage 4: Build the web checkout page
Build a lightweight page that reads the signed URL parameters, initializes the FastSpring session, and redirects back to your app.
Expected query parameters
Your backend’s signed checkout URL must include these parameters; the page reads them on load to reconstruct the secure session.
| Parameter | Required | Description |
|---|---|---|
product | Yes | Product ID (e.g., coinpack-150). Identifies the product to display. |
securePayload | Yes | Base64-encoded and URL-escaped encrypted payload generated by your backend. |
secureKey | Yes | Base64-encoded and URL-escaped encryption key used with the payload. |
returnUrl | Yes | Deep link to redirect to after checkout (e.g., fastspring://shop-return). |
Tip: These parameters come from the
/encoderesponse. Never hard-code them on the page.
Initialize the secure session
After parsing the query string, decode both secure values and initialize the FastSpring session.
const decodedPayload = decodeURIComponent(securePayload);
const decodedKey = decodeURIComponent(secureKey);
fastspring.builder.secure(decodedPayload, decodedKey);Embed checkout attributes
Add a FastSpring container pointing to your checkout and access key.
<div
data-storefront="https://<your-fs-checkout-url>"
data-access-key="ACCESS_KEY">
</div>Placeholder legend
data-storefrontyour FastSpring Embedded Checkout URLdata-access-keyaccess key from Developer Tools > Store Builder Library
Stage 5: Configure the client (Unity example)
Connect the client to your backend: request a signed checkout URL, open checkout, handle the return deep link, poll the economy, and confirm the update.
5.1 StoreConfig setup
In Unity, open the StoreConfig ScriptableObject and set the following fields. If you’re using another engine, create an equivalent configuration file or class.
| Field | Description | Example |
|---|---|---|
storeBaseUrl | Origin of your deployed or local checkout page | https://checkout.example.com |
returnUrl | Deep link to return to the Unity app | fastspring://shop-return |
purchaseCreatorEndpoint | Backend endpoint that returns a signed checkout URL | https://api.example.com/encode |
5.2 Post-checkout flow (deep link, polling, and thank-you)
After checkout, FastSpring redirects the player back to your app via a deep link, such as:
fastspring://shop-return?amount=150Your deep link handler should:
- Parse the
amountquery parameter (display only). - Re-authenticate the player if needed.
- Trigger polling of your economy service.
- Show a thank-you popup once the balance updates.
Tip: In the Unity Editor, press D to simulate a deep link return (
fastspring://shop-return?amount=150).
Polling UGS Economy after return
When a purchase completes, UGS Economy may not yet reflect the change. Use an active polling mechanism (for example, PollUntilBalanceChangesAsync) until the player’s balance refreshes.
Why polling is needed
- The FastSpring → backend → UGS webhook path can take a few seconds.
- UGS does not push real-time balance updates to the Unity client.
- Polling ensures the player sees their updated balance without restarting the app.
Show a thank-you message
After confirming the updated balance, display a success or thank-you message to the player. 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 = granted coin amount5.3 Request and open a signed checkout
To initiate a purchase, the client requests a signed checkout URL from your backend. The backend signs the payload and returns a one-time URL that the client can open securely.
| Actor | What it does |
|---|---|
| Client | Ensures the player is signed in, sends product to /encode, opens the returned checkout URL, and handles the deep link. |
| Backend | Verifies player identity, creates securePayload and secureKey, returns the signed URL, handles the order.completed webhook, and grants the entitlement. |
Example usage
ShoppingManager.Instance.OpenStore("coinpack-150");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 (placeholders)
https://<your-fs-checkout-url>?
product=<your_product_id>&
securePayload=<your_securePayload>&
returnUrl=<your_deep_link>Placeholder legend
<your-fs-checkout-url>your FastSpring Embedded Checkout URL<your_product_id>mapped FastSpring product ID (for example,coinpack-150)<your_securePayload>URL-escapedsecurePayloadreturned by/encode<your_deep_link>deep link return URL (for example,fastspring://shop-return)
5.4 Open checkout across platforms
Once your client can generate a signed checkout URL, open it using the provider that fits each platform.
Opens FastSpring checkout in the system browser (Safari). After checkout completes or is cancelled, Safari redirects back to your app via the deep link (for example, fastspring://shop-return).
Application.OpenURL(fullUrl); // Opens Safari; returns via fastspring://shop-return5.5 Adding custom implementations
To add a new platform implementation:
- Create a class that implements
IShopProvider. - Implement the
OpenStore(string url)method. - Add the appropriate conditional compilation directive in
OpenShopProvider. - If using native plugins (Android and/or iOS), place them in the corresponding
Assets/Pluginsdirectory.
5.6 Economy integration
This example uses Unity Gaming Services (UGS) Economy, but 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 match your own currency ID).
- Keep the product → amount mapping on the backend (do not trust amounts from the client).
Tip: If you use a custom or third-party economy, mirror the same pattern: verify the webhook on your backend, grant the correct entitlement, and let the client poll until the updated balance is confirmed.
Updated about 11 hours ago