ALERT: The Kongregate Receipt Validation API has been deprecated. The flow outlined here should still be followed, replace calls to the Kongregate Receipt Validation service with a custom or 3rd party solution.
Analytics Only Integrations may follow the simplified flow outlined here.
The Kongregate SDK includes a receipt verification API, which is intended to be used by games that do not implement their own server based solution. Game specific server-side Receipt Verification is recommended, if possible. For iOS the SDK’s Receipt Verification API relies on the client to verify the purchase with iTunes Connect. This is less reliable than server based authentication. The Android Receipt Verification API will use Kongregate servers and simply validates the given receipt was signed with appropriate key.
This Purchase Flow Diagram outlines the basic steps of a purchase. Below is a detailed walk through, followed by some sample code for iOS, Android, and Unity. These steps should be followed whether using the Kongregate Receipt Validation API or your own solution.
WARNNG: StartPurchase() and FinishPurchase should only be used when the products IDs follow the
txx_<type>
naming convention.
startPurchase
(API Docs: iOS, Android, Unity) to fire the necessary analytics events.finishPurchase(SUCCESS, transactionDetails, gameFields, dataSignature)
, to fire the appropriate analytics events. transactionDetails
should be the unfinished transactionId on iOS and the full receipt JSON on Android. dataSignature
is not use on iOS. On Android it should be the signature included with the receipt.finishPurchase(RECEIPT_FAIL, transactinDetails, gameFields, null)
to fire the appropriate analytics events. Again, on iOS, simply pass the transactionId
.SKPaymentQueue finishTransaction:
(iOS) or IInAppBillingService.consumePurchase()
(Android)) to complete the purchase. On Unity, invoke the method on the plugin that triggers finishTransaction or consumePurchase.IMPORTANT: if using a Unity plugin on iOS be sure to use one that allows you to delay invokation of finishTransaction
until after the receipt has been verified. (e.g. Prime31)
The SDK provides two ways of handling the verification. The first and simplest is by using a callback, this is the prefered way. The second option is by polling for the status. Use this second option if you are on a platform where callbacks are not possible to perform.
The producer will need to enter the Google Public Key into game configuration form on Kongregate.com.
No special setup is required to use the iOS API.
Option 1) Using a callback:
[myPurchaseManger purchase:product withPurchaseHandler:^(SKPaymentTransaction* transaction){
[KongregateAPI.instance.mtx
verifyTransaction:transaction
completionHandler:^(BOOL valid) {
if (valid) {
//Since it is valid, track the purchase and give the goods
[KongregateAPI.instance.analytics
trackPurchase:transaction.payment.productIdentifier
withQuantity:transaction.payment.quantity
withGameFields:gameFields];
//give the user the goods
[myPurchaseManager productPurchased:product];
} else {
NSLog(@"Invalid transaction");
}
//Always finish the transaction
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}];
}];
Option 2) Polling the status:
NSString *newTransactionId = transaction.transactionId;
[KongregateAPI.instance.mtx verifyTransactionId:newTransactionId];
...
//check the status on our transaction id
NSString *status =[KongregateAPI.instance.mtx receiptVerificationStatus:newTransactionId];
if ([KONG_RECEIPT_VERIFICATION_STATUS_PROCESSING isEqualToString:status]) {
//the transaction status is still processing, check again later
} else {
//now that the processing is complete, we need to make sure we finish the transaction
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
if ([KONG_RECEIPT_VERIFICATION_STATUS_UNKNOWN isEqualToString:status]) {
//the transaction is not found, this is handled the same as invalid
}
if ([KONG_RECEIPT_VERIFICATION_STATUS_INVALID isEqualToString:status]) {
//the transaction is not valid
[KongregateAPI.instance.analytics finishPurchase:KONG_PURCHASE_RECEIPT_FAIL
withTransactionId:transaction.transactionIdentifier
withGameFields:@{ }];
}
if ([KONG_RECEIPT_VERIFICATION_STATUS_VALID isEqualToString:status]) {
//the transaction is valid
[KongregateAPI.instance.analytics finishPurchase:KONG_PURCHASE_SUCCESS
withTransactionId:transaction.transactionIdentifier
withGameFields:@{ @"hard_currency_change": @5 }];
//give the user the goods
[myPurchaseManager productPurchased:product];
}
}
Option 1) Using a callback:
// purchase is a Purchase object from the Google IABHelper sample code. mAPI
// is a reference to the KongregateAPI.
mAPI.mtx().verifyTransaction(purchase.getOriginalJson(), purchase.getSignature(),
new MicrotransactionServices.ReceiptVerificationListener() {
@Override
public void receiptVerificationComplete(boolean success) {
if (success) {
// The receipt is good award the goods
...
// notify the kong SDK to track the analytics
mAPI.analytics().finishPurchase(AnalyticsServices.IabResult.SUCCESS,
purchase.getOriginalJson(), purchaseEvent, purchase.getSignature());
// and consume. mHelper and mConsumeFinishedListener are also
// from the Google IABHelper sample code.
mHelper.consumeAsync(purchase, mConsumeFinishedListener)
} else {
// notify the Kong SDK the receipt validation failed.
mAPI.analytics().finishPurchase(AnalyticsServices.IabResult.RECEIPT_FAIL,
purchase.getOriginalJson(), purchase.getSignature());
// consume the purchase
mHelper.consumeAsync(purchase, mConsumeFinishedListener);
}
}
});
Option 2) Polling the status:
When a purchase is made, schedule the receipt validation.
// maintain a map of purchases pending receipt validation
private Map<String,Purchase> mPurchasesPendingRV = new HashMap<>();
// purchase is a Purchase object from the Google IABHelper sample code.
// mAPI is a reference to the KongregateAPI.
mPurchasesPendingRV.put(purchase.getOrderId(), purchase);
mAPI.mtx().verifyTransaction(purchase.getOriginalJson(), purchase.getSignature());
Listen and handle the receipt validation completion event.
// your method for handling Kongregate Events
private void handleEvent(String event)
if (KongregateEvent.RECEIPT_VERIFICATION_COMPLETE.equals(event)) {
handleReceiptVerificationComplete();
} else ... // handle other kongregaet events
}
// Method checks the status of pending purchases and handle the completed ones.
void handleReceiptVerificationComplete() {
List<String> orderIdsCompleted = new LinkedList<>();
for (String key : mPurchasesPendingRV.keySet()) {
MicrotransactionServices.ReceiptVerificationStatus rvStatus = mAPI.mtx().receiptVerificationStatus(key);
if (!MicrotransactionServices.ReceiptVerificationStatus.PROCESSING.equals(rvStatus)) {
orderIdsCompleted.add(key);
Purchase purchase = mPurchasesPendingRV.get(key);
if (!MicrotransactionServices.ReceiptVerificationStatus.INVALID.equals(rvStatus)) {
// receipt is not invalid, go ahead and award the currency or items
awardCurrency(purchase.getSku());
}
// always consume purchases once RV is complete, so uses can attempt to purchase
// the items again.
mHelper.consumeAsync(purchase, mConsumeFinishedListener);
}
}
// clear out pending list
for (String orderId : orderIdsCompleted) {
mPurchasesPendingRV.remove(orderId);
}
}
Unity only supports the polling method. See KongregateGameObject-Example.cs
included with the Kongregate unity package for sample code.