Never grant paid access based on a boolean returned by the client. A jailbroken device, a tampered build, or a man-in-the-middle can fake it. Authorize against a validated receipt or a verified webhook.
Receipt validation
Validate App Store and Google Play purchases server-side, protect against replay and fraud, and ingest store notifications.
A purchase isn't real until it's verified against the store. Client SDKs report a purchase for a snappy UI, but the authoritative grant comes from server-side validation. CashSDK validates every receipt for you and keeps entitlements in sync.
Why validate server-side#
- Authenticity — confirm the receipt was really issued by Apple or Google.
- Replay protection — reject receipts that have already been redeemed or reused across accounts.
- Fraud signals — surface refunds, revocations, sandbox receipts sent to production, and mismatched bundle ids.
- Truth over time — renewals, cancellations, and refunds change validity after the sale; only the server sees them.
Validate a receipt#
The SDK obtains the store receipt (Apple) or purchase token (Google) during checkout. Send it to your backend, or let the SDK forward it automatically.
Your backend posts the receipt with the customer's app_user_id.
curl https://api.cashsdk.com/v1/receipts/validate \
-H "Authorization: Bearer csk_sk_..." \
-H "Content-Type: application/json" \
-d '{
"platform": "apple",
"receipt": "MII...",
"app_user_id": "u_812"
}'
CashSDK returns the validated entitlements. Grant access from these, not from the client.
{
"valid": true,
"app_user_id": "u_812",
"entitlements": {
"pro": {
"active": true,
"product_id": "pro_monthly",
"expires_at": "2026-08-17T00:00:00Z",
"store": "app_store",
"environment": "production"
}
},
"transaction": {
"id": "txn_9ab21",
"original_transaction_id": "1000000812",
"is_trial": false
},
"fraud": {
"replayed": false,
"sandbox_in_production": false,
"score": 0.02
}
}
platformstringrequired"apple" or "google". Determines how the receipt is verified upstream.
receiptstringrequiredThe base64 App Store receipt or the Google Play purchase token.
app_user_idstringrequiredYour stable identifier for the customer. Ties the purchase to a customer so entitlements sync across devices and platforms.
validbooleanWhether the receipt authenticated and is not replayed or revoked.
entitlementsobjectMap of entitlement id to its active state, product, and expiration.
fraudobjectSignals such as replay detection, sandbox-in-production, and a risk score.
Signature & replay protection#
CashSDK verifies each receipt's cryptographic signature with Apple and Google,
then records its transaction id. A receipt (or its original_transaction_id)
can only ever be bound to one customer — a second attempt on a different
app_user_id is rejected as a replay and flagged in the fraud block.
Ingest store notifications#
Validation is a point-in-time snapshot. To stay current as renewals and refunds happen, CashSDK ingests App Store Server Notifications V2 and Google Play Real-time Developer Notifications. Point both at your CashSDK instance:
- Apple — set the Server Notification URL in App Store Connect to your instance's notification endpoint.
- Google — configure a Pub/Sub topic for Real-time Developer Notifications linked to the same instance.
CashSDK verifies each notification, updates the customer's entitlement, and
emits the matching webhook event to your backend. Test the
Apple path with the MCP request_apple_test_notification tool — see
Testing.