@mpgd/adapter-capacitor 0.7.5 → 0.8.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 CHANGED
@@ -66,15 +66,19 @@ SDK works on a physical device or that a store/ad setup is release-ready.
66
66
 
67
67
  ## App Store server-side transaction signatures
68
68
 
69
- For an iOS game backend, install the optional
70
- `@apple/app-store-server-library@3.1.0` peer and import
71
- `createAppleSignedTransactionVerifier` from
72
- `@mpgd/adapter-capacitor/app-store-server`. Supply DER-encoded Apple root
73
- certificates from the [Apple PKI](https://www.apple.com/certificateauthority/),
74
- the game's bundle ID, environment, and production App Apple ID. Use the
75
- result as `signedTransactionVerifier` in
76
- `createAppStoreGameServicesEvidenceVerifier`. Keep App Store API credentials,
77
- trust roots, and this verifier on the backend, not in the Capacitor game.
69
+ The server-side verifier moved to `@mpgd/game-services/app-store-server`
70
+ (see the [game-services README](../../packages/game-services/README.md)):
71
+ install the optional `@apple/app-store-server-library@3.1.0` peer next to
72
+ `@mpgd/game-services` and import `createAppleSignedTransactionVerifier` from
73
+ there. `@mpgd/adapter-capacitor/app-store-server` is deprecated: it re-exports
74
+ the same function and options type for one release and is removed in the next
75
+ major version. Supply DER-encoded Apple root certificates from the
76
+ [Apple PKI](https://www.apple.com/certificateauthority/), the game's bundle
77
+ ID, environment, and production App Apple ID. Use the result as
78
+ `signedTransactionVerifier` in `createAppStoreGameServicesEvidenceVerifier`.
79
+ Keep App Store API credentials, trust roots, and this verifier on the backend,
80
+ not in the Capacitor game; the adapter itself no longer peers on Apple's
81
+ library.
78
82
 
79
83
  The server fetches the transaction from Apple's API, verifies its JWS, checks
80
84
  the product, bundle, environment, account token and transaction ID, then
@@ -1,12 +1,12 @@
1
- import { type AppStoreEnvironment, type AppStoreSignedTransactionVerifier } from '@mpgd/game-services/app-store-verifier';
2
- export interface CreateAppleSignedTransactionVerifierOptions {
3
- /** DER-encoded Apple root certificates, supplied by the server operator. */
4
- readonly rootCertificates: readonly Uint8Array[];
5
- readonly environment: AppStoreEnvironment;
6
- readonly bundleId: string;
7
- /** Required by Apple's verifier in production. */
8
- readonly appAppleId?: number;
9
- readonly enableOnlineChecks?: boolean;
10
- }
11
- /** Verify the App Store JWS before exposing any decoded purchase fields. */
12
- export declare function createAppleSignedTransactionVerifier(options: CreateAppleSignedTransactionVerifierOptions): AppStoreSignedTransactionVerifier;
1
+ /**
2
+ * @deprecated Moved to `@mpgd/game-services/app-store-server` in A4 of the
3
+ * commerce v2 plan: server-side App Store verification does not belong in the
4
+ * client adapter. This subpath re-exports the same symbols for one release and
5
+ * will be removed in the next major version. Install
6
+ * `@apple/app-store-server-library` next to `@mpgd/game-services`.
7
+ */
8
+ import { createAppleSignedTransactionVerifier as createAppleSignedTransactionVerifierFromGameServices, type CreateAppleSignedTransactionVerifierOptions as GameServicesAppleVerifierOptions } from '@mpgd/game-services/app-store-server';
9
+ /** @deprecated Import `createAppleSignedTransactionVerifier` from `@mpgd/game-services/app-store-server`. */
10
+ export declare const createAppleSignedTransactionVerifier: typeof createAppleSignedTransactionVerifierFromGameServices;
11
+ /** @deprecated Import `CreateAppleSignedTransactionVerifierOptions` from `@mpgd/game-services/app-store-server`. */
12
+ export type CreateAppleSignedTransactionVerifierOptions = GameServicesAppleVerifierOptions;
@@ -1,119 +1,10 @@
1
- import { Environment, SignedDataVerifier, VerificationException, VerificationStatus, } from '@apple/app-store-server-library';
2
- import { AppStoreDependencyUnavailableError, } from '@mpgd/game-services/app-store-verifier';
3
- /** Verify the App Store JWS before exposing any decoded purchase fields. */
4
- export function createAppleSignedTransactionVerifier(options) {
5
- if (!Array.isArray(options.rootCertificates)
6
- || options.rootCertificates.length === 0
7
- || options.rootCertificates.some((certificate) => !(certificate instanceof Uint8Array)
8
- || certificate.length === 0)) {
9
- throw new TypeError('Apple root certificates are required.');
10
- }
11
- if (options.environment !== 'Production' && options.environment !== 'Sandbox') {
12
- throw new TypeError('App Store environment must be Production or Sandbox.');
13
- }
14
- if (options.bundleId.trim() === '') {
15
- throw new TypeError('App Store bundle ID is required.');
16
- }
17
- if (options.environment === 'Production'
18
- && (!Number.isSafeInteger(options.appAppleId) || (options.appAppleId ?? 0) <= 0)) {
19
- throw new TypeError('Production App Store verification requires appAppleId.');
20
- }
21
- if (options.environment === 'Production' && options.enableOnlineChecks === false) {
22
- throw new TypeError('Production App Store verification requires online certificate checks.');
23
- }
24
- const verifier = new SignedDataVerifier(options.rootCertificates.map((certificate) => Buffer.from(certificate)), options.enableOnlineChecks ?? true, options.environment === 'Production' ? Environment.PRODUCTION : Environment.SANDBOX, options.bundleId, options.appAppleId);
25
- return {
26
- async verifyAndDecode(input) {
27
- if (input.environment !== options.environment || input.bundleId !== options.bundleId) {
28
- return { status: 'rejected', reason: 'APP_STORE_VERIFIER_SCOPE_MISMATCH' };
29
- }
30
- if (typeof input.signedTransaction !== 'string'
31
- || input.signedTransaction.length === 0
32
- || input.signedTransaction.length > 512 * 1024) {
33
- return { status: 'rejected', reason: 'APP_STORE_SIGNED_TRANSACTION_INVALID' };
34
- }
35
- input.signal.throwIfAborted();
36
- let decoded;
37
- try {
38
- decoded = await waitForVerification(verifier.verifyAndDecodeTransaction(input.signedTransaction), input.signal);
39
- input.signal.throwIfAborted();
40
- }
41
- catch (error) {
42
- if (input.signal.aborted) {
43
- throw input.signal.reason;
44
- }
45
- if (error instanceof VerificationException) {
46
- if (error.status === VerificationStatus.RETRYABLE_VERIFICATION_FAILURE) {
47
- throw new AppStoreDependencyUnavailableError('Apple signature checks are temporarily unavailable.', error);
48
- }
49
- return { status: 'rejected', reason: 'APP_STORE_SIGNATURE_INVALID' };
50
- }
51
- // Only Apple's explicit retryable status is a transient dependency
52
- // failure. Unexpected adapter/library faults must reach the caller.
53
- throw error;
54
- }
55
- const payload = decodeOneTimeTransaction(decoded);
56
- return payload === undefined
57
- ? { status: 'rejected', reason: 'APP_STORE_SIGNED_TRANSACTION_INVALID' }
58
- : { status: 'verified', payload };
59
- },
60
- };
61
- }
62
- async function waitForVerification(operation, signal) {
63
- signal.throwIfAborted();
64
- let onAbort;
65
- const aborted = new Promise((_resolve, reject) => {
66
- onAbort = () => reject(signal.reason ?? new Error('Verification was aborted.'));
67
- signal.addEventListener('abort', onAbort, { once: true });
68
- if (signal.aborted) {
69
- onAbort();
70
- }
71
- });
72
- try {
73
- return await Promise.race([operation, aborted]);
74
- }
75
- finally {
76
- if (onAbort !== undefined) {
77
- signal.removeEventListener('abort', onAbort);
78
- }
79
- }
80
- }
81
- function decodeOneTimeTransaction(decoded) {
82
- if (!nonEmpty(decoded.transactionId)
83
- || !nonEmpty(decoded.originalTransactionId)
84
- || !nonEmpty(decoded.bundleId)
85
- || !nonEmpty(decoded.productId)
86
- || !validTimestamp(decoded.purchaseDate)
87
- || !validTimestamp(decoded.signedDate)
88
- || (decoded.environment !== 'Production' && decoded.environment !== 'Sandbox')
89
- || (decoded.type !== 'Consumable' && decoded.type !== 'Non-Consumable')
90
- || (decoded.quantity !== undefined
91
- && (!Number.isSafeInteger(decoded.quantity) || decoded.quantity <= 0))
92
- || (decoded.appAccountToken !== undefined && !nonEmpty(decoded.appAccountToken))
93
- || (decoded.revocationDate !== undefined && !validTimestamp(decoded.revocationDate))
94
- || (decoded.expiresDate !== undefined && !validTimestamp(decoded.expiresDate))
95
- || (decoded.isUpgraded !== undefined && typeof decoded.isUpgraded !== 'boolean')) {
96
- return undefined;
97
- }
98
- return {
99
- transactionId: decoded.transactionId,
100
- originalTransactionId: decoded.originalTransactionId,
101
- bundleId: decoded.bundleId,
102
- productId: decoded.productId,
103
- purchaseDate: decoded.purchaseDate,
104
- signedDate: decoded.signedDate,
105
- environment: decoded.environment,
106
- type: decoded.type,
107
- ...(decoded.quantity === undefined ? {} : { quantity: decoded.quantity }),
108
- ...(decoded.appAccountToken === undefined ? {} : { appAccountToken: decoded.appAccountToken }),
109
- ...(decoded.revocationDate === undefined ? {} : { revocationDate: decoded.revocationDate }),
110
- ...(decoded.expiresDate === undefined ? {} : { expiresDate: decoded.expiresDate }),
111
- ...(decoded.isUpgraded === undefined ? {} : { isUpgraded: decoded.isUpgraded }),
112
- };
113
- }
114
- function nonEmpty(value) {
115
- return typeof value === 'string' && value.trim() !== '';
116
- }
117
- function validTimestamp(value) {
118
- return typeof value === 'number' && Number.isSafeInteger(value) && value > 0;
119
- }
1
+ /**
2
+ * @deprecated Moved to `@mpgd/game-services/app-store-server` in A4 of the
3
+ * commerce v2 plan: server-side App Store verification does not belong in the
4
+ * client adapter. This subpath re-exports the same symbols for one release and
5
+ * will be removed in the next major version. Install
6
+ * `@apple/app-store-server-library` next to `@mpgd/game-services`.
7
+ */
8
+ import { createAppleSignedTransactionVerifier as createAppleSignedTransactionVerifierFromGameServices, } from '@mpgd/game-services/app-store-server';
9
+ /** @deprecated Import `createAppleSignedTransactionVerifier` from `@mpgd/game-services/app-store-server`. */
10
+ export const createAppleSignedTransactionVerifier = createAppleSignedTransactionVerifierFromGameServices;
@@ -1,8 +1,13 @@
1
- import type { BridgeMethod, BridgeRequest, BridgeResponse } from '@mpgd/bridge';
1
+ import { type BridgeEvent, type BridgeMethod, type BridgeRequest, type BridgeResponse } from '@mpgd/bridge';
2
2
  import type { AdProvider } from '@mpgd/platform/ads';
3
3
  import { type PlatformProviderAvailability, type PlatformProviderFeature, type ProductType } from '@mpgd/platform';
4
4
  export interface NativeBridge {
5
5
  request(input: BridgeRequest): Promise<BridgeResponse>;
6
+ /**
7
+ * Optional commerce v2 event delivery (`{ protocol: 'mpgd.commerce.v2', sequence, event }`).
8
+ * B1 wires it for store providers; the base gateway works without it.
9
+ */
10
+ subscribe?(listener: (event: BridgeEvent) => void): () => void;
6
11
  }
7
12
  /**
8
13
  * An optional, separately installed provider handles only its declared bridge
package/dist/providers.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { isBridgeCommerceV2Method, } from '@mpgd/bridge';
1
2
  import { isAdMobClientRewardEvidence } from '@mpgd/game-services/admob-client-reward';
2
3
  import { PlatformOperationError, } from '@mpgd/platform';
3
4
  const featureMethods = {
@@ -53,6 +54,12 @@ export function createCapacitorProviderRegistry(input) {
53
54
  throw new Error(`Capacitor provider ${provider.id} repeats a bridge method.`);
54
55
  }
55
56
  for (const method of methods) {
57
+ if (isBridgeCommerceV2Method(method)) {
58
+ // v1 providers own the v1 commerce methods only; a v2 provider arrives
59
+ // with B1 and is registered through its own surface.
60
+ throw new Error(`Capacitor provider ${provider.id} cannot declare commerce v2 method ${method}; `
61
+ + 'commerce v2 is unavailable through the provider registry.');
62
+ }
56
63
  if (!providerMethodSet.has(method)) {
57
64
  throw new Error(`Capacitor provider ${provider.id} cannot replace base method ${method}.`);
58
65
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mpgd/adapter-capacitor",
3
- "version": "0.7.5",
3
+ "version": "0.8.0",
4
4
  "description": "Capacitor platform adapter for mpgd Android and iOS shells.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -43,33 +43,28 @@
43
43
  ],
44
44
  "dependencies": {
45
45
  "@capacitor/app": "8.1.1",
46
- "@mpgd/capacitor-game-services": "0.6.1",
47
- "@mpgd/bridge": "0.11.0",
48
- "@mpgd/platform": "0.18.0",
49
- "@mpgd/game-services": "0.19.1",
50
- "@mpgd/target-config": "0.21.1"
46
+ "@mpgd/capacitor-game-services": "0.6.2",
47
+ "@mpgd/bridge": "0.12.0",
48
+ "@mpgd/platform": "0.19.0",
49
+ "@mpgd/game-services": "0.20.0",
50
+ "@mpgd/target-config": "0.21.2"
51
51
  },
52
52
  "peerDependencies": {
53
- "@apple/app-store-server-library": "^3.1.0",
54
53
  "@capacitor-community/admob": "^8.1.0",
55
54
  "@capacitor/core": "^8.5.1"
56
55
  },
57
56
  "peerDependenciesMeta": {
58
- "@apple/app-store-server-library": {
59
- "optional": true
60
- },
61
57
  "@capacitor-community/admob": {
62
58
  "optional": true
63
59
  }
64
60
  },
65
61
  "devDependencies": {
66
- "@apple/app-store-server-library": "3.1.0",
67
62
  "@capacitor-community/admob": "8.1.0",
68
63
  "@capacitor/core": "8.5.2",
69
64
  "ttsc": "0.30.4",
70
65
  "typescript": "7.0.2",
71
66
  "vitest": "^4.0.0",
72
- "@mpgd/game-runtime": "0.6.1"
67
+ "@mpgd/game-runtime": "0.6.2"
73
68
  },
74
69
  "main": "./dist/index.js",
75
70
  "types": "./dist/index.d.ts",