Step 3. Purchase a product
Create the payment page URL for the product the user selected, and proceed with the payment in an external browser. After the user returns to the app, retrieve undelivered orders to check the payment result and receipt data.
Follow the steps below to purchase a PG product.
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 payment page 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 | Country code (two-letter ISO 3166-1 code) |
Currency | string | Required | Payment currency (three-letter ISO 4217 code) |
Language | string | Required | Language code (two-letter ISO 639-1 code) |
Price | decimal | Required | The expected payment amount. It may include decimals depending on the currency. |
ProductId | string | Required | In-app product ID registered in the market |
ProviderId | PrePurchaseProviderId | Required | The market and payment method identifier (enum). Because this request is for PG payments only, specify only Pg. |
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.Pg,
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 create the payment page URL.
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 create the payment page URL |
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. Create the payment page URL
Create a payment page URL that the user can access based on the order information. When you open the created URL in an external browser, the payment method selection page provided by Hive Axyl appears.
On this page, the user selects the actual PG payment method and proceeds with the payment.
CreatePaymentUrlAsync
To implement payment page URL creation, call CreatePaymentUrlAsync() provided by the Hive Axyl SDK. It creates and returns a payment page URL that the user can access based on the order information.
Call parameters
| Field name | Type | Required | Description |
|---|---|---|---|
| request | OrderRequest | Required | The order (payment page creation) request data. |
| context | ApiCallContext | Optional | Per-call settings object that specifies the idempotency key, cancellation token, and request policy. If omitted, the default values are used. |
OrderRequest
| Field name | Type | Required | Description |
|---|---|---|---|
AppVersion | string | Optional | The app version. |
Country | string | Required | The country code (two-letter ISO 3166-1 code). |
Language | string | Required | The language code (two-letter ISO 639-1 code). It is used to display payment method names in multiple languages. |
Os | OrderRequestOs (enum) | Required | The OS on which the app client is running. Specify the value that matches the build target among Windows, Macos, Android, and Ios. |
ProductId | string | Required | The in-app product PID registered in the market. |
Quantity | int | Required | The purchase quantity. You can specify a value from 1 to 999. |
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. |
ProviderId | OrderRequestProviderId | Required | The payment provider. Because this request is for PG payments only, specify only Pg. |
CustomPrice | string | Optional | A custom payment amount. Use it to request a payment with an amount that the app server specifies instead of the amount set for the product PID. You must use it together with FixedCurrency and GameServerPriceVerifyKey. |
FixedCurrency | string | Optional | The currency to display for the payment method (three-letter ISO 4217 code). Required when you use custom amount (CustomPrice) payments. |
GameServerPriceVerifyKey | string | Optional | The key for verifying the payment amount on the app server. The app server issues it to prevent tampering with the amount in custom amount payments, and it is required when you use CustomPrice. |
IapPayload | string | Optional | Developer-defined metadata to pass to the app server (JSON string). It is passed as is to the app server callback after the purchase is completed. |
Call example
Check the success result of PaymentsCreatePaymentUrlResult 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 OrderRequest
{
AppVersion = "1.0.0",
Country = "KR",
Language = "ko",
Os = OrderRequestOs.Android,
ProductId = "com.example.game.gold100",
Quantity = 1,
ServerId = "server01",
ProviderId = OrderRequestProviderId.Pg
};
PaymentsCreatePaymentUrlResult result = await payments.CreatePaymentUrlAsync(request);
switch (result)
{
case PaymentsCreatePaymentUrlResult.Success success:
// Payment page URL created. Open PayUrl to display the payment page
Debug.Log($"payUrl: {success.Data.PayUrl}");
break;
case PaymentsCreatePaymentUrlResult.PaymentBadRequest:
case PaymentsCreatePaymentUrlResult.PaymentInvalidParameter:
Debug.LogError("Check the request fields and required values.");
break;
// Handle common Failure
case PaymentsCreatePaymentUrlResult.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 (OrderPayUrlResponseData) of PaymentsCreatePaymentUrlResult.Success.
| Field name | Type | Required | Description |
|---|---|---|---|
Data.PayUrl | string | Optional | The payment page URL. |
Data.CreatedAt | DateTimeOffset | Optional | The creation date and time. |
Data.Meta | string? | Optional | Response meta information. |
Response example
Response status
The following table summarizes the success result of PaymentsCreatePaymentUrlResult 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 page URL was created. | Open Data.PayUrl to display the payment page to the user |
PaymentBadRequest | The request cannot be processed. | Check the request values and call conditions |
PaymentInvalidParameter | Invalid parameter. | Check the request fields and send the request again |
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 |
4. Proceed with the payment
Open the created payment page URL in an external browser so that the user can pay. On this page, the user selects a payment method, such as a credit card or micropayment, and then completes the PG payment.
Because PG payments proceed in an external browser, the app is not notified right away that the payment is complete, unlike store payments. When the user finishes the payment and returns to the app, display a UI that indicates the purchase is complete, retrieve undelivered orders, and then request receipt verification from the app server to check the payment result.
5. Optional: Save payment result data
After the user finishes the payment and returns to the app, save the receipt and transaction information that you checked with Get completed payment information. Because this keeps a record of the payment before receipt verification, you can use it for verification, product delivery, customer support, and settlement even if later processing is interrupted. For the full flow, see Record purchase information. This step is optional.
RecordStorePurchaseAsync
RecordStorePurchaseAsync() saves the payment result, such as the receipt and transaction ID, to the Hive Axyl server.
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 | The sealed receipt issued by the Hive Axyl server. Pass the AxylReceipt returned by Get completed payment information exactly as is. |
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. |
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 PG 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 PG 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 PG payment results, specify Pg. |
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 PG 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>();
// After checking the payment result with the undelivered order query, create the request object with the retrieved receipt
var request = new PurchaseRequest
{
AxylReceipt = axylReceipt, // Sealed receipt returned by the undelivered order query
ProviderId = PurchaseRequestProviderId.Pg,
ProductId = "com.game.item.gold_100",
Price = 1200.0m,
Currency = "KRW",
Country = "KR",
Language = "ko",
Quantity = 1,
StoreTransactionId = "imp_448280090638",
RequestType = 1, // 1: new purchase
RequestDate = DateTimeOffset.UtcNow,
ServerId = "server01"
};
PaymentsRecordStorePurchaseResult result = await payments.RecordStorePurchaseAsync(request);
switch (result)
{
case PaymentsRecordStorePurchaseResult.Success success:
// 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 fields 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.