Record purchase information
For consumable product payments, record the purchase attempt information before opening the store payment window, and record the receipt and transaction information after the payment is completed. Based on these two records, receipt verification and product delivery are processed as the same purchase. For subscription products, follow the procedure for saving subscription purchase information in Apple payment and Google payment.
Calling the store payment window, receipt verification, and product delivery are handled in the payment flow of each payment provider. The app server requests receipt verification by calling the Hive Axyl Server API with the receipt that the app client passed. For the roles of the payment confirmation request and delivery result recording, see Confirm payments and record delivery results.
Processing order
1. Save purchase attempt information before payment
CreatePrePurchaseAsync() saves the product the user selected, the expected payment amount, the currency, the country and language, the app server, and IapPayload as a pre-purchase tracking record. Call it right before opening the store payment window.
CreatePrePurchaseAsync
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. |
The PrePurchase request data is as follows.
| Field name | Type | Required | Description |
|---|---|---|---|
Country | string | Required | Country code (ISO 3166-1, two letters) |
Currency | string | Required | Payment currency (ISO 4217, three letters) |
Language | string | Required | Language code (ISO 639-1, two letters) |
Price | decimal | Required | The expected payment amount. Depending on the currency, it may include decimals. |
ProductId | string | Required | In-app product ID registered in the market |
ProviderId | PrePurchaseProviderId | Required | Market and payment method identifier (enum). Members: Apple (Apple App Store), Google (Google Play), Steam (Steam), Pg (web payment PG: PortOne, MyCard, and Xsolla) |
ServerId | string | Optional | Server ID that you check on the App Server tab after you register the app server in App Info > App Server by referring to Hive Console app server |
AccountUuid | string | Optional | The UUIDv5 value generated from the logged-in user's playerId. Pass it before opening the store payment window. See Generate an AccountUuid. |
IapPayload | string | Optional | 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. |
Call example
Check the success result of PaymentsCreatePrePurchaseResult and the domain-specific results (Outcome) defined by this method in the following example 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 selected the payment button
var request = new PrePurchase
{
Country = "KR",
Currency = "KRW",
Language = "ko",
Price = 1200.0m,
ProductId = "com.game.item.gold_100",
ProviderId = PrePurchaseProviderId.Google,
ServerId = "server01",
RequestDate = DateTimeOffset.UtcNow,
AccountUuid = accountUuid // Value that identifies the paying user
};
PaymentsCreatePrePurchaseResult result = await payments.CreatePrePurchaseAsync(request);
switch (result)
{
case PaymentsCreatePrePurchaseResult.Success success:
// Pre-save succeeded. No data is returned. Proceed to open the store payment window.
break;
// Problem with request values — check 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-save succeeded | Proceed to open the store payment window |
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 know. | Treat it as a failure and record the result code |
Failure | A common Failure. See Common error handling. | Handle it according to the common error handling criteria |
2. Save store payment results
RecordStorePurchaseAsync() saves the payment result, such as the receipt and transaction ID, right after the store payment is completed. Because it records the original fact of payment before receipt verification, you can use it as the basis for verification, product delivery, customer support, and settlement even if a later step is interrupted.
RecordStorePurchaseAsync
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. |
The PurchaseRequest request data is as follows.
| Field name | Type | Required | Description |
|---|---|---|---|
AccountUuid | string | Optional | The UUIDv5 value generated from the logged-in user's playerId. Pass it to the purchase result record after the store payment is finished. If you passed it at the payment start step, use the same value. See Generate an AccountUuid. |
AxylReceipt | string | Required | The market receipt. For Apple, pass the JWS; for Google, the purchase token (purchaseToken); and for Steam and PG, the sealed receipt issued by the Hive Axyl server. |
Country | string | Required | The country code (ISO 3166-1, two letters). |
Currency | string | Optional | The payment currency (ISO 4217, three letters). Paired with Price, it is used to compare against the value 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 | Payload (JSON string) that the developer attached to the market payment. It is passed as is to app server verification and the purchase completion callback. |
Language | string | Required | The language code (ISO 639-1, two letters). |
OrderId | string | Optional | The Hive Axyl internal order number. Used for Steam/PG. |
Price | decimal | Optional | The payment amount. Depending on the currency, it may include decimals. If you send it, the server compares it with the recorded amount; if you omit it, no comparison is made. For Apple, Steam, and PG payments, the request is rejected if the amounts differ; for Google payments, the amount is only recorded. The amount kept in the payment history is the value the server confirmed. |
ProductId | string | Required | The in-app product ID registered in the market. |
ProjectInfo | string | Optional | A free-form JSON string for each app. 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 | The market and payment method identifier. |
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 referring to Hive Console app server. |
StoreTransactionId | string | Optional | The store transaction ID on the market side. |
Call example
Check the success result of PaymentsRecordStorePurchaseResult and the domain-specific results (Outcome) defined by this method in the following example 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>();
// Right after the store payment is completed, create the request object with the receipt issued by the market
var request = new PurchaseRequest
{
AxylReceipt = purchaseToken, // Google: purchase token
ProviderId = PurchaseRequestProviderId.Google,
ProductId = "com.game.item.gold_100",
Price = 1200.0m,
Currency = "KRW",
Country = "KR",
Language = "ko",
Quantity = 1,
StoreTransactionId = "GPA.3389-9543-8198-17604",
RequestType = 1, // 1: New purchase
RequestDate = DateTimeOffset.UtcNow,
ServerId = "server01",
AccountUuid = accountUuid // Value that identifies the paying user
};
PaymentsRecordStorePurchaseResult result = await payments.RecordStorePurchaseAsync(request);
switch (result)
{
case PaymentsRecordStorePurchaseResult.Success success:
// Save succeeded. 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, Data (PurchaseResponseData) of PaymentsRecordStorePurchaseResult.Success contains the result.
| 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 successfully | Pass the receipt 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 the request fields, such as AxylReceipt |
PaymentResourceNotFound | The payment information to save cannot be found. | Check the receipt and transaction information |
PaymentUnauthorized | There is no 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 know. | Treat it as a failure and record the result code |
Failure | A common Failure. See Common error handling. | Handle it according to the common error handling criteria |
Next steps
To implement the purchase flow, see Apple App Store payment, Google Play payment, Steam payment, and PG payment.