@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
|
@@ -41,11 +41,18 @@ import { createListenerSet, lazyPeer } from '@chipmobilesdk/rn-core';
|
|
|
41
41
|
import type { RequestPurchaseProps, SubscriptionReplacementModeAndroid } from 'react-native-iap';
|
|
42
42
|
|
|
43
43
|
import type { ProductType, Unsubscribe } from '../config/types';
|
|
44
|
+
import {
|
|
45
|
+
mapErrorCode,
|
|
46
|
+
normalizeProduct,
|
|
47
|
+
normalizePurchase,
|
|
48
|
+
type RawProduct,
|
|
49
|
+
type RawPurchase,
|
|
50
|
+
type RawPurchaseError,
|
|
51
|
+
} from './iapShapes';
|
|
44
52
|
import { PaymentError, redact } from '../diagnostics/errors';
|
|
45
53
|
import type {
|
|
46
54
|
ProviderCapabilities,
|
|
47
55
|
ProviderConnectOutcome,
|
|
48
|
-
ProviderFailureReason,
|
|
49
56
|
ProviderPort,
|
|
50
57
|
ProviderPriceChangeOutcome,
|
|
51
58
|
ProviderProduct,
|
|
@@ -56,6 +63,10 @@ import type {
|
|
|
56
63
|
StoreProductId,
|
|
57
64
|
} from './port';
|
|
58
65
|
|
|
66
|
+
// Translation lives in `iapShapes`; it is published from here, where every
|
|
67
|
+
// consumer has always imported it from.
|
|
68
|
+
export { toProviderPurchaseState } from './iapShapes';
|
|
69
|
+
|
|
59
70
|
/**
|
|
60
71
|
* Exactly what this adapter needs from the binding.
|
|
61
72
|
*
|
|
@@ -78,67 +89,6 @@ interface IapModule {
|
|
|
78
89
|
deepLinkToSubscriptions?(params: { skuAndroid?: string }): Promise<void>;
|
|
79
90
|
}
|
|
80
91
|
|
|
81
|
-
interface RawProduct {
|
|
82
|
-
id?: string;
|
|
83
|
-
productId?: string;
|
|
84
|
-
title?: string;
|
|
85
|
-
name?: string;
|
|
86
|
-
description?: string;
|
|
87
|
-
displayPrice?: string;
|
|
88
|
-
localizedPrice?: string;
|
|
89
|
-
currency?: string;
|
|
90
|
-
currencyCode?: string;
|
|
91
|
-
price?: string | number;
|
|
92
|
-
oneTimePurchaseOfferDetails?: {
|
|
93
|
-
formattedPrice?: string;
|
|
94
|
-
priceAmountMicros?: string | number;
|
|
95
|
-
priceCurrencyCode?: string;
|
|
96
|
-
};
|
|
97
|
-
subscriptionOfferDetails?: RawSubscriptionOffer[];
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
interface RawSubscriptionOffer {
|
|
101
|
-
offerToken?: string;
|
|
102
|
-
offerId?: string;
|
|
103
|
-
basePlanId?: string;
|
|
104
|
-
pricingPhases?: {
|
|
105
|
-
pricingPhaseList?: Array<{
|
|
106
|
-
formattedPrice?: string;
|
|
107
|
-
priceAmountMicros?: string | number;
|
|
108
|
-
priceCurrencyCode?: string;
|
|
109
|
-
billingPeriod?: string;
|
|
110
|
-
recurrenceMode?: number;
|
|
111
|
-
billingCycleCount?: number;
|
|
112
|
-
}>;
|
|
113
|
-
};
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
interface RawPurchase {
|
|
117
|
-
id?: string;
|
|
118
|
-
transactionId?: string;
|
|
119
|
-
productId?: string;
|
|
120
|
-
ids?: string[];
|
|
121
|
-
purchaseToken?: string;
|
|
122
|
-
purchaseStateAndroid?: number | null;
|
|
123
|
-
/** A string in the 16.x binding: `'pending' | 'purchased' | 'unknown'`. */
|
|
124
|
-
purchaseState?: number | string;
|
|
125
|
-
transactionDate?: number;
|
|
126
|
-
isAcknowledgedAndroid?: boolean;
|
|
127
|
-
isAcknowledged?: boolean;
|
|
128
|
-
autoRenewingAndroid?: boolean;
|
|
129
|
-
isAutoRenewing?: boolean;
|
|
130
|
-
expirationDateAndroid?: number;
|
|
131
|
-
transactionReceipt?: string;
|
|
132
|
-
dataAndroid?: string;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
/** What `purchaseErrorListener` delivers. */
|
|
136
|
-
interface RawPurchaseError {
|
|
137
|
-
code?: string;
|
|
138
|
-
message?: string;
|
|
139
|
-
productId?: string | null;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
92
|
/**
|
|
143
93
|
* How long to wait for the outcome event after `requestPurchase` has resolved.
|
|
144
94
|
* The binding emits the event before or as it resolves, so this only bounds the
|
|
@@ -193,12 +143,6 @@ export function toRequestProps(request: ProviderPurchaseRequest): RequestPurchas
|
|
|
193
143
|
};
|
|
194
144
|
}
|
|
195
145
|
|
|
196
|
-
/** Play's `PurchaseState`: 0 unspecified, 1 purchased, 2 pending. */
|
|
197
|
-
const PLAY_STATE_PURCHASED = 1;
|
|
198
|
-
const PLAY_STATE_PENDING = 2;
|
|
199
|
-
|
|
200
|
-
// Required lazily (rn-core's lazy peer) so importing the package does not require
|
|
201
|
-
// the native module.
|
|
202
146
|
const iapPeer = lazyPeer('react-native-iap', () => {
|
|
203
147
|
// Inside `try`, so Metro bundles an app that leaves this optional peer out.
|
|
204
148
|
try {
|
|
@@ -220,154 +164,6 @@ function loadModule(): IapModule {
|
|
|
220
164
|
);
|
|
221
165
|
}
|
|
222
166
|
|
|
223
|
-
function toMicros(value: string | number | undefined): number {
|
|
224
|
-
if (typeof value === 'number') {
|
|
225
|
-
return value;
|
|
226
|
-
}
|
|
227
|
-
if (typeof value === 'string') {
|
|
228
|
-
const parsed = Number.parseInt(value, 10);
|
|
229
|
-
return Number.isFinite(parsed) ? parsed : 0;
|
|
230
|
-
}
|
|
231
|
-
return 0;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
function normalizeProduct(raw: RawProduct, productType: ProductType): ProviderProduct {
|
|
235
|
-
const oneTime = raw.oneTimePurchaseOfferDetails;
|
|
236
|
-
const currencyCode = oneTime?.priceCurrencyCode ?? raw.currencyCode ?? raw.currency ?? '';
|
|
237
|
-
|
|
238
|
-
return {
|
|
239
|
-
storeProductId: raw.id ?? raw.productId ?? '',
|
|
240
|
-
productType,
|
|
241
|
-
title: raw.title ?? raw.name ?? '',
|
|
242
|
-
description: raw.description ?? '',
|
|
243
|
-
// Store-formatted, never built here: only the store knows the right currency
|
|
244
|
-
// symbol, separator, and placement for the user's locale (FR-028).
|
|
245
|
-
formattedPrice: oneTime?.formattedPrice ?? raw.displayPrice ?? raw.localizedPrice ?? '',
|
|
246
|
-
currencyCode,
|
|
247
|
-
priceAmountMicros: toMicros(oneTime?.priceAmountMicros ?? raw.price),
|
|
248
|
-
offers: (raw.subscriptionOfferDetails ?? []).map(offer => normalizeOffer(offer, currencyCode)),
|
|
249
|
-
};
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
function normalizeOffer(
|
|
253
|
-
offer: RawSubscriptionOffer,
|
|
254
|
-
fallbackCurrency: string,
|
|
255
|
-
): ProviderProduct['offers'][number] {
|
|
256
|
-
const phases = offer.pricingPhases?.pricingPhaseList ?? [];
|
|
257
|
-
// The last phase is the recurring price; earlier ones are trial or
|
|
258
|
-
// introductory. Play orders them that way.
|
|
259
|
-
const recurring = phases[phases.length - 1];
|
|
260
|
-
const intro = phases.length > 1 ? phases[0] : undefined;
|
|
261
|
-
const isFreeTrial = intro !== undefined && toMicros(intro.priceAmountMicros) === 0;
|
|
262
|
-
|
|
263
|
-
return {
|
|
264
|
-
offerKey: offer.offerToken ?? offer.offerId ?? offer.basePlanId ?? '',
|
|
265
|
-
billingPeriod: recurring?.billingPeriod ?? '',
|
|
266
|
-
formattedPrice: recurring?.formattedPrice ?? '',
|
|
267
|
-
priceAmountMicros: toMicros(recurring?.priceAmountMicros),
|
|
268
|
-
currencyCode: recurring?.priceCurrencyCode ?? fallbackCurrency,
|
|
269
|
-
trialPeriod: isFreeTrial ? intro?.billingPeriod : undefined,
|
|
270
|
-
introductoryPrice:
|
|
271
|
-
intro && !isFreeTrial
|
|
272
|
-
? {
|
|
273
|
-
formattedPrice: intro.formattedPrice ?? '',
|
|
274
|
-
period: intro.billingPeriod ?? '',
|
|
275
|
-
cycles: intro.billingCycleCount ?? 1,
|
|
276
|
-
}
|
|
277
|
-
: undefined,
|
|
278
|
-
// Play decides eligibility by only returning offers the user qualifies for,
|
|
279
|
-
// so forwarding `true` reports the store's determination rather than
|
|
280
|
-
// computing our own (FR-026).
|
|
281
|
-
isEligible: true,
|
|
282
|
-
};
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
/**
|
|
286
|
-
* Reads the purchase state from whichever field the binding filled.
|
|
287
|
-
*
|
|
288
|
-
* Pending is preserved, never flattened to purchased: that distinction is what
|
|
289
|
-
* stops an entitlement being granted for money that has not arrived (FR-037).
|
|
290
|
-
* And an unrecognized or missing state is `unspecified`, never `purchased`.
|
|
291
|
-
*
|
|
292
|
-
* 0.1.0 read only Play's numeric state and treated anything else as purchased.
|
|
293
|
-
* The 16.x binding hands JavaScript the *string* state and does not carry the
|
|
294
|
-
* numeric one across, so on a device every pending purchase arrived as a string,
|
|
295
|
-
* fell through to the default, and was granted. Unit tests could not see it: they
|
|
296
|
-
* drive the fake store, which never passes through here.
|
|
297
|
-
*/
|
|
298
|
-
export function toProviderPurchaseState(
|
|
299
|
-
raw: Pick<RawPurchase, 'purchaseStateAndroid' | 'purchaseState'>,
|
|
300
|
-
): ProviderPurchase['state'] {
|
|
301
|
-
let numeric: number | undefined;
|
|
302
|
-
if (typeof raw.purchaseStateAndroid === 'number') {
|
|
303
|
-
numeric = raw.purchaseStateAndroid;
|
|
304
|
-
} else if (typeof raw.purchaseState === 'number') {
|
|
305
|
-
numeric = raw.purchaseState;
|
|
306
|
-
}
|
|
307
|
-
if (numeric !== undefined) {
|
|
308
|
-
if (numeric === PLAY_STATE_PENDING) {
|
|
309
|
-
return 'pending';
|
|
310
|
-
}
|
|
311
|
-
return numeric === PLAY_STATE_PURCHASED ? 'purchased' : 'unspecified';
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
switch (typeof raw.purchaseState === 'string' ? raw.purchaseState.toLowerCase() : '') {
|
|
315
|
-
case 'purchased':
|
|
316
|
-
case 'restored':
|
|
317
|
-
return 'purchased';
|
|
318
|
-
case 'pending':
|
|
319
|
-
case 'deferred':
|
|
320
|
-
return 'pending';
|
|
321
|
-
default:
|
|
322
|
-
return 'unspecified';
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
function normalizePurchase(raw: RawPurchase): ProviderPurchase {
|
|
327
|
-
return {
|
|
328
|
-
storeProductId: raw.productId ?? raw.ids?.[0] ?? '',
|
|
329
|
-
transactionId: raw.id ?? raw.transactionId ?? raw.purchaseToken ?? '',
|
|
330
|
-
token: raw.purchaseToken ?? '',
|
|
331
|
-
state: toProviderPurchaseState(raw),
|
|
332
|
-
purchasedAt: raw.transactionDate ?? Date.now(),
|
|
333
|
-
isAcknowledged: raw.isAcknowledgedAndroid ?? raw.isAcknowledged ?? false,
|
|
334
|
-
expiresAt: raw.expirationDateAndroid,
|
|
335
|
-
willAutoRenew: raw.autoRenewingAndroid ?? raw.isAutoRenewing,
|
|
336
|
-
// Opaque material for the app's backend. Carried here and handed to the
|
|
337
|
-
// validation hook only — never logged or put in an event (FR-074).
|
|
338
|
-
verificationPayload: raw.transactionReceipt ?? raw.dataAndroid,
|
|
339
|
-
};
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
/** Maps a binding error code onto the port's vocabulary, so no store code escapes. */
|
|
343
|
-
function mapErrorCode(code: string | undefined): ProviderFailureReason {
|
|
344
|
-
switch (code) {
|
|
345
|
-
case 'E_USER_CANCELLED':
|
|
346
|
-
case 'user-cancelled':
|
|
347
|
-
return 'user_cancelled';
|
|
348
|
-
case 'E_ALREADY_OWNED':
|
|
349
|
-
case 'already-owned':
|
|
350
|
-
case 'item-already-owned':
|
|
351
|
-
return 'already_owned';
|
|
352
|
-
case 'E_ITEM_UNAVAILABLE':
|
|
353
|
-
case 'item-unavailable':
|
|
354
|
-
case 'sku-not-found':
|
|
355
|
-
return 'unknown_product';
|
|
356
|
-
case 'E_NOT_PREPARED':
|
|
357
|
-
case 'not-prepared':
|
|
358
|
-
return 'not_ready';
|
|
359
|
-
case 'E_NETWORK_ERROR':
|
|
360
|
-
case 'network-error':
|
|
361
|
-
return 'network_error';
|
|
362
|
-
case 'E_DEFERRED_PAYMENT':
|
|
363
|
-
// Not a failure — a pending purchase. The caller resolves it from the
|
|
364
|
-
// purchase update rather than from this error.
|
|
365
|
-
return 'store_error';
|
|
366
|
-
default:
|
|
367
|
-
return 'store_error';
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
|
|
371
167
|
const ANDROID_CAPABILITIES: ProviderCapabilities = {
|
|
372
168
|
supportsSubscriptions: true,
|
|
373
169
|
supportsPlanChange: true,
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The binding's shapes, and their translation into the port's vocabulary
|
|
3
|
+
* (PAY-12).
|
|
4
|
+
*
|
|
5
|
+
* The binding speaks Google Play: prices as micros in strings, offer tokens,
|
|
6
|
+
* numeric purchase states, store error codes. The port speaks this package's
|
|
7
|
+
* own. Keeping the translation here, with no I/O, no listeners and no module
|
|
8
|
+
* loading, is what makes it checkable against recorded payloads — and leaves
|
|
9
|
+
* the adapter next door about the calls it actually makes.
|
|
10
|
+
*
|
|
11
|
+
* The raw types are declared narrowly on purpose: they document the coupling,
|
|
12
|
+
* and a binding upgrade that changes one of these shapes fails to typecheck
|
|
13
|
+
* here rather than failing silently on a user's device.
|
|
14
|
+
*/
|
|
15
|
+
import type { ProductType } from '../config/types';
|
|
16
|
+
import type { ProviderFailureReason, ProviderProduct, ProviderPurchase } from './port';
|
|
17
|
+
|
|
18
|
+
export interface RawProduct {
|
|
19
|
+
id?: string;
|
|
20
|
+
productId?: string;
|
|
21
|
+
title?: string;
|
|
22
|
+
name?: string;
|
|
23
|
+
description?: string;
|
|
24
|
+
displayPrice?: string;
|
|
25
|
+
localizedPrice?: string;
|
|
26
|
+
currency?: string;
|
|
27
|
+
currencyCode?: string;
|
|
28
|
+
price?: string | number;
|
|
29
|
+
oneTimePurchaseOfferDetails?: {
|
|
30
|
+
formattedPrice?: string;
|
|
31
|
+
priceAmountMicros?: string | number;
|
|
32
|
+
priceCurrencyCode?: string;
|
|
33
|
+
};
|
|
34
|
+
subscriptionOfferDetails?: RawSubscriptionOffer[];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface RawSubscriptionOffer {
|
|
38
|
+
offerToken?: string;
|
|
39
|
+
offerId?: string;
|
|
40
|
+
basePlanId?: string;
|
|
41
|
+
pricingPhases?: {
|
|
42
|
+
pricingPhaseList?: Array<{
|
|
43
|
+
formattedPrice?: string;
|
|
44
|
+
priceAmountMicros?: string | number;
|
|
45
|
+
priceCurrencyCode?: string;
|
|
46
|
+
billingPeriod?: string;
|
|
47
|
+
recurrenceMode?: number;
|
|
48
|
+
billingCycleCount?: number;
|
|
49
|
+
}>;
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface RawPurchase {
|
|
54
|
+
id?: string;
|
|
55
|
+
transactionId?: string;
|
|
56
|
+
productId?: string;
|
|
57
|
+
ids?: string[];
|
|
58
|
+
purchaseToken?: string;
|
|
59
|
+
purchaseStateAndroid?: number | null;
|
|
60
|
+
/** A string in the 16.x binding: `'pending' | 'purchased' | 'unknown'`. */
|
|
61
|
+
purchaseState?: number | string;
|
|
62
|
+
transactionDate?: number;
|
|
63
|
+
isAcknowledgedAndroid?: boolean;
|
|
64
|
+
isAcknowledged?: boolean;
|
|
65
|
+
autoRenewingAndroid?: boolean;
|
|
66
|
+
isAutoRenewing?: boolean;
|
|
67
|
+
expirationDateAndroid?: number;
|
|
68
|
+
transactionReceipt?: string;
|
|
69
|
+
dataAndroid?: string;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** What `purchaseErrorListener` delivers. */
|
|
73
|
+
export interface RawPurchaseError {
|
|
74
|
+
code?: string;
|
|
75
|
+
message?: string;
|
|
76
|
+
productId?: string | null;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** Play's `PurchaseState`: 0 unspecified, 1 purchased, 2 pending. */
|
|
80
|
+
const PLAY_STATE_PURCHASED = 1;
|
|
81
|
+
const PLAY_STATE_PENDING = 2;
|
|
82
|
+
|
|
83
|
+
// Required lazily (rn-core's lazy peer) so importing the package does not require
|
|
84
|
+
// the native module.
|
|
85
|
+
|
|
86
|
+
export function toMicros(value: string | number | undefined): number {
|
|
87
|
+
if (typeof value === 'number') {
|
|
88
|
+
return value;
|
|
89
|
+
}
|
|
90
|
+
if (typeof value === 'string') {
|
|
91
|
+
const parsed = Number.parseInt(value, 10);
|
|
92
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
93
|
+
}
|
|
94
|
+
return 0;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function normalizeProduct(raw: RawProduct, productType: ProductType): ProviderProduct {
|
|
98
|
+
const oneTime = raw.oneTimePurchaseOfferDetails;
|
|
99
|
+
const currencyCode = oneTime?.priceCurrencyCode ?? raw.currencyCode ?? raw.currency ?? '';
|
|
100
|
+
|
|
101
|
+
return {
|
|
102
|
+
storeProductId: raw.id ?? raw.productId ?? '',
|
|
103
|
+
productType,
|
|
104
|
+
title: raw.title ?? raw.name ?? '',
|
|
105
|
+
description: raw.description ?? '',
|
|
106
|
+
// Store-formatted, never built here: only the store knows the right currency
|
|
107
|
+
// symbol, separator, and placement for the user's locale (FR-028).
|
|
108
|
+
formattedPrice: oneTime?.formattedPrice ?? raw.displayPrice ?? raw.localizedPrice ?? '',
|
|
109
|
+
currencyCode,
|
|
110
|
+
priceAmountMicros: toMicros(oneTime?.priceAmountMicros ?? raw.price),
|
|
111
|
+
offers: (raw.subscriptionOfferDetails ?? []).map(offer => normalizeOffer(offer, currencyCode)),
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export function normalizeOffer(
|
|
116
|
+
offer: RawSubscriptionOffer,
|
|
117
|
+
fallbackCurrency: string,
|
|
118
|
+
): ProviderProduct['offers'][number] {
|
|
119
|
+
const phases = offer.pricingPhases?.pricingPhaseList ?? [];
|
|
120
|
+
// The last phase is the recurring price; earlier ones are trial or
|
|
121
|
+
// introductory. Play orders them that way.
|
|
122
|
+
const recurring = phases[phases.length - 1];
|
|
123
|
+
const intro = phases.length > 1 ? phases[0] : undefined;
|
|
124
|
+
const isFreeTrial = intro !== undefined && toMicros(intro.priceAmountMicros) === 0;
|
|
125
|
+
|
|
126
|
+
return {
|
|
127
|
+
offerKey: offer.offerToken ?? offer.offerId ?? offer.basePlanId ?? '',
|
|
128
|
+
billingPeriod: recurring?.billingPeriod ?? '',
|
|
129
|
+
formattedPrice: recurring?.formattedPrice ?? '',
|
|
130
|
+
priceAmountMicros: toMicros(recurring?.priceAmountMicros),
|
|
131
|
+
currencyCode: recurring?.priceCurrencyCode ?? fallbackCurrency,
|
|
132
|
+
trialPeriod: isFreeTrial ? intro?.billingPeriod : undefined,
|
|
133
|
+
introductoryPrice:
|
|
134
|
+
intro && !isFreeTrial
|
|
135
|
+
? {
|
|
136
|
+
formattedPrice: intro.formattedPrice ?? '',
|
|
137
|
+
period: intro.billingPeriod ?? '',
|
|
138
|
+
cycles: intro.billingCycleCount ?? 1,
|
|
139
|
+
}
|
|
140
|
+
: undefined,
|
|
141
|
+
// Play decides eligibility by only returning offers the user qualifies for,
|
|
142
|
+
// so forwarding `true` reports the store's determination rather than
|
|
143
|
+
// computing our own (FR-026).
|
|
144
|
+
isEligible: true,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Reads the purchase state from whichever field the binding filled.
|
|
150
|
+
*
|
|
151
|
+
* Pending is preserved, never flattened to purchased: that distinction is what
|
|
152
|
+
* stops an entitlement being granted for money that has not arrived (FR-037).
|
|
153
|
+
* And an unrecognized or missing state is `unspecified`, never `purchased`.
|
|
154
|
+
*
|
|
155
|
+
* 0.1.0 read only Play's numeric state and treated anything else as purchased.
|
|
156
|
+
* The 16.x binding hands JavaScript the *string* state and does not carry the
|
|
157
|
+
* numeric one across, so on a device every pending purchase arrived as a string,
|
|
158
|
+
* fell through to the default, and was granted. Unit tests could not see it: they
|
|
159
|
+
* drive the fake store, which never passes through here.
|
|
160
|
+
*/
|
|
161
|
+
export function toProviderPurchaseState(
|
|
162
|
+
raw: Pick<RawPurchase, 'purchaseStateAndroid' | 'purchaseState'>,
|
|
163
|
+
): ProviderPurchase['state'] {
|
|
164
|
+
let numeric: number | undefined;
|
|
165
|
+
if (typeof raw.purchaseStateAndroid === 'number') {
|
|
166
|
+
numeric = raw.purchaseStateAndroid;
|
|
167
|
+
} else if (typeof raw.purchaseState === 'number') {
|
|
168
|
+
numeric = raw.purchaseState;
|
|
169
|
+
}
|
|
170
|
+
if (numeric !== undefined) {
|
|
171
|
+
if (numeric === PLAY_STATE_PENDING) {
|
|
172
|
+
return 'pending';
|
|
173
|
+
}
|
|
174
|
+
return numeric === PLAY_STATE_PURCHASED ? 'purchased' : 'unspecified';
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
switch (typeof raw.purchaseState === 'string' ? raw.purchaseState.toLowerCase() : '') {
|
|
178
|
+
case 'purchased':
|
|
179
|
+
case 'restored':
|
|
180
|
+
return 'purchased';
|
|
181
|
+
case 'pending':
|
|
182
|
+
case 'deferred':
|
|
183
|
+
return 'pending';
|
|
184
|
+
default:
|
|
185
|
+
return 'unspecified';
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export function normalizePurchase(raw: RawPurchase): ProviderPurchase {
|
|
190
|
+
return {
|
|
191
|
+
storeProductId: raw.productId ?? raw.ids?.[0] ?? '',
|
|
192
|
+
transactionId: raw.id ?? raw.transactionId ?? raw.purchaseToken ?? '',
|
|
193
|
+
token: raw.purchaseToken ?? '',
|
|
194
|
+
state: toProviderPurchaseState(raw),
|
|
195
|
+
purchasedAt: raw.transactionDate ?? Date.now(),
|
|
196
|
+
isAcknowledged: raw.isAcknowledgedAndroid ?? raw.isAcknowledged ?? false,
|
|
197
|
+
expiresAt: raw.expirationDateAndroid,
|
|
198
|
+
willAutoRenew: raw.autoRenewingAndroid ?? raw.isAutoRenewing,
|
|
199
|
+
// Opaque material for the app's backend. Carried here and handed to the
|
|
200
|
+
// validation hook only — never logged or put in an event (FR-074).
|
|
201
|
+
verificationPayload: raw.transactionReceipt ?? raw.dataAndroid,
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/** Maps a binding error code onto the port's vocabulary, so no store code escapes. */
|
|
206
|
+
export function mapErrorCode(code: string | undefined): ProviderFailureReason {
|
|
207
|
+
switch (code) {
|
|
208
|
+
case 'E_USER_CANCELLED':
|
|
209
|
+
case 'user-cancelled':
|
|
210
|
+
return 'user_cancelled';
|
|
211
|
+
case 'E_ALREADY_OWNED':
|
|
212
|
+
case 'already-owned':
|
|
213
|
+
case 'item-already-owned':
|
|
214
|
+
return 'already_owned';
|
|
215
|
+
case 'E_ITEM_UNAVAILABLE':
|
|
216
|
+
case 'item-unavailable':
|
|
217
|
+
case 'sku-not-found':
|
|
218
|
+
return 'unknown_product';
|
|
219
|
+
case 'E_NOT_PREPARED':
|
|
220
|
+
case 'not-prepared':
|
|
221
|
+
return 'not_ready';
|
|
222
|
+
case 'E_NETWORK_ERROR':
|
|
223
|
+
case 'network-error':
|
|
224
|
+
return 'network_error';
|
|
225
|
+
case 'E_DEFERRED_PAYMENT':
|
|
226
|
+
// Not a failure — a pending purchase. The caller resolves it from the
|
|
227
|
+
// purchase update rather than from this error.
|
|
228
|
+
return 'store_error';
|
|
229
|
+
default:
|
|
230
|
+
return 'store_error';
|
|
231
|
+
}
|
|
232
|
+
}
|
package/src/config/types.ts
CHANGED
|
@@ -130,6 +130,14 @@ export interface PaymentConfig {
|
|
|
130
130
|
* proceed (FR-015).
|
|
131
131
|
*/
|
|
132
132
|
isProductionBuild: boolean;
|
|
133
|
+
/**
|
|
134
|
+
* Lets a non-production build complete purchases through the real store, so a
|
|
135
|
+
* Play license tester can buy on a debug build (PAY-16). Accepted only with
|
|
136
|
+
* `environment: 'test'`; any other environment rejects it at startup. License
|
|
137
|
+
* testers are not charged; any other account on the device would be, so keep
|
|
138
|
+
* it out of builds that reach real users.
|
|
139
|
+
*/
|
|
140
|
+
allowStoreTestPurchases?: boolean;
|
|
133
141
|
/**
|
|
134
142
|
* Global sell switch. When false, purchases report `disabled` but entitlements
|
|
135
143
|
* already held stay readable — turning off selling is not the same as taking
|
package/src/config/validate.ts
CHANGED
|
@@ -199,6 +199,16 @@ export function validateConfig(
|
|
|
199
199
|
* continuing silently is exactly the failure this package exists to prevent.
|
|
200
200
|
*/
|
|
201
201
|
export function assertEnvironmentSafe(config: PaymentConfig): void {
|
|
202
|
+
if (config.allowStoreTestPurchases === true && config.environment !== 'test') {
|
|
203
|
+
throw new PaymentConfigError({
|
|
204
|
+
code: 'PAYMENT_INVALID_CONFIG',
|
|
205
|
+
message:
|
|
206
|
+
`allowStoreTestPurchases is only accepted with environment "test", not ` +
|
|
207
|
+
`"${config.environment}": it exists for license testers, not for real users.`,
|
|
208
|
+
operation: 'configure',
|
|
209
|
+
offenders: ['allowStoreTestPurchases'],
|
|
210
|
+
});
|
|
211
|
+
}
|
|
202
212
|
if (config.environment === 'production' && !config.isProductionBuild) {
|
|
203
213
|
throw new PaymentConfigError({
|
|
204
214
|
code: 'PAYMENT_PRODUCTION_CONFIG_IN_NON_PRODUCTION_BUILD',
|
|
@@ -213,12 +223,17 @@ export function assertEnvironmentSafe(config: PaymentConfig): void {
|
|
|
213
223
|
}
|
|
214
224
|
|
|
215
225
|
/**
|
|
216
|
-
* Whether this configuration may hand a
|
|
226
|
+
* Whether this configuration may hand a purchase to a real store (FR-014).
|
|
217
227
|
*
|
|
218
|
-
*
|
|
219
|
-
*
|
|
220
|
-
*
|
|
228
|
+
* True in a production build configured for production, and in a `test`
|
|
229
|
+
* environment that opted in with `allowStoreTestPurchases` (PAY-16). False
|
|
230
|
+
* everywhere else. The engine records each refusal in observable status so a
|
|
231
|
+
* test can assert the block happened — the difference between a guarantee and a
|
|
232
|
+
* hope.
|
|
221
233
|
*/
|
|
222
234
|
export function mayCreateRealTransaction(config: PaymentConfig): boolean {
|
|
235
|
+
if (config.environment === 'test' && config.allowStoreTestPurchases === true) {
|
|
236
|
+
return true;
|
|
237
|
+
}
|
|
223
238
|
return config.environment === 'production' && config.isProductionBuild;
|
|
224
239
|
}
|
package/src/core/retry.ts
CHANGED
|
@@ -9,17 +9,10 @@
|
|
|
9
9
|
* schedule without burning real time.
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import {
|
|
13
|
-
computeBackoff,
|
|
14
|
-
runWithRetry as runCoreRetry,
|
|
15
|
-
type RetryHandle,
|
|
16
|
-
} from '@chipmobilesdk/rn-core';
|
|
12
|
+
import { computeBackoff } from '@chipmobilesdk/rn-core';
|
|
17
13
|
|
|
18
14
|
import type { BackoffConfig } from '../config/types';
|
|
19
15
|
|
|
20
|
-
/** Opaque timer handle: a Node Timeout and a browser numeric id are both valid. */
|
|
21
|
-
type TimerHandle = ReturnType<typeof setTimeout> | number;
|
|
22
|
-
|
|
23
16
|
/**
|
|
24
17
|
* Delay before `attempt` (1-based), capped and optionally jittered.
|
|
25
18
|
*
|
|
@@ -43,53 +36,3 @@ function toSpec(config: Required<BackoffConfig>) {
|
|
|
43
36
|
maxAttempts: config.maxAttempts,
|
|
44
37
|
};
|
|
45
38
|
}
|
|
46
|
-
|
|
47
|
-
/** The full schedule, for asserting a retry policy without waiting for it. */
|
|
48
|
-
export function backoffSchedule(config: Required<BackoffConfig>): number[] {
|
|
49
|
-
const delays: number[] = [];
|
|
50
|
-
for (let attempt = 1; attempt <= config.maxAttempts; attempt += 1) {
|
|
51
|
-
delays.push(backoffDelayMs(attempt, config, () => 1));
|
|
52
|
-
}
|
|
53
|
-
return delays;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export type { RetryHandle } from '@chipmobilesdk/rn-core';
|
|
57
|
-
|
|
58
|
-
export interface RetryRunnerOptions {
|
|
59
|
-
config: Required<BackoffConfig>;
|
|
60
|
-
/** Return true when the attempt succeeded and retrying should stop. */
|
|
61
|
-
attempt: (attemptNumber: number) => Promise<boolean>;
|
|
62
|
-
/** Called once the per-session budget is spent without success. */
|
|
63
|
-
onExhausted?: (attempts: number) => void;
|
|
64
|
-
/**
|
|
65
|
-
* Injected so a test can run the whole schedule synchronously instead of
|
|
66
|
-
* waiting minutes of real backoff. The handle is opaque, because a Node
|
|
67
|
-
* `Timeout` and a browser numeric id are both valid here.
|
|
68
|
-
*/
|
|
69
|
-
setTimeoutFn?: (fn: () => void, ms: number) => TimerHandle;
|
|
70
|
-
clearTimeoutFn?: (handle: TimerHandle) => void;
|
|
71
|
-
random?: () => number;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Runs `attempt` under the bounded schedule (rn-core's runner).
|
|
76
|
-
*
|
|
77
|
-
* Exhaustion is reported rather than thrown: for finalization, a spent session
|
|
78
|
-
* budget is not a failure but a handoff to the next launch, and the caller is
|
|
79
|
-
* the only one that knows which of the two it is. A throwing attempt is a failed
|
|
80
|
-
* attempt, nothing more: letting it escape would silently drop a paid transaction.
|
|
81
|
-
*/
|
|
82
|
-
export function runWithRetry(options: RetryRunnerOptions): RetryHandle {
|
|
83
|
-
const setTimeoutFn = options.setTimeoutFn ?? setTimeout;
|
|
84
|
-
const clearTimeoutFn = options.clearTimeoutFn ?? clearTimeout;
|
|
85
|
-
return runCoreRetry({
|
|
86
|
-
spec: toSpec(options.config),
|
|
87
|
-
attempt: options.attempt,
|
|
88
|
-
onExhausted: options.onExhausted,
|
|
89
|
-
random: options.random,
|
|
90
|
-
scheduler: {
|
|
91
|
-
setTimeout: (callback, ms) => setTimeoutFn(callback, ms),
|
|
92
|
-
clearTimeout: handle => clearTimeoutFn(handle as TimerHandle),
|
|
93
|
-
},
|
|
94
|
-
});
|
|
95
|
-
}
|
|
@@ -257,7 +257,7 @@ export function createEmitter(
|
|
|
257
257
|
/**
|
|
258
258
|
* Fields that must never appear in an event payload.
|
|
259
259
|
*
|
|
260
|
-
* Used by `__tests__/
|
|
260
|
+
* Used by `__tests__/diagnostics.test.ts` to walk every emitted event across the
|
|
261
261
|
* whole suite. Enumerating the ban here rather than in the test keeps the rule
|
|
262
262
|
* next to the type it constrains.
|
|
263
263
|
*/
|