Server to Server Check - Server


How to validate with Catappult's API?

In order to validate the transaction with Catappult's API, you will need to send a request from your server to our API. As a response, you will receive information about the transaction and by receiving this response it means that the purchase is valid.


After completing a purchase in your client app, you will receive a receipt that looks like this. This will be your source of information to call our API.

  "Store": "storeName",
  "TransactionID": "anIdWithNumbersAndLetters",
  "Payload": {
    "ItemType": "inapp",
    "ProductId": "full_trajectory",
    "GameOrderId": "anIdWithNumbersAndLetters",
    "OrderQueryToken": "aLargeIdWithNumbersAndLetters",
    "StorePurchaseJsonString": {
      "developerPayload": "unity://unity3d.comcpOrderId=anIdWithNumbersAndLetters&payload=",
      "itemType": "inapp",
      "orderId": "catappult.inapp.purchase.anIdWithNumbersAndLetters",
      "originalJson": {
        "orderId ": "anIdWithNumbersAndLetters",
        "packageName": "",
        "productId ": "yourSKU",
        "purchaseTime": 123456789,
        "purchaseToken": "catappult.inapp.purchase.anIdWithNumbersAndLetters",
        "purchaseState": 0,

API Request URL

From this receipt you can retrieve the necessary information to use as parameters to call our API, using the following GET Request URL:


Description: Web service to validate the purchase and consumption status of an in-app item.


The packageName and productId are inside the originalJson field. The purchaseToken can be obtained from either the originalJson field OR from the StorePurchaseJsonString (with the name orderId instead).

  • packageName The package name of the application where the product was purchased (eg. "com.appcoins.trivialdrivesample")
  • productId The in-app product SKU (eg. "gas")
  • purchaseToken The token provided to the user's device when the product was purchased.
    Our purchase token always starts with catappult.inapp.purchase.

Below there are some snippets in several languages of how one would perform this request:

def validate_purchase(self, package_name: str, sku: str,
                      purchase_token: str, access_token: str) -> bool:
    api_purchase_url = "" \
           "applications/{packageName}/purchases/products/{productId}/tokens/" \
    response = requests.get(api_purchase_url
                            .format(packageName=package_name, productId=sku,
    if response.status_code == 200:
        return True
        return False
private boolean validatePurchase(String packageName, String sku, 
                                 String purchaseToken, String accessToken
                                ) throws Exception {
    String apiPurchaseUrl = String.format("" +                                 "inapp/google/v3/applications/%s/purchases/products/%s/tokens/%s", 
            packageName, sku, purchaseToken);
    HttpGet request = new HttpGet(apiPurchaseUrl);

    Request request = new Request.Builder()
    try (Response response = httpClient.newCall(request).execute()) {
        return response.isSuccessful();
function validatePurchase($packageName, $sku, 
													$purchaseToken, $accessToken) {
    $curl = curl_init();
    $apiPurchaseUrl = 
'' . $packageName . '/purchases/products/' . $sku . '/tokens/' . $purchaseToken

    curl_setopt($ch, CURLOPT_URL, $apiPurchaseUrl);
    curl_setopt($ch, CURLOPT_HEADER, true);
    curl_setopt($ch, CURLOPT_NOBODY, true);
    $response = curl_exec($ch);
    $httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    if ($httpcode == 200) {
        return true;
    } else {
        return false;


If you get a status code 200 OK form the request it means the purchase was successfully validated.

In the response, you have multiple fields providing a variety of information regarding the transaction. Here's a sample response with an explanation for each parameter.

    "kind": "androidpublisher#productPurchase",
    "purchaseTimeMillis": long,
    "purchaseState": integer,
    "consumptionState": integer,
    "developerPayload": string,
    "orderId": string,
    "acknowledgementState": integer,
    "purchaseToken": string,
    "productId": string,
    "regionCode": string

Here is the explanation of each field:

  • kind: Represents a productPurchase.
  • purchaseTimeMillis: The time the product was purchased in milliseconds.
  • purchaseState: The purchase state of the order.
    Possible values are:
    • 0 (Purchased)
    • 1 (Canceled)
  • consumptionState: The consumption state of the in-app product.
    Possible values are:
    • 0 (Yet to be consumed)
    • 1 (Consumed)
  • developerPayload: A developer-specified string that contains supplemental information about an order.
  • orderId: The order id associated with the purchase of the in-app product.
  • acknowledgementState: The acknowledgment state of the in-app product.
    Possible values are:
    • 0 (Yet to be acknowledged)
    • 1 (Acknowledged)
  • purchaseToken: The token provided to the user's device when the product was purchased.
  • productID: The product ID of the product being purchased
  • regionCode: The region code for the place where the purchase was made.


Validating subscriptions follows with exact same logic with only a small change on the URL endpoint.


The only difference is the subscriptionsinstead of purchases.