@mpgd/adapter-capacitor 0.8.0 → 0.9.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/README.md +107 -0
- package/dist/commerce-bridge.d.ts +59 -0
- package/dist/commerce-bridge.js +265 -0
- package/dist/index.d.ts +10 -1
- package/dist/index.js +96 -6
- package/dist/providers.d.ts +46 -1
- package/dist/providers.js +155 -37
- package/dist/store-commerce-provider.d.ts +55 -0
- package/dist/store-commerce-provider.js +437 -0
- package/dist/store-sdk.d.ts +131 -0
- package/dist/store-sdk.js +83 -0
- package/package.json +7 -7
package/dist/providers.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type BridgeEvent, type BridgeMethod, type BridgeRequest, type BridgeResponse } from '@mpgd/bridge';
|
|
2
2
|
import type { AdProvider } from '@mpgd/platform/ads';
|
|
3
|
-
import { type PlatformProviderAvailability, type PlatformProviderFeature, type ProductType } from '@mpgd/platform';
|
|
3
|
+
import { type CommerceCapabilities, type PlatformProviderAvailability, type PlatformProviderFeature, type ProductType } from '@mpgd/platform';
|
|
4
|
+
import { type CommerceProvider } from '@mpgd/platform/commerce';
|
|
4
5
|
export interface NativeBridge {
|
|
5
6
|
request(input: BridgeRequest): Promise<BridgeResponse>;
|
|
6
7
|
/**
|
|
@@ -17,17 +18,61 @@ export interface NativeBridge {
|
|
|
17
18
|
*/
|
|
18
19
|
export interface CapacitorServiceProvider {
|
|
19
20
|
readonly id: string;
|
|
21
|
+
/**
|
|
22
|
+
* Serves the declared `methods`. For a provider with `commerce`, it also
|
|
23
|
+
* serves the ten commerce bridge methods and must implement `subscribe`;
|
|
24
|
+
* `createCommerceServiceProvider` builds one from a `CommerceProvider`.
|
|
25
|
+
*/
|
|
20
26
|
readonly bridge: NativeBridge;
|
|
21
27
|
readonly features: readonly PlatformProviderFeature[];
|
|
28
|
+
/**
|
|
29
|
+
* Declared v1 bridge methods. A provider with `commerce` declares no
|
|
30
|
+
* `commerce.*` method: the four v1 commerce methods and the six
|
|
31
|
+
* `commerce.v2.*` methods are implied and owned by the v2 provider.
|
|
32
|
+
*/
|
|
22
33
|
readonly methods: readonly BridgeMethod[];
|
|
23
34
|
/** Optional native event surface. The base adapter never imports its SDK. */
|
|
24
35
|
readonly adProvider?: AdProvider;
|
|
36
|
+
/**
|
|
37
|
+
* Optional commerce v2 provider (`createStoreCommerceProvider`). Its
|
|
38
|
+
* `getCapabilities()` is the readiness of the declared `nativeIap` and
|
|
39
|
+
* `subscriptionIap` features; `getAvailability` covers the other features.
|
|
40
|
+
*/
|
|
41
|
+
readonly commerce?: CommerceProvider;
|
|
25
42
|
getAvailability(): Promise<Readonly<Partial<Record<PlatformProviderFeature, PlatformProviderAvailability>>>>;
|
|
26
43
|
}
|
|
44
|
+
/** The provider the registry serves commerce v2 from. */
|
|
45
|
+
export interface CapacitorCommerceServiceProvider extends CapacitorServiceProvider {
|
|
46
|
+
readonly commerce: CommerceProvider;
|
|
47
|
+
readonly bridge: NativeBridge & Required<Pick<NativeBridge, 'subscribe'>>;
|
|
48
|
+
}
|
|
49
|
+
export interface CapacitorProviderCapabilitySnapshot {
|
|
50
|
+
readonly availability: Readonly<Record<PlatformProviderFeature, PlatformProviderAvailability>>;
|
|
51
|
+
/**
|
|
52
|
+
* Present when a commerce v2 provider is registered: its validated
|
|
53
|
+
* `getCapabilities()` answer from the same bounded read that produced the
|
|
54
|
+
* `nativeIap` and `subscriptionIap` states, or a degraded block
|
|
55
|
+
* (`availability: 'temporarily-unavailable'`, nothing sold) when that read
|
|
56
|
+
* stalled, failed, or returned a shape the contract rejects.
|
|
57
|
+
*/
|
|
58
|
+
readonly commerce?: CommerceCapabilities;
|
|
59
|
+
}
|
|
27
60
|
export interface CapacitorProviderRegistry {
|
|
28
61
|
readonly byMethod: ReadonlyMap<BridgeMethod, CapacitorServiceProvider>;
|
|
62
|
+
/** The provider that serves commerce v2, when one is registered. */
|
|
63
|
+
readonly commerce?: CapacitorCommerceServiceProvider;
|
|
64
|
+
/** Feature readiness plus the commerce block, from one bounded read per provider. */
|
|
65
|
+
getCapabilitySnapshot(): Promise<CapacitorProviderCapabilitySnapshot>;
|
|
29
66
|
assertMethodReady(method: BridgeMethod, payload?: unknown, productType?: ProductType): Promise<void>;
|
|
30
67
|
getAvailability(): Promise<Readonly<Record<PlatformProviderFeature, PlatformProviderAvailability>>>;
|
|
31
68
|
}
|
|
32
69
|
export declare function createCapacitorProviderRegistry(input: readonly CapacitorServiceProvider[]): CapacitorProviderRegistry;
|
|
33
70
|
export declare function assertProviderResponseData(method: BridgeMethod, data: unknown): void;
|
|
71
|
+
/** The commerce block a gateway reports while its provider cannot be read. */
|
|
72
|
+
export declare function createDegradedCommerceCapabilities(): CommerceCapabilities;
|
|
73
|
+
/**
|
|
74
|
+
* Readiness of the commerce features a v2 provider declares, from its own
|
|
75
|
+
* capability declaration: a feature it does not sell is `unsupported`, and a
|
|
76
|
+
* read that failed or stalled is `temporarily-unavailable`.
|
|
77
|
+
*/
|
|
78
|
+
export declare function deriveCommerceFeatureStates(capabilities: CommerceCapabilities | undefined, features: readonly PlatformProviderFeature[]): Partial<Record<PlatformProviderFeature, PlatformProviderAvailability>>;
|
package/dist/providers.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import { isBridgeCommerceV2Method, } from '@mpgd/bridge';
|
|
1
|
+
import { bridgeCommerceV2Methods, isBridgeCommerceV2Method, } from '@mpgd/bridge';
|
|
2
2
|
import { isAdMobClientRewardEvidence } from '@mpgd/game-services/admob-client-reward';
|
|
3
3
|
import { PlatformOperationError, } from '@mpgd/platform';
|
|
4
|
+
import { createUnsupportedCommerceCapabilities, isCommerceCapabilities, } from '@mpgd/platform/commerce';
|
|
5
|
+
import { assertCommerceBridgeResult } from './commerce-bridge.js';
|
|
4
6
|
const featureMethods = {
|
|
5
7
|
nativeIap: [
|
|
6
8
|
'commerce.getProducts',
|
|
@@ -23,6 +25,12 @@ const featureMethods = {
|
|
|
23
25
|
};
|
|
24
26
|
const providerFeatures = Object.keys(featureMethods);
|
|
25
27
|
const providerMethodSet = new Set(Object.values(featureMethods).flat());
|
|
28
|
+
const commerceFeatures = ['nativeIap', 'subscriptionIap'];
|
|
29
|
+
const v1CommerceMethods = featureMethods.nativeIap;
|
|
30
|
+
const impliedCommerceMethods = [
|
|
31
|
+
...v1CommerceMethods,
|
|
32
|
+
...bridgeCommerceV2Methods,
|
|
33
|
+
];
|
|
26
34
|
const availabilitySet = new Set([
|
|
27
35
|
'unsupported',
|
|
28
36
|
'configuration-required',
|
|
@@ -41,12 +49,14 @@ export function createCapacitorProviderRegistry(input) {
|
|
|
41
49
|
const byMethod = new Map();
|
|
42
50
|
const byFeature = new Map();
|
|
43
51
|
const ids = new Set();
|
|
52
|
+
let commerce;
|
|
44
53
|
for (const provider of providers) {
|
|
45
54
|
if (provider.id.trim() === '' || ids.has(provider.id)) {
|
|
46
55
|
throw new Error(`Capacitor provider ID is empty or registered twice: ${provider.id}`);
|
|
47
56
|
}
|
|
48
57
|
ids.add(provider.id);
|
|
49
|
-
if (provider.features.length === 0
|
|
58
|
+
if (provider.features.length === 0
|
|
59
|
+
|| (provider.methods.length === 0 && provider.commerce === undefined)) {
|
|
50
60
|
throw new Error(`Capacitor provider ${provider.id} must declare features and methods.`);
|
|
51
61
|
}
|
|
52
62
|
const methods = new Set(provider.methods);
|
|
@@ -55,24 +65,43 @@ export function createCapacitorProviderRegistry(input) {
|
|
|
55
65
|
}
|
|
56
66
|
for (const method of methods) {
|
|
57
67
|
if (isBridgeCommerceV2Method(method)) {
|
|
58
|
-
//
|
|
59
|
-
// with B1 and is registered through its own surface.
|
|
68
|
+
// A v2 provider is registered through `commerce`; its bridge methods are implied.
|
|
60
69
|
throw new Error(`Capacitor provider ${provider.id} cannot declare commerce v2 method ${method}; `
|
|
61
|
-
+ 'commerce v2 is unavailable
|
|
70
|
+
+ 'register a v2 provider through `commerce` instead, commerce v2 is unavailable as '
|
|
71
|
+
+ 'a declared method.');
|
|
62
72
|
}
|
|
63
73
|
if (!providerMethodSet.has(method)) {
|
|
64
74
|
throw new Error(`Capacitor provider ${provider.id} cannot replace base method ${method}.`);
|
|
65
75
|
}
|
|
76
|
+
if (provider.commerce !== undefined && v1CommerceMethods.includes(method)) {
|
|
77
|
+
throw new Error(`Capacitor provider ${provider.id} cannot declare both a commerce v2 provider and `
|
|
78
|
+
+ `v1 method ${method}; the registry serves v1 callers from the v2 provider.`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
const implied = provider.commerce === undefined ? [] : impliedCommerceMethods;
|
|
82
|
+
for (const method of [...methods, ...implied]) {
|
|
66
83
|
if (byMethod.has(method)) {
|
|
67
84
|
throw new Error(`Capacitor bridge method ${method} has multiple providers.`);
|
|
68
85
|
}
|
|
69
86
|
}
|
|
87
|
+
const owned = new Set([...methods, ...implied]);
|
|
88
|
+
if (provider.commerce !== undefined) {
|
|
89
|
+
if (!provider.features.some((feature) => commerceFeatures.includes(feature))) {
|
|
90
|
+
throw new Error(`Capacitor provider ${provider.id} declares a commerce v2 provider but no commerce feature.`);
|
|
91
|
+
}
|
|
92
|
+
if (provider.bridge.subscribe === undefined) {
|
|
93
|
+
// Without the event surface a purchase that completes after the store
|
|
94
|
+
// UI closed would never reach the reconciler.
|
|
95
|
+
throw new Error(`Capacitor provider ${provider.id} declares a commerce v2 provider but its bridge has no `
|
|
96
|
+
+ 'subscribe; build it with createCommerceServiceProvider.');
|
|
97
|
+
}
|
|
98
|
+
}
|
|
70
99
|
for (const feature of provider.features) {
|
|
71
100
|
const required = featureMethods[feature];
|
|
72
101
|
if (required === undefined || byFeature.has(feature)) {
|
|
73
102
|
throw new Error(`Capacitor feature ${feature} has multiple or invalid providers.`);
|
|
74
103
|
}
|
|
75
|
-
if (required.some((method) => !
|
|
104
|
+
if (required.some((method) => !owned.has(method))) {
|
|
76
105
|
throw new Error(`Capacitor provider ${provider.id} lacks methods for ${feature}.`);
|
|
77
106
|
}
|
|
78
107
|
byFeature.set(feature, provider);
|
|
@@ -80,67 +109,80 @@ export function createCapacitorProviderRegistry(input) {
|
|
|
80
109
|
if ([...methods].some((method) => !provider.features.some((feature) => featureMethods[feature].includes(method)))) {
|
|
81
110
|
throw new Error(`Capacitor provider ${provider.id} declares an unowned method.`);
|
|
82
111
|
}
|
|
83
|
-
for (const method of
|
|
112
|
+
for (const method of owned) {
|
|
84
113
|
byMethod.set(method, provider);
|
|
85
114
|
}
|
|
115
|
+
if (provider.commerce !== undefined) {
|
|
116
|
+
// Validated above: the bridge implements `subscribe`.
|
|
117
|
+
commerce = provider;
|
|
118
|
+
}
|
|
86
119
|
}
|
|
87
120
|
const readProviderStates = async (provider) => {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
if (!isRecord(reported)) {
|
|
98
|
-
throw new Error('Invalid provider availability.');
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
catch {
|
|
102
|
-
reported = {};
|
|
121
|
+
const states = {};
|
|
122
|
+
const commerceOwned = provider.commerce === undefined
|
|
123
|
+
? []
|
|
124
|
+
: provider.features.filter((feature) => commerceFeatures.includes(feature));
|
|
125
|
+
const bridged = provider.features.filter((feature) => !commerceOwned.includes(feature));
|
|
126
|
+
let commerceBlock;
|
|
127
|
+
if (provider.commerce !== undefined && commerceOwned.length > 0) {
|
|
128
|
+
commerceBlock = await readCommerceCapabilities(provider.commerce);
|
|
129
|
+
Object.assign(states, deriveCommerceFeatureStates(commerceBlock, commerceOwned));
|
|
103
130
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
131
|
+
if (bridged.length > 0) {
|
|
132
|
+
const reported = await readWithTimeout(Promise.resolve().then(() => provider.getAvailability()));
|
|
133
|
+
for (const feature of bridged) {
|
|
134
|
+
const state = isRecord(reported) ? reported[feature] : undefined;
|
|
135
|
+
states[feature] = availabilitySet.has(state)
|
|
136
|
+
? state
|
|
137
|
+
: 'temporarily-unavailable';
|
|
107
138
|
}
|
|
108
139
|
}
|
|
109
|
-
|
|
110
|
-
for (const feature of provider.features) {
|
|
111
|
-
const state = reported[feature];
|
|
112
|
-
states[feature] = availabilitySet.has(state)
|
|
113
|
-
? state
|
|
114
|
-
: 'temporarily-unavailable';
|
|
115
|
-
}
|
|
116
|
-
return states;
|
|
140
|
+
return { states, ...(commerceBlock === undefined ? {} : { commerce: commerceBlock }) };
|
|
117
141
|
};
|
|
118
|
-
const
|
|
142
|
+
const getCapabilitySnapshot = async () => {
|
|
119
143
|
const states = {};
|
|
120
144
|
for (const feature of providerFeatures) {
|
|
121
145
|
states[feature] = 'unsupported';
|
|
122
146
|
}
|
|
147
|
+
let commerceBlock;
|
|
123
148
|
const snapshots = await Promise.all(providers.map(readProviderStates));
|
|
124
149
|
for (const snapshot of snapshots) {
|
|
125
|
-
Object.assign(states, snapshot);
|
|
150
|
+
Object.assign(states, snapshot.states);
|
|
151
|
+
commerceBlock = snapshot.commerce ?? commerceBlock;
|
|
126
152
|
}
|
|
127
|
-
|
|
153
|
+
if (commerce !== undefined && commerceBlock === undefined) {
|
|
154
|
+
// One bounded read serves the feature states and the block, so a stalled,
|
|
155
|
+
// failed, or invalid provider degrades both the same way.
|
|
156
|
+
commerceBlock = createDegradedCommerceCapabilities();
|
|
157
|
+
}
|
|
158
|
+
return {
|
|
159
|
+
availability: states,
|
|
160
|
+
...(commerceBlock === undefined ? {} : { commerce: commerceBlock }),
|
|
161
|
+
};
|
|
128
162
|
};
|
|
163
|
+
const getAvailability = async () => (await getCapabilitySnapshot()).availability;
|
|
129
164
|
return {
|
|
130
165
|
byMethod,
|
|
166
|
+
...(commerce === undefined ? {} : { commerce }),
|
|
131
167
|
getAvailability,
|
|
168
|
+
getCapabilitySnapshot,
|
|
132
169
|
async assertMethodReady(method, payload, productType) {
|
|
133
170
|
const provider = byMethod.get(method);
|
|
134
171
|
if (provider === undefined) {
|
|
135
172
|
return;
|
|
136
173
|
}
|
|
174
|
+
if (isBridgeCommerceV2Method(method)) {
|
|
175
|
+
// The v2 provider reads its own `availability` and fails closed before
|
|
176
|
+
// any store UI opens; the registry adds no second, stale snapshot.
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
137
179
|
if (method === 'ads.unmountBanner') {
|
|
138
180
|
// Teardown must reach the provider that mounted the banner even after
|
|
139
181
|
// its serving state becomes unavailable.
|
|
140
182
|
return;
|
|
141
183
|
}
|
|
142
184
|
// An unrelated provider must not delay a purchase, ad, or identity call.
|
|
143
|
-
const states = await readProviderStates(provider);
|
|
185
|
+
const { states } = await readProviderStates(provider);
|
|
144
186
|
let relevant = provider.features.filter((feature) => featureMethods[feature].includes(method));
|
|
145
187
|
if (method === 'commerce.purchase' && relevant.length > 1) {
|
|
146
188
|
relevant = relevant.filter((feature) => feature === (productType === 'subscription' ? 'subscriptionIap'
|
|
@@ -205,6 +247,73 @@ export function assertProviderResponseData(method, data) {
|
|
|
205
247
|
});
|
|
206
248
|
}
|
|
207
249
|
}
|
|
250
|
+
/**
|
|
251
|
+
* One bounded, validated capability read. A stalled, throwing, or invalid
|
|
252
|
+
* provider yields `undefined`; the block is copied so the gateway never hands
|
|
253
|
+
* out a provider-owned object.
|
|
254
|
+
*/
|
|
255
|
+
async function readCommerceCapabilities(provider) {
|
|
256
|
+
const reported = await readWithTimeout(Promise.resolve().then(() => provider.getCapabilities()));
|
|
257
|
+
if (!isCommerceCapabilities(reported)) {
|
|
258
|
+
return undefined;
|
|
259
|
+
}
|
|
260
|
+
return {
|
|
261
|
+
purchase: { ...reported.purchase },
|
|
262
|
+
catalog: { ...reported.catalog },
|
|
263
|
+
lifecycle: { ...reported.lifecycle },
|
|
264
|
+
authority: { ...reported.authority },
|
|
265
|
+
availability: reported.availability,
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
/** The commerce block a gateway reports while its provider cannot be read. */
|
|
269
|
+
export function createDegradedCommerceCapabilities() {
|
|
270
|
+
return { ...createUnsupportedCommerceCapabilities(), availability: 'temporarily-unavailable' };
|
|
271
|
+
}
|
|
272
|
+
async function readWithTimeout(read) {
|
|
273
|
+
let timer;
|
|
274
|
+
try {
|
|
275
|
+
return await Promise.race([
|
|
276
|
+
read,
|
|
277
|
+
new Promise((_resolve, reject) => {
|
|
278
|
+
timer = setTimeout(() => reject(new Error('Provider availability timed out.')), availabilityTimeoutMs);
|
|
279
|
+
}),
|
|
280
|
+
]);
|
|
281
|
+
}
|
|
282
|
+
catch {
|
|
283
|
+
return undefined;
|
|
284
|
+
}
|
|
285
|
+
finally {
|
|
286
|
+
if (timer !== undefined) {
|
|
287
|
+
clearTimeout(timer);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* Readiness of the commerce features a v2 provider declares, from its own
|
|
293
|
+
* capability declaration: a feature it does not sell is `unsupported`, and a
|
|
294
|
+
* read that failed or stalled is `temporarily-unavailable`.
|
|
295
|
+
*/
|
|
296
|
+
export function deriveCommerceFeatureStates(capabilities, features) {
|
|
297
|
+
const states = {};
|
|
298
|
+
const availability = capabilities === undefined
|
|
299
|
+
|| !availabilitySet.has(capabilities.availability)
|
|
300
|
+
? 'temporarily-unavailable'
|
|
301
|
+
: capabilities.availability;
|
|
302
|
+
for (const feature of features) {
|
|
303
|
+
if (feature === 'nativeIap') {
|
|
304
|
+
states.nativeIap = capabilities !== undefined
|
|
305
|
+
&& !(capabilities.purchase.consumable || capabilities.purchase.durable)
|
|
306
|
+
? 'unsupported'
|
|
307
|
+
: availability;
|
|
308
|
+
}
|
|
309
|
+
else if (feature === 'subscriptionIap') {
|
|
310
|
+
states.subscriptionIap = capabilities !== undefined && !capabilities.purchase.subscription
|
|
311
|
+
? 'unsupported'
|
|
312
|
+
: availability;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
return states;
|
|
316
|
+
}
|
|
208
317
|
function isRecord(value) {
|
|
209
318
|
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
210
319
|
}
|
|
@@ -218,6 +327,15 @@ function isVoidData(value) {
|
|
|
218
327
|
return value === undefined || value === null || (isRecord(value) && Object.keys(value).length === 0);
|
|
219
328
|
}
|
|
220
329
|
function isValidProviderData(method, value) {
|
|
330
|
+
if (isBridgeCommerceV2Method(method)) {
|
|
331
|
+
try {
|
|
332
|
+
assertCommerceBridgeResult(method, value);
|
|
333
|
+
return true;
|
|
334
|
+
}
|
|
335
|
+
catch {
|
|
336
|
+
return false;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
221
339
|
switch (method) {
|
|
222
340
|
case 'commerce.getProducts':
|
|
223
341
|
return Array.isArray(value) && value.every((product) => isRecord(product)
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { PlatformEvidenceEnvelope } from '@mpgd/platform';
|
|
2
|
+
import { type CommerceAuthorityCapabilities, type CommerceCatalogCapabilities, type CommerceErrorCode, type CommerceLifecycleCapabilities, type CommerceLogicalProductId, type CommerceProvider, type CommercePurchaseCapabilities, type PurchaseKind, type StoreId, type StorePurchaseAccountBinding } from '@mpgd/platform/commerce';
|
|
3
|
+
import { type StoreSdk, type StoreSdkErrorCodeMap, type StoreSdkPurchase } from './store-sdk.js';
|
|
4
|
+
/** One catalog product sold through the store under `storeSku`. */
|
|
5
|
+
export interface StoreCommerceProductMapping {
|
|
6
|
+
readonly logicalId: CommerceLogicalProductId;
|
|
7
|
+
readonly storeSku: string;
|
|
8
|
+
readonly kind: PurchaseKind;
|
|
9
|
+
/** Default store offer when the request names none. */
|
|
10
|
+
readonly offerId?: string;
|
|
11
|
+
}
|
|
12
|
+
/** Deep-partial overrides applied over the capabilities composed from the SDK declaration. */
|
|
13
|
+
export interface StoreCommerceCapabilityOverrides {
|
|
14
|
+
readonly purchase?: Partial<CommercePurchaseCapabilities>;
|
|
15
|
+
readonly catalog?: Partial<CommerceCatalogCapabilities>;
|
|
16
|
+
readonly lifecycle?: Partial<CommerceLifecycleCapabilities>;
|
|
17
|
+
}
|
|
18
|
+
export interface CreateStoreCommerceProviderInput {
|
|
19
|
+
readonly sdk: StoreSdk;
|
|
20
|
+
/** Defaults to `sdk.store`. */
|
|
21
|
+
readonly store?: StoreId;
|
|
22
|
+
readonly products: readonly StoreCommerceProductMapping[];
|
|
23
|
+
/** Evidence schema the backend registry routes on, for example `google-play.product-purchase.v2`. */
|
|
24
|
+
readonly evidenceSchema: string;
|
|
25
|
+
/**
|
|
26
|
+
* Evidence payload for a purchase. Defaults to the token, transaction id,
|
|
27
|
+
* and SKU; a store provider replaces it to match its backend verifier.
|
|
28
|
+
*/
|
|
29
|
+
readonly evidencePayload?: (purchase: StoreSdkPurchase) => PlatformEvidenceEnvelope['payload'];
|
|
30
|
+
/** Resolves the player's store account binding before a purchase opens. */
|
|
31
|
+
readonly accountBinding?: () => Promise<StorePurchaseAccountBinding | undefined>;
|
|
32
|
+
readonly capabilities?: StoreCommerceCapabilityOverrides;
|
|
33
|
+
readonly authority: CommerceAuthorityCapabilities;
|
|
34
|
+
/** Per-store error codes merged over `defaultStoreSdkErrorCodeMap`. */
|
|
35
|
+
readonly mapError?: StoreSdkErrorCodeMap;
|
|
36
|
+
}
|
|
37
|
+
export interface StoreCommerceProvider extends CommerceProvider {
|
|
38
|
+
/** Detaches the SDK listeners; buffered events are dropped. */
|
|
39
|
+
dispose(): void;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* `CommerceProvider` over an in-process `StoreSdk`. The provider maps logical
|
|
43
|
+
* ids to store SKUs both ways, composes `CommerceCapabilities` from the SDK
|
|
44
|
+
* declaration, builds `StorePurchase` evidence, buffers SDK events until the
|
|
45
|
+
* first subscriber, and normalizes every SDK failure into a `CommerceError`.
|
|
46
|
+
* It never grants and never sets `settlement`; the backend ledger decides.
|
|
47
|
+
*/
|
|
48
|
+
export declare function createStoreCommerceProvider(input: CreateStoreCommerceProviderInput): StoreCommerceProvider;
|
|
49
|
+
/**
|
|
50
|
+
* Codes that mean an unfinished purchase may exist (RFC "Errors": reconcile,
|
|
51
|
+
* never reopen the store UI blindly). The attempt stays `launched` so the
|
|
52
|
+
* reconciler resolves it from `getAvailablePurchases`; a terminal `failed`
|
|
53
|
+
* would exclude it from recovery.
|
|
54
|
+
*/
|
|
55
|
+
export declare const recoverableErrorCodes: ReadonlySet<CommerceErrorCode>;
|