Skip to main content
Question

How does RevenueCat handle Google Play's lack of fresh signature proof (vs Apple's JWS signedDate)?

  • February 1, 2026
  • 1 reply
  • 66 views

Forum|alt.badge.img

Hello RC community,

I need help understand how Apple vs Google handle signed purchase data. I ran into something that I can't figure out and may be getting wrong.
 

The query:

With Apple's StoreKit 2, when you grab `transaction.jwsRepresentation`, you get `signedDate` field that's generated fresh every time you request it(I assume). So if someone intercepts a JWS token, it goes stale in minutes. Good for security.

But for Google Play Billing, the signature is created once when the purchase happens. `purchaseTime` is static, there's no equivalent to `signedDate`. The same signature comes back every single time you query it(I assume).
 

What I'm wondering:

1. Am I understanding this correctly? Google really doesn't have a freshness mechanism?

2. How does RC deal with this for things like:
   - Detecting stolen/leaked purchase tokens
   - Account transfers  
   - Replay attacks

3. Is this just a known limitation everyone accepts, or is there some workaround I'm missing?
 

Stuff I've already looked at:

- `obfuscatedAccountId` — set at purchase time, doesn't help prove current ownership
- Server-side token validation — tells you the purchase is real, not who's sending it
- Play Integrity API — proves the device/app is legit, not the purchase ownership

This post has been closed for comments

1 reply

matt-heaney
RevenueCat Staff
Forum|alt.badge.img+3
  • RevenueCat Staff
  • February 4, 2026

Hey Deepanshu,

 

Matt from RevenueCat here! Thanks for the question!

 

It’s true that Apple and Google handle purchase data differently in some areas, but RevenueCat handles the heavy lifting for you and gives you a range of options to control what happens in cases such as stolen tokens, account transfers, and replay-style attacks.

 

Let’s break this down.

 

RevenueCat gives you explicit control over how account transfers are handled through your restore behavior configuration. You can choose how RevenueCat should behave when the same underlying purchase is restored or seen under a different App User ID. The most common behaviors here are Transfer to new App User ID and Keep with original App User ID.

 

The default behavior is Transfer to new App User ID, which transfers the purchase between identified users if needed and ensures that only one customer at a time can have access. For example, if User A buys a subscription and User B logs in on the same device and restores, access moves to User B and is revoked from User A.

 

With Keep with original App User ID (we recommend using this with caution), RevenueCat will return an error if a different App User ID attempts to restore or purchase using an existing underlying purchase. This is intended for apps that require an account before purchase and can support account recovery flows.

 

For stolen or leaked purchase data, RevenueCat treats each purchase as belonging to a single App User ID at a time. If the same underlying purchase later appears under a different App User ID, RevenueCat won’t silently grant access to both. Instead, what happens is controlled by your restore behavior: RevenueCat will either transfer ownership to the new App User ID or return an error and keep it with the original App User ID.

 

This same ownership enforcement also protects against replay-style attacks. Simply resending previously seen purchase data can’t be used to grant access to an additional user, because the purchase is always evaluated against its existing owner and your configured restore behavior.

 

You can read more about restore behaviors here: https://www.revenuecat.com/docs/projects/restore-behavior

 

Hope this helps!