@mpgd/game-services 0.21.1 → 0.23.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/types.js CHANGED
@@ -7,6 +7,9 @@ export function assertVerifyPurchaseRequest(input) {
7
7
  return validateRequest(() => {
8
8
  assertRecord(input, 'VerifyPurchaseRequest');
9
9
  assertStoreTarget(input.target);
10
+ if (input.store !== undefined) {
11
+ assertCommerceStoreId(input.store, 'store');
12
+ }
10
13
  assertOptionalGameServicesDeploymentTarget(input.deploymentTarget);
11
14
  assertRequestString(input.playerId, 'playerId');
12
15
  assertRequestString(input.productId, 'productId');
@@ -29,6 +32,49 @@ export function assertVerifyPurchaseResponse(input) {
29
32
  }
30
33
  return input;
31
34
  }
35
+ /** Bounds one ledger status read; a client asks for the grants of one journal window. */
36
+ export const maxPurchaseLedgerStatusEntries = 256;
37
+ /**
38
+ * Validates an untrusted ledger status read. Failures throw
39
+ * `GameServicesRequestValidationError` so transports can answer with a stable 400.
40
+ */
41
+ export function assertPurchaseLedgerStatusRequest(input) {
42
+ return validateRequest(() => {
43
+ assertRecord(input, 'PurchaseLedgerStatusRequest');
44
+ if (!Array.isArray(input.ledgerEntryIds)) {
45
+ throw new Error('ledgerEntryIds must be an array.');
46
+ }
47
+ if (input.ledgerEntryIds.length > maxPurchaseLedgerStatusEntries) {
48
+ throw new Error(`ledgerEntryIds must not exceed ${String(maxPurchaseLedgerStatusEntries)} entries.`);
49
+ }
50
+ for (const ledgerEntryId of input.ledgerEntryIds) {
51
+ assertRequestString(ledgerEntryId, 'ledgerEntryIds[]');
52
+ }
53
+ assertRequestString(input.playerId, 'playerId');
54
+ return input;
55
+ });
56
+ }
57
+ export function assertPurchaseLedgerStatusResponse(input) {
58
+ assertRecord(input, 'PurchaseLedgerStatusResponse');
59
+ if (!Array.isArray(input.statuses)) {
60
+ throw new Error('statuses must be an array.');
61
+ }
62
+ for (const status of input.statuses) {
63
+ assertRecord(status, 'statuses[]');
64
+ assertNonEmptyString(status.ledgerEntryId, 'statuses[].ledgerEntryId');
65
+ if (status.status !== 'active' && status.status !== 'reversed' && status.status !== 'unknown') {
66
+ throw new Error('statuses[].status must be active, reversed, or unknown.');
67
+ }
68
+ assertOptionalNonEmptyString(status.reversedBy, 'statuses[].reversedBy');
69
+ if (status.status === 'reversed' && status.reversedBy === undefined) {
70
+ throw new Error('a reversed status names the entry that reversed it.');
71
+ }
72
+ if (status.status !== 'reversed' && status.reversedBy !== undefined) {
73
+ throw new Error('only a reversed status names a reversing entry.');
74
+ }
75
+ }
76
+ return input;
77
+ }
32
78
  export function assertPurchaseGrantFinalization(input) {
33
79
  assertRecord(input, 'PurchaseGrantFinalization');
34
80
  if (input.status !== 'completed' && input.status !== 'pending') {
@@ -124,6 +170,38 @@ export function assertEntitlementLedgerResult(input) {
124
170
  assertBoolean(input.alreadyProcessed, 'alreadyProcessed');
125
171
  return input;
126
172
  }
173
+ export function assertEntitlementLedgerRevocation(input) {
174
+ assertRecord(input, 'EntitlementLedgerRevocation');
175
+ assertNonEmptyString(input.playerId, 'playerId');
176
+ assertNonEmptyString(input.revokesLedgerEntryId, 'revokesLedgerEntryId');
177
+ assertNonEmptyString(input.idempotencyKey, 'idempotencyKey');
178
+ assertRevocationReason(input.reason);
179
+ assertNonEmptyString(input.revokedAt, 'revokedAt');
180
+ assertPayload(input.payload);
181
+ assertOptionalNonEmptyString(input.evidenceVerificationId, 'evidenceVerificationId');
182
+ return input;
183
+ }
184
+ export function assertEntitlementRevocationTransaction(input) {
185
+ assertEntitlementLedgerRevocation(input);
186
+ assertNonEmptyString(input.ledgerEntryId, 'ledgerEntryId');
187
+ return input;
188
+ }
189
+ export function assertEntitlementLedgerPeriod(input) {
190
+ assertRecord(input, 'EntitlementLedgerPeriod');
191
+ assertNonEmptyString(input.playerId, 'playerId');
192
+ assertNonEmptyString(input.extendsLedgerEntryId, 'extendsLedgerEntryId');
193
+ assertNonEmptyString(input.idempotencyKey, 'idempotencyKey');
194
+ if (!Number.isSafeInteger(input.periodIndex) || input.periodIndex < 0) {
195
+ throw new Error('periodIndex must be a non-negative integer.');
196
+ }
197
+ assertNonEmptyString(input.startedAt, 'startedAt');
198
+ assertNonEmptyString(input.expiresAt, 'expiresAt');
199
+ if (!(Date.parse(input.expiresAt) > Date.parse(input.startedAt))) {
200
+ throw new Error('expiresAt must be a timestamp after startedAt.');
201
+ }
202
+ assertPayload(input.payload);
203
+ return input;
204
+ }
127
205
  export function assertProductGrantTransaction(input) {
128
206
  assertRecord(input, 'ProductGrantTransaction');
129
207
  assertNonEmptyString(input.ledgerEntryId, 'ledgerEntryId');
@@ -150,13 +228,21 @@ function assertRecord(input, label) {
150
228
  throw new Error(`${label} must be an object.`);
151
229
  }
152
230
  }
231
+ /**
232
+ * The purchase target is open (RFC "Open store ids"): any bounded kebab-case
233
+ * identifier passes here, and the backend's commerce evidence registry decides
234
+ * whether the store it names is served.
235
+ */
153
236
  function assertStoreTarget(input) {
154
- if (input !== 'microsoft-store'
155
- && input !== 'android'
156
- && input !== 'ios'
157
- && input !== 'ait'
158
- && input !== 'oneplay') {
159
- throw new Error('target must be microsoft-store, android, ios, ait, or oneplay.');
237
+ assertCommerceStoreId(input, 'target');
238
+ }
239
+ /** A store id is a bounded kebab-case identifier such as `google-play` or `credit-wallet`. */
240
+ export function assertCommerceStoreId(input, label = 'store') {
241
+ if (typeof input !== 'string'
242
+ || input.length === 0
243
+ || input.length > 64
244
+ || !/^[a-z0-9]+(?:-[a-z0-9]+)*$/u.test(input)) {
245
+ throw new Error(`${label} must be a lowercase kebab-case store identifier.`);
160
246
  }
161
247
  }
162
248
  function assertAdRewardTarget(input) {
@@ -262,6 +348,11 @@ function assertPurchaseGrantFinalizationAction(input) {
262
348
  throw new Error('finalization.action must be acknowledge, consume, finish, or complete.');
263
349
  }
264
350
  }
351
+ function assertRevocationReason(input) {
352
+ if (input !== 'refund' && input !== 'chargeback' && input !== 'voided' && input !== 'admin') {
353
+ throw new Error('reason must be refund, chargeback, voided, or admin.');
354
+ }
355
+ }
265
356
  function assertLedgerSource(input) {
266
357
  if (input !== 'purchase' && input !== 'ad_reward' && input !== 'admin') {
267
358
  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.21.1",
3
+ "version": "0.23.0",
4
4
  "description": "Client, contract, server, store, and test helpers for authoritative mpgd game services.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -77,6 +77,18 @@
77
77
  "types": "./dist/apps-in-toss-partner-api.d.ts",
78
78
  "default": "./dist/apps-in-toss-partner-api.js"
79
79
  },
80
+ "./commerce-evidence-registry": {
81
+ "types": "./dist/commerce-evidence-registry.d.ts",
82
+ "default": "./dist/commerce-evidence-registry.js"
83
+ },
84
+ "./commerce-reconciler": {
85
+ "types": "./dist/commerce-reconciler.d.ts",
86
+ "default": "./dist/commerce-reconciler.js"
87
+ },
88
+ "./monetization-operation-store": {
89
+ "types": "./dist/monetization-operation-store.d.ts",
90
+ "default": "./dist/monetization-operation-store.js"
91
+ },
80
92
  "./client": {
81
93
  "types": "./dist/client.d.ts",
82
94
  "default": "./dist/client.js"
@@ -190,9 +202,9 @@
190
202
  "@orpc/client": "2.0.0-beta.14",
191
203
  "@orpc/contract": "2.0.0-beta.14",
192
204
  "@orpc/server": "2.0.0-beta.14",
193
- "@mpgd/catalog": "0.9.2",
194
- "@mpgd/platform": "0.21.0",
195
- "@mpgd/analytics": "0.4.7"
205
+ "@mpgd/platform": "0.22.0",
206
+ "@mpgd/analytics": "0.4.8",
207
+ "@mpgd/catalog": "0.9.3"
196
208
  },
197
209
  "peerDependencies": {
198
210
  "@apple/app-store-server-library": "^3.1.0"
@@ -221,8 +233,8 @@
221
233
  "fix": "ttsc fix",
222
234
  "build": "cd ../.. && node tools/run-ttsx.mjs tools/package/build-packages.ts @mpgd/game-services",
223
235
  "test:packed-transport": "pnpm build && node test/runtime-transport-package-import.mjs && node test/reward-evidence-import.mjs",
224
- "test:unit": "cd ../.. && node tools/run-ttsx.mjs packages/game-services/src/client.test.ts && node tools/run-ttsx.mjs packages/game-services/src/client-progress.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/google-play-publisher-client.test.ts && node tools/run-ttsx.mjs packages/game-services/src/microsoft-store-purchase.test.ts && node tools/run-ttsx.mjs packages/game-services/src/microsoft-store-identity.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/apps-in-toss-partner-api.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 && node tools/run-ttsx.mjs packages/game-services/src/guest-session.test.ts && node tools/run-ttsx.mjs packages/game-services/src/client-reward-evidence.test.ts && node tools/run-ttsx.mjs packages/game-services/src/ad-reward-verifier-registry.test.ts && node tools/run-ttsx.mjs packages/game-services/src/oneplay-reward.test.ts && node tools/run-ttsx.mjs packages/game-services/src/oneplay-purchase.test.ts",
225
- "test:recovery": "cd ../.. && node tools/run-ttsx.mjs packages/game-services/src/monetization-recovery.test.ts && node tools/run-ttsx.mjs packages/game-services/src/app-store-recovery.test.ts",
236
+ "test:unit": "cd ../.. && node tools/run-ttsx.mjs packages/game-services/src/client.test.ts && node tools/run-ttsx.mjs packages/game-services/src/client-progress.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/google-play-publisher-client.test.ts && node tools/run-ttsx.mjs packages/game-services/src/microsoft-store-purchase.test.ts && node tools/run-ttsx.mjs packages/game-services/src/microsoft-store-identity.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/apps-in-toss-partner-api.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 && node tools/run-ttsx.mjs packages/game-services/src/guest-session.test.ts && node tools/run-ttsx.mjs packages/game-services/src/client-reward-evidence.test.ts && node tools/run-ttsx.mjs packages/game-services/src/ad-reward-verifier-registry.test.ts && node tools/run-ttsx.mjs packages/game-services/src/oneplay-reward.test.ts && node tools/run-ttsx.mjs packages/game-services/src/oneplay-purchase.test.ts && node tools/run-ttsx.mjs packages/game-services/src/commerce-registry-routing.test.ts && node tools/run-ttsx.mjs packages/game-services/src/commerce-registry-server.test.ts && node tools/run-ttsx.mjs packages/game-services/src/commerce-ledger-revocation.test.ts",
237
+ "test:recovery": "cd ../.. && node tools/run-ttsx.mjs packages/game-services/src/monetization-recovery.test.ts && node tools/run-ttsx.mjs packages/game-services/src/app-store-recovery.test.ts && node tools/run-ttsx.mjs packages/game-services/src/commerce-reconciler.test.ts && node tools/run-ttsx.mjs packages/game-services/src/commerce-reconciler-conformance.test.ts",
226
238
  "test": "pnpm test:unit && pnpm test:app-store-server && pnpm test:recovery && pnpm test:packed-transport",
227
239
  "test:app-store-server": "vitest run"
228
240
  }