Back to the RevenueCat homepage
RevenueCat SDKGoogle Play Billing

Chapter 15: Security

Building a secure purchase system from scratch means implementing server-side receipt verification, protecting against client-side tampering, validating purchase tokens, and ensuring the client never makes its own access decisions.

With RevenueCat, server-side verification is automatic, every purchase is verified before awaitPurchase() returns. This chapter covers the additional security features RevenueCat provides.

Trusted Entitlements (Response Verification)

The default configuration trusts CustomerInfo responses from RevenueCat's backend without cryptographic verification. An attacker with network access could theoretically intercept and modify the response.

Enable response signature verification to prevent this:

kotlin
PurchasesConfiguration.Builder(context, apiKey)
    .entitlementVerificationMode(EntitlementVerificationMode.INFORMATIONAL)
    .build()

In INFORMATIONAL mode, RevenueCat signs every CustomerInfo response. The SDK verifies the signature and attaches the result to EntitlementInfos.verification:

kotlin
val verification = customerInfo.entitlements.verification
when (verification) {
    VerificationResult.VERIFIED -> { /* response is authentic */ }
    VerificationResult.FAILED -> { /* possible tampering, log and alert */ }
    VerificationResult.NOT_REQUESTED -> { /* verification disabled */ }
    VerificationResult.VERIFIED_ON_DEVICE -> { /* verified locally */ }
}

In INFORMATIONAL mode, a failed verification is logged but does not block access. Change to ENFORCED to block access on verification failure:

kotlin
.entitlementVerificationMode(EntitlementVerificationMode.ENFORCED)

In ENFORCED mode, EntitlementInfo.isActive returns false for any response that fails signature verification. This is the highest client-side security setting, but be aware that legitimate network issues (proxies, some VPNs) could cause verification failures for real users.

The Server Is Always the Authority

Even with EntitlementVerificationMode.ENFORCED, do not make server-side access decisions based solely on what the client reports. For any API that serves premium content, verify entitlement using the RevenueCat REST API or your own database updated by webhooks.

API Key Security

Your RevenueCat Android API key is embedded in your app binary. This is expected, it is a public API key that allows reading and purchasing but not administrative operations. Guard your secret API key (used for the REST API) on your server and never embed it in the client.

Anonymous Users

RevenueCat's anonymous user IDs ($RCAnonymousID:...) are generated on the device and are not secret. They are simply identifiers, not credentials. If a user shares their device, both users can read the anonymous purchase history. For production apps with real users, always identify users with your own authenticated user IDs by calling Purchases.sharedInstance.logIn("your_user_id").

Purchase Token Security

The purchase token is posted to RevenueCat's backend immediately after the purchase. If the network call fails, the SDK retries on the next app launch. The token is verified against Google's servers by RevenueCat, a fabricated or reused token will fail verification and not grant entitlements.

What RevenueCat Handles

  • Receipt validation against Google Play Developer API on every purchase
  • Protection against token reuse (RevenueCat deduplicates tokens)
  • Cryptographic response signing (INFORMATIONAL / ENFORCED modes)
  • Secure transmission to RevenueCat backend (HTTPS)

What You Handle

  • Identifying users with authenticated IDs instead of relying on anonymous IDs
  • Server-side entitlement checks using RevenueCat REST API for premium content
  • Protecting your RevenueCat secret API key on your server

Prefer building from scratch?

The Google Play Billing Handbook covers the same topics with raw BillingClient, Developer API, and RTDNs.

Related chapters

  • Chapter 9: Backend Architecture

    You don't need to build a verification server. RevenueCat already is your verification server.

    Learn more
  • Chapter 5: Configuring the SDK

    A single configure() call replaces the entire connection lifecycle, reconnection, and sync logic.

    Learn more
  • Chapter 6: The Purchase Flow

    awaitPurchase() handles the complete billing flow, verification, and acknowledgement internally.

    Learn more
Security | RevenueCat