This article is to help ensure that your transaction events are correctly implemented so that your events are not rejected and your revenue is properly validated. Code samples are provided at the end of this article. Note, this article focuses on Apple App Store transactions and Google Play transactions.


There are two main stumbling blocks you should be aware of when sending a transaction event to deltaDNA while transaction validation is active on your account:

  • Sending incorrect information may result in the revenue being marked as invalid

  • Since many of the parameters in this event have similar names, sending the required information in the incorrect parameters may result in the event being rejected by deltaDNA due to unexpected formatting


TABLE OF CONTENTS


Required Parameters for Validation

For revenue validation, there are a number of parameters which are required to perform the validation properly. These required parameters however vary between the platforms which are being validated against.


For all revenue validation, you must include the transactionServer parameter. This parameter will help deltaDNA know which store to validate the receipt against. The value for this parameter can be:

  • APPLE

  • GOOGLE

  • AMAZON

  • VALVE

As this article focuses on the Apple App Store and Google Play Store, the parameters required by Amazon will not be covered. Additionally, Steam does not provide transaction validation, so there are no parameters specifically required for transactions coming from their servers.

Apple App Store Validation Parameters

For receipts coming from the Apple App Store, two additional parameters are required for validation:

  • transactionID

  • transactionReceipt

The value in the “transactionReceipt” parameter should be the Base 64 encoded string of the receipt data provided by Apple.


Google Play Store Validation Parameters

For receipts coming from the Google Play Store, two additional parameters are required for validation:

  • transactionReceiptSignature

  • transactionReceipt

The value in the “transactionReceipt” parameter, in this case, should be the json string provided by Google.


Unity IAP Package

When directly using the Apple StoreKit Framework and the Google Play Billing Library, it is easy to retrieve the required information. However, it can be a little more complicated with the Unity IAP Package.


Receipts provided by the IAP package are JSON objects with the following data fields:

  • Store - the store where the transaction was performed

  • TransactionID - the ID of the transaction

  • Payload - the receipt information

For transactions from the Apple App Store, the “Payload” field contains the Base 64 encoded string of the receipt data.


For transactions from the Google Play Store, the “Payload” field contains another JSON object with a “json” data field and a “signature” data field.

Apple App Store Transaction Parameters

To send a transaction event to deltaDNA with the details from the receipt returned by the Unity IAP package, the “transactionID” parameter should be populated with the value from the “TransactionID” data field in the receipt. And the “transactionReceipt” parameter should be populated with the value from the “Payload” data field in the receipt. Remember to set the “transactionServer” parameter to “APPLE”.

Google Play Store Transaction Parameters

To send a transaction event to deltaDNA with the details from the receipt returned by the Unity IAP package, the “transactionReceiptSignature” parameter should be populated with the value from the “signature” data field in the “Payload” data field in the receipt. And the “transactionReceipt” parameter should be populated with the value from the “json” data field in the “Payload” data field in the receipt. Remember to set the “transactionServer” parameter to “GOOGLE”.


IMPORTANT: For Google Play Store transactions, the Unity IAP package sets the value of the “TransactionID” data field to the receipt signature in addition to including the receipt signature in the “Payload” data field. Do not use this value in the “transactionID” parameter in your event or your event will be rejected. This parameter can only accept values which are 72 characters long, the receipt signature is too long for this parameter.



Samples

Apple App Store Transactions

Apple StoreKit Framework (with iOS SDK, using Swift)

// Get the receipt if it's available
if let appStoreReceiptURL = Bundle.main.appStoreReceiptURL,
   FileManager.default.fileExists(atPath: appStoreReceiptURL.path) {


   do {
       let receiptData = try Data(contentsOf: appStoreReceiptURL, options: .alwaysMapped)
       print(receiptData)


       let receiptString = receiptData.base64EncodedString(options: [])
       let transactionId = receiptData.Receipt.In_app.transaction_id

       DDNASDK.sharedInstance().recordEvent(withName: "transaction", eventParams: [
        "transactionName": "IAP - Large Treasure Chest",
        "transactionID": transactionId,
        "transactionServer": "APPLE",
        "transactionReceipt": receiptString,
        "transactionType": "PURCHASE",
        "productsReceived": {
            "virtualCurrencies": [],
            "items": [
                {
                    "item": {
                        "itemName": "Golden Battle Axe",
                        "itemType": "Weapon",
                        "itemAmount": 1
                    }
                }
            ]
        },
        "productsSpent": {
            "realCurrency": {
                "realCurrencyType": "USD",
                "realCurrencyAmount": 499
            }
        },
        "platform": "IOS_MOBILE",
        "sdkVersion": "Unity SDK v5.0.10"
    }

       ])
   }
   catch { print("Couldn't read receipt data with error: " + error.localizedDescription) }
}

Unity IAP Package (with Unity SDK, using C#)

Recording a transaction event normally:

public class ReceiptInfo {
    public string Store;  
    public string TransactionID;
    public string Payload;
}

public PurchaseProcessingResult ProcessPurchase (PurchaseEventArgs e)
{
    GameEvent transactionEvent = new GameEvent("transaction")
        .AddParam("transactionName","IAP - Large Treasure Chest")
        .AddParam("transactionType", "PURCHASE")
        .AddParam("productsReceived", new Product()
            .AddItem("Golden Battle Axe", "Weapon", 1))
        .AddParam("productsSpent", new Product()
            .SetRealCurrency("USD", Product.ConvertCurrency("USD", 4.99m)));

    ReceiptInfo receipt = JsonUtility.FromJson<ReceiptInfo>(e.purchasedProduct.receipt);

    if(receipt.Store == "AppleAppStore") 
    {
        transactionEvent.AddParam("transactionServer", "APPLE")
            .AddParam("transactionID", receipt.TransactionID)
            .AddParam("transactionReceipt", receipt.Payload);

        DDNA.Instance.RecordEvent(transactionEvent);
    }
}

Recording a transaction event using the Transaction helper class:

public class ReceiptInfo {
    public string Store;  
    public string TransactionID;
    public string Payload;
}

public PurchaseProcessingResult ProcessPurchase (PurchaseEventArgs e)
{
    Transaction transactionEvent = new Transaction(
        "IAP - Large Treasure Chest",
        "PURCHASE",
        new Product() // Products Received
        .AddItem("Golden Battle Axe", "Weapon", 1),
        new Product() // Products Spent
        .SetRealCurrency("USD", Product.ConvertCurrency("USD", 4.99m)))

    ReceiptInfo receipt = JsonUtility.FromJson<ReceiptInfo>(e.purchasedProduct.receipt);

    if(receipt.Store == "AppleAppStore") 
    {
        transactionEvent.SetServer("APPLE")
            .SetTransactionId(receipt.TransactionID)
            .SetReceipt(receipt.Payload);

        DDNA.Instance.RecordEvent(transactionEvent);
    }
}

Google Play Transactions

Google Play Billing Library (with Android SDK, using Java)

Recording a transaction event normally:

void handlePurchase(Purchase purchase) {
    String receiptString = purchase.getOriginalJson();
    String receiptSignature = purchase.getSignature();

    DDNA.instance().recordEvent(new Event("transaction")
        .putParam("transactionName", "IAP - Large Treasure Chest")
        .putParam("transactionType", "PURCHASE")
        .putParam("productsReceived", new Product()
            .addItem("Golden Battle Axe", "Weapon", 1))
        .addParam("productsSpent", new Product()
            .setRealCurrency("USD", Product.convertCurrency(DDNA.instance(), "USD", 4.99f)))
        .putParam("transactionServer", "GOOGLE")
        .putParam("transactionReceipt", receiptString)
        .putParam("transactionReceiptSignature", receiptSignature));
}

Recording a transaction event using the Transaction helper class:

void handlePurchase(Purchase purchase) {
    String id = purchase.getOrderId();
    String receiptString = purchase.getOriginalJson();
    String receiptSignature = purchase.getSignature();

    DDNA.instance.recordEvent(new Transaction(
        "IAP - Large Treasure Chest",
        "PURCHASE",
        new Product()
            .addItem("Golden Battle Axe", "Weapon", 1),
        new Product()
            .setRealCurrency("USD", Product.convertCurrency(DDNA.instance(), "USD", 4.99f))
        .putParam("transactionServer", "GOOGLE")
        .putParam("transactionReceipt", receiptString)
        .putParam("transactionReceiptSignature", receiptSignature);
}

Unity IAP Package (with Unity SDK, using C#)

Recording a transaction event normally:

public class ReceiptInfo {
    public string Store;  
    public string TransactionID;
    public string Payload;
}
public class GoogleReceipt {
    public string json;
    public string signature;
}

public PurchaseProcessingResult ProcessPurchase (PurchaseEventArgs e)
{
    GameEvent transactionEvent = new GameEvent("transaction")
        .AddParam("transactionName","IAP - Large Treasure Chest")
        .AddParam("transactionType", "PURCHASE")
        .AddParam("productsReceived", new Product()
            .AddItem("Golden Battle Axe", "Weapon", 1))
        .AddParam("productsSpent", new Product()
            .SetRealCurrency("USD", Product.ConvertCurrency("USD", 4.99m)));

    ReceiptInfo receipt = JsonUtility.FromJson<ReceiptInfo>(e.purchasedProduct.receipt);

    if(receipt.Store == "GooglePlay") 
    {
        GoogleReceipt payload = JsonUtility.FromJson<GoogleReceipt>(receipt.Payload);

        transactionEvent.AddParam("transactionServer", "GOOGLE")
            .AddParam("transactionReceipt", payload.json)
            .AddParam("transactionReceiptSignature", payload.signature);

        DDNA.Instance.RecordEvent(transactionEvent);
    }
}

Recording a transaction event using the Transaction helper class:

public class ReceiptInfo {
    public string Store;  
    public string TransactionID;
    public string Payload;
}
public class GoogleReceipt {
    public string json;
    public string signature;
}

public PurchaseProcessingResult ProcessPurchase (PurchaseEventArgs e)
{
    Transaction transactionEvent = new Transaction(
        "IAP - Large Treasure Chest",
        "PURCHASE",
        new Product() // Products Received
        .AddItem("Golden Battle Axe", "Weapon", 1),
        new Product() // Products Spent
        .SetRealCurrency("USD", Product.ConvertCurrency("USD", 4.99m)))

    ReceiptInfo receipt = JsonUtility.FromJson<ReceiptInfo>(e.purchasedProduct.receipt);

    if(receipt.Store == "GooglePlay") 
    {
        GoogleReceipt payload = JsonUtility.FromJson<GoogleReceipt>(receipt.Payload);

        transactionEvent.SetServer("GOOGLE")
            .SetReceipt(payload.json)
            .SetReceiptSignature(payload.signature);

        DDNA.Instance.RecordEvent(transactionEvent);
    }
}