Catappult Blog

Remote Check

How to validate with Catappult's API?

To validate the transaction with Catappult's API, you need to send a request from your server to our API. In response, you will receive information about the transaction, confirming the purchase is valid.

Client

Swift Client

After completing a purchase in your client app, you will receive an object VerificationResult that indicates whether the AppCoins Billing SDK was able to verify the purchase's signature. If this validation is successful, VerificationResult.verified will have an attribute purchase, which belongs to the following class:

public class Purchase: Codable {
  public let uid: String
  public let sku: String
  public var state: String
  public let orderUid: String
  public let payload: String?
  public let created: String
  public let verification: PurchaseVerification
  
  public class PurchaseVerification: Codable {
    public let type: String
    public let data: PurchaseVerificationData
    public let signature: String
  }
  
  public class PurchaseVerificationData: Codable {
    public let orderId: String
    public let packageName: String
    public let productId: String
    public let purchaseTime: Int
    public let purchaseToken: String
    public let purchaseState: Int
    public let developerPayload: String
  }
}

From this object, you should extract all the necessary parameters to validate the purchase server-side. These parameters are: purchase.verification.data.packageName, purchase.verification.data.productId, and purchase.verification.data.purchaseToken.

After obtaining the relevant parameters on the client, pass this information to your server as shown below:

let result = await products?.first?.purchase()

switch result {
  case .success(let verificationResult):
  
  switch verificationResult {
    case .verified(let purchase):
    
    if await verify_purchase_on_server(package_name: purchase.verification.data.packageName, product_id: purchase.verification.data.productId, purchase_token: purchase.verification.data.purchaseToken) {
    	// Purchase is verified!
    }
  }
}

Implement the verify_purchase_on_server method according to your server’s logic to complete the server-side validation.

Unity Plugin Client

After completing a purchase in your application that is integrating the AppCoins SDK Unity Plugin, you will receive an object PurchaseResult that contains an attribute Purchase, which belongs to the following class:

[Serializable]
public class PurchaseData
{
    public string UID;
    public string Sku;
    public string State;
    public string OrderUID;
    public string Payload;
    public string Created;
    public PurchaseVerification Verification;

    [Serializable]
    public class PurchaseVerification
    {
        public string Type;
        public string Signature;
        public PurchaseVerificationData Data;
    }

    [Serializable]
    public class PurchaseVerificationData
    {
        public string OrderId;
        public string PackageName;
        public string ProductId;
        public int PurchaseTime;
        public string PurchaseToken;
        public int PurchaseState;
        public string DeveloperPayload;
    }
}

From this object, you should extract all the necessary parameters to validate the purchase server-side. These parameters are: Purchase.Verification.Data.PackageName, Purchase.Verification.Data.ProductId, and Purchase.Verification.Data.PurchaseToken.

After obtaining the relevant parameters on the client, pass this information to your server as shown below:

var purchaseResponse = await AppCoinsSDK.Instance.Purchase("antifreeze");

if (purchaseResponse.State == AppCoinsSDK.PURCHASE_STATE_SUCCESS)
{

  string packageName = purchaseResponse.Purchase.Verification.Data.PackageName;
  string productId = purchaseResponse.Purchase.Verification.Data.ProductId;
  string purchaseToken = purchaseResponse.Purchase.Verification.Data.PurchaseToken;

  bool isValid = await VerifyPurchaseOnServer(packageName, productId, purchaseToken);
}

Implement the VerifyPurchaseOnServer method according to your server’s logic to complete the server-side validation.

Server

Using the parameters retrieved on the client and passed to your server, you can construct the following GET request URL to call our API:

GET https://api.catappult.io/product/8.20191001/google/inapp/v3/applications/**packageName**/purchases/products/**productId**/tokens/**token**?platform=IOS

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

Parameters:

  • packageName: The bundle ID 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.

Query Parameters:

You should indicate that this is an iOS purchase by adding the query parameter platform and setting it to IOS.

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/google/inapp/v3/" \
           "applications/{packageName}/purchases/products/{productId}/tokens/" \
           "{purchaseToken}"
 
    response = requests.get(api_purchase_url
                            .format(packageName=package_name, productId=sku,
                                    purchaseToken=purchase_token),
                            params={'platform': 'iOS'})
 
    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);
    
    // Add the query parameter `platform=iOS` to the URL
    String urlWithParams = apiPurchaseUrl + "?platform=iOS";

    Request request = new Request.Builder()
                .url(urlWithParams)
                .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 . '?platform=iOS';

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

Response

If you get a status code 200 OK from 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.

{
  "resource": {
    "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.