Skip to content

Step 3. Purchase a product

Open the Steam payment window for the product the user selected, proceed with the purchase process, and then receive the payment completion signal.

Follow the steps below to purchase a product on Steam.

1. Display the in-app store UI

Implement the in-app store UI in the app and display it to users. When a user chooses a product, let them select the payment button. When the user selects the payment button, proceed to the next step.

2. Optional: Save pre-purchase information

After the user selects the payment button and before the Steam Overlay payment window opens, save the purchase attempt information. For the full flow, see Record purchase information. This step is optional.

Method

CreatePrePurchaseAsync

CreatePrePurchaseAsync() saves the product, expected payment amount, currency, country and language, app server, and IapPayload as a pre-purchase tracking record.

Call parameters

Field name Type Required Description
request PrePurchase Required The request data object for saving pre-purchase information.
context ApiCallContext Optional Per-call settings object that specifies the idempotency key, cancellation token, and request policy. If omitted, the default values are used.

PrePurchase

Field name Type Required Description
Country string Required Pass the user's country code (two-letter ISO 3166-1 code)
Currency string Required Pass the payment currency (three-letter ISO 4217 code). Use the currency value of the product retrieved in Step 2. Get the product list
Language string Required Pass the language code (two-letter ISO 639-1 code)
Price decimal Required The expected payment amount. It may include decimals depending on the currency. Pass the price value of the product retrieved in Step 2. Get the product list
ProductId string Required The unique identifier of the in-app product the user selected. Pass the Product ID retrieved in Step 2. Get the product list
ProviderId PrePurchaseProviderId Required The market and payment method identifier (enum). Because this request is for Steam payments only, specify only Steam.
ServerId string Optional The server ID you check on the App Server tab after you register the app server in App Info > App Server by following Hive Console App Server
AccountUuid string Optional A UUIDv5 value generated from the logged-in user's playerId. Pass it before you start the payment. See Generate an AccountUuid.
IapPayload string Optional The payload (JSON string) that the developer attaches to the market payment. It is passed as is to the app server callback after the purchase is completed.
RequestDate DateTimeOffset Optional The request time (UTC). If omitted, the server's current time is used.

IapPayload usage example

With IapPayload in PrePurchase, you can include app-specific data in the receipt that is issued after the purchase is completed. The following is one example of using IapPayload.

For example, suppose that in a mobile app with three user profiles, A, B, and C, a user purchases an in-app product with profile A. If you request the purchase with a JSON string such as {"character": "A"} in IapPayload, this value is included in the purchase receipt and passed to the app when the payment completes successfully.

If the payment is completed but the product cannot be delivered because of a network error, you must retrieve the failed delivery, verify its receipt again, and then deliver the product. At this point, you need to know which of the user's profiles A, B, and C should receive the product. With the {"character": "A"} information in the IapPayload included in the purchase receipt, you can confirm that profile A is the delivery target and deliver the product accurately.

This is only one example. You can put information that your app needs, such as the product purchase time and purchasing user information, in IapPayload and attach it to the purchase receipt. Use it to fit your app's situation.

Call example

Check the success result of PaymentsCreatePrePurchaseResult and the domain-specific results (Outcome) defined by this method in the example below and in the response status. For the result model and handling principles of common failures (Failure) that prevent the request from being performed, see Common error handling.

using System;
using Hive.Axyl.Payments;
using Hive.Axyl.Core;

// payments: IPaymentsService registered during initialization (for how to retrieve it, see [Install and initialize the module](../init.md))
IPaymentsService payments = HiveCore.Resolve<IPaymentsService>();

// Create the request object with the product information at the time the user selects the payment button
var request = new PrePurchase
{
    Country = "KR",
    Currency = "KRW",
    Language = "ko",
    Price = 1200.0m,
    ProductId = "com.game.item.gold_100",
    ProviderId = PrePurchaseProviderId.Steam,
    ServerId = "server01",
    RequestDate = DateTimeOffset.UtcNow
};

PaymentsCreatePrePurchaseResult result = await payments.CreatePrePurchaseAsync(request);

switch (result)
{
    case PaymentsCreatePrePurchaseResult.Success success:
        // Pre-purchase information saved. No data is returned. Proceed to the Steam payment step.
        break;

    // Request value problem — check the required values and formats
    case PaymentsCreatePrePurchaseResult.PaymentBadRequest:
    case PaymentsCreatePrePurchaseResult.PaymentInvalidParameter:
        Debug.LogError("Check the request parameters.");
        break;

    case PaymentsCreatePrePurchaseResult.UnknownOutcome unknownOutcome:
        Debug.LogWarning($"Unknown result: {unknownOutcome.Code}");
        break;

    // Handle common Failure
    case PaymentsCreatePrePurchaseResult.Failure failure:
        HiveError err = failure.Problem;
        Debug.LogError($"[{err.Code}] {err.Message} (trace: {err.TraceId})");
        break;

    // Safety net: unhandled results
    default:
        Debug.LogWarning($"Unhandled result: {result.GetType().Name}");
        break;
}

Response data

No data is returned on success.

Response example

// In the Success branch, move to the Steam payment step without any returned data.

Response status

The following table summarizes the success result of PaymentsCreatePrePurchaseResult and the domain-specific results (Outcome) defined by this method. For the result model and handling principles of common failures (Failure) that prevent the request from being performed, see Common error handling. No data is returned on success.

Response case Description App client handling
Success Pre-purchase information saved Proceed to the Steam payment step
PaymentBadRequest The payment request cannot be processed. Check the request values and call conditions
PaymentInvalidParameter The payment request parameters are invalid. Check the request field values
UnknownOutcome A domain-specific result that the SDK does not recognize. Treat it as a failure and log the result code
Failure Common Failure. See Common error handling. Handle it according to the common error handling guidelines

3. Receive the Steam payment authorization callback

Steam sends the user's payment authorization result through a callback. First start receiving callbacks with ISteamMicrotransactionsPlugin of the Hive Axyl Steam payment plugin (com.com2usplatform.hiveaxyl.payments.addon.steam), and then proceed with the Steam payment. When the user completes the payment in the Steam payment window, the order number and authorization status are passed through the MicroTxnAuthorizationResponse event.

using Hive.Axyl.Core;
using Hive.Axyl.Payments.Addon.Steam;

// Retrieve the plugin registered with builder.AddSteamMicrotransactions() during initialization.
if (!HiveCore.TryResolve<ISteamMicrotransactionsPlugin>(out var plugin))
{
    Debug.LogError("The Steam payment plugin is not registered.");
    return;
}

plugin.MicroTxnAuthorizationResponse += response =>
{
    if (response.Authorized)
    {
        // The user authorized the payment → proceed to 6. Save payment result data and confirm authorization (order number: response.OrderId)
    }
    else
    {
        // The user canceled the payment → end the purchase flow
    }
};

var startResult = await plugin.StartCallbackListenerAsync();
switch (startResult)
{
    case SteamMicrotransactionsServiceStartCallbackListenerResult.Success:
    case SteamMicrotransactionsServiceStartCallbackListenerResult.AlreadyStarted:
        // Ready to receive callbacks → proceed to payment session initialization (InitiatePurchaseAsync())
        break;

    default:
        // Failed to start listening (for example, Steamworks is not initialized) — do not start the payment flow
        break;
}

4. Proceed with the Steam payment: initialize the payment session

Proceed with the payment through the Steam Web API and receive a transaction ID.

Method

InitiatePurchaseAsync

To implement the start of a Steam payment, call InitiatePurchaseAsync() provided by the Hive Axyl SDK. It initializes the payment session through the Steam Web API, and then issues and returns a transaction ID. When this method runs, Steam displays the Steam Overlay payment window to the user.

Call parameters

Field name Type Required Description
request PurchaseInitRequest Required The request data for initializing the payment session.
context ApiCallContext Optional Per-call settings object that specifies the idempotency key, cancellation token, and request policy. If omitted, the default values are used.

PurchaseInitRequest

Field name Type Required Description
AppVersion string Optional The app version.
Country string Required The country code from the FetchSteamProductsAsync() response.
Currency string Required The currency code from the FetchSteamProductsAsync() response.
IapPayload string Optional App data.
Language string Required The language code.
ProductId string Required The in-app product ID registered in the store.
ProviderId PurchaseInitRequestProviderId Required The market identifier. Because this request is for Steam payments only, specify only Steam.
ServerId string Optional The server ID you check on the App Server tab after you register the app server in App Info > App Server by following Hive Console App Server.
StorePlayerId long Required The Steam 64-bit SteamID.

Call example

Check the success result of PaymentsInitiatePurchaseResult and the domain-specific results (Outcome) defined by this method in the example below and in the response status. For the result model and handling principles of common failures (Failure) that prevent the request from being performed, see Common error handling.

using Hive.Axyl.Payments;
using Hive.Axyl.Core;

// payments: IPaymentsService registered during initialization (for how to retrieve it, see [Install and initialize the module](../init.md))
IPaymentsService payments = HiveCore.Resolve<IPaymentsService>();

var request = new PurchaseInitRequest
{
    Country = "KR",
    Currency = "KRW",
    Language = "ko",
    ProductId = "com.game.product1",
    ProviderId = PurchaseInitRequestProviderId.Steam,
    StorePlayerId = 76561198000000000 // Steam 64-bit SteamID
};

PaymentsInitiatePurchaseResult result = await payments.InitiatePurchaseAsync(request);

switch (result)
{
    case PaymentsInitiatePurchaseResult.Success success:
        // Payment session initialized. Proceed to the next step with the returned receipt and transaction information
        Debug.Log($"orderId: {success.Data.OrderId}, transId: {success.Data.StoreTransactionId}");
        break;

    case PaymentsInitiatePurchaseResult.PaymentBadRequest:
    case PaymentsInitiatePurchaseResult.PaymentInvalidParameter:
        // Check the request values and required fields
        break;

    // Handle common Failure
    case PaymentsInitiatePurchaseResult.Failure failure:
        HiveError err = failure.Problem;
        Debug.LogError($"[{err.Code}] {err.Message} (trace: {err.TraceId})");
        break;

    // Safety net: unhandled results and unknown new results (UnknownOutcome)
    default:
        Debug.LogWarning($"Unhandled result: {result.GetType().Name}");
        break;
}

Response data

On success, the result is contained in Data (PurchaseInitResponseData) of PaymentsInitiatePurchaseResult.Success.

Field name Type Required Description
Data.AxylReceipt string Optional The AES-sealed receipt issued by the server. Keep it without changing the value, and use it as is in the RecordStorePurchaseAsync() request, in the receipt information you pass to the app server, and in the purchase finalization request.
Data.OrderId string Required The Hive Axyl internal order number. Pass it as is in the RecordStorePurchaseAsync() request.
Data.ProductId string Required The in-app product ID registered in the store.
Data.StoreTransactionId string Required The Steam transaction ID (transid of the Steam Web API). Pass it as is in the RecordStorePurchaseAsync() request.
Data.Meta string? Optional Response meta information.

Response example

// Example of success.Data in the Success branch
// success.Data.AxylReceipt = "Bg1THyMwfTZ/pn+SsPSEeykg4LaNHspLdrd8xQpvT/w="
// success.Data.OrderId = "2026010100001"
// success.Data.ProductId = "com.game.product1"
// success.Data.StoreTransactionId = "3390549843"

Response status

The following table summarizes the success result of PaymentsInitiatePurchaseResult and the domain-specific results (Outcome) defined by this method. For the result model and handling principles of common failures (Failure) that prevent the request from being performed, see Common error handling.

Response case Description App client handling
Success The payment session was initialized, and the Hive Axyl receipt, order number, and Steam transaction ID were returned. Proceed to the step that displays the Steam payment window and waits for authorization
PaymentBadRequest The request cannot be processed. Check the request values and call conditions
PaymentInvalidParameter Invalid parameter. Check and fix the request fields
UnknownOutcome A domain-specific result that the SDK does not recognize. Treat it as a failure and log the result code
Failure Common Failure. See Common error handling. Handle it according to the common error handling guidelines

5. Receive the payment authorization status through the MicroTxnAuthorizationResponse event

The order number and the payment authorization status are passed through the MicroTxnAuthorizationResponse event that you registered in step 3. If Authorized is true, proceed to the next step. If it is false, end the purchase flow.

When you no longer need to receive callbacks, call StopCallbackListenerAsync() to stop listening.

6. Save payment result data and confirm authorization

Call RecordStorePurchaseAsync() right after the user authorizes the payment in the Steam Overlay. This step is the payment result saving step of Record purchase information. It is also a required step of the standard Steam payment flow, which confirms the authorization with QueryTxn and changes the order status from 0 to 1.

Method

RecordStorePurchaseAsync

Pass the AES-sealed receipt and the order information returned by InitiatePurchaseAsync() to RecordStorePurchaseAsync(). This method confirms the Steam authorization and saves the payment result.

Call parameters

Field name Type Required Description
request PurchaseRequest Required The request data object for saving the payment result.
context ApiCallContext Optional Per-call settings object that specifies the idempotency key, cancellation token, and request policy. If omitted, the default values are used.

PurchaseRequest

Field name Type Required Description
AccountUuid string Optional A UUIDv5 value generated from the logged-in user's playerId. Pass it when you record the payment result. If you passed it at the payment start step, use the same value. See Generate an AccountUuid.
AxylReceipt string Required Pass Data.AxylReceipt from the payment session initialization response exactly as is. For a purchase recovered through Restore purchases, pass the AxylReceipt from that response. The Steam order number alone cannot replace it.
Country string Required The country code (two-letter ISO 3166-1 code). Example: KR
Currency string Optional The payment currency (three-letter ISO 4217 code). Paired with Price, it is compared with the values that the server recorded. If you send only one of the two, the currency is not compared and only the amount is compared.
IapPayload string Optional The payload (JSON string) that the developer attached to the market payment. It is passed as is to app server verification and to the purchase completion callback.
Language string Required The language code (two-letter ISO 639-1 code). Example: ko
OrderId string Optional The Hive Axyl internal order number from the Steam purchase initialization response.
Price decimal Optional The payment amount. It may include decimals depending on the currency. If you send it, the server compares it with the amount it recorded. If you omit it, no comparison is made. For Steam payments, the request is rejected if the amounts differ. The amount recorded in the payment history is the value in the order information that the server stores.
ProductId string Required The in-app product ID registered in the market. Because Steam payments are processed based on the order information stored on the server, this value is not used for comparison.
ProjectInfo string Optional An app-specific free-form JSON string. The value is only stored and is not passed to the app server. Set values to pass to the app server in IapPayload.
ProviderId PurchaseRequestProviderId Required Because this request is only for saving Steam payment results, specify Steam.
Quantity int Optional Purchase quantity
RequestDate DateTimeOffset Optional The client request time (UTC). If omitted, the server's current time is used.
RequestType int Optional The request type. 1: new purchase, 2: purchase restoration
ServerId string Optional The app server ID registered by following Hive Console App Server.
StoreTransactionId string Optional The Steam store transaction ID.

Call example

Check the success result of PaymentsRecordStorePurchaseResult and the domain-specific results (Outcome) defined by this method in the example below and in the response status. For the result model and handling principles of common failures (Failure) that prevent the request from being performed, see Common error handling.

using System;
using Hive.Axyl.Payments;
using Hive.Axyl.Core;

// payments: IPaymentsService registered during initialization (for how to retrieve it, see [Install and initialize the module](../init.md))
IPaymentsService payments = HiveCore.Resolve<IPaymentsService>();

// Create the authorization confirmation request with the values received in the InitiatePurchaseAsync() success response.
var request = new PurchaseRequest
{
    AxylReceipt = axylReceipt,
    OrderId = orderId,
    ProviderId = PurchaseRequestProviderId.Steam,
    ProductId = "com.game.item.gold_100",
    Price = 1200.0m,
    Currency = "KRW",
    Country = "KR",
    Language = "ko",
    Quantity = 1,
    StoreTransactionId = storeTransactionId,
    RequestType = 1, // 1: new purchase
    RequestDate = DateTimeOffset.UtcNow,
    ServerId = "server01"
};

PaymentsRecordStorePurchaseResult result = await payments.RecordStorePurchaseAsync(request);

switch (result)
{
    case PaymentsRecordStorePurchaseResult.Success success:
        // Authorization confirmed and saved successfully. You can check success.Data.Meta.
        break;

    // Problem with request values or payment status
    case PaymentsRecordStorePurchaseResult.PaymentBadRequest:
    case PaymentsRecordStorePurchaseResult.PaymentInvalidParameter:
    case PaymentsRecordStorePurchaseResult.PaymentResourceNotFound:
    case PaymentsRecordStorePurchaseResult.PaymentUnauthorized:
    case PaymentsRecordStorePurchaseResult.VerifyError:
        Debug.LogError("Check the request values and the payment status.");
        break;

    case PaymentsRecordStorePurchaseResult.UnknownOutcome unknownOutcome:
        Debug.LogWarning($"Unknown result: {unknownOutcome.Code}");
        break;

    // Handle common Failure
    case PaymentsRecordStorePurchaseResult.Failure failure:
        HiveError err = failure.Problem;
        Debug.LogError($"[{err.Code}] {err.Message} (trace: {err.TraceId})");
        break;

    // Safety net: unhandled results
    default:
        Debug.LogWarning($"Unhandled result: {result.GetType().Name}");
        break;
}

Response data

On success, the result is contained in Data (PurchaseResponseData) of PaymentsRecordStorePurchaseResult.Success.

Field name Type Required Description
Data.Meta string Optional Response meta information

Response example

// You can check success.Data.Meta in the Success branch.

Response status

The following table summarizes the success result of PaymentsRecordStorePurchaseResult and the domain-specific results (Outcome) defined by this method. For the result model and handling principles of common failures (Failure) that prevent the request from being performed, see Common error handling.

Response case Description App client handling
Success Payment result saved Pass the receipt information to the app server and proceed to the receipt verification step
PaymentBadRequest The payment request cannot be processed. Check the request values and call conditions
PaymentInvalidParameter The payment request parameters are invalid. Check request field values such as AxylReceipt
PaymentResourceNotFound The payment information to save cannot be found. Check the receipt and transaction information
PaymentUnauthorized You do not have permission for the payment request. Check the app and authentication status
VerifyError An error occurred during payment verification. Check the receipt and the market payment status
UnknownOutcome A domain-specific result that the SDK does not recognize. Treat it as a failure and log the result code
Failure Common Failure. See Common error handling. Handle it according to the common error handling guidelines

Next steps

Proceed to Step 4. Verify receipts.