In-app purchase validation

In-app purchases (IAP) are a key monetization tool for games, enabling purchases for unlocks, consumables, subscriptions, and more. However, without proper validation, IAP implementations are vulnerable to fraud and abuse. Nakama provides secure server-side validation for Apple App Store, Google Play, and Huawei purchases.

This guide explains how Nakama’s IAP validation works, how to prevent common attacks like replay attacks and receipt sharing, and how to handle purchase state changes through real-time notifications.

Ready to implement? Get started with our how-to guides for Apple App Store or Google Play.

Why validate purchases server-side #

Client-side IAP validation is vulnerable to several common attacks:

  • Feeding the client fake purchase responses which indicate success.
  • Replaying a valid purchase response multiple times.
  • Sharing a purchase response with another client, so multiple players can receive the reward from a single purchase.

For in-app purchases, you need a trusted source of truth. Nakama checks and tracks purchases and purchase history, solving a significant set of possible vulnerabilities and pain points:

IssueDescription
Fake purchasesNakama directly connects to Apple, Google and Huawei services to check the validity of all incoming purchase receipts. This verification is completely outside the client’s code, and cannot be intercepted and tampered with. Every purchase receipt is verified, every time, and invalid ones are rejected.
Replay attacksAll transactions are logged, preventing multiple submissions of the same purchase token or receipt.
Receipt sharingSuccessful transactions are bound to the account that submits them. Different users cannot submit the same transaction, even a valid one, in an attempt to receive the associated reward.
Product mismatchesEach validated purchase receipt exposes data (e.g. product ID) that can be used to tie a purchase to a product, preventing attacks that attempt to use a valid (cheap) purchase to unlock a different (expensive) reward.
Single source of truthWhile Nakama maintains an internal record of all transactions, the remote payment provider is always used for validation.

How IAP validation works #

The validation flow is initiated by your game client, but all verification happens server-side in Nakama:

  1. Client makes purchase: The player completes a purchase using the platform SDK (Apple, Google, or Huawei).
  2. Client receives receipt: The platform SDK returns a receipt in a platform-specific format.
  3. Client sends receipt to Nakama: Your game sends the receipt to Nakama’s validation endpoint.
  4. Nakama validates with provider: Nakama contacts the provider’s API (Apple, Google, or Huawei) to verify the receipt is authentic.
  5. Nakama stores the result: After validation, Nakama normalizes the response and stores it in the database, attached to the user.
  6. Game grants reward: Based on the validation response, your game logic grants the appropriate rewards or access.

This flow ensures that all payments are verified by the actual payment provider on the server before your game grants any rewards.

Replay attack prevention #

Every validated purchase and subscription is stored in Nakama’s database. When you validate a receipt, Nakama checks if it’s been validated before and returns a seenBefore boolean flag.

  • seenBefore: false: This is the first time this receipt has been validated - it’s safe to grant rewards.
  • seenBefore: true: This receipt has already been validated - it may be a replay attack or legitimate re-validation.

This mechanism prevents players from using the same receipt multiple times to receive rewards repeatedly.

Purchases are always attached to a user
Purchases are attached to the user ID from the session used during validation. If an account is deleted, then the purchase becomes associated with a system user to maintain the historical record and prevent reuse.

Purchases vs subscriptions #

Nakama supports two distinct entities for IAP validation:

Purchases #

One-time purchases for consumables, unlocks, or permanent items. Key characteristics:

  • Single validation: Once validated, the purchase is recorded.
  • Seen before tracking: Nakama tracks if a receipt has been used before to prevent double-spending.
  • Refund notifications: You can receive notifications when purchases are refunded.

Subscriptions #

Recurring purchases that grant access for a period of time. Key characteristics:

  • Time-based access: Subscriptions have expiry times and active status.
  • State changes: Subscriptions can be renewed, expired, cancelled, or refunded.
  • Real-time updates: Nakama automatically updates subscription state based on provider notifications.
  • Ongoing tracking: Subscription status is continuously monitored through the validation period.

Handling refunds and subscription changes #

Payments and refunds are handled entirely by the platform providers (Apple, Google, Huawei). Nakama never sees payment information directly. However, providers offer notification systems to inform your server about changes.

Real-time notifications #

Both Apple and Google support real-time server-to-server notifications for:

  • Purchase refunds: When a player requests and receives a refund.
  • Subscription renewals: When a subscription auto-renews successfully.
  • Subscription cancellations: When a player cancels their subscription.
  • Subscription expirations: When a subscription reaches its expiry time without renewing.

Once you configure Nakama to receive these notifications, it automatically updates the purchase or subscription state in the database.

Notifications only work for purchases and subscriptions that you’ve already validated through Nakama. If a provider sends a notification for a purchase Nakama hasn’t seen before, it’ll be ignored.

Notification types #

Apple and Google send many different notification types with various subtypes. Nakama simplifies these by normalizing them into 5 categories that track expiry time changes, subscription state, and refund status.

TypeDescriptionApplies to
SUBSCRIBEDInitial subscription purchase or resubscriptionSubscriptions only
RENEWEDSubscription successfully auto-renewedSubscriptions only
EXPIREDSubscription expired and will not renewSubscriptions only
CANCELLEDSubscription cancelled by userSubscriptions only
REFUNDEDPurchase or subscription refundedPurchases & subscriptions

This normalization makes it easier to handle notifications consistently across providers.

If you need access to the original provider notification data for custom processing, it’s available in the providerPayload parameter passed to your notification hooks. This contains the complete raw notification from Apple or Google.

Notification hooks #

You can register callback functions i.e., hooks, to respond to notifications. Your hooks execute after Nakama has updated the purchase or subscription state in the database.

Use notification hooks to:

  • Revoke player access when subscriptions expire or are refunded.
  • Send re-engagement messages when subscriptions are cancelled.
  • Update analytics or tracking systems.
  • Trigger in-game rewards or notifications.

For purchases, only refund notifications are available. For subscriptions, you’ll receive notifications for all state changes.

Understanding validation results #

When you validate a receipt, Nakama returns a validation result containing a list of validated purchases.

Apple receipts #

Because Apple receipts can contain multiple purchases in a single receipt, each validated purchase in the resulting list will contain only the purchases which have been validated and a boolean value to indicate if it’s been seen before. If a purchase has already been validated by Nakama previously, the “seen before” value will be true, letting you discriminate new purchases from repeated validations and protect against replay attacks.

Google and Huawei receipts #

For Google and Huawei, each validation corresponds to a single purchase, which is included in the validation response list. Like Apple, each purchase also has a “seen before” value to prevent replay attacks.

Response contents #

Each validated purchase includes:

  • User ID: The user who made the purchase
  • Product ID: The SKU or product identifier
  • Transaction ID: Unique identifier for the transaction
  • Store: The platform (Apple, Google, Huawei, or Facebook)
  • Purchase time: When the purchase was made
  • Seen before: Boolean indicating if this receipt was previously validated
  • Provider response: The raw response from the provider, in case you need it for custom processing
  • Environment: Whether the purchase was made in production or sandbox
  • Refund time: Set if the purchase was refunded

For subscriptions, the response includes additional fields:

  • Original transaction ID: The ID of the original subscription (not renewals)
  • Expiry time: When the subscription expires
  • Active: Whether the subscription is currently active
  • Provider notification: Raw provider notification data

Error handling #

If the purchase or receipt is invalid, the validation fails for any reason, or the provider is unreachable, you’ll receive an error. Your game should handle these errors gracefully and not grant rewards when validation fails.

Supported platforms #

Apple App Store #

  • Single product purchases
  • Subscription purchases
  • Real-time server notifications for refunds and subscription changes
  • iOS 7+ receipts supported

Set up Apple validation →

Google Play #

  • Single product purchases
  • Subscription purchases
  • Real-time developer notifications for refunds and subscription changes
  • Requires Google Cloud Service Account with Play Console permissions

Set up Google validation →

Huawei AppGallery #

Nakama validates Huawei purchases against their IAP validation service. As suggested by Huawei, the validity of the purchase data is also checked against the provided signature before contacting the Huawei service. If the data is invalid for any reason, the purchase is rejected before validation with Huawei’s validation service.

Validate purchase #

To validate a Huawei purchase, you’ll need both the purchase data and its signature from the Huawei IAP SDK. Nakama first verifies the signature locally, then validates the purchase with Huawei’s servers.

Refer to the function reference page for the provided runtime purchase validation functions.

Facebook Instant Games #

Nakama supports validating purchases made for products on Facebook Instant Games.

Validate purchase #

To validate a Facebook Instant Games purchase, you’ll send the signed request from the Facebook SDK to Nakama. The signed request contains the purchase information that Nakama will verify with Facebook’s servers.

Unity IAP #

If you’re using Unity IAP in your game, you can still validate purchases through Nakama. Unity IAP provides a unified interface for integrating with most popular app stores, including Apple and Google, and Nakama can validate the receipts it generates.

Unity IAP purchase receipts contain the following information:

KeyValue
StoreThe name of the store in use, such as GooglePlay or AppleAppStore.
TransactionIDThis transaction’s unique identifier, provided by the store.
PayloadFor Apple, a Base64 encoded App Receipt. For Google, JSON encoded purchase data and a signature.
The Nakama IAP Validation APIs expect only the content of the “Payload” key to be provided for validation in the respective store endpoints.

Validate purchase #

When validating Unity IAP purchases, you’ll extract the payload from the Unity receipt and route it to the appropriate platform validation endpoint based on the store type. Here’s how to handle Unity IAP receipts in your server-side code:

Subscriptions #

In addition to validation of purchases and subscriptions, you can also get subscriptions by specific product ID or list all subscriptions for a given user.

Get subscription #

After validating a subscription, you can retrieve its current state at any time using the product ID. This is useful for checking subscription status, expiry times, and renewal information without revalidating the receipt.

Refer to the function reference page for the provided runtime subscription validation functions.

List user subscriptions #

You can retrieve all subscriptions for a user, which is useful for displaying their current subscription status in your game or managing multiple active subscriptions. The list supports pagination for users with many subscriptions.

Refer to the function reference page for the provided runtime subscription validation functions.

See also #