Skip to content

Step 2. Get the product list

Retrieve in real time the in-app product information registered in Google Play, such as product names, prices, and currencies. Use this to show users the latest list of products currently available for sale and accurate local prices.

Follow the steps below to retrieve product information.

1. Get the product ID list

Retrieve the list of product IDs registered in the Hive Axyl server. This list includes the unique identifiers (Product IDs) of the products registered in the Hive Console.

Method

ListStoreProductIdsAsync

To implement Product ID list retrieval, call ListStoreProductIdsAsync() provided by the Hive Axyl SDK. It returns, by market, the list of unique identifiers (Product IDs) of the in-app products that the app is selling.

Call parameters

Field name Type Required Description
request StoreRequest Required Request data that contains the market to query and the app 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.

StoreRequest

Field name Type Required Description
AppVersion string Optional Pass the app's current build version as a string. Example: 1.0.0
Country string Required Pass the user's country code (ISO 3166-1 two-letter). Used to filter products. Example: KR
Language string Required Pass the language code (ISO 639-1 two-letter) to use for localizing product names and other text. Example: ko
ProviderId StoreRequestProviderId (enum) Required Market identifier. Because this request is only for retrieving Google Play products, specify only Google.

Call example

Check the success result of PaymentsListStoreProductIdsResult 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>();

// Create a request object that contains the market to query and the app information.
var request = new StoreRequest
{
    AppVersion = "1.0.0",
    Country = "KR",
    Language = "ko",
    ProviderId = StoreRequestProviderId.Google
};

PaymentsListStoreProductIdsResult result = await payments.ListStoreProductIdsAsync(request);

switch (result)
{
    case PaymentsListStoreProductIdsResult.Success success:
        // Check the Product ID list for each market.
        foreach (var store in success.Data.Stores)
        {
            Debug.Log($"{store.ProviderId}: {store.Products.Count} consumable(s), {store.ProductSubscriptions.Count} subscription(s)");
        }
        break;

    case PaymentsListStoreProductIdsResult.PaymentBadRequest:
    case PaymentsListStoreProductIdsResult.PaymentInvalidParameter:
        Debug.LogError("Check the request fields and required values.");
        break;

    // Handle common Failure
    case PaymentsListStoreProductIdsResult.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 (StoreResponseData) of PaymentsListStoreProductIdsResult.Success.

Field name Type Required Description
Data.Stores IReadOnlyList<StoreProduct> Required List of product ID information for each market. Empty if there are no results.
Data.Meta string Optional Response meta information. null if there is none.

StoreProduct

Field name Type Required Description
AppId string Optional App ID. Example: com.game
Products IReadOnlyList<string> Optional List of Product IDs of consumable products.
ProductSubscriptions IReadOnlyList<string> Optional List of Product IDs of subscription products.
ProviderId StoreProductProviderId (enum) Required Market identifier. This response returns Google.

Response example

// Example of success.Data in the Success branch
// success.Data.Stores[0].ProviderId = StoreProductProviderId.Google
// success.Data.Stores[0].AppId = "com.game"
// success.Data.Stores[0].Products = ["com.game.product1", "com.game.product2"]
// success.Data.Stores[0].ProductSubscriptions = ["com.game.subscription1", "com.game.subscription2"]

Response status

The table below summarizes the success result of PaymentsListStoreProductIdsResult 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 Product ID list was retrieved successfully. Data contains the list of product IDs for each market. Proceed to retrieve store product information with the received Product IDs
PaymentBadRequest The request cannot be processed. Check the request values and call conditions
PaymentInvalidParameter Invalid parameter. Check the request fields and retry
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

2. Get Google Play product details (plugin)

Based on the product ID list, call QueryProductDetailsAsync() of the Google payment plugin to retrieve product details directly from the Google Play BillingClient. Use the results to build the in-app store screen or as input data for the subsequent Hive Axyl server product retrieval (FetchGoogleProductsAsync()).

BillingClient connection required

Before you call QueryProductDetailsAsync(), you must connect the BillingClient with StartConnectionAsync(). For how to connect, see Step 1. Set up the integration.

Method

QueryProductDetailsAsync

To retrieve the details of products registered in Google Play, call QueryProductDetailsAsync() provided by the Google payment plugin. Specify ProductType.Inapp for consumable products and ProductType.Subs for subscription products.

Call parameters

Field name Type Required Description
productIds IReadOnlyList<string> Required List of product IDs to retrieve. Pass the Product IDs retrieved in step 1.
productType ProductType Required Product type. Specify Inapp (consumable product) or Subs (subscription product).
ct CancellationToken Optional Cancellation token.

Call example

Check the success result of GooglePlayBillingServiceQueryProductDetailsResult 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. Because the plugin supports the partial-failure model of Google Play Billing Library 9, products that were retrieved successfully (ProductDetailsList) and products that could not be retrieved (UnfetchedProductList) can be returned together.

using System.Collections.Generic;
using System.Linq;
using Hive.Axyl.Core;
using Hive.Axyl.Payments.Addon.Google;
using System.Threading;

// Retrieve the Google payment plugin (must be registered with builder.AddPlayBilling() during initialization)
if (!HiveCore.TryResolve<IGooglePlayBillingPlugin>(out var googlePlugin))
{
    Debug.LogError("The Google Play Billing plugin is not registered.");
    return;
}

// List of consumable product IDs retrieved in step 1
var productIds = new List<string> { "com.game.product1", "com.game.product2" };

GooglePlayBillingServiceQueryProductDetailsResult result =
    await googlePlugin.QueryProductDetailsAsync(productIds, ProductType.Inapp, CancellationToken.None);

switch (result)
{
    case GooglePlayBillingServiceQueryProductDetailsResult.Success success:
        // Products retrieved successfully
        foreach (GoogleProductDetails detail in success.Data.ProductDetailsList)
        {
            Debug.Log($"Product: {detail.ProductId}, name: {detail.Name}, title: {detail.Title}");

            // Price information of consumable products
            if (detail.OneTimePurchaseOfferDetails != null)
            {
                Debug.Log($"  Price: {detail.OneTimePurchaseOfferDetails.FormattedPrice}");
            }

            // Offer information of subscription products
            if (detail.SubscriptionOfferDetails != null)
            {
                foreach (var offer in detail.SubscriptionOfferDetails)
                {
                    Debug.Log($"  Subscription offer token: {offer.OfferToken}");
                }
            }
        }

        // Products that could not be retrieved (partial failure)
        if (success.Data.UnfetchedProductList.Count > 0)
        {
            var unfetchedIds = success.Data.UnfetchedProductList.Select(p => p.ProductId);
            Debug.LogWarning($"Products not retrieved: {string.Join(", ", unfetchedIds)}");
        }
        break;

    case GooglePlayBillingServiceQueryProductDetailsResult.UnknownOutcome:
        Debug.LogWarning("Unknown query result.");
        break;

    case GooglePlayBillingServiceQueryProductDetailsResult.Failure failure:
        Debug.LogError($"Failed to get product details: {failure}");
        break;

    default:
        Debug.LogWarning($"Unhandled result: {result.GetType().Name}");
        break;
}

Response data

On success, the result is contained in Data (QueryProductDetailsResponse) of GooglePlayBillingServiceQueryProductDetailsResult.Success.

Field name Type Description
Data.ProductDetailsList IReadOnlyList<GoogleProductDetails> List of details of the products retrieved successfully. Empty if no products were retrieved.
Data.UnfetchedProductList IReadOnlyList<UnfetchedProduct> List of products that could not be retrieved. Empty if there is no partial failure.

GoogleProductDetails

Field name Type Description
ProductId string Unique identifier of the product.
ProductType ProductType Product type. Inapp (consumable product) or Subs (subscription product).
Title string Product title.
Name string Product name.
Description string Product description.
OneTimePurchaseOfferDetails OneTimePurchaseOfferDetails? Offer information for one-time purchases (consumable/non-consumable). null for subscription products.
OneTimePurchaseOfferDetailsList IReadOnlyList<OneTimePurchaseOfferDetails> List of multiple one-time offers. Empty if there is only a single one-time offer or if the product is a subscription product.
SubscriptionOfferDetails IReadOnlyList<SubscriptionOfferDetails> List of subscription offer information. Empty for consumable products.

Response status

The returned object GooglePlayBillingServiceQueryProductDetailsResult branches into one of the cases below. Handle it with a switch statement.

Response case Description App client handling
Success Product details were retrieved successfully. ProductDetailsList contains the product information, and UnfetchedProductList contains the products that failed. Build the in-app store screen with the retrieved products or use them as input data for FetchGoogleProductsAsync()
UnknownOutcome Unknown result Retry or show an error message
Failure Common Failure. See Common error handling. Handle it according to the common error handling guidelines

3. Optional: Get product details from the Hive Axyl server

Based on the product ID list, retrieve through the Hive Axyl server the details of the products registered in Google Play, such as product names, localized prices, currencies, and product descriptions, in the service standard format (Single Standard). Use the product information retrieved with QueryProductDetailsAsync() of the Google payment plugin in step 2 above as input.

Method

FetchGoogleProductsAsync

To implement Google Play product list retrieval, call FetchGoogleProductsAsync() provided by the Hive Axyl SDK. When you pass the product information retrieved with the Google payment plugin (QueryProductDetailsAsync()) in step 2 above, it returns a product list refined into the service standard format (Single Standard).

Call parameters

Field name Type Required Description
request ProductGoogle Required Request data that contains the product information retrieved from Google Play.
context ApiCallContext Optional Per-call settings object that specifies the idempotency key, cancellation token, and request policy. If omitted, the default values are used.

ProductGoogle

Field name Type Required Description
AppVersion string Optional Pass the app's current build version as a string. Example: 1.0.0
Country string Required Pass the user's country code (ISO 3166-1 two-letter). Example: KR
Currency string Required Pass the payment currency (ISO 4217 three-letter). Example: KRW
Language string Required Pass the language code (ISO 639-1 two-letter) to use for localization. Example: ko
ProductType string Required Product type. subscription (subscription), consumable (consumable)
Products IReadOnlyList<ProductDetails> Required List of product information retrieved from the store. Must contain at least one item.
ProviderId ProductGoogleProviderId (enum) Required Market identifier. Because this request is only for Google Play, specify only Google.

ProductDetails

Field name Type Required Description
Description string Optional Product description.
OneTimePurchaseOfferDetails ProductGoogleOfferDetails Optional One-time purchase offer. If a value is present, the price information of this offer is used.
OneTimePurchaseOfferDetailsList IReadOnlyList<ProductGoogleOfferDetails> Optional List of one-time purchase offers. Used when you pass discount offers and the base offer together.
ProductId string Required Unique identifier (PID) of the product. If it is empty, the request is rejected. Example: com.game.product1
ProductType int Optional Product type. 1 is a consumable product, and 2 is a subscription product.
SubscriptionOfferDetails IReadOnlyList<ProductGoogleSubscriptionOffer> Optional List of subscription offers. The price information is contained in the PricingPhases of each offer.
Title string Optional Product title. Example: 1000 Gold

ProductGoogleOfferDetails

Field name Type Required Description
FormattedPrice string Optional Price formatted by Google Play for display.
OfferId string Optional Offer ID. null for the base offer.
OfferToken string Optional Offer token. Use the new value each time you retrieve the product again.
PreorderDetails string Optional Raw JSON string of the preorder offer information. If the plugin object exists, use the result of ToJson().
PriceAmount decimal Optional Price in currency units. If you also pass PriceAmountMicros, the micro-unit value is used.
PriceAmountMicros long Optional Price in micro-units. If you pass this value, it is used as the price basis.
PriceCurrencyCode string Optional Currency code. Uses the ISO 4217 three-letter format.
RentalDetails string Optional Raw JSON string of the rental offer information. If the plugin object exists, use the result of ToJson().

When you build the input JSON yourself, use keys that match the C# property names, such as productId and oneTimePurchaseOfferDetails. Pass the product and offer information retrieved from Google Play without changing it.

ProductGoogleSubscriptionOffer

Field name Type Required Description
BasePlanId string Optional Base plan ID.
OfferId string Optional Subscription offer ID. null for the base offer.
OfferTags IReadOnlyList<string> Optional Subscription offer tags.
OfferToken string Optional Subscription offer token. Use the new value each time you retrieve the product again.
PricingPhases IReadOnlyList<ProductGooglePricingPhase> Optional List of subscription pricing phases.

ProductGooglePricingPhase

Field name Type Required Description
BillingCycleCount int Optional Number of billing cycles. 0 for an infinitely recurring phase.
BillingPeriod string Optional Billing period in the ISO 8601 duration format.
FormattedPrice string Optional Price formatted by Google Play for display.
PriceAmount decimal Optional Price in currency units. If you also pass PriceAmountMicros, the micro-unit value is used.
PriceAmountMicros long Optional Price in micro-units. If you pass this value, it is used as the price basis.
PriceCurrencyCode string Optional Currency code. Uses the ISO 4217 three-letter format.
RecurrenceMode int Optional Recurrence mode. 1 recurs infinitely, 2 recurs a finite number of times, and 3 is charged only once.

Plugin result mapping

GoogleProductDetails and ProductDetails have similar names but are different C# types, so you cannot assign one directly to the other. Convert the ProductDetailsList from step 2 with the mapper below and set the result in Products of the step 3 request. The mapper maps all offers and pricing phases, and converts only PreorderDetails and RentalDetails of one-time offers into raw JSON strings.

using System.Collections.Generic;
using System.Linq;
using Hive.Axyl.Payments;
using Google = Hive.Axyl.Payments.Addon.Google;

internal static class GoogleProductMapper
{
    public static IReadOnlyList<ProductDetails> Map(IReadOnlyList<Google.GoogleProductDetails> sources)
    {
        return sources.Select(Map).ToArray();
    }

    private static ProductDetails Map(Google.GoogleProductDetails source)
    {
        return new ProductDetails
        {
            Description = source.Description,
            OneTimePurchaseOfferDetails = source.OneTimePurchaseOfferDetails is { } offer
                ? Map(offer)
                : null,
            OneTimePurchaseOfferDetailsList = source.OneTimePurchaseOfferDetailsList.Select(Map).ToArray(),
            ProductId = source.ProductId,
            ProductType = (int)source.ProductType,
            SubscriptionOfferDetails = source.SubscriptionOfferDetails.Select(Map).ToArray(),
            Title = source.Title
        };
    }

    private static ProductGoogleOfferDetails Map(Google.OneTimePurchaseOfferDetails source)
    {
        return new ProductGoogleOfferDetails
        {
            FormattedPrice = source.FormattedPrice,
            OfferId = source.OfferId,
            OfferToken = source.OfferToken,
            PreorderDetails = source.PreorderDetails?.ToJson(),
            PriceAmount = source.PriceAmount,
            PriceCurrencyCode = source.PriceCurrencyCode,
            RentalDetails = source.RentalDetails?.ToJson()
        };
    }

    private static ProductGoogleSubscriptionOffer Map(Google.SubscriptionOfferDetails source)
    {
        return new ProductGoogleSubscriptionOffer
        {
            BasePlanId = source.BasePlanId,
            OfferId = source.OfferId,
            OfferTags = source.OfferTags.ToArray(),
            OfferToken = source.OfferToken,
            PricingPhases = source.PricingPhases.Select(phase => new ProductGooglePricingPhase
            {
                BillingCycleCount = phase.BillingCycleCount,
                BillingPeriod = phase.BillingPeriod,
                FormattedPrice = phase.FormattedPrice,
                PriceAmount = phase.PriceAmount,
                PriceCurrencyCode = phase.PriceCurrencyCode,
                RecurrenceMode = (int)phase.RecurrenceMode
            }).ToArray()
        };
    }
}

Call example

Check the success result of PaymentsFetchGoogleProductsResult 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>();

// Convert ProductDetailsList from the QueryProductDetailsAsync() success response and put it in the request.
var request = new ProductGoogle
{
    AppVersion = "1.0.0",
    Country = "KR",
    Currency = "KRW",
    Language = "ko",
    ProductType = "consumable",
    ProviderId = ProductGoogleProviderId.Google,
    Products = GoogleProductMapper.Map(productDetailsList)
};

PaymentsFetchGoogleProductsResult result = await payments.FetchGoogleProductsAsync(request);

switch (result)
{
    case PaymentsFetchGoogleProductsResult.Success success:
        // Build the in-app store screen with the product list refined into the standard format.
        foreach (var product in success.Data.Products)
        {
            Debug.Log($"{product.ProductId}: {product.Title} ({product.DisplayPrice})");
        }
        break;

    // Handle request validation failure results (Outcome)
    case PaymentsFetchGoogleProductsResult.PaymentBadRequest:
    case PaymentsFetchGoogleProductsResult.PaymentInvalidParameter:
        // Check the request fields and required values.
        break;

    // Handle common Failure
    case PaymentsFetchGoogleProductsResult.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 (ProductResponseData) of PaymentsFetchGoogleProductsResult.Success.

Field name Type Required Description
Data.AppVersion string Optional App version.
Data.Country string Optional Country information.
Data.Currency string Optional Currency.
Data.Language string Required Language.
Data.Products IReadOnlyList<ProductProducts> Optional List of product information refined into the standard format.
Data.ProviderId ProductProviderId (enum) Required Market identifier. This response returns Google.
Data.Meta string Optional Response meta information. null if there is none.

ProductProducts

Field name Type Required Description
Currency string Optional Currency. Example: KRW
Description string Optional Product description.
DisplayOriginalPrice string Optional Price before discount, for display. Example: ₩1,100
DisplayPrice string Optional Price for display. Example: 1,200 KRW
Offers IReadOnlyList<ProductOffer> Optional List of one-time or subscription offers. null if there are no offers.
OriginalPrice decimal Optional Price before discount. Example: 1100 or 11.00
Price decimal Optional Price. Example: 1200 or 9.99
ProductId string Optional Unique identifier (PID) of the product.
ProductType string Optional Product type. subscription (subscription), consumable (consumable)
Title string Optional Product title. Example: 1000 Gold

ProductOffer

Field name Type Required Description
BasePlanId string Optional Base plan ID of the subscription product. null for one-time purchase offers.
Currency string Optional Currency code. Uses the ISO 4217 three-letter format.
DisplayPrice string Optional Price to display on the screen.
OfferToken string Optional Offer token. The value sent in the request is returned as is. Google Play issues a new one each time the product is retrieved, so do not cache it; use the value from a new retrieval.
Price decimal Optional Price in currency units.

Response example

// Example of success.Data in the Success branch
// success.Data.ProviderId = ProductProviderId.Google
// success.Data.Currency = "KRW"
// success.Data.Products[0].ProductId = "com.game.product1"
// success.Data.Products[0].Title = "1000 Gold"
// success.Data.Products[0].Price = 1200
// success.Data.Products[0].DisplayPrice = "1,200 KRW"
// success.Data.Products[0].ProductType = "consumable"

Response status

The table below summarizes the success result of PaymentsFetchGoogleProductsResult 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 product list was retrieved successfully. Data contains the product list in the standard format. Build the in-app store screen with the product list
PaymentBadRequest The request cannot be processed. Check the request values and call conditions
PaymentInvalidParameter Invalid parameter. Check the request field values and retry
PaymentResourceNotFound The payment information cannot be found. Check the receipt, order number, and 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

Purchase a product.