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.
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
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.
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
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.
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
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.