@mpgd/game-services 0.8.2 → 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/client.js +51 -5
- 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/notification-delivery.js +1 -0
- package/dist/runtime.js +2 -1
- package/dist/server.d.ts +3 -1
- package/dist/server.js +199 -14
- package/dist/types.d.ts +15 -3
- package/dist/types.js +47 -2
- package/dist/verified-leaderboard-durability-conformance.d.ts +39 -0
- package/dist/verified-leaderboard-durability-conformance.js +193 -0
- package/package.json +42 -5
package/dist/client.js
CHANGED
|
@@ -29,7 +29,7 @@ export function createGameServicesClient(input) {
|
|
|
29
29
|
return {
|
|
30
30
|
async purchase(purchaseInput) {
|
|
31
31
|
const target = input.target;
|
|
32
|
-
if (!
|
|
32
|
+
if (!isGameServicesCommerceTarget(target)) {
|
|
33
33
|
const purchase = {
|
|
34
34
|
status: 'failed',
|
|
35
35
|
entitlementIds: [],
|
|
@@ -48,6 +48,25 @@ export function createGameServicesClient(input) {
|
|
|
48
48
|
};
|
|
49
49
|
}
|
|
50
50
|
const purchase = await input.gateway.commerce.purchase(purchaseInput);
|
|
51
|
+
if (target === 'verse8') {
|
|
52
|
+
const status = purchase.status === 'completed' ? 'rejected' : purchase.status;
|
|
53
|
+
await analytics.track({
|
|
54
|
+
name: 'purchase_rejected',
|
|
55
|
+
properties: {
|
|
56
|
+
productId: purchaseInput.productId,
|
|
57
|
+
status,
|
|
58
|
+
reason: purchase.status === 'completed'
|
|
59
|
+
? 'verse8_grants_require_agent8_purchase_event'
|
|
60
|
+
: purchase.status === 'pending'
|
|
61
|
+
? 'agent8_purchase_event_pending'
|
|
62
|
+
: undefined,
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
return {
|
|
66
|
+
status,
|
|
67
|
+
purchase,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
51
70
|
if (purchase.status !== 'completed' || purchase.transactionId === undefined) {
|
|
52
71
|
await analytics.track({
|
|
53
72
|
name: 'purchase_rejected',
|
|
@@ -93,7 +112,7 @@ export function createGameServicesClient(input) {
|
|
|
93
112
|
},
|
|
94
113
|
async claimRewardedAd(rewardInput) {
|
|
95
114
|
const target = input.target;
|
|
96
|
-
if (!
|
|
115
|
+
if (!isGameServicesAdRewardTarget(target)) {
|
|
97
116
|
const reward = {
|
|
98
117
|
status: 'unavailable',
|
|
99
118
|
rewardGranted: false,
|
|
@@ -157,6 +176,23 @@ export function createGameServicesClient(input) {
|
|
|
157
176
|
return result;
|
|
158
177
|
},
|
|
159
178
|
async submitLeaderboardScore(scoreInput) {
|
|
179
|
+
const target = input.target;
|
|
180
|
+
if (!isGameServicesLeaderboardTarget(target)) {
|
|
181
|
+
await analytics.track({
|
|
182
|
+
name: 'leaderboard_submitted',
|
|
183
|
+
properties: {
|
|
184
|
+
leaderboardId: scoreInput.leaderboardId,
|
|
185
|
+
score: scoreInput.score,
|
|
186
|
+
submitted: false,
|
|
187
|
+
reason: 'unsupported_target',
|
|
188
|
+
},
|
|
189
|
+
});
|
|
190
|
+
return {
|
|
191
|
+
submitted: false,
|
|
192
|
+
platformSubmitted: false,
|
|
193
|
+
alreadyProcessed: false,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
160
196
|
const platformResult = await input.gateway.leaderboard.submitScore(scoreInput);
|
|
161
197
|
if (!platformResult.submitted) {
|
|
162
198
|
await analytics.track({
|
|
@@ -174,7 +210,7 @@ export function createGameServicesClient(input) {
|
|
|
174
210
|
};
|
|
175
211
|
}
|
|
176
212
|
const record = await input.backend.leaderboard.recordScore({
|
|
177
|
-
target
|
|
213
|
+
target,
|
|
178
214
|
playerId: input.playerId,
|
|
179
215
|
...scoreInput,
|
|
180
216
|
});
|
|
@@ -281,8 +317,18 @@ export function createGameServicesIdempotencyKey(input) {
|
|
|
281
317
|
function normalizeSegment(value) {
|
|
282
318
|
return value.replaceAll(/[^a-zA-Z0-9_-]+/g, '-').replaceAll(/^-|-$/g, '').slice(0, 64);
|
|
283
319
|
}
|
|
284
|
-
function
|
|
285
|
-
return target === 'android' || target === 'ios' || target === 'ait';
|
|
320
|
+
function isGameServicesCommerceTarget(target) {
|
|
321
|
+
return target === 'android' || target === 'ios' || target === 'ait' || target === 'verse8';
|
|
322
|
+
}
|
|
323
|
+
function isGameServicesAdRewardTarget(target) {
|
|
324
|
+
return target === 'android' || target === 'ios' || target === 'ait' || target === 'verse8';
|
|
325
|
+
}
|
|
326
|
+
function isGameServicesLeaderboardTarget(target) {
|
|
327
|
+
return target === 'browser'
|
|
328
|
+
|| target === 'android'
|
|
329
|
+
|| target === 'ios'
|
|
330
|
+
|| target === 'ait'
|
|
331
|
+
|| target === 'reddit';
|
|
286
332
|
}
|
|
287
333
|
async function sendGameServicesBackendRequest(transport, endpoint, body) {
|
|
288
334
|
const response = await transport.send({
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
import type { AdPlacements, ProductCatalog } from '@mpgd/catalog';
|
|
2
|
-
import type { ClaimAdRewardRequest, EntitlementLedgerPayload, VerifyPurchaseRequest } from './types';
|
|
2
|
+
import type { ClaimAdRewardRequest, EntitlementLedgerPayload, PurchaseGrantFinalization, VerifyPurchaseRequest } from './types';
|
|
3
3
|
export type EvidenceVerificationDecision = {
|
|
4
4
|
readonly status: 'verified';
|
|
5
5
|
readonly verificationId: string;
|
|
6
6
|
readonly verifiedAt: string;
|
|
7
7
|
readonly payload?: EntitlementLedgerPayload;
|
|
8
|
+
/**
|
|
9
|
+
* An authoritative provider identity for cross-idempotency replay checks.
|
|
10
|
+
* `null` explicitly suppresses the client-reported platform identity.
|
|
11
|
+
*/
|
|
12
|
+
readonly platformEvidenceId?: string | null;
|
|
8
13
|
} | {
|
|
9
14
|
readonly status: 'pending';
|
|
10
15
|
readonly reason?: string;
|
|
@@ -26,6 +31,21 @@ export interface VerifyAdRewardEvidenceInput {
|
|
|
26
31
|
readonly signal: AbortSignal;
|
|
27
32
|
readonly timeoutMs: number;
|
|
28
33
|
}
|
|
34
|
+
export interface FinalizePurchaseGrantInput {
|
|
35
|
+
readonly request: VerifyPurchaseRequest;
|
|
36
|
+
readonly product: ProductCatalog['products'][number];
|
|
37
|
+
readonly platformProductId: string;
|
|
38
|
+
readonly evidenceVerificationId: string;
|
|
39
|
+
readonly evidencePayload?: EntitlementLedgerPayload;
|
|
40
|
+
readonly ledgerEntryId: string;
|
|
41
|
+
readonly alreadyProcessed: boolean;
|
|
42
|
+
readonly signal: AbortSignal;
|
|
43
|
+
readonly timeoutMs: number;
|
|
44
|
+
}
|
|
45
|
+
export interface GameServicesPurchaseGrantFinalizer {
|
|
46
|
+
supportsPurchaseGrant?(input: Omit<FinalizePurchaseGrantInput, 'signal'>): boolean;
|
|
47
|
+
finalizePurchaseGrant(input: FinalizePurchaseGrantInput): Promise<PurchaseGrantFinalization>;
|
|
48
|
+
}
|
|
29
49
|
export interface GameServicesEvidenceVerifier {
|
|
30
50
|
verifyPurchase(input: VerifyPurchaseEvidenceInput): Promise<EvidenceVerificationDecision>;
|
|
31
51
|
verifyAdReward(input: VerifyAdRewardEvidenceInput): Promise<EvidenceVerificationDecision>;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type GooglePlayAcknowledgementState, type GooglePlayConsumptionState, type GooglePlayPurchaseState } from './google-play-purchase';
|
|
2
|
+
export declare const googlePlayProductPurchaseConformanceEvidence: Readonly<{
|
|
3
|
+
schema: "google-play.product-purchase.v2";
|
|
4
|
+
payload: Readonly<{
|
|
5
|
+
purchaseToken: "google-play-conformance-token";
|
|
6
|
+
}>;
|
|
7
|
+
}>;
|
|
8
|
+
export interface CreateGooglePlayProductPurchaseConformanceFixtureInput {
|
|
9
|
+
readonly productId?: string;
|
|
10
|
+
readonly orderId?: string;
|
|
11
|
+
readonly purchaseState?: Exclude<GooglePlayPurchaseState, 'PURCHASE_STATE_UNSPECIFIED'>;
|
|
12
|
+
readonly purchaseCompletionTime?: string;
|
|
13
|
+
readonly consumptionState?: Exclude<GooglePlayConsumptionState, 'CONSUMPTION_STATE_UNSPECIFIED'>;
|
|
14
|
+
readonly acknowledgementState?: Exclude<GooglePlayAcknowledgementState, 'ACKNOWLEDGEMENT_STATE_UNSPECIFIED'>;
|
|
15
|
+
readonly quantity?: number;
|
|
16
|
+
readonly refundableQuantity?: number;
|
|
17
|
+
readonly rentOffer?: boolean;
|
|
18
|
+
readonly obfuscatedExternalAccountId?: string;
|
|
19
|
+
readonly obfuscatedExternalProfileId?: string;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Builds a deterministic `purchases.productsv2` response for boundary and
|
|
23
|
+
* provider-client conformance tests. It intentionally contains no credentials,
|
|
24
|
+
* live purchase tokens, or environment-specific endpoints.
|
|
25
|
+
*/
|
|
26
|
+
export declare function createGooglePlayProductPurchaseConformanceFixture(input?: CreateGooglePlayProductPurchaseConformanceFixtureInput): Readonly<Record<string, unknown>>;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { googlePlayProductPurchaseEvidenceSchema, } from './google-play-purchase';
|
|
2
|
+
export const googlePlayProductPurchaseConformanceEvidence = Object.freeze({
|
|
3
|
+
schema: googlePlayProductPurchaseEvidenceSchema,
|
|
4
|
+
payload: Object.freeze({
|
|
5
|
+
purchaseToken: 'google-play-conformance-token',
|
|
6
|
+
}),
|
|
7
|
+
});
|
|
8
|
+
/**
|
|
9
|
+
* Builds a deterministic `purchases.productsv2` response for boundary and
|
|
10
|
+
* provider-client conformance tests. It intentionally contains no credentials,
|
|
11
|
+
* live purchase tokens, or environment-specific endpoints.
|
|
12
|
+
*/
|
|
13
|
+
export function createGooglePlayProductPurchaseConformanceFixture(input = {}) {
|
|
14
|
+
return {
|
|
15
|
+
productLineItem: [
|
|
16
|
+
{
|
|
17
|
+
productId: input.productId ?? 'coins_100_android',
|
|
18
|
+
productOfferDetails: {
|
|
19
|
+
quantity: input.quantity ?? 1,
|
|
20
|
+
refundableQuantity: input.refundableQuantity ?? 1,
|
|
21
|
+
consumptionState: input.consumptionState ?? 'CONSUMPTION_STATE_YET_TO_BE_CONSUMED',
|
|
22
|
+
...(input.rentOffer === true ? { rentOfferDetails: {} } : {}),
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
],
|
|
26
|
+
purchaseStateContext: {
|
|
27
|
+
purchaseState: input.purchaseState ?? 'PURCHASED',
|
|
28
|
+
},
|
|
29
|
+
orderId: input.orderId ?? 'GPA.conformance-1',
|
|
30
|
+
purchaseCompletionTime: input.purchaseCompletionTime ?? '2030-01-02T03:04:05.000Z',
|
|
31
|
+
acknowledgementState: input.acknowledgementState ?? 'ACKNOWLEDGEMENT_STATE_PENDING',
|
|
32
|
+
...(input.obfuscatedExternalAccountId === undefined
|
|
33
|
+
? {}
|
|
34
|
+
: { obfuscatedExternalAccountId: input.obfuscatedExternalAccountId }),
|
|
35
|
+
...(input.obfuscatedExternalProfileId === undefined
|
|
36
|
+
? {}
|
|
37
|
+
: { obfuscatedExternalProfileId: input.obfuscatedExternalProfileId }),
|
|
38
|
+
};
|
|
39
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { GameServicesEvidenceVerifier, GameServicesPurchaseGrantFinalizer, VerifyPurchaseEvidenceInput } from './evidence-verification';
|
|
2
|
+
export declare const googlePlayProductPurchaseEvidenceSchema = "google-play.product-purchase.v2";
|
|
3
|
+
export type GooglePlayPurchaseState = 'PURCHASED' | 'CANCELLED' | 'PENDING' | 'PURCHASE_STATE_UNSPECIFIED';
|
|
4
|
+
export type GooglePlayConsumptionState = 'CONSUMPTION_STATE_YET_TO_BE_CONSUMED' | 'CONSUMPTION_STATE_CONSUMED' | 'CONSUMPTION_STATE_UNSPECIFIED';
|
|
5
|
+
export type GooglePlayAcknowledgementState = 'ACKNOWLEDGEMENT_STATE_PENDING' | 'ACKNOWLEDGEMENT_STATE_ACKNOWLEDGED' | 'ACKNOWLEDGEMENT_STATE_UNSPECIFIED';
|
|
6
|
+
export interface GooglePlayProductPurchaseClient {
|
|
7
|
+
getProductPurchaseV2(input: {
|
|
8
|
+
readonly packageName: string;
|
|
9
|
+
readonly purchaseToken: string;
|
|
10
|
+
readonly signal: AbortSignal;
|
|
11
|
+
}): Promise<unknown>;
|
|
12
|
+
acknowledgeProductPurchase(input: {
|
|
13
|
+
readonly packageName: string;
|
|
14
|
+
readonly productId: string;
|
|
15
|
+
readonly purchaseToken: string;
|
|
16
|
+
readonly signal: AbortSignal;
|
|
17
|
+
}): Promise<void>;
|
|
18
|
+
consumeProductPurchase(input: {
|
|
19
|
+
readonly packageName: string;
|
|
20
|
+
readonly productId: string;
|
|
21
|
+
readonly purchaseToken: string;
|
|
22
|
+
readonly signal: AbortSignal;
|
|
23
|
+
}): Promise<void>;
|
|
24
|
+
}
|
|
25
|
+
export interface CreateGooglePlayProductPurchaseBoundaryInput {
|
|
26
|
+
readonly client: GooglePlayProductPurchaseClient;
|
|
27
|
+
readonly packageName: string;
|
|
28
|
+
readonly now?: () => string;
|
|
29
|
+
/**
|
|
30
|
+
* Explicitly permits purchases without a Google account identifier. Use only
|
|
31
|
+
* when the caller binds `playerId` to an authenticated application account.
|
|
32
|
+
*/
|
|
33
|
+
readonly allowUnboundAuthenticatedPlayer?: boolean;
|
|
34
|
+
readonly resolveObfuscatedAccountId?: (playerId: string, signal: AbortSignal) => Promise<string | undefined> | string | undefined;
|
|
35
|
+
readonly resolveObfuscatedProfileId?: (playerId: string, signal: AbortSignal) => Promise<string | undefined> | string | undefined;
|
|
36
|
+
}
|
|
37
|
+
export interface GooglePlayProductPurchaseBoundary extends GameServicesPurchaseGrantFinalizer {
|
|
38
|
+
verifyPurchase(input: VerifyPurchaseEvidenceInput): ReturnType<GameServicesEvidenceVerifier['verifyPurchase']>;
|
|
39
|
+
}
|
|
40
|
+
export declare function createGooglePlayProductPurchaseBoundary(input: CreateGooglePlayProductPurchaseBoundaryInput): GooglePlayProductPurchaseBoundary;
|