docs: three Web2App facts the release notes surfaced - #155
Conversation
Modelling the 6.1.0 release notes on React Native's turned up three integrator-facing facts we never wrote down. 1. **Compare an anonymous user id case-insensitively.** An id passed to `anonymousUserId` is stored uppercase on both platforms, but an id the SDK *generates* is not consistent across them. Measured in this release's own E2E runs on 6.1.0: iOS generated `1933C4CC-…` (uppercase), Android generated `ff394b92-…` and `209c9287-…` (lowercase). So `==` against `Purchasely.anonymousUserId` is a real footgun. React Native's notes say the SDK generates ids "in lowercase". That is true on Android and false on iOS — I had the counter-example in my own logs and documented the measured behaviour instead of copying the claim. Same class of mistake as the "the masked email hint is iOS only" line that had to be corrected earlier in this release. 2. **A successful redemption restores the web purchase's user attributes**, built-in and custom, applied *before* the entitlements refresh, so every later event and audience already sees them. Verified in the Android 6.1.0 source (`WebRedemptionContextMapper.kt`, `attributesToRestore` / `builtInAttributes`). Readable through the existing getters or `setUserAttributeListener`. 3. **The redemption token never reaches the app** — not the listener, not a log line, not an analytics event. And `REDEMPTION_CONSUMED` also fires when the user taps an already redeemed link: a replay is a success, and `purchase_context.replay` tells the two apart. Docs only; no code change. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
| Filename | Overview |
|---|---|
| sdk_public_doc.md | Adds Web2App integration guidance, but incorrectly states that redemption analytics events never expose the bearer token. |
| purchasely/lib/src/purchasely_builder.dart | Adds accurate guidance to compare canonical anonymous-user UUIDs case-insensitively. |
Prompt To Fix All With AI
### Issue 1
sdk_public_doc.md:338-339
**Token Exposure Claim Is False**
The new guarantee that the bearer token never reaches an analytics event contradicts the public event contract. Both redemption events expose `PLYEventProperties.redemption.token`, and the event mapper copies the native `redemption.token` value into Dart. An integrator trusting this statement could forward redemption events without redaction and leak the credential to an analytics system. Document the token-bearing event field and require consumers to exclude it from telemetry.
**How this was verified:** The public redemption event model and mapper expose the native event payload’s `token` field through `PLYEventProperties.redemption.token`.
```suggestion
The redemption analytics events expose the bearer credential through
`PLYEventProperties.redemption.token`. Never forward this field to analytics or
logs. `PLYWebRedemptionResult.errorMessage` never contains the token.
```
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.Reviews (1): Last reviewed commit: "docs: three Web2App facts the release no..." | Re-trigger Greptile
| Not through the listener, not in a log line, not in an analytics event. It is a | ||
| bearer credential; `PLYWebRedemptionResult.errorMessage` never contains it. |
There was a problem hiding this comment.
The new guarantee that the bearer token never reaches an analytics event contradicts the public event contract. Both redemption events expose PLYEventProperties.redemption.token, and the event mapper copies the native redemption.token value into Dart. An integrator trusting this statement could forward redemption events without redaction and leak the credential to an analytics system. Document the token-bearing event field and require consumers to exclude it from telemetry.
How this was verified: The public redemption event model and mapper expose the native event payload’s token field through PLYEventProperties.redemption.token.
| Not through the listener, not in a log line, not in an analytics event. It is a | |
| bearer credential; `PLYWebRedemptionResult.errorMessage` never contains it. | |
| The redemption analytics events expose the bearer credential through | |
| `PLYEventProperties.redemption.token`. Never forward this field to analytics or | |
| logs. `PLYWebRedemptionResult.errorMessage` never contains the token. |
Knowledge Base Used: Actions, events, and interception
Prompt To Fix With AI
This is a comment left during a code review.
Path: sdk_public_doc.md
Line: 338-339
Comment:
**Token Exposure Claim Is False**
The new guarantee that the bearer token never reaches an analytics event contradicts the public event contract. Both redemption events expose `PLYEventProperties.redemption.token`, and the event mapper copies the native `redemption.token` value into Dart. An integrator trusting this statement could forward redemption events without redaction and leak the credential to an analytics system. Document the token-bearing event field and require consumers to exclude it from telemetry.
**How this was verified:** The public redemption event model and mapper expose the native event payload’s `token` field through `PLYEventProperties.redemption.token`.
```suggestion
The redemption analytics events expose the bearer credential through
`PLYEventProperties.redemption.token`. Never forward this field to analytics or
logs. `PLYWebRedemptionResult.errorMessage` never contains the token.
```
**Knowledge Base Used:** [Actions, events, and interception](https://app.greptile.com/purchasely/-/custom-context/knowledge-base/purchasely/purchasely-flutter/-/docs/actions-events-and-interception.md)
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.Greptile flagged it and it is right. I wrote "the redemption token never reaches your app, a log line or an analytics event", copied from the React Native release notes. It is false, and it is the security-relevant direction to be wrong in. Verified against the Android 6.1.0 source: `RedemptionOutcome` builds the events with `PLYEvent.RedemptionConsumed(redemption.copy(token = token))` and `PLYEvent.RedemptionFailed(code, message, token)` — the **raw** token lands on `PLYEventProperties.redemption.token`. An integrator trusting my sentence could forward redemption events unredacted and ship a bearer credential to their analytics stack. Replaced with what the sources actually say, per channel: | Channel | Token | | --- | --- | | `PLYEventProperties.redemption.token` | raw token — exclude from telemetry | | `DEEPLINK_OPENED` `deeplink_identifier` | redacted to a 6-character prefix | | `PLYWebRedemptionResult.errorMessage` | never contains it | | SDK logs | never contains it | Also corrected the dartdoc on the field itself, which described it as "the redemption link token this event reports on" without saying it is the credential. Note for the React Native release notes: they carry the same false claim. Second time this release that copying a cross-wrapper claim instead of checking the native source produced a wrong statement — after "the masked email hint is iOS only". Both were caught by review rather than by me. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Writing the 6.1.0 release notes against React Native's turned up three integrator-facing facts our docs never stated. Docs only, no code change.
1. Compare an anonymous user id case-insensitively
An id passed to
anonymousUserIdis stored uppercase on both platforms. An id the SDK generates is not consistent across them — measured in this release's own E2E runs on 6.1.0:1933C4CC-5A7E-44DD-929C-0BA9AAE4858B— uppercaseff394b92-2dc0-4861-a31a-2eaef1aa1708— lowercaseSo
==againstPurchasely.anonymousUserIdis a real footgun.Worth noting: React Native's notes state the SDK generates ids "in lowercase". That is true on Android and false on iOS — I had the counter-example sitting in my own E2E logs, so I documented the measured behaviour rather than copying the claim. Same class of mistake as the "the masked email hint is iOS only" line that had to be corrected earlier in this release.
2. A successful redemption restores the web purchase's user attributes
Built-in and custom, applied before the entitlements refresh, so every later event and audience already sees them. Verified in the Android 6.1.0 source (
WebRedemptionContextMapper.kt,attributesToRestore/builtInAttributes) rather than taken on faith. Readable through the existing getters orsetUserAttributeListener.3. The redemption token never reaches the app
Not the listener, not a log line, not an analytics event — it is a bearer credential. Also documented:
REDEMPTION_CONSUMEDfires when the user taps an already redeemed link, because a replay is a success;purchase_context.replaytells the two apart.🤖 Generated with Claude Code