Integrate a webshop into a Unity game
Open a webshop from a Unity game with an authenticated player session.
Read time 7 minutesLast updated 13 hours 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.
shop.unity.com/{studio}/game/{slug}Open the webshop from your game
Use the SDK helper
The Unity IAP SDK providesRedirectToWebshopRedirectToWebshopIPaymentProvidersExtendedPurchaseServicecatalogListingId// 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 withSetComplianceCheckRedirectToWebshopfalsePurchasingUnavailableStoreController(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, callSetWebshopPresentationMode(CheckoutPresentationMode)IPaymentProvidersExtendedPurchaseServiceSetCheckoutPresentationModeManual 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 withApplication.OpenURLGETstorefront-linkBy default, the service returns a URL automatically based on the environment state:https://webshop.services.api.unity.com/v1/projects/{projectId}/environments/{environmentId}/storefront-link
- 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.
source=draftThe integration uses the following Authentication SDK tokens:https://webshop.services.api.unity.com/v1/projects/{projectId}/environments/{environmentId}/storefront-link?source=draft
- 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 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.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; }}
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" />
Handle the link at runtime
Subscribe toApplication.deepLinkActivatedApplication.absoluteURLThe shop only sendsvoid 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.}
status=successTest opening the shop
Test the draft preview before you publish, then repeat the test against the live shop. To test a draft, callOpenShopdraftPreview: truesource=draft- 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
sessionTokenprojectId