@chipmobilesdk/rn-payment 0.5.0 → 0.7.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 +172 -0
- package/README.md +47 -16
- package/lib/commonjs/adapter/createStoreProvider.js +110 -0
- package/lib/commonjs/adapter/createStoreProvider.js.map +1 -0
- package/lib/commonjs/adapter/iap-adapter.js +393 -0
- package/lib/commonjs/adapter/iap-adapter.js.map +1 -0
- package/lib/commonjs/adapter/iapShapes.js +169 -0
- package/lib/commonjs/adapter/iapShapes.js.map +1 -0
- package/lib/commonjs/adapter/port.js +6 -0
- package/lib/commonjs/adapter/port.js.map +1 -0
- package/lib/commonjs/config/types.js +2 -0
- package/lib/commonjs/config/types.js.map +1 -0
- package/lib/commonjs/config/validate.js +181 -0
- package/lib/commonjs/config/validate.js.map +1 -0
- package/lib/commonjs/core/mutex.js +31 -0
- package/lib/commonjs/core/mutex.js.map +1 -0
- package/lib/commonjs/core/retry.js +37 -0
- package/lib/commonjs/core/retry.js.map +1 -0
- package/lib/commonjs/core/timers.js +207 -0
- package/lib/commonjs/core/timers.js.map +1 -0
- package/lib/commonjs/diagnostics/errors.js +83 -0
- package/lib/commonjs/diagnostics/errors.js.map +1 -0
- package/lib/commonjs/diagnostics/events.js +126 -0
- package/lib/commonjs/diagnostics/events.js.map +1 -0
- package/lib/commonjs/diagnostics/messageKeys.js +70 -0
- package/lib/commonjs/diagnostics/messageKeys.js.map +1 -0
- package/lib/commonjs/engine/connection.js +189 -0
- package/lib/commonjs/engine/connection.js.map +1 -0
- package/lib/commonjs/engine/engine.js +279 -0
- package/lib/commonjs/engine/engine.js.map +1 -0
- package/lib/commonjs/engine/finalize.js +325 -0
- package/lib/commonjs/engine/finalize.js.map +1 -0
- package/lib/commonjs/engine/foreground.js +26 -0
- package/lib/commonjs/engine/foreground.js.map +1 -0
- package/lib/commonjs/engine/internals.js +31 -0
- package/lib/commonjs/engine/internals.js.map +1 -0
- package/lib/commonjs/engine/pending.js +102 -0
- package/lib/commonjs/engine/pending.js.map +1 -0
- package/lib/commonjs/engine/products.js +198 -0
- package/lib/commonjs/engine/products.js.map +1 -0
- package/lib/commonjs/engine/purchase.js +395 -0
- package/lib/commonjs/engine/purchase.js.map +1 -0
- package/lib/commonjs/engine/purchaseFailure.js +77 -0
- package/lib/commonjs/engine/purchaseFailure.js.map +1 -0
- package/lib/commonjs/engine/receiptValidation.js +90 -0
- package/lib/commonjs/engine/receiptValidation.js.map +1 -0
- package/lib/commonjs/engine/state.js +201 -0
- package/lib/commonjs/engine/state.js.map +1 -0
- package/lib/commonjs/engine/subscriptions.js +122 -0
- package/lib/commonjs/engine/subscriptions.js.map +1 -0
- package/lib/commonjs/engine/sync.js +163 -0
- package/lib/commonjs/engine/sync.js.map +1 -0
- package/lib/commonjs/entitlements/aggregate.js +212 -0
- package/lib/commonjs/entitlements/aggregate.js.map +1 -0
- package/lib/commonjs/entitlements/mapping.js +41 -0
- package/lib/commonjs/entitlements/mapping.js.map +1 -0
- package/lib/commonjs/entitlements/policy.js +110 -0
- package/lib/commonjs/entitlements/policy.js.map +1 -0
- package/lib/commonjs/index.js +266 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/commonjs/package.json +1 -0
- package/lib/commonjs/persistence/mmkv-store.js +149 -0
- package/lib/commonjs/persistence/mmkv-store.js.map +1 -0
- package/lib/commonjs/persistence/snapshot.js +229 -0
- package/lib/commonjs/persistence/snapshot.js.map +1 -0
- package/lib/commonjs/react/PaymentProvider.js +96 -0
- package/lib/commonjs/react/PaymentProvider.js.map +1 -0
- package/lib/commonjs/react/hooks.js +202 -0
- package/lib/commonjs/react/hooks.js.map +1 -0
- package/lib/commonjs/testing/conformance.js +136 -0
- package/lib/commonjs/testing/conformance.js.map +1 -0
- package/lib/commonjs/testing/fakeStore.js +281 -0
- package/lib/commonjs/testing/fakeStore.js.map +1 -0
- package/lib/commonjs/testing/index.js +45 -0
- package/lib/commonjs/testing/index.js.map +1 -0
- package/lib/module/adapter/createStoreProvider.js +106 -0
- package/lib/module/adapter/createStoreProvider.js.map +1 -0
- package/lib/module/adapter/iap-adapter.js +383 -0
- package/lib/module/adapter/iap-adapter.js.map +1 -0
- package/lib/module/adapter/iapShapes.js +160 -0
- package/lib/module/adapter/iapShapes.js.map +1 -0
- package/lib/module/adapter/port.js +4 -0
- package/lib/module/adapter/port.js.map +1 -0
- package/lib/module/config/types.js +2 -0
- package/lib/module/config/types.js.map +1 -0
- package/lib/module/config/validate.js +175 -0
- package/lib/module/config/validate.js.map +1 -0
- package/lib/module/core/mutex.js +22 -0
- package/lib/module/core/mutex.js.map +1 -0
- package/lib/module/core/retry.js +33 -0
- package/lib/module/core/retry.js.map +1 -0
- package/lib/module/core/timers.js +198 -0
- package/lib/module/core/timers.js.map +1 -0
- package/lib/module/diagnostics/errors.js +76 -0
- package/lib/module/diagnostics/errors.js.map +1 -0
- package/lib/module/diagnostics/events.js +117 -0
- package/lib/module/diagnostics/events.js.map +1 -0
- package/lib/module/diagnostics/messageKeys.js +64 -0
- package/lib/module/diagnostics/messageKeys.js.map +1 -0
- package/lib/module/engine/connection.js +185 -0
- package/lib/module/engine/connection.js.map +1 -0
- package/lib/module/engine/engine.js +274 -0
- package/lib/module/engine/engine.js.map +1 -0
- package/lib/module/engine/finalize.js +322 -0
- package/lib/module/engine/finalize.js.map +1 -0
- package/lib/module/engine/foreground.js +21 -0
- package/lib/module/engine/foreground.js.map +1 -0
- package/lib/module/engine/internals.js +26 -0
- package/lib/module/engine/internals.js.map +1 -0
- package/lib/module/engine/pending.js +95 -0
- package/lib/module/engine/pending.js.map +1 -0
- package/lib/module/engine/products.js +195 -0
- package/lib/module/engine/products.js.map +1 -0
- package/lib/module/engine/purchase.js +390 -0
- package/lib/module/engine/purchase.js.map +1 -0
- package/lib/module/engine/purchaseFailure.js +72 -0
- package/lib/module/engine/purchaseFailure.js.map +1 -0
- package/lib/module/engine/receiptValidation.js +87 -0
- package/lib/module/engine/receiptValidation.js.map +1 -0
- package/lib/module/engine/state.js +196 -0
- package/lib/module/engine/state.js.map +1 -0
- package/lib/module/engine/subscriptions.js +119 -0
- package/lib/module/engine/subscriptions.js.map +1 -0
- package/lib/module/engine/sync.js +158 -0
- package/lib/module/engine/sync.js.map +1 -0
- package/lib/module/entitlements/aggregate.js +204 -0
- package/lib/module/entitlements/aggregate.js.map +1 -0
- package/lib/module/entitlements/mapping.js +35 -0
- package/lib/module/entitlements/mapping.js.map +1 -0
- package/lib/module/entitlements/policy.js +104 -0
- package/lib/module/entitlements/policy.js.map +1 -0
- package/lib/module/index.js +59 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/module/persistence/mmkv-store.js +144 -0
- package/lib/module/persistence/mmkv-store.js.map +1 -0
- package/lib/module/persistence/snapshot.js +221 -0
- package/lib/module/persistence/snapshot.js.map +1 -0
- package/lib/module/react/PaymentProvider.js +90 -0
- package/lib/module/react/PaymentProvider.js.map +1 -0
- package/lib/module/react/hooks.js +192 -0
- package/lib/module/react/hooks.js.map +1 -0
- package/lib/module/testing/conformance.js +130 -0
- package/lib/module/testing/conformance.js.map +1 -0
- package/lib/module/testing/fakeStore.js +276 -0
- package/lib/module/testing/fakeStore.js.map +1 -0
- package/lib/module/testing/index.js +13 -0
- package/lib/module/testing/index.js.map +1 -0
- package/lib/typescript/commonjs/adapter/createStoreProvider.d.ts +45 -0
- package/lib/typescript/commonjs/adapter/createStoreProvider.d.ts.map +1 -0
- package/lib/typescript/commonjs/adapter/iap-adapter.d.ts +80 -0
- package/lib/typescript/commonjs/adapter/iap-adapter.d.ts.map +1 -0
- package/lib/typescript/commonjs/adapter/iapShapes.d.ts +94 -0
- package/lib/typescript/commonjs/adapter/iapShapes.d.ts.map +1 -0
- package/lib/typescript/commonjs/adapter/port.d.ts +184 -0
- package/lib/typescript/commonjs/adapter/port.d.ts.map +1 -0
- package/lib/typescript/commonjs/config/types.d.ts +326 -0
- package/lib/typescript/commonjs/config/types.d.ts.map +1 -0
- package/lib/typescript/commonjs/config/validate.d.ts +63 -0
- package/lib/typescript/commonjs/config/validate.d.ts.map +1 -0
- package/lib/typescript/commonjs/core/mutex.d.ts +17 -0
- package/lib/typescript/commonjs/core/mutex.d.ts.map +1 -0
- package/lib/typescript/commonjs/core/retry.d.ts +19 -0
- package/lib/typescript/commonjs/core/retry.d.ts.map +1 -0
- package/lib/typescript/commonjs/core/timers.d.ts +142 -0
- package/lib/typescript/commonjs/core/timers.d.ts.map +1 -0
- package/lib/typescript/commonjs/diagnostics/errors.d.ts +55 -0
- package/lib/typescript/commonjs/diagnostics/errors.d.ts.map +1 -0
- package/lib/typescript/commonjs/diagnostics/events.d.ts +158 -0
- package/lib/typescript/commonjs/diagnostics/events.d.ts.map +1 -0
- package/lib/typescript/commonjs/diagnostics/messageKeys.d.ts +54 -0
- package/lib/typescript/commonjs/diagnostics/messageKeys.d.ts.map +1 -0
- package/lib/typescript/commonjs/engine/connection.d.ts +30 -0
- package/lib/typescript/commonjs/engine/connection.d.ts.map +1 -0
- package/lib/typescript/commonjs/engine/engine.d.ts +93 -0
- package/lib/typescript/commonjs/engine/engine.d.ts.map +1 -0
- package/lib/typescript/commonjs/engine/finalize.d.ts +53 -0
- package/lib/typescript/commonjs/engine/finalize.d.ts.map +1 -0
- package/lib/typescript/commonjs/engine/foreground.d.ts +15 -0
- package/lib/typescript/commonjs/engine/foreground.d.ts.map +1 -0
- package/lib/typescript/commonjs/engine/internals.d.ts +28 -0
- package/lib/typescript/commonjs/engine/internals.d.ts.map +1 -0
- package/lib/typescript/commonjs/engine/pending.d.ts +51 -0
- package/lib/typescript/commonjs/engine/pending.d.ts.map +1 -0
- package/lib/typescript/commonjs/engine/products.d.ts +82 -0
- package/lib/typescript/commonjs/engine/products.d.ts.map +1 -0
- package/lib/typescript/commonjs/engine/purchase.d.ts +61 -0
- package/lib/typescript/commonjs/engine/purchase.d.ts.map +1 -0
- package/lib/typescript/commonjs/engine/purchaseFailure.d.ts +15 -0
- package/lib/typescript/commonjs/engine/purchaseFailure.d.ts.map +1 -0
- package/lib/typescript/commonjs/engine/receiptValidation.d.ts +42 -0
- package/lib/typescript/commonjs/engine/receiptValidation.d.ts.map +1 -0
- package/lib/typescript/commonjs/engine/state.d.ts +46 -0
- package/lib/typescript/commonjs/engine/state.d.ts.map +1 -0
- package/lib/typescript/commonjs/engine/subscriptions.d.ts +61 -0
- package/lib/typescript/commonjs/engine/subscriptions.d.ts.map +1 -0
- package/lib/typescript/commonjs/engine/sync.d.ts +36 -0
- package/lib/typescript/commonjs/engine/sync.d.ts.map +1 -0
- package/lib/typescript/commonjs/entitlements/aggregate.d.ts +97 -0
- package/lib/typescript/commonjs/entitlements/aggregate.d.ts.map +1 -0
- package/lib/typescript/commonjs/entitlements/mapping.d.ts +27 -0
- package/lib/typescript/commonjs/entitlements/mapping.d.ts.map +1 -0
- package/lib/typescript/commonjs/entitlements/policy.d.ts +75 -0
- package/lib/typescript/commonjs/entitlements/policy.d.ts.map +1 -0
- package/lib/typescript/commonjs/index.d.ts +44 -0
- package/lib/typescript/commonjs/index.d.ts.map +1 -0
- package/lib/typescript/commonjs/package.json +1 -0
- package/lib/typescript/commonjs/persistence/mmkv-store.d.ts +45 -0
- package/lib/typescript/commonjs/persistence/mmkv-store.d.ts.map +1 -0
- package/lib/typescript/commonjs/persistence/snapshot.d.ts +106 -0
- package/lib/typescript/commonjs/persistence/snapshot.d.ts.map +1 -0
- package/lib/typescript/commonjs/react/PaymentProvider.d.ts +57 -0
- package/lib/typescript/commonjs/react/PaymentProvider.d.ts.map +1 -0
- package/lib/typescript/commonjs/react/hooks.d.ts +80 -0
- package/lib/typescript/commonjs/react/hooks.d.ts.map +1 -0
- package/lib/typescript/commonjs/testing/conformance.d.ts +41 -0
- package/lib/typescript/commonjs/testing/conformance.d.ts.map +1 -0
- package/lib/typescript/commonjs/testing/fakeStore.d.ts +69 -0
- package/lib/typescript/commonjs/testing/fakeStore.d.ts.map +1 -0
- package/lib/typescript/commonjs/testing/index.d.ts +12 -0
- package/lib/typescript/commonjs/testing/index.d.ts.map +1 -0
- package/lib/typescript/module/adapter/createStoreProvider.d.ts +45 -0
- package/lib/typescript/module/adapter/createStoreProvider.d.ts.map +1 -0
- package/lib/typescript/module/adapter/iap-adapter.d.ts +80 -0
- package/lib/typescript/module/adapter/iap-adapter.d.ts.map +1 -0
- package/lib/typescript/module/adapter/iapShapes.d.ts +94 -0
- package/lib/typescript/module/adapter/iapShapes.d.ts.map +1 -0
- package/lib/typescript/module/adapter/port.d.ts +184 -0
- package/lib/typescript/module/adapter/port.d.ts.map +1 -0
- package/lib/typescript/module/config/types.d.ts +326 -0
- package/lib/typescript/module/config/types.d.ts.map +1 -0
- package/lib/typescript/module/config/validate.d.ts +63 -0
- package/lib/typescript/module/config/validate.d.ts.map +1 -0
- package/lib/typescript/module/core/mutex.d.ts +17 -0
- package/lib/typescript/module/core/mutex.d.ts.map +1 -0
- package/lib/typescript/module/core/retry.d.ts +19 -0
- package/lib/typescript/module/core/retry.d.ts.map +1 -0
- package/lib/typescript/module/core/timers.d.ts +142 -0
- package/lib/typescript/module/core/timers.d.ts.map +1 -0
- package/lib/typescript/module/diagnostics/errors.d.ts +55 -0
- package/lib/typescript/module/diagnostics/errors.d.ts.map +1 -0
- package/lib/typescript/module/diagnostics/events.d.ts +158 -0
- package/lib/typescript/module/diagnostics/events.d.ts.map +1 -0
- package/lib/typescript/module/diagnostics/messageKeys.d.ts +54 -0
- package/lib/typescript/module/diagnostics/messageKeys.d.ts.map +1 -0
- package/lib/typescript/module/engine/connection.d.ts +30 -0
- package/lib/typescript/module/engine/connection.d.ts.map +1 -0
- package/lib/typescript/module/engine/engine.d.ts +93 -0
- package/lib/typescript/module/engine/engine.d.ts.map +1 -0
- package/lib/typescript/module/engine/finalize.d.ts +53 -0
- package/lib/typescript/module/engine/finalize.d.ts.map +1 -0
- package/lib/typescript/module/engine/foreground.d.ts +15 -0
- package/lib/typescript/module/engine/foreground.d.ts.map +1 -0
- package/lib/typescript/module/engine/internals.d.ts +28 -0
- package/lib/typescript/module/engine/internals.d.ts.map +1 -0
- package/lib/typescript/module/engine/pending.d.ts +51 -0
- package/lib/typescript/module/engine/pending.d.ts.map +1 -0
- package/lib/typescript/module/engine/products.d.ts +82 -0
- package/lib/typescript/module/engine/products.d.ts.map +1 -0
- package/lib/typescript/module/engine/purchase.d.ts +61 -0
- package/lib/typescript/module/engine/purchase.d.ts.map +1 -0
- package/lib/typescript/module/engine/purchaseFailure.d.ts +15 -0
- package/lib/typescript/module/engine/purchaseFailure.d.ts.map +1 -0
- package/lib/typescript/module/engine/receiptValidation.d.ts +42 -0
- package/lib/typescript/module/engine/receiptValidation.d.ts.map +1 -0
- package/lib/typescript/module/engine/state.d.ts +46 -0
- package/lib/typescript/module/engine/state.d.ts.map +1 -0
- package/lib/typescript/module/engine/subscriptions.d.ts +61 -0
- package/lib/typescript/module/engine/subscriptions.d.ts.map +1 -0
- package/lib/typescript/module/engine/sync.d.ts +36 -0
- package/lib/typescript/module/engine/sync.d.ts.map +1 -0
- package/lib/typescript/module/entitlements/aggregate.d.ts +97 -0
- package/lib/typescript/module/entitlements/aggregate.d.ts.map +1 -0
- package/lib/typescript/module/entitlements/mapping.d.ts +27 -0
- package/lib/typescript/module/entitlements/mapping.d.ts.map +1 -0
- package/lib/typescript/module/entitlements/policy.d.ts +75 -0
- package/lib/typescript/module/entitlements/policy.d.ts.map +1 -0
- package/lib/typescript/module/index.d.ts +44 -0
- package/lib/typescript/module/index.d.ts.map +1 -0
- package/lib/typescript/module/package.json +1 -0
- package/lib/typescript/module/persistence/mmkv-store.d.ts +45 -0
- package/lib/typescript/module/persistence/mmkv-store.d.ts.map +1 -0
- package/lib/typescript/module/persistence/snapshot.d.ts +106 -0
- package/lib/typescript/module/persistence/snapshot.d.ts.map +1 -0
- package/lib/typescript/module/react/PaymentProvider.d.ts +57 -0
- package/lib/typescript/module/react/PaymentProvider.d.ts.map +1 -0
- package/lib/typescript/module/react/hooks.d.ts +80 -0
- package/lib/typescript/module/react/hooks.d.ts.map +1 -0
- package/lib/typescript/module/testing/conformance.d.ts +41 -0
- package/lib/typescript/module/testing/conformance.d.ts.map +1 -0
- package/lib/typescript/module/testing/fakeStore.d.ts +69 -0
- package/lib/typescript/module/testing/fakeStore.d.ts.map +1 -0
- package/lib/typescript/module/testing/index.d.ts +12 -0
- package/lib/typescript/module/testing/index.d.ts.map +1 -0
- package/package.json +49 -13
- package/skills/sdk-rn-payment/SKILL.md +4 -4
- package/src/adapter/iap-adapter.ts +12 -216
- package/src/adapter/iapShapes.ts +232 -0
- package/src/config/types.ts +8 -0
- package/src/config/validate.ts +19 -4
- package/src/core/retry.ts +1 -58
- package/src/diagnostics/events.ts +1 -1
- package/src/engine/connection.ts +194 -0
- package/src/engine/engine.ts +140 -700
- package/src/engine/internals.ts +40 -0
- package/src/engine/products.ts +39 -17
- package/src/engine/purchase.ts +22 -130
- package/src/engine/purchaseFailure.ts +48 -0
- package/src/engine/receiptValidation.ts +101 -0
- package/src/engine/state.ts +253 -0
- package/src/engine/sync.ts +161 -0
- package/src/entitlements/aggregate.ts +3 -5
- package/src/entitlements/mapping.ts +0 -22
- package/src/persistence/mmkv-store.ts +10 -3
- package/src/persistence/snapshot.ts +102 -27
- package/src/react/PaymentProvider.tsx +73 -24
- package/src/react/hooks.ts +13 -3
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The persisted snapshot: schema, versioning, and migration.
|
|
5
|
+
*
|
|
6
|
+
* What is stored here is **exhaustively** what the package keeps on the device,
|
|
7
|
+
* and the README repeats this list so an app owner can complete a Data safety
|
|
8
|
+
* declaration accurately (FR-056, POL-002).
|
|
9
|
+
*
|
|
10
|
+
* Deliberately absent: payment details, card data, billing address, user
|
|
11
|
+
* identity, raw receipts, verification payloads, and backend tokens.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* 2 since 0.6.0: the never-written `entitlements` map and `finalized` state are
|
|
16
|
+
* gone (PAY-12). Entitlements are always derived from purchases, never stored.
|
|
17
|
+
*/
|
|
18
|
+
export const SNAPSHOT_SCHEMA_VERSION = 2;
|
|
19
|
+
|
|
20
|
+
/** Where a transaction stands with the store. */
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Where a transaction stands in the finalization queue.
|
|
24
|
+
*
|
|
25
|
+
* `queued` is the load-bearing value: it is written **before** the store call is
|
|
26
|
+
* attempted, so an app killed mid-finalize leaves a recoverable record rather
|
|
27
|
+
* than a paid purchase that Google will auto-refund (FR-041, FR-042).
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
/** Whether the app's backend has ruled on this transaction (FR-072a). */
|
|
31
|
+
|
|
32
|
+
export function emptySnapshot(scopeToken) {
|
|
33
|
+
return {
|
|
34
|
+
schemaVersion: SNAPSHOT_SCHEMA_VERSION,
|
|
35
|
+
scopeToken,
|
|
36
|
+
purchases: [],
|
|
37
|
+
lastSyncedAt: undefined
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* A migration from one schema version to the next.
|
|
43
|
+
*
|
|
44
|
+
* Kept as an ordered chain rather than a switch so each step stays small and
|
|
45
|
+
* independently testable, and so a device several versions behind is carried
|
|
46
|
+
* forward one hop at a time (NFR-009).
|
|
47
|
+
*/
|
|
48
|
+
|
|
49
|
+
/** Registered migrations, one hop each. */
|
|
50
|
+
export const SNAPSHOT_MIGRATIONS = [{
|
|
51
|
+
// v1 stored an `entitlements` map nothing ever wrote to, and a `finalized`
|
|
52
|
+
// purchase state nothing ever set (PAY-12). Both go; a `finalized` record,
|
|
53
|
+
// should one exist, is a purchased one whose finalize progress is kept.
|
|
54
|
+
from: 1,
|
|
55
|
+
to: 2,
|
|
56
|
+
migrate(input) {
|
|
57
|
+
const rest = {
|
|
58
|
+
...input
|
|
59
|
+
};
|
|
60
|
+
delete rest.entitlements;
|
|
61
|
+
const purchases = Array.isArray(rest.purchases) ? rest.purchases.map(record => typeof record === 'object' && record !== null && record.state === 'finalized' ? {
|
|
62
|
+
...record,
|
|
63
|
+
state: 'purchased'
|
|
64
|
+
} : record) : rest.purchases;
|
|
65
|
+
return {
|
|
66
|
+
...rest,
|
|
67
|
+
schemaVersion: 2,
|
|
68
|
+
purchases
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
}];
|
|
72
|
+
export function migrateSnapshot(raw) {
|
|
73
|
+
if (typeof raw !== 'object' || raw === null) {
|
|
74
|
+
return {
|
|
75
|
+
ok: false,
|
|
76
|
+
reason: 'unreadable'
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
let working = {
|
|
80
|
+
...raw
|
|
81
|
+
};
|
|
82
|
+
const version = working.schemaVersion;
|
|
83
|
+
if (typeof version !== 'number' || !Number.isInteger(version) || version < 1) {
|
|
84
|
+
return {
|
|
85
|
+
ok: false,
|
|
86
|
+
reason: 'unreadable'
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
if (version > SNAPSHOT_SCHEMA_VERSION) {
|
|
90
|
+
// A newer app version wrote this. Downgrading cannot know what was added,
|
|
91
|
+
// so refuse rather than silently drop fields.
|
|
92
|
+
return {
|
|
93
|
+
ok: false,
|
|
94
|
+
reason: 'unsupported_version'
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
let current = version;
|
|
98
|
+
let migrated = false;
|
|
99
|
+
while (current < SNAPSHOT_SCHEMA_VERSION) {
|
|
100
|
+
const step = SNAPSHOT_MIGRATIONS.find(m => m.from === current);
|
|
101
|
+
if (!step) {
|
|
102
|
+
return {
|
|
103
|
+
ok: false,
|
|
104
|
+
reason: 'unsupported_version'
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
working = step.migrate(working);
|
|
108
|
+
current = step.to;
|
|
109
|
+
migrated = true;
|
|
110
|
+
}
|
|
111
|
+
const snapshot = coerceSnapshot(working);
|
|
112
|
+
return snapshot ? {
|
|
113
|
+
ok: true,
|
|
114
|
+
snapshot,
|
|
115
|
+
migrated
|
|
116
|
+
} : {
|
|
117
|
+
ok: false,
|
|
118
|
+
reason: 'unreadable'
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Validates shape after migration.
|
|
124
|
+
*
|
|
125
|
+
* Runs even on a non-migrated record because the bytes came off a device where
|
|
126
|
+
* anything could have happened to them — a partial write, a rooted user, a
|
|
127
|
+
* truncated file.
|
|
128
|
+
*/
|
|
129
|
+
function coerceSnapshot(input) {
|
|
130
|
+
const purchases = input.purchases;
|
|
131
|
+
if (!Array.isArray(purchases)) {
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
const lastSyncedAt = typeof input.lastSyncedAt === 'number' ? input.lastSyncedAt : undefined;
|
|
135
|
+
const scopeToken = typeof input.scopeToken === 'string' ? input.scopeToken : undefined;
|
|
136
|
+
return {
|
|
137
|
+
schemaVersion: SNAPSHOT_SCHEMA_VERSION,
|
|
138
|
+
scopeToken,
|
|
139
|
+
// Record by record: one damaged entry is dropped, not trusted and not
|
|
140
|
+
// allowed to discard the rest (PAY-13). A dropped record comes back with the
|
|
141
|
+
// next sync, if the store still reports it.
|
|
142
|
+
purchases: purchases.flatMap(record => {
|
|
143
|
+
const valid = coercePurchase(record);
|
|
144
|
+
return valid ? [valid] : [];
|
|
145
|
+
}),
|
|
146
|
+
lastSyncedAt
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
const PURCHASE_STATES = new Set(['pending', 'purchased', 'consumed', 'revoked']);
|
|
150
|
+
const FINALIZE_STATES = new Set(['not_required', 'queued', 'in_flight', 'done', 'failed']);
|
|
151
|
+
const VALIDATION_STATES = new Set(['not_required', 'approved', 'rejected', 'queued_for_revalidation']);
|
|
152
|
+
const PRODUCT_TYPES = new Set(['one_time_permanent', 'one_time_consumable', 'subscription']);
|
|
153
|
+
|
|
154
|
+
/** A persisted purchase with every field checked, or null when any is wrong. */
|
|
155
|
+
function coercePurchase(input) {
|
|
156
|
+
if (typeof input !== 'object' || input === null) {
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
const record = input;
|
|
160
|
+
const text = value => typeof value === 'string' && value !== '';
|
|
161
|
+
const count = value => typeof value === 'number' && Number.isFinite(value) && value >= 0;
|
|
162
|
+
if (!text(record.transactionId) || !text(record.productKey) || !text(record.token) || !PRODUCT_TYPES.has(record.productType) || !PURCHASE_STATES.has(record.state) || !FINALIZE_STATES.has(record.finalizeState) || !VALIDATION_STATES.has(record.validationState) || !count(record.purchasedAt) || !count(record.attempts) || record.expiresAt !== undefined && !count(record.expiresAt) || record.willAutoRenew !== undefined && typeof record.willAutoRenew !== 'boolean') {
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
165
|
+
return {
|
|
166
|
+
transactionId: record.transactionId,
|
|
167
|
+
productKey: record.productKey,
|
|
168
|
+
productType: record.productType,
|
|
169
|
+
token: record.token,
|
|
170
|
+
state: record.state,
|
|
171
|
+
purchasedAt: record.purchasedAt,
|
|
172
|
+
finalizeState: record.finalizeState,
|
|
173
|
+
attempts: record.attempts,
|
|
174
|
+
validationState: record.validationState,
|
|
175
|
+
expiresAt: record.expiresAt,
|
|
176
|
+
willAutoRenew: record.willAutoRenew
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Decides a purchase's finalization state from the store's view plus what we
|
|
182
|
+
* already knew.
|
|
183
|
+
*
|
|
184
|
+
* Shared by the sync path and the purchase path so they cannot drift — and they
|
|
185
|
+
* must not, because a disagreement here is how a paid transaction goes
|
|
186
|
+
* unacknowledged and gets auto-refunded three days later.
|
|
187
|
+
*
|
|
188
|
+
* The subtle case is a **pending purchase that settles**. Its record was written
|
|
189
|
+
* with `not_required`, which was correct while the money had not arrived. Once the
|
|
190
|
+
* store reports it `purchased`, that value must be re-evaluated rather than
|
|
191
|
+
* preserved — otherwise the transaction is never queued, never acknowledged, and
|
|
192
|
+
* Google revokes it. Progress is preserved only for a transaction that was
|
|
193
|
+
* already in the queue.
|
|
194
|
+
*/
|
|
195
|
+
export function resolveFinalizeState(incoming, existing) {
|
|
196
|
+
if (incoming.state === 'pending') {
|
|
197
|
+
// Nothing to settle: there is no entitlement to report as delivered.
|
|
198
|
+
return 'not_required';
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Already settled at the store, whatever we thought locally.
|
|
202
|
+
if (incoming.isAcknowledged) {
|
|
203
|
+
return 'done';
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Genuine in-queue progress is preserved, so a sync cannot reset an attempt
|
|
207
|
+
// count or lose an in-flight finalize.
|
|
208
|
+
if (existing && existing.finalizeState !== 'not_required' && existing.finalizeState !== 'done') {
|
|
209
|
+
return existing.finalizeState;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Covers both a brand-new purchase and a previously-pending one that just
|
|
213
|
+
// settled. Either way it is now owed a finalize call.
|
|
214
|
+
return 'queued';
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/** Transactions still owed a finalize call, drained at startup and on foreground (FR-042). */
|
|
218
|
+
export function pendingFinalizations(snapshot) {
|
|
219
|
+
return snapshot.purchases.filter(p => (p.finalizeState === 'queued' || p.finalizeState === 'in_flight' || p.finalizeState === 'failed') && p.state !== 'revoked');
|
|
220
|
+
}
|
|
221
|
+
//# sourceMappingURL=snapshot.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["SNAPSHOT_SCHEMA_VERSION","emptySnapshot","scopeToken","schemaVersion","purchases","lastSyncedAt","undefined","SNAPSHOT_MIGRATIONS","from","to","migrate","input","rest","entitlements","Array","isArray","map","record","state","migrateSnapshot","raw","ok","reason","working","version","Number","isInteger","current","migrated","step","find","m","snapshot","coerceSnapshot","flatMap","valid","coercePurchase","PURCHASE_STATES","Set","FINALIZE_STATES","VALIDATION_STATES","PRODUCT_TYPES","text","value","count","isFinite","transactionId","productKey","token","has","productType","finalizeState","validationState","purchasedAt","attempts","expiresAt","willAutoRenew","resolveFinalizeState","incoming","existing","isAcknowledged","pendingFinalizations","filter","p"],"sourceRoot":"../../../src","sources":["persistence/snapshot.ts"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* React integration.
|
|
5
|
+
*
|
|
6
|
+
* The provider owns the engine's lifecycle so an app does not have to remember to
|
|
7
|
+
* clean up. It renders nothing of its own — no paywall, no price table, no
|
|
8
|
+
* button. Every surface belongs to the app (UX-001).
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import React, { createContext, useContext, useEffect, useRef, useState } from 'react';
|
|
12
|
+
import { createEngine } from "../engine/engine.js";
|
|
13
|
+
import { syncOnForeground as watchForeground } from "../engine/foreground.js";
|
|
14
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
15
|
+
const PaymentContext = /*#__PURE__*/createContext(null);
|
|
16
|
+
export function PaymentProvider(props) {
|
|
17
|
+
const {
|
|
18
|
+
autoInitialize = true,
|
|
19
|
+
syncOnForeground = false,
|
|
20
|
+
children
|
|
21
|
+
} = props;
|
|
22
|
+
const external = props.engine;
|
|
23
|
+
const key = props.engine ? undefined : props.engineKey ?? props.config.scopeToken;
|
|
24
|
+
|
|
25
|
+
// The latest config and deps, read only when an engine is built.
|
|
26
|
+
const latest = useRef(props);
|
|
27
|
+
latest.current = props;
|
|
28
|
+
const build = () => {
|
|
29
|
+
const current = latest.current;
|
|
30
|
+
if (current.engine) {
|
|
31
|
+
return current.engine;
|
|
32
|
+
}
|
|
33
|
+
return createEngine(current.config, current.deps);
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
// Built once, not once per render: validating the catalog and opening storage
|
|
37
|
+
// are not render work, and a new engine per render reconnected to the billing
|
|
38
|
+
// service every time the app's root re-rendered (FR-018, PAY-07).
|
|
39
|
+
const [owned, setOwned] = useState(() => external ? null : build());
|
|
40
|
+
const builtFor = useRef(key);
|
|
41
|
+
const cleanedUp = useRef(new WeakSet());
|
|
42
|
+
const engine = external ?? owned ?? build();
|
|
43
|
+
useEffect(() => {
|
|
44
|
+
if (external) {
|
|
45
|
+
// The app's engine, so the app's lifecycle.
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
const cleaned = cleanedUp.current;
|
|
49
|
+
// An engine cleaned up by an earlier run of this effect — StrictMode's
|
|
50
|
+
// mount-unmount-mount in development, or a changed `engineKey` — is never
|
|
51
|
+
// reused: it would answer every call with "cleaned up". A fresh one replaces
|
|
52
|
+
// it and this effect runs again for that one.
|
|
53
|
+
if (!owned || cleaned.has(owned) || builtFor.current !== key) {
|
|
54
|
+
builtFor.current = key;
|
|
55
|
+
setOwned(build());
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
58
|
+
if (autoInitialize) {
|
|
59
|
+
owned.initialize().catch(() => undefined);
|
|
60
|
+
}
|
|
61
|
+
// Cleanup on unmount is the whole reason the provider owns the engine:
|
|
62
|
+
// after it, no event fires and no timer survives (FR-082).
|
|
63
|
+
return () => {
|
|
64
|
+
cleaned.add(owned);
|
|
65
|
+
owned.cleanup();
|
|
66
|
+
};
|
|
67
|
+
// `build` reads the latest props through a ref, so it is not a dependency.
|
|
68
|
+
}, [external, owned, key, autoInitialize]);
|
|
69
|
+
useEffect(() => syncOnForeground ? watchForeground(engine) : undefined, [engine, syncOnForeground]);
|
|
70
|
+
return /*#__PURE__*/_jsx(PaymentContext.Provider, {
|
|
71
|
+
value: engine,
|
|
72
|
+
children: children
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* The engine from context.
|
|
78
|
+
*
|
|
79
|
+
* Throws when used outside a provider, deliberately: returning null would push a
|
|
80
|
+
* null check into every consumer and let a genuine wiring mistake read as "no
|
|
81
|
+
* entitlement" — the exact conflation this package exists to prevent.
|
|
82
|
+
*/
|
|
83
|
+
export function usePaymentEngine() {
|
|
84
|
+
const engine = useContext(PaymentContext);
|
|
85
|
+
if (!engine) {
|
|
86
|
+
throw new Error('usePaymentEngine must be used inside a <PaymentProvider>. Wrap your app ' + 'or the screen that reads entitlements.');
|
|
87
|
+
}
|
|
88
|
+
return engine;
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=PaymentProvider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["React","createContext","useContext","useEffect","useRef","useState","createEngine","syncOnForeground","watchForeground","jsx","_jsx","PaymentContext","PaymentProvider","props","autoInitialize","children","external","engine","key","undefined","engineKey","config","scopeToken","latest","current","build","deps","owned","setOwned","builtFor","cleanedUp","WeakSet","cleaned","has","initialize","catch","add","cleanup","Provider","value","usePaymentEngine","Error"],"sourceRoot":"../../../src","sources":["react/PaymentProvider.tsx"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Hooks over the engine.
|
|
5
|
+
*
|
|
6
|
+
* Each subscribes on mount and unsubscribes on unmount, so repeated mount/unmount
|
|
7
|
+
* leaves no duplicate subscription and no surviving listener (FR-006, PERF-002).
|
|
8
|
+
*
|
|
9
|
+
* Reads are synchronous: the engine holds entitlements in memory, so a hook never
|
|
10
|
+
* returns a loading placeholder for an entitlement it already knows (PERF-001).
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { useExternalStore } from '@chipmobilesdk/rn-core/react';
|
|
14
|
+
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
15
|
+
import { usePaymentEngine } from "./PaymentProvider.js";
|
|
16
|
+
/**
|
|
17
|
+
* One entitlement, re-rendering only when that one changes.
|
|
18
|
+
*
|
|
19
|
+
* The returned `status` is `unknown` until the first sync completes. That is the
|
|
20
|
+
* point: an app must be able to tell "not yet known" from "not entitled" so a
|
|
21
|
+
* paying user never sees the unpaid experience flash (FR-047).
|
|
22
|
+
*/
|
|
23
|
+
export function useEntitlement(key) {
|
|
24
|
+
const engine = usePaymentEngine();
|
|
25
|
+
// The engine keeps an unchanged entitlement's object across recomputes, so this
|
|
26
|
+
// selection only changes, and the component only re-renders, when it changes.
|
|
27
|
+
const select = useCallback(map => map[key] ?? engine.getEntitlement(key), [engine, key]);
|
|
28
|
+
return useExternalStore(engine.subscribeEntitlements, engine.getAllEntitlements, select);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Every entitlement at once, for a screen that shows overall plan state. */
|
|
32
|
+
export function useEntitlements() {
|
|
33
|
+
const engine = usePaymentEngine();
|
|
34
|
+
return useExternalStore(engine.subscribeEntitlements, engine.getAllEntitlements);
|
|
35
|
+
}
|
|
36
|
+
function narrowPending(state, keys) {
|
|
37
|
+
return keys ? {
|
|
38
|
+
...state,
|
|
39
|
+
purchases: state.purchases.filter(p => keys.includes(p.productKey))
|
|
40
|
+
} : state;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Pending purchases, in this session or an earlier one (FR-101). Added in 0.2.0.
|
|
45
|
+
*
|
|
46
|
+
* Pass product keys to narrow it to the products one screen sells. Read
|
|
47
|
+
* `status` before the list: under `unknown`, an empty list is not "nothing
|
|
48
|
+
* pending", and a Buy button shown on it could charge a user twice.
|
|
49
|
+
*/
|
|
50
|
+
export function usePendingPurchases(productKeys) {
|
|
51
|
+
const engine = usePaymentEngine();
|
|
52
|
+
// Joined so a caller passing a literal array does not resubscribe each render.
|
|
53
|
+
const keyToken = productKeys ? productKeys.join('|') : null;
|
|
54
|
+
const [state, setState] = useState(() => narrowPending(engine.getPendingPurchases(), productKeys ?? null));
|
|
55
|
+
useEffect(() => {
|
|
56
|
+
const keys = keyToken === null ? null : keyToken.split('|').filter(Boolean);
|
|
57
|
+
setState(narrowPending(engine.getPendingPurchases(), keys));
|
|
58
|
+
return engine.subscribePendingPurchases(next => setState(narrowPending(next, keys)));
|
|
59
|
+
}, [engine, keyToken]);
|
|
60
|
+
return state;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** Package status, so an app can tell "cannot sell here" from "not paid" (FR-022). */
|
|
64
|
+
export function usePaymentStatus() {
|
|
65
|
+
const engine = usePaymentEngine();
|
|
66
|
+
return useExternalStore(engine.subscribeStatus, engine.getStatus);
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Starting a purchase.
|
|
70
|
+
*
|
|
71
|
+
* `lastResult` is the full typed union rather than a boolean, because a screen has
|
|
72
|
+
* to treat `user_cancelled` differently from `store_error` — showing an error
|
|
73
|
+
* banner to someone who simply changed their mind is the most common paywall bug.
|
|
74
|
+
*/
|
|
75
|
+
export function usePurchase() {
|
|
76
|
+
const engine = usePaymentEngine();
|
|
77
|
+
const [isPurchasing, setIsPurchasing] = useState(false);
|
|
78
|
+
const [lastResult, setLastResult] = useState();
|
|
79
|
+
const purchase = useCallback(async (productKey, options) => {
|
|
80
|
+
setIsPurchasing(true);
|
|
81
|
+
try {
|
|
82
|
+
const result = await engine.purchase(productKey, options);
|
|
83
|
+
setLastResult(result);
|
|
84
|
+
return result;
|
|
85
|
+
} finally {
|
|
86
|
+
setIsPurchasing(false);
|
|
87
|
+
}
|
|
88
|
+
}, [engine]);
|
|
89
|
+
const consume = useCallback(ref => engine.consume(ref), [engine]);
|
|
90
|
+
return {
|
|
91
|
+
purchase,
|
|
92
|
+
consume,
|
|
93
|
+
isPurchasing,
|
|
94
|
+
lastResult
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Store-supplied product information for a paywall.
|
|
99
|
+
*
|
|
100
|
+
* Every price here is already formatted by the store for the user's locale. An app
|
|
101
|
+
* must render `formattedPrice` and never build its own string from
|
|
102
|
+
* `priceAmountMicros` — that field exists for sorting and comparison only
|
|
103
|
+
* (FR-028).
|
|
104
|
+
*/
|
|
105
|
+
export function useProducts(productKeys) {
|
|
106
|
+
const engine = usePaymentEngine();
|
|
107
|
+
const [products, setProducts] = useState([]);
|
|
108
|
+
const [missing, setMissing] = useState([]);
|
|
109
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
110
|
+
const [error, setError] = useState();
|
|
111
|
+
|
|
112
|
+
// Joined so the effect depends on the contents rather than the array identity:
|
|
113
|
+
// a caller passing a literal would otherwise re-query on every render.
|
|
114
|
+
const keyToken = productKeys.join('|');
|
|
115
|
+
// Each load's number. Only the latest may write: when the keys change while a
|
|
116
|
+
// query is out, the older answer must not overwrite the newer one (PAY-20).
|
|
117
|
+
const latestRequest = useRef(0);
|
|
118
|
+
const load = useCallback(async fresh => {
|
|
119
|
+
const request = ++latestRequest.current;
|
|
120
|
+
const keys = keyToken === '' ? [] : keyToken.split('|');
|
|
121
|
+
if (keys.length === 0) {
|
|
122
|
+
setProducts([]);
|
|
123
|
+
setMissing([]);
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
setIsLoading(true);
|
|
127
|
+
try {
|
|
128
|
+
const result = fresh ? await engine.refreshProducts(keys) : await engine.getProducts(keys);
|
|
129
|
+
if (request !== latestRequest.current) {
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
if (result.ok) {
|
|
133
|
+
setProducts(result.products);
|
|
134
|
+
setMissing(result.missing);
|
|
135
|
+
setError(undefined);
|
|
136
|
+
} else {
|
|
137
|
+
setError(result.error);
|
|
138
|
+
}
|
|
139
|
+
} finally {
|
|
140
|
+
if (request === latestRequest.current) {
|
|
141
|
+
setIsLoading(false);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}, [engine, keyToken]);
|
|
145
|
+
useEffect(() => {
|
|
146
|
+
load(false).catch(() => undefined);
|
|
147
|
+
}, [load]);
|
|
148
|
+
const refresh = useCallback(() => load(true), [load]);
|
|
149
|
+
return {
|
|
150
|
+
products,
|
|
151
|
+
missing,
|
|
152
|
+
isLoading,
|
|
153
|
+
error,
|
|
154
|
+
refresh
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Sync and restore, for a "Restore purchases" control.
|
|
159
|
+
*
|
|
160
|
+
* `restore`'s outcome distinguishes "restored" from "nothing found", so the app
|
|
161
|
+
* can avoid telling a user their purchases came back when none existed (FR-061).
|
|
162
|
+
*/
|
|
163
|
+
export function useSync() {
|
|
164
|
+
const engine = usePaymentEngine();
|
|
165
|
+
const [isSyncing, setIsSyncing] = useState(false);
|
|
166
|
+
const [lastSyncedAt, setLastSyncedAt] = useState(() => engine.getLastSyncedAt());
|
|
167
|
+
const sync = useCallback(async () => {
|
|
168
|
+
setIsSyncing(true);
|
|
169
|
+
try {
|
|
170
|
+
return await engine.sync('manual');
|
|
171
|
+
} finally {
|
|
172
|
+
setIsSyncing(false);
|
|
173
|
+
setLastSyncedAt(engine.getLastSyncedAt());
|
|
174
|
+
}
|
|
175
|
+
}, [engine]);
|
|
176
|
+
const restore = useCallback(async () => {
|
|
177
|
+
setIsSyncing(true);
|
|
178
|
+
try {
|
|
179
|
+
return await engine.restore();
|
|
180
|
+
} finally {
|
|
181
|
+
setIsSyncing(false);
|
|
182
|
+
setLastSyncedAt(engine.getLastSyncedAt());
|
|
183
|
+
}
|
|
184
|
+
}, [engine]);
|
|
185
|
+
return {
|
|
186
|
+
sync,
|
|
187
|
+
restore,
|
|
188
|
+
isSyncing,
|
|
189
|
+
lastSyncedAt
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
//# sourceMappingURL=hooks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["useExternalStore","useCallback","useEffect","useRef","useState","usePaymentEngine","useEntitlement","key","engine","select","map","getEntitlement","subscribeEntitlements","getAllEntitlements","useEntitlements","narrowPending","state","keys","purchases","filter","p","includes","productKey","usePendingPurchases","productKeys","keyToken","join","setState","getPendingPurchases","split","Boolean","subscribePendingPurchases","next","usePaymentStatus","subscribeStatus","getStatus","usePurchase","isPurchasing","setIsPurchasing","lastResult","setLastResult","purchase","options","result","consume","ref","useProducts","products","setProducts","missing","setMissing","isLoading","setIsLoading","error","setError","latestRequest","load","fresh","request","current","length","refreshProducts","getProducts","ok","undefined","catch","refresh","useSync","isSyncing","setIsSyncing","lastSyncedAt","setLastSyncedAt","getLastSyncedAt","sync","restore"],"sourceRoot":"../../../src","sources":["react/hooks.ts"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* What any `ProviderPort` must do (IOS-12, PAY-13).
|
|
5
|
+
*
|
|
6
|
+
* The same checks run against the fake store and against the Play adapter over a
|
|
7
|
+
* simulated binding (or the real one, from a demo screen on a device), so a test
|
|
8
|
+
* that drives the fake proves something about the device path. Each check builds
|
|
9
|
+
* its own port over the same small store, through a harness.
|
|
10
|
+
*/
|
|
11
|
+
import { ensure, runChecks } from '@chipmobilesdk/rn-core/testing';
|
|
12
|
+
/** The store every check runs against. */
|
|
13
|
+
export const CONFORMANCE_STORE = {
|
|
14
|
+
products: [{
|
|
15
|
+
storeProductId: 'conformance.remove_ads',
|
|
16
|
+
productType: 'one_time_permanent',
|
|
17
|
+
formattedPrice: '€1.99'
|
|
18
|
+
}, {
|
|
19
|
+
storeProductId: 'conformance.coins',
|
|
20
|
+
productType: 'one_time_consumable',
|
|
21
|
+
formattedPrice: '€0.99'
|
|
22
|
+
}],
|
|
23
|
+
owned: [{
|
|
24
|
+
storeProductId: 'conformance.remove_ads',
|
|
25
|
+
token: 'token-owned',
|
|
26
|
+
state: 'purchased'
|
|
27
|
+
}, {
|
|
28
|
+
storeProductId: 'conformance.coins',
|
|
29
|
+
token: 'token-pending',
|
|
30
|
+
state: 'pending'
|
|
31
|
+
}]
|
|
32
|
+
};
|
|
33
|
+
export const providerPortChecks = [{
|
|
34
|
+
name: 'connects, and disconnects safely before a connect and twice',
|
|
35
|
+
async run(harness) {
|
|
36
|
+
const {
|
|
37
|
+
port
|
|
38
|
+
} = harness.create(CONFORMANCE_STORE);
|
|
39
|
+
await port.disconnect();
|
|
40
|
+
const outcome = await port.connect();
|
|
41
|
+
ensure(outcome.ok, `connect failed: ${JSON.stringify(outcome)}`);
|
|
42
|
+
await port.disconnect();
|
|
43
|
+
await port.disconnect();
|
|
44
|
+
}
|
|
45
|
+
}, {
|
|
46
|
+
name: 'reports a dropped service to observers, and nothing after unsubscribing',
|
|
47
|
+
async run(harness) {
|
|
48
|
+
const subject = harness.create(CONFORMANCE_STORE);
|
|
49
|
+
const seen = [];
|
|
50
|
+
const stop = subject.port.observeConnection(connected => seen.push(connected));
|
|
51
|
+
await subject.port.connect();
|
|
52
|
+
ensure(seen.at(-1) === true, `after connect observers saw ${JSON.stringify(seen)}`);
|
|
53
|
+
subject.dropService();
|
|
54
|
+
ensure(seen.at(-1) === false, `after the drop observers saw ${JSON.stringify(seen)}`);
|
|
55
|
+
stop();
|
|
56
|
+
const before = seen.length;
|
|
57
|
+
await subject.port.connect();
|
|
58
|
+
ensure(seen.length === before, 'notified after unsubscribing');
|
|
59
|
+
}
|
|
60
|
+
}, {
|
|
61
|
+
name: 'returns the requested products with the store-formatted price',
|
|
62
|
+
async run(harness) {
|
|
63
|
+
const {
|
|
64
|
+
port
|
|
65
|
+
} = harness.create(CONFORMANCE_STORE);
|
|
66
|
+
await port.connect();
|
|
67
|
+
const products = await port.queryProducts(['conformance.remove_ads'], 'one_time_permanent');
|
|
68
|
+
ensure(products.length === 1, `expected one product, got ${products.length}`);
|
|
69
|
+
const [product] = products;
|
|
70
|
+
ensure(product?.storeProductId === 'conformance.remove_ads', 'wrong product returned');
|
|
71
|
+
ensure(product.productType === 'one_time_permanent', `product type ${product.productType}`);
|
|
72
|
+
ensure(product.formattedPrice === '€1.99', `price ${product.formattedPrice}`);
|
|
73
|
+
}
|
|
74
|
+
}, {
|
|
75
|
+
name: 'flags a pending purchase rather than filtering it out',
|
|
76
|
+
async run(harness) {
|
|
77
|
+
const {
|
|
78
|
+
port
|
|
79
|
+
} = harness.create(CONFORMANCE_STORE);
|
|
80
|
+
await port.connect();
|
|
81
|
+
const owned = await port.queryOwned();
|
|
82
|
+
const pending = owned.find(purchase => purchase.token === 'token-pending');
|
|
83
|
+
const bought = owned.find(purchase => purchase.token === 'token-owned');
|
|
84
|
+
ensure(pending?.state === 'pending', `pending purchase reported as ${pending?.state}`);
|
|
85
|
+
ensure(bought?.state === 'purchased', `bought purchase reported as ${bought?.state}`);
|
|
86
|
+
}
|
|
87
|
+
}, {
|
|
88
|
+
name: 'reports a bought purchase for the product that was requested',
|
|
89
|
+
async run(harness) {
|
|
90
|
+
const subject = harness.create(CONFORMANCE_STORE);
|
|
91
|
+
await subject.port.connect();
|
|
92
|
+
subject.nextPurchaseEnds('bought');
|
|
93
|
+
const outcome = await subject.port.launchPurchase({
|
|
94
|
+
storeProductId: 'conformance.coins',
|
|
95
|
+
productType: 'one_time_consumable'
|
|
96
|
+
});
|
|
97
|
+
ensure(outcome.ok, `purchase failed: ${JSON.stringify(outcome)}`);
|
|
98
|
+
ensure(outcome.purchase.storeProductId === 'conformance.coins', 'purchase for another product');
|
|
99
|
+
ensure(outcome.purchase.token !== '', 'purchase has no token to finalize');
|
|
100
|
+
}
|
|
101
|
+
}, {
|
|
102
|
+
name: 'reports a cancelled flow as user_cancelled',
|
|
103
|
+
async run(harness) {
|
|
104
|
+
const subject = harness.create(CONFORMANCE_STORE);
|
|
105
|
+
await subject.port.connect();
|
|
106
|
+
subject.nextPurchaseEnds('cancelled');
|
|
107
|
+
const outcome = await subject.port.launchPurchase({
|
|
108
|
+
storeProductId: 'conformance.remove_ads',
|
|
109
|
+
productType: 'one_time_permanent'
|
|
110
|
+
});
|
|
111
|
+
ensure(!outcome.ok, 'a cancelled flow reported a purchase');
|
|
112
|
+
ensure(outcome.reason === 'user_cancelled', `reason ${outcome.reason}`);
|
|
113
|
+
}
|
|
114
|
+
}, {
|
|
115
|
+
name: 'acknowledges and consumes safely twice',
|
|
116
|
+
async run(harness) {
|
|
117
|
+
const {
|
|
118
|
+
port
|
|
119
|
+
} = harness.create(CONFORMANCE_STORE);
|
|
120
|
+
await port.connect();
|
|
121
|
+
await port.acknowledge('token-owned');
|
|
122
|
+
await port.acknowledge('token-owned');
|
|
123
|
+
await port.consume('token-pending');
|
|
124
|
+
await port.consume('token-pending');
|
|
125
|
+
}
|
|
126
|
+
}];
|
|
127
|
+
export function runProviderPortConformance(harness) {
|
|
128
|
+
return runChecks(() => harness, providerPortChecks);
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=conformance.js.map
|