Integrate a webshop into a Unity game
Open a webshop from a Unity game with an authenticated player session.
Read time 7 minutesLast updated a month ago
Open a webshop from your Unity game so players can buy products from your In-App Purchasing (IAP) catalog.
Webshops use the IAP catalog and payment providers that you already configured. Players can complete purchases on the web without a separate in-game storefront.
To open the shop, add an in-game action, such as a button, that opens the shop URL with the player’s authenticated session attached. The session identifies the player to the webshop.
Prerequisites
Before you start, make sure you meet the following prerequisites:
- A Unity project that integrates In-App Purchasing.
- The Authentication SDK (version 3.7.1 or later) in your project.
- A webshop in the target environment.
To open the live shop, publish the webshop so it’s available at . To test a draft, you only need a created webshop in the target environment.
shop.unity.com/{studio}/game/{slug}For more information, refer to Create and publish your first webshop.
Open the webshop from your game
Use the SDK helper
The Unity IAP SDK provides to open your webshop with the required parameters and compliance checks.
RedirectToWebshopCall through the for the payment provider. Leave empty to open the webshop front page, or pass a listing ID to open a product page directly:
RedirectToWebshopIPaymentProvidersExtendedPurchaseServicecatalogListingId// Open the front page UnityIAPServices.StoreController(PaymentProvider.Name).PaymentProvidersExtendedPurchaseService.RedirectToWebshop();// Open a specific product page UnityIAPServices.StoreController(PaymentProvider.Name).PaymentProvidersExtendedPurchaseService.RedirectToWebshop(catalogListingId: "your-listing-id");
Require compliance approval before opening
To gate the webshop on a compliance check, register a callback with before calling . The callback runs before each redirect. Returning cancels the redirect and fails the purchase with :
SetComplianceCheckRedirectToWebshopfalsePurchasingUnavailableStoreController(PaymentProvider.Name).PaymentProvidersExtendedPurchaseService .SetComplianceCheck(async context => await ShowComplianceDialog(context));
Control how the webshop opens
By default, the SDK opens the webshop in an external browser. To change this, call on the for the payment provider before redirecting. This setting is independent of .
SetWebshopPresentationMode(CheckoutPresentationMode)IPaymentProviderStoreExtendedServiceSetCheckoutPresentationModeManual integration
If you can't use the SDK helper, construct and open the webshop URL directly.
To open a webshop from a button or other in-game action, resolve the shop URL from the Webshop service, then open the URL with .
Application.OpenURLResolving the URL at runtime lets the same build open the published shop or an environment draft preview. This lets you test a draft before you publish.
To resolve the URL, send a request to the endpoint:
GETstorefront-linkhttps://webshop.services.api.unity.com/v1/projects/{projectId}/environments/{environmentId}/storefront-link
By default, the service returns a URL automatically based on the environment state:
- A published production environment returns the live public storefront URL. The live URL resolves anonymously.
- A non-production environment or an unpublished production environment returns a short-lived draft preview URL.
To preview unpublished changes to a live shop, add the query parameter. This returns the draft preview URL, even for a published production environment:
source=drafthttps://webshop.services.api.unity.com/v1/projects/{projectId}/environments/{environmentId}/storefront-link?source=draft
The integration uses the following Authentication SDK tokens:
- The access token authorizes the request when you resolve a draft preview. Send the access token in the
storefront-linkheader.Authorization: Bearer - The session token authenticates the player in the browser. Mint a short-lived, single-use restricted token with right before you open the shop, then append its session token to the resolved URL as the
GenerateRestrictedTokenAsyncquery parameter.sessionToken
After you resolve the URL, append the session token, project ID, and environment so the shop opens for the correct player.
For more information about each query parameter and how the shop behaves when parameters are missing, refer to Open a webshop from your game.
using System;using System.Collections;using System.Collections.Generic;using Unity.Services.Authentication;using UnityEngine;using UnityEngine.Networking;public class WebshopLauncher : MonoBehaviour{ // Replace these with the values from your Unity Cloud project and webshop configuration. const string ProjectId = "<your-project-id>"; const string EnvironmentId = "<your-environment-id>"; // used in the API request path const string EnvironmentName = "production"; // used in the shop URL const string StorefrontLinkEndpoint = "https://webshop.services.api.unity.com/v1/projects/{0}/environments/{1}/storefront-link"; // draftPreview: request source=draft to preview unpublished changes even on a live env. public void OpenShop(string locale, string currency, bool draftPreview = false) { StartCoroutine(OpenShopRoutine(locale, currency, draftPreview)); } IEnumerator OpenShopRoutine(string locale, string currency, bool draftPreview) { var signedIn = AuthenticationService.Instance.IsSignedIn; // The access token is required for draft previews; live storefronts open anonymously // and ignore any token sent. Send it whenever the player is signed in. if (draftPreview && !signedIn) { Debug.LogWarning("WebshopLauncher: draft preview requires the player to be signed in."); yield break; } // 1. Ask the Webshop service for the storefront URL. var endpoint = string.Format(StorefrontLinkEndpoint, ProjectId, EnvironmentId); if (draftPreview) endpoint += "?source=draft"; using var request = UnityWebRequest.Get(endpoint); request.SetRequestHeader("Accept", "application/json"); if (signedIn) request.SetRequestHeader("Authorization", $"Bearer {AuthenticationService.Instance.AccessToken}"); yield return request.SendWebRequest(); if (request.result != UnityWebRequest.Result.Success) { Debug.LogError($"WebshopLauncher: storefront-link request failed " + $"({request.responseCode}): {request.error}"); yield break; } var link = JsonUtility.FromJson<StorefrontLinkResponse>(request.downloadHandler.text); if (link == null || string.IsNullOrEmpty(link.storefrontUrl)) { Debug.LogError("WebshopLauncher: storefront-link response did not contain a storefrontUrl."); yield break; } // 2. Mint a short-lived, single-use restricted token for the webshop redirect. var tokenOptions = new RestrictedTokenOptions { Services = new List<string> { "no-svc" }, // ID token unusable against any real service SingleUse = true, // consumed by the webshop on first refresh TtlSeconds = 60, // minted right before redirect }; var tokenTask = AuthenticationService.Instance.GenerateRestrictedTokenAsync(tokenOptions); yield return new WaitUntil(() => tokenTask.IsCompleted); if (tokenTask.IsFaulted) { Debug.LogError($"WebshopLauncher: failed to generate restricted token: {tokenTask.Exception}"); yield break; } // 3. Open the shop. var sessionToken = tokenTask.Result.SessionToken; var shopUrl = BuildShopUrl(link.storefrontUrl, sessionToken, locale, currency); Application.OpenURL(shopUrl); } static string BuildShopUrl(string storefrontUrl, string sessionToken, string locale, string currency) { var url = storefrontUrl; url = AppendParam(url, "sessionToken", sessionToken); url = AppendParam(url, "projectId", ProjectId); url = AppendParam(url, "environment", EnvironmentName); url = AppendParam(url, "locale", locale); url = AppendParam(url, "currency", currency); return url; } static string AppendParam(string url, string key, string value) { if (string.IsNullOrEmpty(value)) return url; var separator = url.Contains("?") ? '&' : '?'; return $"{url}{separator}{key}={UnityWebRequest.EscapeURL(value)}"; } [Serializable] class StorefrontLinkResponse { public string storefrontUrl; public bool live; }}
After the shop opens, it removes these parameters from the URL and persists the player's session in the browser. For more information about sessions and persistence, refer to Open a webshop from your game.
If you don't have locale or currency to pass, then omit those parameters. By default, the IAP catalog uses the player’s browser locale and defaults to US dollars (USD). For more information about catalog locale handling, refer to Catalog and payments in webshops.
Handle inbound deep links
When you set a Deeplink URL for the webshop, the shop returns the player to your game through that custom URL scheme. The shop uses the return deep link after a purchase and when a player selects Connect to game on the unauthenticated landing page.
To receive return deep links, register the custom URL scheme on the device and handle incoming links at runtime. For more information about how the shop builds the return URL, refer to Open a webshop from your game.
Register the URL scheme
Declare the same scheme that you set in the Deeplink URL field in the Dashboard. The operating system uses the scheme to route the link to your game.
- iOS and macOS: Add the scheme under Edit > Project Settings > Player > Other Settings > Supported URL schemes. Unity writes it into the built app's (
Info.plist) at build time. Prefer this over hand-editing the generatedCFBundleURLTypes, which is regenerated on every build.Info.plist - Android: Add an with a
intent-filterentry to your activity, through a custom main manifest or the Gradle manifest template.<data android:scheme="mygame" />
The scheme must match the Deeplink URL value exactly. After you change an Android manifest, reinstall the game so the operating system picks up the new scheme.
Handle the link at runtime
Subscribe to for links that arrive while the game is running. At startup, also check to handle cold starts, where the deep link launches the game.
Application.deepLinkActivatedApplication.absoluteURLvoid Awake(){ // Links that arrive while the game is running. Application.deepLinkActivated += OnReturnFromWebshop; // Cold start: the deep link launched the game. if (!string.IsNullOrEmpty(Application.absoluteURL)) OnReturnFromWebshop(Application.absoluteURL);}void OnReturnFromWebshop(string url){ // Handle the post-purchase return: the shop appends ?status=success // (and playerId when available) after a completed purchase. if (new Uri(url).Query.Contains("status=success")) { // Purchase completed on the web — refresh the player's entitlements. } // The Connect to game sign-in link is reopened by the SDK automatically, // so it needs no handling here.}
The shop only sends , so handle that case here. The SDK automatically reopens the Connect to game sign-in link, as described in the previous note.
status=successTest opening the shop
Test the draft preview before you publish, then repeat the test against the live shop. To test a draft, call with and confirm the player is signed in. You can target a non-production environment, or use to preview unpublished changes on your production environment.
OpenShopdraftPreview: truesource=draftTo verify the integration, follow these steps:
- Build and install your game on a mobile device.
- Trigger the in-game button that calls to launch the device's system browser.
OpenShop - Confirm the browser opens to the resolved storefront URL with your locale and currency in the URL. A draft opens an environment-scoped preview URL. The live shop opens .
shop.unity.com/{studio}/game/{slug} - Confirm the shop opens to the authenticated product list rather than the unauthenticated landing page.
- Complete a sandbox purchase. Refer to the relevant IAP payment provider's sandbox documentation for test credentials.
- When the draft works as expected, call with
OpenShopand repeat the test against the live published shop.draftPreview: false
If the shop opens to the unauthenticated landing page instead of the product list, confirm your game passed a valid and on the URL. Refer to Troubleshooting webshops.
sessionTokenprojectId