@mpgd/capacitor-play-billing 0.3.5 → 0.3.7
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 +11 -5
- package/dist/commerce-provider.d.ts +1 -1
- package/dist/store-sdk.d.ts +12 -7
- package/dist/store-sdk.js +52 -11
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -69,11 +69,17 @@ when `includePending` is set; each carries `token = purchaseToken` and
|
|
|
69
69
|
`transactionId = orderId`, or the `play-token-sha256:<hex>` token key until
|
|
70
70
|
Play assigns an order id, so the same purchase deduplicates in the ledger. A
|
|
71
71
|
purchased entry whose `isAcknowledged` flag is absent counts as unfinished so a
|
|
72
|
-
paid purchase is never dropped. A purchase for a SKU that is not in `products`
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
72
|
+
paid purchase is never dropped. A purchase for a SKU that is not in `products`
|
|
73
|
+
(a retired product) is still listed and emitted, with that product id as
|
|
74
|
+
`sku` and the purchase token intact, so the reconciler can submit it when a
|
|
75
|
+
logical id is still known. A multi-line purchase is listed the same way,
|
|
76
|
+
with every product id joined by a comma (not a product-id character, so it
|
|
77
|
+
cannot match one catalog SKU). The reconciler then fails that purchase's
|
|
78
|
+
catalog lookup visibly instead of granting one line of it.
|
|
79
|
+
`onUnmappedPurchase` still receives both, as diagnostics only: the backend
|
|
80
|
+
finalizer acknowledges or consumes a token only after the client submits it
|
|
81
|
+
and the ledger grants, and it has no path that can acknowledge a token
|
|
82
|
+
without a logical product id. The plugin's `purchaseUpdated` listener backs
|
|
77
83
|
`addListener`: deliveries leave in the order the plugin reported them (a
|
|
78
84
|
purchase without an order id waits for the token digest, and later updates
|
|
79
85
|
queue behind it), and a `cancelled` or `failed` update without a purchase is
|
|
@@ -36,7 +36,7 @@ export interface CreateCapacitorPlayBillingCommerceProviderInput {
|
|
|
36
36
|
readonly store?: StoreId;
|
|
37
37
|
/** Extra plugin codes merged over `playBillingStoreSdkErrorCodeMap`. */
|
|
38
38
|
readonly mapError?: StoreSdkErrorCodeMap;
|
|
39
|
-
/** Diagnostics for
|
|
39
|
+
/** Diagnostics for a retired SKU or a multi-line purchase. The purchase is still surfaced; see the SDK input. */
|
|
40
40
|
readonly onUnmappedPurchase?: (purchase: PlayPurchase) => void;
|
|
41
41
|
}
|
|
42
42
|
/**
|
package/dist/store-sdk.d.ts
CHANGED
|
@@ -26,11 +26,13 @@ export interface CreateCapacitorPlayBillingStoreSdkInput {
|
|
|
26
26
|
/** Defaults to `Capacitor.getPlatform() === 'android'`. */
|
|
27
27
|
readonly isAndroid?: () => boolean;
|
|
28
28
|
/**
|
|
29
|
-
* Diagnostics
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
* logical id
|
|
33
|
-
*
|
|
29
|
+
* Diagnostics for a plugin purchase the current catalog does not sell, or
|
|
30
|
+
* that names more than one product id. The purchase is still returned and
|
|
31
|
+
* emitted (token and product ids) so the reconciler can submit it when a
|
|
32
|
+
* logical id is known, or fail that purchase visibly when it is not. This
|
|
33
|
+
* hook is not a substitute for that delivery: the backend finalizer only
|
|
34
|
+
* acknowledges a token the client submits, and it has no path that can
|
|
35
|
+
* acknowledge one without a logical product id.
|
|
34
36
|
*/
|
|
35
37
|
readonly onUnmappedPurchase?: (purchase: PlayPurchase) => void;
|
|
36
38
|
}
|
|
@@ -64,8 +66,11 @@ export declare class PlayBillingStoreSdkError extends Error implements StoreSdkE
|
|
|
64
66
|
* provider normalizes (`PLAY_BILLING_TIMEOUT`, `PLAY_BILLING_INTERRUPTED`, and
|
|
65
67
|
* `PLAY_BILLING_EMPTY_PURCHASE` become `OUTCOME_UNKNOWN` and stay recoverable).
|
|
66
68
|
* `transactionId` is the order id or, until Play assigns one, the token key
|
|
67
|
-
* `play-token-sha256:<hex>` existing ledgers already use.
|
|
68
|
-
*
|
|
69
|
+
* `play-token-sha256:<hex>` existing ledgers already use. An unfinished
|
|
70
|
+
* purchase whose SKU is not in `products`, and a multi-line purchase, are
|
|
71
|
+
* still listed and emitted so the reconciler can see the token. Nothing here
|
|
72
|
+
* grants, and nothing here acknowledges or consumes: the backend finalizer
|
|
73
|
+
* owns both.
|
|
69
74
|
*/
|
|
70
75
|
export declare function createCapacitorPlayBillingStoreSdk(input: CreateCapacitorPlayBillingStoreSdkInput): CapacitorPlayBillingStoreSdk;
|
|
71
76
|
/** Validates a Play purchase token: non-empty, at most 4096 characters, no padding or control characters. */
|
package/dist/store-sdk.js
CHANGED
|
@@ -68,8 +68,11 @@ export class PlayBillingStoreSdkError extends Error {
|
|
|
68
68
|
* provider normalizes (`PLAY_BILLING_TIMEOUT`, `PLAY_BILLING_INTERRUPTED`, and
|
|
69
69
|
* `PLAY_BILLING_EMPTY_PURCHASE` become `OUTCOME_UNKNOWN` and stay recoverable).
|
|
70
70
|
* `transactionId` is the order id or, until Play assigns one, the token key
|
|
71
|
-
* `play-token-sha256:<hex>` existing ledgers already use.
|
|
72
|
-
*
|
|
71
|
+
* `play-token-sha256:<hex>` existing ledgers already use. An unfinished
|
|
72
|
+
* purchase whose SKU is not in `products`, and a multi-line purchase, are
|
|
73
|
+
* still listed and emitted so the reconciler can see the token. Nothing here
|
|
74
|
+
* grants, and nothing here acknowledges or consumes: the backend finalizer
|
|
75
|
+
* owns both.
|
|
73
76
|
*/
|
|
74
77
|
export function createCapacitorPlayBillingStoreSdk(input) {
|
|
75
78
|
const plugin = input.plugin ?? CapacitorPlayBilling;
|
|
@@ -117,7 +120,10 @@ export function createCapacitorPlayBillingStoreSdk(input) {
|
|
|
117
120
|
// Diagnostics never break delivery.
|
|
118
121
|
}
|
|
119
122
|
};
|
|
120
|
-
/**
|
|
123
|
+
/**
|
|
124
|
+
* Whether a plugin purchase maps to one configured SKU. Anything else is
|
|
125
|
+
* still surfaced; this only feeds the diagnostics hook.
|
|
126
|
+
*/
|
|
121
127
|
const isMapped = (purchase) => {
|
|
122
128
|
const sku = skuOf(purchase);
|
|
123
129
|
if (sku !== undefined && products.has(sku)) {
|
|
@@ -188,7 +194,14 @@ export function createCapacitorPlayBillingStoreSdk(input) {
|
|
|
188
194
|
emitLateTerminalResult(outcome);
|
|
189
195
|
return;
|
|
190
196
|
}
|
|
191
|
-
if (!isRecord(purchase)
|
|
197
|
+
if (!isRecord(purchase)) {
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
// A retired SKU and a multi-line purchase stay in the event. The
|
|
201
|
+
// reconciler submits a token only when it can resolve a logical id, and
|
|
202
|
+
// otherwise fails the catalog lookup instead of dropping the token.
|
|
203
|
+
isMapped(purchase);
|
|
204
|
+
if (surfaceSku(purchase) === undefined) {
|
|
192
205
|
return;
|
|
193
206
|
}
|
|
194
207
|
enqueue(() => emitConverted(purchase));
|
|
@@ -352,9 +365,10 @@ export function createCapacitorPlayBillingStoreSdk(input) {
|
|
|
352
365
|
if (!isUnfinished(purchase, includePending)) {
|
|
353
366
|
continue;
|
|
354
367
|
}
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
// reconciler
|
|
368
|
+
isMapped(purchase);
|
|
369
|
+
if (surfaceSku(purchase) === undefined) {
|
|
370
|
+
// Not a purchase the reconciler can name: no product id, or an id
|
|
371
|
+
// the evidence rules reject. The diagnostics hook already saw it.
|
|
358
372
|
continue;
|
|
359
373
|
}
|
|
360
374
|
available.push(await toSdkPurchase(purchase));
|
|
@@ -434,7 +448,7 @@ function convertPurchase(purchase) {
|
|
|
434
448
|
if (state !== 'pending' && state !== 'purchased') {
|
|
435
449
|
throw evidenceInvalid('Play purchase state is invalid.');
|
|
436
450
|
}
|
|
437
|
-
const sku =
|
|
451
|
+
const sku = surfaceSku(purchase);
|
|
438
452
|
if (sku === undefined) {
|
|
439
453
|
throw evidenceInvalid('Play purchase must name exactly one product.');
|
|
440
454
|
}
|
|
@@ -478,13 +492,40 @@ function buildSdkPurchase(purchase, parsed) {
|
|
|
478
492
|
: {}),
|
|
479
493
|
};
|
|
480
494
|
}
|
|
481
|
-
function
|
|
495
|
+
function productIdsOf(purchase) {
|
|
482
496
|
if (!isRecord(purchase)) {
|
|
483
497
|
return undefined;
|
|
484
498
|
}
|
|
485
499
|
const products = purchase.products ?? purchase.productIds;
|
|
486
|
-
|
|
487
|
-
|
|
500
|
+
if (!Array.isArray(products) || products.length === 0) {
|
|
501
|
+
return undefined;
|
|
502
|
+
}
|
|
503
|
+
const ids = [];
|
|
504
|
+
for (const id of products) {
|
|
505
|
+
if (typeof id !== 'string' || !productIdPattern.test(id) || ids.includes(id)) {
|
|
506
|
+
return undefined;
|
|
507
|
+
}
|
|
508
|
+
ids.push(id);
|
|
509
|
+
}
|
|
510
|
+
return ids;
|
|
511
|
+
}
|
|
512
|
+
function skuOf(purchase) {
|
|
513
|
+
const ids = productIdsOf(purchase);
|
|
514
|
+
return ids?.length === 1 ? ids[0] : undefined;
|
|
515
|
+
}
|
|
516
|
+
/**
|
|
517
|
+
* The SKU a surfaced purchase carries. One product id is that id, including
|
|
518
|
+
* a retired SKU that is no longer in `products`. Several ids are joined with
|
|
519
|
+
* a comma, which is not a product-id character, so the joined value cannot
|
|
520
|
+
* equal one catalog SKU and the reconciler fails the lookup instead of
|
|
521
|
+
* granting one line of a multi-line purchase.
|
|
522
|
+
*/
|
|
523
|
+
function surfaceSku(purchase) {
|
|
524
|
+
const ids = productIdsOf(purchase);
|
|
525
|
+
if (ids === undefined) {
|
|
526
|
+
return undefined;
|
|
527
|
+
}
|
|
528
|
+
return ids.length === 1 ? ids[0] : ids.join(',');
|
|
488
529
|
}
|
|
489
530
|
/**
|
|
490
531
|
* The purchase state under its Billing 9.1.0 name, or under the 0.1 name
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mpgd/capacitor-play-billing",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.7",
|
|
4
4
|
"description": "Opt-in Android Google Play Billing purchase collector for mpgd Capacitor games.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"peerDependencies": {
|
|
40
40
|
"@capacitor/core": "^8.5.1",
|
|
41
41
|
"@mpgd/adapter-capacitor": "^0.12.0",
|
|
42
|
-
"@mpgd/bridge": "^0.
|
|
42
|
+
"@mpgd/bridge": "^0.15.0",
|
|
43
43
|
"@mpgd/platform": "^0.27.0"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
@@ -48,10 +48,10 @@
|
|
|
48
48
|
"ttsc": "0.32.1",
|
|
49
49
|
"typescript": "7.0.2",
|
|
50
50
|
"vitest": "^4.0.0",
|
|
51
|
-
"@mpgd/adapter-capacitor": "0.12.
|
|
52
|
-
"@mpgd/bridge": "0.
|
|
53
|
-
"@mpgd/
|
|
54
|
-
"@mpgd/
|
|
51
|
+
"@mpgd/adapter-capacitor": "0.12.3",
|
|
52
|
+
"@mpgd/bridge": "0.15.0",
|
|
53
|
+
"@mpgd/platform": "0.27.2",
|
|
54
|
+
"@mpgd/game-services": "0.27.2"
|
|
55
55
|
},
|
|
56
56
|
"main": "./dist/index.js",
|
|
57
57
|
"types": "./dist/index.d.ts",
|