Skip to content

In-app Purchase Validation

The spectrum of monetisation models and tools is extremely varied. From ad-supported, microtransactions, freemium, one-off purchases, and everything in between. A key tool in many of these solutions is the In-App Purchase, which enables single purchases for unlocks, in-game consumables, subscriptions for premium access, and more.

There are a number of readily available attacks against the most common in-app purchase implementations.

These are usually focused around:

  • 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.
  • ...and more, with new vulnerabilities emerging occasionally.

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

In-App Purchase Validation is available for Apple and Google purchases, regardless of platform. Both single product and subscription purchases are supported.

Fake Purchases

Nakama 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 Attacks

All transactions are logged, preventing multiple submissions of the same purchase token or receipt.

Receipt Sharing

Successful 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 Mismatches

Each 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 Truth

While Nakama maintains an internal record of all transactions, the remote payment provider is always used for validation.


Nakama supports validating purchases made for products and subscription in iOS.

Apple purchase receipts are sent to Apple for validation. As suggested by Apple, both Production and Sandbox servers are used to validate receipts depending on the priority setup in the Nakama configuration.


To validate receipts against the App Store, Nakama requires your app's shared secret. You can setup a shared secret in App Store Connect under your app's In-App Purchases management section.

Apple App Store Connect

Make a record of your shared secret:

Apple App Store Connect Shared Secret

You'll need to set the value of Nakama's configuration flag to the value of the Shared Secret above. For more info, take a look at the configuration page.

Validate Purchase

Nakama only supports validating iOS 7+ receipts.

Apple receipts can contain multiple purchases, Nakama will validate all of them and store them as individual purchase records.

curl " \
  --user 'defaultkey:' \
  --data '{"receipt":"base64_encoded_receipt_data"}'

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


Nakama supports validating purchases made for products and subscription on Android.


To validate receipts against the Play Store, Nakama requires your Google Service Account ClientEmail and PrivateKey. The values must be set in Nakama's and configuration flags values, respectively. For more info, take a look at the configuration page.

To get these values, first you'll need to setup a Service Account in the Google API Console. You can refer to the Google Play documentation to create it.

Create Service Account

Once a service account is created, you'll need to create a key:

Create Key

Download the key as a JSON file.

Create Key

Open it, extract the values of ClientEmail and PrivateKey and set them as the respective Nakama configuration values for:


For more info, take a look at the configuration page.

Finally you will need to ensure you grant Nakama access to the purchase validation APIs. Navigate back to Google Play Developer Console and navigate to Settings > API Access.

The service account you created in the previous steps should be listed above. You'll need to grant access to the service account to access the API:

Create API Access

Make sure that you give the service account access to Visibility, View Financial Data, and Manage Orders. These permissions are required for Nakama to validate receipts against Google Play.

Grand Access

Navigate to Users & Permissions to check that the service account is setup correctly.

List users with access

Validate Purchase

curl " \
  --user 'defaultkey:' \
  --data '{"purchase":"json_encoded_purchase_data"}'

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


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.

curl " \
  --user 'defaultkey:' \
  --data '{"purchase":"json_encoded_purchase_data","signature":"purchase_data_signature"}'

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

Interpreting the validation result

A validation result contains a list of validated purchases.

Because Apple may contain multiple purchases in a single receipt, the resulting list will contain only the purchases which have been validated and are new to Nakama's purchase ledger. If a purchase has already been validated by Nakama previously, it won't be included in the list, allowing the developer to discriminate new purchases and protect against replay attacks.

For Google and Huawei, each validation corresponds to a single purchase, which is included in the validation response list if new, and omitted otherwise.

Each validated purchase also includes the payload of the provider validation response, should the developer need it for any reason.

Should the purchase/receipt be invalid, the validation fail for any reason or the provider be unreachable, an error will be returned.