Applies to: iOS / iTunes purchases using brainCloud's appStore.verifyPurchase API
Symptom: APP_WARNING — "No items were processed in transaction [storeId: itunes]"
Overview
When a player completes an in-app purchase on iOS, your game client sends the Apple receipt to brainCloud for verification via appStore.verifyPurchase. brainCloud forwards that receipt to Apple's validation server and processes the response.
In some cases — even though the purchase succeeded on Apple's side — brainCloud logs an APP_WARNING and awards nothing. This article explains why that happens and how to prevent it.
How Receipt Validation Works
Player completes a purchase in the iOS app
Apple generates a receipt and stores it on the device
Your client reads the receipt and sends it to brainCloud
brainCloud forwards the receipt to Apple's validation server
Apple returns a JSON response
brainCloud reads the
in_apparray to identify purchased itemsbrainCloud awards items and currencies defined in your product catalog
Step 6 is where things can go wrong. If in_app is empty, brainCloud has nothing to process and logs the warning.
What the Warning Looks Like
You'll find this entry under App > Global > Logs > Recent Errors:
{
"type": "APP_WARNING",
"service": "appStore",
"operation": "VERIFY_PURCHASE",
"message": "No items were processed in transaction [storeId: itunes]",
"context": {
"resultCode": 0,
"transactionSummary": {
"transactionDetails": [],
"unprocessedCount": 0,
"processedCount": 0
},
"rewards": {}
}
}
Notice that resultCode is 0. From Apple's perspective the purchase succeeded — but brainCloud received an incomplete receipt and could not process it.
Why Is in_app Empty?
Apple's validation response includes two fields that can contain purchase data:
Field | What it contains |
| Purchases present in the receipt that was submitted |
| All recent purchases tied to the Apple ID, fetched fresh from Apple's servers |
brainCloud reads in_app. When in_app is empty but latest_receipt_info contains the purchase, the submitted receipt is stale — it was generated before the purchase took place.
Common Causes
1. Stale or cached receipt
The most frequent cause. The client sent a receipt that predates the purchase. Apple's in_app array only includes transactions that occurred after the receipt was created.
How to spot it: Compare receipt_creation_date to purchase_date from latest_receipt_info. If the receipt was created before the purchase, it's stale.
"receipt_creation_date": "2026-04-04 21:49:24 Etc/GMT" ← receipt created Apr 4 "purchase_date": "2026-04-14 19:49:15 Etc/GMT" ← purchase made Apr 14
A 10-day-old receipt cannot contain a purchase made today.
2. Receipt read before StoreKit finishes processing
After SKPaymentQueue delivers the transaction, the app must wait for StoreKit to fully process it, then fetch a fresh receipt before calling brainCloud. Reading the receipt too early can result in an empty in_app array.
3. Receipt already validated or consumed
Apple may remove entries from in_app after repeated validations or after acknowledgement. The transaction then appears only in latest_receipt_info as a historical record.
Does latest_receipt_info Belong to the Correct Player?
latest_receipt_info reflects purchases made by the Apple ID that signed the receipt — not necessarily the brainCloud player who submitted the request. You cannot confirm a match from receipt data alone.
To verify identity:
Look up the player by profile ID in User > User Browser
Confirm they have an iTunes identity linked to their brainCloud account
Cross-reference the
transaction_idin App > Global > Marketplace > Transactions
If transaction_id equals original_transaction_id, it's a first-time purchase. If they differ, it's a restored purchase.
Impact on POST Hook Scripts
If you have a POST API hook on appStore.VERIFY_PURCHASE, when this warning occurs:
transactionSummary.transactionDetailswill be an empty array[]rewardswill be an empty object{}resultCodewill still be0
Guard against this in your hook script to avoid null reference errors:
var transactionDetails = msg.transactionSummary.transactionDetails;
if (transactionDetails && transactionDetails.length > 0) {
// process items
} else {
// nothing was processed — handle gracefully
}
How to Fix It
The fix is entirely on the client side: always send a fresh receipt after a purchase completes.
Recommended iOS client flow
SKPaymentQueuedelivers the transaction (transactionState == .purchased)Call
SKReceiptRefreshRequestto fetch a fresh receipt from AppleWait for the refresh callback to complete
Read the receipt from
Bundle.main.appStoreReceiptURLBase64-encode the receipt data
Send to brainCloud via
appStore.verifyPurchaseCall
SKPaymentQueue.default().finishTransaction()only after brainCloud confirms
Rules to follow
Never use a cached receipt for purchase verification
Always refresh the receipt after StoreKit delivers a new transaction
Always call
finishTransactiononly after brainCloud has confirmed the purchase
Summary
Detail |
|
Warning message |
|
Root cause | The |
Why it's empty | A stale or cached receipt was submitted; the purchase only appears in |
brainCloud behavior | Logs |
Fix | Refresh the receipt client-side after every purchase before calling |
Hook script impact | Guard against empty |
