Set up selling game keys

You can sell game keys via a direct link, store UI, or widget.

Note

You can sell game keys for real currency only after signing a license agreement with Xsolla. To do this, in Publisher Account, go to the Agreements & Taxes > Agreements > Licensing Agreement section, complete the agreement form, and wait for the confirmation. It may take up to 3 business days to review the agreement.

ItemSelling method
One copy of a game (game key).Direct link, widget, or store interface. When selling through the store interface, use the quick purchase method without creating a cart.
Several copies of the game (game keys) or several games in a cart.Store interface. Use the Site Builder or integrate the Shop Builder API.

You can sell game keys to authorized and unauthorized users.

Authorization allows you to:

You can authorize users using the Login product or your own authorization system. Detailed setup information is provided in these instructions.

Note
Users can refund the keys. After successful refund, you will get the list of the affected keys in Xsolla email. We recommend you block the access to these keys on any third-party platforms.
Note

Access to the game is granted automatically after purchasing the game key. However, game platforms may set their own activation key rules.

You can limit the display time of the game key package in the catalog, e.g., during seasonal sales. To do this, pass the start and end date of the availability period per ISO 8601 in the periods object of the corresponding API call:

To limit the number of game keys available for purchase by the user, follow the instructions.

Game keys can be sold via a direct link that opens the payment UI in the following format:

Copy
Full screen
Small screen
    1https://purchase.xsolla.com/pages/buy?type={YOUR-ITEM-TYPE}&project_id={YOUR_PROJECT_ID}&sku={YOUR-ITEM-SKU}
    

    In addition to the main query parameters, you can also pass parameters for customizing the payment interface and selling to authenticated users.

    Add the following data to this link:

    • YOUR-ITEM-TYPE — item type. Possible values:

    • YOUR-PROJECT-ID — ID of your project that can be found in your Publisher Account next to the project name.

    • YOUR-ITEM-SKU — game key package SKU, for example:

      • order456 — SKU without a specific platform.
      • pre.order123_steam — SKU with a specific platform.

    We recommend using platform-specific game key packages. This helps avoid compatibility issues when using or activating the keys. In all cases, a platform-specific suffix must be added to the SKU — either automatically or manually, depending on how the key package is created:

    • When you create key packages in the Publisher Account, the system automatically adds a platform-specific suffix with underscore (e.g. _steam, _playstation) to the SKU you enter.

    • When you create platform-specific key packages using API calls, you can specify the platform suffix in any format that includes only lowercase and uppercase Latin alphanumeric characters, periods, dashes, and underscores.

    List of supported Xsolla platforms and their example suffixes:

    NameSKU suffix example
    Steam_steam
    PlayStation_playstation
    Xbox_xbox
    Uplay_uplay
    Origin_origin
    DRM Free_drmfree
    GOG_gog
    Epic Games_epicgames
    Nintendo Switch eShop_nintendo_eshop
    Discord Game Store_discord_game_store
    Oculus_oculus
    Viveport_viveport
    Google Stadia_stadia
    MY.GAMES Store_my_game

    To ensure the link works correctly, you can retrieve the exact SKU using the Get games list API call and include it in the payment link as the SKU value. The item SKU is returned in items.unit_items.sku parameter (typically, this SKU follows the format game_key_sku_drm_sku).

    Example URL for selling a game on Steam:

    Copy
    Full screen
    Small screen
      1https://purchase.xsolla.com/pages/buy?type=game_key&project;_id=123456&sku;=pre.order123_steam
      

      To make the payment UI match your game’s style, configure the theme, size, and other UI parameters as described in the guide on how to customize pay station. Add the ui_settings parameter to the URL and pass a settings.ui JSON object with Base64 encoding as the value.

      Example URL for opening the payment UI with a custom theme:

      Copy
      Full screen
      Small screen
        1https://purchase.xsolla.com/pages/buy?type={YOUR-ITEM-TYPE}&project_id={YOUR_PROJECT_ID}&sku={YOUR-ITEM-SKU}&ui_settings=ewoJCQkic2l6ZSI6ICJzbWFsbCIsCgkJCSJ0aGVtZSI6ICJkYXJrIgoJCX0=
        

        To sell keys to authenticated users, pass a user access token in the xsolla_login_token parameter. This token depends on the selected authentication method.

        Example URL for opening the payment UI with a token:

        Copy
        Full screen
        Small screen
          1https://purchase.xsolla.com/pages/buy?type={YOUR-ITEM-TYPE}&project_id={YOUR_PROJECT_ID}&sku={YOUR_ITEM_SKU}&xsolla_login_token={ACCESS_TOKEN}
          

          To test the payment flow in a sandbox mode, add the mode=sandbox parameter to the URL. You can use test bank cards to complete transactions.

          Example URL for opening the payment UI in sandbox mode:

          Copy
          Full screen
          Small screen
            1https://purchase.xsolla.com/pages/buy?type={YOUR-ITEM-TYPE}&project_id={YOUR_PROJECT_ID}&sku={YOUR-ITEM-SKU}&mode=sandbox
            
            Notice
            When purchasing in the sandbox mode, uploaded keys are not used. The system simulates a successful purchase without delivering actual keys.

            Selling via store UI

            You can sell game keys through the store interface. To create a store, you can:

            To sell game keys packages using the Shop Builder API:

            1. To display a catalog, use the Get games list method.
            2. Implement the purchase of game keys:

            Choose a suitable authentication option for the methods to work correctly.
            Note
            When selling a game through the Shop Builder API, you should implement a feature that allows players to select a specific platform on the client. To get the SKU, pass the value of the items.unit_items.sku parameter from the request to get the list of games.

            Selling via widget

            You can add a widget to your page to sell game keys and customize it. To copy the widget code, go to the Widget customization section after creating the keys package in your Publisher Account.

            If a game is sold on a single platform, the widget will display the game price specific to that platform.

            Example: Buy now for $10.

            If a game is sold on multiple platforms, the widget will display the lowest price among the platforms.

            Example: Get from $10.

            In the order creation window, the user can see prices for all platforms and make a choice.

            You can also display the price for a specific platform in the widget by specifying the platform SKU in the drm parameter.

            Widget code example:

            Copy
            Full screen
            Small screen
             1<div id="xsolla-buy-button-widget"></div>
             2
             3<script>
             4  var options = {
             5    project_id: "101010",
             6    sku: "my_key",
             7    user: {
             8      auth: "9qs9VyCIQQXBlzJQcfETIKWZDvhi4Sz1"
             9    },
            10    drm: "steam",
            11    item_type: "unit",
            12    api_settings: {
            13      sandbox: true
            14    },
            15    widget_ui: {
            16      target_element: "#xsolla-buy-button-widget",
            17      theme: {
            18        foreground: "green",
            19        background: "light"
            20      }
            21    },
            22    payment_widget_ui: {
            23      lightbox: {
            24        height: "700px",
            25        spinner: "round"
            26      }
            27    }
            28  };
            29
            30  var s = document.createElement("script");
            31  s.type = "text/javascript";
            32  s.async = true;
            33  s.src = "https://cdn.xsolla.net/embed/buy-button/3.1.8/widget.min.js";
            34  s.addEventListener("load", function () {
            35    var widgetInstance = XBuyButtonWidget.create(options);
            36  });
            37  document.getElementsByTagName("head")[0].appendChild(s);
            38</script>
            39
            40<style>
            41  #xsolla-buy-button-widget {
            42    /* place code for button positioning here */
            43    margin: 10px;
            44  }
            45
            46  /* Styles the button itself */
            47  .x-buy-button-widget.x-buy-button-widget__tiny .x-buy-button-widget-payment-button {
            48    background-color: #ff005b;
            49    color: black;
            50  }
            51</style>
            
            Note
            Widget parameters

            ParameterTypeDescription
            project_id
            integerID of the project in which game keys or bundles with game keys, in-game items or bundles with items, are uploaded.
            item_type
            stringItem type. Can take values of virtual_good, virtual_currency, game_key, unit. The unit type is used when there are multiple platforms for distributing the game.
            sku
            stringUnique item ID.
            drm
            stringDistribution platform SKU, for example, steam. Allows displaying the price for a specific platform.
            api_settings
            objectEnvironment and host configuration settings:
            • host — host for performing requests. The default value is — store.xsolla.com
            • api_host — host for performing API requests. The default value is — store.xsolla.com/api
            • sandbox — set true value, to test the payment process. sandbox-secure.xsolla.com will be used instead secure.xsolla.com to process payments (see Testing the payment process)
            user
            objectAn object with user data.
            user.auth
            stringUser authorization token: JSON Web Token or Pay Station access token.
            user.locale
            stringUser locale. Determines the language of the button text and payment interface. It is used a two-letter language code based on ISO_639-1.
            widget_ui.theme
            objectThe color theme of the widget, determining its appearance. It can take values {foreground:[‘blue’,‘red’,‘green’,‘gold’], background:[’light’,‘dark’]}
            widget_ui.template
            stringTemplate. Possible values:
            • standard (default) - includes game image, title, and stylized button
            • simple — only displays the button without any styles applied
            widget_ui.target_element
            stringElement of the page, where the widget should be rendered (jQuery selector should be used, for example #widget-example). Required

            Parameters that determine the appearance of the payment interface

            ParameterTypeDescription
            payment_ui
            objectPayment interface appearance parameters.
            payment_widget_ui
            objectAn object with parameters that determine the appearance of the payment interface.
            payment_widget_ui.lightbox
            objectAn object with options for the modal window in which the payment interface is opened.
            payment_widget_ui.lightbox.width
            stringLightbox frame width. If null, depends on Pay Station width. Default is null.
            payment_widget_ui.lightbox.height
            stringLightbox frame height. If null, depends on Pay Station height. Default is 100%.
            payment_widget_ui.lightbox.zIndex
            integerDefines arrangement order. Default is 1000.
            payment_widget_ui.lightbox.overlayOpacity
            integerOpacity of the widget’s background (0 — completely ​​transparent, 1 — completely opaque). The default value is 60% (.6).
            payment_widget_ui.lightbox.overlayBackground
            stringOverlay background color. Default is #000000.
            payment_widget_ui.lightbox.contentBackground
            stringFrame background color. Default is #ffffff. Note that these color changes do not affect the Pay Station iframe itself, only the settings of the lightbox that hold it.
            payment_widget_ui.lightbox.spinner
            stringType of animated loading indicator. Can be xsolla or round. Default is xsolla.
            payment_widget_ui.lightbox.spinnerColor
            stringSpinner color. No default value.
            payment_widget_ui.childWindow
            objectSettings for the child window in which the payment interface is opened. Works for the mobile version.
            payment_widget_ui.childWindow.target
            stringThe property that determines where the child window should be opened. It can take values of _blank, _self, _parent. The default value is — _blank.

            Widget methods

            • var widgetInstance = XBuyButtonWidget.create(options) — create the widget instance and render it on the page.
            • widgetInstance.on(event, handler) — attaches an event handler function for the event to the widget.
              • event (string) — event type.
              • handler (function) — a function to execute when the event is triggered.
            • widgetInstance.off(event, handler) — removes an event handler.
              • event (string) — event type.
              • handler (function) — a handler function previously attached for the event.

            List of events:

            ParameterDescription
            initWidget initialized.
            openWidget opened.
            loadPayment UI (Pay Station) loaded.
            closePayment UI (Pay Station) closed.
            statusUser is on the status page.
            status-invoiceUser is on the status page; payment in progress.
            status-deliveringEvent when the user was moved on the status page, payment was completed, and we’re sending payment notification.
            status-doneUser is on the status page; payment credited to the user’s account.
            status-troubledEvent when the user was moved on the status page, but the payment failed.

            You can access the list of events using XBuyButtonWidget.eventTypes object.

            Button customization

            1. Open your project in Publisher Account and go to the Items catalog > Game keys section.
            2. Select a game key and go to the Widget customization tab.
            Note
            If there are no game keys, create a new one.
            1. In the Customize block, select the background color.
            Note
            You can also modify the theme object in code so that the parameter background has an empty string as a value.
            1. When you add the widget code to your page, it includes inherited styles. Add the styles below to override these styles.
            Notice
            Added these in a style tag below the script tag that you got from the Widget customization tab for CSS inheritance/priority reasons.
            Copy
            Full screen
            Small screen
             1/* This should be used for button positioning but note this technically repositions the entire widget */
             2#xsolla-buy-button-widget {
             3  /* place code for button positioning here */
             4}
             5
             6/* Styles the button itself */
             7.x-buy-button-widget.x-buy-button-widget__tiny
             8  .x-buy-button-widget-payment-button {
             9  background-color: #ff005b;
            10  color: black;
            11}
            12
            13/* Button on hover */
            14.x-buy-button-widget.x-buy-button-widget__tiny
            15  .x-buy-button-widget-payment-button:hover {
            16  background-color: #ff005b;
            17}
            18
            19/* The following are style overrides to leave you with just the button */
            20
            21/* space immediately surrounding button */
            22.x-buy-button-widget-button-block.x-buy-button-widget-button-block__light {
            23  background-color: white;
            24}
            25
            26/* space above button (including game title area) */
            27.x-buy-button-widget.x-buy-button-widget__tiny
            28  .x-buy-button-widget-game-logo {
            29  height: 200px;
            30  background-image: none !important;
            31  background-color: white;
            32}
            33
            34 /* Game title (located right above button) */
            35.x-buy-button-widget-game-name.x-buy-button-widget-game-name__light {
            36  display: none !important;
            37}
            
            Notice
            • The id/class names above and the code snippet are used in conjunction with the copied widget code (the image at step 5). For this reason, it is important that you implement the copied widget code.
            • You can use the code above as is or modify the code based on your scenario. The code comments (lines 1, 3, 5, 11, 16, 18, 22, 29) are there to help determine what each CSS rule targets and assist in future styling. For example, if you want just the button (and not the entire widget), you’ll need to substitute the background color of your page in the places below where the color is white — lines 20 and 27.

            How to create multiple buttons or SKUs

            You can refer to Xsolla Pay2Play Widget Simple Integration 4 buttons for a code example on how this is accomplished using the Pay2Play widget.

            The structure is similar to the Buy Button widget code. An example of a migration:

            Copy
            Full screen
            Small screen
             1<div id="xsolla-buy-button-widget"></div>
             2<div id="xsolla-buy-button-widget-2"></div>
             3
             4<script>
             5  var options = {
             6    project_id: "191204",
             7    sku: "789",
             8    item_type: "unit",
             9    api_settings: {
            10      sandbox: false
            11    },
            12    widget_ui: {
            13      target_element: "#xsolla-buy-button-widget",
            14      theme: {
            15        foreground: "gold",
            16        background: ""
            17      }
            18    },
            19    payment_widget_ui: {
            20      lightbox: {
            21        height: "700px",
            22        spinner: "round"
            23      }
            24    }
            25  };
            26
            27  // options for second buy button - note the different SKU and target_element
            28  var options2 = {
            29    project_id: "191204",
            30    sku: "123",
            31    item_type: "unit",
            32    api_settings: {
            33      sandbox: false
            34    },
            35    widget_ui: {
            36      target_element: "#xsolla-buy-button-widget-2",
            37      theme: {
            38        foreground: "gold",
            39        background: ""
            40      }
            41    },
            42    payment_widget_ui: {
            43      lightbox: {
            44        height: "700px",
            45        spinner: "round"
            46      }
            47    }
            48  };
            49
            50  var s = document.createElement("script");
            51  s.type = "text/javascript";
            52  s.async = true;
            53  s.src = "https://cdn.xsolla.net/embed/buy-button/3.1.4/widget.min.js";
            54
            55  // one event listener per widget instance. repeat for more buttons, passing in different SKUs
            56  s.addEventListener("load", function () {
            57    var widgetInstance = XBuyButtonWidget.create(options);
            58  });
            59  s.addEventListener("load", function () {
            60    var widgetInstance2 = XBuyButtonWidget.create(options2);
            61  });
            62
            63  document.getElementsByTagName("head")[0].appendChild(s);
            64</script>
            
            Notice
            • Lines 1 and 2 — one div per button, each with a unique ID.
            • Starting on line 26 are the options for the second button (laid out in the options2 object). You will need a set of options like the ones above for each Buy button. Note the different sku (line28) and target_element (line 34, targeting the div on line 2).
            Was this article helpful?
            Thank you!
            Is there anything we can improve? Message
            We’re sorry to hear that
            Please explain why this article wasn’t helpful to you. Message
            Thank you for your feedback!
            We’ll review your message and use it to help us improve your experience.
            Last updated: November 18, 2025

            Found a typo or other text error? Select the text and press Ctrl+Enter.

            Report a problem
            We always review our content. Your feedback helps us improve it.
            Provide an email so we can follow up
            Thank you for your feedback!
            We couldn't send your feedback
            Try again later or contact us at doc_feedback@xsolla.com.