Server to Server Check - Server

How does it work with Google?

When using Google IAB you need to send a GET http request to the following URL:

GET https://www.googleapis.com/androidpublisher/v3/applications/**packageName**/purchases/products/**productId**/tokens/**token**

Where:

  • 'packageName' is the application ID;
  • 'productId' is the sku of the item;
  • 'token' is the purchaseToken that you get in the purchase receipt.

You can check more info here:
https://developers.google.com/android-publisher/api-ref/purchases/products/get

If this request responds OK the purchase is valid, else it is not.

The response will be in this format:
https://developers.google.com/android-publisher/api-ref/purchases/products#resource

How to make it work for Aptoide?

Simply replace the GET Request URL by this one:

GET https://api.catappult.io/product/8.20191001/inapp/google/v3/applications/**package_name**/purchases/products/**sku**/tokens/*purchase_token**

The next table summarizes the differences in the call from Google to Aptoide

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

Parameters:

  • package_name The package name of the application where the product was purchased (for example, 'com.appcoins.trivialdrivesample')
  • sku The in-app product SKU (for example, 'gas')
  • purchase_token The token provided to the user's device when the product was purchased. Our purchase token always starts with catappult.inapp.purchase
    Below is a fake receipt, for the sake of clarity, depicting the values you need to get from it and send on the request.
{
  "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": "your.package.name",
        "productId ": "yourSKU",
        "purchaseTime": 123456789,
        "purchaseToken": "catappult.inapp.purchase.anIdWithNumbersAndLetters",
        "purchaseState": 0,
        "developerPayload":"unity://unity3d.com?cpOrderId=anIdWithNumbersAndLetters&payload="
      }
    }
  }
}

The package_name and sku are the data you get from the client side call and the purchaseToken can be obtained from the originalJson field OR from the StorePurchaseJsonString (with the name orderId instead).

After making this request, if you get a return code 200 it means that the purchase was successfully validated.

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 = "https://api.catappult.io/product/8.20191001/inapp/google/v3/" \
           "applications/{package_name}/purchases/products/{sku}/tokens/" \
           "{purchase_token}"
 
    response = requests.get(api_purchase_url
                            .format(package_name=package_name, sku=sku,
                                    purchase_token=purchase_token))
 
    if response.status_code == 200:
        return True
    else:
        return False
private boolean validatePurchase(String packageName, String sku, 
                                 String purchaseToken, String accessToken
                                ) throws Exception {
    String apiPurchaseUrl = String.format("https://api.catappult.io/product/8.20191001/" +                                 "inapp/google/v3/applications/%s/purchases/products/%s/tokens/%s", 
            packageName, sku, purchaseToken);
    HttpGet request = new HttpGet(apiPurchaseUrl);

    Request request = new Request.Builder()
                .url(apiPurchaseUrl)
                .build();
    try (Response response = httpClient.newCall(request).execute()) {
        return response.isSuccessful();
    }
}
function validatePurchase($packageName, $sku, 
                                                    $purchaseToken, $accessToken) {
    $curl = curl_init();
    $apiPurchaseUrl = 
'https://api.catappult.io/product/8.20191001/inapp/google/v3/applications/' . $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);
    curl_close($ch);
    if ($httpcode == 200) {
        return true;
    } else {
        return false;
    }
}

This is the response with the explanation of each parameter.
Here's a sample response with the explanation of each parameter.

{
  "kind": "androidpublisher#productPurchase",
  "purchaseTimeMillis": long,
  "purchaseState": integer,
  "consumptionState": integer,
  "developerPayload": string,
  "orderId": string,
  "purchaseType": integer,
  "acknowledgementState": integer
}
  • 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.
  • purchaseType: The type of purchase of the in-app product. It is only set if the purchase wasn't made using the standard in-app billing flow.
    Possible values are:
    • 0 (Test)
  • acknowledgementState: The acknowledgment state of the in-app product.
    Possible values are:
    • 0 (Yet to be acknowledged)
    • 1 (Acknowledged)