Skip to content

Apple StoreKit payment Add-on

This Add-on wraps the Product, Transaction, and AppStore APIs of StoreKit 2, Apple's in-app purchase feature, on iOS and macOS. It performs product retrieval, purchases, transaction retrieval, transaction finishing, and App Store sync, and passes the product and transaction fields that StoreKit 2 returns as is, without processing them. It also does not reduce the signature verification result of a transaction to two values, success and failure; it passes the verification status together with the error cause.

The transaction received from a purchase is recorded on the Hive Axyl server through the Payments module, and then the app server verifies the receipt with the Hive Axyl Server API. Both steps use AppleTransaction.JwsRepresentation as the receipt value. For how to set the value, see AxylReceipt rules, and for the verification flow, see Receipt verification.

Module information

  • Package: com.com2usplatform.hiveaxyl.payments.addon.apple
  • Interface: IAppleStoreKitPlugin
  • Namespace: Hive.Axyl.Payments.Addon.Apple
  • Registration method: AddStoreKit()
  • Supported platforms: iOS, macOS
  • Minimum requirements: iOS 17+, macOS 15+, Unity 6000.0+

Registration and retrieval

Register the Add-on together with the Payments module in the registration step of HiveBootstrap.Initialize, and then retrieve it with HiveCore.TryResolve<T>().

using Hive.Axyl.Core;
using Hive.Axyl.Core.Unity;   // HiveBootstrap
using Hive.Axyl.Payments;
using Hive.Axyl.Payments.Addon.Apple;

HiveBootstrap.Initialize(config, builder =>
{
    builder.AddPayments()
           .AddStoreKit();
});

if (HiveCore.TryResolve<IAppleStoreKitPlugin>(out var storeKit))
{
    // Code that runs only in iOS and macOS builds
}
Not registered in the Unity Editor

This Add-on is registered only in players built for iOS or macOS. It is not registered in the Unity Editor even when the platform matches, so using HiveCore.Resolve<T>() throws RegistrationNotFoundException. Always check with TryResolve<T>() before using it.

For the package installation and registration procedure, see Install the Hive Axyl Apple payment plugin.

Method summary

Every method takes CancellationToken ct = default as its last parameter and throws ArgumentNullException if request is null. For the calling conventions, see Call context.

This Add-on provides the following methods.

Methods

GetProductsAsync

Calls Product.products(for:) to retrieve the products that match the requested product IDs. If no products match, the call does not fail; it succeeds with an empty list.

Task<AppleStoreKitServiceGetProductsResult> GetProductsAsync(GetProductsRequest request, CancellationToken ct = default)

Result cases — AppleStoreKitServiceGetProductsResult

Result case Wire code Description
Success — Retrieved successfully. If no products match, Data.Products is empty.
UnknownOutcome UNKNOWN A new result that this SDK version does not recognize.
Failure FAILURE The call could not be completed. Check the cause with the HiveError in Problem.

Exceptions

  • ArgumentNullException: When request is null
  • ArgumentException: When request.ProductIds is null or empty

For the implementation procedure, see Get Apple App Store product information.

PurchaseAsync

Starts a purchase with Product.purchase(options:). On success, it returns the transaction created by the purchase, regardless of whether its signature was verified. If the app user closes the payment screen, the result branches to UserCanceled; if the purchase is pending because of Ask to Buy, which requires parental approval, or Strong Customer Authentication (SCA), the result branches to Pending.

Task<AppleStoreKitServicePurchaseResult> PurchaseAsync(PurchaseRequest request, CancellationToken ct = default)

Result cases — AppleStoreKitServicePurchaseResult

Result case Wire code Description
Success — Received the purchase transaction. Check Data.Transaction.VerificationStatus before delivering the product.
UserCanceled user_canceled The app user closed the StoreKit payment screen or login dialog.
Pending pending The purchase is pending because of Ask to Buy or SCA. The transaction is delivered later through the TransactionUpdated event of the subscription started with StartTransactionObserverAsync().
UnknownOutcome UNKNOWN A new result that this SDK version does not recognize.
Failure FAILURE The call could not be completed. Check the cause with the HiveError in Problem.

UserCanceled implements IUserCanceledOutcome.

User cancellation and code cancellation are different

If the app user closes the payment screen, the result is UserCanceled. In contrast, if code cancels the call through CancellationToken, the result is a Failure that contains the Cancelled code.

Call example

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

var request = new PurchaseRequest
{
    ProductId = "{productId}",
    Options = new PurchaseOptions
    {
        AppAccountToken = accountUuid,   // AccountUuid created from the Player ID
    },
};

var result = await storeKit.PurchaseAsync(request);

switch (result)
{
    case AppleStoreKitServicePurchaseResult.Success success:
        AppleTransaction transaction = success.Data.Transaction;
        AppleVerificationStatus status = transaction.VerificationStatus;   // Check before delivery
        string receipt = transaction.JwsRepresentation;                     // AxylReceipt of the Payments module
        break;

    case AppleStoreKitServicePurchaseResult.UserCanceled:
        // The app user closed the payment screen.
        break;

    case AppleStoreKitServicePurchaseResult.Pending:
        // The purchase is pending. The transaction is delivered through the TransactionUpdated event of the subscription started with StartTransactionObserverAsync.
        break;

    case AppleStoreKitServicePurchaseResult.Failure failure:
        HiveError error = failure.Problem;
        break;

    default:
        // Unhandled results and UnknownOutcome
        break;
}

For the purchased transaction, use JwsRepresentation as AxylReceipt and save the payment result with RecordStorePurchaseAsync().

The Payments module also has a PurchaseRequest

The Hive.Axyl.Payments namespace also has a PurchaseRequest with the same name. If you import both namespaces in one file and use PurchaseRequest as is, the type is ambiguous and a compile error occurs. To use both types in one file, assign an alias to each type, such as using ApplePurchaseRequest = Hive.Axyl.Payments.Addon.Apple.PurchaseRequest; and using PaymentsPurchaseRequest = Hive.Axyl.Payments.PurchaseRequest;.

For the implementation procedure, see Request Apple App Store payment.

StartTransactionObserverAsync

Starts subscribing to the StoreKit Transaction.updates asynchronous stream. Transactions that arrive through the stream are delivered through the TransactionUpdated event.

Task<AppleStoreKitServiceStartTransactionObserverResult> StartTransactionObserverAsync(StartTransactionObserverRequest request, CancellationToken ct = default)

Result cases — AppleStoreKitServiceStartTransactionObserverResult

Result case Wire code Description
Success — Started subscribing.
AlreadyStarted already_started The Transaction.updates subscription is already running.
UnknownOutcome UNKNOWN A new result that this SDK version does not recognize.
Failure FAILURE The call could not be completed. Check the cause with the HiveError in Problem.

StopTransactionObserverAsync

Stops subscribing to Transaction.updates. Calling it when not subscribed is not an error.

Task<AppleStoreKitServiceStopTransactionObserverResult> StopTransactionObserverAsync(StopTransactionObserverRequest request, CancellationToken ct = default)

Result cases — AppleStoreKitServiceStopTransactionObserverResult

Result case Wire code Description
Success — Stopped subscribing.
UnknownOutcome UNKNOWN A new result that this SDK version does not recognize.
Failure FAILURE The call could not be completed. Check the cause with the HiveError in Problem.

GetCurrentEntitlementsAsync

Reads Transaction.currentEntitlements to the end and returns the transactions for which the user currently has entitlement. It returns all transactions as is, regardless of whether their signatures were verified. If the user has no entitlements, the call does not fail; it succeeds with an empty list.

Task<AppleStoreKitServiceGetCurrentEntitlementsResult> GetCurrentEntitlementsAsync(GetCurrentEntitlementsRequest request, CancellationToken ct = default)

Result cases — AppleStoreKitServiceGetCurrentEntitlementsResult

Result case Wire code Description
Success — Retrieved successfully.
UnknownOutcome UNKNOWN A new result that this SDK version does not recognize.
Failure FAILURE The call could not be completed. Check the cause with the HiveError in Problem.

For the implementation procedure, see Get current entitlements.

GetAllTransactionsAsync

Reads Transaction.all to the end and returns the user's full transaction history. The result list can be large.

Task<AppleStoreKitServiceGetAllTransactionsResult> GetAllTransactionsAsync(GetAllTransactionsRequest request, CancellationToken ct = default)

Result cases — AppleStoreKitServiceGetAllTransactionsResult

Result case Wire code Description
Success — Retrieved successfully.
UnknownOutcome UNKNOWN A new result that this SDK version does not recognize.
Failure FAILURE The call could not be completed. Check the cause with the HiveError in Problem.

GetUnfinishedTransactionsAsync

Reads Transaction.unfinished to the end and returns the transactions that have not been finished yet.

Task<AppleStoreKitServiceGetUnfinishedTransactionsResult> GetUnfinishedTransactionsAsync(GetUnfinishedTransactionsRequest request, CancellationToken ct = default)

Result cases — AppleStoreKitServiceGetUnfinishedTransactionsResult

Result case Wire code Description
Success — Retrieved successfully.
UnknownOutcome UNKNOWN A new result that this SDK version does not recognize.
Failure FAILURE The call could not be completed. Check the cause with the HiveError in Problem.

For the implementation procedure, see Get unfinished transactions.

FinishTransactionAsync

Finishes a transaction with Transaction.finish(). Calling it again on a transaction that is already finished is not an error. If no transaction matches the transaction ID, the result branches to TransactionNotFound.

Task<AppleStoreKitServiceFinishTransactionResult> FinishTransactionAsync(FinishTransactionRequest request, CancellationToken ct = default)

Result cases — AppleStoreKitServiceFinishTransactionResult

Result case Wire code Description
Success — Finished the transaction.
TransactionNotFound transaction_not_found No transaction matches the specified transaction ID.
UnknownOutcome UNKNOWN A new result that this SDK version does not recognize.
Failure FAILURE The call could not be completed. Check the cause with the HiveError in Problem.

For the implementation procedure, see Finish Apple transactions.

SyncAsync

Syncs with the App Store through AppStore.sync() to restore purchases. The OS may display an Apple ID reauthentication dialog, and if the app user closes this dialog, the result branches to UserCanceled.

Task<AppleStoreKitServiceSyncResult> SyncAsync(SyncRequest request, CancellationToken ct = default)

Result cases — AppleStoreKitServiceSyncResult

Result case Wire code Description
Success — Synced successfully.
UserCanceled user_canceled The app user closed the StoreKit dialog.
UnknownOutcome UNKNOWN A new result that this SDK version does not recognize.
Failure FAILURE The call could not be completed. Check the cause with the HiveError in Problem.

UserCanceled implements IUserCanceledOutcome.

For the implementation procedure, see Sync purchases.

GetStorefrontAsync

Gets the current App Store storefront with Storefront.current. StoreKit 2 does not require separate initialization settings.

Task<AppleStoreKitServiceGetStorefrontResult> GetStorefrontAsync(GetStorefrontRequest request, CancellationToken ct = default)

Result cases — AppleStoreKitServiceGetStorefrontResult

Result case Wire code Description
Success — Retrieved successfully.
UnknownOutcome UNKNOWN A new result that this SDK version does not recognize.
Failure FAILURE The call could not be completed. Check the cause with the HiveError in Problem.

Events

TransactionUpdated

Raised every time a transaction is delivered through the StoreKit Transaction.updates stream. It is raised only while subscribed through StartTransactionObserverAsync(), and you stop it with StopTransactionObserverAsync(). It is invoked on the engine main thread, so you can use engine APIs inside the handler.

event Action<AppleTransaction> TransactionUpdated
Parameter Type Description
— AppleTransaction The transaction delivered through the stream.

For each delivered transaction, the app checks VerificationStatus and then requests receipt verification from the app server or finishes the transaction with FinishTransactionAsync(). For a transaction whose products you deliver, finish it only after the app server completes receipt verification and product delivery. The results of purchases for which PurchaseAsync() ended with Pending are also delivered through this event.

Data types

AppleProduct

The raw StoreKit Product information.

Field Type Required Description
Id string Required Product.id, the product identifier.
DisplayName string Required Product.displayName.
Description string Required Product.description.
DisplayPrice string Required Product.displayPrice, the localized display price string.
Price decimal Required The price that holds Product.price without loss of precision.
PriceLocale string Required The locale identifier of Product.priceFormatStyle.
ProductType AppleProductType Required Product.type.
Subscription SubscriptionInfo? Optional Product.subscription, which exists only for auto-renewable subscriptions.
IsFamilyShareable bool Required Product.isFamilyShareable.

AppleTransaction

The raw StoreKit Transaction information. All fields are passed without processing, so the app interprets their meaning. Whether a transaction is valid is ultimately determined by the result of the app server verifying JwsRepresentation with the Hive Axyl Server API.

Field Type Required Description
Id ulong Required Transaction.id, the StoreKit transaction identifier. Because it is sensitive information, SDK logs show only its last four digits.
OriginalId ulong Required Transaction.originalID, the ID of the original transaction.
ProductId string Required Transaction.productID.
ProductType AppleProductType Required Transaction.productType.
PurchaseDate DateTimeOffset Required Transaction.purchaseDate. Because it is sensitive information, it is not recorded in SDK logs.
OriginalPurchaseDate DateTimeOffset Required Transaction.originalPurchaseDate. Because it is sensitive information, it is not recorded in SDK logs.
ExpirationDate DateTimeOffset? Optional Transaction.expirationDate, which exists only for auto-renewable subscriptions. It has no value if there is no expiration time.
RevocationDate DateTimeOffset? Optional Transaction.revocationDate, which exists only when the transaction was revoked.
RevocationReason AppleRevocationReason Required Transaction.revocationReason. It is valid only when RevocationDate exists, and it is Unspecified for transactions that were not revoked.
WebOrderLineItemId string? Optional Transaction.webOrderLineItemID, which exists only for auto-renewable subscriptions.
SubscriptionGroupId string? Optional Transaction.subscriptionGroupID, which exists only for auto-renewable subscriptions.
AppAccountToken string? Optional Transaction.appAccountToken, the UUID that the app specified. Because it is sensitive information, it is not recorded in SDK logs.
Quantity int Required Transaction.purchasedQuantity.
TransactionReason AppleTransactionReason Required Transaction.reason.
SignedDate DateTimeOffset Required Transaction.signedDate, the time when Apple signed the JWS.
Environment AppleEnvironment Required Transaction.environment.
OwnershipType AppleOwnershipType Required Transaction.ownershipType.
JwsRepresentation string Required Transaction.jwsRepresentation, the JWS string that Apple signed. Server verification is based on this value; put it without modification in AxylReceipt of Payments module requests and in axylReceipt of the receipt verification request that the app server sends. Because it is sensitive information, it is not recorded in SDK logs.
VerificationStatus AppleVerificationStatus Required The VerificationResult branch of this transaction.
VerificationError AppleVerificationError Required The cause of the signature verification error. It is valid only when VerificationStatus is Unverified.
BundleId string Required Transaction.bundleID, the bundle identifier associated with the transaction.
AppBundleId string? Optional Transaction.appBundleID, which is available on iOS 16 or later.
IsUpgraded bool Required Transaction.isUpgraded, which indicates whether the transaction was replaced by a subscription upgrade.
OfferType AppleOfferType Required Transaction.offerType. It is Unspecified if no offer was applied.
OfferId string? Optional Transaction.offerID.
OfferPaymentModeStringRepresentation string? Optional The raw string of Transaction.offerPaymentModeStringRepresentation.
OfferPeriod string? Optional Transaction.offerPeriod, an ISO 8601 duration string.
Price decimal? Optional The price that holds Transaction.price at the time of purchase without loss of precision.
Currency string? Optional Transaction.currency, the ISO 4217 currency code.
StorefrontCountryCode string Required The countryCode of the storefront at the time of purchase.
DeviceVerification string Required The Base64-encoded Transaction.deviceVerification value.
DeviceVerificationNonce string Required Transaction.deviceVerificationNonce, a UUID.
AppTransactionId string? Optional Transaction.appTransactionID, which is available on iOS 17 or later.
SignedRenewalInfo string? Optional The renewal information JWS of an auto-renewable subscription. Because it is sensitive information, it is not recorded in SDK logs.

FinishTransactionRequest

Field Type Required Description
TransactionId ulong Required The Transaction.id of the transaction to finish. Because it is sensitive information, SDK logs show only its last four digits.

FinishTransactionResponse

No fields.

GetAllTransactionsRequest

No fields. Pass an empty instance.

GetAllTransactionsResponse

Field Type Required Description
Transactions IReadOnlyList<AppleTransaction> Required The list of all of the user's transactions read from Transaction.all. It is an empty list if there is no transaction history.

GetCurrentEntitlementsRequest

No fields. Pass an empty instance.

GetCurrentEntitlementsResponse

Field Type Required Description
Transactions IReadOnlyList<AppleTransaction> Required The list of transactions for which the user currently has entitlement, read from Transaction.currentEntitlements. It is an empty list if the user has no entitlements.

GetProductsRequest

Field Type Required Description
ProductIds IReadOnlyList<string> Required The list of product IDs to retrieve with Product.products(for:). If it is null or empty, ArgumentException is thrown.

GetProductsResponse

Field Type Required Description
Products IReadOnlyList<AppleProduct> Required The list of products that match the requested IDs. The order is not guaranteed, and it is an empty list if no products match.

GetStorefrontRequest

No fields. Pass an empty instance.

GetStorefrontResponse

Field Type Required Description
CountryCode string Required Storefront.countryCode, the ISO 3166-1 alpha-3 region code.
Identifier string Required Storefront.id, the App Store storefront identifier.

GetUnfinishedTransactionsRequest

No fields. Pass an empty instance.

GetUnfinishedTransactionsResponse

Field Type Required Description
Transactions IReadOnlyList<AppleTransaction> Required The list of transactions that have not been finished yet, read from Transaction.unfinished. It is an empty list if there are none.

Offer

The raw StoreKit Product.SubscriptionOffer information.

Field Type Required Description
Id string? Optional SubscriptionOffer.id. It is null for an introductory offer.
Type AppleOfferType Required SubscriptionOffer.type.
Price decimal Required The price that holds SubscriptionOffer.price without loss of precision.
DisplayPrice string Required SubscriptionOffer.displayPrice.
Period Period Required SubscriptionOffer.period.
PeriodCount int Required SubscriptionOffer.periodCount.
PaymentMode ApplePaymentMode Required SubscriptionOffer.paymentMode.

Period

The StoreKit Product.SubscriptionPeriod information.

Field Type Required Description
Value int Required SubscriptionPeriod.value.
Unit ApplePeriodUnit Required SubscriptionPeriod.unit.

PurchaseOptions

The Product.PurchaseOption values passed to Product.purchase(options:). Only appAccountToken and quantity are supported; promotionalOffer and simulatesAskToBuyInSandbox are not supported.

Field Type Required Description
AppAccountToken string? Optional Product.PurchaseOption.appAccountToken, a UUID that the app specifies. You must set the same value as the AccountUuid that you send in Payments module requests and receipt verification requests so that the account identifier comparison in the receipt verification response results in a match. Because it is sensitive information, it is not recorded in SDK logs.
Quantity int? Optional Product.PurchaseOption.quantity, the purchase quantity of a consumable product.

PurchaseRequest

Field Type Required Description
ProductId string Required The ID of the product to purchase.
Options PurchaseOptions? Optional Purchase options.

PurchaseResponse

Field Type Required Description
Transaction AppleTransaction Required The transaction created by the purchase. It is the raw transaction wrapped in StoreKit VerificationResult, so check VerificationStatus before delivery.

StartTransactionObserverRequest

No fields. Pass an empty instance.

StartTransactionObserverResponse

No fields.

StopTransactionObserverRequest

No fields. Pass an empty instance.

StopTransactionObserverResponse

No fields.

SubscriptionInfo

The raw StoreKit Product.SubscriptionInfo information.

Field Type Required Description
SubscriptionGroupId string Required SubscriptionInfo.subscriptionGroupID.
SubscriptionPeriod Period Required SubscriptionInfo.subscriptionPeriod.
IntroductoryOffer Offer? Optional SubscriptionInfo.introductoryOffer, which exists only when an introductory offer is set.
PromotionalOffers IReadOnlyList<Offer> Required The raw list of SubscriptionInfo.promotionalOffers. The app is responsible for selecting offers and generating signatures.

SyncRequest

No fields. Pass an empty instance.

SyncResponse

No fields.

Enums

Specify Add-on enums by their C# member names. The 'Value' column in the tables is the integer used for serialization.

AppleEnvironment

Corresponds to Transaction.environment. It has the Apple prefix so that its name does not conflict with System.Environment.

C# member Value Description
Unspecified 0 The default value, with no environment specified.
Production 1 A transaction processed in the production App Store environment.
Sandbox 2 A transaction processed in the sandbox test environment.
Xcode 3 A transaction created with a local StoreKit configuration file run in Xcode.

AppleOfferType

Corresponds to Transaction.offerType or Product.SubscriptionOffer.type.

C# member Value Description
Unspecified 0 No offer is applied.
Introductory 1 An offer for customers who have never received an introductory offer in the subscription group.
Promotional 2 A signed offer for existing or lapsed subscribers.
Code 3 An offer redeemed with a subscription offer code. Applies only to transactions.
WinBack 4 An offer that encourages previous subscribers to resubscribe. For transactions, it is available on iOS 18 or later.

AppleOwnershipType

Corresponds to Transaction.ownershipType.

C# member Value Description
Unspecified 0 The default value, with no ownership type specified.
Purchased 1 The current user purchased the product.
FamilyShared 2 The user accesses the product through Family Sharing.

ApplePaymentMode

Corresponds to Product.SubscriptionOffer.PaymentMode.

C# member Value Description
Unspecified 0 The default value, with no payment mode specified.
PayAsYouGo 1 Pays for each billing period of the offer.
PayUpFront 2 Pays for the entire offer period at once.
FreeTrial 3 Provides the offer period free of charge.

ApplePeriodUnit

Corresponds to Product.SubscriptionPeriod.Unit.

C# member Value Description
Unspecified 0 The default value, with no period unit specified.
Day 1 The unit is days.
Week 2 The unit is weeks.
Month 3 The unit is months.
Year 4 The unit is years.

AppleProductType

Corresponds to Product.ProductType or Transaction.productType.

C# member Value Description
Unspecified 0 The default value, with no product type specified. StoreKit does not return this value.
Consumable 1 A consumable product that is used up when used and can be purchased again.
NonConsumable 2 A non-consumable product that the user keeps once purchased.
AutoRenewable 3 A subscription that renews automatically until the user cancels it.
NonRenewable 4 A subscription that is used only for a fixed period and does not renew automatically.

AppleRevocationReason

Corresponds to Transaction.revocationReason.

C# member Value Description
Unspecified 0 The transaction was not revoked.
DeveloperIssue 1 The App Store issued a refund because of a problem with the app.
Other 2 The App Store issued a refund for another reason, such as a customer support request.

AppleTransactionReason

Corresponds to Transaction.reason.

C# member Value Description
Unspecified 0 The default value, with no transaction reason specified.
Purchase 1 A transaction that the user purchased directly.
Renewal 2 A transaction in which the subscription was renewed automatically.

AppleVerificationError

The VerificationResult.VerificationError cause that StoreKit reports when VerificationStatus is Unverified. The cause is passed as is, without being changed or reduced.

C# member Value Description
Unspecified 0 There is no verification error. This is the value when VerificationStatus is not Unverified.
InvalidSignature 1 The JWS signature does not match the expected value.
RevokedCertificate 2 The certificate that signed the JWS has been revoked.
InvalidDeviceVerification 3 The device verification value in the JWS does not match the value expected for this device.
InvalidCertificateChain 4 The certificate chain that signed the JWS is not valid.
InvalidEncoding 5 The JWS cannot be decoded because it is not encoded in the format that StoreKit expects.
MissingRequiredProperties 6 The JWS is missing properties required for verification.

AppleVerificationStatus

The branch of VerificationResult<Transaction>.

C# member Value Description
Unspecified 0 The default value, with no verification status specified.
Verified 1 StoreKit verified the transaction JWS signature. VerificationError does not apply.
Unverified 2 StoreKit could not verify the transaction JWS signature. Check the cause in VerificationError.