@chipmobilesdk/rn-payment 0.4.1 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,207 @@
1
1
  # @chipmobilesdk/rn-payment
2
2
 
3
+ ## 0.6.0
4
+
5
+ ### Minor Changes
6
+
7
+ - The engine is split by responsibility, and its internals are off the public type. ([#19](https://github.com/operationd8979/chip-mobile-sdk/pull/19))
8
+
9
+ - `PaymentEngine` no longer has `resolvedConfig`, `nextCorrelationId`, `emitter`,
10
+ `snapshot`, `commitSnapshot`, `mayCreateRealTransaction` or `recordBlockedOperation`.
11
+ They were documented as internal, but any consumer could call `commitSnapshot` and
12
+ grant itself an entitlement the store never sold, and a hand-written engine double had
13
+ to implement all of them.
14
+ - `entitlement_revoked` now carries the real reason: `validation_rejected` when your
15
+ backend said no, `consumed` for a consumed item, `expired` for a lapsed subscription,
16
+ and `revoked_by_store` only when the store stopped reporting the purchase. Before, every
17
+ revocation said `revoked_by_store`.
18
+ - `clearLocalData()` also drops cached product prices.
19
+ - A `not_ready` purchase result reports the engine's actual status instead of always
20
+ `connecting`. `usePurchase().purchase` takes a `ProductKey`, as it always accepted.
21
+ - Local storage moves to schema version 2: the never-written `entitlements` map and
22
+ `finalized` purchase state are gone. A version 1 record is migrated in place on first
23
+ load, so nothing needs to be done.
24
+ - Internally, `engine.ts` is 346 lines (was 921): state, connection and sync are their
25
+ own modules, and sync and the purchase flow build purchase records with one function.
26
+ The unused `runWithRetry`, `backoffSchedule`, `sourcesFor` and `entitlementsCoveredBy`
27
+ helpers are gone.
28
+
29
+ - The provider keeps one engine, stored records are checked one by one, and product
30
+ queries run in parallel. ([#19](https://github.com/operationd8979/chip-mobile-sdk/pull/19))
31
+
32
+ - `PaymentProvider` builds its engine once and keeps it across renders. Inline
33
+ `config` and `deps` objects no longer reconnect to the store on every render of your
34
+ root. A new engine is built only when the new `engineKey` prop changes (default:
35
+ `config.scopeToken`). It now works under React StrictMode, where it used to be left
36
+ holding a cleaned-up engine that never became ready. Pass `engine` to share an engine
37
+ you created and own; the provider then neither initializes nor cleans it up.
38
+ - `initialize()` on a cleaned-up engine reports `PAYMENT_NOT_CONFIGURED` even if the
39
+ engine had been ready, instead of answering `ok`.
40
+ - Every stored purchase record is checked field by field when read. A damaged one is
41
+ dropped and comes back with the next sync, rather than being trusted or taking the
42
+ rest down with it. Only a `purchased` record grants an entitlement; a state this
43
+ version does not know never does.
44
+ - A failed write to local storage is reported as an `internal_error` with
45
+ `PAYMENT_STORAGE_UNAVAILABLE` and operation `persist`, instead of being swallowed.
46
+ A custom `SnapshotStore.save` may return `false` to report one.
47
+ - One-time and subscription products are queried in parallel, identical requests in
48
+ flight share one store call, and `useProducts` never lets an older answer overwrite a
49
+ newer one. `status.blockedOperations` keeps the 20 most recent.
50
+
51
+ - `allowStoreTestPurchases` lets a debug build buy through the real store for Play
52
+ license testers. It is accepted only with `environment: 'test'`; any other environment
53
+ rejects it at startup with `PAYMENT_INVALID_CONFIG`. Without it, non-production
54
+ purchases are still refused as `environment_blocked`, as before. The README no longer
55
+ says the billing library is uninstalled or the adapter unverified, and describes what
56
+ adding iOS actually involves. ([#19](https://github.com/operationd8979/chip-mobile-sdk/pull/19))
57
+
58
+ ### Patch Changes
59
+
60
+ - Internal: no module in these packages is over 500 lines any more. The camera controller's session I/O moved to `sessionRuntime` and its contract to `contract.ts`; the Play adapter's shape translation to `iapShapes`; the purchase flow's failure mapping and its receipt-validation hook to modules of their own; the AI client's quota request to `quotaRequest`; the in-memory adapter's attachments and the fake ads engine's clock likewise. Behaviour is unchanged — the splits are by responsibility, and every package's suite passes as it stood. ([#19](https://github.com/operationd8979/chip-mobile-sdk/pull/19))
61
+
62
+ The repository also measures what it promised: `npm run perf` prints the median of nine runs of the deterministic hot paths against written budgets (recurrence expansion, theme construction, translation lookup, chart geometry, filter compilation), the demo app has a Performance screen reporting startup phases, time to interactive and the same benchmarks on Hermes, and `npm run check:file-size` keeps the limit from drifting. The budgets and the measured figures are recorded in `docs/standards.md` §VI.
63
+
64
+ ## 0.5.0
65
+
66
+ ### Minor Changes
67
+
68
+ - One diagnostic stream: install a sink with `installSdkSink` from
69
+ `@chipmobilesdk/rn-core` and it receives what every package observes (events,
70
+ failures, warnings) as `SdkDiagnostic`s, redacted. Listeners are notified through
71
+ rn-core, so a listener that throws no longer stops the others nor fails the call that
72
+ notified, and its error is reported rather than swallowed. ([#17](https://github.com/operationd8979/chip-mobile-sdk/pull/17))
73
+
74
+ - rn-ads: a banner's auto-refresh is a new cycle, so its `loaded` and `impression`
75
+ events are no longer deduplicated away; the dedupe memory is bounded. A throwing
76
+ status, consent or placement listener is reported as an `internal_error` event.
77
+ - rn-i18n: new `onDiagnostic` config receives every diagnostic (`I18nDiagnostic`,
78
+ with a stable `code`); without it, warnings go to `console.warn` in development
79
+ builds only. The warn-once memory is bounded.
80
+ - rn-chart: now depends on rn-core for its diagnostics.
81
+ - rn-payment: status, entitlement and pending listeners report a throw to the logger
82
+ instead of swallowing it.
83
+
84
+ - The package's errors extend `SdkError` from the new `@chipmobilesdk/rn-core`
85
+ dependency: `isSdkError` recognises them alongside every other package's, and each
86
+ carries `pkg`, `code` and `retryable`. Names and codes are unchanged. ([#17](https://github.com/operationd8979/chip-mobile-sdk/pull/17))
87
+
88
+ - rn-ai and rn-camera: `retryable` is the new name of `recoverable`, which stays as a
89
+ deprecated alias.
90
+ - rn-auth: `retryable` is true for `network-unavailable`. `redact` also removes URLs,
91
+ e-mail addresses and UUIDs.
92
+ - rn-ads: `redact` also removes JWTs and e-mail addresses, and reduces an `AdsError`
93
+ to its code.
94
+ - rn-ai: an error message quoted from the gateway is pattern-redacted.
95
+ - rn-camera: `redact` also removes JWTs, e-mail addresses and UUIDs.
96
+ - rn-theme: `ThemeConfigError` has `code: 'invalid_config'` and `ThemeContrastError`
97
+ `code: 'contrast'` (new `ThemeErrorCode` type).
98
+ - rn-i18n: `I18nConfigError` has `code: 'invalid_config'` and `UnitConversionError`
99
+ `code: 'unit_conversion'` (new `I18nErrorCode` type).
100
+
101
+ - Timers, retries and bounds now come from `@chipmobilesdk/rn-core`, and the drift
102
+ between the copies is fixed. ([#17](https://github.com/operationd8979/chip-mobile-sdk/pull/17))
103
+
104
+ - rn-ads: a retry policy out of bounds throws `retry_out_of_bounds`, as documented,
105
+ instead of `timeout_out_of_bounds`; a placement's `minRequestIntervalMs` out of
106
+ bounds throws `invalid_config`.
107
+ - rn-payment: backoff validates `initialDelayMs` (100 ms to 60 s, not above
108
+ `maxDelayMs`) and `factor` (1 to 10); `factor: 0` used to retry at once, forever.
109
+ Every timeout, backoff or interval out of bounds now throws the new
110
+ `PAYMENT_INVALID_CONFIG` instead of `PAYMENT_INVALID_CATALOG`, which is about
111
+ products.
112
+ - rn-ai: concurrent calls share one behavior-config request, so a cold start makes
113
+ one instead of one per call. A silent retry after a config reload waits
114
+ `recovery.retryBackoffMs`, which was parsed but never applied.
115
+
116
+ - Native peers load through `lazyPeer` from `@chipmobilesdk/rn-core`, on first use,
117
+ and a missing one is each package's own typed error. ([#17](https://github.com/operationd8979/chip-mobile-sdk/pull/17))
118
+
119
+ - rn-auth: creating `auth` no longer loads react-native-app-auth or
120
+ react-native-keychain; each loads on first use. New entrypoints
121
+ `@chipmobilesdk/rn-auth/app-auth` and `/keychain`. The default platform comes from
122
+ rn-core.
123
+ - rn-camera: a preview whose VisionCamera feed cannot load fails the session with
124
+ `cameraUnavailable` (new `controller.reportSurfaceUnavailable()`), instead of
125
+ showing black with no error. A missing VisionCamera module is `cameraUnavailable`.
126
+ - rn-notification: `createMmkvStore` reports a fallback to memory as
127
+ `STATE_STORE_DEGRADED` (to the SDK sinks and a new `onDegraded` option) and
128
+ exposes `durable`.
129
+ - rn-payment: an iOS app validates its catalog against the iOS product ids; the
130
+ default platform used to be Android everywhere. A missing react-native-iap is
131
+ `PAYMENT_UNAVAILABLE`.
132
+ - rn-theme: `buildThemes` runs without React Native (new `hairlineWidth` option) and
133
+ importing the package no longer loads Unistyles; `applyThemeMode` loads it on
134
+ first use.
135
+ - rn-ads, rn-local-db: the same loading, with the same error codes as before.
136
+
137
+ - Hooks read through `useSyncExternalStore` (rn-core's `useExternalStore`) and
138
+ re-render only when what they return changes. ([#17](https://github.com/operationd8979/chip-mobile-sdk/pull/17))
139
+
140
+ - rn-auth: `useProfile` and `useAccessTier` no longer re-render for a status change,
141
+ and the provider's context no longer changes on every transition. New
142
+ `useAuthSelector`. `auth.getState()` returns the same frozen object until the state
143
+ changes, instead of a copy per call.
144
+ - rn-payment: `useEntitlement` re-renders only when its own entitlement changes; the
145
+ engine keeps unchanged entitlement objects across syncs and does not notify for a
146
+ sync that changed nothing.
147
+ - rn-ads: `usePlacementState` and `useAdsStatus` read tear-free; `useAdsEvents` keeps
148
+ one subscription when given an inline listener.
149
+ - rn-i18n: `useTranslation` returns the same `t` until the locale changes, and
150
+ `useLocale`'s `setLocale` is stable.
151
+ - rn-ai: new `useAiBehaviorState`, with `client.subscribe` and a stable
152
+ `client.getBehaviorState()`.
153
+
154
+ - Every package's own copy is an rn-core message catalog that an app can translate. ([#17](https://github.com/operationd8979/chip-mobile-sdk/pull/17))
155
+
156
+ - rn-i18n: new `runtimeEntriesFromCatalog(catalog)` registers any SDK catalog in one
157
+ line, converting `{name}` variables to `{{name}}`.
158
+ - rn-ai: new `messageKeyForError(error)` gives the copy for every error code, and
159
+ `aiMessageSet`. The input placeholder default is neutral ("Type a message…"), and
160
+ two keys were added for errors that had none (`ai.error.signIn`, `ai.error.generic`).
161
+ - rn-auth: new `authMessages`, the catalog split into user and developer copy.
162
+ - rn-payment: new `paymentMessages`.
163
+ - rn-chart: summaries, announcements, adjustable-action labels, built-in state text
164
+ and legend hints come from `chartStrings`; pass `strings` to translate them. The
165
+ `accessibility.summaryFormatter` prop, declared before, now replaces the summary.
166
+
167
+ - Port conformance suites: one list of checks that a package's fake and its real adapter
168
+ both pass, so a test written against the fake proves something about the device. ([#17](https://github.com/operationd8979/chip-mobile-sdk/pull/17))
169
+
170
+ - rn-core: `@chipmobilesdk/rn-core/testing` adds `runChecks`, `expectConformance`,
171
+ `ensure` and `createFaultRegistry` (deterministic fault injection by named point).
172
+ - rn-camera: a `./testing` entrypoint with `runPermissionPortConformance`,
173
+ `runCameraEngineConformance` and the test adapter, which stays on the main
174
+ entrypoint too until the next major. `createTestCamera(...).prompts()` counts system
175
+ prompts.
176
+ - rn-payment: `runProviderPortConformance` in `./testing`, passed by the fake store and
177
+ the Play adapter.
178
+ - rn-ads: `runAdsEngineConformance` in `./testing`, passed by the fake engine and the
179
+ AdMob adapter.
180
+ - rn-auth: a `./testing` entrypoint with `createMemorySecureStore`,
181
+ `runSecureTokenStoreConformance` and `runOidcAuthClientConformance`, passed by the
182
+ in-memory doubles and the keychain and app-auth adapters.
183
+
184
+ - One app-lifecycle source, and a startup that needs no extra frame. ([#17](https://github.com/operationd8979/chip-mobile-sdk/pull/17))
185
+
186
+ - rn-core: `appLifecycle()`, a shared, reference-counted `AppState` subscription that
187
+ reports changes only (`active`, `inactive`, `background`), with `onForeground` for a
188
+ return from the background and `createManualLifecycle` in `./testing`.
189
+ - rn-camera, rn-ads and rn-notification hear the lifecycle through it instead of
190
+ subscribing to `AppState` each, so an app holds one native listener. The camera
191
+ controller's core layer no longer imports `react-native`.
192
+ - rn-payment: `syncOnForeground(engine)` and `<PaymentProvider syncOnForeground>` call
193
+ `handleForeground()` on each return from the background. Off by default; calling
194
+ `handleForeground()` yourself works as before.
195
+ - rn-i18n: `i18n.initSync()` resolves the locale before the first render when the
196
+ storage adapter has the new optional `getItemSync`, and returns false otherwise.
197
+ `localeStorageFromKeyValue(port)` builds such an adapter from rn-core's
198
+ `KeyValuePort` (MMKV).
199
+
200
+ ### Patch Changes
201
+
202
+ - Updated dependencies ([#17](https://github.com/operationd8979/chip-mobile-sdk/pull/17), [#17](https://github.com/operationd8979/chip-mobile-sdk/pull/17), [#17](https://github.com/operationd8979/chip-mobile-sdk/pull/17)):
203
+ - @chipmobilesdk/rn-core@0.1.0
204
+
3
205
  ## 0.4.1
4
206
 
5
207
  ### Patch Changes
package/README.md CHANGED
@@ -91,12 +91,9 @@ npm install react-native-iap react-native-mmkv react-native-nitro-modules
91
91
  > do **not** add it yourself. No runtime permission is requested and no sensitive
92
92
  > API is touched, so there is no permission prompt and no denial path to handle.
93
93
  >
94
- > **Verified state in this repository:** the app manifest at
95
- > `android/app/src/main/AndroidManifest.xml` declares `INTERNET`, `CAMERA`, and
96
- > notification permissions, and **no** `BILLING` entry — correct, because the
97
- > billing library is not installed yet. Once it is, confirm the merged manifest
98
- > contains exactly one `com.android.vending.BILLING` entry and no new runtime
99
- > permission:
94
+ > **Check your merged manifest** once `react-native-iap` is installed: it should
95
+ > contain exactly one `com.android.vending.BILLING` entry, added by the billing
96
+ > library, and no new runtime permission:
100
97
  >
101
98
  > ```bash
102
99
  > # After a debug build, inspect the merge report:
@@ -164,9 +161,13 @@ const config = {
164
161
  isProductionBuild: true,
165
162
  };
166
163
 
164
+ // Created once, outside the component: the store adapter holds the billing
165
+ // connection.
166
+ const deps = { provider: createStoreProvider() };
167
+
167
168
  export function App() {
168
169
  return (
169
- <PaymentProvider config={config} deps={{ provider: createStoreProvider() }}>
170
+ <PaymentProvider config={config} deps={deps}>
170
171
  <YourApp />
171
172
  </PaymentProvider>
172
173
  );
@@ -182,6 +183,13 @@ function AdSlot() {
182
183
  }
183
184
  ```
184
185
 
186
+ The provider builds its engine once and keeps it across renders; `config` and `deps`
187
+ are read when it is built. It builds a new one when `engineKey` changes (by default
188
+ `config.scopeToken`, so switching accounts gets a fresh engine), and it survives React
189
+ StrictMode in development. To own the engine yourself — create it at startup, share it
190
+ with code outside React — pass it instead: `<PaymentProvider engine={engine}>`. The
191
+ provider then neither initializes nor cleans it up.
192
+
185
193
  `createStoreProvider()` picks the platform adapter. On a platform with no adapter
186
194
  it returns a stub that reports `unavailable` instead of throwing, so your app
187
195
  still starts and runs — it only loses the ability to sell.
@@ -192,6 +200,13 @@ what "foreground" means for an app that may be backgrounded by its own modal, a
192
200
  biometric prompt, or the store's own purchase sheet. It is rate-limited
193
201
  regardless, so calling it liberally is safe.
194
202
 
203
+ If "back from the background" is what you mean, opt in instead of wiring `AppState`:
204
+ `<PaymentProvider syncOnForeground>` or `syncOnForeground(engine)` (returns an
205
+ unsubscribe). Both listen through rn-core's shared lifecycle port, so they add no
206
+ native subscription of their own, and a trip through `inactive` alone does not count.
207
+ On Android the Play purchase sheet does background the app, so a purchase is followed
208
+ by one rate-limited sync.
209
+
195
210
  ---
196
211
 
197
212
  ## Catalog and entitlement mapping
@@ -544,6 +559,20 @@ Two layers:
544
559
  recorded in `getStatus().blockedOperations`, and emitted as
545
560
  `environment_blocked` — observable in state, so a test can assert it rather
546
561
  than scraping logs.
562
+ 3. **License testing, opted in.** `environment: 'test'` with
563
+ `allowStoreTestPurchases: true` lets a debug build buy through the real store,
564
+ so a Play license tester can check a purchase without a release build from a
565
+ track. Any other environment rejects the option at construction. License
566
+ testers are not charged; anyone else signed in on the device would be.
567
+
568
+ ```ts
569
+ const config = {
570
+ // ...
571
+ environment: 'test',
572
+ isProductionBuild: false,
573
+ allowStoreTestPurchases: true, // license testers only
574
+ };
575
+ ```
547
576
 
548
577
  > **Why this blocks where `@chipmobilesdk/rn-ads` substitutes.** The ads package
549
578
  > swaps in Google's official *test ad units* when a non-production build supplies
@@ -604,7 +633,9 @@ import { DEFAULT_USER_MESSAGES, USER_MESSAGE_KEYS, isUserMessageKey } from '@chi
604
633
 
605
634
  User-actionable and developer copy are **separate maps**, so you cannot
606
635
  accidentally show "Payment catalog is invalid" to a customer. `isUserMessageKey()`
607
- lets you assert that at your own boundary.
636
+ lets you assert that at your own boundary. `paymentMessages` is the same catalog as
637
+ an rn-core message set; register the user copy with rn-i18n in one line:
638
+ `runtimeEntriesFromCatalog(paymentMessages.user)`.
608
639
 
609
640
  Message keys are public API; renaming one is a breaking change.
610
641
 
@@ -612,6 +643,9 @@ Message keys are public API; renaming one is a breaking change.
612
643
 
613
644
  ## Events and errors
614
645
 
646
+ Every error is a `PaymentError` (or `PaymentConfigError`) with a stable `code`, the
647
+ `operation` and a `retryable` flag. `PaymentError` extends `SdkError` from `@chipmobilesdk/rn-core`, so `isSdkError(error)` recognises it alongside every other package's errors, `error.pkg` names this package, and `error.redactedCause` (when present) is the underlying failure reduced to log-safe text.
648
+
615
649
  20 event types, each carrying a timestamp and — within a purchase cycle — one
616
650
  `correlationId`. The bar is that the event stream **alone** is enough to
617
651
  reconstruct a transaction's lifecycle, so "the user says they paid but has no
@@ -647,6 +681,11 @@ The failure goes to your `logger`, not back into the event stream.
647
681
  Errors carry a stable `code`, a log-safe `message`, the `operation`, a `retryable`
648
682
  flag, and a redacted `cause`. Error codes are public API.
649
683
 
684
+ A timeout, backoff or interval outside its bounds throws `PaymentConfigError` with
685
+ `PAYMENT_INVALID_CONFIG`, naming the field in `offenders`. Backoff checks every
686
+ field: `initialDelayMs` 100 ms to 60 s and not above `maxDelayMs`, `factor` 1 to
687
+ 10, `maxDelayMs` 5 s to 10 min, `maxAttempts` 1 to 10.
688
+
650
689
  ---
651
690
 
652
691
  ## Wiring an entitlement to another system
@@ -710,6 +749,12 @@ store.pushPurchaseUpdate(...); // deliver a settlement with no sync
710
749
  `./testing` is a separate entrypoint so test doubles cannot reach a production
711
750
  bundle.
712
751
 
752
+ `runProviderPortConformance(harness)` holds the checks every store adapter must pass:
753
+ connection and a dropped service, products with the store's own price, pending
754
+ purchases flagged rather than filtered, a bought and a cancelled flow, and
755
+ finalization that is safe twice. The package runs it against the fake store and the
756
+ Play adapter; an iOS adapter, or a device run from a demo screen, uses the same suite.
757
+
713
758
  ### Licence testers, for device verification
714
759
 
715
760
  1. Play Console → **Setup → Licence testing**, add tester accounts.
@@ -728,10 +773,8 @@ Exhaustively, for your Data safety declaration:
728
773
 
729
774
  | Stored | Why |
730
775
  |---|---|
731
- | Entitlement keys and their states | So entitlements are readable offline |
732
- | Granting product keys | So you can show the current plan |
733
- | Subscription expiry and renewal status | So lifecycle is readable offline |
734
- | Unfinished-transaction records (transaction id, product key, token, attempt count) | So a paid purchase is never lost to an app kill |
776
+ | Purchase records (transaction id, product key, product type, token, purchase time, store state, finalize state and attempt count, backend validation state) | So entitlements are readable offline and a paid purchase is never lost to an app kill. Entitlements themselves are derived from these, never stored |
777
+ | Subscription expiry and renewal status, on the purchase record | So lifecycle is readable offline |
735
778
  | Pending-purchase records (the same fields, plus the time the store reports the purchase was made) | So a payment still in flight is visible after an app kill. Unchanged since `0.1.0`; `0.2.0` only reads them |
736
779
  | Last successful sync time | So data age is observable |
737
780
  | Schema version | So the record can be migrated |
@@ -822,9 +865,11 @@ Your obligations as the app owner — this package cannot discharge them for you
822
865
  non-default policy needs server data.
823
866
  2. **iOS is contract-only.** `createStoreProvider()` returns an `unavailable` stub
824
867
  on iOS; the host app still starts and runs.
825
- 3. **The Android adapter is unverified on a device.** Only its purchase-state
826
- mapping has been checked against an installed binding. See
827
- [Compatibility](#compatibility).
868
+ 3. **Debug builds do not reach the store unless you opt in.** Outside production a
869
+ purchase reports `environment_blocked`. For a license tester on a debug build,
870
+ set `environment: 'test'` and `allowStoreTestPurchases: true`; only license
871
+ testers are not charged. See [Compatibility](#compatibility) for the devices and
872
+ binding the Android adapter was verified with.
828
873
  4. **Price-change confirmation is unsupported on Android.** Play notifies the user
829
874
  itself and offers no client-side flow, so `confirmPriceChange` reports
830
875
  `unsupported` rather than faking a confirmation.
@@ -840,7 +885,12 @@ Your obligations as the app owner — this package cannot discharge them for you
840
885
 
841
886
  ## Adding iOS later
842
887
 
843
- Implement `ProviderPort` for StoreKit. Nothing else changes.
888
+ Implement `ProviderPort` for StoreKit and return it from `createStoreProvider()` on
889
+ iOS. Your entitlement checks, hooks and purchase calls do not change. What an iOS
890
+ release adds: `storeProductIds.ios` in each catalog entry, the adapter's own
891
+ `capabilities()` (StoreKit can tell grace period from account hold, and supports
892
+ externally initiated purchases, so some states Android reports as `indeterminate`
893
+ become distinct), and its conformance run (`runProviderPortConformance`).
844
894
 
845
895
  | Play concept | Port concept | StoreKit |
846
896
  |---|---|---|
@@ -867,6 +917,10 @@ Local storage carries a schema version and a forward migration chain. A record
867
917
  that cannot be migrated is discarded and rebuilt from the store — safe, because
868
918
  discarding yields `unknown`, never a false `not_entitled`.
869
919
 
920
+ Schema version 2 (0.6.0) drops two things version 1 stored and never used: an
921
+ empty `entitlements` map and a `finalized` purchase state nothing set. A version 1
922
+ record is migrated in place on first load.
923
+
870
924
  ### Migrating `0.1.0` to `0.2.0`
871
925
 
872
926
  **One union gained a member**, so an exhaustive `switch` over it needs the new
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chipmobilesdk/rn-payment",
3
- "version": "0.4.1",
3
+ "version": "0.6.0",
4
4
  "private": false,
5
5
  "license": "UNLICENSED",
6
6
  "description": "Reusable in-app payment and entitlement capability for ChipMobileSdk React Native apps",
@@ -27,9 +27,12 @@
27
27
  ],
28
28
  "sideEffects": false,
29
29
  "scripts": {
30
- "typecheck": "tsc -p tsconfig.json --noEmit",
30
+ "typecheck": "tsc -p tsconfig.json && tsc -p tsconfig.test.json",
31
31
  "pack:check": "npm pack --dry-run"
32
32
  },
33
+ "dependencies": {
34
+ "@chipmobilesdk/rn-core": "^0.1.0"
35
+ },
33
36
  "peerDependencies": {
34
37
  "react": ">=19",
35
38
  "react-native": ">=0.85",
@@ -68,10 +68,10 @@ rule: a chargeable purchase is permitted only when `environment: 'production'` *
68
68
  `PAYMENT_PRODUCTION_CONFIG_IN_NON_PRODUCTION_BUILD` at construction and never connects.
69
69
 
70
70
  Consequence worth knowing before you plan device testing: **licence-tester purchases are
71
- real Play transactions**, so verifying them needs a release-configuration build
72
- (`isProductionBuild: !__DEV__`, `environment: 'production'`) installed from a Play track.
73
- Debug builds are structurally unable to reach the store. Drive every behaviour that is
74
- not "does Play accept this" through the fake store instead. See
71
+ real Play transactions**. A debug build reaches the store only with `environment: 'test'`
72
+ and `allowStoreTestPurchases: true` (licence testers are not charged; anyone else would
73
+ be); otherwise it gets `environment_blocked`. Drive every behaviour that is not "does
74
+ Play accept this" through the fake store instead. See
75
75
  `## Environments and the production guard`.
76
76
 
77
77
  Unlike `@chipmobilesdk/rn-ads`, there is **no test-id substitution**: Play has no test
@@ -91,6 +91,8 @@ Unlike `@chipmobilesdk/rn-ads`, there is **no test-id substitution**: Play has n
91
91
 
92
92
  Call `handleForeground()` yourself on app resume — the package will not guess what
93
93
  "foreground" means for a host backgrounded by its own modal or the store's purchase sheet.
94
+ When a return from the background is enough, opt in with `<PaymentProvider
95
+ syncOnForeground>` or `syncOnForeground(engine)` (rn-core's shared lifecycle port).
94
96
 
95
97
  ## Public API
96
98
 
@@ -103,8 +105,8 @@ environment; everything else returns a typed result) · `createStoreProvider` ·
103
105
  `DEFAULT_USER_MESSAGES`, `DEFAULT_DEVELOPER_MESSAGES`, `USER_MESSAGE_KEYS`,
104
106
  `DEVELOPER_MESSAGE_KEYS`, `isUserMessageKey` · timeout/retry/cache defaults and bounds
105
107
  (`DEFAULT_STALE_AFTER_MS`, `DEFAULT_PURCHASE_LAUNCH_TIMEOUT_MS`, `STALE_AFTER_BOUNDS`, …)
106
- · `createFakeStore`, `makePurchase` from `@chipmobilesdk/rn-payment/testing` (test
107
- doubles only).
108
+ · `createFakeStore`, `makePurchase`, `runProviderPortConformance` from
109
+ `@chipmobilesdk/rn-payment/testing` (test doubles and the store-adapter suite only).
108
110
 
109
111
  Engine: `initialize`, `cleanup`, `getStatus`, `subscribeStatus`, `getEntitlement`,
110
112
  `getAllEntitlements`, `subscribeEntitlements`, `getPendingPurchases`,