@mpgd/game-services 0.9.0 → 0.10.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/dist/admob-ssv-conformance.d.ts +19 -0
- package/dist/admob-ssv-conformance.js +258 -0
- package/dist/admob-ssv.d.ts +54 -0
- package/dist/admob-ssv.js +566 -0
- package/dist/app-store-verifier-conformance.d.ts +25 -0
- package/dist/app-store-verifier-conformance.js +28 -0
- package/dist/app-store-verifier.d.ts +87 -0
- package/dist/app-store-verifier.js +439 -0
- package/dist/apps-in-toss-evidence-verification-conformance.d.ts +26 -0
- package/dist/apps-in-toss-evidence-verification-conformance.js +613 -0
- package/dist/apps-in-toss-evidence-verification.d.ts +162 -0
- package/dist/apps-in-toss-evidence-verification.js +458 -0
- package/dist/evidence-verification.d.ts +21 -1
- package/dist/google-play-purchase-conformance.d.ts +26 -0
- package/dist/google-play-purchase-conformance.js +39 -0
- package/dist/google-play-purchase.d.ts +40 -0
- package/dist/google-play-purchase.js +559 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -0
- package/dist/server.d.ts +3 -1
- package/dist/server.js +199 -14
- package/dist/types.d.ts +10 -0
- package/dist/types.js +32 -0
- package/package.json +37 -4
package/dist/server.js
CHANGED
|
@@ -4,7 +4,7 @@ import { createAnalyticsReporter } from '@mpgd/analytics';
|
|
|
4
4
|
import { gameServicesBackendEndpoints, } from './client';
|
|
5
5
|
import { gameServicesContract } from './contract';
|
|
6
6
|
import { createRejectingGameServicesEvidenceVerifier, } from './evidence-verification';
|
|
7
|
-
import { assertClaimAdRewardRequest, assertClaimAdRewardResponse, assertEntitlementLedgerGrant, assertEntitlementLedgerResult, assertLeaderboardScoreTransaction, assertProductGrantTransaction, assertRecordLeaderboardScoreRequest, assertRecordLeaderboardScoreResponse, assertVerifyPurchaseRequest, assertVerifyPurchaseResponse, } from './types';
|
|
7
|
+
import { assertClaimAdRewardRequest, assertClaimAdRewardResponse, assertEntitlementLedgerGrant, assertEntitlementLedgerResult, assertLeaderboardScoreTransaction, assertProductGrantTransaction, assertPurchaseGrantFinalization, assertRecordLeaderboardScoreRequest, assertRecordLeaderboardScoreResponse, assertVerifyPurchaseRequest, assertVerifyPurchaseResponse, } from './types';
|
|
8
8
|
export class InMemoryGameServicesStore {
|
|
9
9
|
entitlementTransactionsByKey = new Map();
|
|
10
10
|
entitlementTransactionsByEvidence = new Map();
|
|
@@ -129,6 +129,7 @@ export function createGameServicesBackend(input) {
|
|
|
129
129
|
const version = input.version ?? '0.0.0';
|
|
130
130
|
const evidenceVerifier = input.evidenceVerifier ?? createRejectingGameServicesEvidenceVerifier();
|
|
131
131
|
const evidenceVerificationTimeoutMs = resolveEvidenceVerificationTimeout(input.evidenceVerificationTimeoutMs);
|
|
132
|
+
const purchaseGrantFinalizationTimeoutMs = resolvePurchaseGrantFinalizationTimeout(input.purchaseGrantFinalizationTimeoutMs);
|
|
132
133
|
const analytics = createAnalyticsReporter({
|
|
133
134
|
target: 'server',
|
|
134
135
|
sessionId: input.analyticsSessionId ?? 'game-services',
|
|
@@ -145,6 +146,10 @@ export function createGameServicesBackend(input) {
|
|
|
145
146
|
now,
|
|
146
147
|
evidenceVerifier,
|
|
147
148
|
evidenceVerificationTimeoutMs,
|
|
149
|
+
purchaseGrantFinalizationTimeoutMs,
|
|
150
|
+
...(input.purchaseGrantFinalizer === undefined
|
|
151
|
+
? {}
|
|
152
|
+
: { purchaseGrantFinalizer: input.purchaseGrantFinalizer }),
|
|
148
153
|
});
|
|
149
154
|
await analytics.track({
|
|
150
155
|
name: verification.verified ? 'purchase_granted' : 'purchase_rejected',
|
|
@@ -155,6 +160,10 @@ export function createGameServicesBackend(input) {
|
|
|
155
160
|
ledgerEntryId: verification.ledgerEntryId,
|
|
156
161
|
alreadyProcessed: verification.alreadyProcessed,
|
|
157
162
|
reason: verification.reason,
|
|
163
|
+
finalizationStatus: verification.finalization?.status,
|
|
164
|
+
finalizationAction: verification.finalization?.action,
|
|
165
|
+
finalizationAlreadyCompleted: verification.finalization?.alreadyCompleted,
|
|
166
|
+
finalizationReason: verification.finalization?.reason,
|
|
158
167
|
},
|
|
159
168
|
});
|
|
160
169
|
return verification;
|
|
@@ -313,20 +322,25 @@ async function verifyPurchaseWithStore(input, context) {
|
|
|
313
322
|
grantId: request.productId,
|
|
314
323
|
target: request.target,
|
|
315
324
|
};
|
|
316
|
-
const
|
|
317
|
-
|
|
318
|
-
if (!matchesEntitlementRetry(existing, retryIdentity)) {
|
|
325
|
+
const completeExistingRetry = async (transaction) => {
|
|
326
|
+
if (!matchesEntitlementRetry(transaction, retryIdentity)) {
|
|
319
327
|
return assertVerifyPurchaseResponse({
|
|
320
328
|
verified: false,
|
|
321
329
|
alreadyProcessed: false,
|
|
322
330
|
reason: 'IDEMPOTENCY_KEY_CONFLICT',
|
|
323
331
|
});
|
|
324
332
|
}
|
|
333
|
+
const finalization = await finalizeExistingPurchaseGrant(request, transaction, context);
|
|
325
334
|
return assertVerifyPurchaseResponse({
|
|
326
335
|
verified: true,
|
|
327
|
-
ledgerEntryId:
|
|
336
|
+
ledgerEntryId: transaction.ledgerEntryId,
|
|
328
337
|
alreadyProcessed: true,
|
|
338
|
+
...(finalization === undefined ? {} : { finalization }),
|
|
329
339
|
});
|
|
340
|
+
};
|
|
341
|
+
const existing = await findEntitlementTransactionByIdempotency(context.store, retryIdentity);
|
|
342
|
+
if (existing !== undefined) {
|
|
343
|
+
return completeExistingRetry(existing);
|
|
330
344
|
}
|
|
331
345
|
const product = context.catalog.products.find((entry) => entry.id === request.productId);
|
|
332
346
|
if (product === undefined) {
|
|
@@ -354,6 +368,12 @@ async function verifyPurchaseWithStore(input, context) {
|
|
|
354
368
|
});
|
|
355
369
|
}, context.evidenceVerificationTimeoutMs);
|
|
356
370
|
if (verification.status !== 'verified') {
|
|
371
|
+
// A matching grant can land while provider verification is in flight. Recheck
|
|
372
|
+
// before returning a stale provider state such as an already-consumed token.
|
|
373
|
+
const racedExisting = await findEntitlementTransactionByIdempotency(context.store, retryIdentity);
|
|
374
|
+
if (racedExisting !== undefined) {
|
|
375
|
+
return completeExistingRetry(racedExisting);
|
|
376
|
+
}
|
|
357
377
|
return assertVerifyPurchaseResponse({
|
|
358
378
|
verified: false,
|
|
359
379
|
alreadyProcessed: false,
|
|
@@ -362,6 +382,7 @@ async function verifyPurchaseWithStore(input, context) {
|
|
|
362
382
|
: verification.reason,
|
|
363
383
|
});
|
|
364
384
|
}
|
|
385
|
+
const verificationPayload = createPurchaseVerificationPayload(verification);
|
|
365
386
|
const grant = await recordEntitlementGrantWithEvidence(context.store, {
|
|
366
387
|
playerId: request.playerId,
|
|
367
388
|
grantId: product.id,
|
|
@@ -371,11 +392,16 @@ async function verifyPurchaseWithStore(input, context) {
|
|
|
371
392
|
grant: product.grant,
|
|
372
393
|
evidenceVerificationId: verification.verificationId,
|
|
373
394
|
payload: {
|
|
374
|
-
...
|
|
395
|
+
...verificationPayload,
|
|
375
396
|
target: request.target,
|
|
376
397
|
productId: request.productId,
|
|
398
|
+
productType: product.type,
|
|
377
399
|
platformProductId,
|
|
378
|
-
|
|
400
|
+
...(verification.platformEvidenceId === null
|
|
401
|
+
? {}
|
|
402
|
+
: {
|
|
403
|
+
platformTransactionId: verification.platformEvidenceId ?? request.platformTransactionId,
|
|
404
|
+
}),
|
|
379
405
|
purchasedAt: request.purchasedAt,
|
|
380
406
|
evidenceVerificationId: verification.verificationId,
|
|
381
407
|
evidenceVerifiedAt: verification.verifiedAt,
|
|
@@ -388,20 +414,160 @@ async function verifyPurchaseWithStore(input, context) {
|
|
|
388
414
|
reason: 'EVIDENCE_ALREADY_PROCESSED',
|
|
389
415
|
});
|
|
390
416
|
}
|
|
391
|
-
if (grant.result.alreadyProcessed
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
417
|
+
if (grant.result.alreadyProcessed) {
|
|
418
|
+
const recordedTransaction = await context.store.getEntitlementTransaction(grant.result.ledgerEntryId);
|
|
419
|
+
const storedEvidenceVerificationId = recordedTransaction?.evidenceVerificationId
|
|
420
|
+
?? recordedTransaction?.payload.evidenceVerificationId;
|
|
421
|
+
if (recordedTransaction === undefined
|
|
422
|
+
|| !matchesEntitlementRetry(recordedTransaction, retryIdentity)
|
|
423
|
+
|| storedEvidenceVerificationId !== verification.verificationId) {
|
|
424
|
+
return assertVerifyPurchaseResponse({
|
|
425
|
+
verified: false,
|
|
426
|
+
alreadyProcessed: false,
|
|
427
|
+
reason: 'IDEMPOTENCY_KEY_CONFLICT',
|
|
428
|
+
});
|
|
429
|
+
}
|
|
398
430
|
}
|
|
431
|
+
const finalization = await finalizePurchaseGrant(context.purchaseGrantFinalizer, {
|
|
432
|
+
request,
|
|
433
|
+
product,
|
|
434
|
+
platformProductId,
|
|
435
|
+
evidenceVerificationId: verification.verificationId,
|
|
436
|
+
...(verification.payload === undefined
|
|
437
|
+
? {}
|
|
438
|
+
: { evidencePayload: verification.payload }),
|
|
439
|
+
ledgerEntryId: grant.result.ledgerEntryId,
|
|
440
|
+
alreadyProcessed: grant.result.alreadyProcessed,
|
|
441
|
+
timeoutMs: context.purchaseGrantFinalizationTimeoutMs,
|
|
442
|
+
});
|
|
399
443
|
return assertVerifyPurchaseResponse({
|
|
400
444
|
verified: true,
|
|
401
445
|
ledgerEntryId: grant.result.ledgerEntryId,
|
|
402
446
|
alreadyProcessed: grant.result.alreadyProcessed,
|
|
447
|
+
...(finalization === undefined ? {} : { finalization }),
|
|
448
|
+
});
|
|
449
|
+
}
|
|
450
|
+
async function finalizeExistingPurchaseGrant(request, transaction, context) {
|
|
451
|
+
if (context.purchaseGrantFinalizer === undefined) {
|
|
452
|
+
return undefined;
|
|
453
|
+
}
|
|
454
|
+
const currentProduct = context.catalog.products.find((entry) => entry.id === request.productId);
|
|
455
|
+
const storedPlatformProductId = readStoredPlatformProductId(transaction);
|
|
456
|
+
const product = createStoredPurchaseProduct(request, transaction) ?? currentProduct;
|
|
457
|
+
const platformProductId = storedPlatformProductId
|
|
458
|
+
?? currentProduct?.platformProductIds[request.target];
|
|
459
|
+
const evidenceVerificationId = transaction.evidenceVerificationId
|
|
460
|
+
?? transaction.payload.evidenceVerificationId;
|
|
461
|
+
const finalizationRequest = createStoredPurchaseRequest(request, transaction);
|
|
462
|
+
if (product === undefined
|
|
463
|
+
|| platformProductId === undefined
|
|
464
|
+
|| typeof evidenceVerificationId !== 'string'
|
|
465
|
+
|| evidenceVerificationId.length === 0) {
|
|
466
|
+
return assertPurchaseGrantFinalization({
|
|
467
|
+
status: 'pending',
|
|
468
|
+
alreadyCompleted: false,
|
|
469
|
+
reason: 'PURCHASE_FINALIZER_CONTEXT_UNAVAILABLE',
|
|
470
|
+
});
|
|
471
|
+
}
|
|
472
|
+
return finalizePurchaseGrant(context.purchaseGrantFinalizer, {
|
|
473
|
+
request: finalizationRequest,
|
|
474
|
+
product,
|
|
475
|
+
platformProductId,
|
|
476
|
+
evidenceVerificationId,
|
|
477
|
+
evidencePayload: transaction.payload,
|
|
478
|
+
ledgerEntryId: transaction.ledgerEntryId,
|
|
479
|
+
alreadyProcessed: true,
|
|
480
|
+
timeoutMs: context.purchaseGrantFinalizationTimeoutMs,
|
|
403
481
|
});
|
|
404
482
|
}
|
|
483
|
+
async function finalizePurchaseGrant(finalizer, input) {
|
|
484
|
+
if (finalizer === undefined) {
|
|
485
|
+
return undefined;
|
|
486
|
+
}
|
|
487
|
+
const controller = new AbortController();
|
|
488
|
+
const timeoutDecision = assertPurchaseGrantFinalization({
|
|
489
|
+
status: 'pending',
|
|
490
|
+
alreadyCompleted: false,
|
|
491
|
+
reason: 'PURCHASE_FINALIZER_TIMEOUT',
|
|
492
|
+
});
|
|
493
|
+
let timedOut = false;
|
|
494
|
+
let timeout;
|
|
495
|
+
try {
|
|
496
|
+
if (finalizer.supportsPurchaseGrant?.(input) === false) {
|
|
497
|
+
return undefined;
|
|
498
|
+
}
|
|
499
|
+
return await Promise.race([
|
|
500
|
+
finalizer.finalizePurchaseGrant({ ...input, signal: controller.signal })
|
|
501
|
+
.then(assertPurchaseGrantFinalization)
|
|
502
|
+
.catch((error) => {
|
|
503
|
+
if (timedOut) {
|
|
504
|
+
return timeoutDecision;
|
|
505
|
+
}
|
|
506
|
+
throw error;
|
|
507
|
+
}),
|
|
508
|
+
new Promise((resolve) => {
|
|
509
|
+
timeout = setTimeout(() => {
|
|
510
|
+
timedOut = true;
|
|
511
|
+
resolve(timeoutDecision);
|
|
512
|
+
controller.abort();
|
|
513
|
+
}, input.timeoutMs);
|
|
514
|
+
}),
|
|
515
|
+
]);
|
|
516
|
+
}
|
|
517
|
+
catch {
|
|
518
|
+
return assertPurchaseGrantFinalization({
|
|
519
|
+
status: 'pending',
|
|
520
|
+
alreadyCompleted: false,
|
|
521
|
+
reason: 'PURCHASE_FINALIZER_ERROR',
|
|
522
|
+
});
|
|
523
|
+
}
|
|
524
|
+
finally {
|
|
525
|
+
if (timeout !== undefined) {
|
|
526
|
+
clearTimeout(timeout);
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
function readStoredPlatformProductId(transaction) {
|
|
531
|
+
const platformProductId = transaction.payload.platformProductId;
|
|
532
|
+
return typeof platformProductId === 'string' && platformProductId.length > 0
|
|
533
|
+
? platformProductId
|
|
534
|
+
: undefined;
|
|
535
|
+
}
|
|
536
|
+
function createStoredPurchaseProduct(request, transaction) {
|
|
537
|
+
const productType = transaction.payload.productType;
|
|
538
|
+
const platformProductId = readStoredPlatformProductId(transaction);
|
|
539
|
+
if (transaction.grant === undefined
|
|
540
|
+
|| platformProductId === undefined
|
|
541
|
+
|| (productType !== 'consumable'
|
|
542
|
+
&& productType !== 'non_consumable'
|
|
543
|
+
&& productType !== 'subscription')) {
|
|
544
|
+
return undefined;
|
|
545
|
+
}
|
|
546
|
+
return {
|
|
547
|
+
id: request.productId,
|
|
548
|
+
type: productType,
|
|
549
|
+
grant: transaction.grant,
|
|
550
|
+
platformProductIds: { [request.target]: platformProductId },
|
|
551
|
+
};
|
|
552
|
+
}
|
|
553
|
+
/**
|
|
554
|
+
* Reuses the current request only for retry dimensions already matched against
|
|
555
|
+
* the stored grant, while restoring provider identity and time metadata from
|
|
556
|
+
* the durable transaction for a coherent finalization retry.
|
|
557
|
+
*/
|
|
558
|
+
function createStoredPurchaseRequest(request, transaction) {
|
|
559
|
+
const platformTransactionId = transaction.payload.platformTransactionId;
|
|
560
|
+
const purchasedAt = transaction.payload.purchasedAt;
|
|
561
|
+
return {
|
|
562
|
+
...request,
|
|
563
|
+
...(typeof platformTransactionId === 'string' && platformTransactionId.length > 0
|
|
564
|
+
? { platformTransactionId }
|
|
565
|
+
: {}),
|
|
566
|
+
...(typeof purchasedAt === 'string' && purchasedAt.length > 0
|
|
567
|
+
? { purchasedAt }
|
|
568
|
+
: {}),
|
|
569
|
+
};
|
|
570
|
+
}
|
|
405
571
|
async function claimAdRewardWithStore(input, context) {
|
|
406
572
|
const request = assertClaimAdRewardRequest(input);
|
|
407
573
|
const retryIdentity = {
|
|
@@ -538,6 +704,14 @@ async function verifyEvidence(verify, timeoutMs) {
|
|
|
538
704
|
}
|
|
539
705
|
}
|
|
540
706
|
}
|
|
707
|
+
function createPurchaseVerificationPayload(verification) {
|
|
708
|
+
if (verification.platformEvidenceId !== null || verification.payload === undefined) {
|
|
709
|
+
return verification.payload;
|
|
710
|
+
}
|
|
711
|
+
const { platformTransactionId: suppressedPlatformTransactionId, ...payload } = verification.payload;
|
|
712
|
+
void suppressedPlatformTransactionId;
|
|
713
|
+
return payload;
|
|
714
|
+
}
|
|
541
715
|
function assertEvidenceVerificationDecision(decision) {
|
|
542
716
|
if (decision.status === 'verified') {
|
|
543
717
|
assertNonEmptyDecisionString(decision.verificationId, 'verificationId');
|
|
@@ -559,6 +733,10 @@ function assertEvidenceVerificationDecision(decision) {
|
|
|
559
733
|
}
|
|
560
734
|
}
|
|
561
735
|
}
|
|
736
|
+
if (decision.platformEvidenceId !== undefined
|
|
737
|
+
&& decision.platformEvidenceId !== null) {
|
|
738
|
+
assertNonEmptyDecisionString(decision.platformEvidenceId, 'platformEvidenceId');
|
|
739
|
+
}
|
|
562
740
|
return decision;
|
|
563
741
|
}
|
|
564
742
|
if (decision.status === 'pending') {
|
|
@@ -739,6 +917,13 @@ function resolveEvidenceVerificationTimeout(timeoutMs) {
|
|
|
739
917
|
}
|
|
740
918
|
return resolved;
|
|
741
919
|
}
|
|
920
|
+
function resolvePurchaseGrantFinalizationTimeout(timeoutMs) {
|
|
921
|
+
const resolved = timeoutMs ?? 15000;
|
|
922
|
+
if (!Number.isFinite(resolved) || resolved <= 0) {
|
|
923
|
+
throw new Error('purchaseGrantFinalizationTimeoutMs must be a positive finite number.');
|
|
924
|
+
}
|
|
925
|
+
return resolved;
|
|
926
|
+
}
|
|
742
927
|
function assertNonEmptyDecisionString(value, label) {
|
|
743
928
|
if (typeof value !== 'string' || value.length === 0) {
|
|
744
929
|
throw new Error(`Evidence verification ${label} must be a non-empty string.`);
|
package/dist/types.d.ts
CHANGED
|
@@ -24,11 +24,20 @@ export interface VerifyPurchaseResponse {
|
|
|
24
24
|
readonly ledgerEntryId?: string;
|
|
25
25
|
readonly alreadyProcessed: boolean;
|
|
26
26
|
readonly reason?: string;
|
|
27
|
+
readonly finalization?: PurchaseGrantFinalization;
|
|
28
|
+
}
|
|
29
|
+
export type PurchaseGrantFinalizationAction = 'acknowledge' | 'consume' | 'finish' | 'complete';
|
|
30
|
+
export interface PurchaseGrantFinalization {
|
|
31
|
+
readonly status: 'completed' | 'pending';
|
|
32
|
+
readonly action?: PurchaseGrantFinalizationAction;
|
|
33
|
+
readonly alreadyCompleted: boolean;
|
|
34
|
+
readonly reason?: string;
|
|
27
35
|
}
|
|
28
36
|
export interface ClaimAdRewardRequest {
|
|
29
37
|
readonly target: GameServicesAdRewardTarget;
|
|
30
38
|
readonly playerId: string;
|
|
31
39
|
readonly placementId: LogicalAdPlacementId;
|
|
40
|
+
/** Platform evidence identity; AIT uses the game-issued reward correlation identifier. */
|
|
32
41
|
readonly platformImpressionId?: string;
|
|
33
42
|
readonly idempotencyKey: AdRewardIdempotencyKey;
|
|
34
43
|
readonly completedAt: string;
|
|
@@ -82,6 +91,7 @@ export interface LeaderboardScoreTransaction extends RecordLeaderboardScoreReque
|
|
|
82
91
|
}
|
|
83
92
|
export declare function assertVerifyPurchaseRequest(input: VerifyPurchaseRequest): VerifyPurchaseRequest;
|
|
84
93
|
export declare function assertVerifyPurchaseResponse(input: VerifyPurchaseResponse): VerifyPurchaseResponse;
|
|
94
|
+
export declare function assertPurchaseGrantFinalization(input: PurchaseGrantFinalization): PurchaseGrantFinalization;
|
|
85
95
|
export declare function assertClaimAdRewardRequest(input: ClaimAdRewardRequest): ClaimAdRewardRequest;
|
|
86
96
|
export declare function assertClaimAdRewardResponse(input: ClaimAdRewardResponse): ClaimAdRewardResponse;
|
|
87
97
|
export declare function assertRecordLeaderboardScoreRequest(input: RecordLeaderboardScoreRequest): RecordLeaderboardScoreRequest;
|
package/dist/types.js
CHANGED
|
@@ -15,6 +15,30 @@ export function assertVerifyPurchaseResponse(input) {
|
|
|
15
15
|
assertOptionalNonEmptyString(input.ledgerEntryId, 'ledgerEntryId');
|
|
16
16
|
assertBoolean(input.alreadyProcessed, 'alreadyProcessed');
|
|
17
17
|
assertOptionalNonEmptyString(input.reason, 'reason');
|
|
18
|
+
if (input.finalization !== undefined) {
|
|
19
|
+
assertPurchaseGrantFinalization(input.finalization);
|
|
20
|
+
}
|
|
21
|
+
return input;
|
|
22
|
+
}
|
|
23
|
+
export function assertPurchaseGrantFinalization(input) {
|
|
24
|
+
assertRecord(input, 'PurchaseGrantFinalization');
|
|
25
|
+
if (input.status !== 'completed' && input.status !== 'pending') {
|
|
26
|
+
throw new Error('finalization.status must be completed or pending.');
|
|
27
|
+
}
|
|
28
|
+
if (input.action !== undefined) {
|
|
29
|
+
assertPurchaseGrantFinalizationAction(input.action);
|
|
30
|
+
}
|
|
31
|
+
if (input.status === 'completed' && input.action === undefined) {
|
|
32
|
+
throw new Error('completed finalization requires an action.');
|
|
33
|
+
}
|
|
34
|
+
assertBoolean(input.alreadyCompleted, 'finalization.alreadyCompleted');
|
|
35
|
+
assertOptionalNonEmptyString(input.reason, 'finalization.reason');
|
|
36
|
+
if (input.status === 'pending' && input.alreadyCompleted) {
|
|
37
|
+
throw new Error('pending finalization cannot already be completed.');
|
|
38
|
+
}
|
|
39
|
+
if (input.status === 'pending' && input.reason === undefined) {
|
|
40
|
+
throw new Error('pending finalization requires a reason.');
|
|
41
|
+
}
|
|
18
42
|
return input;
|
|
19
43
|
}
|
|
20
44
|
export function assertClaimAdRewardRequest(input) {
|
|
@@ -140,6 +164,14 @@ function assertBoolean(input, label) {
|
|
|
140
164
|
throw new Error(`${label} must be a boolean.`);
|
|
141
165
|
}
|
|
142
166
|
}
|
|
167
|
+
function assertPurchaseGrantFinalizationAction(input) {
|
|
168
|
+
if (input !== 'acknowledge'
|
|
169
|
+
&& input !== 'consume'
|
|
170
|
+
&& input !== 'finish'
|
|
171
|
+
&& input !== 'complete') {
|
|
172
|
+
throw new Error('finalization.action must be acknowledge, consume, finish, or complete.');
|
|
173
|
+
}
|
|
174
|
+
}
|
|
143
175
|
function assertLedgerSource(input) {
|
|
144
176
|
if (input !== 'purchase' && input !== 'ad_reward' && input !== 'admin') {
|
|
145
177
|
throw new Error('source must be purchase, ad_reward, or admin.');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mpgd/game-services",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"description": "Client, contract, server, store, and test helpers for authoritative mpgd game services.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -29,6 +29,30 @@
|
|
|
29
29
|
"types": "./dist/index.d.ts",
|
|
30
30
|
"default": "./dist/index.js"
|
|
31
31
|
},
|
|
32
|
+
"./admob-ssv": {
|
|
33
|
+
"types": "./dist/admob-ssv.d.ts",
|
|
34
|
+
"default": "./dist/admob-ssv.js"
|
|
35
|
+
},
|
|
36
|
+
"./admob-ssv-conformance": {
|
|
37
|
+
"types": "./dist/admob-ssv-conformance.d.ts",
|
|
38
|
+
"default": "./dist/admob-ssv-conformance.js"
|
|
39
|
+
},
|
|
40
|
+
"./app-store-verifier": {
|
|
41
|
+
"types": "./dist/app-store-verifier.d.ts",
|
|
42
|
+
"default": "./dist/app-store-verifier.js"
|
|
43
|
+
},
|
|
44
|
+
"./app-store-verifier-conformance": {
|
|
45
|
+
"types": "./dist/app-store-verifier-conformance.d.ts",
|
|
46
|
+
"default": "./dist/app-store-verifier-conformance.js"
|
|
47
|
+
},
|
|
48
|
+
"./apps-in-toss-evidence-verification": {
|
|
49
|
+
"types": "./dist/apps-in-toss-evidence-verification.d.ts",
|
|
50
|
+
"default": "./dist/apps-in-toss-evidence-verification.js"
|
|
51
|
+
},
|
|
52
|
+
"./apps-in-toss-evidence-verification-conformance": {
|
|
53
|
+
"types": "./dist/apps-in-toss-evidence-verification-conformance.d.ts",
|
|
54
|
+
"default": "./dist/apps-in-toss-evidence-verification-conformance.js"
|
|
55
|
+
},
|
|
32
56
|
"./client": {
|
|
33
57
|
"types": "./dist/client.d.ts",
|
|
34
58
|
"default": "./dist/client.js"
|
|
@@ -41,6 +65,14 @@
|
|
|
41
65
|
"types": "./dist/evidence-verification.d.ts",
|
|
42
66
|
"default": "./dist/evidence-verification.js"
|
|
43
67
|
},
|
|
68
|
+
"./google-play-purchase": {
|
|
69
|
+
"types": "./dist/google-play-purchase.d.ts",
|
|
70
|
+
"default": "./dist/google-play-purchase.js"
|
|
71
|
+
},
|
|
72
|
+
"./google-play-purchase-conformance": {
|
|
73
|
+
"types": "./dist/google-play-purchase-conformance.d.ts",
|
|
74
|
+
"default": "./dist/google-play-purchase-conformance.js"
|
|
75
|
+
},
|
|
44
76
|
"./notification-delivery": {
|
|
45
77
|
"types": "./dist/notification-delivery.d.ts",
|
|
46
78
|
"default": "./dist/notification-delivery.js"
|
|
@@ -86,12 +118,13 @@
|
|
|
86
118
|
"dist"
|
|
87
119
|
],
|
|
88
120
|
"dependencies": {
|
|
121
|
+
"@noble/secp256k1": "3.1.0",
|
|
89
122
|
"@orpc/client": "2.0.0-beta.14",
|
|
90
123
|
"@orpc/contract": "2.0.0-beta.14",
|
|
91
124
|
"@orpc/server": "2.0.0-beta.14",
|
|
92
|
-
"@mpgd/analytics": "0.3.7",
|
|
93
125
|
"@mpgd/catalog": "0.5.0",
|
|
94
|
-
"@mpgd/platform": "0.7.0"
|
|
126
|
+
"@mpgd/platform": "0.7.0",
|
|
127
|
+
"@mpgd/analytics": "0.3.7"
|
|
95
128
|
},
|
|
96
129
|
"devDependencies": {
|
|
97
130
|
"ttsc": "0.18.4",
|
|
@@ -107,6 +140,6 @@
|
|
|
107
140
|
"lint": "ttsc --noEmit",
|
|
108
141
|
"format": "ttsc format",
|
|
109
142
|
"fix": "ttsc fix",
|
|
110
|
-
"test": "cd ../.. && node tools/run-ttsx.mjs packages/game-services/src/client.test.ts && node tools/run-ttsx.mjs packages/game-services/src/runtime.test.ts && node tools/run-ttsx.mjs packages/game-services/src/types.test.ts && node tools/run-ttsx.mjs packages/game-services/src/server.test.ts && node tools/run-ttsx.mjs packages/game-services/src/platform-order.test.ts && node tools/run-ttsx.mjs packages/game-services/src/progress-link.test.ts && node tools/run-ttsx.mjs packages/game-services/src/notification-delivery.test.ts && node tools/run-ttsx.mjs packages/game-services/src/verified-leaderboard.test.ts && node tools/run-ttsx.mjs packages/game-services/src/verified-leaderboard-conformance.test.ts && node tools/run-ttsx.mjs packages/game-services/src/verified-leaderboard-durability-conformance.test.ts && node tools/run-ttsx.mjs packages/game-services/src/verified-leaderboard-transport.test.ts"
|
|
143
|
+
"test": "cd ../.. && node tools/run-ttsx.mjs packages/game-services/src/client.test.ts && node tools/run-ttsx.mjs packages/game-services/src/runtime.test.ts && node tools/run-ttsx.mjs packages/game-services/src/types.test.ts && node tools/run-ttsx.mjs packages/game-services/src/server.test.ts && node tools/run-ttsx.mjs packages/game-services/src/google-play-purchase.test.ts && node tools/run-ttsx.mjs packages/game-services/src/platform-order.test.ts && node tools/run-ttsx.mjs packages/game-services/src/progress-link.test.ts && node tools/run-ttsx.mjs packages/game-services/src/notification-delivery.test.ts && node tools/run-ttsx.mjs packages/game-services/src/admob-ssv.test.ts && node tools/run-ttsx.mjs packages/game-services/src/app-store-verifier.test.ts && node tools/run-ttsx.mjs packages/game-services/src/apps-in-toss-evidence-verification-conformance.test.ts && node tools/run-ttsx.mjs packages/game-services/src/verified-leaderboard.test.ts && node tools/run-ttsx.mjs packages/game-services/src/verified-leaderboard-conformance.test.ts && node tools/run-ttsx.mjs packages/game-services/src/verified-leaderboard-durability-conformance.test.ts && node tools/run-ttsx.mjs packages/game-services/src/verified-leaderboard-transport.test.ts"
|
|
111
144
|
}
|
|
112
145
|
}
|