Skip to main content

WELCOME

Welcome to the Xsolla SDK

AvailableMobile · Unity · Windows StoresSDK Explorer →

Xsolla SDK is a versatile solution built by Xsolla — a global video game commerce leader trusted by the game studios behind Crossout, Modern Warship, and Fantasy Tales: Sword and Magic. It empowers developers to seamlessly integrate in-game payments via Xsolla Pay Station across mobile, PC and web-based games, supporting monetization for both in-store builds (e.g., App Store iOS and Google Play builds in the USA) and out-of-store distributed game builds (e.g., Android APK, Notarized iOS, Xsolla Launcher and standalone PC). All of this helps developers overcome challenges like restricted market access, high native payment fees, and fragmented user experiences, maximizing revenue potential across every distribution channel.

1,000+payment methods
200+countries
130+currencies
25+UI languages

INTERACTIVE DEMO

SDK Explorer

Walk through every step of the integration — authentication, catalog loading, purchasing, and finalization — in a live, interactive environment. No code required.

SDK Explorer — interactive demo of Xsolla SDK payment flow

SDK Explorer — try the full payment flow in your browser

Explore the SDK →


PRIMARY SOLUTION

Buy Button: Instant Web Shop Access

The April 2025 contempt ruling in Epic v. Apple requires Apple to allow external payment links in US iOS apps; the December 2025 appellate modification permits Apple to charge a commission on such transactions, with the rate under court review. The Epic v. Google injunction (effective October 29, 2025), subsequently settled in November 2025, requires Google Play to permit alternative billing options and external payment links for US users. The Xsolla SDK is fully compliant with both Apple's revised App Store guidelines and Google Play's updated billing policies.

The Buy Button ↗ (xsolla.com) feature enables direct access to your Web Shop payment UI:

One-Tap Purchase Flow

Players access payment UI for specific items instantly — no extra navigation steps.

Seamless Integration

Launch web payment UI directly from your game without breaking user flow.

Expanded Payment Options

Full Web Shop payment range including Apple Pay and global alternatives.

Enhanced Conversion

Streamlined purchase flow reduces friction and increases completion rates.

Advanced Analytics

MMP attribution via Adjust ↗ (developers.xsolla.com), AppsFlyer ↗ (developers.xsolla.com), Airbridge ↗ (developers.xsolla.com), and Singular ↗ (developers.xsolla.com) — maintaining full visibility into your campaigns and revenue.

DISTRIBUTION STRATEGY

Supporting Multiple Monetization Models

With the Xsolla SDK, we bring ease of integration to all developers as part of our "Equal Access for Everyone" initiative, greatly simplifying the initial phases of integration while allowing for further fine-tuning and customization. Setting and integrating your project with Xsolla SDK automatically provides access to a fully-fledged, highly customizable Xsolla Web Shop — establishing your direct-to-consumer connection outside of your application.

SDK overview — payment flows, distribution, and direct-to-consumer in one platform

Out-of-store payments allow developers to distribute their own application files — like APKs for Android or Notarized versions of apps for iOS (e.g., through Web Distribution) — outside the confines of traditional app stores. When developers opt for distributing games independently, in a direct-to-consumer way, they retain greater control over their profits. This approach is further supported by evolving regulations such as the Digital Markets Act (DMA), which expands developers' freedom to offer alternative payment methods. The answer to these complexities lies in Xsolla Pay Station, accessible through the Xsolla SDK.

PAY STATION

Solutions

Xsolla Pay Station is specifically designed to enable developers to monetize their applications efficiently, preserving a larger share of revenue. With over 1,000 payment options, Xsolla Pay Station opens up monetization opportunities in regions and through methods not typically supported by platform-native billing, letting developers tap into new markets and cater to a broader audience.

Additionally, pairing the Xsolla SDK with distribution solutions like those from Digital Turbine amplifies this advantage. Digital Turbine drives large-scale APK distribution through partnerships with major carriers and device manufacturers, putting games directly on millions of devices worldwide. Beyond pre-installs, its SingleTap technology delivers instant installs from ads, links, or messages from a CDN of your choice — bypassing "scare screens," app store friction and boosting conversions. This combination lets developers go direct-to-player at scale, keep UA workflows intact, and capture a greater share of their revenue.

Please reach out to your Account Manager and/or Business Development team for recipes for success to supercharge your out-of-store distribution strategy with up-to-date tips and tricks.

Alternative Payment Options

If you're interested in implementing Apple's Alternative Payment options ↗ (developer.apple.com) on the App Store or Google Play's Alternative Billing ↗ (developer.android.com), please contact your Account Manager and/or Business Development team for guidance and implementation support.


GET STARTED

Full documentation (text version for AI assistants & reference)

Plain-text export: llms-full.txt · llms.txt

Welcome to the Xsolla SDK

https://developers.xsolla.com/sdk/

AvailableMobile · Unity · Windows Stores[SDK Explorer →](https://developers.xsolla.com/sdk/demo/)

*Xsolla SDK* is a versatile solution built by Xsolla — a global video game commerce leader trusted by the game studios behind *Crossout*, *Modern Warship*, and *Fantasy Tales: Sword and Magic*. It empowers developers to seamlessly integrate in-game payments via **Xsolla Pay Station** across mobile, PC and web-based games, supporting monetization for both in-store builds (e.g., App Store iOS and Google Play builds in the USA) and out-of-store distributed game builds (e.g., Android APK, Notarized iOS, Xsolla Launcher and standalone PC). All of this helps developers overcome challenges like restricted market access, high native payment fees, and fragmented user experiences, maximizing revenue potential across every distribution channel.1,000+payment methods200+countries130+currencies25+UI languages  

## SDK Explorer
 

Walk through every step of the integration — authentication, catalog loading, purchasing, and finalization — in a live, interactive environment. No code required. [![SDK Explorer — interactive demo of Xsolla SDK payment flow]](https://developers.xsolla.com/sdk/demo/)

SDK Explorer — try the full payment flow in your browser 

[**Explore the SDK →**](https://developers.xsolla.com/sdk/demo/)   

## Buy Button: Instant Web Shop Access
 

The April 2025 contempt ruling in Epic v. Apple requires Apple to allow external payment links in US iOS apps; the December 2025 appellate modification permits Apple to charge a commission on such transactions, with the rate under court review. The Epic v. Google injunction (effective October 29, 2025), subsequently settled in November 2025, requires Google Play to permit alternative billing options and external payment links for US users. The Xsolla SDK is fully compliant with both Apple's revised App Store guidelines and Google Play's updated billing policies.  

The [**Buy Button ↗**](https://xsolla.com/mobile-buy-button) [(xsolla.com)](https://xsolla.com/mobile-buy-button) feature enables direct access to your Web Shop payment UI: **One-Tap Purchase Flow**

Players access payment UI for specific items instantly — no extra navigation steps.**Seamless Integration**

Launch web payment UI directly from your game without breaking user flow.**Expanded Payment Options**

Full Web Shop payment range including Apple Pay and global alternatives.**Enhanced Conversion**

Streamlined purchase flow reduces friction and increases completion rates.**Advanced Analytics**

MMP attribution via [Adjust ↗](https://developers.xsolla.com/solutions/web-shop/analytics/adjust/) [(developers.xsolla.com)](https://developers.xsolla.com/solutions/web-shop/analytics/adjust/), [AppsFlyer ↗](https://developers.xsolla.com/solutions/web-shop/analytics/appsflyer/) [(developers.xsolla.com)](https://developers.xsolla.com/solutions/web-shop/analytics/appsflyer/), [Airbridge ↗](https://developers.xsolla.com/solutions/web-shop/analytics/airbridge/) [(developers.xsolla.com)](https://developers.xsolla.com/solutions/web-shop/analytics/airbridge/), and [Singular ↗](https://developers.xsolla.com/solutions/web-shop/analytics/singular/) [(developers.xsolla.com)](https://developers.xsolla.com/solutions/web-shop/analytics/singular/) — maintaining full visibility into your campaigns and revenue.   

## Supporting Multiple Monetization Models
 

With the **Xsolla SDK**, we bring ease of integration to all developers as part of our **"Equal Access for Everyone"** initiative, greatly simplifying the initial phases of integration while allowing for further fine-tuning and customization. Setting and integrating your project with **Xsolla SDK** automatically provides access to a fully-fledged, highly customizable **Xsolla Web Shop** — establishing your direct-to-consumer connection outside of your application. 

SDK overview — payment flows, distribution, and direct-to-consumer in one platform 

**Out-of-store** payments allow developers to distribute their own application files — like APKs for Android or Notarized versions of apps for iOS (e.g., through Web Distribution) — outside the confines of traditional app stores. When developers opt for distributing games independently, in a direct-to-consumer way, they retain greater control over their profits. This approach is further supported by evolving regulations such as the **Digital Markets Act** (DMA), which expands developers' freedom to offer alternative payment methods. The answer to these complexities lies in **Xsolla Pay Station**, accessible through the **Xsolla SDK**.  

## Solutions
 

**Xsolla Pay Station** is specifically designed to enable developers to monetize their applications efficiently, preserving a larger share of revenue. With over 1,000 payment options, Xsolla Pay Station opens up monetization opportunities in regions and through methods not typically supported by platform-native billing, letting developers tap into new markets and cater to a broader audience.  

Additionally, pairing the **Xsolla SDK** with distribution solutions like those from *Digital Turbine* amplifies this advantage. *Digital Turbine* drives large-scale APK distribution through partnerships with major carriers and device manufacturers, putting games directly on millions of devices worldwide. Beyond pre-installs, its SingleTap technology delivers instant installs from ads, links, or messages from a CDN of your choice — bypassing "*scare screens*," app store friction and boosting conversions. This combination lets developers go direct-to-player at scale, keep UA workflows intact, and capture a greater share of their revenue. 

Please reach out to your Account Manager and/or Business Development team for recipes for success to supercharge your out-of-store distribution strategy with up-to-date tips and tricks. 

### Alternative Payment Options
 

If you're interested in implementing [Apple's Alternative Payment options ↗](https://developer.apple.com/support/apps-using-alternative-payment-providers-in-the-eu) [(developer.apple.com)](https://developer.apple.com/support/apps-using-alternative-payment-providers-in-the-eu) on the App Store or [Google Play's Alternative Billing ↗](https://developer.android.com/google/play/billing/alternative) [(developer.android.com)](https://developer.android.com/google/play/billing/alternative), please contact your Account Manager and/or Business Development team for guidance and implementation support.   [SDK**Mobile SDK**iOS · Android→](https://developers.xsolla.com/sdk/mobilesdk/)[SDK**Unity SDK**iOS · Android · Windows · macOS · Web→](https://developers.xsolla.com/sdk/unity/)[SDK**Windows Stores SDK**Windows · Epic Games→](https://developers.xsolla.com/sdk/windows/)

News

https://developers.xsolla.com/sdk/documentation-changelog

Documentation updates and release notes for the Xsolla SDK. 

This page contains information about any additions or updates to the **Xsolla SDK** documentation. 

### [3.1.8] - 2026-06-02
 
 
- (Android, iOS, Unity, Windows) Clarified in the **initialization** docs that the SDK loads **Virtual Items** only. 
 

### [3.1.7] - 2026-05-08
 
 
- Fixed the [**integration widget**](https://developers.xsolla.com/sdk/integration-widget) layout to remove forced horizontal padding, preventing visual overflow when the browser window is narrow. 
 

### [3.1.6] - 2026-04-02
 
 
- (Android) Added [**`withRedirectAppRelaunch`**](https://developers.xsolla.com/sdk/mobilesdk/sdk/configuration/android#payments) to the `Config.Payments` API reference. 
- (Unity) Added Android-specific [**external browser app relaunch**](https://developers.xsolla.com/sdk/unity/sdk/configuration/android#external-browser-app-relaunch) setting documentation. 
 

### [3.1.5] - 2026-04-01
 
 
- Added a **Download SDK** button to the navigation bar. 
- (Android) Expanded the [**consumption**](https://developers.xsolla.com/sdk/mobilesdk/sdk/purchase/android) section with updated purchase flow diagrams. 
- (Android) Updated the [**client-side validation**](https://developers.xsolla.com/sdk/mobilesdk/sdk/validation/android) diagram to include the consume request to Xsolla services. 
- (Android) Added an [**event listeners**](https://developers.xsolla.com/sdk/mobilesdk/sdk/configuration/android#xsolla-events-api) API reference to configuration docs. 
- Updated the [**integration widget**](https://developers.xsolla.com/sdk/integration-widget) with a correct **How to Install** link for Android GitHub Maven. 
 

### [3.1.4] - 2026-03-18
 
 
- Updated the SDK availability links. 
- (Android) Added [**billing flow cancellation API**](https://developers.xsolla.com/sdk/mobilesdk/sdk/purchase/android) and token-based params builder. 
- (Android) Added [**GitHub repository installation**](https://developers.xsolla.com/sdk/mobilesdk/sdk/installation/android) with tabbed GitHub/Maven Central layout. 
- (Unity) Updated [**configuration**](https://developers.xsolla.com/sdk/unity/sdk/configuration) with email consent, restore interval, locale settings, and full builder table. 
- (Unity) Added Android-specific [**cancellation reason querying**](https://developers.xsolla.com/sdk/unity/sdk/configuration/android#cancellation-reason-querying) and [**WebView splash screen**](https://developers.xsolla.com/sdk/unity/sdk/configuration/android#webview-splash-screen) settings. 
 

### [3.1.3] - 2026-03-05
 
 
- Added interactive [**SDK integration widget**](https://developers.xsolla.com/sdk/integration-widget). 
- (Android, Unity) Updated configuration API references and method names. 
- (iOS) Added [**SPM installation**](https://developers.xsolla.com/sdk/mobilesdk/sdk/installation/ios) instructions. 
- Reworked landing and Publisher Account pages with new layouts. 
- Reworked quick-start guides with timeline layouts. 
- Standardized terminology and proper noun capitalization across documentation. 
 

### [3.1.2] - 2025-12-22
 
 
- (Unity) Updated [**Configuration**](https://developers.xsolla.com/sdk/unity/sdk/configuration) around mobile purchase settings, purchase restoration, and deep link overrides. 
- (iOS, Unity) Added “purchase cancelled” handling examples to purchase flow docs. 
- (iOS, Unity) Clarified iOS Storefront behavior for alternative distribution installs. 
- (iOS) Added an advanced guide for **Universal Links** and linked it from deep link configuration docs. 
 

### [3.1.1] - 2025-10-16
 
 
- (Unity) Fixed the product registration in the [**extended example**](https://developers.xsolla.com/sdk/unity/quick-start/extended-example#configuration--initialization). 
 

### [3.1.0] - 2025-10-07
 ℹ️ANDROID

Following the Supreme Court's refusal to pause the ruling, **effective October 29, 2025**, Google must permit U.S. **Google Play** apps to guide users to **external payment methods via link-outs** and allow the use of **alternative billing solutions**. This change enables developers to avoid Google's fees and inform users about lower-cost options outside Google Play—fostering more competitive pricing and greater revenue control.

Read more on the Xsolla Blog: [**New platform rules for Android—what developers need to know ↗**](https://xsolla.com/blog/new-platform-rules-for-android-and-what-developers-need-to-know) ([https://xsolla.com/blog/new-platform-rules-for-android-and-what-developers-need-to-know](https://xsolla.com/blog/new-platform-rules-for-android-and-what-developers-need-to-know))

Learn more about the Buy Button: [**Xsolla Buy Button ↗**](https://xsolla.com/mobile-buy-button) ([https://xsolla.com/mobile-buy-button](https://xsolla.com/mobile-buy-button)) 
 
- (Android) Added information on [**Buy Button**](https://developers.xsolla.com/sdk/mobilesdk/sdk/configuration/android#buy-button-solution). 
- (Android) Added information on [**Web Shop link-outs**](https://developers.xsolla.com/sdk/mobilesdk/sdk/purchase/android?purchase-method=linkout-webshop#starting-purchasing-flow). 
 

### [3.0.4] - 2025-09-23
 
 
- (Android) Added information on [**advanced Events API settings**](https://developers.xsolla.com/sdk/mobilesdk/sdk/configuration/android#xsolla-events-api). 
 

### [3.0.3] - 2025-09-02
 
 
- (Android) Added information on integration [**helpers**](https://developers.xsolla.com/sdk/mobilesdk/sdk/advanced/detectstore?platform=android). 
- (Unity) Added information on integration [**helpers for Android**](https://developers.xsolla.com/sdk/unity/sdk/advanced/detectstore?platform=android). 
- (Android) Added information on additional [**purchasing flow launching methods**](https://developers.xsolla.com/sdk/mobilesdk/sdk/purchase/android#starting-purchasing-flow). 
- (Android) Added a collapsible section under [**`Default`**](https://developers.xsolla.com/sdk/mobilesdk/sdk/purchase/android?purchase-method=standard#starting-purchasing-flow) purchasing method tab describing advanced settings. 
- (Unity) Added a collapsible section with advanced purchasing flow settings under the [**`Unity InAppPurchasing-less`**](https://developers.xsolla.com/sdk/unity/sdk/purchase?integration-method=without) integration method's tab. 
 

### [3.0.2] - 2025-08-21
 
 
- Clarified Web Shop link-out behavior in [iOS Configuration](https://developers.xsolla.com/sdk/mobilesdk/sdk/configuration/ios#web-shop-link-outs): SDK opens Web Shop and cancels SDK purchase callbacks; fulfillment should be handled via webhooks/server. 
- Added a new [Unity Configuration: Web Shop Link-outs](https://developers.xsolla.com/sdk/unity/sdk/configuration#web-shop-link-outs) section with setup code. 
 

### [3.0.1] - 2025-08-18
 
 
- Documented default Pay Station payment method [configuration](https://developers.xsolla.com/sdk/mobilesdk/sdk/configuration/ios#default-payment-method) on iOS. 
 

### [3.0.0] - 2025-08-12
 
 
- Major documentation rework around the new **Xsolla SDK**, which supersedes the legacy **Xsolla Mobile SDK**. 
- **Unity** is now a dedicated top-level section with guides for all supported platforms (Mobile, PC, Web). 
- **Mobile** documentation is split into platform-specific pages: separate Android and iOS guides across quick start, installation, initialization, configuration, purchase, testing, validation, and advanced topics. 
- Added information on authentication using third-party social provider access tokens. 
 

### [1.1.4] – 2025-08-06
 
 
- **Events API**: introduced full documentation for the Xsolla Events API in Publisher Account webhooks and in Android & Unity SDK configuration guides, including setup steps, code samples, and caution notes. 
- **Trusted Web Activity**: added Android and Unity sections covering TWA configuration, splash-screen options, fallback behavior, and accompanying assets. 
- **iOS configuration**: expanded with purchase restoration settings, automatic vs. manual deep-link handling (incl. URL-scheme setup), and orientation management examples. 
- **Quick-start & overview pages**: updated top-level README and quick-start guides with concise notes on DMA compliance, global payment methods, and rapid onboarding. 
 

### [1.1.3] - 2025-07-16
 
 
- Enhanced [iOS Configuration](https://developers.xsolla.com/sdk/mobilesdk/sdk/configuration/ios) with improved organization and structure, deep link handling guidance, Web Shop link-outs and Buy Button Solution documentation, and Purchase Restoration Settings configuration. 
 

### 2025-06-26
 

On 26th of June, [Apple has released](https://developer.apple.com/news/?id=awedznci) the following changes for apps distributed on the App Store in the EU. Developers can now **communicate and promote offers** for digital goods or services, including steering to external payment options through the **StoreKit External Purchase Link Entitlement**.

While you can use Xsolla Mobile SDK to serve external payments and comply with these new EU requirements through Apple's **Alternative Terms Addendum for Apps in the EU** or **StoreKit External Purchase Link Entitlement (EU) Addendum** (contact your account manager for access and implementation guidance), we recommend using **[Web Shops](https://developers.xsolla.com/sdk/mobilesdk/quick-start/webshop) as a more viable and streamlined alternative** for maximum flexibility and reduced complexity in the current regulatory landscape.

[Read more about Apple's EU payment rules ↗](https://developer.apple.com/support/communication-and-promotion-of-offers-on-the-app-store-in-the-eu/) 

### [1.1.2] - 2025-06-25
 
 
- Unity now has its own subsection (`Unity`) 
- Aggregated mobile platforms (Android/iOS) under their own section (`Mobile`) 
- Added diagrams and flow mapping comparison tables to `Purchase Validation` (Android) 
- Added links to virtual items external documentation in **Publisher Account** -> **Virtual Items** 
- Moved iOS US & EU compliance into a separate page (**Unity** -> **SDK** -> **Configuration** -> **iOS**) 
 

### [1.1.1] - 2025-06-09
 
 
- Improved [Quick Start Guide](https://developers.xsolla.com/sdk/mobilesdk/quick-start) with better organization and clearer structure 
- Added [Storefront Detection](https://developers.xsolla.com/sdk/mobilesdk/sdk/advanced/detectstore) documentation for Unity integration 
- Enhanced [Installation Guide](https://developers.xsolla.com/sdk/mobilesdk/sdk/installation/ios) with iOS version support details 
- Updated [Webshop Integration](https://developers.xsolla.com/sdk/mobilesdk/quick-start/webshop) documentation with Buy Button information 
- Updated [Configuration](https://developers.xsolla.com/sdk/mobilesdk/sdk/configuration/ios) with custom login token configuration example for iOS 
- Added cross-platform compatibility information to [Unity Integration](https://developers.xsolla.com/sdk/unity/quick-start/extended-example) examples 
- Improved navigation and external links throughout the documentation 
- Added information about Buy Button and alternative billing options to the landing page 
 

### [1.1.0] - 2025-05-07
 
 
- Updated to ensure compliance with Apple's revised App Store guidelines. 
- The Xsolla Mobile SDK is now stable and ready for production use. 
- Added an [article](https://developers.xsolla.com/sdk/mobilesdk/sdk/advanced/ios-storefront) on how to detect iOS Storefront. 
- Improved an [article](https://developers.xsolla.com/sdk/mobilesdk/sdk/advanced/detectstore) on how to detect store installation for iOS. 
 

### [1.0.1] - 2024-07-26
 
 
- Added [Glossary](https://developers.xsolla.com/sdk/welcome/glossary). 
- Expanded [Core Concepts (iOS)](https://developers.xsolla.com/sdk/welcome/core-concepts-ios). 
- Updated [Core Concepts (Android)](https://developers.xsolla.com/sdk/welcome/core-concepts-android). 
 

### [1.0.0]
 
 
- Initial revision.

Integration

https://developers.xsolla.com/sdk/integration-widget

Loading Integration Widget…

Xsolla SDK for Mobile

https://developers.xsolla.com/sdk/mobilesdk

AvailableXsolla SDK for Mobile[SDK Explorer →](https://developers.xsolla.com/sdk/demo/)

The **Xsolla SDK for Mobile** empowers developers to seamlessly integrate in-game payments via **Xsolla Pay Station** across iOS and Android games, supporting monetization for both in-store builds (e.g., App Store iOS and Google Play builds in the USA) and out-of-store distributed game builds (e.g., Android APK, Notarized iOS). The SDK empowers developers to connect directly with players, maintain control over the checkout experience, and keep up to 95% of their revenue. Games automatically benefit from updates for new monetization tools, regulatory compliance, and global policy changes.

Trusted by the studios behind *Crossout*, *Modern Warship*, and *Fantasy Tales: Sword and Magic* to power direct-to-consumer commerce across iOS and Android at global scale.  

## Platform Support
 

### iOS
 

Following the **April 30, 2025** U.S. court decision, developers may include external payment links in iOS apps. The **Xsolla SDK for Mobile** is fully compliant with Apple's revised App Store guidelines, supporting both in-app Pay Station flows and link-out payments via Web Shop. 

### Android
 

Following the **October 29, 2025** Epic v. Google injunction, U.S. Google Play apps may use alternative billing solutions, external payment links, and are no longer subject to anti-steering restrictions. The SDK supports out-of-store APK distribution, Google Play Billing, and browser-based checkout — all in one integration.  

## Key Benefits for Mobile Developers
 **iOS & Android Integration**

Purpose-built SDKs for iOS (Swift/Obj-C) and Android, each designed for its platform's distribution model and compliance requirements. The same Xsolla infrastructure powers both.**DMA & Court-Order Compliant**

Full compliance with Apple's revised App Store guidelines and Google Play's updated billing policies — including link-out payments, alternative billing, and out-of-store distribution, out-of-the-box.**Up to 95% Revenue Retention**

Distribute directly via APK, Notarized iOS, or link-out flows. Bypass platform fees, maintain pricing control, and build a direct-to-consumer revenue channel that grows with your player base.**1,000+ Payment Methods**

Global payment infrastructure covering 200+ regions, 130+ currencies, and 25+ UI languages — with localized checkout flows and advanced anti-fraud included at no extra cost.**Compliance Updates Automatically**

Apple and Google policy changes propagate to the SDK automatically. No emergency releases, no compliance debt — stay current without interrupting your development cycle.  

## Getting Started
 

Ready to integrate the Xsolla SDK into your iOS or Android project? Follow the Quick Start guide to process your first test payment in minutes, or contact your Account Manager for personalized implementation guidance. **Xsolla Buy Button**Fast Path

One-tap Web Shop purchase flows with 1,000+ payment methods — no SDK installation required. Launch browser-based checkout directly from your iOS or Android game, fully compliant with Apple and Google billing policies.[Learn more →](https://developers.xsolla.com/sdk/mobilesdk/buy-button/)  [QUICK START**Integration Guide**Process your first test payment on iOS or Android→](https://developers.xsolla.com/sdk/mobilesdk/quick-start/)[FAST PATH**Buy Button**One-tap external payments — no full SDK required→](https://developers.xsolla.com/sdk/mobilesdk/buy-button/)[REFERENCE**Full SDK Docs**Installation, configuration, and advanced features→](https://developers.xsolla.com/sdk/mobilesdk/sdk/installation/)

Google Play User Choice Billing

https://developers.xsolla.com/sdk/mobilesdk/addendum/android/google-play-user-choice-billing

## Overview
 
 
- **Program**: Google Play lets approved apps in select countries present a user choice screen so players can select either Google Play Billing (GPB) or a third-party payment service provider (PSP). 
- **Xsolla path**: Keep GPB available, and launch **Xsolla Pay Station** when players choose the alternative option. No separate Android branch is required. 
 

## What Xsolla provides
 
 
- 1000+ localized payment methods to complement GPB and boost conversion in markets where native billing underperforms. 
- SKU parity by mirroring GPB product IDs as [Virtual Items](https://developers.xsolla.com/sdk/publisher-account/virtual-items) (IAPs) in your Xsolla Catalog. 
- Authentication via [Xsolla Login](https://developers.xsolla.com/sdk/mobilesdk/sdk/configuration/android#authentication) or your own token service. 
- Webhooks and reporting that align with standard Pay Station deployments. 
 

## Further guidance
 

Please route any questions about Google Play User Choice Billing through your Xsolla account manager.

Buy Button ⚡

https://developers.xsolla.com/sdk/mobilesdk/buy-button

ℹ️U.S. rules for iOS and Android — external payment link-outs

**iOS — Effective Apr 30, 2025:** Developers may include external payment links in U.S. apps. The **Xsolla SDK** aligns with Apple’s updated guidelines.

**Android — Effective Oct 29, 2025:** Following the **Epic v. Google** injunction, U.S. **Google Play** apps may guide users to **external payment methods via link-outs**, use **alternative billing solutions**, and are no longer subject to **anti-steering restrictions**. 

## Fastest non-SDK approach (Recommended)
 

For both platforms, the fastest *recommended* non-SDK approach is the **Buy Button via link-outs to Web Shop** 

Buy Button integration via Web Shop link is a quick, low-code way to enable item-specific purchases from your game using a browser-based checkout. It offers: 
 
- A seamless flow — tapping Buy Button opens the Web Shop checkout for a specific item, allowing users to automatically return to the game after a successful payment. 
- Support for a wide range of payment methods, including one-click payments via Apple Pay, for fast and familiar mobile checkout. 
- Fast setup — if your Web Shop is already set up, you only need to add a link with parameters to the game. 
- Instant onboarding — if you don’t have Web Shop yet, you can get started quickly by instantly creating Web Shop, adding payment links to your game, and going live. 
 tip

For additional information, please visit: [**Buy Button via link-outs to Web Shop** ↗](https://developers.xsolla.com/solutions/web-shop/other/instant-purchase) 
([https://developers.xsolla.com/solutions/web-shop/other/instant-purchase](https://developers.xsolla.com/solutions/web-shop/other/instant-purchase)). 

## SDK approach
 

Use Web Shop link-outs when you want minimal code changes by redirecting the user to your existing Xsolla Web Shop to complete the purchase. caution

In Web Shop link-out mode, the SDK does not execute its native in-app payment flow. It only opens the Web Shop and cancels any SDK purchase callbacks. Handle fulfillment and post-payment updates via your Web Shop processes (for example, webhooks or server-side logic), not via SDK purchase callbacks. 

Select your platform to get started with the integration: 
 
- [Android](https://developers.xsolla.com/sdk/mobilesdk/buy-button/android) 
- [iOS](https://developers.xsolla.com/sdk/mobilesdk/buy-button/ios)

Buy Button - Android

https://developers.xsolla.com/sdk/mobilesdk/buy-button/android

Two simple ways to add external payments to your Android app: redirect users to your Web Shop, or launch Pay Station's native in-app UI. Both are production-ready — pick the one that fits your workflow. Recommended

#### Web Shop Link-outs

Redirect users to your existing Xsolla Web Shop in the browser.

- Already have an Xsolla Web Shop
- Want minimal code changes
- Need web and mobile payment consistency
- Doing a phased migration from web

#### Buy Button Solution

Full payment UI handled natively inside your app — no browser redirect.

- Want a fully native integration
- Need 1,000+ payment methods out of the box
- Prefer Xsolla to manage the payment UI
- Want automatic regional compliance
 
 important

To enable **U.S.-only** link-outs **effective October 29, 2025**, first detect the **Google Play** storefront country code — see the [**guide**](https://developers.xsolla.com/sdk/mobilesdk/sdk/advanced/detect-gpb-storefront-android). 

```java
final Context context = // Your activity context
final String webshopUrl = "https://yourwebshop.xsolla.site";
final String sku = "key_1";                // Virtual item SKU from your Xsolla Catalog
final String userId = "user_12345";        // Current user's ID
final String redirectUrl = "yourgame://";  // Deep link back to your app

WebShop.launchBillingFlow(context, webshopUrl, sku, userId, redirectUrl,
    new WebShop.ILaunchBillingFlowCallback() {
        @Override
        public void onLaunchBillingFlow(@NonNull final Either<Error, Void> result) {
            result.voidFold(
                err -> Log.e(TAG, "Link-out error: " + err.getMessage()),
                __  -> Log.d(TAG, "Web Shop opened successfully")
            );
        }
    }
);
```

This opens your Web Shop in the device browser. Fulfillment happens via your server-side webhooks — see [webhook setup](https://developers.xsolla.com/sdk/publisher-account/webhooks).

```java
final Config.Payments paymentsConfig = Config.Payments.getDefault()
    .withBuyButtonSolutionEnabled(true);
```

Enable this once in your SDK configuration — Buy Button activates automatically for all [default purchase flows](https://developers.xsolla.com/sdk/mobilesdk/sdk/purchase/android/?purchase-method=standard#starting-purchasing-flow). See [configuration docs](https://developers.xsolla.com/sdk/mobilesdk/sdk/configuration/android) for themes, logos, and custom domains. Still not sure?

**Web Shop** is great if you have an existing store or want to start with minimal changes. **Pay Station** is the fastest path to a fully native integration. Both support the same payment methods and regions worldwide.

Buy Button - iOS

https://developers.xsolla.com/sdk/mobilesdk/buy-button/ios

### Buy Button Solution (Recommended)
 ℹ️U.S. rules iOS — external payment link-outs

**iOS — Effective Apr 30, 2025:** Developers may include external payment links in U.S. apps. The **Xsolla SDK** aligns with Apple’s updated guidelines. 

The *Buy Button Solution* provides a complete, native payment experience with minimal integration effort. This approach handles the entire payment flow within your app using Xsolla's optimized UI components. 

**When to use Buy Button Solution:** 
 
- You want a quick, native payment integration 
- You prefer Xsolla to handle payment UI and optimization 
- You need support for multiple payment methods out of the box 
- You want automatic compliance with regional payment regulations 
 

```objectivec
// Enable Buy Button solution
settings.useBuyButtonSolution = YES; 
```

```swift
// Enable Buy Button solution
settings.useBuyButtonSolution = true
```
 

### Web Shop Link-outs
 

The Web Shop approach redirects users to your existing Xsolla Web Shop in a browser, then returns them to your app after purchase completion. This method is ideal for gradual migration or when you want to maintain consistency with existing web-based payment flows. 

**When to use Web Shop link-outs:** 
 
- You already have an established Xsolla Web Shop 
- You want to test Xsolla integration with minimal code changes 
- You need to maintain payment consistency across multiple platforms 
- You're implementing a phased migration from web to mobile payments 
 caution

In Web Shop link-out mode, the SDK does not execute its native in-app payment flow. It only opens the Web Shop and immediately cancels any SDK purchase callbacks. This mode is intended for minimal code changes: handle fulfillment and post-payment updates via your Web Shop processes (for example, webhooks or server-side logic), not via SDK purchase callbacks. 

#### Configuring Web Shop Integration
 

Use the helper method to configure the Web Shop redirect flow: 

```objectivec
[settings setupBuyButtonWebShopWithUrl:[NSURL URLWithString:@"https://yourwebshop.xsolla.site"] 
                                userId:@"<USER_ID>" 
                             returnUrl:@"yourgame://" 
                          openExternally:YES]; 
```

```swift
settings.setupBuyButtonWebShop(url: URL(string: "https://yourwebshop.xsolla.site")!, 
                               userId: "<USER_ID>", 
                               returnUrl: "yourgame://", 
                               openExternally: true)
```
 

**Parameter explanation:** 
 
- `url`: The URL of your Xsolla Web Shop where users complete purchases 
- `userId`: A unique identifier for the user in your system, used for purchase attribution and user tracking 
- `returnUrl`: The deep link URL scheme that brings users back to your app after purchase completion 
- `openExternally`: Whether to open the Web Shop in an external browser (recommended for compliance and better user experience) 
 

#### Initiating Web Shop Purchases
 

After configuration, initiate purchases by calling the purchase method with your product SKU: 

```objectivec
// Attempt to purchase an item via Web Shop
BOOL handledExternally = [[SKPaymentQueue defaultQueue] purchaseWithSKU:@"com.yourgame.item"];

if (handledExternally) {
    // Web Shop opened successfully, update UI to reflect the redirect
    NSLog(@"Purchase redirected to Web Shop");
    // Optionally show a loading indicator or message to the user
}
```

```swift
// Attempt to purchase an item via Web Shop
let handledExternally: Bool = SKPaymentQueue.default().purchase(withSKU: "com.yourgame.item")

if handledExternally {
    // Web Shop opened successfully, update UI to reflect the redirect
    print("Purchase redirected to Web Shop")
    // Optionally show a loading indicator or message to the user
}
```

Quick Start — Mobile SDK

https://developers.xsolla.com/sdk/mobilesdk/quick-start

This quick start guide will walk you through integrating *Xsolla Mobile SDK* to accept payments in your game. Using a pre-configured test project ID, we'll cover the essentials: setting up basic user authentication, importing SKUs, and processing a test payment. By the end, you'll have a functional payment system integrated into your game, demonstrating how Xsolla can streamline your monetization process. Let's get started and have your game accepting payments in just a few simple steps!

⚡ **DMA-ready & revenue-boosting**: leverage direct APK/IPA distribution alongside in-app compliance with DMA—unlocking access to 1,000+ global payment methods and bypassing app-store fees to maximize your margins. 

## Choose Your Platform
 

Select your platform to get started with the integration: 
 
- [Android](https://developers.xsolla.com/sdk/mobilesdk/quick-start/android) 
- [iOS](https://developers.xsolla.com/sdk/mobilesdk/quick-start/ios) 
 

Each platform-specific guide will walk you through: 
 
- Installing the SDK 
- Configuring the SDK 
- Initializing the SDK 
- Making a purchase 
- Collecting payments

Quick Start - Android

https://developers.xsolla.com/sdk/mobilesdk/quick-start/android

Get the **Xsolla Mobile SDK** integrated and process your first test payment. We'll use pre-configured test credentials so you can start in minutes. 

## Getting Started
 

Ensure your project targets **Android 7.0+** (`minSdk 24`), then add Maven Central and the **Xsolla Mobile SDK** dependency: 

```kotlin
// settings.gradle
repositories {
    mavenCentral()

    maven {
        url = uri("https://raw.githubusercontent.com/xsolla/xsolla-sdk-android/main")
    }
}
```
 

```kotlin
// build.gradle
dependencies {
    def version_mobilesdk = '<version>'
    implementation "com.xsolla.android:mobile:$version_mobilesdk"
}
```
 

See [installation details](https://developers.xsolla.com/sdk/mobilesdk/sdk/installation/android#prerequisites) for more configuration options. 

## Authenticate

Add this to your Activity's `onCreate()`:

```java
import com.xsolla.android.mobile.*;

@Nullable
private BillingClient mBillingClient;

final Config.Common configCommon = Config.Common.getDefault()
    .withDebugEnabled(true)
    .withLogLevel(LogLevel.VERBOSE)
    .withSandboxEnabled(true);

final Config.Integration configIntegration = Config.Integration.forXsolla(
    Config.Integration.Xsolla.Authentication.forAutoJWT(
        ProjectId.parse(301871).getRightOrThrow(),  // Test project
        LoginUuid.parse("dfcb133b-6d0b-4937-b8d2-c4f4d58fb53a").getRightOrThrow()  // Test login ID
    )
);

final Config config = new Config(
    configCommon,
    configIntegration,
    Config.Payments.getDefault(),
    Config.Analytics.getDefault()
);

// Inside onCreate(), after creating Config:
mBillingClient = BillingClient.newBuilder(this)
    .setConfig(config)
    .setListener((billingResult, purchases) -> {
        // Handle purchase completion — see Finalize Purchase
    })
    .build();

mBillingClient.startConnection(new BillingClientStateListener() {
    @Override
    public void onBillingServiceDisconnected() {
        Log.d(TAG, "Disconnected from Xsolla services.");
    }

    @Override
    public void onBillingSetupFinished(@NonNull final BillingResult billingResult) {
        if (!billingResult.isSuccess()) {
            Log.e(TAG, "Connection failed: " + billingResult.getResponseCode());
            return;
        }
    }
});
```

This creates your `Config` with `AutoJWT` authentication, sandbox mode, and debug logging, then initializes the `BillingClient` and connects to Xsolla services. For production, replace the test project ID and login ID with your own from [**Publisher Account**](https://developers.xsolla.com/sdk/publisher-account). See [configuration docs](https://developers.xsolla.com/sdk/mobilesdk/sdk/configuration/android) for advanced options.important

If you are using your own **Project/Login IDs**, make sure to enable **Device ID login** in your Project. Learn how to enable it here: [https://developers.xsolla.com/sdk/publisher-account/login#enable-device-id-login-in-publisher-account](https://developers.xsolla.com/sdk/publisher-account/login#enable-device-id-login-in-publisher-account)

## Load Catalog

Query your product catalog once connected:

```java
mBillingClient.queryProductDetailsAsync(
    QueryProductDetailsParams.newBuilder()
        .setProductList(Arrays.asList(
            QueryProductDetailsParams.Product.newBuilder()
                .setProductId("money.100")  // Test product SKU
                .setProductType(BillingClient.ProductType.INAPP)
                .build()
        ))
        .build(),
    (result, productDetailsList) -> {
        // Launch purchase — see Purchase Product
    }
);
```

This loads product `key_1` from your catalog. See [initialization docs](https://developers.xsolla.com/sdk/mobilesdk/sdk/initialization/android) for reconnection handling and advanced options.

## Purchase Product

Use the product details from your catalog query to launch a purchase:

```java
// Inside productDetails callback (from Load Catalog):
if (billingResult.isSuccess() &&
    productDetailsList != null && !productDetailsList.isEmpty()) {

    mBillingClient.launchBillingFlow(
        activity,  // Your Activity reference
        BillingFlowParams.newBuilder()
            .setProductDetailsParamsList(Arrays.asList(
                BillingFlowParams.ProductDetailsParams.newBuilder()
                    .setProductDetails(productDetailsList.get(0))
                    .build()
            ))
            .build()
    );
} else {
    Log.e(TAG, "No products available: " + billingResult.getResponseCode());
}
```

This opens **Xsolla Pay Station** where users complete payment. For testing, use a [test card ↗](https://developers.xsolla.com/doc/pay-station/testing/test-cards):

![Choose payment method]

![Processing payment]

![Payment confirmed]

## Finalize Purchase

Once the purchase completes, consume it and award the product to the user:

```java
// Inside onPurchasesUpdated callback:
if (billingResult.isSuccess() && purchases != null) {
    for (Purchase purchase : purchases) {
        mBillingClient.consumeAsync(
            ConsumeParams.newBuilder()
                .setPurchaseToken(purchase.getPurchaseToken())
                .build(),
            (result, purchaseToken) -> {
                if (result.isSuccess()) {
                    Log.d(TAG, "Purchase consumed - award item to user");
                    // Award the product (virtual item) to the user here
                } else {
                    Log.e(TAG, "Consumption failed: " + result.getResponseCode());
                }
            }
        );
    }
} else {
    Log.e(TAG, "Purchase failed or cancelled: " + billingResult.getResponseCode());
}
```

This consumes the purchase and lets you award the product (virtual item). See [purchase flow docs](https://developers.xsolla.com/sdk/mobilesdk/sdk/purchase/android) for error scenarios and advanced options. 

## What's next?
 

You've built a complete payment integration: authentication, catalog loading, purchase flow, and fulfillment. Your app now has access to 1,000+ payment methods across 200+ countries. 
 
- **[Set up your project](https://developers.xsolla.com/sdk/publisher-account)** — create your Publisher Account and replace the sandbox test credentials with production ones 
- **[Buy Button](https://developers.xsolla.com/sdk/mobilesdk/buy-button/android)** — fastest path to external payments 
- **[Extended example](https://developers.xsolla.com/sdk/mobilesdk/quick-start/extended-examples/android-example)** — complete working implementation 
- **[Full SDK docs](https://developers.xsolla.com/sdk/mobilesdk/sdk/installation/android)** — advanced features, retry policies, custom UI

Android Extended Example

https://developers.xsolla.com/sdk/mobilesdk/quick-start/extended-examples/android-example

**Xsolla Mobile SDK** requires very few steps to get a working integration, which normally consists of these steps:
 
- Configuring and initializing the SDK 
- Launching a payment collection flow 
- Awarding the purchased content to the user (i.e. consumption) 

Here is a *fully-fledged* example implementation that achieves the aforementioned functionality in just under **150 lines of code**: 

```java
package com.example.yoursdkintegration;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.xsolla.android.mobile.*;
import com.xsolla.android.mobile.common.LogLevel;

import java.util.Arrays;
import java.util.List;

public class YourSDKIntegrationActivity extends Activity {
    private static final String TAG = "YourSDKIntegration";

    @Nullable
    private BillingClient mBillingClient = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        final Config.Common configCommon = Config.Common.getDefault()
            .withDebugEnabled(true)
            .withLogLevel(LogLevel.VERBOSE)
            .withSandboxEnabled(true);

        final Config.Integration configIntegration = Config.Integration.forXsolla(
            Config.Integration.Xsolla.Authentication.forAutoJWT(
                ProjectId.parse(301871).getRightOrThrow(),
                LoginUuid.parse("dfcb133b-6d0b-4937-b8d2-c4f4d58fb53a").getRightOrThrow()
            )
        );

        final Config.Analytics configAnalytics = Config.Analytics.getDefault();

        final Config.Payments configPayments = Config.Payments.getDefault();

        final Config config = new Config(
            configCommon,
            configIntegration,
            configPayments,
            configAnalytics
        );

        mBillingClient = BillingClient.newBuilder(this)
            .setConfig(config)
            .setListener(new PurchasesUpdatedListener() {
                @Override
                public void onPurchasesUpdated(
                    @NonNull BillingResult billingResult, @Nullable List<Purchase> purchases
                ) {
                    if (billingResult.isSuccess() && purchases != null) {
                        for (int i = 0; i < purchases.size(); ++i) {
                            final ConsumeParams consumeParams = ConsumeParams.newBuilder()
                                .setPurchaseToken(purchases.get(i).getPurchaseToken())
                                .build();

                            mBillingClient.consumeAsync(consumeParams, new ConsumeResponseListener() {
                                @Override
                                public void onConsumeResponse(
                                    @NonNull BillingResult billingResult, String purchaseToken
                                ) {
                                    Log.d(TAG,
                                        "Purchase consumption response: " +
                                        billingResult.getResponseCode()
                                    );
                                }
                            });
                        }
                    } else {
                        Log.e(TAG,
                            "Purchase consumption response: " +
                            billingResult.getResponseCode()
                        );
                    }
                }
            })
            .build();

        mBillingClient.startConnection(new BillingClientStateListener() {
            @Override
            public void onBillingServiceDisconnected() {
                Log.d(TAG, "Disconnected.");
            }

            @Override
            public void onBillingSetupFinished(@NonNull BillingResult billingResult) {
                if (billingResult.isSuccess()) {
                    final QueryProductDetailsParams queryProductDetailsParams =
                        QueryProductDetailsParams.newBuilder()
                            .setProductList(Arrays.asList(
                                QueryProductDetailsParams.Product.newBuilder()
                                    .setProductId("money.100")
                                    .setProductType(BillingClient.ProductType.INAPP)
                                    .build()
                            ))
                            .build();

                    mBillingClient.queryProductDetailsAsync(
                        queryProductDetailsParams, new ProductDetailsResponseListener() {
                            @Override
                            public void onProductDetailsResponse(
                                @NonNull BillingResult billingResult,
                                @Nullable List<ProductDetails> productDetailsList
                            ) {
                                if (billingResult.isSuccess() && productDetailsList != null &&
                                    !productDetailsList.isEmpty()) {
                                    // We'll just use the very first element on the list, but you'd normally
                                    // want to handle the rest of the product details as well.
                                    final ProductDetails productDetails = productDetailsList.get(0);

                                    // Create parameters for the purchasing flow..
                                    final BillingFlowParams billingFlowParams =
                                        BillingFlowParams.newBuilder()
                                            .setProductDetailsParamsList(Arrays.asList(
                                                BillingFlowParams.ProductDetailsParams.newBuilder()
                                                    .setProductDetails(productDetails)
                                                    .build()
                                            ))
                                            .build();

                                    // Initiate a purchasing flow.
                                    mBillingClient.launchBillingFlow(
                                        YourSDKIntegrationActivity.this, billingFlowParams
                                    );
                                } else {
                                    Log.e(TAG,
                                        "Received an invalid product details response: " +
                                        billingResult.getResponseCode()
                                    );
                                }
                            }
                        }
                    );
                } else {
                    Log.e(TAG, "Failed to connect.");
                }
            }
        });
    }
}
```
 important

If you are using your own **Project/Login IDs**, make sure to enable **Device ID login** in your Project. Learn how to enable it here: [https://developers.xsolla.com/sdk/publisher-account/login#enable-device-id-login-in-publisher-account](https://developers.xsolla.com/sdk/publisher-account/login#enable-device-id-login-in-publisher-account)

Quick Start - iOS

https://developers.xsolla.com/sdk/mobilesdk/quick-start/ios

Get the **Xsolla Mobile SDK** integrated and process your first test payment. We'll use pre-configured test credentials so you can start in minutes.

⚡ **DMA-ready & revenue-boosting**: leverage direct IPA distribution alongside in-app compliance with DMA — unlocking access to 1,000+ global payment methods and bypassing app-store fees to maximize your margins. 

## Getting Started
 ℹ️U.S. rules iOS — external payment link-outs

**iOS — Effective Apr 30, 2025:** Developers may include external payment links in U.S. apps. The **Xsolla SDK** aligns with Apple's updated guidelines. 

Add the **Xsolla Mobile SDK** to your project using Swift Package Manager: 
 
- In Xcode, go to **File → Add Package Dependencies…** 
- Enter the repository URL: `https://github.com/xsolla/xsolla-sdk-ios` 
- Set the dependency rule to **Up to Next Major Version** and click **Add Package**. 
- Select **XsollaMobileSDK** and add it to your target. 
 

Then set up a URL scheme for your target: 
 
- Select your target and open the *Info* tab. 
- Click ➕ in the *URL Types* section. 
- Set the URL Scheme to `$(PRODUCT_BUNDLE_IDENTIFIER)`. 
 

![URL Scheme] 

See [installation details](https://developers.xsolla.com/sdk/mobilesdk/sdk/installation/ios) for more configuration options. 

## Authenticate

Configure the SDK with test credentials, start the payment queue, and register a transaction observer:

```swift
import XsollaMobileSDK

let settings = SKPaymentSettings(projectId: 301871,
                                 loginProjectId: "dfcb133b-6d0b-4937-b8d2-c4f4d58fb53a",
                                 platform: .standalone)

settings.useSandbox = true
settings.openExternalBrowser = true  // U.S. compliance; set to false for EU (DMA)

SKPaymentQueue.default().start(settings)
SKPaymentQueue.default().add(self)  // Handle purchase completion — see Finalize Purchase
```

```objectivec
#import <XsollaMobileSDK/StoreKitWrapper.h>

SKPaymentSettings* settings = [[SKPaymentSettings alloc] initWithProjectId: 301871
                                                    loginProjectId:@"dfcb133b-6d0b-4937-b8d2-c4f4d58fb53a"
                                                    platform: SKPaymentPlatformStandalone
                                                    paystationUIThemeId: SKPaystationThemeDark
                                                    paystationUISize: SKPaystationSizeMedium];

settings.useSandbox = YES;
settings.openExternalBrowser = YES;  // U.S. compliance; set to NO for EU (DMA)

SKPaymentQueue* queue = [SKPaymentQueue defaultQueue];
[queue startWithSettings: settings];
[queue addTransactionObserver: self];  // Handle purchase completion — see Finalize Purchase
```

This creates your settings with sandbox mode and test credentials, then starts the `SKPaymentQueue` and registers a transaction observer. For production, replace the test project ID and login ID with your own from [**Publisher Account**](https://developers.xsolla.com/sdk/publisher-account). See [configuration docs](https://developers.xsolla.com/sdk/mobilesdk/sdk/configuration/ios) for advanced options including [storefront detection](https://developers.xsolla.com/sdk/mobilesdk/sdk/advanced/ios-storefront).important

If you are using your own **Project/Login IDs**, make sure to enable **Device ID login** in your Project. Learn how to enable it here: [https://developers.xsolla.com/sdk/publisher-account/login#enable-device-id-login-in-publisher-account](https://developers.xsolla.com/sdk/publisher-account/login#enable-device-id-login-in-publisher-account)

## Load Catalog

Query your product catalog once connected:

```swift
let skus: Set<String> = ["your_sku1", "your_sku2"]
let req = SKProductsRequest(productIdentifiers: skus)

req.delegate = self
req.start()

// Conform your class to SKProductsRequestDelegate
extension YourClass: SKProductsRequestDelegate {
    func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
        self.products = response.products
        // Launch purchase — see Purchase Product
    }
}
```

```objectivec
NSSet *skus = [NSSet setWithArray:@[@"your_sku1", @"your_sku2"]];
SKProductsRequest* req = [[SKProductsRequest alloc] initWithProductIdentifiers:skus];

req.delegate = self;
[req start];

// Conform your class to SKProductsRequestDelegate
@interface YourClass (SKProductsRequestDelegate) <SKProductsRequestDelegate>
@end

@implementation YourClass (SKProductsRequestDelegate)

- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
    self.products = response.products;
    // Launch purchase — see Purchase Product
}

@end
```

This loads your products by SKU. See [initialization docs](https://developers.xsolla.com/sdk/mobilesdk/sdk/initialization/ios) for reconnection handling and advanced options.

## Purchase Product

Use the product details from your catalog query to launch a purchase:

```swift
let product = products.first!  // Previously fetched product
let payment = SKPayment(product: product)
SKPaymentQueue.default().add(payment)
```

```objectivec
SKProduct *product = ...;  // Previously fetched product
SKPayment *payment = [SKPayment paymentWithProduct:product];
[[SKPaymentQueue defaultQueue] addPayment:payment];
```

This opens **Xsolla Pay Station** where users complete payment. For testing, use a [test card ↗](https://developers.xsolla.com/doc/pay-station/testing/test-cards):

![Choose payment method]

![Processing payment]

![Payment confirmed]

## Finalize Purchase

Handle completed transactions in your `SKPaymentTransactionObserver` and finish each one:

```swift
extension YourClass: SKPaymentTransactionObserver {
    func paymentQueue(_: SKPaymentQueue, updatedTransactions: [SKPaymentTransaction]) {
        for transaction in updatedTransactions {
            switch transaction.transactionState {
            case .purchased:
                // Award the product (virtual item) to the user here
                SKPaymentQueue.default().finishTransaction(transaction)
            case .failed:
                // Handle error
                SKPaymentQueue.default().finishTransaction(transaction)
            default:
                break
            }
        }
    }
}
```

```objectivec
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions {
    for(SKPaymentTransaction *transaction in transactions) {
        switch (transaction.transactionState) {
            case SKPaymentTransactionStatePurchased:
                // Award the product (virtual item) to the user here
                [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
                break;
            case SKPaymentTransactionStateFailed:
                // Handle error
                [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
                break;
            default: break;
        }
    }
}
```

This processes purchase results and finishes transactions. Always call `finishTransaction` to acknowledge each transaction. See [purchase flow docs](https://developers.xsolla.com/sdk/mobilesdk/sdk/purchase/ios) for error scenarios and advanced options. 

## What's next?
 

You've built a complete payment integration: authentication, catalog loading, purchase flow, and fulfillment. Your app now has access to 1,000+ payment methods across 200+ countries. 
 
- **[Set up your project](https://developers.xsolla.com/sdk/publisher-account)** — create your Publisher Account and replace the sandbox test credentials with production ones 
- **[Buy Button](https://developers.xsolla.com/sdk/mobilesdk/buy-button/ios)** — fastest path to external payments 
- **[Full SDK docs](https://developers.xsolla.com/sdk/mobilesdk/sdk/installation/ios)** — advanced features, configuration, custom UI

Web Shop and Xsolla SDK: A Symbiotic Relationship

https://developers.xsolla.com/sdk/mobilesdk/quick-start/webshop

**Xsolla Web Shop** and **Xsolla SDK** are designed to work seamlessly together, creating a powerful ecosystem for game monetization both inside and outside your game. This synergy not only enhances your monetization strategy but also simplifies the integration process for both solutions.

📈 **Boost revenue across channels**: combine Web Shop and Xsolla SDK to reach players worldwide with over 1,000 payment methods, seamless Buy Button integration, and a unified backend—driving higher conversions with minimal added effort. 

### From Web Shop to Xsolla SDK: A Natural Progression
 

If your game already utilizes Xsolla's Web Shop, you're well-positioned to integrate the Xsolla SDK with minimal effort: 
 
- Existing Infrastructure: Your Web Shop integration means you already have server hooks and backend integrations in place for user authentication, inventory management, and purchase processing. 
- Product Mirroring: To enable in-app purchases via the Xsolla SDK, you primarily need to mirror your existing Web Shop products as in-app products. This process is straightforward, as the groundwork is already laid. 
- Unified Backend: With Web Shop in place, your backend is primed to handle requests from the Xsolla SDK, requiring only minor adjustments to support in-app transactions. 
 

### From Xsolla SDK to Web Shop: Opening New Channels Effortlessly
 

Conversely, if you've integrated Xsolla SDK, launching a Web Shop becomes a breeze: 
 
- Ready-to-Go Infrastructure: The Xsolla SDK integration means your game already communicates with Xsolla's backend services for user authentication and purchase processing. 
- Xsolla Catalog in Place: Your in-app products (Virtual Items with SKUs) are already defined in Xsolla's system, making it simple to extend these offerings to a web-based storefront. 
- Minimal Additional Setup: With the core integration complete via the Xsolla SDK, setting up a Web Shop requires just a few additional tweaks to go live, such as customizing the web interface and defining web-specific offers. 
 

### The Power of Integration
 

By leveraging both Xsolla SDK and Web Shop, you create a comprehensive monetization ecosystem that offers: 
 
- Seamless commerce player experience across platforms 
- Unified product catalog (Xsolla Catalog with Virtual Items) for easier management 
- Flexibility to offer platform-specific deals and promotions 
- Optional data collection for better player insights 
- Increased touchpoints for player engagement and monetization 
 

### Buy Button: Seamless Web Shop Integration
 

The Buy Button feature enhances the integration between your game and Web Shop by enabling direct link-outs to specific items: 
 
- **Streamlined Purchase Flow**: Players can tap the Buy Button in your game to instantly access the Web Shop payment UI for a specific item, eliminating extra navigation steps 
- **Familiar Payment Experience**: Leverage the full range of Web Shop payment methods, including Apple Pay, providing users with a seamless and trusted checkout process 
- **Simplified Integration**: Implement the Buy Button with minimal effort, as it builds upon your existing Web Shop infrastructure 
- **Enhanced Conversion**: Reduce purchase friction by enabling one-tap access to the payment flow, increasing conversion rates 
- **Advanced Analytics**: Track purchases and measure lifetime value (LTV) with MMP (Mobile Measurement Partners) like AppsFlyer, maintaining full visibility into your campaigns and revenue 
 note

For detailed implementation instructions, including Android/iOS storefront detection, URL parameters, and analytics integration, see our [Buy Button documentation ↗](https://developers.xsolla.com/solutions/web-shop/other/instant-purchase/) ([https://developers.xsolla.com/solutions/web-shop/other/instant-purchase/](https://developers.xsolla.com/solutions/web-shop/other/instant-purchase/)). 

Whether you start with Web Shop or Xsolla SDK, adding the other component becomes a straightforward process, allowing you to quickly expand your monetization channels and provide more options for your players to engage with and support your game. important

For more information, please follow the link: 
 [Xsolla Mobile Web Shop ↗](https://xsolla.com/mobile-web-shop) ([https://xsolla.com/mobile-web-shop](https://xsolla.com/mobile-web-shop))

How to detect Google Play storefront (Android)?

https://developers.xsolla.com/sdk/mobilesdk/sdk/advanced/detect-gpb-storefront-android

When distributing your app through **Google Play**, you may need to detect the current storefront to implement region-specific functionality. The storefront represents the Google Play region associated with the user’s account, identified by a two-letter country code in **ISO-3166-1 alpha-2** format (e.g., `"US"` for United States). important

This detection is particularly important in light of the **Epic v. Google injunction effective October 29, 2025**, which permits U.S. Google Play apps to guide users to external payment methods via link-outs and prohibits anti-steering restrictions. 

You can use storefront detection to: 
 
- Enable alternative billing options specifically for U.S. users 
- Implement region-specific payment flows 
- Control feature availability based on local billing regulations 
- Ensure compliance with U.S. court-mandated requirements 
 

You can use the following snippet to retrieve the country code of the Google Play storefront: 

```java
final Context context = ...

IntegrationUtils.queryGooglePlayCountryCodeAsync(context, result -> {
    result.voidFold(
        error -> {
            // Handle the error here.
        },
        countryCode -> {
            if (countryCode.equalsIgnoreCase("us")) {
                // Handle the US storefront.
            } else {
                // Handle all other storefront variants.
            }
        }
    );
});
```
 important

This snippet is most useful, when **distributing** and **installing** your app **through Google Play store**. To check whether the app has been installed from Google Play store or side-loaded (third-party stores or APK distribution) you can use the `IntegrationUtils.isInstalledFromGooglePlayStore()` utility function. tip

Prior to retrieving the Google Play storefront country code, it's advised (but not mandatory) to verify whether Google Play Store is installed on the device as the query function will result in error if the store is not available. For this purpose you can rely on `IntegrationUtils.isGooglePlayStoreInstalled()` utility function.

How to detect store installation?

https://developers.xsolla.com/sdk/mobilesdk/sdk/advanced/detectstore

When distributing your app through alternative channels outside of official app stores (like Google Play Store or Apple's App Store), you may need to detect where your app was installed from. This is particularly important for: 
 
- Implementing different payment flows for alternative distribution channels 
- Adjusting app behavior based on the installation source 
- Ensuring compliance with platform-specific requirements 
- Managing features that should only be available in certain distribution channels 
 

To determine where the game was installed from, use the snippets below. 

### How to check if Google Play Store is installed?

Can be useful in scenarios where certain functionality should not run outside the Google Play ecosystem. This snippet lets you check whether the device has the **Google Play Store** installed:

```java
final Context context = ...

if (IntegrationUtils.IsGooglePlayStoreInstalled(context)) {
    // The store is installed on the device..
} else {
    // The store is NOT installed on the device..
}
```

```java
import android.content.Context;
import com.xsolla.android.mobile.IntegrationUtils;
```

```java
final Context context = ...;

final boolean installed = IntegrationUtils.isGooglePlayStoreInstalled(context);

if (installed) {
    // Google Play Store is installed.
} else {
    // Google Play Store couldn't be found on the device.
}
```

### Checking if the App is Installed from Google Play Store

This snippet helps to determine whether the app was installed directly from the **Google Play Store** or side-loaded (installed from a third-party store or an APK):

```java
final Context context = ...

if (IntegrationUtils.IsInstalledFromGooglePlayStore(context)) {
    // The app was installed from Google Play Store..
} else {
    // The app was NOT installed (side-loaded) from Google Play Store..
}
```

```java
import android.content.Context;
import com.xsolla.android.mobile.IntegrationUtils;
```

```java
final Context context = ...;

final boolean installed = IntegrationUtils.isInstalledFromGooglePlayStore(context);

if (installed) {
    // The app was installed from Google Play Store.
} else {
    // The app wasn't installed from Google Play Store.
}
```

### Querying the country code of Google Play Store

Use this snippet to query the country code for the Google Play's storefront.important

The country code format is based on ISO-3166-1 alpha2 (UN country codes).

[https://unicode.org/cldr/charts/latest/supplemental/territory_containment_un_m_49.html](https://unicode.org/cldr/charts/latest/supplemental/territory_containment_un_m_49.html)

```java
import android.content.Context;
import com.xsolla.android.mobile.IntegrationUtils;
import com.xsolla.android.mobile.Either;
import com.xsolla.android.mobile.Error;
```

```java
final Context context = ...;

IntegrationUtils.queryGooglePlayCountryCodeAsync(
    context, new IntegrationUtils.IQueryGooglePlayCountryCodeCallback() {
        @Override
        public void onQueryGooglePlayCountryCode(@NonNull final Either<Error, String> result) {
            result.voidFold(
                err -> {
                    // Handle the error here.
                },
                countryCode -> {
                    if (countryCode.equalsIgnoreCase("us")) {
                        // Handle the US storefront.
                    } else {
                        // Handle all other storefront variants.
                    }
                }
            );
        }
    }
);
```

### Querying the installer's package name

If you need to know the exact package name of your app's installer, use the following code below.

```java
import android.content.Context;
import com.xsolla.android.mobile.IntegrationUtils;
import com.xsolla.android.mobile.Either;
import com.xsolla.android.mobile.Error;
```

```java
final Context context = ...;

final Either<Error, String> result = IntegrationUtils.getInstallerPackageName(context);

result.voidFold(
    err -> {
        // Handle the error.
    },
    packageName -> {
        if (packageName.equalsIgnoreCase("com.xxx.yyy.zzz")) {
            // Handle the expected package name.
        } else {
            // Handle the unknown package name.
        }
    }
);
```

Or if you prefer a simplified approach that omits error handling, use this code:

```java
import android.content.Context;
import com.xsolla.android.mobile.IntegrationUtils;
```

```java
final Context context = ...;

final boolean matched = IntegrationUtils.isInstallerPackageName(
    context, "con.xxx.yyy.zzz"
);

if (matched) {
    // The package name matched.
} else {
    // The package name didn't match.
}
```

To determine alternative app distribution status, use the following code snippets:

```objectivec
[SKPaymentQueue checkIfRunningInAlternativeDistributionWithCompletion:^(BOOL isRunningInAlternativeDistribution) {
    settings.enablePayments = isRunningInAlternativeDistribution;

    [[SKPaymentQueue defaultQueue] startWithSettings:settings];
}];
```

```swift
SKPaymentQueue.checkIfRunningInAlternativeDistribution { isInAlternativeStore in
    settings.enablePayments = isInAlternativeStore
    
    SKPaymentQueue.default().start(settings)
}
```

You can also implement the official method described in [Apple's Documentation ↗](https://developer.apple.com/documentation/appdistribution/distributing-your-app-on-an-alternative-marketplace#Enable-your-app-to-run-on-other-platforms) ([https://developer.apple.com/documentation/appdistribution/distributing-your-app-on-an-alternative-marketplace#Enable-your-app-to-run-on-other-platforms](https://developer.apple.com/documentation/appdistribution/distributing-your-app-on-an-alternative-marketplace#Enable-your-app-to-run-on-other-platforms)) to detect alternative app distribution. The following implementation is written in Swift but includes Objective-C compatibility. Call `getMarketplaceOrWeb()` and the callback will return `true` if the app is distributed through an alternative marketplace or web distribution.

```swift
getMarketplaceOrWeb { isAlternative in
    settings.enablePayments = isAlternative

    SKPaymentQueue.default().start(settings)
}

@objc
@MainActor
public static func getMarketplaceOrWeb(_ callback: @escaping ((Bool) -> Void)) {
    Task(priority: .high) {
        let flag = await isMarketplaceOrWeb()
        callback(flag)
    }
}

static func isMarketplaceOrWeb() async -> Bool {
    if #available(iOS 17.4, *) {
    #if canImport(MarketplaceKit)
        do {
        let currentDistributor = try await AppDistributor.current
        switch currentDistributor {
        case .appStore:
            return false
        case .testFlight:
            return false
        case .marketplace(let bundleId): // Alternative App Marketplace; the argument String identifies the marketplace app bundle ID
            return true
        case .web: // Alternative Web distribution
            return true
        case .other:
            return false
        @unknown default:
            return false
        }
        } catch { return false }
    #endif
    }

    return false
}
```

How to detect iOS Storefront?

https://developers.xsolla.com/sdk/mobilesdk/sdk/advanced/ios-storefront

When distributing your app through Apple's App Store, you may need to detect the current storefront to implement region-specific functionality. The storefront represents the App Store region that the user is currently using, identified by a three-letter country code (e.g., "USA" for United States). 

This detection is particularly important following the US court decision dated April 30, 2025, which allows developers to connect external payment links in the United States. You can use the storefront detection to: 
 
- Enable external payment links specifically for US users 
- Implement region-specific payment flows 
- Control feature availability based on regional requirements 
- Ensure compliance with local regulations 
 note

If the app is installed via **alternative distribution** (for example, an alternative marketplace/web distribution under the DMA), the Storefront country code may be unavailable and returned as an **empty string**. In that case, use the [**alternative store installation detection**](https://developers.xsolla.com/sdk/mobilesdk/sdk/advanced/detectstore) guide to determine whether the app is running in an alternative distribution environment. 

To determine the current iOS storefront and control SDK functionality based on the region, use the following code snippets: 

```objectivec
[SKPaymentQueue loadCurrentStorefrontCountryCodeWithCompletion:^(NSString* _Nullable countryCode) {
    settings.enablePayments = countryCode && [countryCode isEqualToString:@"USA"];
    
    [[SKPaymentQueue defaultQueue] startWithSettings:settings];
}];
```

```swift
SKPaymentQueue.loadCurrentStorefrontCountryCode { countryCode in
    settings.enablePayments = countryCode == "USA"
    
    SKPaymentQueue.default().start(settings)
}
```
 

The `loadCurrentStorefrontCountryCode` method asynchronously retrieves the three-letter country code for the current Storefront. You can use this information to enable or disable SDK functionality for specific regions. 

Alternatively, you can use Apple's [Storefront ↗](https://developer.apple.com/documentation/storekit/storefront) ([https://developer.apple.com/documentation/storekit/storefront](https://developer.apple.com/documentation/storekit/storefront)) class directly, as demonstrated below: note

We recommend against using the Objective-C `SKStorefront` implementation as it performs synchronous loading that blocks the main thread. This can lead to UI freezes and poor user experience, as noted in Apple's [official documentation ↗](https://developer.apple.com/documentation/storekit/skpaymentqueue/storefront). 

```swift
let storefront = await Storefront.current   
let countryCode = storefront?.countryCode

settings.enablePayments = countryCode == "USA"
    
SKPaymentQueue.default().start(settings)
```
 

For more information, see the implementation in Apple's [official documentation ↗](https://developer.apple.com/documentation/storekit/skpaymentqueue/storefront) ([https://developer.apple.com/documentation/storekit/skpaymentqueue/storefront](https://developer.apple.com/documentation/storekit/skpaymentqueue/storefront)).

iOS Universal Links

https://developers.xsolla.com/sdk/mobilesdk/sdk/advanced/ios-universal-links

When using **External Browser** flows, returning from Pay Station to your app via a **custom URL scheme** can trigger an iOS confirmation dialog (for example, “Open this page in *App Name*?”). Using **Universal Links** (HTTPS) avoids this prompt and provides a smoother redirect back to the game. 

The SDK supports Universal Links for redirecting back to your app. 

## Requirements
 

To use Universal Links, you must configure your iOS project and your domain: 
 
- **Associated Domains**: enable and configure in Xcode. See Apple docs: [Supporting Associated Domains](https://developer.apple.com/documentation/xcode/supporting-associated-domains). 
- **Universal Links** support. See Apple docs: [Supporting universal links in your app](https://developer.apple.com/documentation/xcode/supporting-universal-links-in-your-app). 
 

## SDK handling (automatic vs manual)
 

If the SDK's automatic URL handling (swizzling) is enabled, Universal Links are handled automatically. 

If you disabled automatic URL handling, you must forward the Universal Link to the SDK so it can process the callback (same idea as manual deep link handling, but the URL comes from `NSUserActivity.webpageURL`). 

### AppDelegate
 

```objectivec
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary<UIApplicationLaunchOptionsKey, id> *)launchOptions {

    // Cold start (optional): app was launched by a Universal Link
    NSDictionary *userActivityDict = launchOptions[UIApplicationLaunchOptionsUserActivityDictionaryKey];
    NSUserActivity *activity = userActivityDict[UIApplicationLaunchOptionsUserActivityKey];
    NSURL *url = activity.webpageURL;
    if (url) {
        [[SKPaymentQueue defaultQueue] openWithUrl:url];
    }

    return YES;
}

// Warm start: app receives a Universal Link while running/suspended
- (BOOL)application:(UIApplication *)application
continueUserActivity:(NSUserActivity *)userActivity
  restorationHandler:(void (^)(NSArray<id<UIUserActivityRestoring>> * _Nullable))restorationHandler {

    NSURL *url = userActivity.webpageURL;
    if (!url) { return NO; }

    // Forward to SDK
    BOOL handledBySDK = [[SKPaymentQueue defaultQueue] openWithUrl:url];
    return handledBySDK;
}
```

```swift
func application(_ application: UIApplication,
                 didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

    // Cold start (optional): app was launched by a Universal Link
    if let userActivityDict = launchOptions?[.userActivityDictionary] as? [AnyHashable: Any],
       let activity = userActivityDict["UIApplicationLaunchOptionsUserActivityKey"] as? NSUserActivity,
       let url = activity.webpageURL {
        _ = SKPaymentQueue.default().open(url: url)
    }

    return true
}

// Warm start: app receives a Universal Link while running/suspended
func application(_ application: UIApplication,
                 continue userActivity: NSUserActivity,
                 restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {

    guard let url = userActivity.webpageURL else { return false }

    // Forward to SDK
    let handledBySDK = SKPaymentQueue.default().open(url: url)
    return handledBySDK
}
```
 

### SceneDelegate
 

```objectivec
// Cold start: app was launched by a Universal Link
- (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions {

    for (NSUserActivity *activity in connectionOptions.userActivities) {
        NSURL *url = activity.webpageURL;
        if (!url) { continue; }

        // Forward to SDK
        [[SKPaymentQueue defaultQueue] openWithUrl:url];
        break;
    }
}

// Warm start: app receives a Universal Link while running/suspended
- (void)scene:(UIScene *)scene continueUserActivity:(NSUserActivity *)userActivity {
    NSURL *url = userActivity.webpageURL;
    if (!url) { return; }

    // Forward to SDK
    [[SKPaymentQueue defaultQueue] openWithUrl:url];
}
```

```swift
// Cold start: app was launched by a Universal Link
func scene(_ scene: UIScene,
           willConnectTo session: UISceneSession,
           options connectionOptions: UIScene.ConnectionOptions) {

    if let userActivity = connectionOptions.userActivities.first,
       let url = userActivity.webpageURL {
        _ = SKPaymentQueue.default().open(url: url)
    }
}

// Warm start: app receives a Universal Link while running/suspended
func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
    guard let url = userActivity.webpageURL else { return }

    // Forward to SDK
    _ = SKPaymentQueue.default().open(url: url)
}
```

Trusted Web Activity (Android)

https://developers.xsolla.com/sdk/mobilesdk/sdk/advanced/trusted-web-activity

A [**Trusted Web Activity (TWA) ↗**](https://developer.chrome.com/docs/android/trusted-web-activity) ([https://developer.chrome.com/docs/android/trusted-web-activity](https://developer.chrome.com/docs/android/trusted-web-activity)) enables Android apps to display web content in full-screen mode using Chrome, without any browser UI elements. Unlike WebView, *TWAs* run in the user's browser context, allowing better performance, access to browser features (like push notifications and background sync), and shared cookies, permissions, and storage between the app and the browser. important

To enable *Trusted Web Activity*, contact your integration manager with the app’s package name and certificate hash. DID YOU KNOW?

*Trusted Web Activity* support is project-independent and applies at the app level — based on the package name and signing certificate only. 

### Configuring for Trusted Web Activity in the SDK
 

When configuring the SDK, supply an activity setting to the config like this: 

```java
final Config.Payments configPayments = Config.Payments.getDefault()
    .withActivity(Config.Payments.Activity.forTrustedWebActivity());
```
 

This ensures that the *Trusted Web Activity* will be used by the payment flow. warning

*Trusted Web Activity* availability depends on the device's system configuration, i.e. a compatible browser needs to be installed. If *TWA* support is not available, then the SDK falls back to one of the default activity types for payments (e.g. *Custom Tabs* or *WebView*, depending on the environment). 

An example of the *TWA* in action: [](https://developers.xsolla.com/sdk/assets/images/twa-1-737175ed3a44e80fb60131748dda4361.jpg) 

### (Optional) Orientation locking
 

*Trusted Web Activity* natively supports orientation locking when it's shown on the screen. This can be set up during the configuration step with these lines: 

```java
final Config.Payments configPayments = Config.Payments.getDefault()
    .withActivityOrientationLock(
        // orientation setting
    );
```
 

Orientation setting is one of the following values: 
 
- `Config.Payments.ACTIVITY_ORIENTATION_LOCK_PORTRAIT` - forces the *TWA* to be displayed in *portrait* mode. 
- `Config.Payments.ACTIVITY_ORIENTATION_LOCK_LANDSCAPE` - forces the *TWA* to be displayed in *landscape* mode. 
- `null` - disables the orientation locking. 
 

### (Optional) Adding a customizable splash screen
 

*Trusted Web Activity* also supports a customizable splash screen that's shown right before the *TWA* itself. To enable it, modify the configuration setting we added [earlier](https://developers.xsolla.com/sdk/mobilesdk/sdk/advanced/trusted-web-activity#configuring-for-trusted-web-activity-in-the-sdk): 

```java
final Config.Payments configPayments = Config.Payments.getDefault()
    .withActivity(Config.Payments.Activity.forTrustedWebActivityWithImage(
        // image settings go here
    ));
```
 

An example of the *TWA* splash screen: [](https://developers.xsolla.com/sdk/assets/images/twa-2-fd93e4a14c1a92ff7574a04d1a7abf15.jpg) 

#### Setting an image for the splash screen
 

Image can be supplied via two methods: a *drawable ID* and a filepath to the image: 
 
- A *drawable ID*: important The drawable needs to be added to the Android resource manually beforehand.  ```java final Config.Payments configPayments = Config.Payments.getDefault()     .withActivity(Config.Payments.Activity.forTrustedWebActivityWithImage(         Config.Payments.Activity.TrustedWebActivity.Image.forDrawableId(             <your_drawable_id>         )     )); ```  tip SVG format is also supported, but needs to be wrapped into a valid drawable XML. 
- A filepath: important The image file needs to be discoverable. Supports all major raster image formats.  ```java final Config.Payments configPayments = Config.Payments.getDefault()     .withActivity(Config.Payments.Activity.forTrustedWebActivityWithImage(         Config.Payments.Activity.TrustedWebActivity.Image.forFilepath(             <filepath_to_your_splash_screen_image>         )     )); ``` 
 
 note

If you're working with **Unity**, consider visiting this page for more details: [**Trusted Web Activity - Unity (Android)**](https://developers.xsolla.com/sdk/unity/sdk/advanced/trusted-web-activity).

Configuration

https://developers.xsolla.com/sdk/mobilesdk/sdk/configuration

Choose your platform: 
 
- [Android](https://developers.xsolla.com/sdk/mobilesdk/sdk/configuration/android) 
- [iOS](https://developers.xsolla.com/sdk/mobilesdk/sdk/configuration/ios)

Configuration - Android

https://developers.xsolla.com/sdk/mobilesdk/sdk/configuration/android

The **Xsolla Mobile SDK** for Android splits its configuration into four major settings subcategories, each represented by its own specific class: 
 
- [**Common**](https://developers.xsolla.com/sdk/mobilesdk/sdk/configuration/android#common) - settings that define SDK's generic properties. 
- [**Integration**](https://developers.xsolla.com/sdk/mobilesdk/sdk/configuration/android#integration) - settings that define how the SDK integrates with **Xsolla** services.    - [**Authentication**](https://developers.xsolla.com/sdk/mobilesdk/sdk/configuration/android#authentication) - settings that define how the SDK authenticates with **Xsolla** services.  - [**Events**](https://developers.xsolla.com/sdk/mobilesdk/sdk/configuration/android#xsolla-events-api) - settings that define how the SDK works with the **Xsolla Events API**. 
- [**Payments**](https://developers.xsolla.com/sdk/mobilesdk/sdk/configuration/android#payments) - settings that define **Xsolla Pay Station** behavior (appearance, redirects, etc). 
- [**Analytics**](https://developers.xsolla.com/sdk/mobilesdk/sdk/configuration/android#analytics) - settings that define the SDK's analytics related meta-data. 
 

To create a `Config` for SDK from all of the subcategories, use the following snippet: 

```java
final Config config = new Config(
    configCommon,
    configIntegration,
    configPayments,
    configAnalytics
);
```
 

## Common
 

Call `Config.Common.getDefault()` for default configuration. Here's an example best suited for testing: 

```java
final Config.Common configCommon = Config.Common.getDefault()
    .withSandboxEnabled(true)
    .withLogLevel(LogLevel.VERBOSE)
    .withDebugEnabled(true);
```
 

Below is a list of all settings supported by `Config.Common`: 

| Setting | Value | Description |
| --- | --- | --- |
| withSandboxEnabled | boolean | Are test payments enabled? If enabled, no real transactions. The project should not be in LIVE mode for the sandbox to work. |
| withLogLevel | enum LogLevel (VERBOSE, DEBUG, INFO, WARN, ERROR) | Controls the verbosity of the logging (use DEBUG if encountered any difficulties and would like to get more information on what's going under the hood). |
| withSkipNonExistentProductsOnQuery | boolean | Does queryProductDetailsAsync() safely ignore any missing requested products on query or fail right away? |
| withDeferredAuthenticationEnabled | boolean | Controls whether the authentication is postponed until after it really becomes needed for the SDK functionality, potentially speeding up the initialization flow. |
| withDebugEnabled | boolean | Enables the debug mode (more logging, profiling, etc). |
| withLocaleOverride | LocaleInfo | A specific locale to use for pricing and formatting for returned products and Pay Station (automatic if null). |
| withFallbackToDeviceLocale | boolean | Shall device's current locale be used if no override has been explicitly specified (otherwise it will be deduced automatically by Pay Station)? |
| withFetchPersonalizedProductsOnly | boolean | Return personalized products only (default true); set to false for generic catalogs. |
| withUseGeoLocaleOnProductQuery | boolean | Force use of geo locale when querying products (default false). |
| withRetryPolicies | RetryPolicies | Override retry behavior globally or per operation (authentication, create order, query products, etc.). |
 

### Retry policies and profiles
 

Retry policy overrides (per operation): 

| Setting | Value | Description |
| --- | --- | --- |
| withDefaultRetryProfileOverride | RetryProfile | Fallback profile applied if an operation-specific profile is not provided. |
| withAuthenticateRetryProfileOverride | RetryProfile | Overrides retries for authentication. |
| withCreateOrderRetryProfileOverride | RetryProfile | Overrides retries for order creation. |
| withPendingOrderStatusQueryRetryProfileOverride | RetryProfile | Overrides retries when polling pending order status. |
| withQueryProductsRetryProfileOverride | RetryProfile | Overrides retries for product list queries. |
| withQueryPurchasesRetryProfileOverride | RetryProfile | Overrides retries for purchase list queries. |
| withConsumePurchasesRetryProfileOverride | RetryProfile | Overrides retries for purchase consumption. |
 

Use `RetryProfile` variants to build overrides for specific operations or a global default: 

Fixed delay between attempts.

```java
final RetryProfile retryProfile = RetryProfile.uniform(
    PositiveInteger.create(6).getRightOrThrow(),
    PositiveInteger.create(1000).getRightOrThrow()
);
```

| Argument | Description |
| --- | --- |
| maxNumAttempts | Positive integer; total number of attempts allowed (initial try + retries). |
| intervalMillis | Fixed delay in milliseconds between attempts. |

Delay grows exponentially from the base interval, with optional caps and jitter.

```java
final RetryProfile retryProfile = RetryProfile.exponentialBackout(
    PositiveInteger.create(5).getRightOrThrow(),
    PositiveInteger.create(500).getRightOrThrow(),
    PositiveInteger.create(4000).getRightOrThrow(),
    null // optional jitter cap
);
```

| Argument | Description |
| --- | --- |
| maxNumAttempts | Positive integer; total number of attempts allowed (initial try + retries). |
| baseIntervalMillis | Initial delay in milliseconds; subsequent attempts grow exponentially from this base. |
| maxIntervalMillis (optional) | Cap for the backoff delay in milliseconds; if omitted, delay can grow until attempts are exhausted. |
| maxRandomExtraDelayMillis (optional) | Adds up to this many milliseconds of jitter to each backoff delay; null to disable jitter. |
 

```java
final RetryPolicies retryPolicies = RetryPolicies.getDefault()
    .withDefaultRetryProfileOverride(retryProfile)      // pick from a tab above
    .withAuthenticateRetryProfileOverride(retryProfile);

final Config.Common configCommon = Config.Common.getDefault()
    .withRetryPolicies(retryPolicies);
```
 

## Integration
 

### Authentication
 

```java
final Config.Integration configIntegration = Config.Integration.forXsolla(
    Config.Integration.Xsolla.Authentication.forAutoJWT(
        ProjectId.parse(<your_project_id>).getRightOrThrow(),
        LoginUuid.parse(<your_login_id>).getRightOrThrow()
    )
);
```

Authenticates using a custom login **JWT** (must be valid). Useful for situations when JWT is generated or acquired externally (e.g. using the app's backend or services like Xsolla Login, etc) and fed into the SDK.

```java
final Config.Integration configIntegration = Config.Integration.forXsolla(
    Config.Integration.Xsolla.Authentication.forJWT(
        ProjectId.parse(<your_project_id>).getRightOrThrow(),
        JWT.parse(<jwt>).getRightOrThrow()
    )
);
```

**With manual JWT token updating:**

If your JWT tokens expire and need to be refreshed after the billing client is connected, use the `JWTRefresher` parameter:

```java
final JWTRefresher jwtRefresher = new JWTRefresher();

final Config.Integration configIntegration = Config.Integration.forXsolla(
    Config.Integration.Xsolla.Authentication.forJWT(
        ProjectId.parse(<your_project_id>).getRightOrThrow(),
        JWT.parse(<jwt>).getRightOrThrow(),
        jwtRefresher
    )
);

// Later, when you need to update the token:
jwtRefresher.update(JWT.parse(<new_jwt>).getRightOrThrow());
```

Authenticates using OAuth2.

```java
final Config.Integration configIntegration = Config.Integration.forXsolla(
    Config.Integration.Xsolla.Authentication.forOAuth2(
        ProjectId.parse(<your_project_id>).getRightOrThrow(),
        LoginUuid.parse(<your_login_id>).getRightOrThrow(),
        OAuth2ClientId.parse(<oauth2_client_id>).getRightOrThrow()
    )
);
```

Authenticates using a social access token received from a 3rd-party provider.

First of all, for this approach to work you'll need to figure out which social provider you're going to work with and whether it's supported by Xsolla authentication services:All supported social providers names
 
- google 
- facebook 
- twitter 
- linkedin 
- naver 
- baidu 
- amazon 
- apple 
- battlenet 
- discord 
- github 
- kakao 
- mailru 
- microsoft 
- msn 
- ok 
- paypal 
- psn 
- qq 
- reddit 
- steam 
- twitch 
- vimeo 
- vk 
- wechat 
- weibo 
- yahoo 
- yandex 
- youtube 
- xbox 
- babka 
- epicgames 

`LoginUtils.SocialProvider` holds a set of predefined social provider values for convenience. To get a matching instance from a string, use `LoginUtils.SocialProvider.parse("<social-provider-name>")`.

```java
final LoginUtils.SocialProvider socialProvider = LoginUtils.SocialProvider.EPICGAMES;
final String socialAccessToken = "<social-access-token>";
```

Overall, there are two paths for this approach:
 
- **With OAuth2 (recommended)**  ```java final Config.Integration configIntegration = Config.Integration.forXsolla(     Config.Integration.Xsolla.Authentication.forSocial(         ProjectId.parse(<your_project_id>).getRightOrThrow(),         LoginUuid.parse(<your_login_id>).getRightOrThrow(),         OAuth2ClientId.parse(<oauth2_client_id>).getRightOrThrow(),         socialProvider,         socialAccessToken     ) ); ``` 
- **Without OAuth2**  ```java final Config.Integration configIntegration = Config.Integration.forXsolla(     Config.Integration.Xsolla.Authentication.forSocial(         ProjectId.parse(<your_project_id>).getRightOrThrow(),         LoginUuid.parse(<your_login_id>).getRightOrThrow(),         socialProvider,         socialAccessToken     ) ); ``` 
 

Information on how to obtain **Product ID** and **Login ID** can be found [**here**](https://developers.xsolla.com/sdk/publisher-account/getids). 

You can also supply a [**Xsolla Login ↗**](https://xsolla.com/login) ([https://xsolla.com/login](https://xsolla.com/login)) token via the [**Custom Login Token**](https://developers.xsolla.com/sdk/mobilesdk/sdk/configuration/android?auth-method=xsolla-jwt-custom#authentication) approach. 

### Xsolla Events API
 

The **Xsolla Events API** serves as an alternative to webhook-based integrations, where the integrator’s backend usually handles most of the logic (purchase validation, awarding, etc.). Instead of sending webhooks directly, the API intercepts them and stores their payloads in a remote queue managed by Xsolla. This approach enables serverless integrations for projects that lack backend capacity or don’t require one, yet still prefer handling the entire transaction flow directly from the app. 

To use it, contact your **integration manager** to have it enabled for your project in *Publisher Account*. 

Opting for the ***Xsolla Events API*** **disables** your project’s **webhooks** — you **cannot use both simultaneously**. 

Below is the minimal events configuration required to enable the functionality in SDK: 

```java
final Config.Integration.Xsolla.Events configEvents =
    Config.Integration.Events
        .newBuilder()
        .build()
```
 

If you would like to listen to incoming events, assign an optional custom event listener using `Events.Builder.withEventListener` method: 

```java
final Config.Integration.Xsolla.Events configEvents =
    Config.Integration.Events
        .newBuilder()
        .withEventListener(event -> {
            // Don't consume the event (mark as processed on the queue).
            return false;
        })
        .build()
```
 

Now, assign the events config to the integration config: 

```java
final Config.Integration configIntegration =
    Config.Integration
        .forXsolla(
            // ...your authentication configuration
        )
        .withEvents(configEvents);
```
 

Below is the list of advanced *Events API* related settings: 

| Setting | Value | Description |
| --- | --- | --- |
| withDefaultQueryPeriodMillis | Long | Amount of time between the consecutive event queries in milliseconds (-1 if disabled, null for default value; default - 5000ms). |
| withQueryProfileIfExpectingPurchases | EventQueryProfile | A specific query profile to apply when expecting a purchase (e.g. just completed a purchasing flow and waiting for a confirmation and want to do an accelerated querying for a limited amount of time; off by default). |
| withExternalFreePurchaseHandlingEnabled | boolean | Should external purchase events that originated by claiming a free item be handled? (off by default) |
 

## Payments
 

Call `Config.Payments.getDefault()` for default configuration. Here's an example that forces the close button to appear in **Xsolla Pay Station**: 

```java
final Config.Payments configPayments = Config.Payments.getDefault()
    .withCloseButton(Config.Payments.CloseButton.show());
```
 

Below is a list of all settings supported by `Config.Payments`: 

| Setting | Value | Description |
| --- | --- | --- |
| withRedirectUrl | URL | URL to redirect to when returning from Xsolla Pay Station. |
| withRedirectTimeoutSecs | Integer | Amount of time before Xsolla Pay Station automatically closes (if null, then uses the default value). |
| withRedirectButtonText | String | Text on the 'return' link in Xsolla Pay Station. |
| withRedirectCondition | Integer | Determines under what conditions automatic closure takes place (REDIRECT_CONDITION_DISABLED, REDIRECT_CONDITION_SUCCESS, REDIRECT_CONDITION_SUCCESS_OR_CANCELED, REDIRECT_CONDITION_ANY). |
| withTheme | Config.Payments.Theme | Defines the visual aspect of Xsolla Pay Station (Theme.Light, Theme.Dark, Theme.Custom). |
| withCloseButton | Config.Payments.CloseButton | Defines the state of the close button in Xsolla Pay Station (not to be confused with the 'return' link; CloseButton.Hide, CloseButton.Show). |
| withActivity | Config.Payments.Activity | Determines which approach is used for displaying Xsolla Pay Station (Activity.System, Activity.WebView, Activity.CustomTabs, Activity.TrustedWebActivity - for TWA see more information here). |
| withActivityOrientationLock | Integer | Specifies a desired screen orientation to lock to, when displaying Xsolla Pay Station (availability depends on the chosen Config.Payments.Activity; ACTIVITY_ORIENTATION_LOCK_PORTRAIT, ACTIVITY_ORIENTATION_LOCK_LANDSCAPE). |
| withBuyButtonSolutionEnabled | boolean | Enables/disables the Buy Button Solution's state (see here for more information) |
| withEmailCollectionConsentOptInEnabled | boolean | Enable opt-in for collecting the user's email as part of the payment flow (for example, newsletter consent). |
| withInvokePurchasesUpdatedForMissingOrderId | boolean | Invoke purchase callback even if order ID is missing (for order-id-less flows). |
| withShowLogoEnabled | Boolean | Controls visibility of the Xsolla logo in Pay Station. Pass true to show, false to hide, or null for default behavior. |
| withEventListeners | EventListeners | Register listeners for Pay Station navigation events (page opened, loaded, closed). Use EventListeners builder to configure callbacks. |
| withDomainOverrideConfig | DomainOverrideConfig | Override default Pay Station domains for production and sandbox environments. Use DomainOverrideConfig builder to specify custom domains. |
| withCustomUserId | String | A custom user identifier to associate with the payment session in Pay Station (null to omit). |
| withQueryCancellationReasonEnabled | boolean | Whether to query for the reason a payment was cancelled when the purchase flow ends without completion. |
| withRedirectAppRelaunch | boolean | When the app is killed during a payment flow in an external browser, enables the return-to-app button in Pay Station to relaunch the app instead of silently failing. Off by default. |
 

### Custom Tabs / Trusted Web Activity provider controls
 

For Custom Tabs and Trusted Web Activity flows, control which browser providers are allowed and their priority: 

```java
final Config.Payments configPayments = Config.Payments.getDefault()
    .withActivity(Config.Payments.Activity.forCustomTabs())
    .withEmailCollectionConsentOptInEnabled(true)
    .withActivity(
        Config.Payments.Activity.forCustomTabs()
            .withBlacklistedProviders(Set.of("com.android.chrome"))
            .withInternalProviderBlacklistEnabled(true)
            .withBlacklistedTWAProviders(Set.of("com.vendor.browser"))
            .withInternalTWAProviderBlacklistEnabled(true)
            .withProviderWeights(Map.of("com.google.android.apps.chrome", 2.0f))
            .withFallbackToFirstAvailableTWAProvider(true)
    );
```
 

### Buy Button Solution
 

The *Buy Button Solution* provides a complete, native payment experience with minimal integration effort. This approach handles the entire payment flow within your app using Xsolla's optimized UI components. 

**When to use Buy Button Solution:** 
 
- You want a fully native integration 
- You need 1,000+ payment methods out of the box 
- You prefer Xsolla to manage the payment UI 
- You want automatic regional compliance 
 

To enable the *Buy Button Solution* for your payments (works with [**default flows**](https://developers.xsolla.com/sdk/mobilesdk/sdk/purchase/android/?purchase-method=standard#starting-purchasing-flow) only), use the snippet below: 

```java
final Config.Payments configPayments =
    Config.Payments.getDefault()
        .withBuyButtonSolutionEnabled(true);
```
 

### Event Listeners
 

`EventListeners` is an optional, immutable callback container that lets you hook into **Pay Station** UI lifecycle events without subclassing or modifying the billing flow. Attach it to your payments config via `.withEventListeners(...)`. 

All callbacks are dispatched on the **main thread** — safe to update UI directly. 

| Interface | Method | Fires when |
| --- | --- | --- |
| IOpenedListener | onOpened() | Pay Station activity has been started. |
| ILoadedListener | onLoaded() | WebView loading finishes (fired for successful load, failed load, and aborted load alike — the SDK does not distinguish between these outcomes at the EventListeners level). |
| ICompletedListener | onCompleted() | Payment succeeded and onPurchasesUpdated was called with OK and a non-empty purchases list. |
| ICancelledListener | onCancelled() | Flow ended with a USER_CANCELED response code (fires in addition to onPurchasesUpdated(USER_CANCELED, null) — does not replace it). |
 info

For the SKU-based flow, `onCompleted` only fires if the purchases list is non-empty. Orders with no valid order ID on Xsolla's side are silently skipped. 

Build an `EventListeners` instance using the copy-on-write builder — each `with*` call returns a new instance. All listeners are optional; unset listeners are simply not called. Passing `null` to `.withEventListeners(...)` disables all listeners. 

```java
final EventListeners listeners = EventListeners.getDefault()
    .withOpenedListener(() -> { /* Pay Station opened */ })
    .withLoadedListener(() -> { /* Pay Station loaded */ })
    .withCompletedListener(() -> { /* payment completed */ })
    .withCancelledListener(() -> { /* payment cancelled */ });

final Config.Payments configPayments = Config.Payments.getDefault()
    .withEventListeners(listeners);
```
 

## Analytics
 

Analytics configuration allows you to pass metadata to the SDK that Xsolla can use for internal analytics and reporting. While not strictly required, it can help improve visibility into app performance, user behavior, and transaction flows — and may assist in diagnosing or resolving issues more efficiently. 

```java
final Config.Analytics configAnalytics =
    Config.Analytics.getDefault()
        .withGameEngine("<game-engine-identifier>")
        .withGameEngineVersion("<game-engine-version>">);
```
 

Below is a list of all settings supported by `Config.Analytics`: 

| Setting | Value | Description |
| --- | --- | --- |
| withGameEngine | String | A custom game engine identifier or null for defaults. |
| withGameEngineVersion | String | A custom game engine version or null for defaults. |
 

Before configuring analytics in the SDK, contact your **integration manager** to confirm whether it's viable for your integration. 

## Testing purchase flow in sandbox mode
 

See [**here**](https://developers.xsolla.com/sdk/mobilesdk/sdk/testing/android) for more details on how to enable the sandbox mode and avoid spending real money while testing the integration.

Configuration - iOS

https://developers.xsolla.com/sdk/mobilesdk/sdk/configuration/ios

This guide covers how to configure the **Xsolla Mobile SDK** for iOS to enable payment processing in your mobile application. The SDK provides multiple integration approaches, from full native implementations to web-based solutions, allowing you to choose the method that best fits your app's requirements and development timeline. 

## Basic SDK Configuration
 

The *Xsolla Mobile SDK* requires configuration with your project credentials from the [**Xsolla Publisher Account ↗**](https://publisher.xsolla.com/) ([https://publisher.xsolla.com/](https://publisher.xsolla.com/)). These settings control how the SDK connects to Xsolla's services and handles payment flows. 

### Required Configuration Parameters
 
 
- **Project ID**: Your Xsolla project identifier (numeric value) 
- **Login Project ID**: Your Xsolla Login project identifier (UUID string) 
- **Platform**: Specifies the target platform (typically `.standalone` for mobile apps) 
 

For initial development and testing, you can use the example credentials shown below: 

```objectivec
#import <XsollaMobileSDK/StoreKitWrapper.h>

// Initialize SDK settings with your project credentials
SKPaymentSettings* settings = [[SKPaymentSettings alloc] initWithProjectId: 301871
                                                    loginProjectId:@"dfcb133b-6d0b-4937-b8d2-c4f4d58fb53a"
                                                    platform: SKPaymentPlatformStandalone
                                                    paystationUIThemeId: SKPaystationThemeDark
                                                    paystationUISize: SKPaystationSizeMedium];

// Enable sandbox mode for testing (disable for production)
settings.useSandbox = YES;

// Enable the SDK functionality (set to NO to completely disable payments)
settings.enablePayments = YES;

// Configure external browser behavior (see compliance section below)
settings.openExternalBrowser = YES; // Set based on your compliance requirements

// Initialize the payment queue with your settings
SKPaymentQueue* queue = [SKPaymentQueue defaultQueue];
[queue startWithSettings: settings]; 
[queue addTransactionObserver: self];
```

```swift
import XsollaMobileSDK

// Initialize SDK settings with your project credentials
let settings = SKPaymentSettings(projectId: 301871,
                                 loginProjectId: "dfcb133b-6d0b-4937-b8d2-c4f4d58fb53a",
                                 platform: .standalone)

// Enable sandbox mode for testing (disable for production)
settings.useSandbox = true

// Enable the SDK functionality (set to false to completely disable payments)
settings.enablePayments = true

// Configure external browser behavior (see compliance section below)
settings.openExternalBrowser = true // Set based on your compliance requirements

// Initialize the payment queue with your settings
SKPaymentQueue.default().start(settings)
SKPaymentQueue.default().add(self)
```
 

### Regional Compliance Configuration
 

The `openExternalBrowser` setting is crucial for compliance with regional regulations and platform policies. Configure this setting based on your target markets: **United States Compliance**

For compliance with U.S. regulations regarding redirecting players to web payment pages:

```objectivec
settings.openExternalBrowser = YES; 
```
**European Union Compliance (DMA)**

For compliance with EU Digital Markets Act (DMA) requirements for in-app payments:

```objectivec
settings.openExternalBrowser = NO; 
```
**United States Compliance**

For compliance with U.S. regulations regarding redirecting players to web payment pages:

```swift
settings.openExternalBrowser = true
```
**European Union Compliance (DMA)**

For compliance with EU Digital Markets Act (DMA) requirements for in-app payments:

```swift
settings.openExternalBrowser = false
```
 

For more advanced compliance scenarios, you can programmatically detect the user's region and distribution channel to automatically configure the appropriate settings. Learn how to [**detect iOS storefront**](https://developers.xsolla.com/sdk/mobilesdk/sdk/advanced/ios-storefront) for implementing U.S.-specific regulations, or [**detect alternative app distribution status**](https://developers.xsolla.com/sdk/mobilesdk/sdk/advanced/detectstore) for enabling EU-specific compliance flows based on the app's installation source. 

### Essential Configuration Notes
 
 
- **Enable Payments**: The SDK must be explicitly enabled by setting `enablePayments` to `true`. This allows you to completely disable payment functionality if needed without removing the SDK integration. 
- **Sandbox Mode**: Always use `useSandbox = true` during development and testing to avoid processing real transactions. 
- **Transaction Observer**: Don't forget to add your class as a transaction observer to receive payment completion callbacks. 
 

### Default Payment Method
 

You can set a default payment method for Xsolla Pay Station. Configure it globally in `settings` (applies to all purchases) or per purchase. The value is the Pay Station payment method identifier (for example, `3175` is Apple Pay). 

```objectivec
// Global default (set before starting the SDK)
settings.paymentMethodId = [NSNumber numberWithInt:3175]; // 3175 = Apple Pay

// Per purchase override
SKMutablePayment *payment = [SKMutablePayment paymentWithProduct:product];
payment.paymentMethodId = [NSNumber numberWithInt:3175]; // 3175 = Apple Pay
[[SKPaymentQueue defaultQueue] addPayment:payment];
```

```swift
// Global default (set before starting the SDK)
settings.paymentMethod = 3175 // 3175 = Apple Pay

// Per purchase override
let payment = SKPayment(product: product)
payment.paymentMethod = 3175 // 3175 = Apple Pay
SKPaymentQueue.default().add(payment)
```
 note

If both are set, the per-purchase value overrides the global default for that transaction. 

### Purchase Restoration Settings
 

By default, the SDK automatically handles local purchase restoration to ensure users can recover their purchases when reinstalling your app or when the app crashes or goes out of memory during payment processing. However, this behavior should be adjusted based on your backend integration architecture. 

Alternatively, if you prefer a serverless approach without maintaining webhooks, you can enable the **Xsolla Events API** for purchase restoration (see the subsection below) while keeping local purchase restoration enabled. 

For webhook-based integrations where your backend receives and processes purchase notifications directly from Xsolla, disable local restoration to prevent conflicts with server-side transaction management: 

```objectivec
// Disable local purchase restoration for webhook-based integrations
settings.useLocalPurchaseRestoration = NO;
```

```swift
// Disable local purchase restoration for webhook-based integrations
settings.useLocalPurchaseRestoration = false
```
 warning

**When to disable local purchase restoration:**
 
- Your backend uses webhooks to receive purchase notifications from Xsolla 
- You handle transaction lifecycle management on your server 
- You want to avoid duplicate purchase processing between local and server-side systems 

**Important limitations:**
 
- Restored purchases cannot be validated through the standard validation flow 
- Transaction finishing must be handled by your backend when webhooks are used 
- If local purchase restoration is disabled, the SDK will not perform restoration via either local restoration or the Events API 
 

This setting directly impacts the `finishTransaction(_ transaction: SKPaymentTransaction)` method behavior described in the [**Purchase Flow guide**](https://developers.xsolla.com/sdk/mobilesdk/sdk/purchase/ios). When local restoration is disabled, your backend becomes responsible for managing the complete transaction lifecycle through webhook notifications. Note that with local restoration disabled, unfinished transactions will not reappear on app relaunch, making proper backend handling essential. 

#### Using Xsolla Events API for Restoration
 

The Xsolla Events API serves as an alternative to webhook-based integrations, where the integrator’s backend usually handles most of the logic (purchase validation, awarding, etc.). Instead of sending webhooks directly, the API intercepts them and stores their payloads in a remote queue managed by Xsolla. This approach enables serverless integrations for projects that lack backend capacity or don't require one, yet still prefer handling the entire transaction flow directly from the app. 

Opting for the ***Xsolla Events API*** **disables** your project's **webhooks** — you **cannot use both simultaneously**. **Enabling Xsolla Events API in Publisher Account**

Follow these steps to enable the Events API:
 
- Open your Publisher Account. 
- In the left sidebar, select the project you want to enable the Events API for. 
- From the left sidebar, expand the Payments menu and select Webhooks. 
- On the Webhooks page, click Webhooks on the right.  ![PublisherAccountEventsAPI] 
- Select Use API. 

Your project is now configured to use the Xsolla Events API, and the SDK can leverage this configuration.tip

To turn the Events API mode off, follow the steps above, but select Use Webhooks this time. 

##### Enabling Events in the iOS SDK
 

Enable Events-based purchase restoration in the SDK configuration: 

```objectivec
// Enable Events API for purchase restoration
settings.useEventsApiForPurchaseRestoration = YES;
```

```swift
// Enable Events API for purchase restoration
settings.useEventsApiForPurchaseRestoration = true
```
 info

For additional information, see the [**webhooks page**](https://developers.xsolla.com/sdk/publisher-account/webhooks#enabling-xsolla-events-api). note

Events-based restoration requires `settings.useLocalPurchaseRestoration = true`. If you disable local purchase restoration, the SDK will not query or use the Events API for restoration. 

## Authentication with Xsolla Login
 

If your app uses Xsolla Login for user authentication, you can provide the login token to the SDK for seamless integration with Xsolla's payment services. 

### Setting the Login Token
 

Configure the login token after initializing your settings but before starting the payment queue: 

```objectivec
// After initializing settings, set the Xsolla Login token
settings.customLoginToken = @"your_xsolla_login_token";

// Then start the SDK
SKPaymentQueue* queue = [SKPaymentQueue defaultQueue];
[queue startWithSettings: settings];
```

```swift
// After initializing settings, set the Xsolla Login token
settings.customLoginToken = "your_xsolla_login_token"

// Then start the SDK
SKPaymentQueue.default().start(settings)
```
 note

The Xsolla Login token should be obtained from your [**Xsolla Login ↗**](https://xsolla.com/login) ([https://xsolla.com/login](https://xsolla.com/login)) integration. Ensure the token is valid and set before starting the payment queue to enable authenticated payment flows. 

## Testing in Sandbox Mode
 

During development, always enable sandbox mode to test your payment integration without processing real transactions or charges. The sandbox environment simulates the complete payment flow while using test data. 

### Enabling Sandbox Mode
 

```objectivec
settings.useSandbox = YES; // Objective-C
settings.useSandbox = true // Swift
```
 tip

For comprehensive testing guidance, including test card numbers and payment scenarios, see our [**iOS Testing Guide**](https://developers.xsolla.com/sdk/mobilesdk/sdk/testing/ios). This covers avoiding real charges, testing different payment methods, and validating your integration. 

## Payment Integration Options
 

The Xsolla Mobile SDK offers multiple payment integration approaches to suit different development needs and timelines. 

### Buy Button Solution (Recommended)
 

The Buy Button Solution provides a complete, native payment experience with minimal integration effort. This approach handles the entire payment flow within your app using Xsolla's optimized UI components. 

For more details (including Web Shop link-outs vs Pay Station and platform notes), see [**Buy Button - iOS**](https://developers.xsolla.com/sdk/mobilesdk/buy-button/ios). 

**When to use Buy Button Solution:** 
 
- You want a quick, native payment integration 
- You prefer Xsolla to handle payment UI and optimization 
- You need support for multiple payment methods out of the box 
- You want automatic compliance with regional payment regulations 
 

```objectivec
// Enable Buy Button solution
settings.useBuyButtonSolution = YES; 
```

```swift
// Enable Buy Button solution
settings.useBuyButtonSolution = true
```
 

### Web Shop Link-outs
 

The Web Shop approach redirects users to your existing Xsolla Web Shop in a browser, then returns them to your app after purchase completion. This method is ideal for gradual migration or when you want to maintain consistency with existing web-based payment flows. 

**When to use Web Shop link-outs:** 
 
- You already have an established Xsolla Web Shop 
- You want to test Xsolla integration with minimal code changes 
- You need to maintain payment consistency across multiple platforms 
- You're implementing a phased migration from web to mobile payments 
 caution

In Web Shop link-out mode, the SDK does not execute its native in-app payment flow. It only opens the Web Shop and immediately cancels any SDK purchase callbacks. This mode is intended for minimal code changes: handle fulfillment and post-payment updates via your Web Shop processes (for example, webhooks or server-side logic), not via SDK purchase callbacks. 

#### Configuring Web Shop Integration
 

Use the helper method to configure the Web Shop redirect flow: 

```objectivec
[settings setupBuyButtonWebShopWithUrl:[NSURL URLWithString:@"https://yourwebshop.xsolla.site"] 
                                userId:@"<USER_ID>" 
                             returnUrl:@"yourgame://" 
                          openExternally:YES]; 
```

```swift
settings.setupBuyButtonWebShop(url: URL(string: "https://yourwebshop.xsolla.site")!, 
                               userId: "<USER_ID>", 
                               returnUrl: "yourgame://", 
                               openExternally: true)
```
 

**Parameter explanation:** 
 
- `url`: The URL of your Xsolla Web Shop where users complete purchases 
- `userId`: A unique identifier for the user in your system, used for purchase attribution and user tracking 
- `returnUrl`: The deep link URL scheme that brings users back to your app after purchase completion 
- `openExternally`: Whether to open the Web Shop in an external browser (recommended for compliance and better user experience) 
 

#### Initiating Web Shop Purchases
 

After configuration, initiate purchases by calling the purchase method with your product SKU: 

```objectivec
// Attempt to purchase an item via Web Shop
BOOL handledExternally = [[SKPaymentQueue defaultQueue] purchaseWithSKU:@"com.yourgame.item"];

if (handledExternally) {
    // Web Shop opened successfully, update UI to reflect the redirect
    NSLog(@"Purchase redirected to Web Shop");
    // Optionally show a loading indicator or message to the user
}
```

```swift
// Attempt to purchase an item via Web Shop
let handledExternally: Bool = SKPaymentQueue.default().purchase(withSKU: "com.yourgame.item")

if handledExternally {
    // Web Shop opened successfully, update UI to reflect the redirect
    print("Purchase redirected to Web Shop")
    // Optionally show a loading indicator or message to the user
}
```
 **Backend Integration**

With the Web Shop approach, you don't need to fetch product catalogs through the mobile SDK (though you can for display purposes). Purchase fulfillment should be handled on your backend using the same webhooks and processes you use for standard Web Shop transactions, ensuring consistency across your payment infrastructure. 

## Deep Links Configuration
 

Deep links are essential for completing payment flows when users are redirected to external payment pages and need to return to your app. The SDK provides both automatic and manual deep link handling options. tip

The SDK also supports **Universal Links** (HTTPS), which can be used instead of custom URL schemes (and avoid the “Open in *App Name*?” prompt when returning from an external browser). See [**iOS Universal Links**](https://developers.xsolla.com/sdk/mobilesdk/sdk/advanced/ios-universal-links). 

### Setting Up URL Scheme
 

Before configuring deep link handling, you must set up a custom URL scheme for your app. This URL scheme enables external sources (like payment pages) to redirect users back to your application. 

**To configure your URL scheme:** 
 
- Select your project in the Xcode project navigator 
- Select your app target 
- Open the **Info** tab 
- Scroll down to **URL Types** and click the ➕ button 
- Set the **URL Schemes** field to `$(PRODUCT_BUNDLE_IDENTIFIER)` 
 

![URL Scheme Configuration] tip

Using `$(PRODUCT_BUNDLE_IDENTIFIER)` ensures your URL scheme matches your app's bundle identifier, which guarantees uniqueness and prevents conflicts with other apps. 

### Automatic Deep Link Handling (Default)
 

By default, the Xsolla Mobile SDK automatically handles deep links using method swizzling—a technique that intercepts and modifies iOS method calls at runtime. This means the SDK automatically captures incoming URLs and processes them without requiring additional code from you. 

**Benefits of automatic handling:** 
 
- Zero additional code required 
- Works seamlessly with most app architectures 
- Handles all Xsolla-related URLs automatically 
- Maintains compatibility with your existing URL handling 
 

The automatic handling works out of the box once you've configured your URL scheme as described above. 

### Manual Deep Link Handling
 

In some scenarios, you may need to disable automatic handling and implement manual deep link processing. This is typically necessary when: 
 
- Your app has existing custom URL handling logic that conflicts with method swizzling 
- You're using a framework (like React Native or Unity) that doesn't work well with method swizzling 
- You need granular control over the deep link processing flow 
- You're experiencing issues with automatic handling in your specific setup 
- Your app architecture requires explicit URL routing 
 

#### Implementing Manual Handling
 

**Step 1: Disable Automatic Handling** 

Before starting the SDK, disable automatic URL handling: 

```objectivec
// Disable automatic deep link handling before starting SDK
settings.swizzleUrlHandling = NO;

// Then start the SDK as usual
SKPaymentQueue* queue = [SKPaymentQueue defaultQueue];
[queue startWithSettings: settings];
```

```swift
// Disable automatic deep link handling before starting SDK
settings.swizzleUrlHandling = false

// Then start the SDK as usual
SKPaymentQueue.default().start(settings)
```
 

**Step 2: Implement Manual URL Handling** 

Add URL handling to your `AppDelegate` or `SceneDelegate`. The SDK's `openWithUrl:` method returns a boolean indicating whether it recognized and handled the URL: 

```objectivec
// In your AppDelegate.m
- (BOOL)application:(UIApplication *)app 
            openURL:(NSURL *)url 
            options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
    
    // First, try to handle the URL with Xsolla SDK
    BOOL handledBySDK = [[SKPaymentQueue defaultQueue] openWithUrl:url];
    
    if (handledBySDK) {
        // URL was recognized and handled by Xsolla SDK
        return YES;
    }
    
    // Handle other URLs specific to your app
    // Add your custom URL handling logic here
    return [self handleCustomURL:url];
}

// Alternative: In your SceneDelegate.m (for iOS 13+)
- (void)scene:(UIScene *)scene openURLContexts:(NSSet<UIOpenURLContext *> *)URLContexts {
    for (UIOpenURLContext *context in URLContexts) {
        NSURL *url = context.URL;
        
        // Try to handle with Xsolla SDK first
        BOOL handledBySDK = [[SKPaymentQueue defaultQueue] openWithUrl:url];
        
        if (!handledBySDK) {
            // Handle other URLs specific to your app
            [self handleCustomURL:url];
        }
    }
}
```

```swift
// In your AppDelegate.swift
func application(_ app: UIApplication, 
                open url: URL, 
                options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
    
    // First, try to handle the URL with Xsolla SDK
    let handledBySDK = SKPaymentQueue.default().open(url: url)
    
    if handledBySDK {
        // URL was recognized and handled by Xsolla SDK
        return true
    }
    
    // Handle other URLs specific to your app
    return handleCustomURL(url)
}

// Alternative: In your SceneDelegate.swift (for iOS 13+)
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
    for urlContext in URLContexts {
        let url = urlContext.url
        
        // Try to handle with Xsolla SDK first
        let handledBySDK = SKPaymentQueue.default().open(url: url)
        
        if !handledBySDK {
            // Handle other URLs specific to your app
            handleCustomURL(url)
        }
    }
}
```
 **Return Value Usage**

The `openWithUrl:` method returns `true`/`YES` if the URL was recognized as a Xsolla payment callback and processed by the SDK. This allows you to implement fallback logic for URLs that aren't related to Xsolla payments, ensuring your app can handle all types of deep links appropriately. 

**Step 3: Handle Pay Station Screen Orientation (Optional)** 

If your app supports a different orientation than you want for the Pay Station screen, you need to implement additional orientation handling. For example, if your game supports only landscape orientation but you want the Pay Station to appear in portrait mode, add the following method to your `AppDelegate`: 

```objectivec
// In your AppDelegate.m
- (UIInterfaceOrientationMask)application:(UIApplication *)application 
                 supportedInterfaceOrientationsForWindow:(UIWindow *)window {
    
    // Since the app may not support portrait, we must ensure that for the UIWindow 
    // that SDK creates it returns correct value for portrait support
    UIInterfaceOrientationMask mask = [[SKPaymentQueue defaultQueue] supportedInterfaceOrientationsForWindow:window];
    
    if (mask != 0) {
        return mask;
    }
    
    // Fall back to default app orientation support
    return [application supportedInterfaceOrientationsForWindow:window];
}
```

```swift
// In your AppDelegate.swift
func application(_ application: UIApplication, 
                supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
    
    // Since the app may not support portrait, we must ensure that for the UIWindow 
    // that SDK creates it returns correct value for portrait support
    let mask = SKPaymentQueue.default().supportedInterfaceOrientationsFor(window: window)
    
    if !mask.isEmpty {
        return mask
    }
    
    // Fall back to default app orientation support
    return application.supportedInterfaceOrientations(for: window)
}
```
 

This method allows the SDK to specify orientation requirements for its own windows (like the Pay Station screen) while maintaining your app's normal orientation restrictions for other windows. Note that this is automatically handled if automatic deep link handling is enabled. **Choosing the Right Approach**

If you're unsure whether you need manual deep link handling, start with the default automatic handling. It works for most applications and requires no additional code. You can always switch to manual handling later if you encounter specific issues or have requirements that necessitate more control over the URL handling process. 

## Next Steps
 

After configuring the SDK, you're ready to initialize it and start processing payments. Continue to the [**iOS Initialization Guide**](https://developers.xsolla.com/sdk/mobilesdk/sdk/initialization/ios) to learn how to set up payment observers and handle transaction events in your application.

Initialization

https://developers.xsolla.com/sdk/mobilesdk/sdk/initialization

Choose your platform: 
 
- [Android](https://developers.xsolla.com/sdk/mobilesdk/sdk/initialization/android) 
- [iOS](https://developers.xsolla.com/sdk/mobilesdk/sdk/initialization/ios)

Initialization - Android

https://developers.xsolla.com/sdk/mobilesdk/sdk/initialization/android

After [configuring](https://developers.xsolla.com/sdk/mobilesdk/sdk/configuration/android), the **Xsolla Mobile SDK** needs to be initialized and connected to Xsolla services. Initialization is a 3-step process: 

## Create a billing client

Create a `BillingClient` with your `Config` and a `PurchasesUpdatedListener`, guarding against double initialization:

```java
if (billingClient == null) {
    billingClient = BillingClient.newBuilder(this)
        .setConfig(config) // Config built in the previous step
        .setListener((billingResult, purchases) -> {
            // Invoked for every purchase flow outcome — success, cancel, or error.
            // Handle consumption or entitlement grant here.
        })
        .build();
}
```

## Start a connection

Call `startConnection()` — `onBillingSetupFinished()` fires when the SDK is authenticated and ready:

```java
billingClient.startConnection(new BillingClientStateListener() {
    @Override
    public void onBillingSetupFinished(@NonNull final BillingResult billingResult) {
        if (billingResult.isSuccess()) {
            // Connected — safe to query products and launch purchases.
        }
    }

    @Override
    public void onBillingServiceDisconnected() {
        // Connection dropped — release the client so it's recreated on next use.
        billingClient.endConnection();
        billingClient = null;
    }
});
```

## Query product details

Inside `onBillingSetupFinished()`, fetch `ProductDetails` for each SKU — required by the [purchasing flow](https://developers.xsolla.com/sdk/mobilesdk/sdk/purchase/android):

```java
// Build the list of SKUs to fetch — add one Product entry per item in your catalog.
final QueryProductDetailsParams params = QueryProductDetailsParams.newBuilder()
    .setProductList(Arrays.asList(
        QueryProductDetailsParams.Product.newBuilder()
            .setProductId("your_sku")               // SKU from your Xsolla Catalog
            .setProductType(BillingClient.ProductType.INAPP)
            .build()
    ))
    .build();

billingClient.queryProductDetailsAsync(params, (billingResult, productDetailsList) -> {
    // Store productDetailsList — it's passed directly to the purchasing flow.
});
```

The SDK loads only Virtual Items from your Xsolla Catalog — Virtual Currency, Bundles, and other catalog types are not returned.

Initialization - iOS

https://developers.xsolla.com/sdk/mobilesdk/sdk/initialization/ios

After [configuring](https://developers.xsolla.com/sdk/mobilesdk/sdk/configuration/ios), the **Xsolla Mobile SDK** needs to be initialized and connected to **Xsolla** services.

Initialization is done using settings created in previous step. Put this logic somewhere in your initialization code, perhaps, in AppDelegate's [didFinishLaunchingWithOptions ↗](https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622921-application) ([https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622921-application](https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622921-application)). 

```objectivec
SKPaymentQueue* queue = [SKPaymentQueue defaultQueue];
[queue startWithSettings: settings]; // settings from the previous step

// conform your class to SKPaymentTransactionObserver and implement its method
@interface YourClass (SKPaymentTransactionObserver) <SKPaymentTransactionObserver>
@end

@implementation YourClass (SKPaymentTransactionObserver)

- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions {
    for(SKPaymentTransaction *transaction in transactions) {
        switch (transaction.transactionState) {
            case SKPaymentTransactionStateFailed: 
                // purchase failed, present an error
                break;
            case SKPaymentTransactionStatePurchased: 
                // award the player with the purchased item (SKU: transaction.payment.productIdentifier)
                break;
            default: break;
        }
    }
}

@end
```

```swift
SKPaymentQueue.default().start(settings)
SKPaymentQueue.default().add(self)

// conform your class to SKPaymentTransactionObserver and implement its method
extension YourClass: SKPaymentTransactionObserver {
    func paymentQueue(_: SKPaymentQueue, updatedTransactions: [SKPaymentTransaction]) {
        for transaction in updatedTransactions {
            switch transaction.transactionState {
            case .failed:
                // purchase failed, present an error
            case .purchased:
                // award the player with the purchased item (SKU: transaction.payment.productIdentifier)
            default:
                break
            }
        }
    }
}
```
 

After `SKPaymentQueue` is started and observer is added, application can request SKU information: 

```objectivec
NSSet *skus = [NSSet setWithArray:@[@"your_sku1", @"your_sku2"]];
SKProductsRequest* req = [[SKProductsRequest alloc] initWithProductIdentifiers:skus];

req.delegate = self;
[req start];

// conform your class to SKProductsRequestDelegate and implement its method
@interface YourClass (SKProductsRequestDelegate) <SKProductsRequestDelegate>
@end

@implementation YourClass (SKProductsRequestDelegate)

- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
    // save loaded products somewhere
    self.products = response.products
}

@end
```

```swift
let skus: Set<String> = ["your_sku1", "your_sku2"]
let req = SKProductsRequest(productIdentifiers: skus)

req.delegate = self
req.start()

// conform your class to SKProductsRequestDelegate and implement its method
extension YourClass: SKProductsRequestDelegate {
    func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
        // save loaded products somewhere
        self.products = response.products
    }
}
```
 

The SDK loads only Virtual Items from your Xsolla Catalog — Virtual Currency, Bundles, and other catalog types are not returned. 

For more information, see the implementation in Apple's [official documentation ↗](https://developer.apple.com/documentation/storekit/setting-up-the-transaction-observer-for-the-payment-queue) ([https://developer.apple.com/documentation/storekit/setting-up-the-transaction-observer-for-the-payment-queue](https://developer.apple.com/documentation/storekit/setting-up-the-transaction-observer-for-the-payment-queue)).

Installation

https://developers.xsolla.com/sdk/mobilesdk/sdk/installation

Choose your platform: 
 
- [Android](https://developers.xsolla.com/sdk/mobilesdk/sdk/installation/android) 
- [iOS](https://developers.xsolla.com/sdk/mobilesdk/sdk/installation/ios)

Installation - Android

https://developers.xsolla.com/sdk/mobilesdk/sdk/installation/android

### Prerequisites
 warning
 
- Android SDK >= **24** 
- Java >= **11** 
 

### Installation Steps
 
 
- Add the repository to your project's Gradle script (usually `settings.gradle`): 
 

```kotlin
dependencyResolutionManagement {
    repositories {
        google()
        mavenCentral()

        maven {
            url = uri("https://raw.githubusercontent.com/xsolla/xsolla-sdk-android/main")
        }
    }
}
```

*Coming soon.* 
 
- Add a new dependency for *Xsolla Mobile SDK* to your module's `build.gradle`: 
 

```kotlin
dependencies {
    def version_mobilesdk = '<version_code>' // e.g. '3.0.37'

    implementation "com.xsolla.android:mobile:$version_mobilesdk"
}
```

Installation - iOS

https://developers.xsolla.com/sdk/mobilesdk/sdk/installation/ios

## iOS Version Requirements
 

The *Xsolla Mobile SDK* supports iOS 12.0 and later as the minimum deployment target. However, please note that some features that rely on Apple's APIs for External Purchases or Alternative App Distributions may require iOS 17.5 or later. 

## Installation via Swift Package Manager
 

### Using Xcode
 
 
- In Xcode, go to **File → Add Package Dependencies…** 
- Enter the repository URL:  ```text https://github.com/xsolla/xsolla-sdk-ios ``` 
- Set the dependency rule to **Up to Next Major Version** and specify the version (e.g., `3.8.0`). 
- Click **Add Package**. 
- In the package products dialog, select **XsollaMobileSDK** and add it to your target. 
 

### Using Package.swift
 

Add the dependency to your `Package.swift` file: 

```swift
dependencies: [
    .package(url: "https://github.com/xsolla/xsolla-sdk-ios", from: "3.8.0")
]
```
 

Then add `XsollaMobileSDK` to your target's dependencies: 

```swift
.target(
    name: "YourTarget",
    dependencies: [
        .product(name: "XsollaMobileSDK", package: "xsolla-sdk-ios"),
    ]
)
```

Purchasing flow

https://developers.xsolla.com/sdk/mobilesdk/sdk/purchase

Choose your platform: 
 
- [Android](https://developers.xsolla.com/sdk/mobilesdk/sdk/purchase/android) 
- [iOS](https://developers.xsolla.com/sdk/mobilesdk/sdk/purchase/ios)

Purchase flow - Android

https://developers.xsolla.com/sdk/mobilesdk/sdk/purchase/android

Purchasing flow is a multistage process, which involves [**payment collection**](https://developers.xsolla.com/sdk/mobilesdk/quick-start/android/#purchase-product), purchase [**validation**](https://developers.xsolla.com/sdk/mobilesdk/sdk/validation/android) and [**consumption**](https://developers.xsolla.com/sdk/mobilesdk/sdk/purchase/android#consuming-purchased-goods). 

## Launch a billing flow

You have multiple options on how to start a purchasing flow depending on your needs and goals.

To start a purchasing flow, pass one of the `ProductDetails` [acquired earlier](https://developers.xsolla.com/sdk/mobilesdk/sdk/initialization/android) with a `BillingFlowParams` into `launchBillingFlow` method:

```java
// Parent activity to open the Xsolla Pay Station with.
final Activity activity = // ...

mBillingClient.launchBillingFlow(activity, BillingFlowParams
    .newBuilder()
    .setProductDetailsParamsList(Arrays.asList(
        BillingFlowParams.ProductDetailsParams
            .newBuilder()
            .setProductDetails(productDetails)
            .build()
    ))
    .build()
);
```
**Advanced Settings**

`BillingFlowParams.Builder` supports multiple other properties that can be specified prior to launching a billing flow using `BillingClient.launchBillingFlow()`:

| Setting | Value | Description |
| --- | --- | --- |
| setObfuscatedAccountId | string | Sets an obfuscated account ID that can be used upon purchasing flow completion (callback or webhook). |
| setObfuscatedProfileId | string | Sets an obfuscated account ID that can be used upon purchasing flow completion (callback or webhook). |
| setDeveloperPayload | string | Allows passing developer's arbitrary meta-data with the purchasing flow, which can be used upon purchasing flow completion (callback or webhook). |
| setExternalTransactionId | string | Allows setting a custom unique transaction ID that should be associated with the launched purchasing flow (make sure to enable external transaction IDs in your Publisher Account as well or the purchasing flow will fail). |
| setTrackingId | string | A tracking ID intended for Xsolla's internal analytics (consult with your integration manager beforehand). |
| setForcePaymentMethodId | int | Forces a specific payment method to be selected for the launched purchasing flow (IDs can be found in your Publisher Account). |
| setCanceller | BillingFlowCanceller | Attaches a canceller that lets you programmatically cancel the billing flow after it has been launched. |

**Cancelling a billing flow**

Create a `BillingFlowCanceller`, pass it to the params builder, and call `cancel()` when needed (for example, on a timeout or user action). The SDK will stop any in-progress retries and return a `USER_CANCELED` result.

```java
final BillingFlowCanceller canceller = new BillingFlowCanceller();

mBillingClient.launchBillingFlow(activity, BillingFlowParams
    .newBuilder()
    .setProductDetailsParamsList(Arrays.asList(
        BillingFlowParams.ProductDetailsParams
            .newBuilder()
            .setProductDetails(productDetails)
            .build()
    ))
    .setCanceller(canceller)
    .build()
);

// Cancel the flow at any point from any thread
canceller.cancel();
```

This approach allows to initiate the billing flow using an externally generated Xsolla Pay Station payment token.

```java
// Parent activity to open the Xsolla Pay Station with.
final Activity activity = // ...

final String paymentToken = "<your-externally-generated-payment-token>";
final String developerPayload = null;

mBillingClient.launchBillingFlow(
    activity, paymentToken, developerPayload
);
```
**Advanced Settings**

For more control over the token-based billing flow, use the `BillingFlowTokenParams` builder instead:

```java
final BillingFlowCanceller canceller = new BillingFlowCanceller();

mBillingClient.launchBillingFlow(activity, BillingFlowTokenParams
    .newBuilder()
    .setPaymentToken("<your-externally-generated-payment-token>")
    .setDeveloperPayload(null)
    .setCanceller(canceller)
    .build()
);

// Cancel the flow at any point from any thread
canceller.cancel();
```

| Setting | Value | Description |
| --- | --- | --- |
| setPaymentToken | string | The externally generated Xsolla Pay Station payment token. |
| setDeveloperPayload | string | Allows passing developer's arbitrary meta-data with the purchasing flow, which can be used upon purchasing flow completion (callback or webhook). |
| setCanceller | BillingFlowCanceller | Attaches a canceller that lets you programmatically cancel the billing flow after it has been launched. |

Two ways to integrate **Buy Button**: redirect users to your [**Web Shop**](https://developers.xsolla.com/sdk/mobilesdk/buy-button/android?purchase-method=webshop-linkouts), or use the native in-app [**Buy Button Solution**](https://developers.xsolla.com/sdk/mobilesdk/buy-button/android?purchase-method=buy-button).important

Following the **Epic v. Google** ruling effective **October 29, 2025**, U.S. **Google Play** apps can use the Xsolla SDK purchasing flow directly, but must first detect the **storefront country code** using the guide provided [**here**](https://developers.xsolla.com/sdk/mobilesdk/sdk/advanced/detect-gpb-storefront-android).

## Validate the purchase (recommended)

For instructions on how to validate your purchases, see [**Purchase validation**](https://developers.xsolla.com/sdk/mobilesdk/sdk/validation/android).warning

Although validation is **NOT** a mandatory step, it's highly recommended to minimize the chance of fraud and any other malevolent intents.

## Consume purchased goods

The final step in the purchasing flow is to award the purchased goods to the user and mark them as processed. This is known as **purchase consumption**. Once consumed, the item is no longer listed as a pending purchase and — depending on your product configuration — the user may purchase it again.

How consumption is handled depends on whether your integration processes purchases **on the client** or **on your backend via webhooks**.

### Client-based (no webhooks)

In a client-based integration, the game client is responsible for awarding goods to the user and notifying Xsolla that the purchase has been processed. This is done inside the `PurchasesUpdatedListener.onPurchasesUpdated()` callback after a successful purchase (and, ideally, after [**validation**](https://developers.xsolla.com/sdk/mobilesdk/sdk/validation/android)):

```java
final Purchase purchase = // ...

final ConsumeParams consumeParams = ConsumeParams
    .newBuilder()
    .setPurchaseToken(purchase.getPurchaseToken())
    .build();

// Notify Xsolla that the purchase has been processed and mark it as consumed.
mBillingClient.consumeAsync(consumeParams, new ConsumeResponseListener() {
    @Override
    public void onConsumeResponse(
        @NonNull final BillingResult billingResult, final String purchaseToken
    ) {
        if (billingResult.isSuccess()) {
            // Consumption succeeded — award the purchased goods to the user here.
        } else {
            // Consumption failed — do not grant entitlement.
        }
    }
});
```
important

It's highly recommended to perform at least some form of [**validation**](https://developers.xsolla.com/sdk/mobilesdk/sdk/validation/android) before consuming the purchased goods.

### Server-based (webhooks)

In a server-based integration, your backend handles the full purchase lifecycle — validation, awarding, and consumption — triggered by a webhook from Xsolla. The `PurchasesUpdatedListener.onPurchasesUpdated()` callback is still invoked on the client when a purchase completes, but it carries no authority in this flow and should not be used to drive any purchase logic.

**The simplest approach** is to ignore the callback entirely and let your backend confirm the outcome.

**A more complete approach** is to use it purely for UI purposes — for example, showing a pending state for the purchased product and clearing it once your server confirms the result:

```java
final PurchasesUpdatedListener purchasesUpdatedListener = new PurchasesUpdatedListener() {
    @Override
    public void onPurchasesUpdated(
        @NonNull final BillingResult billingResult, @Nullable final List<Purchase> purchases
    ) {
        if (billingResult.isSuccess() && purchases != null) {
            // The purchase was initiated successfully.
            // Your backend will handle validation, consumption, and entitlement via webhook.
            // Optionally, show a pending/processing state in the UI here and
            // update it once your server relays the confirmed result back to the client.
        } else {
            // The purchase did not complete (e.g. user cancelled or a connection error occurred).
            // No action required — your backend will not receive a webhook for this purchase.
        }
    }
};
```
info

For details on how the webhook-based flow works end-to-end, including consumption handled on the backend, see [**Purchase validation — Server-side**](https://developers.xsolla.com/sdk/mobilesdk/sdk/validation/android#server-side-validation).

#### Querying existing purchases

`BillingClient.queryPurchases()` is a manual call — the SDK does not invoke it automatically. In a webhook-based integration it is generally not useful for consumable products, since your server is the authority on purchase state. Its practical use is limited to checking ownership of **non-consumable** products with SKUs known to the app upfront.

Purchase flow - iOS

https://developers.xsolla.com/sdk/mobilesdk/sdk/purchase/ios

Purchasing flow is a multistage process, which involves [payment collection](https://developers.xsolla.com/sdk/mobilesdk/quick-start/ios/#purchase-product), purchase [validation](https://developers.xsolla.com/sdk/mobilesdk/sdk/purchase/ios#validating-purchases-recommended) and [consumption](https://developers.xsolla.com/sdk/mobilesdk/sdk/purchase/ios#consuming-purchased-goods). 

### Starting purchasing flow
 

We can use previously acquired `SKProduct` info to create payment and initiate purchase: 

```objectivec
SKProduct *product = ...;  // previously fetched product
SKPayment *payment = [SKPayment paymentWithProduct:product];
[[SKPaymentQueue defaultQueue] addPayment:payment];
```

```swift
let product = ... // previously fetched product
let payment = SKPayment(product: product)
SKPaymentQueue.default().add(payment)
```
 

### Validating purchases (recommended)
 warning

Although validation is **NOT** a mandatory step, yet it's highly recommended to minimize the chance of fraud and any other malevolent intents. 

For instructions on how to validate your purchases, see [**Purchase validation**](https://developers.xsolla.com/sdk/mobilesdk/sdk/validation/ios). 

### Consuming purchased goods
 

The very final step in the chain of purchasing flow stages is to award the purchased goods to the user and mark these goods as "awarded". The process also known as **purchase consumption**. 

Use the code below to do just that: 

Purchase result will become known in `paymentQueue:updatedTransactions:` method:

```objectivec
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions {
    for(SKPaymentTransaction *transaction in transactions) {
        switch (transaction.transactionState) {
            case SKPaymentTransactionStateFailed: 
                // Check if the user cancelled the purchase (don't treat as an error)
                if (transaction.error.code == SKErrorPaymentCancelled) {
                    [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
                    break;
                }
                // Always acknowledge transaction and finish it
                [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
                break;
            case SKPaymentTransactionStatePurchased: 
                // here you can save the purchase and award it to the user
                // Always acknowledge transaction and finish it after it was saved
                [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
                break;
            default: break;
        }
    }
}
```

Purchase result will become known in `paymentQueue(_:updatedTransactions:)` method:

```swift
func paymentQueue(_: SKPaymentQueue, updatedTransactions: [SKPaymentTransaction]) {
    for transaction in updatedTransactions {
        switch transaction.transactionState {
        case .failed:
            // Check if the user cancelled the purchase (don't treat as an error)
            if (transaction.error as? SKXError)?.errorCode == .paymentCancelled {
                SKPaymentQueue.default().finishTransaction(transaction)
                break
            }
            // Always acknowledge transaction and finish it
            SKPaymentQueue.default().finishTransaction(transaction)
        case .purchased:
            // here you can save the purchase and award it to the user
            // Always acknowledge transaction and finish it after it was saved
            SKPaymentQueue.default().finishTransaction(transaction)
        default:
            break
        }
    }
}
```
 important

It's highly recommended to perform at least some form of [**validation**](https://developers.xsolla.com/sdk/mobilesdk/sdk/purchase/ios#validating-purchases-recommended) before consuming the purchased goods.

Testing & Sandbox

https://developers.xsolla.com/sdk/mobilesdk/sdk/testing

Choose your platform: 
 
- [Android](https://developers.xsolla.com/sdk/mobilesdk/sdk/testing/android) 
- [iOS](https://developers.xsolla.com/sdk/mobilesdk/sdk/testing/ios)

Testing & Sandbox - Android

https://developers.xsolla.com/sdk/mobilesdk/sdk/testing/android

This section provides various snippets and examples of how to configure the sandbox environment to be able to make test payments, make logging more verbose, and so on. 

### Enabling sandbox mode
 

```java
final var configCommon = Config.Common.getDefault()
    // ...other settings
    .withSandboxEnabled(true);
```
 

### Enabling additional logging
 

```java
final var configCommon = Config.Common.getDefault()
    // ...other settings
    .withLogLevel(LogLevel.DEBUG)
    // Forces even more internal data to be logged (e.g. profiling stats, etc).
    .withDebugEnabled(true);
```
 

Before going live, turn **sandbox mode** and **debug logging** OFF. 

### Test cards
 

For a list of cards to simulate payments in sandbox mode, see [here ↗](https://developers.xsolla.com/doc/pay-station/testing/test-cards) ([https://developers.xsolla.com/doc/pay-station/testing/test-cards](https://developers.xsolla.com/doc/pay-station/testing/test-cards)). important

The test cards won't work outside the **sandbox mode**.

Testing & Sandbox - iOS

https://developers.xsolla.com/sdk/mobilesdk/sdk/testing/ios

This section provides various snippets and examples of how to configure the sandbox environment to be able to make test payments, make logging more verbose, and so on. 

### Enabling sandbox mode
 

```objectivec
SKPaymentSettings* settings = ...;

settings.useSandbox = YES;
```

```swift
let settings = SKPaymentSettings(...)

settings.useSandbox = true
```
 warning

Remember to turn the **sandbox** mode **OFF** before going **LIVE**. 

### Enabling additional logging
 

```objectivec
SKPaymentSettings* settings = ...;

settings.logLevel = SKLogLevelDebug;
```

```swift
let settings = SKPaymentSettings(...)

settings.logLevel = .debug
```
 warning

Make sure the debug **logging** is **OFF** before going **LIVE**. 

### Test cards
 

For a list of cards to simulate payments in sandbox mode, see [here ↗](https://developers.xsolla.com/doc/pay-station/testing/test-cards) ([https://developers.xsolla.com/doc/pay-station/testing/test-cards](https://developers.xsolla.com/doc/pay-station/testing/test-cards)). important

The test cards won't work outside the **sandbox mode**.

Purchase validation

https://developers.xsolla.com/sdk/mobilesdk/sdk/validation

Choose your platform: 
 
- [Android](https://developers.xsolla.com/sdk/mobilesdk/sdk/validation/android) 
- [iOS](https://developers.xsolla.com/sdk/mobilesdk/sdk/validation/ios)

Purchase validation - Android

https://developers.xsolla.com/sdk/mobilesdk/sdk/validation/android

Each purchase should be validated before awarding it to the end-user. This assists in preventing unauthorized purchases and ensures the integrity of your in-app economy. Validating purchases is a critical security measure to prevent fraud, protect your revenue, and ensure fair user experiences.

Essentially, that are two distinct validation approaches readily available. 

### Server-side Validation
 

The most secure and reliable way to validate purchases is by using **server-to-server** (**S2S**) communication, with the client completely removed from the validation process. This minimizes security risks and ensures that entitlements are only granted based on verified data received directly from Xsolla. 

With Xsolla, server-side validation is implemented through a **webhook-based model**: 
 
- Once a user completes a purchase through your app, Xsolla services automatically trigger a [**webhook event**](https://developers.xsolla.com/sdk/publisher-account/webhooks#user-validation-user_validation) to your backend. 
- This webhook contains all the necessary purchase data, including the *Xsolla* `order ID`, enabling your server to validate and process the purchase asynchronously. 
- Your backend can then use this data to update the user’s entitlements, balance, inventory, etc., without requiring any additional input from the client. 
 

This flow ensures that the client has no role in deciding the outcome of a purchase — all validation logic lives on the server, using trusted data that originates from Xsolla itself. tip

For more information about how webhooks work, see our [**Webhooks**](https://developers.xsolla.com/sdk/publisher-account/webhooks) guide.  How does this compare to Google Play Billing?

This table provides a quick-glance comparison between **Xsolla’s webhook-based server-side validation** approach and **Google Play Billing’s API-driven validation**. While both are designed to assure the legitimacy of purchases, they operate through fundamentally different infrastructures and workflows. Notably, **Xsolla does not use or abstract Google Play Developer APIs**; instead, it provides an independent validation mechanism through webhooks.

| Feature/Aspect | Xsolla Mobile SDK | Google Play Billing |
| --- | --- | --- |
| Validation Flow | Webhook-driven: Xsolla services send a notification to your backend when a purchase completes. Your backend then validates and processes the order. | API-driven: Your backend polls or queries the Google Play Developer API using a purchaseToken received from the client to verify purchase authenticity. |
| Primary Identifier | Xsolla order ID | Google Play purchaseToken |
| Verification Authority | Xsolla's backend infrastructure and your own server logic | Google Play Developer API (purchases.products:get, purchases.subscriptionsv2:get, etc.) |
| Backend Integration Pattern | Reactive: Webhook-based model automatically pushes purchase data to your backend. | Polling or request-based: Your server must pull data from Google's APIs after receiving a purchaseToken from the client. |
| Asynchronous Handling | ✅ Yes. Webhooks trigger upon purchase completion, enabling early or background processing. | ⚠️ Possible, but typically requires polling or client-to-server relay before querying Google. |
| Security Handling | Simplified. Xsolla ensures validation before triggering webhook to your backend. You validate order ID and fulfill the order accordingly. | Complex. Your backend must securely handle API tokens, verify signatures, and respond to purchase states appropriately. |
| Integration Complexity | Lower — No need to manage credential scopes or external billing endpoints. Xsolla manages purchase flow and signals your backend. | Higher — Requires OAuth, service account setup, token lifecycle management, and granular error handling. |
 

### Client-side Validation
 

Provides means of validating a purchase on the user's device without a need for a backend (server). While it's better than nothing, it has inherent drawbacks: anything that runs on the client can potentially be exploited. This approach offers a rudimentary level of validation but is generally **not recommended for critical applications** due to its inherent security vulnerabilities.  

When [initializing](https://developers.xsolla.com/sdk/mobilesdk/sdk/initialization/android) a `BillingClient`, extend the `onPurchasesUpdated` callback of `PurchasesUpdatedListener` with the code below to enable a rudimentary (non-secure) purchase validation on the client: 

```java
final PurchasesUpdatedListener purchasesUpdatedListener = new PurchasesUpdatedListener() {
    @Override
    public void onPurchasesUpdated(
        @NonNull final BillingResult billingResult, @Nullable final List<Purchase> purchases
    ) {
        if (billingResult.isSuccess() && purchases != null) {
            // Validate and consume purchases one by one.
            for (int i = 0; i < purchases.size(); ++i) {
                final ValidateParams validateParams = ValidateParams.newBuilder()
                    .setPurchaseToken(purchases.get(i).getPurchaseToken())
                    .build();

                // This `validateAsync` method is part of the Xsolla Mobile SDK.
                // It performs a client-side network call to Xsolla's services for validation.
                // For developers coming from GPB, this serves a similar *purpose* to how you
                // might send a `purchaseToken` from the client to your *own* backend for validation,
                // but here, Xsolla's services act as that immediate validation point.
                mBillingClient.validateAsync(validateParams, new ValidateResponseListener() {
                    @Override
                    public void onValidateResponse(
                        @NonNull final BillingResult billingResult, @Nullable final String purchaseToken
                    ) {
                        // Make sure validation of the purchase was a success..
                        if (billingResult.isSuccess() && !TextUtils.isEmpty(purchaseToken)) {
                            // Prepare consumption parameters based on the purchase
                            // token received with the validation response.
                            final ConsumeParams consumeParams = ConsumeParams.newBuilder()
                                .setPurchaseToken(purchaseToken)
                                .build();

                            // Notify Xsolla that the purchase has been processed and mark it as consumed.
                            mBillingClient.consumeAsync(consumeParams,
                                // TODO: Handle consumption success/failure.
                                // After successful consumption, grant entitlement to the user.
                            );
                        } else {
                            // Purchase validation has failed on the client-side.
                            // Handle error, do not grant entitlement.
                        }
                    }
                });
            }
        } else {
            // An error occurred, while trying to update the purchases.
            // This could be due to BillingClient connection issues or a user cancellation.
            // Handle error appropriately, do not grant entitlement.
        }
    }
}
```
 How does this compare to Google Play Billing?

The Xsolla Mobile SDK follows a structure similar to Google Play Billing, which helps streamline development and simplifies adoption for those familiar with GPB. The table below outlines how common validation and fulfillment flows compare between the two:

| Feature | Xsolla Mobile SDK | Google Play Billing |
| --- | --- | --- |
| Validation | validateAsync(...) – sends purchaseToken to Xsolla services for basic validation | No built-in validation; purchaseToken must be sent manually to your backend |
| Consumption & Entitlement | consumeAsync(...) – called after validation to mark the purchase and unlock content | consumeAsync(...) – called after custom backend validation to complete the flow |
 

## Conclusion and Recommendations
 

The Xsolla Mobile SDK supports both **client-side** and **server-side** purchase validation flows to accommodate a wide range of application needs and developer workflows. Each approach comes with trade-offs in terms of security, complexity, and integration effort. 

For production environments, especially where real monetary value or access to premium content is involved, **server-side validation is the strongly recommended method**. It ensures that all entitlements are granted based on verified purchase data received directly through **Xsolla's secure webhook system**, with no reliance on data provided by the client. This significantly reduces the risk of fraud or tampering. 

Client-side validation, while faster to implement and sufficient for low-risk use cases, should be treated as a lightweight solution. It involves making validation requests from the device directly to Xsolla's services and can be bypassed if used in isolation. This method may be appropriate for prototypes or apps offering non-critical, low-value items. info

The *Xsolla Mobile SDK* provides flexible validation options to help developers start quickly and scale securely. For best results, adopt the **server-side webhook-based validation** as your primary mechanism for managing purchase authenticity and entitlement fulfillment.

Purchase validation - iOS

https://developers.xsolla.com/sdk/mobilesdk/sdk/validation/ios

Each purchase should be validated before awarding it to the end-user. This assists in preventing unauthorized purchases and ensures the integrity of your in-app economy. Validating purchases is a critical security measure to prevent fraud, protect your revenue, and ensure fair user experiences.

Essentially, that are two distinct validation approaches readily available. 

### Server-side Validation
 

The most secure and reliable way to validate purchases is by using **server-to-server** (**S2S**) communication, with the client completely removed from the validation process. This minimizes security risks and ensures that entitlements are only granted based on verified data received directly from Xsolla. 

With Xsolla, server-side validation is implemented through a **webhook-based model**: 
 
- Once a user completes a purchase through your app, Xsolla services automatically trigger a [**webhook event**](https://developers.xsolla.com/sdk/publisher-account/webhooks#user-validation-user_validation) to your backend. 
- This webhook contains all the necessary purchase data, including the **Xsolla** `order ID`, enabling your server to validate and process the purchase asynchronously. 
- Your backend can then use this data to update the user's entitlements, balance, inventory, etc., without requiring any additional input from the client. 
 

This flow ensures that the client has no role in deciding the outcome of a purchase — all validation logic lives on the server, using trusted data that originates from Xsolla itself. tip

For more information about how webhooks work, see our [**Webhooks**](https://developers.xsolla.com/sdk/publisher-account/webhooks) guide.  

### Client-side Validation
 

Provides means of validating a purchase on the user's device without a need for a backend (server). It's better than nothing, but has its own drawbacks, i.e. anything that runs on the client can potentially be exploited.  

Before finishing transaction, it can be validated on client side. Unfinished transactions restored after app was closed can't be validated. 

```objectivec
SKPaymentTransaction* transaction = ...;
[[SKPaymentQueue defaultQueue] validateTransaction:transaction completion:^(BOOL isValid, NSError * _Nullable error) {
  // isValid will be YES for valid transactions
}];
```

```swift
let transaction: SKPaymentTransaction = ...
SKPaymentQueue.default().validateTransaction(transaction) { isValid, error in
  // isValid will be true for valid transactions
}
```
 

## Comparison with Native StoreKit Validation
 

While Xsolla provides streamlined validation approaches, it's helpful to understand how they compare to Apple's native StoreKit validation [methods ↗](https://developer.apple.com/documentation/storekit/choosing-a-receipt-validation-technique) ([https://developer.apple.com/documentation/storekit/choosing-a-receipt-validation-technique](https://developer.apple.com/documentation/storekit/choosing-a-receipt-validation-technique)). 

### Server-side Validation Differences
 

| Aspect | Xsolla Webhook-based | Apple's verifyReceipt API |
| --- | --- | --- |
| Implementation | Webhook automatically triggered by Xsolla | Manual server-to-server calls to Apple's endpoint |
| Security | High - No client involvement | High - Direct validation with Apple |
| Setup Complexity | Low - Configure webhook endpoint | Medium - Implement receipt parsing and API calls |
| Data Format | JSON webhook payload | Base64-encoded receipt → JSON response |
| Timing | Asynchronous, real-time notifications | Synchronous, on-demand validation |
| Transaction History | Individual purchase events | Complete receipt with all transactions |
| Network Requirements | Webhook endpoint accessible to Xsolla | Secure HTTPS to Apple's servers |
 

With **Xsolla's webhook-based approach**, purchase validation happens automatically when Xsolla pushes verified purchase data to your backend endpoint, eliminating the need to handle receipt extraction, parsing, or validation requests. 

With **Apple's native approach**, your app must extract receipt data from the app bundle and your server must make validation requests to Apple's endpoints (`https://buy.itunes.apple.com/verifyReceipt` for production or `https://sandbox.itunes.apple.com/verifyReceipt` for sandbox), then parse the JSON response. 

### Client-side Validation Differences
 

| Aspect | Xsolla Mobile SDK | Apple's Native Local Validation |
| --- | --- | --- |
| Implementation | Simple SDK method call | Complex PKCS #7 container decoding |
| Code Complexity | Low - Single function call | High - ASN.1 parsing, cryptographic validation |
| Validation Steps | Handled internally by SDK | Manual verification of certificate chain, bundle ID, version, device hash |
| Dependencies | Xsolla Mobile SDK | Security frameworks |
| Maintenance | Managed by Xsolla | Developer maintains cryptographic code |
| Security Level | Medium - Client-side limitations apply | Medium - Client-side limitations apply |
 

**Xsolla's client-side validation** provides a simplified interface: 

```swift
SKPaymentQueue.default().validateTransaction(transaction) { isValid, error in
    // Simple validation result
}
```
 

**Apple's native local validation** requires extensive implementation including decoding receipts as PKCS #7 containers using ASN.1 notation, verifying certificate chains and signature validity, computing SHA-1 hashes for device identification, and validating multiple receipt fields (bundle ID, version, device hash).

Offerwall SDK

https://developers.xsolla.com/sdk/offerwall

Coming SoonSDK binaries · API reference · Integration guides

The **Xsolla Offerwall SDK** will let you surface an offerwall directly inside your game — presenting advertiser tasks, granting virtual rewards, and tracking completions natively, without leaving the app.7xhigher retention by Day 3010–14xmore likely to convert to paying120%LTV increase among daily users90%revenue share for developers 

**Interested?** SDK binaries, API reference, and integration guides are in development — reach out to your Account Manager for details.  

## Monetize Your Entire Player Base
 

Most games monetize only 2–5% of their player base through in-app purchases. **Xsolla Offerwall** is an interactive advertising platform that turns the remaining 95% into a revenue source — without asking them to spend money. Players earn virtual rewards (in-game currencies, items, and other content) by completing tasks from advertisers, creating a value exchange where everyone benefits. 

Offerwall users show 7x higher retention by Day 30 and are 10–14x more likely to convert into paying users. Games that integrate offerwalls see up to 120% LTV increase among daily offerwall users — making it both a monetization tool and a retention driver. **Industry-Leading Revenue Share**

Xsolla offers developers up to 90% revenue share — far exceeding the typical industry average of 30–60%.**Fraud Prevention**

SMS-based user verification improves traffic quality and prevents fraudulent completions, protecting your revenue.**Xsolla Points**

A cross-game rewards currency that gives players lasting value beyond a single title, encouraging long-term engagement.**Global Reach**

Quests and rewards designed for a global audience, with advertiser demand spanning multiple regions and verticals.  

## Rewarded Engagement Models
 

The **Xsolla Offerwall** supports multiple task types to maximize engagement and fill rates: **Cost Per Install (CPI)**

Players earn rewards by installing and opening a promoted app — high-value actions that drive installs for advertisers.**Cost Per Engagement (CPE)**

Rewards for completing in-app milestones such as reaching a game level, finishing a tutorial, or achieving a goal.**Cost Per Acquisition (CPA)**

Players complete sign-ups, subscriptions, or purchases — the highest-value conversion events for advertisers.**Cost Per Click (CPC)**

Lightweight tasks where players engage with content, watch videos, or interact with promotional material.  

**Xsolla Offerwall** is also available as a no-code addition to your [**Xsolla Web Shop** ↗](https://xsolla.com/mobile-web-shop) [(xsolla.com)](https://xsolla.com/mobile-web-shop) — activate it from **Publisher Account** and it appears as a native section in your storefront. For more information, visit [**Offerwall** ↗](https://xsolla.com/offerwall) [(xsolla.com)](https://xsolla.com/offerwall).

Publisher Account

https://developers.xsolla.com/sdk/publisher-account

**Publisher Account** is the central control panel for your Xsolla integration — where you create your project, set up authentication, manage your product catalog, and configure webhooks.  

## Create Your Project
 [Recommended

I already have an app on the App Store or Google Play

Import your app's store listing automatically. Xsolla pre-populates your Web Shop with game data — imagery, title, and branding — and you get a Project ID in minutes.Set up from existing store page →](https://developers.xsolla.com/sdk/publisher-account/new-project/setup-fasttrack)[I'm starting fresh

Create a new project, configure Xsolla Login, and get your Project ID and Login ID for SDK integration.Set up manually →](https://developers.xsolla.com/sdk/publisher-account/new-project/setup-manual)  **Authentication**

Configure Xsolla Login with Device ID for seamless auth, or server-side login for full backend control.**Product Catalog**

Create and manage Virtual Items (SKUs) with multi-currency pricing, or import directly from Google Play or App Store.**Webhooks & Events**

Configure payment webhooks for backend transaction handling, or use the Xsolla Events API for serverless purchase validation.**Go Live**

Sign the licensing agreement and switch out of sandbox mode to enable real payments.

Finding Your Project IDs

https://developers.xsolla.com/sdk/publisher-account/getids

Locate your **Project ID** and **Login ID** in the **Publisher Account** — you'll need both to initialize the SDK.  

## Project ID
 

Your **Project ID** is displayed in the **Publisher Account** next to your project name. ![Project ID location in Publisher Account] 

## Login ID
 

To find your **Login ID**, navigate to **Login** > **Dashboard** in the **Publisher Account** and copy the ID displayed next to your Login project name. ![Login ID location in Publisher Account]  

## Using the IDs with SDK
 

Use these IDs when initializing the SDK in your project. 

```csharp
var settings = XsollaStoreClientSettings.Builder.Create()
    .SetProjectId(<Project ID>)
    .SetLoginId(<Login ID>)
    ...
```
tip

For more information on how to configure the SDK, visit [Configure SDK](https://developers.xsolla.com/sdk/unity/sdk/configuration).

```java
final Config billingClientConfig = new Config(
    Config.Common.getDefault(),
    Config.Integration.forXsolla(
        Config.Integration.Xsolla.Authentication.forAutoJWT(
            ProjectId.parse(<Project ID>).getRight(),
            LoginUUID.parse(<Login ID>).getRight()
        )
    ),
    Config.Payments.getDefault()
);
```
tip

For more information on how to configure the SDK, visit [Configure SDK](https://developers.xsolla.com/sdk/mobilesdk/sdk/configuration/android).

```objectivec
SKPaymentSettings* settings = [[SKPaymentSettings alloc] initWithProjectId: <Project ID>
                                                            loginProjectId: <Login ID>
                                                                  platform: SKPaymentPlatformStandalone
                                                      paystationUIThemeId: SKPaystationThemeDark
                                                          paystationUISize: SKPaystationSizeMedium];

SKPaymentQueue* queue = [SKPaymentQueue defaultQueue];
[queue startWithSettings: settings];
```

```swift
let settings = SKPaymentSettings(projectId: <Project ID>,
                                loginProjectId: <Login ID>,
                                platform: .standalone)

SKPaymentQueue.default().start(settings)
```
tip

For more information on how to configure the SDK, visit [Configure SDK](https://developers.xsolla.com/sdk/mobilesdk/sdk/configuration/ios).

Going Live

https://developers.xsolla.com/sdk/publisher-account/gotolive

Sign the licensing agreement and switch from sandbox to production — the final steps before accepting real payments.  

## Sign the Licensing Agreement
 important

You must sign an agreement for every new project in your **Publisher Account**. 

To sign the licensing agreement and enable real payments: 
 
- Open your **Publisher Account**. 
- In the side menu, go to **Agreements and taxes** > **Agreements** section. 
- On the **Licensing Agreement** tab, click **Fill out application form**. 
- Provide your information in the corresponding fields. 
 

Your application will be reviewed within **3 business days**. If questions arise, Xsolla will contact the person listed in the application. Once approved, the agreement status changes to **Signed**. 

Before you sign the licensing agreement, you can open the payment UI only in **sandbox mode**. 

## Switch to Production Environment
 
 
- Make sure you signed the licensing agreement with Xsolla. 
- Set **sandbox** to **false** from the SDK configuration code. 
 

```csharp
var configuration = XsollaStoreClientConfiguration.Builder.Create()
    .SetSandbox(false)
    .SetLogLevel(XsollaLogLevel.None)
    // ...other settings
    .Build();
```

```java
final Config billingClientConfig = new Config(
  Config.Common.getDefault()
      .withSandboxEnabled(false)
      .withLogLevel(LogLevel.ERROR)
      .withDebugEnabled(false),
      // ...other settings
);
```

```objectivec
SKPaymentSettings* settings = ...;

settings.useSandbox = NO;
settings.logLevel = SKLogLevelError;
```

```swift
let settings = SKPaymentSettings(...)

settings.useSandbox = false
settings.logLevel = .error
```
 

## Test Cards and Sandbox Mode
 warning

After the first **real payment** is made, a **strict sandbox** payment policy takes effect. 

Payments made in sandbox mode are available only to users specified in the **Publisher Account** under **Company settings** > **Users**.

Setting Up Authentication

https://developers.xsolla.com/sdk/publisher-account/login

Configure an authentication method for your **Xsolla Login** project — choose the approach that fits your app's complexity.  

## Login with Device ID
 important

This is **required** if you are using **Xsolla SDK** and you don't have existing Xsolla Login integration. 

Best suited for casual apps that don't need advanced features (e.g. server validation, explicit authentication with Xsolla, etc). 

### Enable Device ID Login in Publisher Account
 
 
- In the **Login methods** block, choose any option and click **Configure**. 
- In the top settings block, click **Login API integration**. 
- Set the **Login with device ID** toggle to **ON**. 
- Click **Save changes**. 
 ![device ID login settings] 

For more information, see [Device ID authentication ↗](https://developers.xsolla.com/doc/login/authentication-options/device-id) ([https://developers.xsolla.com/doc/login/authentication-options/device-id](https://developers.xsolla.com/doc/login/authentication-options/device-id)). 

## Login on Game Server
 

The most advanced method — relies on your project's backend to authenticate and validate users. Makes extensive use of webhooks for communication between the backend and Xsolla services. For more information, see the [**Auth by custom ID ↗**](https://developers.xsolla.com/api/login/server-custom-id/auth-by-custom-id) ([https://developers.xsolla.com/api/login/server-custom-id/auth-by-custom-id](https://developers.xsolla.com/api/login/server-custom-id/auth-by-custom-id)) API reference.

Create Project from Store Page

https://developers.xsolla.com/sdk/publisher-account/new-project/setup-fasttrack

Link your **Google Play** or **App Store** listing — Xsolla automatically pulls your game's imagery, title, developer information, and color scheme to pre-populate your **Publisher Account** project and **Web Shop**.  
 
- Sign up or log in to your **Publisher Account**.  To sign up, visit [publisher.xsolla.com ↗](https://publisher.xsolla.com/) ([https://publisher.xsolla.com/](https://publisher.xsolla.com/)). 
- Create a project in the **Publisher Account** (if you haven't already). 
- Navigate to **Storefronts** > **Websites** section within your project. 
- Click the **Create site** button and select the **Web Shop** template. 
- Add a URL to an external platform where your game is available (Google Play Store or Apple App Store). [![create site]](https://developers.xsolla.com/sdk/assets/images/web-shop-create-site-1-55c3c75c111949355a8045234f0fc380.png) 
- Click **Create Web Shop**. 
 

Xsolla automatically fetches your app's information from the store and populates your project and **Web Shop** with the game data. 

The **Web Shop** template is styled to match your game and pairs directly with your **Xsolla SDK** integration.  

## Set Up Authentication
 

Enable **Device ID** login for your project — seamless authentication with no player credentials required. Continue to [Setting up authentication](https://developers.xsolla.com/sdk/publisher-account/login).

Create Project Manually

https://developers.xsolla.com/sdk/publisher-account/new-project/setup-manual

Create a **Publisher Account** project from scratch and configure **Xsolla Login** — get your Project ID and Login ID for SDK integration.  

## Sign up for a Publisher Account

The Publisher Account is the primary tool for configuring Xsolla features, as well as working with analytics and transactions.

To sign up, visit [Publisher Account ↗](https://publisher.xsolla.com/) ([https://publisher.xsolla.com/](https://publisher.xsolla.com/)).

## Create a New Project

Configure your project details:
 
- Select project type: **Game**, **Game platform**, or **Other**. 
- Enter your project name in English. 
- Choose the release platforms. 
- Select monetization approaches. 
- Indicate development stage. 
- For games: specify genre and game engine. 
- Add product/game link *(optional)*. 
[![project info]](https://cdn.xsolla.net/developers/current/images/general/create-project/project-info.png)
 
- Click **Finish** to complete project creation. 

Your project will appear in the side menu.

![created project]

You'll need your **Project ID** to configure and integrate the SDK. Find it in your **Publisher Account** next to the project name — see [**Finding your Project ID**](https://developers.xsolla.com/sdk/publisher-account/getids#project-id) for reference.

## Set up Xsolla Login

To set up Xsolla Login, follow these steps:
 
- Navigate to **Players** > **Login** from the menu. 
- Click **Create Login project**. 
[![create login project]](https://cdn.xsolla.net/developers/current/images/guides/login/integration-guide/2-create-login-project.svg)
 
- Select **Standard Login project** and complete the setup. 
[![select project type]](https://cdn.xsolla.net/developers/current/images/guides/login/integration-guide/3-select-project-type.svg)

You'll need your project's **Login ID** for SDK integration. Find it on your Login project page next to the project name — see [**Finding your Login ID**](https://developers.xsolla.com/sdk/publisher-account/getids#login-id) for reference.  

## Set Up Authentication
 

Configure an authentication method for your **Xsolla Login** project. Continue to [Setting up authentication](https://developers.xsolla.com/sdk/publisher-account/login).

Configuring Xsolla Catalog

https://developers.xsolla.com/sdk/publisher-account/virtual-items

Set up your **Virtual Items** (IAPs) — the purchasable products in your **Xsolla Catalog**, equivalent to in-app purchases from Google Play Billing or StoreKit. important

Your Xsolla Catalog consists of [**Virtual Items ↗**](https://developers.xsolla.com/doc/shop-builder/features/virtual-items) ([https://developers.xsolla.com/doc/shop-builder/features/virtual-items](https://developers.xsolla.com/doc/shop-builder/features/virtual-items)). Each Virtual Item has a name, description, SKU (product identifier), and a price. 

Import in-app purchases (IAPs) from App Store Connect and synchronize them as Virtual Items in your Xsolla Catalog by following [**this guide ↗**](https://developers.xsolla.com/doc/shop-builder/how-to/import-catalog/?_xm=383621481969483790#shop_builder_how_to_import_appstore_catalog) ([https://developers.xsolla.com/doc/shop-builder/how-to/import-catalog/?_xm=383621481969483790#shop_builder_how_to_import_appstore_catalog](https://developers.xsolla.com/doc/shop-builder/how-to/import-catalog/?_xm=383621481969483790#shop_builder_how_to_import_appstore_catalog)).warning

Make sure to perform this action after every App Store in-app purchase catalog update.

Import in-app products (IAPs) from Google Play and synchronize them as Virtual Items in your Xsolla Catalog by following [**this guide ↗**](https://developers.xsolla.com/doc/shop-builder/how-to/import-catalog/?_xm=383621481969483790#shop_builder_how_to_import_google_catalog) ([https://developers.xsolla.com/doc/shop-builder/how-to/import-catalog/?_xm=383621481969483790#shop_builder_how_to_import_google_catalog](https://developers.xsolla.com/doc/shop-builder/how-to/import-catalog/?_xm=383621481969483790#shop_builder_how_to_import_google_catalog)).warning

Make sure to perform this action after every Google Play product catalog update.

Use this method to manually manage your Xsolla Catalog (IAPs) through the Publisher Account interface.

### Add a Virtual Item with Basic Settings to the Catalog

 
- Open your project in Publisher Account and go to **Items catalog** > **All items** section. 
- Click **Add** and select **Virtual item** from the drop-down list. 

![virtual items create]
 
- Configure the basic settings. Specify the following:    - image (optional)  - SKU  - one or multiple groups  - item name  - item description (optional)    Leave the item type set to the default one — **Consumable** (recommended). 
- Set up virtual item pricing:    - To create a free virtual item, in the **Paid or free** field, select **Free item**.  - To create a paid virtual item, in the **Paid or free** field, select **Paid item**, and specify the price in one or more currencies.   note To ensure that the API calls for getting the catalog work correctly, make sure that the default currency and the list of currencies in which prices are specified match for all items. 
- Change the item status to **Available**. 

![virtual items status]
 
- Click **Create item**. 

For advanced catalog features (virtual currency, bundles, item groups, and more), see the [**catalog creation guide ↗**](https://developers.xsolla.com/doc/shop-builder/features/virtual-items/?_xm=383621481969483790#shop_builder_virtual_items_set_up_in_pa) ([https://developers.xsolla.com/doc/shop-builder/features/virtual-items/?_xm=383621481969483790#shop_builder_virtual_items_set_up_in_pa](https://developers.xsolla.com/doc/shop-builder/features/virtual-items/?_xm=383621481969483790#shop_builder_virtual_items_set_up_in_pa)).warning

There is a file size limit of **7MB** (~**1000** records).

No public documentation is available at this moment. Please contact your Account Manager for more details and manual import/sync.

You can use Rest API to manage Virtual Items (IAPs) in the **Publisher Account**. This will allow you to set up inventory sync between your game server product catalog and Xsolla Catalog.
 
- #### Get Items   ```text GET /v2/project/{project_id}/admin/items/virtual_items ```   Documentation can be found [here ↗](https://developers.xsolla.com/api/igs-bb/operation/admin-get-virtual-items-list/) ([https://developers.xsolla.com/api/igs-bb/operation/admin-get-virtual-items-list/](https://developers.xsolla.com/api/igs-bb/operation/admin-get-virtual-items-list/)) 
- #### Create Item   ```text POST /v2/project/{project_id}/admin/items/virtual_items ```   Documentation can be found [here ↗](https://developers.xsolla.com/api/igs-bb/operation/admin-create-virtual-item/) ([https://developers.xsolla.com/api/igs-bb/operation/admin-create-virtual-item/](https://developers.xsolla.com/api/igs-bb/operation/admin-create-virtual-item/)) 
- #### Update Item   ```text PUT /v2/project/{project_id}/admin/items/virtual_items/sku/{item_sku} ```   Documentation can be found [here ↗](https://developers.xsolla.com/api/igs-bb/operation/admin-update-virtual-item/) ([https://developers.xsolla.com/api/igs-bb/operation/admin-update-virtual-item/](https://developers.xsolla.com/api/igs-bb/operation/admin-update-virtual-item/)) 
- #### Delete Item   ```text DELETE /v2/project/{project_id}/admin/items/virtual_items/sku/{item_sku} ```   Documentation can be found [here ↗](https://developers.xsolla.com/api/igs-bb/operation/admin-delete-virtual-item/) ([https://developers.xsolla.com/api/igs-bb/operation/admin-delete-virtual-item/](https://developers.xsolla.com/api/igs-bb/operation/admin-delete-virtual-item/))

Webhooks

https://developers.xsolla.com/sdk/publisher-account/webhooks

Notifications about events in the Xsolla ecosystem — when a payment or user validation occurs, Xsolla sends an HTTP request with event data to your game server. 

## Setting up Webhooks in Publisher Account
 

![webhooks settings in Publisher Account] 

### Enabling Webhooks
 
 
- Open your project in the *Publisher Account*. 
- In the side menu, go to **Settings** and select **Webhooks**. 
- In the **Webhook server** field, specify the URL of your server where you want webhooks to be sent (e.g. `https://www.my-backend.com/webhooks`). 
- A secret key to sign project webhooks is generated by default. To generate a new secret key, click the refresh icon. 
- Click **Enable webhooks**. 
 

### Disabling Webhooks
 
 
- Open your project in the *Publisher Account*. 
- In the side menu, go to **Settings** and select **Webhooks**. 
- Click **Disable webhooks**. 
 

### Enabling Xsolla Events API
 

The **Xsolla Events API** (Events API) serves as an alternative to the conventional webhook-based integrations, where the integrator’s backend usually handles most of the logic (purchase validation, awarding, etc.). Instead of sending webhooks directly, the API intercepts them and stores their payloads in a remote queue managed by Xsolla. This approach enables serverless integrations for projects that lack backend capacity or don't require one, yet still prefer handling the entire transaction flow directly from the app. The SDK leverages the Events API to support this type of integration. 

Opting for the **Xsolla Events API** **disables** your project's **webhooks** — you **cannot use both simultaneously**. ![Events API settings in Publisher Account] 

To enable the *Events API* for your project, follow these simple steps: 
 
- Open your project in the *Publisher Account*. 
- In the side menu, go to **Settings** and select **Webhooks**. 
- Select **Use API**. 
 

To initialize the SDK in Events API mode, see one of the following platform-specific guides below: 
 
- [**Android**](https://developers.xsolla.com/sdk/mobilesdk/sdk/configuration/android#xsolla-events-api) 
- [**iOS**](https://developers.xsolla.com/sdk/mobilesdk/sdk/configuration/ios#using-xsolla-events-api-for-restoration) 
- [**Unity**](https://developers.xsolla.com/sdk/unity/sdk/configuration/#using-xsolla-events-api-for-restoration) 
 

## Working with Webhooks
 

Below are the examples of commonly used webhooks. 

### User Validation (user_validation)
 Example

```bash
curl -v 'https://your.hostname/your/uri' \
    -X POST \
    -H 'Accept: application/json' \
    -H 'Content-Type: application/json' \
    -H 'Authorization: Signature 13342703ccaca5064ad33ba451d800c5e823db8f' \
    -d '{
        "notification_type":"user_validation",
        "settings": {
            "project_id": 18404,
            "merchant_id": 2340
        },
        "user": {
            "ip": "127.0.0.1",
            "phone": "18777976552",
            "email": "email@example.com",
            "id": "1234567",
            "name": "John Smith",
            "country": "US"
        }
    }'
```
 

### Payment (payment)
 Example

```bash
curl -v 'https://your.hostname/your/uri' \
    -X POST \
    -d '{
            "notification_type": "payment",
            "settings": {
            "project_id": 18404,
            "merchant_id": 2340
        },
        "purchase": {
            "subscription": {
                "plan_id": "b5dac9c8",
                "subscription_id": "10",
                "product_id": "Demo Product",
                "date_create": "2014-09-22T19:25:25+04:00",
                "date_next_charge": "2014-10-22T19:25:25+04:00",
                "currency": "USD",
                "amount": 9.99
            },
            "checkout": {
                "currency": "USD",
                "amount": 50
            },
            "total": {
                "currency": "USD",
                "amount": 200
            },
            "promotions": [{
                "technical_name": "Demo Promotion",
                "id": 853
            }],
            "coupon": {
                "coupon_code": "ICvj45S4FUOyy",
                "campaign_code": "1507"
            },
            "order": {
                "id": 1234
                "lineitems": [
                    {
                        "sku": "test_1",
                        "quantity": 1,
                        "price": {
                            "currency": "EUR",
                            "amount": 6.5
                        }
                    }
                ]
            }
        },
        "user": {
            "ip": "127.0.0.1",
            "phone": "18777976552",
            "email": "email@example.com",
            "id": "1234567",
            "name": "John Smith",
            "country": "US"
        },
        "transaction": {
            "id": 1,
            "external_id": 1,
            "payment_date": "2014-09-24T20:38:16+04:00",
            "payment_method": 1,
            "payment_method_name": "PayPal",
            "payment_method_order_id": 1234567890123456789,
            "dry_run": 1,
            "agreement": 1
        },
        "payment_details": {
            "payment": {
                "currency": "USD",
                "amount": 230
            },
            "vat": {
                "currency": "USD",
                "amount": 0,
                "percent": 20
            },
            "sales_tax": {
                "currency": "USD",
                "amount": 0,
                "percent": 0
            },
            "direct_wht": {
                "currency": "USD",
                "amount": 0,
                "percent": 0
            },
            "payout_currency_rate": "1",
            "payout": {
                "currency": "USD",
                "amount": 200
            },
            "xsolla_fee": {
                "currency": "USD",
                "amount": 10
            },
            "payment_method_fee": {
                "currency": "USD",
                "amount": 20
            },
            "repatriation_commission": {
                "currency": "USD",
                "amount": 10
            }
        },
        "custom_parameters": {
            "parameter1": "value1",
            "parameter2": "value2"
        }
    }'
```
 tip

For a complete list of supported webhooks visit [**here ↗**](https://developers.xsolla.com/webhooks/overview).

Xsolla SDK for Unity

https://developers.xsolla.com/sdk/unity

AvailableXsolla SDK for Unity[SDK Explorer →](https://developers.xsolla.com/sdk/demo/)

The **Xsolla SDK for Unity** empowers developers to seamlessly integrate in-game payments via **Xsolla Pay Station** across **mobile**, **PC**, and **web** Unity projects, supporting monetization for both in-store builds (e.g., App Store iOS and Google Play builds in the USA) and out-of-store distributed game builds (e.g., Android APK, Notarized iOS, standalone PC) — all through a single integration. Connect directly with players, maintain control over the checkout experience, and keep up to 95% of your revenue. Games automatically benefit from updates for new monetization tools, regulatory compliance, and global policy changes.

Trusted by the studios behind *Crossout*, *Modern Warship*, and *Fantasy Tales: Sword and Magic* to power cross-platform commerce across iOS, Android, PC, Web, and in-store ecosystems.  

## Platform Support
 

### iOS & Android
 

Following the **April 30, 2025** U.S. court decision, developers may include external payment links in iOS apps. Following the **October 29, 2025** Epic v. Google injunction, U.S. Google Play apps may use alternative billing and link-outs. The **Xsolla SDK for Unity** is fully compliant with both Apple's revised App Store guidelines and Google Play's updated billing policies. 

### Comprehensive Coverage
 
 
- **iOS**: Full support with compliant link-out payment options 
- **Android**: Out-of-store distribution and alternative billing support 
- **Windows**: Desktop monetization support 
- **macOS**: macOS application support 
- **Web**: WebGL monetization support 
  

## Key Benefits for Unity Developers
 **One Integration, Every Platform**

Get up and running on iOS, Android, Windows, macOS, and Web with a single SDK. Wherever your Unity game ships, your monetization stays consistent — and in your control.**DMA & Court-Order Compliant**

Full compliance with Apple's revised App Store guidelines and Google Play's updated billing policies — including link-out payments and alternative billing solutions, out-of-the-box.**Up to 95% Revenue Retention**

Connect directly with players outside the app stores. Bypass platform fees, maintain pricing control, and build direct player relationships that drive long-term engagement.**1,000+ Payment Methods**

Global payment infrastructure covering 200+ regions, 130+ currencies, and 25+ UI languages — with localized checkout flows and advanced anti-fraud included.**Built for Unity Workflows**

Native Unity IAP integration, easy install via Package Manager, and support for Unity 2021+ across all targets. Compliance and policy updates ship automatically — no manual SDK updates required.  

## Getting Started
 

Ready to integrate the Xsolla SDK into your Unity project? Follow the Quick Start guide to process your first test payment in minutes, or contact your Account Manager for personalized implementation guidance. **Xsolla Buy Button**Fast Path

One-tap Web Shop purchase flows with 1,000+ payment methods — no SDK installation required. Launch browser-based checkout directly from your Unity game across PC, mobile (compliant with platform policies where applicable), and web.[Learn more →](https://developers.xsolla.com/sdk/unity/mobile/buy-button)  [QUICK START**Integration Guide**Process your first test payment in 5 steps→](https://developers.xsolla.com/sdk/unity/quick-start)[FAST PATH**Buy Button**One-tap external payments — no full SDK required→](https://developers.xsolla.com/sdk/unity/mobile/buy-button)[REFERENCE**Full SDK Docs**Advanced features, retry policies, custom UI→](https://developers.xsolla.com/sdk/unity/sdk/installation)

Xsolla SDK for Unity - Mobile

https://developers.xsolla.com/sdk/unity/mobile

ℹ️U.S. rules for iOS and Android — external payment link-outs and alternative billing solutions

**iOS — Effective Apr 30, 2025:** Developers may include external payment links in U.S. apps. The **Xsolla SDK** aligns with Apple's updated guidelines.

**Android — Effective Oct 29, 2025:** Following the **Epic v. Google** injunction, U.S. **Google Play** apps may guide users to **external payment methods via link-outs**, use **alternative billing solutions**, and are no longer subject to **anti-steering restrictions**.

For both platforms, the fastest recommended non-SDK approach is the [**Buy Button via link-outs to Web Shop** ↗](https://developers.xsolla.com/solutions/web-shop/other/instant-purchase) ([https://developers.xsolla.com/solutions/web-shop/other/instant-purchase](https://developers.xsolla.com/solutions/web-shop/other/instant-purchase)).

⚡ **Seamless mobile commerce**: Integrate DMA-compliant billing, out-of-store distribution, and access to over **1,000 local payment methods**—all within your Unity mobile builds.

This guide helps you integrate the Xsolla SDK for Unity to start accepting payments in your Unity game on Android and iOS. By the end, you'll have a fully functioning payment system in your game and experience firsthand how Xsolla streamlines monetization.

Let's dive in and get your game ready to accept payments in just a few easy steps! 

## Quick start

This quick start guide makes it simple to integrate the *Xsolla SDK for Unity* and start accepting payments in your *Unity* game. We'll use a pre-configured test project to cover all the essentials: setting up basic user authentication, importing your products (SKUs), and processing a test payment.

For detailed instructions, please refer to the [**Quick start**](https://developers.xsolla.com/sdk/unity/quick-start).

## Setting up a Publisher Account

Before you begin integrating Xsolla SDK into your game, you'll need to register a Publisher Account.

The Publisher Account is the primary tool for configuring Xsolla features, working with analytics and transactions similar to Google Play Console and App Store Connect for SKU management.important

You'll need to have an active Publisher Account to obtain a Project ID you'd use throughout the whole SDK integration process.

For detailed instructions, please refer to [**Setting up a Publisher Account**](https://developers.xsolla.com/sdk/publisher-account).

## Install the SDK

Now that you've set up your Publisher Account, the next step is to install the Xsolla SDK for Unity plugin in your Unity project.

For general installation instructions, see the [**Installation Guide**](https://developers.xsolla.com/sdk/unity/sdk/installation).

#### Platform-specific Details

For platform-specific instructions, follow the links below:

The SDK installation process on Android requires resolving project dependencies.

To learn how to do that, see [**Resolving Android dependencies ↗**](https://developers.xsolla.com/sdk/unity/sdk/installation/resolving-android-dependencies)

No additional steps required for iOS

## Configure the SDK

Once you've installed the SDK, the next step is configuration.

For detailed instructions, please refer to the [**Configuration Guide**](https://developers.xsolla.com/sdk/unity/sdk/configuration).

#### Platform-specific Details

For platform-specific instructions, follow the links below:

### Buy Button Solution or Web Shop Link-outs (Recommended)
important

To enable **U.S.-only** link-outs **effective October 29, 2025**, first detect the **Google Play** storefront country code using the guide provided [**here**](https://developers.xsolla.com/sdk/unity/sdk/advanced/detect-gpb-storefront-android).

For details on configuring the *Buy Button* solution or *Web Shop Link-outs*, see [**here**](https://developers.xsolla.com/sdk/unity/mobile/buy-button).

### Standard

When distributing your app through alternative channels outside of Google Play Store, you may need to detect where your app was installed from.

To learn how to do that, see [**How to detect store installation**](https://developers.xsolla.com/sdk/unity/sdk/advanced/detectstore).

For compliance in the U.S. regarding redirecting players to the web and/or in-app payment compliance in Europe under the DMA see:
 
- [**How to detect iOS Storefront**](https://developers.xsolla.com/sdk/unity/sdk/advanced/ios-storefront) 
- [**Configuration - iOS**](https://developers.xsolla.com/sdk/unity/sdk/configuration/ios) 

## Initialize the SDK

With configuration complete, you're now ready to initialize the SDK in your Unity project.

For detailed steps and best practices, refer to [**Initialization**](https://developers.xsolla.com/sdk/unity/sdk/initialization).

## Make a Purchase

Now that the SDK is initialized, your app is ready to start processing payments.

For detailed instructions, please refer to [**Make Purchase**](https://developers.xsolla.com/sdk/unity/sdk/purchase).

## Collect Payment

The SDK leverages **Xsolla Pay Station** for *secure* payment collection.

These steps will guide you through a test payment collection flow:
 
- Choose the **Card** payment method (use one of the test cards listed [**here ↗**](https://developers.xsolla.com/doc/pay-station/testing/test-cards) ([https://developers.xsolla.com/doc/pay-station/testing/test-cards](https://developers.xsolla.com/doc/pay-station/testing/test-cards))) and click **Pay** to initiate the payment:  ![quick start3] 
- Wait for the payment to go through:  ![quick start3] 
- Once the payment is completed, you'll see a confirmation:  ![quick start3] 

 
- Choose the **Card** payment method:  ![quick start3] 
- Use one of the test cards listed [**here ↗**](https://developers.xsolla.com/doc/pay-station/testing/test-cards) ([https://developers.xsolla.com/doc/pay-station/testing/test-cards](https://developers.xsolla.com/doc/pay-station/testing/test-cards)) and click **Pay** to confirm the payment:  ![quick start3] 
- Once the payment goes through, you'll see a confirmation:  ![quick start3] 

## Purchase Validation

After setting up payments, you might want to validate your purchase to ensure transaction security and reliability.

For detailed instructions, please refer to [**Purchase Validation**](https://developers.xsolla.com/sdk/unity/sdk/validation).

## Testing & Sandbox

As a final step, use the sandbox environment to safely test your entire payment integration without real transactions.

For detailed instructions, please refer to the [**Testing & Sandbox**](https://developers.xsolla.com/sdk/unity/sdk/testing).

## What's next?

Congratulations! 🎉 You've successfully integrated **Xsolla SDK for Unity**!

This achievement unlocks access to over 1000 global payment methods and sets the foundation for integrating more Xsolla solutions, including our powerful Web Shop.

Buy Button - Unity

https://developers.xsolla.com/sdk/unity/mobile/buy-button

Two simple ways to add external payments to your Unity app: redirect users to your Web Shop, or launch Pay Station's native in-app UI. Both are production-ready — pick the one that fits your workflow. Recommended

#### Web Shop Link-outs

Redirect users to your existing Xsolla Web Shop in the browser.

- Already have an Xsolla Web Shop
- Want minimal code changes
- Need web and mobile payment consistency
- Doing a phased migration from web

#### Buy Button Solution

Full payment UI handled natively inside your app — no browser redirect.

- Want a fully native integration
- Need 1,000+ payment methods out of the box
- Prefer Xsolla to manage the payment UI
- Want automatic regional compliance
 
 important

To enable **U.S.-only** link-outs, first detect the storefront country code: [**Android**](https://developers.xsolla.com/sdk/unity/sdk/advanced/detect-gpb-storefront-android) (effective Oct 29, 2025) · [**iOS**](https://developers.xsolla.com/sdk/unity/sdk/advanced/ios-storefront) (effective Apr 30, 2025). 

```csharp
// Provide Web Shop URL in settings
var settings = XsollaClientSettings.Builder.Create()
    // An URL to your Xsolla-based web shop.
    .SetWebShopUrl("https://yourwebshop.xsolla.site")
    // A deeplink URL used to return the purchase result to your app.
    // E.g. `myapp://open`.
    .SetRedirectUrl("<schema>://<host>")
    .Build();

// Enable Web Shop simple mode and pass user identifier
var configuration = XsollaClientConfiguration.Builder.Builder.Create()
    .SetSettings(settings)
    // Make sure the mode is switched to `XsollaClientConfiguration.SimpleMode.WebShop` for link-outs.
    .SetSimpleMode(XsollaClientConfiguration.SimpleMode.WebShop)
    // User ID to be associated with the purchase.
    .SetUserId("<USER_ID>")
    .Build();
```

This opens your Web Shop in the device browser. Fulfillment happens via your server-side webhooks — see [webhook setup](https://developers.xsolla.com/sdk/publisher-account/webhooks).

```csharp
var settings = XsollaClientSettings.Builder.Create()
    // It's important to set this setting to `true`.
    .SetUseBuyButtonSolution(true)
    // .. your other settings
    .Build();

var configuration = XsollaClientConfiguration.Builder.Builder.Create()
    .SetSettings(settings)
    // Make sure the mode is switched to `XsollaClientConfiguration.SimpleMode.Off` for the `Buy Button` solution.
    .SetSimpleMode(XsollaClientConfiguration.SimpleMode.Off)
    // .. your other settings
    .Build();
```

Enable this once in your SDK configuration — Buy Button activates automatically for all [default purchase flows](https://developers.xsolla.com/sdk/unity/sdk/purchase). See [configuration docs](https://developers.xsolla.com/sdk/unity/sdk/configuration) for advanced options. Still not sure?

**Web Shop** is great if you have an existing store or want to start with minimal changes. **Pay Station** is the fastest path to a fully native integration. Both support the same payment methods and regions worldwide.

Xsolla SDK for Unity - PC

https://developers.xsolla.com/sdk/unity/pc

info

**Xsolla SDK for Unity** is an evolution of the enterprise-level Xsolla SDK solution, which was designed for integration into large-scale projects.

💡 **Global-ready PC monetization**: leverage Xsolla's payment engine for compliant, localized checkout—via Web Shop, Buy Button, or direct API—to maximize revenue in premium and F2P models alike.

This guide helps you integrate the Xsolla SDK for Unity to start accepting payments in your Unity game on PC. By the end, you'll have a fully functioning payment system in your game and experience firsthand how Xsolla streamlines monetization.

Let's dive in and get your game ready to accept payments in just a few easy steps! 

## Quick start

This quick start guide makes it simple to integrate the *Xsolla SDK for Unity* on PC and start accepting payments in your *Unity* game. We'll use a pre-configured test project to cover all the essentials: setting up basic user authentication, importing your products (SKUs), and processing a test payment.

For detailed instructions, please refer to the [**Quick Start Guide**](https://developers.xsolla.com/sdk/unity/quick-start).

## Setting up a Publisher Account

Before you begin integrating Xsolla SDK into your game, you'll need to register a Publisher Account.

The Publisher Account is the primary tool for configuring Xsolla features, working with analytics and transactions similar to Google Play Console and App Store Connect for SKU management.important

You'll need to have an active Publisher Account to obtain a Project ID you'd use throughout the whole SDK integration process.

For detailed instructions, please refer to [**Setting up a Publisher Account**](https://developers.xsolla.com/sdk/publisher-account).

## Install the SDK

Now that you've set up your Publisher Account, the next step is to install the Xsolla SDK for Unity plugin in your Unity project.

For detailed installation instructions, see the [**Installation Guide**](https://developers.xsolla.com/sdk/unity/sdk/installation).

## Configure the SDK

Once you've installed the SDK, the next step is configuration.

For detailed instructions, please refer to the [**Configuration Guide**](https://developers.xsolla.com/sdk/unity/sdk/configuration).info

For Unity Editor, Windows, and macOS builds, the `WebViewType` will default to `Auto` and will launch in the system's default external browser.

This behavior ensures compatibility and consistency during development and testing across platforms.

## Initialize the SDK

With configuration complete, you're now ready to initialize the SDK in your Unity project.

For detailed steps and best practices, refer to the [**Initialization Guide**](https://developers.xsolla.com/sdk/unity/sdk/initialization).

## Make a Purchase

Now that the SDK is initialized, your app is ready to start processing payments.

For detailed instructions, please refer to [**Make Purchase**](https://developers.xsolla.com/sdk/unity/sdk/purchase).

## Collect Payment

The SDK leverages **Xsolla Pay Station** for *secure* payment collection.

These steps will guide you through a test payment collection flow:
 
- Choose the **Card** payment method:  ![quick start3] 
- Use one of the test cards listed [**here ↗**](https://developers.xsolla.com/doc/pay-station/testing/test-cards) ([https://developers.xsolla.com/doc/pay-station/testing/test-cards](https://developers.xsolla.com/doc/pay-station/testing/test-cards)) and click **Pay** to confirm the payment:  ![quick start3] 
- Once the payment goes through, you'll see a confirmation:  ![quick start3] 

## Purchase Validation

After setting up payments, you might want to validate your purchase to ensure transaction security and reliability.

For detailed instructions, please refer to [**Purchase Validation**](https://developers.xsolla.com/sdk/unity/sdk/validation).

## Testing & Sandbox

As a final step, use the sandbox environment to safely test your entire payment integration without real transactions.

For detailed instructions, please refer to the [**Testing & Sandbox**](https://developers.xsolla.com/sdk/unity/sdk/testing).

## What's next?

Congratulations! 🎉 You've successfully integrated **Xsolla SDK for Unity** on PC!

This achievement unlocks access to over 1000 global payment methods and sets the foundation for integrating more Xsolla solutions, including our powerful Web Shop.

Quick Start — Unity SDK

https://developers.xsolla.com/sdk/unity/quick-start

Get the **Xsolla SDK for Unity** integrated and process your first test payment. We'll use pre-configured test credentials so you can start in minutes.

🔥 **Proven in production**: follow the exact integration path used by games like *Crossout* and *Modern Warship*, complete with **DMA-compliant billing** and **1,000+ payment methods** ready out of the box. 

## Getting Started
 

Install the SDK via Unity Package Manager: 
 
- In the Unity Editor, click **Window** > **Package Manager**. 
- Click the ➕ icon and select **Add package from git URL...**. 
- Enter `https://github.com/xsolla/xsolla-sdk-unity.git` and click **Add**. 
 

Then configure your project credentials: 
 
- Go to **Window > Xsolla > SDK > Edit Settings**. 
- In the Inspector panel, set these test values: 
 

| Property | Value |
| --- | --- |
| Project ID | 301871 |
| Login ID | dfcb133b-6d0b-4937-b8d2-c4f4d58fb53a |
 

![Settings asset] 

See [installation details](https://developers.xsolla.com/sdk/unity/sdk/installation) for information on more installation possibilities. PREREQUISITE

Make sure Unity **In-App Purchasing `4.15+`** is installed.

See the official [Unity IAP setup guide ↗](https://docs.unity3d.com/Packages/com.unity.purchasing@4.15/manual/GettingStarted.html) ([https://docs.unity3d.com/Packages/com.unity.purchasing@4.15/manual/GettingStarted.html](https://docs.unity3d.com/Packages/com.unity.purchasing@4.15/manual/GettingStarted.html)).

⚠️ Unity **In-App Purchasing `5.x`** is **NOT** supported at this moment. 

## Authenticate

Add this to your `MonoBehaviour`'s `Start()` method:

```csharp
using UnityEngine.Purchasing;

public class YourSDKIntegrationBehaviour : MonoBehaviour, IDetailedStoreListener {
    void Start() {
        var settings = XsollaStoreClientSettingsAsset.Instance().settings;

        var configuration = XsollaStoreClientConfiguration.Builder.Create()
            .SetSettings(settings)
            .SetSandbox(true)
            .SetLogLevel(XsollaLogLevel.Debug)
            .Build();

        // Continue with Load Catalog
    }
}
```

This loads your project settings and creates a `Configuration` with sandbox mode and debug logging. For production, replace the test project ID and login ID with your own from [**Publisher Account**](https://developers.xsolla.com/sdk/publisher-account). See [configuration docs](https://developers.xsolla.com/sdk/unity/sdk/configuration) for advanced options.important

If you are using your own **Project/Login IDs**, make sure to enable **Device ID login** in your Project. Learn how to enable it here: [https://developers.xsolla.com/sdk/publisher-account/login#enable-device-id-login-in-publisher-account](https://developers.xsolla.com/sdk/publisher-account/login#enable-device-id-login-in-publisher-account)iOS

For U.S. compliance, use `WebViewType.External`; for EU (DMA) compliance, use `WebViewType.Auto`:

```csharp
settings = XsollaClientSettings.Builder.Update(settings)
    .SetWebViewType(XsollaClientSettings.WebViewType.External)  // or .Auto for EU
    .Build();
```

## Load Catalog

Register your products and initialize Unity IAP. Unlike native mobile SDKs where catalog loading is a separate async call, Unity IAP fetches your products automatically during initialization — just declare your SKUs.

```csharp
// Inside Start(), after creating configuration:
var module = XsollaPurchasingModule.Builder.Create()
    .SetConfiguration(configuration)
    .Build();

var configurationBuilder = ConfigurationBuilder.Instance(module)
    .AddProduct("money.100", ProductType.Consumable);  // Test product SKU

UnityPurchasing.Initialize(this, configurationBuilder);
```

This registers your test product and starts the purchasing system. When `OnInitialized` fires, your catalog is loaded and ready to purchase. See [initialization docs](https://developers.xsolla.com/sdk/unity/sdk/initialization) for Unity IAP setup and advanced options.

## Purchase Product

The `OnInitialized` callback fires when the catalog is loaded. Use it to launch a purchase:

```csharp
// IDetailedStoreListener callback — catalog loaded and ready:
public void OnInitialized(IStoreController controller, IExtensionProvider extensions) {
    controller.InitiatePurchase("money.100");
}
```

This opens **Xsolla Pay Station** where users complete payment. For testing, use a [test card ↗](https://developers.xsolla.com/doc/pay-station/testing/test-cards) ([https://developers.xsolla.com/dev-resources/testing/test-cards/](https://developers.xsolla.com/dev-resources/testing/test-cards/)):

![Choose payment method]

![Processing payment]

![Payment confirmed]

## Finalize Purchase

Once the purchase completes, process it and award the product to the user:

```csharp
// IDetailedStoreListener callback — purchase completed:
public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs args) {
    if (args.purchasedProduct.definition.id == "money.100") {
        Debug.Log("Purchase consumed - award item to user");
        // Award the product (virtual item) to the user here
    }
    return PurchaseProcessingResult.Complete;
}
```

Returning `PurchaseProcessingResult.Complete` acknowledges the transaction. See [purchase flow docs](https://developers.xsolla.com/sdk/unity/sdk/purchase) for error scenarios and advanced options. 

## What's next?
 

You've built a complete payment integration: authentication, catalog loading, purchase flow, and fulfillment. Your app now has access to 1,000+ payment methods across 200+ countries. 
 
- **[Set up your project](https://developers.xsolla.com/sdk/publisher-account)** — create your Publisher Account and replace the sandbox test credentials with production ones 
- **[Buy Button](https://developers.xsolla.com/sdk/unity/mobile/buy-button)** — fastest path to external payments 
- **[Extended example](https://developers.xsolla.com/sdk/unity/quick-start/extended-example)** — complete working implementation 
- **[Full SDK docs](https://developers.xsolla.com/sdk/unity/sdk/installation)** — advanced features, retry policies, custom UI

Extended Example (IAP)

https://developers.xsolla.com/sdk/unity/quick-start/extended-example

**Xsolla SDK for Unity** seamlessly integrates with *Unity InAppPurchasing* by providing an extension module for it. 

This integration works seamlessly across both iOS and Android platforms, allowing you to implement in-app purchases effortlessly. 

All you need to do is configure and initialize the module anywhere in your code before attempting to make a purchase. 

## Configuration & Initialization
 PREREQUISITE

Make sure Unity **In-App Purchasing `4.15+`** is installed.

See the official [Unity IAP setup guide ↗](https://docs.unity3d.com/Packages/com.unity.purchasing@4.15/manual/GettingStarted.html) ([https://docs.unity3d.com/Packages/com.unity.purchasing@4.15/manual/GettingStarted.html](https://docs.unity3d.com/Packages/com.unity.purchasing@4.15/manual/GettingStarted.html)).

⚠️ Unity **In-App Purchasing `5.x`** is **NOT** supported at this moment. 

```csharp
public class MyIAPManager
{
    
    // ...

    public MyIAPManager()
    {
        var settings = XsollaStoreClientSettings.Builder.Create()
            .SetProjectId(301871)
            .SetLoginId("dfcb133b-6d0b-4937-b8d2-c4f4d58fb53a")
            .SetSandbox(true)
            .Build();

        var configuration = XsollaStoreClientConfiguration.Builder.Create()
            .SetSettings(settings)
            .Build();

        var module = XsollaPurchasingModule.Builder.Create()
            .SetConfiguration(configuration)
            .Build();

        var builder = ConfigurationBuilder.Instance(module);

        builder.AddProduct("money.100", ProductType.Consumable, new IDs
        {
           { "money.100", XsollaPurchasingModule.StoreName },
           { "money_100_google", GooglePlay.Name },
           { "money_100_mac", MacAppStore.Name }
        });

        UnityPurchasing.Initialize(this, builder);
    }

    // ...

}
```
 important

If you are using your own **Project/Login IDs**, make sure to enable **Device ID login** in your Project. Learn how to enable it here: [https://developers.xsolla.com/sdk/publisher-account/login#enable-device-id-login-in-publisher-account](https://developers.xsolla.com/sdk/publisher-account/login#enable-device-id-login-in-publisher-account) tip

The official **Unity IAP** initialization guide can be found [**here ↗**](https://docs.unity3d.com/Packages/com.unity.purchasing@4.15/manual/UnityIAPInitialization.html) ([https://docs.unity3d.com/Packages/com.unity.purchasing@4.15/manual/UnityIAPInitialization.html](https://docs.unity3d.com/Packages/com.unity.purchasing@4.15/manual/UnityIAPInitialization.html)). 

## A fully-fledged implementation
 

We'll base our code on the existing official Unity IAP sample called **"01 Buying Consumables"**: 

[![publisher account 6]](https://developers.xsolla.com/sdk/assets/files/example-6b727680d84cf9a5e89ca70611261c35.png) 

The code: 

```csharp
using UnityEngine;
using UnityEngine.Purchasing;
using UnityEngine.Purchasing.Extension;
using UnityEngine.UI;
using Xsolla.SDK.Common;
using Xsolla.SDK.Store;
using Xsolla.SDK.UnityPurchasing;

namespace Samples.Purchasing.Core.BuyingConsumables
{
    public class BuyingConsumables : MonoBehaviour, IDetailedStoreListener
    {
        IStoreController m_StoreController; // The Unity Purchasing system.

        //Your products IDs. They should match the ids of your products in your store.
        public string moneyProductId = "money.100";
        public string ticketProductId = "ticket.10";

        public Text MoneyCountText;
        public Text TicketCountText;

        int m_MoneyCount;
        int m_TicketCount;

        void Start()
        {
            InitializePurchasing();
            UpdateUI();
        }

        void InitializePurchasing()
        {
            //Remove this line from the original code.
            //var builder = ConfigurationBuilder.Instance(StandardPurchasingModule.Instance());

            //Add these lines here..
            var settings = XsollaStoreClientSettings.Builder.Create()
                .SetProjectId(301871)
                .SetLoginId("dfcb133b-6d0b-4937-b8d2-c4f4d58fb53a")
                .SetSandbox(true)
                .Build();

            var configuration = XsollaStoreClientConfiguration.Builder.Create()
                .SetSettings(settings)
                .Build();

            var module = XsollaPurchasingModule.Builder.Create()
                .SetConfiguration(configuration)
                .Build();

            var builder = ConfigurationBuilder.Instance(module);

            //Add products that will be purchasable and indicate its type.
            builder.AddProduct(moneyProductId, ProductType.Consumable);
            builder.AddProduct(ticketProductId, ProductType.Consumable);

            UnityPurchasing.Initialize(this, builder);
        }

        public void BuyMoney()
        {
            m_StoreController.InitiatePurchase(moneyProductId);
        }

        public void BuyTicket()
        {
            m_StoreController.InitiatePurchase(ticketProductId);
        }

        public void OnInitialized(IStoreController controller, IExtensionProvider extensions)
        {
            Debug.Log("In-App Purchasing successfully initialized");
            m_StoreController = controller;
        }

        public void OnInitializeFailed(InitializationFailureReason error)
        {
            OnInitializeFailed(error, null);
        }

        public void OnInitializeFailed(InitializationFailureReason error, string message)
        {
            var errorMessage = $"Purchasing failed to initialize. Reason: {error}.";

            if (message != null)
            {
                errorMessage += $" More details: {message}";
            }

            Debug.Log(errorMessage);
        }

        public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs args)
        {
            //Retrieve the purchased product
            var product = args.purchasedProduct;

            //Add the purchased product to the players inventory
            if (product.definition.id == moneyProductId)
            {
                AddMoney();
            }
            else if (product.definition.id == ticketProductId)
            {
                AddTicket();
            }

            Debug.Log($"Purchase Complete - Product: {product.definition.id}");

            //We return Complete, informing IAP that the processing on our side is done and the transaction can be closed.
            return PurchaseProcessingResult.Complete;
        }

        public void OnPurchaseFailed(Product product, PurchaseFailureReason failureReason)
        {
            Debug.Log($"Purchase failed - Product: '{product.definition.id}', PurchaseFailureReason: {failureReason}");
        }

        public void OnPurchaseFailed(Product product, PurchaseFailureDescription failureDescription)
        {
            Debug.Log($"Purchase failed - Product: '{product.definition.id}'," +
                $" Purchase failure reason: {failureDescription.reason}," +
                $" Purchase failure details: {failureDescription.message}");
        }

        void AddMoney()
        {
            m_MoneyCount++;
            UpdateUI();
        }

        void AddTicket()
        {
            m_TicketCount++;
            UpdateUI();
        }

        void UpdateUI()
        {
            MoneyCountText.text = $"Your Money: {m_MoneyCount}";
            TicketCountText.text = $"Your Tickets: {m_TicketCount}";
        }
    }
}
```

How to detect Google Play storefront (Unity/Android)?

https://developers.xsolla.com/sdk/unity/sdk/advanced/detect-gpb-storefront-android

When distributing your app through **Google Play**, you may need to detect the current storefront to implement region-specific functionality. The storefront represents the Google Play region associated with the user’s account, identified by a two-letter country code in **ISO-3166-1 alpha-2** format (e.g., `"US"` for United States). important

This detection is particularly important in light of the **Epic v. Google** injunction effective **October 29, 2025**, which permits U.S. **Google Play** apps to guide users to **external payment methods via link-outs**, use **alternative billing solutions**, and prohibits **anti-steering restrictions**. 

You can use storefront detection to: 
 
- Enable alternative billing options specifically for U.S. users 
- Implement region-specific payment flows 
- Control feature availability based on local billing regulations 
- Ensure compliance with U.S. court-mandated requirements 
 

You can use the following snippet to retrieve the country code of the Google Play storefront: important

This snippet is most useful, when **distributing** and **installing** your app **through Google Play store**. To check whether the app has been installed from Google Play store or side-loaded (third-party stores or APK distribution) you can use the `XsollaStoreClientInfo.IsInstalledFromGooglePlayStore()` utility function. tip

Prior to retrieving the Google Play storefront country code, it's advised (but not mandatory) to verify whether Google Play Store is installed on the device as the query function will result in error if the store is not available. For this purpose you can rely on `XsollaStoreClientInfo.IsGooglePlayStoreInstalled()` utility function. 

```csharp
XsollaStoreClientInfo.QueryGooglePlayCountryCodeAsync(
    onSuccess: countryCode =>
    {
        if (countryCode.IsCode("us"))
        {
            // Handle the US storefront.
        }
        else
        {
            // Handle all other storefront variants.
        }
    },
    onError: err =>
    {
        // Handle the error here.
    }
);
```

How to detect store installation?

https://developers.xsolla.com/sdk/unity/sdk/advanced/detectstore

When distributing your app through alternative channels outside of official app stores (like Google Play Store or Apple's App Store), you may need to detect where your app was installed from. This is particularly important for: 
 
- Implementing different payment flows for alternative distribution channels 
- Adjusting app behavior based on the installation source 
- Ensuring compliance with platform-specific requirements 
- Managing features that should only be available in certain distribution channels 
 

To detect whether the game is running in alternative distribution (outside official app stores), use the snippets below: 

Below are multiple helper snippets that can be leveraged to adjust your app's behavior depending on various factors like the installer's package name, Google Play Store's country code, etc.

### How to check if Google Play Store is installed?

Can be useful in scenarios, when a part of functionality shouldn't even be attempted to be run outside Google Play Store ecosystem. This snippet will allow you to determine, if the device has **Google Play Store** installed.

```csharp
if (XsollaStoreClientInfo.IsGooglePlayStoreInstalled())
{
    Debug.Log("Google Play Store found!");
}
else
{
    Debug.Log("Google Play Store NOT found!");
}
```

### Checking if the App is Installed from Google Play Store

This snippet helps to determine whether the app was installed directly from the *Google Play Store* or side-loaded (e.g. installed from an APK).

```csharp
if (XsollaStoreClientInfo.IsInstalledFromGooglePlayStore())
{
    Debug.Log("App was installed from Google Play Store");
}
else
{
    Debug.Log("App was NOT installed from Google Play Store");
}
```

### Querying the installer's package name

If you need to know the exact package name of your app's installer, use the following code below.

```csharp
var installerPackageName = XsollaStoreClientInfo.GetInstallerPackageName();

var matched = installerPackageName.Equals("com.xxx.yyy.zzz", StringComparison.OrdinalIgnoreCase);

if (matched)
{
    Debug.Log($"Expected installer package name: {installerPackageName}");
}
else
{
    Debug.Log($"Unexpected installer package name: {installerPackageName}");
}
```

Or if you prefer a simplified approach, where you don't care about the actual installer's package name, use this code:

```csharp
var matched = XsollaStoreClientInfo.IsInstallerPackageName("com.xxx.yyy.zzz");

if (matched)
{
    Debug.Log("Expected installer package name found!");
}
else
{
    Debug.Log("Expected installer package name NOT found!");
}
```

```csharp
XsollaPurchasingStoreInfo.GetAppleDistributionStatus((isRunningInAlternativeDistribution, error) =>
{
    if (error != null)
        Debug.LogError($"Failed to get Apple Distribution: {error}");
    else
        Debug.Log("Apple Distribution is alternative: " + isRunningInAlternativeDistribution);
});
```

How to detect iOS Storefront?

https://developers.xsolla.com/sdk/unity/sdk/advanced/ios-storefront

When distributing your app through Apple's App Store, you may need to detect the current storefront to implement region-specific functionality. The storefront represents the App Store region that the user is currently using, identified by a three-letter country code (e.g., "USA" for United States). 

This detection is particularly important following the US court decision dated April 30, 2025, which allows developers to connect external payment links in the United States. You can use the storefront detection to: 
 
- Enable external payment links specifically for US users 
- Implement region-specific payment flows 
- Control feature availability based on regional requirements 
- Ensure compliance with local regulations 
 note

If the app is installed via **alternative distribution** (for example, an alternative marketplace/web distribution under the DMA), the Storefront country code may be unavailable and returned as an **empty string**. In that case, use the [**store installation detection**](https://developers.xsolla.com/sdk/unity/sdk/advanced/detectstore) guide to determine whether the app is running in an alternative distribution environment. 

To determine the current iOS storefront and control SDK functionality based on the region, use the following code snippet: 

```csharp
XsollaStoreClientInfo.GetAppleStorefront((storefront, error) =>
{
    if (error != null)
        Debug.LogError($"Failed to get Apple Storefront: {error}");
    else
        Debug.Log("Apple Storefront: " + storefront);
});
```

Trusted Web Activity (Unity/Android)

https://developers.xsolla.com/sdk/unity/sdk/advanced/trusted-web-activity

A [**Trusted Web Activity (TWA) ↗**](https://developer.chrome.com/docs/android/trusted-web-activity) ([https://developer.chrome.com/docs/android/trusted-web-activity](https://developer.chrome.com/docs/android/trusted-web-activity)) enables Android apps to display web content in full-screen mode using Chrome, without any browser UI elements. Unlike WebView, *TWAs* run in the user's browser context, allowing better performance, access to browser features (like push notifications and background sync), and shared cookies, permissions, and storage between the app and the browser. important

To enable *Trusted Web Activity*, contact your integration manager with the app’s package name and certificate hash. DID YOU KNOW?

*Trusted Web Activity* support is project-independent and applies at the app level — based on the package name and signing certificate only. 

### Configuring for Trusted Web Activity in the SDK
 

When configuring the SDK, supply an activity setting to the config like this: 

```csharp
var settings = XsollaStoreClientSettings.Builder.Create()
    // ...other settings
    .SetWebViewType(XsollaStoreClientSettings.WebViewType.Trusted)
    // ...other settings
    .Build();
```
 

This ensures that the *Trusted Web Activity* will be used by the payment flow. warning

*Trusted Web Activity* availability depends on the device's system configuration, i.e. a compatible browser needs to be installed. If *TWA* support is not available, then the SDK falls back to one of the default activity types for payments (e.g. *Custom Tabs* or *WebView*, depending on the environment). 

An example of the *TWA* in action: [](https://developers.xsolla.com/sdk/assets/images/twa-1-737175ed3a44e80fb60131748dda4361.jpg) 

### (Optional) Orientation locking
 

*Trusted Web Activity* natively supports orientation locking when it's shown on the screen. This can be set up during the configuration step with these lines: 

```csharp
var settings = XsollaStoreClientSettings.Builder.Create()
    // ...other settings
    .SetWebViewOrientationLock(XsollaStoreClientSettings.OrientationLock.Auto)
    // ...other settings
    .Build();
```
 

Orientation setting is one of the following values: 
 
- `XsollaStoreClientSettings.OrientationLock.Portrait` - forces the *TWA* to be displayed in *portrait* mode. 
- `XsollaStoreClientSettings.OrientationLock.Landscape` - forces the *TWA* to be displayed in *landscape* mode. 
- `XsollaStoreClientSettings.OrientationLock.Auto` - disables the orientation locking. 
 

### (Optional) Adding a customizable splash screen
 

*Trusted Web Activity* also supports a customizable splash screen that's shown right before the *TWA* itself. 

To enable it, you need to modify the [configuration setting](https://developers.xsolla.com/sdk/unity/sdk/advanced/trusted-web-activity#configuring-for-trusted-web-activity-in-the-sdk) we added earlier by specifying a custom splash screen image using one of the supported approaches. 

An example of the *TWA* splash screen: [](https://developers.xsolla.com/sdk/assets/images/twa-2-fd93e4a14c1a92ff7574a04d1a7abf15.jpg) 

#### Setting an image for the splash screen
 

Image can be supplied via two methods: a *drawable ID* and a filepath to the image: 
 
- A *drawable ID*: important The drawable needs to be added to the Android resource manually beforehand.  ```csharp var settings = XsollaStoreClientSettings.Builder.Create()     // ...other settings     .SetWebViewSplashScreenDrawableIdAndroid(<your_splash_screen_drawable_id>)     // ...other settings     .Build(); ```  tip SVG format is also supported, but needs to be wrapped into a valid drawable XML. 
- A filepath: important The image file needs to be discoverable. Supports all major raster image formats.  ```csharp var settings = XsollaStoreClientSettings.Builder.Create()     // ...other settings     .SetWebViewSplashScreenImage(<filepath_to_your_splash_screen_image>)     // ...other settings     .Build(); ``` 
 
 note

If you're using the SDK for Android directly, consider visiting this page for more details: [**Trusted Web Activity - Android**](https://developers.xsolla.com/sdk/mobilesdk/sdk/advanced/trusted-web-activity).

Configuration

https://developers.xsolla.com/sdk/unity/sdk/configuration

In Unity, settings can be set up either using a **settings asset** or via **code**: 
 
- The asset allows for a **codeless** approach, where you don't have to modify the code to adjust the configuration. 
- The **code** approach works better for apps, where the configuration is modified dynamically (e.g., flags and values are received from the app's backend). 
 

In the **Unity Editor**:
 
- Go to **Window** > **Xsolla** > **SDK** > **Edit Settings** from the main menu. 
- Go to the **Inspector** panel. 
- Fill in the **Project ID** and **Login ID** fields. 

[![publisher account 6]](https://developers.xsolla.com/sdk/assets/files/settings-5b8f2955492ac0b0b88f04d06f6382a2.png)tip

To access the asset's settings from the code, use the following snippet:

```csharp
var settings = XsollaStoreClientSettingsAsset.Instance().settings;
```

```csharp
var settings = XsollaStoreClientSettings.Builder.Create()
    .SetProjectId("< your_project_id >")
    .SetLoginId("< your_login_id >")
    .SetLocalPurchasesRestore(true)
    .SetWebViewType(XsollaStoreClientSettings.WebViewType.Auto)
    .SetWebViewOrientationLock(XsollaStoreClientSettings.OrientationLock.Auto)
    .SetUIThemeSize(XsollaStoreClientSettings.ThemeSize.Auto)
    .SetUIThemeStyle(XsollaStoreClientSettings.ThemeStyle.Auto)
    .SetUICustomTheme("")
    .SetUseCloseButton(XsollaStoreClientSettings.CloseButton.Auto)
    .SetUIControlTintColor(Color.clear)
    .SetUIBarTintColor(Color.clear)
    .SetRedirectUrl("")
    .SetRedirectButtonText("")
    .SetRedirectDelay(6)
    .SetEmailCollectionConsentOptIn(null)
    .Build();
```
 

### Android
 

For platform-specific settings, including Custom Tabs / TWA browser provider controls, see [**Configuration - Android**](https://developers.xsolla.com/sdk/unity/sdk/configuration/android). 

### iOS
 important

For compliance in the U.S. regarding redirecting players to the web and/or in-app payment compliance in Europe under the DMA see [**Configuration - iOS**](https://developers.xsolla.com/sdk/unity/sdk/configuration/ios). 

### Buy Button Solution
 

The Buy Button Solution provides a complete, native payment experience with minimal integration effort. This approach handles the entire payment flow within your app using Xsolla's optimized UI components. 

For a full guide (including Web Shop link-outs vs Pay Station, platform notes, and examples), see [**Buy Button ⚡**](https://developers.xsolla.com/sdk/unity/mobile/buy-button). 

**When to use Buy Button Solution:** 
 
- You want a quick, native payment integration 
- You prefer Xsolla to handle payment UI and optimization 
- You need support for multiple payment methods out of the box 
- You want automatic compliance with regional payment regulations 
 

To enable the Buy Button Solution for your payments (works with **default purchase flows** only), use the snippet below: 

```csharp
var settings = XsollaClientSettings.Builder.Create()
    .SetUseBuyButtonSolution(true)
    .Build();
```
 note

Make sure `XsollaClientConfiguration.SimpleMode` is set to `Off`. The Buy Button Solution won't work in Web Shop link-out mode (`SimpleMode.WebShop`). 

### Web Shop Link-outs
 

Use Web Shop link-outs when you want minimal code changes by redirecting the user to your existing Xsolla Web Shop to complete the purchase. caution

In Web Shop link-out mode, the SDK does not execute its native in-app payment flow. It only opens the Web Shop and cancels any SDK purchase callbacks. Handle fulfillment and post-payment updates via your Web Shop processes (for example, webhooks or server-side logic), not via SDK purchase callbacks. 

Configure by providing the Web Shop URL to settings and enabling the Web Shop simple mode in configuration: 

```csharp
// Provide Web Shop URL in settings
var settings = XsollaClientSettings.Builder.Create()
    .SetWebShopUrl("https://yourwebshop.xsolla.site")
    .Build();

// Enable Web Shop simple mode and pass user identifier
var configuration = XsollaClientConfiguration.Builder.Builder.Create()
    .SetSettings(settings)
    .SetSimpleMode(XsollaClientConfiguration.SimpleMode.WebShop)
    .SetUserId("<USER_ID>")
    .Build();
```
 

### Purchase Restoration Settings
 

By default, the SDK automatically handles local purchase restoration to ensure users can recover their purchases when reinstalling your app or when the app crashes or goes out of memory during payment processing. However, this behavior should be adjusted based on your backend integration architecture. 

Alternatively, if you prefer a serverless approach without maintaining webhooks, you can enable the **Xsolla Events API** for purchase restoration (see the subsection below) while keeping local purchase restoration enabled. 

For webhook-based integrations where your backend receives and processes purchase notifications directly from Xsolla, disable local restoration to prevent conflicts with server-side transaction management: 

```csharp
var settings = XsollaStoreClientSettings.Builder.Create()
    //..
    .SetLocalPurchasesRestore(false)
    .SetLocalPurchasesRestoreInterval(0)
    .Build();
```
 
 
- `SetLocalPurchasesRestore`: Enables or disables local purchase restoration (`true` by default). 
- `SetLocalPurchasesRestoreInterval`: Interval in seconds between local purchase restoration attempts (`0` by default — uses SDK defaults). 
 warning

**When to disable local purchase restoration:**
 
- Your backend uses webhooks to receive purchase notifications from Xsolla 
- You handle transaction lifecycle management on your server 
- You want to avoid duplicate purchase processing between local and server-side systems 

**Important limitations:**
 
- Restored purchases cannot be validated through the standard validation flow 
- Transaction finishing must be handled by your backend when webhooks are used 
- If local purchase restoration is disabled, the SDK will not perform restoration via either local restoration or the Events API 
 

This setting directly impacts transaction finishing behavior. When local restoration is disabled, your backend becomes responsible for managing the complete transaction lifecycle through webhook notifications. Note that with local restoration disabled, unfinished transactions will not reappear on app relaunch, making proper backend handling essential. 

#### Using Xsolla Events API for Restoration
 

The **Xsolla Events API** serves as an alternative to webhook-based integrations, where the integrator's backend usually handles most of the logic (purchase validation, awarding, etc.). Instead of sending webhooks directly, the API intercepts them and stores their payloads in a remote queue managed by Xsolla. This approach enables serverless integrations for projects that lack backend capacity or don't require one, yet still prefer handling the entire transaction flow directly from the app. CAUTION

Opting for the *Xsolla Events API* disables your project's webhooks. In other words, you cannot simultaneously handle webhooks on your backend and use the *Events API* in your app. **Enabling Xsolla Events API in Publisher Account**

Follow these steps to enable the *Xsolla Events API*:
 
- Open your *Publisher Account*. 
- In the left sidebar, select the project you want to enable the *Xsolla Events API* for. 
- From the left sidebar, expand the **Payments** menu and select **Webhooks**. 
- On the **Webhooks** page, click **Webhooks** on the right.  ![PublisherAccountEventsAPI] 
- Select **Use API**. 

Your project is now configured to use the *Xsolla Events API*, and the SDK can leverage this configuration.tip

To turn the *Xsolla Events API* mode off, follow the steps above, but select **Use Webhooks** this time. 

##### Enabling Events in the Unity SDK
 

Enable Events-based purchase restoration in the SDK configuration: 

```csharp
var settings = XsollaStoreClientSettings.Builder.Create()
    //..    
    .SetWebhooksMode(XsollaStoreClientSettings.WebhooksMode.EventsApi)
    .Build();
```
 info

For additional information, see the [**webhooks page**](https://developers.xsolla.com/sdk/publisher-account/webhooks#enabling-xsolla-events-api). note

Events-based restoration requires `SetLocalPurchasesRestore(true)`. If you disable local purchase restoration, the SDK will not query or use the Events API for restoration. 

### Custom Pay Station Domains
 

Override the default Pay Station domains for production and sandbox environments. This is useful when you have custom domain setup for your Pay Station. 

```csharp
var configuration = XsollaStoreClientConfiguration.Builder.Create()
    .SetSettings(settings)
    .SetCustomPayStationDomain(
        domainProduction: "pay.yourdomain.com",
        domainSandbox: "sandbox-pay.yourdomain.com"
    )
    .Build();
```
 note

Both domains must be valid absolute URIs. Invalid domains will be logged and ignored. You can pass empty strings or null for either domain to use defaults. 

### Logo Visibility
 

Control the visibility of the Xsolla logo in Pay Station using the `VisibleLogo` setting: 

```csharp
var settings = XsollaStoreClientSettings.Builder.Create()
    .SetVisibleLogo(XsollaClientSettings.VisibleLogo.Auto)  // Auto (default)
    // .SetVisibleLogo(XsollaClientSettings.VisibleLogo.Show)  // Always show
    // .SetVisibleLogo(XsollaClientSettings.VisibleLogo.Hide)  // Always hide
    .Build();
```
 

**Options:** 
 
- `Auto` - Automatically determine whether to show the logo (default behavior) 
- `Show` - Always display the Xsolla logo 
- `Hide` - Never display the Xsolla logo 
 

### Build configuration from the settings
 

```csharp
var configuration = XsollaStoreClientConfiguration.Builder.Create()
    // Set the settings object you've acquired in previous step here..
    .SetSettings(settings)
    .SetSandbox(true)
    .SetLogLevel(XsollaLogLevel.Debug)
    .SetLocale("en")
    .SetFallbackToDefaultLocaleIfNotSet(true)
    .SetFetchProductsWithGeoLocale(false)
    .SetFetchPersonalizedProductsOnly(true)
    .SetTrackingId("")
    .Build();
```
 

Below is the full list of configuration builder methods: 

| Setting | Value | Description |
| --- | --- | --- |
| SetSettings | XsollaClientSettings | The settings object built in the previous step. |
| SetAccessToken | string | Access token for authentication (see Assign access token). |
| SetSandbox | bool | Enables sandbox mode for test payments (default false). |
| SetLogLevel | XsollaLogLevel | Controls the verbosity of SDK logging (None, Error, Warning, Debug). |
| SetDelayedConfigurationTask | Task<OnDelayedConfiguration> | Task for deferred configuration when the access token is not yet available (see Delayed access token). |
| SetLocale | string | Override locale for pricing and formatting (empty string for automatic). |
| SetFallbackToDefaultLocaleIfNotSet | bool | Use the default locale if no override is specified (default false). |
| SetFetchProductsWithGeoLocale | bool | Fetch products using geo-detected locale (default false). |
| SetFetchPersonalizedProductsOnly | bool | Return only personalized products, excluding items whose per-user purchase limit has been reached (default true). |
| SetUserId | string | User identifier, required for Web Shop link-out mode. |
| SetTrackingId | string | Tracking ID for analytics (empty string to omit). |
| SetSimpleMode | SimpleMode | Simple mode setting (Off, WebShop, ServerTokens). |
| SetCustomPayStationDomain | string, string | Override production and sandbox Pay Station domains (see Custom Pay Station Domains). |
 

### Assign Xsolla access token to the configuration
 note

The access token [**JWT-structure ↗**](https://developers.xsolla.com/api/login/overview/#section/JWT-structure) ([https://developers.xsolla.com/api/login/overview/#section/JWT-structure](https://developers.xsolla.com/api/login/overview/#section/JWT-structure)). warning

A valid **access token** must already be known prior to the SDK initialization step.

```csharp
var configuration = XsollaStoreClientConfiguration.Builder.Create()
    .SetSettings(settings)
    .SetAccessToken("< your access token >")
    .Build();
```

If the **access token** is not known yet, when building the configuration, you can easily feed it into the SDK asynchronously using a `Task`. This works best for apps, where the **access token** might be acquired at some later point (e.g., from the app's backend) and cannot be provided right away.

```csharp
var configuration = XsollaStoreClientConfiguration.Builder.Create()
    .SetSettings(settings)
    .SetDelayedConfigurationTask( DelayedTask() )
    .Build();

async Task<XsollaStoreClientConfiguration.OnDelayedConfiguration> DelayedTask()
{
    var task = await Server.GetXsollaTokenAsync();

    return configuration => XsollaStoreClientConfiguration.Builder.Update(configuration)
        .SetSettings( 
            XsollaStoreClientSettings.Builder.Update(configuration.settings)
                .SetProjectId(task.Result.projectId) 
                .Build();
        )   
        .SetAccessToken(task.Result.accessToken)
        .Build();
}
```
 

### Updating access token at runtime
 

If you need to update the access token during the session (for example, after token refresh), use the `UpdateAccessToken` method: 

```csharp
storeClient.UpdateAccessToken("<new_access_token>", error =>
{
    if (error != null)
    {
        Debug.LogError($"Failed to update access token: {error.message}");
        return;
    }

    Debug.Log("Access token updated successfully");
});
```
 tip

This is useful when your access token expires and you need to refresh it without reinitializing the entire SDK. 

### Using device ID instead of the access token
 

Below is an example with the fields required to be set for the device ID authentication approach to work: 

```csharp
var settings = XsollaStoreClientSettings.Builder.Create()
    .SetProjectId("< your_project_id >")
    .SetLoginId("< your_login_id >")
    // ..other properties
    .Build();
```
 

### Using OAuth2 instead of the access token
 

Below is an example with the fields required to be set for the OAuth2 authentication approach to work: 

```csharp
var settings = XsollaStoreClientSettings.Builder.Create()
    .SetProjectId("< your_project_id >")
    .SetLoginId("< your_login_id >")
    .SetOAuthClientId("< your_oauth_client_id >")
    // ..other properties
    .Build();
```
 

### Using a third-party social provider's access token
 

Below is an example of the settings required for the social provider authentication approach to work: 
 
- **With OAuth2**  ```csharp var settings = XsollaStoreClientSettings.Builder.Create()     .SetProjectId("< your_project_id >")     .SetLoginId("< your_login_id >")     .SetOAuthClientId("< your_oauth_client_id >")     .SetSocialProvider(new XsollaClientSettings.SocialProvider() {         name = "epicgames",         accessToken = "<social-access-token>"     })     // ..other properties     .Build(); ``` 
- **Without OAuth2**  ```csharp var settings = XsollaStoreClientSettings.Builder.Create()     .SetProjectId("< your_project_id >")     .SetLoginId("< your_login_id >")     .SetSocialProvider(new XsollaClientSettings.SocialProvider() {         name = "epicgames",         accessToken = "<social-access-token>"     })     // ..other properties     .Build(); ``` 
 

### Deep Links Configuration
 

Deep links are used to return the player back to your game after completing the payment flow. 

By default, the SDK handles redirects automatically. You only need to configure the settings below if you want to **override** the default redirect behavior (for example, use your own redirect URL, change the auto-redirect timing, or override the redirect button/link text). tip

On **iOS**, you can use **Universal Links** (HTTPS) for redirects back to the game to avoid the “Open in *App Name*?” prompt when returning from an external browser. See [**iOS Universal Links**](https://developers.xsolla.com/sdk/mobilesdk/sdk/advanced/ios-universal-links). 

**Setting up redirect URL and delay:** 

```csharp
settings = XsollaClientSettings.Builder.Update(settings)
    .SetRedirectUrl("myapp://payment")
    .SetRedirectDelay(6)
    .SetRedirectButtonText("Back to the game")
    .Build();
```
 

**Parameter explanation:** 
 
- `SetRedirectUrl`: Overrides the URL used to redirect the player back to the game after purchase completion. 
- `SetRedirectDelay`: The delay (in seconds) before Pay Station **automatically redirects** the player back to the game (so the user doesn't need to tap a redirect button). 
- `SetRedirectButtonText`: Overrides the text shown on the redirect button/link in Pay Station. 
 tip

If you override `SetRedirectUrl`, it can be either a **custom scheme** (for example, `myapp://...`) or a **universal link** (HTTPS). Ensure your app is configured to handle the chosen type in the platform-specific project settings (iOS / Android). If you keep the SDK defaults, you typically don't need to configure anything additional. 

### Email Collection Consent
 

Enable opt-in for collecting the user's email as part of the payment flow (for example, newsletter consent): 

```csharp
var settings = XsollaStoreClientSettings.Builder.Create()
    //..
    .SetEmailCollectionConsentOptIn(true)
    .Build();
```
 

Pass `true` to enable, `false` to disable, or `null` to use the default behavior. 

### Testing purchase flow in sandbox mode
 

See [**Testing**](https://developers.xsolla.com/sdk/unity/sdk/testing) guide for more details on how to enable the sandbox mode and avoid spending real money while testing the integration.

Configuration - Android

https://developers.xsolla.com/sdk/unity/sdk/configuration/android

### Custom Tabs / Trusted Web Activity browser selection
 

Use `WebViewType.System` or `WebViewType.Trusted` to enable browser provider controls for Custom Tabs / TWA. Configure provider blacklists, priority weights, and TWA fallback via advanced Android settings: 

| Setting | Value | Description |
| --- | --- | --- |
| SetUserBlacklistedProviders | HashSet<string> | Exclude specific Custom Tabs/TWA providers from being used. |
| SetInternalProviderBlacklistEnabled | bool | Apply the SDK's internal provider blacklist in addition to user-provided exclusions. |
| SetUserBlacklistedTWAProviders | HashSet<string> | Exclude specific TWA providers; lower priority than SetUserBlacklistedProviders. |
| SetInternalTWAProviderBlacklistEnabled | bool | Apply the SDK's internal TWA provider blacklist in addition to user-provided exclusions. |
| SetUserProviderPriorityWeights | Dictionary<string, float> | Adjust provider selection priority via per-package weights (higher weight = higher priority). |
| SetFallbackToFirstAvailableTWAProvider | bool | If requested provider lacks TWA support, fall back to the first available TWA-compatible provider. |
 

```csharp
var advanced = new XsollaClientSettings.AdvancedSettingsAndroid()
    .SetUserBlacklistedProviders(new HashSet<string>{ "com.android.chrome" })
    .SetInternalProviderBlacklistEnabled(true)
    .SetUserBlacklistedTWAProviders(new HashSet<string>{ "com.custom.browser" })
    .SetInternalTWAProviderBlacklistEnabled(true)
    .SetUserProviderPriorityWeights(new Dictionary<string, float>{{ "com.google.android.apps.chrome", 2.0f }})
    .SetFallbackToFirstAvailableTWAProvider(true);

var settings = XsollaStoreClientSettings.Builder.Create()
    .SetWebViewType(XsollaStoreClientSettings.WebViewType.Trusted)
    .SetAdvancedSettingsAndroid(advanced)
    .Build();
```
 

### Cancellation Reason Querying
 

When enabled, the SDK distinguishes between a user closing the payment screen without paying (`Cancelled`) and a payment that was attempted but failed (reported as an error). When disabled (default), both cases report `Cancelled`. 

```csharp
var advanced = new XsollaClientSettings.AdvancedSettingsAndroid()
    .SetQueryCancellationReasonEnabled(true);

var settings = XsollaStoreClientSettings.Builder.Create()
    .SetAdvancedSettingsAndroid(advanced)
    .Build();
```
 

### External browser app relaunch
 

When the app is killed during a payment flow in an external browser, the return-to-app button in Pay Station silently fails by default. Enable this setting so the redirect brings the user back to the app instead. Requires **`WebViewType.External`**. 

```csharp
var advanced = new XsollaClientSettings.AdvancedSettingsAndroid()
    .SetRedirectAppRelaunchEnabled(true);

var settings = XsollaStoreClientSettings.Builder.Create()
    .SetWebViewType(XsollaStoreClientSettings.WebViewType.External)
    .SetAdvancedSettingsAndroid(advanced)
    .Build();
```
 

### WebView splash screen
 

Set a splash screen image displayed in the Android WebView while Pay Station loads: 

```csharp
var settings = XsollaStoreClientSettings.Builder.Create()
    //..
    .SetWebViewSplashScreenImage("path/to/splash.png")
    .SetWebViewSplashScreenDrawableIdAndroid(0x7f080001) // your Android drawable resource ID
    .Build();
```
 
 
- `SetWebViewSplashScreenImage`: File path to the splash screen image. 
- `SetWebViewSplashScreenDrawableIdAndroid`: Android drawable resource ID for the splash screen image.

Configuration - iOS

https://developers.xsolla.com/sdk/unity/sdk/configuration/ios

### Compliance in the U.S. regarding redirecting players to the web
 

Use `WebViewType.External`: 

```csharp
settings = XsollaClientSettings.Builder.Update(settings)
            .SetWebViewType(XsollaClientSettings.WebViewType.External)
            .Build();
```
 

### In-app payment compliance in Europe under the DMA
 

Use `WebViewType.Auto`: 

```csharp
settings = XsollaClientSettings.Builder.Update(settings)
            .SetWebViewType(XsollaClientSettings.WebViewType.Auto)
            .Build();
```
 

For more advanced compliance scenarios, you can programmatically detect the user's region and distribution channel to automatically configure the appropriate settings. Learn how to [**detect iOS storefront**](https://developers.xsolla.com/sdk/unity/sdk/advanced/ios-storefront) for implementing U.S.-specific regulations, or [**detect alternative app distribution status**](https://developers.xsolla.com/sdk/unity/sdk/advanced/detectstore) for enabling EU-specific compliance flows based on the app's installation source.

Initialization

https://developers.xsolla.com/sdk/unity/sdk/initialization

After [configuring](https://developers.xsolla.com/sdk/unity/sdk/configuration), the **Xsolla SDK for Unity** needs to be initialized and connected to **Xsolla** services. PREREQUISITE

Make sure Unity **In-App Purchasing `4.15+`** is installed.

See the official [Unity IAP setup guide ↗](https://docs.unity3d.com/Packages/com.unity.purchasing@4.15/manual/GettingStarted.html) ([https://docs.unity3d.com/Packages/com.unity.purchasing@4.15/manual/GettingStarted.html](https://docs.unity3d.com/Packages/com.unity.purchasing@4.15/manual/GettingStarted.html)).

⚠️ Unity **In-App Purchasing `5.x`** is **NOT** supported at this moment.

```cs
public class App : MonoBehaviour, IStoreListener
{
    private IStoreController _storeController;

    void Start()
    {
        // See the configuration step for more details on how to prepare a config.
        var configuration = ...

        var module = XsollaPurchasingModule.Builder.Create()
         .SetConfiguration(configuration)
         .Build();

        var configurationBuilder = ConfigurationBuilder
         .Instance(module)
         .AddProduct("product1", ProductType.Consumable)
         .AddProduct("product2", ProductType.Consumable)
         .AddProduct("product3", ProductType.Consumable)
         .AddProduct("product4", ProductType.Consumable);
         
        UnityPurchasing.Initialize(this, configurationBuilder);
    }

    public void OnInitialized(IStoreController controller, IExtensionProvider extensions)
    {
        _storeController = controller;

        // handle initialized
    }

    public void OnInitializeFailed(InitializationFailureReason error)
    {
        // handle initialization failed
    }

    public void OnInitializeFailed(InitializationFailureReason error, string message)
    {
        // handle initialization failed
    }

    public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs purchaseEvent)
    {
        // implement purchase flow

        return PurchaseProcessingResult.Complete;
    }

    public void OnPurchaseFailed(Product product, PurchaseFailureReason failureReason)
    {
        // handle purchase failed
    }

    public void OnPurchaseFailed(Product product, PurchaseFailureDescription failureDescription)
    {
        // handle purchase failed
    }
}
```

```csharp
var storeClient = XsollaStoreClient.Builder.Create()
    .SetConfiguration(configuration)
    .SetOnRestore((item, error) => {
        if (error != null)
        {
            // handle error
            return;
        }
          
        // handle delayed purchased item
    })
    .AddProducts(productIds)
    .AddProduct("product1")
    .AddProduct("product2")
    .AddProduct("product3")
    .AddProduct("product4")
    .Build();

storeClient.Initialize((products, error) =>
{
    if (error != null)
    {
        // handle error
        return;
    }

    // products contains all requested products
});
```
 

The SDK loads only Virtual Items from your Xsolla Catalog — Virtual Currency, Bundles, and other catalog types are not returned.

Installation

https://developers.xsolla.com/sdk/unity/sdk/installation

There are multiple ways to install the **Xsolla SDK for Unity** plugin in **Unity**. All of them use the *Unity's Package Manager*. 

### Option 1: From a Git URL
 
 
- In Unity, go to **Window** > **Package Manager**. 
- Click ➕ > **Add package from git URL...**. 
- Enter:  ```text https://github.com/xsolla/xsolla-sdk-unity.git ``` 
- Click **Add**. 
 note

For more information on installing plugins from a Git URL, visit the official Unity documentation [here ↗](https://docs.unity3d.com/Manual/upm-ui-giturl.html) ([https://docs.unity3d.com/Manual/upm-ui-giturl.html](https://docs.unity3d.com/Manual/upm-ui-giturl.html)). 

### Option 2: From a Unity package (.unitypackage)
 
 
- Choose **Assets** > **Import Package** > **Custom Package**. A file browser appears, prompting you to locate the *.unitypackage* file. 
 

![install 1] note

For more information on installing plugins from Unity packages, visit the official Unity documentation [here ↗](https://docs.unity3d.com/Manual/AssetPackagesImport.html) ([https://docs.unity3d.com/Manual/AssetPackagesImport.html](https://docs.unity3d.com/Manual/AssetPackagesImport.html)). 

### Option 3: From a tarball
 
 
- Open your Unity project or create a new one. 
- Go to **Window** > **Package Manager**. 
- Add a package as a dependence:    - Click the ➕ icon and select **Add package from tarball**.  - Select the downloaded SDK package.  - Click **Open** and wait for the import to finish. 
- If you don't have **EDM4U** (Google External Dependency Manager) installed, get the latest version (external-dependency-manager-latest.unitypackage) into the project from [GitHub ↗](https://github.com/googlesamples/unity-jar-resolver) ([https://github.com/googlesamples/unity-jar-resolver](https://github.com/googlesamples/unity-jar-resolver)). 
 note

For more information on installing plugins from tarballs, visit the official Unity documentation [here ↗](https://docs.unity3d.com/Manual/upm-ui-tarball.html) ([https://docs.unity3d.com/Manual/upm-ui-tarball.html](https://docs.unity3d.com/Manual/upm-ui-tarball.html)).

Resolving Android dependencies

https://developers.xsolla.com/sdk/unity/sdk/installation/resolving-android-dependencies

To resolve the Android dependencies, follow these steps: 
 
- From the main menu of **Unity editor**, go to **Assets** > **External Dependency Manager** > **Android Resolver** and click on **Resolve**.  ![external dependency1] 
- Wait for the dependency manager to finish. 
- Once the process is done, a popup notifying of completion will appear on the screen.  ![external dependency2] 
- All dependencies are now resolved and the popup can be safely closed. 
 warning

All external dependencies are resolved through Google's **External Dependency Manager** (EDM4U), which comes prepackaged with the SDK plugin. This helps to alleviate the need to specify any of the native Android library dependencies by editing project's Gradle template scripts directly. Preferably, EDM4U should be run every time the SDK plugin is either installed anew or updated to a newer release.

Xsolla SDK for Unity - Migrating from 2.x to 3.x

https://developers.xsolla.com/sdk/unity/sdk/migration2xto3x

info

**Xsolla SDK for Unity** is an evolution of the enterprise-level Xsolla SDK solution, designed for integration into large-scale projects.

A step-by-step guide for migrating your Unity project from **Xsolla SDK 2.x** to **Xsolla SDK 3.x**, suitable for developers at any experience level. 

## Prerequisites
 
 
- Unity 2019.4 or later. 
- Backup your project (e.g., via Git). 
 

## 1. Remove the Old SDK (2.x)
 

### 1.1 via Unity Package Manager
 
 
- Open **Packages/manifest.json**. 
- Remove the line:  ```json "com.xsolla.mobilesdk": "<version>" ```  VERSION `<version>` could look something like **2.0.22**. 
- Save the file. 
 

### 1.2 Manual install (UnityPackage)
 
 
- In the **Project** window, delete:  ```plaintext Assets/Xsolla.MobileSDK ``` 
- If present, also remove:  ```plaintext Packages/com.xsolla.mobilesdk ``` 
 

## 2. Install the New SDK (3.x)
 

Xsolla SDK 3.x is distributed as a `.unitypackage` or `.tar`. 

Follow the [**installation guide**](https://developers.xsolla.com/sdk/unity/sdk/installation/) to install the new SDK version (3.x). 

## 3. Resolve Android Dependencies (Optional)
 

After installing the 3.x release, you may want to resolve Android dependencies if you're explicitly targeting Android in your project. 

Follow the [**Android dependency resolving guide**](https://developers.xsolla.com/sdk/unity/sdk/installation/resolving-android-dependencies) to resolve Android dependencies. info

Xsolla SDK relies on Android AARs; the resolver automatically sets up all required libraries. 

## 4. Update Namespaces and Assembly Definitions
 

All namespaces have changed from `Xsolla.MobileSDK.*` to `Xsolla.SDK.*`. To update: 
 
- In your code editor, open **Find and Replace**. 
- Set the search scope to your *project folder*. 
- Replace all occurrences, for example:  ```diff - using Xsolla.MobileSDK.Common; + using Xsolla.SDK.Common; ``` 
- Repeat for sub-namespaces: Store, Login, UnityPurchasing, etc. 
 

**Assembly Definition files (.asmdef)** 

If you explicitly referenced **Xsolla.MobileSDK** in one of your assembly definition files (.asemdef), you will need to update those definitions with **Xsolla.SDK** now. 

## 5. Updated SDK Configuration
 

### 5.1 Code-based Configuration
 

If your project configures the SDK using `XsollaClientSettings`: 
 
- Ensure all `using` directives reference the 3.x namespace:  ```diff - using Xsolla.MobileSDK.Common; + using Xsolla.SDK.Common; ``` 
- Instantiate the settings object with the new type:  ```csharp var settings = new Xsolla.SDK.Common.XsollaClientSettings(projectId, storeId, accessToken); ``` 
 

### 5.2 Asset-based Configuration
 

If you're using [**asset-based settings**](https://developers.xsolla.com/sdk/unity/sdk/configuration/) (under the *Asset* tab) there's no need to take any extra steps as migration of the settings will be automatic. tip

It's highly recommended to double-check the parameters after migration to assure their correctness. 

## 6. Test & Validate
 
 
- Restart Unity to refresh all assets. 
- Open **Window ▶ Package Manager** and confirm **Xsolla SDK 3.x** is installed. 
- Build your project with the Xsolla SDK 3.x and in case of errors:    - Make sure you located and updated all of the namespaces.  - Check whether assembly definitions point at 3.x now and no 2.x entries remain.  - *(Optional)* Open the Xsolla SDK settings from the menu and double-check the parameters.  - *(Optional)* Verify Android dependencies resolved correctly. 
 

## 7. Additional Resources
 
 
- **SDK Documentation:** [https://developers.xsolla.com/sdk/mobilesdk/](https://developers.xsolla.com/sdk/mobilesdk/) 
- **Unity Manual (Importing Packages):** [https://docs.unity3d.com/Manual/CustomPackages.html](https://docs.unity3d.com/Manual/CustomPackages.html) 
 

For further assistance, contact **[integration@xsolla.com](mailto:integration@xsolla.com)**.

Purchase flow

https://developers.xsolla.com/sdk/unity/sdk/purchase

Purchasing flow is a multistage process, which involves [payment collection](https://developers.xsolla.com/sdk/unity/quick-start/#purchase-product), purchase [validation](https://developers.xsolla.com/sdk/unity/sdk/purchase#validating-purchases-recommended) and [consumption](https://developers.xsolla.com/sdk/unity/sdk/purchase#consuming-purchased-goods). 

### Starting purchasing flow
 

Use one of the snippets below to launch a purchasing flow depending on whether you use *Unity InApp Purchasing* or not: 

```cs
_storeController.InitiatePurchase(product);

// ...

public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs purchaseEvent)
{
    // handle the purchased item here

    return PurchaseProcessingResult.Complete;
}
```

```cs
storeClient.PurchaseProduct(productId, (product, error) =>
{
    if (error != null)
    {
        // handle error
        return;
    }

    var receipt = product.ToReceipt();

    // handle the purchased product here (e.g. validate or consume)..
});
```
**Advanced Settings**

Use a more complete `XsollaStoreClient.PurchaseProduct()` variant for extended functionality:

```cs
XsollaStoreClient.PurchaseProduct(
    string productId,
    XsollaStoreClientPurchaseArgs args,
    Action<XsollaStoreClientPurchasedProduct, XsollaStoreClientError> completionHandler
)
```

### Developer Payload

Use the example below to set your custom developer payload for a purchasing flow:

```cs
var args = new XsollaStoreClientPurchaseArgs() {
    developerPayload = "<your-custom-developer-payload>"
};

XsollaStoreClient.PurchaseProduct(
    productId, args, completionHandler: (product, err) =>
    {
        // Handle the result.
    }
);
```

### External ID

Allows providing your own custom unique ID for a transaction.BEWARE

You need to enable external ID support in your *Publisher Account* beforehand or purchasing will fail.

Use the example below to set the external ID for a purchasing flow:

```cs
var args = new XsollaStoreClientPurchaseArgs() {
    externalId = "<your-custom-unique-external-id"
};

XsollaStoreClient.PurchaseProduct(
    productId, args, completionHandler: (product, err) =>
    {
        // Handle the result.
    }
);
```

### Forcing a Payment Method

Allows selecting a specific payment method for a purchasing flow.tip

Payment method IDs can be found in your *Publisher Account*.

```cs
var args = new XsollaStoreClientPurchaseArgs() {
    paymentMethodId = <payment-id>
};

XsollaStoreClient.PurchaseProduct(
    productId, args, completionHandler: (product, err) =>
    {
        // Handle the result.
    }
);        
```
 

### Handling user cancellation
 

When a player cancels a purchase, handle it separately from actual errors. 

```cs
public void OnPurchaseFailed(Product product, PurchaseFailureReason failureReason)
{
    if (failureReason == PurchaseFailureReason.UserCancelled)
    {
        // User cancelled the purchase — don't show an error
        return;
    }

    // Handle other failure reasons
}

// Or (newer API)
public void OnPurchaseFailed(Product product, PurchaseFailureDescription failureDescription)
{
    if (failureDescription.reason == PurchaseFailureReason.UserCancelled)
    {
        // User cancelled the purchase — don't show an error
        return;
    }

    // Handle other failure reasons
}
```

```cs
void OnPurchase(XsollaStoreClientPurchasedProduct product, XsollaStoreClientError error)
{
    if (error?.code == XsollaStoreClientPurchaseErrorCode.Cancelled)
    {
        // User cancelled the purchase — don't show an error
        return;
    }

    if (error != null)
    {
        // Handle other errors
        return;
    }

    // Handle successful purchase
}
```
 

### Validating purchases (recommended)
 warning

Although validation is **NOT** a mandatory step, yet it's highly recommended to minimize the chance of fraud and any other malevolent intents. 

For instructions on how to validate your purchases, see [**Purchase Validation**](https://developers.xsolla.com/sdk/unity/sdk/validation) guide. 

### Consuming purchased goods
 

The very final step in the chain of purchasing flow stages is to award the purchased goods to the user and mark these goods as "awarded". The process also known as **purchase consumption**. 

Use the code below to do just that: 

```cs
_storeController.InitiatePurchase(product);

// ....

public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs purchaseEvent)
{
    // handle purchased item

    return PurchaseProcessingResult.Pending;
}

// ....

_storeController.ConfirmPendingPurchase(product);
```

```cs
storeClient.ConsumeProduct(item.sku, item.quantity, item.transactionId, error => {
    if (error != null)
    {
        return;
    }

    // handle purchased item
});
```
 important

It's highly recommended to perform at least some form of [**validation**](https://developers.xsolla.com/sdk/unity/sdk/purchase#validating-purchases-recommended) before consuming the purchased goods.

Testing & Sandbox

https://developers.xsolla.com/sdk/unity/sdk/testing

This section provides various snippets and examples of how to configure the sandbox environment to be able to make test payments, make logging more verbose, and so on. 

### Enabling sandbox mode
 

```csharp
var configuration = XsollaStoreClientConfiguration.Builder.Create()
    // ...
    .SetSandbox(true)
    .Build();
```
 

#### Testing in Unity Editor
 

Testing in the Unity Editor uses the current platform's implementation (e.g., Windows or macOS). warning

Remember to turn the **sandbox** mode **OFF** before going **LIVE**. 

### Enabling additional logging
 

```csharp
var configuration = XsollaStoreClientConfiguration.Builder.Create()
    // ...
    .SetLogLevel(XsollaLogLevel.Debug)
    .Build();
```
 warning

Make sure the debug **logging** is **OFF** before going **LIVE**. 

### Test cards
 

For a list of cards to simulate payments in sandbox mode, see [here ↗](https://developers.xsolla.com/doc/pay-station/testing/test-cards) ([https://developers.xsolla.com/doc/pay-station/testing/test-cards](https://developers.xsolla.com/doc/pay-station/testing/test-cards)). important

The test cards won't work outside the **sandbox mode**.

Purchase Validation

https://developers.xsolla.com/sdk/unity/sdk/validation

Each purchase should be validated before awarding it to the end-user. This assists in preventing unauthorized purchases and ensures the integrity of your in-app economy. Validating purchases is a critical security measure to prevent fraud, protect your revenue, and ensure fair user experiences. 

Essentially, that are two distinct validation approaches readily available. 

### Server-side Validation
 

The most secure and reliable way to validate purchases is by using **server-to-server** (**S2S**) communication, with the client completely removed from the validation process. This minimizes security risks and ensures that entitlements are only granted based on verified data received directly from Xsolla. 

With Xsolla, server-side validation is implemented through a **webhook-based model**: 
 
- Once a user completes a purchase through your app, Xsolla services automatically trigger a [**webhook event**](https://developers.xsolla.com/sdk/publisher-account/webhooks#user-validation-user_validation) to your backend. 
- This webhook contains all the necessary purchase data, including the **Xsolla** `order ID`, enabling your server to validate and process the purchase asynchronously. 
- Your backend can then use this data to update the user’s entitlements, balance, inventory, etc., without requiring any additional input from the client. 
 

This flow ensures that the client has no role in deciding the outcome of a purchase — all validation logic lives on the server, using trusted data that originates from Xsolla itself. tip

For more information about how webhooks work, see [**here**](https://developers.xsolla.com/sdk/publisher-account/webhooks).  

### Client-side Validation
 

Provides means of validating a purchase on the user's device without a need for a backend (server). It's better than nothing, but has its own drawbacks, i.e. anything that runs on the client can potentially be exploited.  

```csharp
public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs args)
{
    var product = purchaseEvent.purchasedProduct;
    var validator = extensions.GetExtension<IXsollaPurchasingStoreExtension>().GetValidator();

    validator.ValidatePurchase(product.receipt, (success, error) =>
    {
        if (error == null && success)
        {
            // Purchase is Valid, handle the purchased item..
        }
        else
        {
            // Purchase is NOT valid or there was an error performing validation..
        }
    });
}
```

```csharp
void MyOnPurchaseCallback(XsollaStoreClientPurchasedProduct product, XsollaStoreClientError error)
{
    _storeClient.ValidatePurchase(product.ToReceipt(), (success, error) =>
    {
        if (error == null && success)
        {
            // Purchase is Valid, handle the purchased item..
        }
        else
        {
            // Purchase is NOT valid or there was an error performing validation..
        }
    });
}
```

Xsolla SDK for Unity - Web

https://developers.xsolla.com/sdk/unity/web

info

**Xsolla SDK for Unity** is an evolution of the enterprise-level Xsolla SDK solution, which was designed for integration into large-scale projects.

This guide helps you integrate the Xsolla SDK for Unity to start accepting payments in your Unity game on Web. By the end, you'll have a fully functioning payment system in your game and experience firsthand how Xsolla streamlines monetization.

Let's dive in and get your game ready to accept payments in just a few easy steps! 

## Quick start

This quick start guide makes it simple to integrate the *Xsolla SDK for Unity* on Web and start accepting payments in your *Unity* game. We'll use a pre-configured test project to cover all the essentials: setting up basic user authentication, importing your products (SKUs), and processing a test payment.

For detailed instructions, please refer to the [**Quick Start Guide**](https://developers.xsolla.com/sdk/unity/quick-start).

## Setting up a Publisher Account

Before you begin integrating Xsolla SDK into your game, you'll need to register a Publisher Account.

The Publisher Account is the primary tool for configuring Xsolla features, working with analytics and transactions similar to Google Play Console and App Store Connect for SKU management.important

You'll need to have an active Publisher Account to obtain a Project ID you'd use throughout the whole SDK integration process.

For detailed instructions, please refer to [**Setting up a Publisher Account**](https://developers.xsolla.com/sdk/publisher-account).

## Install the SDK

Now that you've set up your Publisher Account, the next step is to install the Xsolla SDK for Unity plugin in your Unity project.

For detailed installation instructions, see the [**Installation Guide**](https://developers.xsolla.com/sdk/unity/sdk/installation).

## Configure the SDK

Once you've installed the SDK, the next step is configuration.

For detailed instructions, please refer to the [**Configuration Guide**](https://developers.xsolla.com/sdk/unity/sdk/configuration).info

Using **a device ID** to log in is not supported on the Web platform. You must provide an access token instead. For more information, please refer to [**Authentication via custom ID**](https://developers.xsolla.com/sdk/publisher-account/login#login-on-game-server)

Alternatively, you can use the [**Delayed configuration**](https://developers.xsolla.com/sdk/unity/sdk/configuration/#assign-xsolla-access-token-to-the-configuration) feature to postpone SDK initialization until an access token is obtained.

## Initialize the SDK

With configuration complete, you're now ready to initialize the SDK in your Unity project.

For detailed steps and best practices, refer to the [**Initialization Guide**](https://developers.xsolla.com/sdk/unity/sdk/initialization).

## Make a Purchase

Now that the SDK is initialized, your app is ready to start processing payments.

For detailed instructions, please refer to [**Make Purchase**](https://developers.xsolla.com/sdk/unity/sdk/purchase).

## Collect Payment

The SDK leverages **Xsolla Pay Station** for *secure* payment collection.

These steps will guide you through a test payment collection flow:
 
- Choose the **Card** payment method:  ![quick start3] 
- Use one of the test cards listed [**here ↗**](https://developers.xsolla.com/doc/pay-station/testing/test-cards) ([https://developers.xsolla.com/doc/pay-station/testing/test-cards](https://developers.xsolla.com/doc/pay-station/testing/test-cards)) and click **Pay** to confirm the payment:  ![quick start3] 
- Once the payment goes through, you'll see a confirmation:  ![quick start3] 

## Purchase Validation

After setting up payments, you might want to validate your purchase to ensure transaction security and reliability.

For detailed instructions, please refer to [**Purchase Validation**](https://developers.xsolla.com/sdk/unity/sdk/validation).

## Testing & Sandbox

As a final step, use the sandbox environment to safely test your entire payment integration without real transactions.

For detailed instructions, please refer to the [**Testing & Sandbox**](https://developers.xsolla.com/sdk/unity/sdk/testing).

## What's next?

Congratulations! 🎉 You've successfully integrated **Xsolla SDK for Unity** on Web!

This achievement unlocks access to over 1000 global payment methods and sets the foundation for integrating more Xsolla solutions, including our powerful Web Shop.

Core Concepts (Android)

https://developers.xsolla.com/sdk/welcome/core-concepts-android

Going beyond the Google Play Billing Library, to **Xsolla SDK** involves a few essential steps to ensure seamless integration and functionality. Below is a structured breakdown of the **three core concepts** required for supporting expanded range of payments. 

## Authentication and Login
 
 
- [**Google Play Sign-In ↗**](https://support.google.com/googleplay/answer/3129346) ([https://support.google.com/googleplay/answer/3129346](https://support.google.com/googleplay/answer/3129346)): Users are automatically logged into the Google Play ecosystem via Google Play Services, which naturally happens in background, providing developers with an ID for billing (in a private but hidden way). 
- [**Xsolla Login**](https://developers.xsolla.com/sdk/mobilesdk/sdk/configuration/android#authentication): Xsolla provides developers a way to implement specific login authentication SDK APIs, integrating it with developer's server or relying on client-side authentication methods for simpler games, e.g., *hypercasual*. These APIs are crucial for passing the user's ID or device ID to Xsolla services, ensuring that users are properly recognized within the Xsolla products (e.g., for seamless recurring payments). Upon successful authentication, Xsolla provides a token that is essential for initiating purchasing flows. 
 

## SKU and In-App Purchases (IAP)
 Product Terminology

In this documentation:
 
- **IAP (In-App Purchase)** = Google Play Billing's purchase mechanism 
- **SKU (Stock Keeping Unit)** = Product identifier used in code 
- **Virtual Items** = Items in your Xsolla Catalog (IAPs) 

When migrating from Google Play to Xsolla, your IAPs become Virtual Items in the Xsolla Catalog, each with its own SKU for code references. 
 
- [**Google Play Billing ↗**](https://developer.android.com/google/play/billing/getting-ready#products) ([https://developer.android.com/google/play/billing/getting-ready#products](https://developer.android.com/google/play/billing/getting-ready#products)): In Google Play Billing, products (also known as SKUs or IAPs) are identified by a unique **Product ID** (e.g., '*com.company.gameapp.keys_1*') and a descriptive **Product Name** (e.g., '*5 Keys*'). For instance, '*5 Keys*' might represent a key pack priced at $1. 
- **Xsolla Catalog**: The Xsolla Catalog contains your digital goods as [**Virtual Items**](https://developers.xsolla.com/sdk/publisher-account/virtual-items) (IAPs). To transition from Google Play Billing, developers import their existing Product IDs from Google Play Console. Each product becomes a Virtual Item with its own SKU identifier for use in code. 
 

## Initializing the Pay Station
 
 
- **The Pay Station** serves as the gateway for your users' purchases. It requires both the access token and the specific SKU (e.g., '*keys_1*') identifying which Virtual Item (IAP) to purchase. Developers must ensure these elements are correctly configured, enabling users to buy items seamlessly, mirroring the purchase experience they would have via Google Play Billing. 
- With your [*Virtual Items*](https://developers.xsolla.com/sdk/publisher-account/virtual-items) (IAPs) set up and an [*access token*](https://developers.xsolla.com/sdk/mobilesdk/sdk/configuration/android#authentication) obtained (e.g., based on user/device ID or via your server), the next crucial step is to [*initialize the Pay Station*](https://developers.xsolla.com/sdk/mobilesdk/sdk/purchase/android). This is where the actual transaction process begins by forming an order. 
 

## Webhooks
 

[Webhooks](https://developers.xsolla.com/sdk/publisher-account/webhooks) are notifications about events occurring in the system. When a specific event occurs, Xsolla sends an HTTP request, transmitting event data to your game server (or client, in case of simplified client-only integration e.g. for *hypercasual games*). These webhooks are essential for the game client and/or server to receive notification on successful/unsuccessful payments and user authentication attempts. 

## Conclusion
 

By following these steps, developers can effectively map the Xsolla ecosystem to expand beyond Google Play Billing library, providing a comprehensive alternative that opens up a broader array of payment options globally. This expansion not only supports a more diversified monetization strategy but also enhances user engagement by leveraging Xsolla's extensive set of value-add solutions. 

For guidance on regulated Google Play billing programs, see the [Google Play User Choice Billing reference](https://developers.xsolla.com/sdk/mobilesdk/addendum/android/google-play-user-choice-billing).

Core Concepts (iOS)

https://developers.xsolla.com/sdk/welcome/core-concepts-ios

Expanding from Apple's StoreKit Framework to **Xsolla SDK** involves several key steps to ensure seamless integration and functionality. Here's a structured breakdown of **the core concepts** required for supporting an expanded range of payments on iOS: 

## Authentication and Login
 
 
- **Awareness of Apple ID / Account**: iOS users are typically signed in to their Apple ID on their devices, providing a seamless identity layer for transactions. 
- [**Xsolla Login**](https://developers.xsolla.com/sdk/mobilesdk/sdk/configuration/ios): Xsolla provides developers a way to implement specific login authentication SDK APIs, integrating it with developer's server or relying on client-side authentication methods for simpler games, e.g. hypercasual. These APIs are crucial for passing the user's ID or device ID to Xsolla services, ensuring that users are properly recognized within the Xsolla products (e.g. for seamless recurring payments). Upon successful authentication, Xsolla provides a token that is essential for initiating purchasing flows. 
 

## SKU and In-App Purchases (IAP)
 Product Terminology

In this documentation:
 
- **IAP (In-App Purchase)** = StoreKit's purchase mechanism 
- **SKU (Stock Keeping Unit)** = Product identifier used in code 
- **Virtual Items** = Items in your Xsolla Catalog (IAPs) 

When migrating from App Store to Xsolla, your IAPs become Virtual Items in the Xsolla Catalog, each with its own SKU for code references. 
 
- [**In-app Purchases sold via StoreKit Framework ↗**](https://developer.apple.com/help/app-store-connect/manage-in-app-purchases/create-consumable-or-non-consumable-in-app-purchases) ([https://developer.apple.com/help/app-store-connect/manage-in-app-purchases/create-consumable-or-non-consumable-in-app-purchases](https://developer.apple.com/help/app-store-connect/manage-in-app-purchases/create-consumable-or-non-consumable-in-app-purchases)): Products within an iOS app are identified via a "Product ID" (e.g., `com.game.key_1`) accompanied by a "Reference Name" (e.g., "Key Pack 1"). In this example, it refers to a key pack worth $1. 
- **Xsolla Catalog**: The Xsolla Catalog contains your digital goods as [**Virtual Items**](https://developers.xsolla.com/sdk/publisher-account/virtual-items) (IAPs). To transition these products to Xsolla, developers import their **App Store Connect Product IDs** from App Store Connect. Each product becomes a Virtual Item with its own SKU identifier for use in code, ensuring that products from the App Store ecosystem are accurately represented and synchronized. 
 

## Initializing the Pay Station
 
 
- After [setting up SKUs](https://developers.xsolla.com/sdk/publisher-account/virtual-items) and [obtaining an access token](https://developers.xsolla.com/sdk/mobilesdk/sdk/configuration/ios) (e.g. based on user/device ID, or via server), the next step is to [initialize the Pay Station](https://developers.xsolla.com/sdk/mobilesdk/sdk/purchase/ios). This is where the actual transaction process begins by forming an order. 
- The Pay Station requires both the access token and the specific SKU (e.g., "key_1") identifying which Virtual Item (IAP) to purchase. Developers need to ensure that both elements are correctly configured to allow users to buy items seamlessly, mirroring the purchase experience they would have via StoreKit Framework. 
 

## Webhooks
 

[Webhooks](https://developers.xsolla.com/sdk/publisher-account/webhooks) are notifications about events occurring in the system. When a specific event occurs, Xsolla sends an HTTP request, transmitting event data to your game server (or client, in case of simplified client-only integration e.g. for *hypercasual games*). These webhooks are essential for the game client and/or server to receive notification on successful/unsuccessful payments and user authentication attempts. 

## Alternative Payments and App Store Policies
 

With recent changes in App Store policies, developers now have more options for implementing alternative payment methods, especially in certain regions. *Xsolla SDK* is designed to help navigate these new possibilities while ensuring compliance with Apple's guidelines. note

To implement region-specific payment methods, you can use the [iOS Storefront detection](https://developers.xsolla.com/sdk/mobilesdk/sdk/advanced/ios-storefront) feature. This allows you to enable or disable payment methods based on the user's current App Store region, ensuring compliance with regional policies and requirements. 

## Conclusion
 

By following these steps, developers can effectively map the Xsolla ecosystem to expand beyond the StoreKit, providing a comprehensive alternative that opens up a broader array of payment options globally. This expansion not only supports a more diversified monetization strategy but also enhances user engagement by leveraging Xsolla's extensive set of value-add solutions, all while maintaining compliance with iOS platform requirements.

Mobile Game Distribution Glossary: The DMA Era and U.S. Court Rulings

https://developers.xsolla.com/sdk/welcome/glossary

## Introduction

The Digital Markets Act (DMA) and U.S. court rulings have introduced new concepts in mobile game distribution and monetization. This glossary explains these terms and how **Xsolla SDK** supports developers in this evolving landscape.

As the industry adapts to regulatory changes, understanding new distribution methods and payment options is crucial. **Xsolla SDK** empowers developers to navigate this new ecosystem, providing compliant solutions that open up new monetization avenues.

**Xsolla SDK** supports the following key concepts: 

## iOS / Apple App Store:
 
 
- **External Payment Links (United States)** — Effective April 30, 2025:    - Following the U.S. court decision dated April 30, 2025, developers are now allowed to connect external payment links in the United States  - Implement link-outs to Xsolla Pay Station or Web Shop for U.S. users  - Use iOS storefront detection to identify U.S. users and enable region-specific payment flows  - **Xsolla SDK** aligns with Apple's updated guidelines for U.S. compliance  - Configure `openExternalBrowser = true` for U.S. compliance when redirecting players to web payment pages 
- [**Alternative App Marketplaces ↗**](https://developer.apple.com/support/alternative-app-marketplace-in-the-eu) ([https://developer.apple.com/support/alternative-app-marketplace-in-the-eu](https://developer.apple.com/support/alternative-app-marketplace-in-the-eu)) on iOS:    - Apple's term for alternative App Stores, available in EU markets  - Implement Xsolla Pay Station for in-app payments outside App Store (from 0% store fees for < 1M installs to limited fees by Apple)  - Power your Marketplace wallet with top-up solution via Xsolla Pay Station 
- [**Web Distribution ↗**](https://developer.apple.com/support/web-distribution-eu) ([https://developer.apple.com/support/web-distribution-eu](https://developer.apple.com/support/web-distribution-eu)) on iOS:    - Apple's term for side-loading on iOS via official publisher websites  - Distribute your app directly to users from your website  - Implement direct in-app payments via Xsolla Pay Station without App Store fees (up to 1M installs per year, further fees apply from Apple's side) 
- [**Notarization ↗**](https://developer.apple.com/help/app-store-connect/distributing-apps-in-the-european-union/submit-for-notarization) ([https://developer.apple.com/help/app-store-connect/distributing-apps-in-the-european-union/submit-for-notarization](https://developer.apple.com/help/app-store-connect/distributing-apps-in-the-european-union/submit-for-notarization)) for iOS:    - Light approval process by Apple for distribution outside of App Store  - Ensures basic safety and integrity checks for your app  - **Xsolla SDK** is compatible with notarized apps, maintaining security standards 
- [**Alternative Payment Options ↗**](https://developer.apple.com/support/apps-using-alternative-payment-providers-in-the-eu) ([https://developer.apple.com/support/apps-using-alternative-payment-providers-in-the-eu](https://developer.apple.com/support/apps-using-alternative-payment-providers-in-the-eu)) on the App Store:    - Integrate Xsolla Pay Station within App Store apps (with Apple's fees, limited regions) via **Xsolla SDK**  - Offer users 1,000+ payment methods across 200+ countries 
 

## Android / Google Play:
 
 
- **External Payment Methods via Link-outs and Alternative Billing Solutions (United States)** — Effective October 29, 2025 (through November 1, 2027):    - Following the **Epic v. Google** injunction — affirmed by the Ninth Circuit on July 31, 2025 (rehearing denied September 12, 2025), and denied stay by the U.S. Supreme Court on October 6, 2025 — U.S. **Google Play** apps may guide users to **external payment methods via link-outs** and use **alternative billing solutions**  - Developers are no longer subject to **anti-steering restrictions** in the United States  - Developers must enroll in the applicable Google program and accept updated terms of service: [**External Content Links Program ↗**](https://support.google.com/googleplay/android-developer/answer/16470497) ([https://support.google.com/googleplay/android-developer/answer/16470497](https://support.google.com/googleplay/android-developer/answer/16470497)) for link-outs to external purchase pages, and/or [**Alternative Billing Program ↗**](https://support.google.com/googleplay/android-developer/answer/16497028) ([https://support.google.com/googleplay/android-developer/answer/16497028](https://support.google.com/googleplay/android-developer/answer/16497028)) for using non-Google billing within the app. Developers can participate in both programs simultaneously  - Under the current injunction, developers use these programs with **no fees to Google** through November 1, 2027. Google has published a fee schedule in its program documentation but is **not collecting any fees** while the injunction is in effect: for the **External Content Links Program** — 10% for subscriptions / 20% for other in-app items, plus **$2.85 per app install** or **$3.65 per game install** (within 24 hours of a link click); for the **Alternative Billing Program** — 10% for subscriptions / 25% for other in-app items. A proposed Epic-Google settlement (no final hearing date confirmed; parties ordered to submit briefs in February 2026; FTC has filed serious objections; Judge Donato publicly skeptical after a secret $800M side-deal between Epic and Google was revealed in court on January 22, 2026) would, if approved, supersede both the injunction and Google's published rates with 9% for non-gameplay items / 20% for gameplay-affecting items + a separate 5% Google Play Billing fee, effective through June 2032; if rejected, the no-fee regime under the injunction continues through November 1, 2027  - Implement link-outs to Xsolla Pay Station or Web Shop for U.S. users  - Use Google Play storefront detection to identify U.S. users (country code "US") and enable region-specific payment flows  - **Xsolla SDK** ensures full compliance with Google's revised requirements for U.S. apps 
- [**Side-loading on Android ↗**](https://developer.android.com/distribute/marketing-tools/alternative-distribution) ([https://developer.android.com/distribute/marketing-tools/alternative-distribution](https://developer.android.com/distribute/marketing-tools/alternative-distribution)):    - Install apps (APKs) directly from websites or third-party sources  - Implement payments with 0% fees to Google  - Retain full control over your app distribution and monetization  - **Xsolla SDK** seamlessly integrates with side-loaded apps  - **Developer verification requirement (upcoming):** Starting March 2026, Google requires all developers to [**register and verify their identity ↗**](https://developer.android.com/developer-verification) ([https://developer.android.com/developer-verification](https://developer.android.com/developer-verification)), including for sideloaded apps. Enforcement begins September 2026 in select regions (Brazil, Indonesia, Singapore, Thailand), with global rollout planned for 2027 and beyond. Apps from unverified developers will be blocked on certified Android devices. See [**Google's verification guide ↗**](https://support.google.com/android-developer-console/answer/16561738) ([https://support.google.com/android-developer-console/answer/16561738](https://support.google.com/android-developer-console/answer/16561738)) for details 
- APK Marketing & Distribution:    - Leverage partnerships with channels like *Digital Turbine* for wider reach and enable instant APK installation on Android devices  - Tap into new user acquisition channels beyond Google Play and pave your own way  - Xsolla is compatible with attribution tools for your APK distribution, ensuring you can continue optimizing your performance 
 

## Conclusion
 

The mobile gaming landscape is rapidly changing due to regulations like the DMA, U.S. court rulings (including the April 30, 2025 iOS decision and the Epic v. Google injunction effective October 29, 2025 through November 1, 2027), and evolving platform policies. **Xsolla SDK** offers a comprehensive toolkit to navigate these changes, helping you maximize revenue and reach across all available channels. 

We update this information regularly. For the most current details, see the [News](https://developers.xsolla.com/sdk/documentation-changelog). For tailored guidance, please consult your Business Development representative or Customer Success Manager. 

By using **Xsolla SDK**, you're positioning your game to thrive in this dynamic environment, unlocking new revenue streams and innovative distribution channels.

Xsolla SDK for Windows

https://developers.xsolla.com/sdk/windows

AvailableXsolla SDK for Windows[SDK Explorer →](https://developers.xsolla.com/sdk/demo/)

The **Xsolla SDK for Windows** brings out-of-store commerce, in-app purchases, and global payments to games on the Microsoft platform. With a **single integration**, the SDK empowers developers to connect directly with players, maintain control over the checkout experience, and keep up to 95% of their revenue. It supports side-by-side in-store checkout where allowed, enabling flexible billing strategies and loyalty incentives. Games automatically benefit from updates for new monetization tools, regulatory compliance, and global policy changes.

With built-in **IAP catalog sync**, streamlined tax onboarding for **flexible payouts**, and support for **more payment methods and local currencies**, the Xsolla SDK makes scaling Windows games across stores and regions simple. Developers can focus on building their game, while Xsolla handles the complexity of payments, payouts, and compliance.  

## Platform Support
 

### Epic Games Store & 3rd-Party Commerce
 

Epic Game Store policy allows developers to use alternative payment methods, including browser-based flows, provided they handle processing and fulfillment. See Epic's official documentation for details. 

That means developers can run their own payment system alongside—or instead of—the Store's commerce, giving full control over offers to players via features like dual billing, Web Shop purchases, and player incentives. Explore **[this article](https://developers.xsolla.com/sdk/windowstores/epic/)** for more details. 

### Game Engine Coverage
 info

If you are using *Unity* for your project, we recommend integrating the **Xsolla SDK for Unity**. For more details, please visit [**this page**](https://developers.xsolla.com/sdk/unity). 
 
- **Unity**: Use the [**Xsolla SDK for Unity**](https://developers.xsolla.com/sdk/unity) for Windows games built with *Unity*. 
- **Custom Engines**: [**Native SDKs**](https://developers.xsolla.com/sdk/windowstores/sdk/installation/) available in C++ and C# for full flexibility. 
- **Defold**: Integrate payments quickly with the **Xsolla SDK for Defold**, available on *Defold Asset Portal*. 
- **More Coming Soon**: Support for additional engines is in progress — stay tuned! 
  

## Key Benefits for Windows Developers
 **One Integration, Many Stores**

Get up and running on Epic Games Store and other Windows stores with a single SDK. Wherever your game lives, your monetization strategy stays consistent — and in your control.**Dual Billing Support**

Offer players side-by-side checkout options to let them choose how they pay. Reward loyalty, encourage competition, let commerce become a feature. See [best practices](https://developers.xsolla.com/sdk/windowstores/epic/dual-billing) for more.**Catalog Sync Across Platforms**

Sync your IAPs to your Xsolla Catalog (as Virtual Items with SKUs) effortlessly. Avoid juggling dev portals and managing redundant item definitions across multiple stores.**Flexible Payouts Without the Paperwork**

Onboarding and tax compliance can be a time sink — Xsolla offers a smooth, global tax interview process and payout flexibility to get you monetizing faster.**More Payment Options, Global Reach**

Xsolla supports more global payment methods, more local currencies, localized checkout flows, advanced anti-fraud, and compliant payment processing out-of-the-box.  

## Getting Started
 **Xsolla Buy Button**Fast Path

One-tap Web Shop purchase flows with 1,000+ payment methods — no SDK installation required. Launch browser-based checkout directly from within your Windows game.[Learn more →](https://xsolla.com/mobile-buy-button)  [EPIC GAMES**Epic Games Store**Enable commerce on Epic with dual billing and Web Shop→](https://developers.xsolla.com/sdk/windowstores/epic/)[SDK DOCS**Installation Guide**Step-by-step SDK integration for C++ and C# engines→](https://developers.xsolla.com/sdk/windowstores/sdk/installation/)

Xsolla SDK: Powering Commerce for Games on Epic Games Store

https://developers.xsolla.com/sdk/windowstores/epic

Epic Games Store policy allows developers to use alternative payment options, giving studios the freedom to choose the tools that best fit their business. Xsolla’s SDKs and services are designed to work alongside or in place of Epic’s native commerce system—empowering you to optimize checkout, streamline operations, and build stronger relationships with your players. 

## Player-Centric Opportunities
 

**Dual Billing with Player-Centric Benefits**: Xsolla SDK supports **dual billing**, allowing you to offer your own Xsolla-powered checkout side by side with the Store's commerce. Incentivize adoption with bonuses, rewards, or extra in-game currency—while keeping the experience compliant and transparent. For more information see **[best practices for dual billing](https://developers.xsolla.com/sdk/windowstores/epic/dual-billing)**. Example messaging

"Pay with Xsolla – Get 10% more!" 

You can also choose to replace platform commerce with Xsolla’s SDK for greater control over your monetization, pricing, catalog, and player data. 

**Enable Web-Based Purchases via Buy Button**: Xsolla **[Buy Button](https://developers.xsolla.com/sdk/mobilesdk/quick-start/webshop#buy-button-seamless-web-shop-integration)** lets you launch secure, custom payment flows from within your game. This redirects the player to a web checkout experience that runs in the browser, taking advantage of frictionless checkout and saved payment methods. 

Buy Button is simple to implement and integrates seamlessly with your in-game logic and backend systems. 

**Add a Web Shop with Epic Login Support**: You can also offer direct purchases through a **[Web Shop](https://developers.xsolla.com/sdk/mobilesdk/quick-start/webshop)** powered by Xsolla Site Builder and Pay Station. Players can now log in with their *Epic Account*, or enable **[authentication with device ID](https://developers.xsolla.com/sdk/publisher-account/login)**, making the experience seamless and account-linked. 

## Integration Options for Epic Games Store
 

Xsolla provides a growing number of SDKs to support games distributed via the Epic Games Store: 
 
- **Unity SDK**: The **[Xsolla SDK for Unity](https://developers.xsolla.com/sdk/unity)** is the ideal drop-in option for *Unity*-based games. 
- **Windows SDK**: Native **[Windows SDK](https://developers.xsolla.com/sdk/windowstores/sdk/installation/)** (C++ and C#) for custom tech stacks. 
- **Defold SDK**: Lightweight integration for games using the Defold engine, available in the *Defold Asset Portal*. 
- **Mobile SDK**: Native **[iOS and Android SDKs](https://developers.xsolla.com/sdk/mobilesdk/quick-start/)** designed for use with the **Epic Games Mobile Store**, supporting one-tap purchases outside traditional app store billing. 
- **Web Shop + *Sign in with Epic***: **[Web Shop](https://developers.xsolla.com/sdk/mobilesdk/quick-start/webshop)** complements Xsolla SDK and enables direct-to-player commerce. 
 

## Your Game, Your Players
 

Xsolla’s tools are built to reduce overhead—not add it. With centralized SKU management, global payments, and player-friendly checkout flows, you can focus on your game and offer your players choices while Xsolla tackles the complexities in the new era of game commerce. 

## What's Next?
 
 
- **[SDK Documentation](https://developers.xsolla.com/sdk/windowstores/sdk/installation/)** - Follow a step-by-step guide to integrate the SDK into your Windows game 
- **[Contact Support ↗](https://xsolla.com/epic-games-store?utm_medium=marketing&utm_content=windows-sdk)** ([https://xsolla.com/epic-games-store?utm_medium=marketing&utm_content=windows-sdk](https://xsolla.com/epic-games-store?utm_medium=marketing&utm_content=windows-sdk)) - Reach out to your Account Manager for personalized implementation guidance

Simplifying SKU & IAP Catalog Management with Xsolla

https://developers.xsolla.com/sdk/windowstores/epic/catalog-sync

Managing in-game product catalogs across multiple systems is one of the more tedious—but critical—tasks in game commerce. Every platform has its own requirements for defining virtual items, managing SKUs, and keeping catalogs up to date. This process often involves repeated manual work across dev portals, backend services, and in-game logic.

With Xsolla's catalog sync, developers can centralize their IAP definitions in a single source of truth—**Publisher Account**—and automatically keep their game's product catalog consistent across systems. See how in **[this article](https://developers.xsolla.com/sdk/publisher-account/virtual-items)**. 

## How It Works
 

When you connect your game to Xsolla, you define your Xsolla Catalog (Virtual Items/IAPs with SKUs, pricing, item types, bundles, etc.) once in the Publisher Account. These definitions can then be pulled directly into: 
 
- Your game client (via SDK or API) 
- Your backend services 
- Your Web Shop or Buy Button setup 
 

The result is that any updates made to item names, prices, availability, or configuration only need to be made in one place—Publisher Account—and those changes propagate automatically. 

## Benefits
 
 
- **Reduces Human Error**: Manual catalog entry across multiple systems is prone to typos, misconfigured prices, or mismatched SKUs. Syncing eliminates that risk. 
- **Saves Time**: Avoid duplicating work across every platform or dev environment. One update syncs everywhere. 
- **Improves Consistency Across Channels**: Whether a player is buying an item in one of your game versions or through your web store, they see the same items at the same prices. 
- **Supports A/B Testing and Live Ops**: Since catalog changes can be deployed centrally and in real-time, it's easier to test offers, update pricing for events, or roll out new bundles quickly. 
- **Easier Maintenance**: Teams can hand off catalog responsibilities to Live Ops, Product Management, or Monetization roles without requiring engineering work or access to platform portals. 
 

## When to Use It
 

If you're running your game on more than one store, offering web-based purchases, or frequently updating your IAPs, syncing through Publisher Account helps reduce overhead and improve the quality of your monetization setup. Even for games on a single storefront, it's a useful tool to simplify your operations and future-proof your backend.

Best Practices for Dual Billing

https://developers.xsolla.com/sdk/windowstores/epic/dual-billing

Implementing a dual billing experience in your Windows game gives players more choice at checkout—and that choice should feel meaningful. As Epic Games has outlined in their own guidance, developers are encouraged to offer players multiple payment options and to allow those options to compete on value.

If you're presenting players with both Epic's built-in payment system and an external option like Xsolla, it's important to ensure that both feel fair and rewarding. One best practice is to match or exceed any benefits the platform provides.

It's recommended to offer at least the same effective bonus—either as additional in-game currency, extra content, or a clear incentive like: "*Pay with Xsolla – Get 5% more!*"

This approach gives players more value for their money and positions your game as one that respects player choice, while also creating a reason to explore the alternative checkout option. 

When implementing dual billing, keep the following in mind: 
 
- **Be transparent**: Make the bonus clear at the point of checkout. A well-placed message or icon can guide player behavior without being disruptive. 
- **Keep rewards in balance**: Don't over-optimize toward one payment flow. The goal is to offer a choice, not a hard sell. 
- **Test what resonates**: A small bonus (even 5-10%) can shift player behavior significantly. Try A/B testing different offers and phrasing to find what works best for your audience. 
 

Above all, your billing experience should reflect a relationship toward the player of choice, fairness, and trust. Dual billing is not about steering players away from one provider—it's about giving them the power to decide which option delivers more value.

Configuration

https://developers.xsolla.com/sdk/windowstores/sdk/configuration

To Configure SDk please use following code: 

```csharp
var settings = XsollaStoreClientSettings.Builder.Create()
    .SetProjectId("< your_project_id >")
    .SetLoginId("< your_login_id >")
    .SetRestorePurchasesOnInit(true)
    .SetWebViewType(XsollaStoreClientSettings.WebViewType.Auto)
    .SetWebViewOrientationLock(XsollaStoreClientSettings.OrientationLock.Auto)
    .SetUIThemeSize(XsollaStoreClientSettings.ThemeSize.Auto)
    .SetUIThemeStyle(XsollaStoreClientSettings.ThemeStyle.Auto)
    .SetUICustomTheme("")
    .SetUseCloseButton(XsollaStoreClientSettings.CloseButton.Auto)
    .SetUIControlTintColor(Color.clear)
    .SetUIBarTintColor(Color.clear)
    .SetRedirectUrl("")
    .SetRedirectButtonText("")
    .SetRedirectDelay(6)
    .Build();
```

```cpp
auto settings = CXsollaClientSettings::Create();
settings->SetProjectId("< your_project_id >");
settings->SetLoginId("< your_login_id >");
```
 

### Events
 

The **Xsolla Events API** serves as an alternative to webhook-based integrations, where the integrator’s backend usually handles most of the logic (purchase validation, awarding, etc.). Instead of sending webhooks directly, the API intercepts them and stores their payloads in a remote queue managed by Xsolla. This approach enables serverless integrations for projects that lack backend capacity or don't require one, yet still prefer handling the entire transaction flow directly from the app. CAUTION

Opting for the *Xsolla Events API* disables your project's webhooks. In other words, you cannot simultaneously handle webhooks on your backend and use the *Events API* in your app. **Enabling Xsolla Events API in Publisher Account**

Follow these steps to enable the *Xsolla Events API*:
 
- Open your *Publisher Account*. 
- In the left sidebar, select the project you want to enable the *Xsolla Events API* for. 
- From the left sidebar, expand the **Payments** menu and select **Webhooks**. 
- On the **Webhooks** page, click **Webhooks** on the right.  ![PublisherAccountEventsAPI] 
- Select **Use API**. 

Your project is now configured to use the *Xsolla Events API*, and the SDK can leverage this configuration.tip

To turn the *Xsolla Events API* mode off, follow the steps above, but select **Use Webhooks** this time. 

Below is the minimal events configuration required to enable the events functionality in the SDK: 

```csharp
var settings = XsollaStoreClientSettings.Builder.Create()
    //..    
    .SetWebhooksMode(XsollaStoreClientSettings.WebhooksMode.EventsApi)
    .Build();
```

```cpp
auto settings = CXsollaClientSettings::Create();
//..
settings->SetWebhooksMode(CXsollaClientSettings::WebhooksMode::EventsApi);
```
 info

For additional information, see the [**webhooks page**](https://developers.xsolla.com/sdk/publisher-account/webhooks#enabling-xsolla-events-api). 

### Build configuration from the settings
 

```csharp
var configuration = XsollaStoreClientConfiguration.Builder.Create()
    // Set the settings object you've acquired in previous step here..
    .SetSettings(settings)
    .SetSandbox(true)
    .SetLogLevel(XsollaLogLevel.Debug)
    .Build();
```

```cpp
auto configuration = CXsollaClientConfiguration::Create();
configuration->SetSettings(settings);
configuration->SetSandbox(true);
configuration->SetLogLevel(CXsollaClientConfiguration::XsollaLogLevel::Debug);
```
 

### Assign Xsolla access token to the configuration
 note

The access token [**JWT-structure ↗**](https://developers.xsolla.com/api/login/overview/#section/JWT-structure) ([https://developers.xsolla.com/api/login/overview/#section/JWT-structure](https://developers.xsolla.com/api/login/overview/#section/JWT-structure)). warning

A valid **access token** must already be known prior to the SDK initialization step.

```csharp
var configuration = XsollaStoreClientConfiguration.Builder.Create()
    .SetSettings(settings)
    .SetAccessToken("< your access token >")
    .Build();
```

```cpp
auto configuration = CXsollaClientConfiguration::Create();
configuration->SetSettings(settings);
configuration->SetAccessToken("< your access token >");
```
 

### Using device ID instead of the access token
 

Below is an example with the fields required to be set for the device ID authentication approach to work: 

```csharp
var settings = XsollaStoreClientSettings.Builder.Create()
    .SetProjectId("< your_project_id >")
    .SetLoginId("< your_login_id >")
    .Build();
```

```cpp
auto settings = CXsollaClientSettings::Create();
settings->SetProjectId("< your_project_id >");
settings->SetLoginId("< your_login_id >");
```
 

### Using OAuth2 instead of the access token
 

Below is an example with the fields required to be set for the OAuth2 authentication approach to work: 

```csharp
var settings = XsollaStoreClientSettings.Builder.Create()
    .SetProjectId("< your_project_id >")
    .SetLoginId("< your_login_id >")
    .SetOAuthClientId("< your_oauth_client_id >")
    // ..other properties
    .Build();
```

```cpp
auto settings = CXsollaClientSettings::Create();
settings->SetProjectId("< your_project_id >");
settings->SetLoginId("< your_login_id >");
settings->SetOAuthClientId("< your_oauth_client_id >")
```
 

### Testing purchase flow in sandbox mode
 

See [**Testing**](https://developers.xsolla.com/sdk/windowstores/sdk/testing) guide for more details on how to enable the sandbox mode and avoid spending real money while testing the integration.

Initialization

https://developers.xsolla.com/sdk/windowstores/sdk/initialization

After [configuring](https://developers.xsolla.com/sdk/windowstores/sdk/configuration), the **Xsolla SDK for Windows** needs to be initialized and connected to **Xsolla** services. 

```csharp
var storeClient = XsollaStoreClient.Builder.Create()
    .SetConfiguration(configuration)
    .SetOnRestore((item, error) => {
        if (error != null)
        {
            // handle error
            return;
        }
          
        // handle delayed purchased item
    })
    .AddProducts(productIds)
    .AddProduct("product1")
    .AddProduct("product2")
    .AddProduct("product3")
    .AddProduct("product4")
    .Build();

storeClient.Initialize((products, error) =>
{
    if (error != null)
    {
        // handle error
        return;
    }

    // products contains all requested products
});
```

```cpp
auto products = std::vector<std::string>{
	"product1",
	"product2",
	"product3",
};

auto client = new CXsollaStoreClient(configuration, products, [](std::shared_ptr<CXsollaStoreClientPurchasedProduct> product, std::shared_ptr<CXsollaStoreClientError> error) {
	std::cout << "C++ Restore successful for product: " << product->GetSku() << "\n";
});

client->Initialize([client](std::vector<std::shared_ptr<CXsollaStoreClientProduct>> products, std::shared_ptr<CXsollaStoreClientError> error) {
	if (error == nullptr) {
		std::cout << "C++ Initialization successful.\n";
	} else {
		std::cout << "C++ Initialization failed with error code: " << error->GetMessage() << "\n";
	}
});
```
 

The SDK loads only Virtual Items from your Xsolla Catalog — Virtual Currency, Bundles, and other catalog types are not returned.

Installation

https://developers.xsolla.com/sdk/windowstores/sdk/installation

#### Option 1: From archive
 
 
- Extract SDK to you project 
- Add dependencies to you project

Purchase flow

https://developers.xsolla.com/sdk/windowstores/sdk/purchase

Purchasing flow is a multistage process, which involves [payment collection](https://developers.xsolla.com/sdk/windowstores/sdk/purchase#starting-purchasing-flow), purchase [validation](https://developers.xsolla.com/sdk/windowstores/sdk/purchase#validating-purchases-recommended) and [consumption](https://developers.xsolla.com/sdk/windowstores/sdk/purchase#consuming-purchased-goods). 

### Starting purchasing flow
 

Use one of the snippets below to launch a purchasing flow depending on whether you use *Unity InApp Purchasing* or not: 

```cs
storeClient.PurchaseProduct(productId, (product, error) =>
{
    if (error != null)
    {
        // handle error
        return;
    }

    var receipt = product.ToReceipt();

    // handle the purchased product here (e.g. validate or consume)..
});
```

```cpp
client->PurchaseProduct(productId, 
    [](std::shared_ptr<CXsollaStoreClientPurchasedProduct> product, std::shared_ptr<CXsollaStoreClientError> error) {
    	if (error == nullptr) {
    		std::cout << "C++ Purchase successful for product: " << product->GetSku() << "\n";
    
    		auto receipt = CXsollaStoreClientHelpers::ConvertToReceipt(product)
    		// handle the purchased product here (e.g. validate or consume)..
    	}
    	else {
    		std::cout << "C++ Purchase failed with error code: " << error->GetMessage() << "\n";
    	}
    }
);
```
 

### Validating purchases (recommended)
 warning

Although validation is **NOT** a mandatory step, yet it's highly recommended to minimize the chance of fraud and any other malevolent intents. 

For instructions on how to validate your purchases, see [**Purchase Validation**](https://developers.xsolla.com/sdk/windowstores/sdk/validation) guide. 

### Consuming purchased goods
 

The very final step in the chain of purchasing flow stages is to award the purchased goods to the user and mark these goods as "awarded". The process also known as **purchase consumption**. 

Use the code below to do just that: 

```cs
storeClient.ConsumeProduct(item.sku, item.quantity, item.transactionId, error => {
    if (error != null)
    {
        return;
    }

    // handle purchased item
});
```

```cpp
client->ConsumeProduct( item->GetSku(), item->GetQuantity(), item->GetTransactionId(), 
    [](std::shared_ptr<CXsollaStoreClientError> error) {
        if (error == nullptr) {
            std::cout << "C++ Consume successful.\n";
        } else {
            std::cout << "C++ Consume failed with error code: " << error->GetMessage() << "\n";
        }
    }
);
```
 important

It's highly recommended to perform at least some form of [**validation**](https://developers.xsolla.com/sdk/windowstores/sdk/purchase#validating-purchases-recommended) before consuming the purchased goods.

Testing & Sandbox

https://developers.xsolla.com/sdk/windowstores/sdk/testing

This section provides various snippets and examples of how to configure the sandbox environment to be able to make test payments, make logging more verbose, and so on. 

### Enabling sandbox mode
 

```csharp
var configuration = XsollaStoreClientConfiguration.Builder.Create()
    // ...
    .SetSandbox(true)
    .Build();
```

```cpp
auto configuration = CXsollaClientConfiguration::Create();
// ...
configuration->SetSandbox(true);
```
 

#### Testing in Unity Editor
 

Testing in the Unity Editor uses the current platform's implementation (e.g., Windows or macOS). warning

Remember to turn the **sandbox** mode **OFF** before going **LIVE**. 

### Enabling additional logging
 

```csharp
var configuration = XsollaStoreClientConfiguration.Builder.Create()
    // ...
    .SetLogLevel(XsollaLogLevel.Debug)
    .Build();
```

```cpp
auto configuration = CXsollaClientConfiguration::Create();
// ...
configuration->SetLogLevel(CXsollaClientConfiguration::XsollaLogLevel::Debug);
```
 warning

Make sure the debug **logging** is **OFF** before going **LIVE**. 

### Test cards
 

For a list of cards to simulate payments in sandbox mode, see [here ↗](https://developers.xsolla.com/doc/pay-station/testing/test-cards) ([https://developers.xsolla.com/doc/pay-station/testing/test-cards](https://developers.xsolla.com/doc/pay-station/testing/test-cards)). important

The test cards won't work outside the **sandbox mode**.

Purchase Validation

https://developers.xsolla.com/sdk/windowstores/sdk/validation

Each purchase should be validated before awarding it to the end-user. This assists in preventing unauthorized purchases and ensures the integrity of your in-app economy. Validating purchases is a critical security measure to prevent fraud, protect your revenue, and ensure fair user experiences. 

Essentially, that are two distinct validation approaches readily available. 

### Server-side Validation
 

The most secure and reliable way to validate purchases is by using **server-to-server** (**S2S**) communication, with the client completely removed from the validation process. This minimizes security risks and ensures that entitlements are only granted based on verified data received directly from Xsolla. 

With Xsolla, server-side validation is implemented through a **webhook-based model**: 
 
- Once a user completes a purchase through your app, Xsolla services automatically trigger a [**webhook event**](https://developers.xsolla.com/sdk/publisher-account/webhooks#user-validation-user_validation) to your backend. 
- This webhook contains all the necessary purchase data, including the **Xsolla** `order ID`, enabling your server to validate and process the purchase asynchronously. 
- Your backend can then use this data to update the user’s entitlements, balance, inventory, etc., without requiring any additional input from the client. 
 

This flow ensures that the client has no role in deciding the outcome of a purchase — all validation logic lives on the server, using trusted data that originates from Xsolla itself. tip

For more information about how webhooks work, see [**here**](https://developers.xsolla.com/sdk/publisher-account/webhooks).  

### Client-side Validation
 

Provides means of validating a purchase on the user's device without a need for a backend (server). It's better than nothing, but has its own drawbacks, i.e. anything that runs on the client can potentially be exploited.  

```csharp
void MyOnPurchaseCallback(XsollaStoreClientPurchasedProduct product, XsollaStoreClientError error)
{
    _storeClient.ValidatePurchase(product.ToReceipt(), (success, error) =>
    {
        if (error == null && success)
        {
            // Purchase is Valid, handle the purchased item..
        }
        else
        {
            // Purchase is NOT valid or there was an error performing validation..
        }
    });
}
```

```cpp
client->ValidatePurchase(
    CXsollaStoreClientHelpers::ConvertToReceipt(product),
    [client, product](bool valid, std::shared_ptr<CXsollaStoreClientError> error) {
        if (error == nullptr) {
            std::cout << "C++ Purchase validation successful. Valid: " << (valid ? "true" : "false") << "\n";
        } else {
            std::cout << "C++ Purchase validation failed with error code: " << error->GetMessage() << "\n";
        }
    }
);
```