Enable Buy Button with custom checkout
Why is it important
Following recent updates to Apple’s policies in certain regions, developers are now permitted to guide users from their apps to external websites to accept payments for virtual items.
You can add visible buttons, banners, messages, and other calls to action that take users directly from your game to item purchase using a secure, browser-based checkout (to your Web Shop or payment UI) in a single click — without violating Apple’s rules or risking enforcement.
Buy Button integration via Headless checkout is ideal if you want to create your own custom payment UI and create a unique user experience. This integration option allows users to seamlessly proceed from the game to purchase in a browser using a wide range of payment methods, including one-click payments via Apple Pay, for fast and familiar mobile checkout experience.
Headless checkout is a payment acceptance solution based on the headless application architecture, where functionality is accessible via API. This approach separates the backend and business logic from the UI.
To use Headless checkout for iOS game applications:
- Create your own store.
- Integrate Headless checkout into your store.
- Add a link in your game that takes users to your store.
If you are looking for the fastest low-code integration option, check the Web Shop-based integration.
If you’re using a custom Web Shop that is not built with Xsolla Site Builder and want to integrate a ready-made payment UI into your game, check the integration via the Xsolla Mobile SDK.
How it works
Apple’s requirements:
In-app WebViews for external purchases are not allowed — payments must occur via Safari or the default browser.
External purchase links are currently permitted only for iOS applications on the United States storefront. Note that Apple’s app review guidelines refer to the
United States storefront, not user location .
User flow:
- The user opens the game application on iOS.
- The user clicks the purchase button next to the desired item.
- Your store opens in a web browser. To ensure a seamless user experience, implement end-to-end authorization.
- The user selects an item and makes a purchase without leaving the store.
- The user is redirected to the game application after successful transaction.
- The application receives the purchase confirmation via a webhook.
Integration flow
- Create a project in Publisher Account and sign a licensing agreement with Xsolla.
- Create an item catalog.
- Implement backend interactions: create a token and set up webhooks.
- Install SDK.
- Integrate SDK on the application side.
- Add a link in your game that takes users to your store where Headless checkout is integrated.
Create project and sign licensing agreement
Publisher Account is the main tool to configure Xsolla features, as well as to work with analytics and transactions.
To sign up, go to Publisher Account and create an account. To create a project, click Create project in the side menu and provide any necessary information. You can modify the settings later.

To sign the licensing agreement, go to the Agreements & Taxes > Agreements section and complete the agreement form.
Create item catalog
In-App Purchase (IAP) products are represented through virtual items in the Xsolla ecosystem. They have a name, description, SKU, and price. To set up your IAP SKU product catalog, you can:
- Upload a JSON file to quickly add your catalog to Publisher Account.
- Create an item catalog using the API calls from the Virtual items & currency > Admin documentation section.
Implement backend interaction
Create token
When the user clicks the purchase button, a payment token must be created. This token is used to open the payment UI and contains information about the user, the item, and additional parameters passed to Xsolla. Refer to the documentation for detailed information. To use sandbox mode, pass the“mode”: “sandbox”
parameter in the body of the request for getting a token.Configure webhooks
To enable webhooks:
- In your project inside Publisher Account, go to the Project setting > Webhooks section.
- In the Webhook server field, specify the server URL — where you want to receive webhooks in the
https://example.com
format. You can also specify the URL you find in a tool for testing webhooks. - A secret key to sign project webhooks is generated by default. If you want to generate a new secret key, click the refresh icon.
- Click Enable webhooks.

For the full operation of the in-game store and payment management, it is necessary to implement the processing of the main webhooks:
Webhook name | Description |
---|---|
User validation > User validation (user_validation ) | Is sent at different stages of the payment process to ensure the user is registered in the game. |
Game services > Combined webhooks > Successful payment for order (order_paid ) | It contains payment data, transaction details, and information about purchased items. Use the data from the webhook to add items to the user. |
Game services > Combined webhooks > Order cancellation (order_canceled ) | It contains data of the canceled payment, transaction details, and information about purchased items. Use the data from the webhook to remove the purchased items. |
Install SDK
- Install the SDK as an npm package by running the command:
1npm install --save @xsolla/pay-station-sdk
- Initialize the SDK by passing environment parameters:
- typescript
1import { headlessCheckout } from '@xsolla/pay-station-sdk';
2
3await headlessCheckout.init({
4 sandbox: true,
5});
- Pass the payment token for the initialized SDK:
- typescript
1await headlessCheckout.setToken(accessToken);
Integrate SDK on application side
After installing and initializing SDK:
- Initialize the payment UI specifying the payment method ID. The
headlessCheckout.form.init
method returns an object used for further interaction with the payment UI.
- typescript
1await headlessCheckout.form.init({
2 paymentMethodId: 3175, // Apple Pay payment ID
3});
- Add the handling of the
show_fields
event for displaying additional fields.
- typescript
1headlessCheckout.form.onNextAction((nextAction) => {
2 switch (nextAction.type) {
3 case 'show_fields':
4 this.handleShowFieldsAction(nextAction);
5 }
6});
- Add the following components to the HTML markup of the payment UI:
- Mandatory components:
psdk-legal
— to display information about legal documents.psdk-total
— to display the total purchase amount.
- Payment form components. You can either use the built-in
psdk-payment-form
component or manually create payment UI elements using the ready-to-use components. - The payment button component —
psdk-apple-pay
. You can also use thepsdk-submit-button
component that already includespsdk-apple-pay
.
- Mandatory components:
- html
1<psdk-legal></psdk-legal>
2<psdk-total></psdk-total>
3
4
5<psdk-payment-form></psdk-payment-form>
6<psdk-apple-pay text="Apple Pay"></psdk-apple-pay>
- Add the handling of the
check_status
event for the payment status change.
- typescript
1headlessCheckout.form.onNextAction((nextAction) => {
2 switch (nextAction.type) {
3 case 'check_status': {
4 showStatus = true;
5 }
6 }
7});
- Add the
psdk-status
component to the HTML markup of the payment UI to display a payment status.
- html
1@if (showStatus) {
2 <psdk-status></psdk-status>
3}
How to detect iOS storefront
To determine the current iOS storefront and adjust SDK functionality based on the region, use the following code snippets:
obj-c
- obj-c
- swift
1[SKPaymentQueue loadCurrentStorefrontCountryCodeWithCompletion:^(NSString* _Nullable countryCode) {
2 settings.enablePayments = countryCode && [countryCode isEqualToString:@"USA"];
3
4 [[SKPaymentQueue defaultQueue] startWithSettings:settings];
5}];
1SKPaymentQueue.loadCurrentStorefrontCountryCode { countryCode in
2 settings.enablePayments = countryCode == "USA"
3
4 SKPaymentQueue.default().start(settings)
5}
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 native Storefront directly, as shown below:
SKStorefront
implementation, as it performs synchronous loading that blocks the main thread. This can lead to UI freezes and degraded user experience, as noted in Apple’s official documentation.- swift
1let storefront = await Storefront.current
2let countryCode = storefront?.countryCode
3
4settings.enablePayments = countryCode == "USA"
5
6SKPaymentQueue.default().start(settings)
One-click payment via Apple Pay
One-click payment allows users to pay with Apple Pay, a familiar and secure native payment method, on supported devices. To configure one-click payment:
- Create a request to enable this option. To do so:
a. Open your Publisher Account and go to the Support Hub section.
b. Click Submit request.
c. In the window that opens, fill in the fields:
- Summary. For example, Apple Pay one-click payment setup.
- Description. Specify the domain used for opening the payment UI, e.g.,
amazing.store.com
. - Project ID. Select a project ID from the drop-down list. If you want to configure the one-click payment option for multiple projects, specify their IDs in the Description field.
d. Click Send.
- Wait for your domain association file. This step is performed by Xsolla:
- Xsolla registers your domain with Apple.
- Xsolla receives the domain association file from Apple.
- Xsolla emails you the domain association file and provides instructions on where to upload it.
- Update the SDK initialization script as shown below:
- typescript
1const config: InitialOptions = {
2 isWebview: false,
3 theme: 'default',
4 language: parameters.language,
5 topLevelDomain: 'amazing.store.com',
6 isApplePayInstantFlowEnabled: true
7};
8
9await initHeadlessCheckoutLib(config);
- Reply to Xsolla email and confirm that you have uploaded the domain association file to the specified location and updated the SDK initialization script.
- Wait for confirmation from Xsolla that one-click payment has been successfully enabled in your project.
Found a typo or other text error? Select the text and press Ctrl+Enter.