@onesub/server 0.2.0 → 0.3.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.
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Unit tests for Apple and Google product receipt validators.
3
+ * Covers: validateAppleConsumableReceipt, validateGoogleProductReceipt
4
+ */
5
+ export {};
6
+ //# sourceMappingURL=providers.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"providers.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/providers.test.ts"],"names":[],"mappings":"AAAA;;;GAGG"}
@@ -0,0 +1,261 @@
1
+ /**
2
+ * Unit tests for Apple and Google product receipt validators.
3
+ * Covers: validateAppleConsumableReceipt, validateGoogleProductReceipt
4
+ */
5
+ import { describe, it, expect, vi, beforeAll, beforeEach } from 'vitest';
6
+ import { generateKeyPairSync } from 'crypto';
7
+ import { validateAppleConsumableReceipt } from '../providers/apple.js';
8
+ import { validateGoogleProductReceipt } from '../providers/google.js';
9
+ // ── Apple helpers ──────────────────────────────────────────────────────────
10
+ /**
11
+ * Build a minimal JWS token whose payload is the given object.
12
+ * With skipJwsVerification=true, decodeJwt() only base64url-decodes the payload.
13
+ */
14
+ function makeJws(payload) {
15
+ const header = Buffer.from(JSON.stringify({ alg: 'ES256' })).toString('base64url');
16
+ const body = Buffer.from(JSON.stringify(payload)).toString('base64url');
17
+ return `${header}.${body}.fakesig`;
18
+ }
19
+ const APPLE_CONFIG = { bundleId: 'com.example.app', skipJwsVerification: true };
20
+ function validApplePayload(overrides) {
21
+ return {
22
+ bundleId: 'com.example.app',
23
+ type: 'Consumable',
24
+ productId: 'credits_100',
25
+ transactionId: 'txn_apple_001',
26
+ originalTransactionId: 'orig_apple_001',
27
+ purchaseDate: Date.now(),
28
+ ...overrides,
29
+ };
30
+ }
31
+ // ── Google helpers ──────────────────────────────────────────────────────────
32
+ let testPrivateKey;
33
+ beforeAll(() => {
34
+ // Generate a real RSA key pair once — needed for the JWT assertion in getAccessToken().
35
+ // 1024-bit is sufficient for tests (not production).
36
+ const { privateKey } = generateKeyPairSync('rsa', { modulusLength: 1024 });
37
+ testPrivateKey = privateKey.export({ type: 'pkcs8', format: 'pem' });
38
+ });
39
+ beforeEach(() => {
40
+ vi.restoreAllMocks();
41
+ });
42
+ /**
43
+ * Create a fresh Google config with a unique client_email per call.
44
+ * The module-level token cache in google.ts is keyed by the full JSON string,
45
+ * so varying the email ensures each test starts with a cold cache.
46
+ */
47
+ function makeGoogleConfig() {
48
+ return {
49
+ packageName: 'com.example.app',
50
+ serviceAccountKey: JSON.stringify({
51
+ client_email: `test-${Math.random()}@test.iam.gserviceaccount.com`,
52
+ private_key: testPrivateKey,
53
+ token_uri: 'https://oauth2.googleapis.com/token',
54
+ }),
55
+ };
56
+ }
57
+ /**
58
+ * Mock global fetch with URL-based routing:
59
+ * - oauth2.googleapis.com → returns a fake access token
60
+ * - androidpublisher.googleapis.com → returns the given product purchase object
61
+ */
62
+ function mockGoogleFetch(productPurchase) {
63
+ vi.spyOn(global, 'fetch').mockImplementation(async (url) => {
64
+ const urlStr = String(url);
65
+ if (urlStr.includes('oauth2.googleapis.com')) {
66
+ return {
67
+ ok: true,
68
+ json: async () => ({ access_token: 'test_access_token', expires_in: 3600 }),
69
+ text: async () => '',
70
+ };
71
+ }
72
+ if (urlStr.includes('androidpublisher.googleapis.com')) {
73
+ return {
74
+ ok: true,
75
+ json: async () => productPurchase,
76
+ text: async () => JSON.stringify(productPurchase),
77
+ };
78
+ }
79
+ throw new Error(`[test] Unexpected fetch URL: ${urlStr}`);
80
+ });
81
+ }
82
+ // ============================================================================
83
+ // validateAppleConsumableReceipt
84
+ // ============================================================================
85
+ describe('validateAppleConsumableReceipt', () => {
86
+ it('returns null when bundleId is missing in receipt', async () => {
87
+ const jws = makeJws(validApplePayload({ bundleId: undefined }));
88
+ expect(await validateAppleConsumableReceipt(jws, APPLE_CONFIG)).toBeNull();
89
+ });
90
+ it('returns null when bundleId mismatches config', async () => {
91
+ const jws = makeJws(validApplePayload({ bundleId: 'com.attacker.app' }));
92
+ expect(await validateAppleConsumableReceipt(jws, APPLE_CONFIG)).toBeNull();
93
+ });
94
+ it('returns null for Auto-Renewable Subscription type (not a consumable)', async () => {
95
+ const jws = makeJws(validApplePayload({ type: 'Auto-Renewable Subscription' }));
96
+ expect(await validateAppleConsumableReceipt(jws, APPLE_CONFIG)).toBeNull();
97
+ });
98
+ it('returns null for NonRenewingSubscription type', async () => {
99
+ const jws = makeJws(validApplePayload({ type: 'Non-Renewing Subscription' }));
100
+ expect(await validateAppleConsumableReceipt(jws, APPLE_CONFIG)).toBeNull();
101
+ });
102
+ it('returns null when the purchase is revoked (refunded)', async () => {
103
+ const jws = makeJws(validApplePayload({ revocationDate: Date.now() - 1000 }));
104
+ expect(await validateAppleConsumableReceipt(jws, APPLE_CONFIG)).toBeNull();
105
+ });
106
+ it('returns null when receipt is older than 72 hours', async () => {
107
+ const jws = makeJws(validApplePayload({ purchaseDate: Date.now() - 73 * 60 * 60 * 1000 }));
108
+ expect(await validateAppleConsumableReceipt(jws, APPLE_CONFIG)).toBeNull();
109
+ });
110
+ it('accepts a receipt that is exactly 71 hours old', async () => {
111
+ const jws = makeJws(validApplePayload({ purchaseDate: Date.now() - 71 * 60 * 60 * 1000 }));
112
+ expect(await validateAppleConsumableReceipt(jws, APPLE_CONFIG)).not.toBeNull();
113
+ });
114
+ it('returns null when expectedProductId mismatches receipt', async () => {
115
+ const jws = makeJws(validApplePayload({ productId: 'credits_200' }));
116
+ expect(await validateAppleConsumableReceipt(jws, APPLE_CONFIG, 'credits_100')).toBeNull();
117
+ });
118
+ it('validates productId when expectedProductId matches', async () => {
119
+ const jws = makeJws(validApplePayload({ productId: 'credits_100' }));
120
+ expect(await validateAppleConsumableReceipt(jws, APPLE_CONFIG, 'credits_100')).not.toBeNull();
121
+ });
122
+ it('returns null when JWS is malformed', async () => {
123
+ expect(await validateAppleConsumableReceipt('not.a.valid.jws.format', APPLE_CONFIG)).toBeNull();
124
+ });
125
+ it('returns valid result with correct fields for a Consumable purchase', async () => {
126
+ const now = Date.now();
127
+ const jws = makeJws(validApplePayload({
128
+ transactionId: 'txn_abc',
129
+ originalTransactionId: 'orig_abc',
130
+ purchaseDate: now,
131
+ }));
132
+ const result = await validateAppleConsumableReceipt(jws, APPLE_CONFIG);
133
+ expect(result).not.toBeNull();
134
+ expect(result?.transactionId).toBe('txn_abc');
135
+ expect(result?.productId).toBe('credits_100');
136
+ expect(result?.purchasedAt).toBe(new Date(now).toISOString());
137
+ });
138
+ it('uses transactionId (not originalTransactionId) for consumables', async () => {
139
+ const jws = makeJws(validApplePayload({
140
+ transactionId: 'per_tx_id',
141
+ originalTransactionId: 'shared_orig_id',
142
+ }));
143
+ const result = await validateAppleConsumableReceipt(jws, APPLE_CONFIG);
144
+ // per-transaction ID must be used — originalTransactionId is shared across re-purchases
145
+ expect(result?.transactionId).toBe('per_tx_id');
146
+ expect(result?.transactionId).not.toBe('shared_orig_id');
147
+ });
148
+ it('falls back to originalTransactionId when transactionId is absent', async () => {
149
+ const jws = makeJws(validApplePayload({
150
+ transactionId: undefined,
151
+ originalTransactionId: 'orig_only',
152
+ }));
153
+ const result = await validateAppleConsumableReceipt(jws, APPLE_CONFIG);
154
+ expect(result?.transactionId).toBe('orig_only');
155
+ });
156
+ it('accepts NonConsumable type', async () => {
157
+ const jws = makeJws(validApplePayload({ type: 'NonConsumable', productId: 'premium_unlock' }));
158
+ const result = await validateAppleConsumableReceipt(jws, APPLE_CONFIG);
159
+ expect(result).not.toBeNull();
160
+ });
161
+ });
162
+ // ============================================================================
163
+ // validateGoogleProductReceipt
164
+ // ============================================================================
165
+ describe('validateGoogleProductReceipt', () => {
166
+ it('returns null when serviceAccountKey is not provided', async () => {
167
+ const result = await validateGoogleProductReceipt('some_token', 'credits_100', { packageName: 'com.example.app' });
168
+ expect(result).toBeNull();
169
+ });
170
+ it('returns null when purchaseState is 1 (canceled)', async () => {
171
+ mockGoogleFetch({ purchaseState: 1, orderId: 'GPA.canceled' });
172
+ const result = await validateGoogleProductReceipt('token_canceled', 'credits_100', makeGoogleConfig());
173
+ expect(result).toBeNull();
174
+ });
175
+ it('returns null when purchaseState is 2 (pending)', async () => {
176
+ mockGoogleFetch({ purchaseState: 2, orderId: 'GPA.pending' });
177
+ const result = await validateGoogleProductReceipt('token_pending', 'credits_100', makeGoogleConfig());
178
+ expect(result).toBeNull();
179
+ });
180
+ it('returns null for consumable when consumptionState is 1 (already consumed)', async () => {
181
+ mockGoogleFetch({
182
+ purchaseState: 0,
183
+ consumptionState: 1,
184
+ purchaseTimeMillis: String(Date.now()),
185
+ orderId: 'GPA.already_consumed',
186
+ });
187
+ const result = await validateGoogleProductReceipt('token_consumed', 'credits_100', makeGoogleConfig(), 'consumable');
188
+ expect(result).toBeNull();
189
+ });
190
+ it('does NOT block non-consumable when consumptionState is 1', async () => {
191
+ // Non-consumables may have consumptionState=1 after acknowledgement — this is normal
192
+ mockGoogleFetch({
193
+ purchaseState: 0,
194
+ consumptionState: 1,
195
+ purchaseTimeMillis: String(Date.now()),
196
+ orderId: 'GPA.nc_acknowledged',
197
+ });
198
+ const result = await validateGoogleProductReceipt('token_nc', 'premium_unlock', makeGoogleConfig(), 'non_consumable');
199
+ expect(result).not.toBeNull();
200
+ expect(result?.transactionId).toBe('GPA.nc_acknowledged');
201
+ });
202
+ it('returns null when receipt is older than 72 hours', async () => {
203
+ const oldMs = String(Date.now() - 73 * 60 * 60 * 1000);
204
+ mockGoogleFetch({
205
+ purchaseState: 0,
206
+ purchaseTimeMillis: oldMs,
207
+ orderId: 'GPA.old',
208
+ });
209
+ const result = await validateGoogleProductReceipt('token_old', 'credits_100', makeGoogleConfig());
210
+ expect(result).toBeNull();
211
+ });
212
+ it('accepts a receipt that is exactly 71 hours old', async () => {
213
+ const recentMs = String(Date.now() - 71 * 60 * 60 * 1000);
214
+ mockGoogleFetch({
215
+ purchaseState: 0,
216
+ consumptionState: 0,
217
+ purchaseTimeMillis: recentMs,
218
+ orderId: 'GPA.recent',
219
+ });
220
+ const result = await validateGoogleProductReceipt('token_recent', 'credits_100', makeGoogleConfig(), 'consumable');
221
+ expect(result).not.toBeNull();
222
+ });
223
+ it('returns null when orderId is missing', async () => {
224
+ mockGoogleFetch({
225
+ purchaseState: 0,
226
+ purchaseTimeMillis: String(Date.now()),
227
+ // orderId absent
228
+ });
229
+ const result = await validateGoogleProductReceipt('token_no_order', 'credits_100', makeGoogleConfig());
230
+ expect(result).toBeNull();
231
+ });
232
+ it('returns valid result with orderId as transactionId on success', async () => {
233
+ const now = Date.now();
234
+ mockGoogleFetch({
235
+ purchaseState: 0,
236
+ consumptionState: 0,
237
+ purchaseTimeMillis: String(now),
238
+ orderId: 'GPA.valid_order_123',
239
+ });
240
+ const result = await validateGoogleProductReceipt('token_valid', 'credits_100', makeGoogleConfig(), 'consumable');
241
+ expect(result).not.toBeNull();
242
+ expect(result?.transactionId).toBe('GPA.valid_order_123');
243
+ expect(result?.purchasedAt).toBe(new Date(now).toISOString());
244
+ });
245
+ it('returns null when Play API returns an error response', async () => {
246
+ vi.spyOn(global, 'fetch').mockImplementation(async (url) => {
247
+ const urlStr = String(url);
248
+ if (urlStr.includes('oauth2.googleapis.com')) {
249
+ return {
250
+ ok: true,
251
+ json: async () => ({ access_token: 'tok', expires_in: 3600 }),
252
+ text: async () => '',
253
+ };
254
+ }
255
+ return { ok: false, text: async () => 'Not Found', json: async () => ({}) };
256
+ });
257
+ const result = await validateGoogleProductReceipt('token_bad', 'credits_100', makeGoogleConfig());
258
+ expect(result).toBeNull();
259
+ });
260
+ });
261
+ //# sourceMappingURL=providers.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"providers.test.js","sourceRoot":"","sources":["../../src/__tests__/providers.test.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { describe, it, expect, beforeEach } from 'vitest';
2
- import { InMemorySubscriptionStore } from '../store.js';
2
+ import { InMemorySubscriptionStore, InMemoryPurchaseStore } from '../store.js';
3
3
  const makeSub = (overrides) => ({
4
4
  userId: 'user_123',
5
5
  productId: 'com.example.pro_monthly',
@@ -68,4 +68,56 @@ describe('InMemorySubscriptionStore', () => {
68
68
  expect(await store.getByTransactionId('txn_2')).toEqual(sub2);
69
69
  });
70
70
  });
71
+ // ── InMemoryPurchaseStore ────────────────────────────────────────────────────
72
+ const makePurchase = (overrides) => ({
73
+ userId: 'user_1',
74
+ productId: 'credits_10',
75
+ platform: 'apple',
76
+ type: 'consumable',
77
+ transactionId: 'txn_p1',
78
+ purchasedAt: '2026-01-01T00:00:00.000Z',
79
+ quantity: 1,
80
+ ...overrides,
81
+ });
82
+ describe('InMemoryPurchaseStore', () => {
83
+ let purchaseStore;
84
+ beforeEach(() => {
85
+ purchaseStore = new InMemoryPurchaseStore();
86
+ });
87
+ it('savePurchase + getPurchasesByUserId returns purchases', async () => {
88
+ const p = makePurchase();
89
+ await purchaseStore.savePurchase(p);
90
+ const results = await purchaseStore.getPurchasesByUserId('user_1');
91
+ expect(results).toHaveLength(1);
92
+ expect(results[0]).toEqual(p);
93
+ });
94
+ it('getPurchaseByTransactionId returns the correct purchase', async () => {
95
+ const p = makePurchase();
96
+ await purchaseStore.savePurchase(p);
97
+ expect(await purchaseStore.getPurchaseByTransactionId('txn_p1')).toEqual(p);
98
+ expect(await purchaseStore.getPurchaseByTransactionId('unknown')).toBeNull();
99
+ });
100
+ it('hasPurchased returns true for existing purchase', async () => {
101
+ await purchaseStore.savePurchase(makePurchase({ type: 'non_consumable', productId: 'premium_unlock' }));
102
+ expect(await purchaseStore.hasPurchased('user_1', 'premium_unlock')).toBe(true);
103
+ expect(await purchaseStore.hasPurchased('user_1', 'other_product')).toBe(false);
104
+ expect(await purchaseStore.hasPurchased('user_2', 'premium_unlock')).toBe(false);
105
+ });
106
+ it('stores multiple consumable purchases for same user+product', async () => {
107
+ await purchaseStore.savePurchase(makePurchase({ transactionId: 'txn_1' }));
108
+ await purchaseStore.savePurchase(makePurchase({ transactionId: 'txn_2' }));
109
+ await purchaseStore.savePurchase(makePurchase({ transactionId: 'txn_3' }));
110
+ const results = await purchaseStore.getPurchasesByUserId('user_1');
111
+ expect(results).toHaveLength(3);
112
+ });
113
+ it('getPurchasesByUserId returns empty for unknown user', async () => {
114
+ expect(await purchaseStore.getPurchasesByUserId('unknown')).toEqual([]);
115
+ });
116
+ it('stores purchases for different users independently', async () => {
117
+ await purchaseStore.savePurchase(makePurchase({ userId: 'a', transactionId: 't1' }));
118
+ await purchaseStore.savePurchase(makePurchase({ userId: 'b', transactionId: 't2' }));
119
+ expect(await purchaseStore.getPurchasesByUserId('a')).toHaveLength(1);
120
+ expect(await purchaseStore.getPurchasesByUserId('b')).toHaveLength(1);
121
+ });
122
+ });
71
123
  //# sourceMappingURL=store.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"store.test.js","sourceRoot":"","sources":["../../src/__tests__/store.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"store.test.js","sourceRoot":"","sources":["../../src/__tests__/store.test.ts"],"names":[],"mappings":"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"}
@@ -15,6 +15,26 @@ export declare function decodeJws<T>(jws: string, skipVerification?: boolean): P
15
15
  * deprecating it.
16
16
  */
17
17
  export declare function validateAppleReceipt(receipt: string, config: AppleConfig): Promise<SubscriptionInfo | null>;
18
+ /**
19
+ * Result of validating an Apple consumable or non-consumable receipt.
20
+ */
21
+ export interface AppleProductResult {
22
+ /** Per-transaction unique ID (transactionId, not originalTransactionId) */
23
+ transactionId: string;
24
+ productId: string;
25
+ purchasedAt: string;
26
+ }
27
+ /**
28
+ * Validate an Apple StoreKit 2 JWS signedTransaction for a consumable or
29
+ * non-consumable product.
30
+ *
31
+ * Differences from validateAppleReceipt (subscriptions):
32
+ * - Checks tx.type is 'Consumable' or 'NonConsumable' (not a subscription)
33
+ * - Uses tx.transactionId (per-purchase unique) instead of originalTransactionId
34
+ * - Enforces a 72-hour receipt age limit to block replay attacks
35
+ * - Does NOT check expiresDate (one-time purchases don't expire)
36
+ */
37
+ export declare function validateAppleConsumableReceipt(signedTransaction: string, config: AppleConfig, expectedProductId?: string): Promise<AppleProductResult | null>;
18
38
  /**
19
39
  * Decode an Apple Server Notification V2 payload.
20
40
  * Returns the derived subscription update to be merged into the store.
@@ -1 +1 @@
1
- {"version":3,"file":"apple.d.ts","sourceRoot":"","sources":["../../src/providers/apple.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,wBAAwB,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAGrG,KAAK,WAAW,GAAG,WAAW,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC,CAAC;AAwC5D;;;;GAIG;AACH,wBAAsB,SAAS,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,gBAAgB,UAAQ,GAAG,OAAO,CAAC,CAAC,CAAC,CAepF;AAsBD;;;;;;;GAOG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,WAAW,GAClB,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAkClC;AAED;;;GAGG;AACH,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,wBAAwB,EACjC,mBAAmB,UAAQ,GAC1B,OAAO,CAAC;IAAE,qBAAqB,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,gBAAgB,CAAC,QAAQ,CAAC,CAAC;IAAC,SAAS,EAAE,OAAO,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAC,CA6B9H"}
1
+ {"version":3,"file":"apple.d.ts","sourceRoot":"","sources":["../../src/providers/apple.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,wBAAwB,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAGrG,KAAK,WAAW,GAAG,WAAW,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC,CAAC;AA2C5D;;;;GAIG;AACH,wBAAsB,SAAS,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,gBAAgB,UAAQ,GAAG,OAAO,CAAC,CAAC,CAAC,CAepF;AAsBD;;;;;;;GAOG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,WAAW,GAClB,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAwClC;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,2EAA2E;IAC3E,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;;GASG;AACH,wBAAsB,8BAA8B,CAClD,iBAAiB,EAAE,MAAM,EACzB,MAAM,EAAE,WAAW,EACnB,iBAAiB,CAAC,EAAE,MAAM,GACzB,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAkEpC;AAED;;;GAGG;AACH,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,wBAAwB,EACjC,mBAAmB,UAAQ,GAC1B,OAAO,CAAC;IAAE,qBAAqB,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,gBAAgB,CAAC,QAAQ,CAAC,CAAC;IAAC,SAAS,EAAE,OAAO,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAC,CA6B9H"}
@@ -1,5 +1,7 @@
1
1
  import { decodeJwt, createRemoteJWKSet, jwtVerify } from 'jose';
2
2
  import { SUBSCRIPTION_STATUS } from '@onesub/shared';
3
+ /** Maximum age for consumable receipts (72 hours). Older receipts may indicate replay attacks. */
4
+ const MAX_CONSUMABLE_RECEIPT_AGE_MS = 72 * 60 * 60 * 1000;
3
5
  /**
4
6
  * Module-level JWKS fetcher for Apple's public keys.
5
7
  * Cached automatically by jose's createRemoteJWKSet.
@@ -59,11 +61,16 @@ export async function validateAppleReceipt(receipt, config) {
59
61
  if (!tx.originalTransactionId || !tx.productId || !tx.expiresDate) {
60
62
  return null;
61
63
  }
62
- // Validate bundle ID if provided
63
- if (tx.bundleId && tx.bundleId !== config.bundleId) {
64
+ // Validate bundle ID missing or mismatched both rejected
65
+ if (!tx.bundleId || tx.bundleId !== config.bundleId) {
64
66
  console.warn('[onesub/apple] Bundle ID mismatch:', tx.bundleId, '!==', config.bundleId);
65
67
  return null;
66
68
  }
69
+ // Reject Sandbox receipts in production
70
+ if (process.env['NODE_ENV'] === 'production' && tx.environment !== 'Production') {
71
+ console.warn('[onesub/apple] Sandbox receipt rejected in production:', tx.environment);
72
+ return null;
73
+ }
67
74
  const status = deriveStatus(tx, null);
68
75
  const purchasedAt = tx.originalPurchaseDate ?? tx.purchaseDate ?? Date.now();
69
76
  return {
@@ -77,6 +84,74 @@ export async function validateAppleReceipt(receipt, config) {
77
84
  willRenew: status === SUBSCRIPTION_STATUS.ACTIVE, // refined by renewal info in webhook
78
85
  };
79
86
  }
87
+ /**
88
+ * Validate an Apple StoreKit 2 JWS signedTransaction for a consumable or
89
+ * non-consumable product.
90
+ *
91
+ * Differences from validateAppleReceipt (subscriptions):
92
+ * - Checks tx.type is 'Consumable' or 'NonConsumable' (not a subscription)
93
+ * - Uses tx.transactionId (per-purchase unique) instead of originalTransactionId
94
+ * - Enforces a 72-hour receipt age limit to block replay attacks
95
+ * - Does NOT check expiresDate (one-time purchases don't expire)
96
+ */
97
+ export async function validateAppleConsumableReceipt(signedTransaction, config, expectedProductId) {
98
+ let tx;
99
+ try {
100
+ tx = await decodeJws(signedTransaction, config.skipJwsVerification);
101
+ }
102
+ catch {
103
+ console.warn('[onesub/apple] Failed to decode consumable JWS');
104
+ return null;
105
+ }
106
+ // bundleId must be present and match
107
+ if (!tx.bundleId || tx.bundleId !== config.bundleId) {
108
+ console.warn('[onesub/apple] Bundle ID mismatch:', tx.bundleId, '!==', config.bundleId);
109
+ return null;
110
+ }
111
+ // Must be a one-time purchase type (not a subscription)
112
+ if (tx.type !== 'Consumable' && tx.type !== 'NonConsumable') {
113
+ console.warn('[onesub/apple] Invalid purchase type for product validation:', tx.type);
114
+ return null;
115
+ }
116
+ // Reject Sandbox receipts in production
117
+ if (process.env['NODE_ENV'] === 'production' && tx.environment !== 'Production') {
118
+ console.warn('[onesub/apple] Sandbox receipt rejected in production:', tx.environment);
119
+ return null;
120
+ }
121
+ if (!tx.productId) {
122
+ console.warn('[onesub/apple] No productId in transaction');
123
+ return null;
124
+ }
125
+ if (expectedProductId && tx.productId !== expectedProductId) {
126
+ console.warn('[onesub/apple] Product ID mismatch:', tx.productId, '!==', expectedProductId);
127
+ return null;
128
+ }
129
+ // Reject refunded purchases
130
+ if (tx.revocationDate) {
131
+ console.warn('[onesub/apple] Purchase was revoked/refunded');
132
+ return null;
133
+ }
134
+ // Reject receipts older than 72 hours (replay attack prevention)
135
+ if (tx.purchaseDate && Date.now() - tx.purchaseDate > MAX_CONSUMABLE_RECEIPT_AGE_MS) {
136
+ console.warn('[onesub/apple] Consumable receipt too old (>72h)');
137
+ return null;
138
+ }
139
+ // For consumables, transactionId is per-purchase unique.
140
+ // originalTransactionId is shared across re-purchases, so it must not be used
141
+ // as the deduplication key for consumables.
142
+ const transactionId = tx.transactionId ?? tx.originalTransactionId;
143
+ if (!transactionId) {
144
+ console.warn('[onesub/apple] No transactionId in consumable transaction');
145
+ return null;
146
+ }
147
+ return {
148
+ transactionId,
149
+ productId: tx.productId,
150
+ purchasedAt: tx.purchaseDate
151
+ ? new Date(tx.purchaseDate).toISOString()
152
+ : new Date().toISOString(),
153
+ };
154
+ }
80
155
  /**
81
156
  * Decode an Apple Server Notification V2 payload.
82
157
  * Returns the derived subscription update to be merged into the store.
@@ -1 +1 @@
1
- {"version":3,"file":"apple.js","sourceRoot":"","sources":["../../src/providers/apple.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"apple.js","sourceRoot":"","sources":["../../src/providers/apple.ts"],"names":[],"mappings":"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"}
@@ -28,6 +28,25 @@ type GoogleNotificationType = (typeof GOOGLE_NOTIFICATION_TYPE)[keyof typeof GOO
28
28
  * @param config - Google config with packageName and optional serviceAccountKey
29
29
  */
30
30
  export declare function validateGoogleReceipt(receipt: string, productId: string, config: GoogleConfig): Promise<SubscriptionInfo | null>;
31
+ /**
32
+ * Result of validating a Google Play product (consumable / non-consumable) receipt.
33
+ */
34
+ export interface GoogleProductResult {
35
+ /** orderId — unique per Google Play transaction, safe deduplication key */
36
+ transactionId: string;
37
+ purchasedAt: string;
38
+ }
39
+ /**
40
+ * Validate a Google Play purchase token for a one-time product (consumable or
41
+ * non-consumable). Uses purchases.products, not purchases.subscriptions.
42
+ *
43
+ * Security checks applied beyond basic API verification:
44
+ * - purchaseState must be 0 (completed)
45
+ * - consumptionState checked for consumables: already-consumed tokens indicate replay
46
+ * - Receipt age limited to 72 hours
47
+ * - orderId used as transactionId (per-purchase unique, unlike purchaseToken)
48
+ */
49
+ export declare function validateGoogleProductReceipt(purchaseToken: string, productId: string, config: GoogleConfig, type?: 'consumable' | 'non_consumable'): Promise<GoogleProductResult | null>;
31
50
  /**
32
51
  * Decode a Google RTDN Pub/Sub notification.
33
52
  * Returns the extracted subscription notification data, or null for test pings.
@@ -1 +1 @@
1
- {"version":3,"file":"google.d.ts","sourceRoot":"","sources":["../../src/providers/google.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,yBAAyB,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAGtG,KAAK,YAAY,GAAG,WAAW,CAAC,kBAAkB,CAAC,QAAQ,CAAC,CAAC,CAAC;AAuB9D;;;GAGG;AACH,QAAA,MAAM,wBAAwB;;;;;;;;;;;;;;CAcpB,CAAC;AAEX,KAAK,sBAAsB,GAAG,CAAC,OAAO,wBAAwB,CAAC,CAAC,MAAM,OAAO,wBAAwB,CAAC,CAAC;AAyJvG;;;;;;GAMG;AACH,wBAAsB,qBAAqB,CACzC,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,YAAY,GACnB,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CA6BlC;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,yBAAyB,GAAG;IAC5E,gBAAgB,EAAE,sBAAsB,CAAC;IACzC,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;CACrB,GAAG,IAAI,CAuBP;AAED;;GAEG;AACH,wBAAgB,0BAA0B,CAAC,gBAAgB,EAAE,sBAAsB,GAAG,OAAO,CAQ5F;AAED,wBAAgB,4BAA4B,CAAC,gBAAgB,EAAE,sBAAsB,GAAG,OAAO,CAK9F;AAED,wBAAgB,2BAA2B,CAAC,gBAAgB,EAAE,sBAAsB,GAAG,OAAO,CAE7F"}
1
+ {"version":3,"file":"google.d.ts","sourceRoot":"","sources":["../../src/providers/google.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,yBAAyB,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAGtG,KAAK,YAAY,GAAG,WAAW,CAAC,kBAAkB,CAAC,QAAQ,CAAC,CAAC,CAAC;AAuB9D;;;GAGG;AACH,QAAA,MAAM,wBAAwB;;;;;;;;;;;;;;CAcpB,CAAC;AAEX,KAAK,sBAAsB,GAAG,CAAC,OAAO,wBAAwB,CAAC,CAAC,MAAM,OAAO,wBAAwB,CAAC,CAAC;AAmMvG;;;;;;GAMG;AACH,wBAAsB,qBAAqB,CACzC,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,YAAY,GACnB,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CA6BlC;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,2EAA2E;IAC3E,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;;GASG;AACH,wBAAsB,4BAA4B,CAChD,aAAa,EAAE,MAAM,EACrB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,YAAY,EACpB,IAAI,GAAE,YAAY,GAAG,gBAAmC,GACvD,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC,CAgDrC;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,yBAAyB,GAAG;IAC5E,gBAAgB,EAAE,sBAAsB,CAAC;IACzC,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;CACrB,GAAG,IAAI,CAuBP;AAED;;GAEG;AACH,wBAAgB,0BAA0B,CAAC,gBAAgB,EAAE,sBAAsB,GAAG,OAAO,CAQ5F;AAED,wBAAgB,4BAA4B,CAAC,gBAAgB,EAAE,sBAAsB,GAAG,OAAO,CAK9F;AAED,wBAAgB,2BAA2B,CAAC,gBAAgB,EAAE,sBAAsB,GAAG,OAAO,CAE7F"}
@@ -18,6 +18,8 @@ const GOOGLE_NOTIFICATION_TYPE = {
18
18
  SUBSCRIPTION_REVOKED: 12,
19
19
  SUBSCRIPTION_EXPIRED: 13,
20
20
  };
21
+ /** Maximum age for product receipts (72 hours). */
22
+ const MAX_PRODUCT_RECEIPT_AGE_MS = 72 * 60 * 60 * 1000;
21
23
  /**
22
24
  * Module-level token cache. Keyed by the raw serviceAccountKey string so that
23
25
  * different service accounts (rare but possible) are cached independently.
@@ -109,6 +111,23 @@ async function fetchSubscriptionPurchase(packageName, productId, purchaseToken,
109
111
  }
110
112
  return resp.json();
111
113
  }
114
+ /**
115
+ * Fetch a one-time product purchase from the Google Play Developer API.
116
+ * Uses purchases.products instead of purchases.subscriptions.
117
+ */
118
+ async function fetchProductPurchase(packageName, productId, purchaseToken, accessToken) {
119
+ const url = `https://androidpublisher.googleapis.com/androidpublisher/v3/applications/` +
120
+ `${encodeURIComponent(packageName)}/purchases/products/` +
121
+ `${encodeURIComponent(productId)}/tokens/${encodeURIComponent(purchaseToken)}`;
122
+ const resp = await fetch(url, {
123
+ headers: { Authorization: `Bearer ${accessToken}` },
124
+ });
125
+ if (!resp.ok) {
126
+ const body = await resp.text();
127
+ throw new Error(`[onesub/google] Play Products API error ${resp.status}: ${body}`);
128
+ }
129
+ return resp.json();
130
+ }
112
131
  /**
113
132
  * Derive a SubscriptionStatus from a Google Play purchase resource.
114
133
  */
@@ -158,6 +177,60 @@ export async function validateGoogleReceipt(receipt, productId, config) {
158
177
  willRenew: purchase.autoRenewing ?? false,
159
178
  };
160
179
  }
180
+ /**
181
+ * Validate a Google Play purchase token for a one-time product (consumable or
182
+ * non-consumable). Uses purchases.products, not purchases.subscriptions.
183
+ *
184
+ * Security checks applied beyond basic API verification:
185
+ * - purchaseState must be 0 (completed)
186
+ * - consumptionState checked for consumables: already-consumed tokens indicate replay
187
+ * - Receipt age limited to 72 hours
188
+ * - orderId used as transactionId (per-purchase unique, unlike purchaseToken)
189
+ */
190
+ export async function validateGoogleProductReceipt(purchaseToken, productId, config, type = 'non_consumable') {
191
+ if (!config.serviceAccountKey) {
192
+ console.warn('[onesub/google] No serviceAccountKey — cannot validate product receipt');
193
+ return null;
194
+ }
195
+ let purchase;
196
+ try {
197
+ const token = await getCachedAccessToken(config.serviceAccountKey);
198
+ purchase = await fetchProductPurchase(config.packageName, productId, purchaseToken, token);
199
+ }
200
+ catch (err) {
201
+ console.error('[onesub/google] Product receipt validation failed:', err);
202
+ return null;
203
+ }
204
+ // purchaseState 0 = completed (1 = canceled, 2 = pending)
205
+ if (purchase.purchaseState !== 0) {
206
+ console.warn('[onesub/google] Purchase not completed, state:', purchase.purchaseState);
207
+ return null;
208
+ }
209
+ // For consumables: consumptionState 1 means already consumed by a previous request.
210
+ // This is the primary replay-attack signal for consumables on Android.
211
+ if (type === 'consumable' && purchase.consumptionState === 1) {
212
+ console.warn('[onesub/google] Consumable already consumed — possible replay attack');
213
+ return null;
214
+ }
215
+ // Reject receipts older than 72 hours
216
+ if (purchase.purchaseTimeMillis) {
217
+ const purchaseTime = parseInt(purchase.purchaseTimeMillis, 10);
218
+ if (Date.now() - purchaseTime > MAX_PRODUCT_RECEIPT_AGE_MS) {
219
+ console.warn('[onesub/google] Product receipt too old (>72h)');
220
+ return null;
221
+ }
222
+ }
223
+ if (!purchase.orderId) {
224
+ console.warn('[onesub/google] No orderId in product purchase');
225
+ return null;
226
+ }
227
+ return {
228
+ transactionId: purchase.orderId,
229
+ purchasedAt: purchase.purchaseTimeMillis
230
+ ? new Date(parseInt(purchase.purchaseTimeMillis, 10)).toISOString()
231
+ : new Date().toISOString(),
232
+ };
233
+ }
161
234
  /**
162
235
  * Decode a Google RTDN Pub/Sub notification.
163
236
  * Returns the extracted subscription notification data, or null for test pings.
@@ -1 +1 @@
1
- {"version":3,"file":"google.js","sourceRoot":"","sources":["../../src/providers/google.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAyBrD;;;GAGG;AACH,MAAM,wBAAwB,GAAG;IAC/B,sBAAsB,EAAE,CAAC;IACzB,oBAAoB,EAAE,CAAC;IACvB,qBAAqB,EAAE,CAAC;IACxB,sBAAsB,EAAE,CAAC;IACzB,oBAAoB,EAAE,CAAC;IACvB,4BAA4B,EAAE,CAAC;IAC/B,sBAAsB,EAAE,CAAC;IACzB,mCAAmC,EAAE,CAAC;IACtC,qBAAqB,EAAE,CAAC;IACxB,mBAAmB,EAAE,EAAE;IACvB,mCAAmC,EAAE,EAAE;IACvC,oBAAoB,EAAE,EAAE;IACxB,oBAAoB,EAAE,EAAE;CAChB,CAAC;AAsBX;;;GAGG;AACH,IAAI,WAAW,GAA6D,IAAI,CAAC;AACjF,IAAI,cAAc,GAA2B,IAAI,CAAC;AAElD;;;;;;;;GAQG;AACH,KAAK,UAAU,oBAAoB,CAAC,iBAAyB;IAC3D,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACvB,IAAI,WAAW,IAAI,WAAW,CAAC,GAAG,KAAK,iBAAiB,IAAI,WAAW,CAAC,SAAS,GAAG,GAAG,GAAG,MAAM,EAAE,CAAC;QACjG,OAAO,WAAW,CAAC,KAAK,CAAC;IAC3B,CAAC;IACD,IAAI,CAAC,cAAc,EAAE,CAAC;QACpB,cAAc,GAAG,cAAc,CAAC,iBAAiB,CAAC;aAC/C,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE;YACd,WAAW,GAAG,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,EAAE,GAAG,EAAE,iBAAiB,EAAE,CAAC;YACnF,OAAO,KAAK,CAAC;QACf,CAAC,CAAC;aACD,OAAO,CAAC,GAAG,EAAE,GAAG,cAAc,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/C,CAAC;IACD,OAAO,cAAc,CAAC;AACxB,CAAC;AAED;;;GAGG;AACH,KAAK,UAAU,cAAc,CAAC,iBAAyB;IACrD,IAAI,GAIH,CAAC;IAEF,IAAI,CAAC;QACH,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAe,CAAC;IACpD,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAC;IACpE,CAAC;IAED,MAAM,QAAQ,GAAG,GAAG,CAAC,SAAS,IAAI,qCAAqC,CAAC;IACxE,MAAM,KAAK,GAAG,kDAAkD,CAAC;IACjE,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;IAE1C,wBAAwB;IACxB,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;IAC/F,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CACzB,IAAI,CAAC,SAAS,CAAC;QACb,GAAG,EAAE,GAAG,CAAC,YAAY;QACrB,KAAK;QACL,GAAG,EAAE,QAAQ;QACb,GAAG,EAAE,GAAG;QACR,GAAG,EAAE,GAAG,GAAG,IAAI;KAChB,CAAC,CACH,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;IAExB,MAAM,YAAY,GAAG,GAAG,MAAM,IAAI,OAAO,EAAE,CAAC;IAE5C,2DAA2D;IAC3D,MAAM,EAAE,UAAU,EAAE,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,CAAC;IAC9C,MAAM,IAAI,GAAG,UAAU,CAAC,YAAY,CAAC,CAAC;IACtC,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;IAC1B,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;IAE1D,MAAM,SAAS,GAAG,GAAG,YAAY,IAAI,SAAS,EAAE,CAAC;IAEjD,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,QAAQ,EAAE;QACjC,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,cAAc,EAAE,mCAAmC,EAAE;QAChE,IAAI,EAAE,IAAI,eAAe,CAAC;YACxB,UAAU,EAAE,6CAA6C;YACzD,SAAS;SACV,CAAC;KACH,CAAC,CAAC;IAEH,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,yCAAyC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;IAC1E,CAAC;IAED,MAAM,IAAI,GAAG,CAAC,MAAM,IAAI,CAAC,IAAI,EAAE,CAA8B,CAAC;IAC9D,IAAI,CAAC,IAAI,CAAC,YAAY;QAAE,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAC;IACvF,OAAO,IAAI,CAAC,YAAY,CAAC;AAC3B,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,yBAAyB,CACtC,WAAmB,EACnB,SAAiB,EACjB,aAAqB,EACrB,WAAmB;IAEnB,MAAM,GAAG,GAAG,4EAA4E,kBAAkB,CAAC,WAAW,CAAC,4BAA4B,kBAAkB,CAAC,SAAS,CAAC,WAAW,kBAAkB,CAAC,aAAa,CAAC,EAAE,CAAC;IAE/N,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;QAC5B,OAAO,EAAE,EAAE,aAAa,EAAE,UAAU,WAAW,EAAE,EAAE;KACpD,CAAC,CAAC;IAEH,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;QACb,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;QAC/B,MAAM,IAAI,KAAK,CAAC,kCAAkC,IAAI,CAAC,MAAM,KAAK,IAAI,EAAE,CAAC,CAAC;IAC5E,CAAC;IAED,OAAO,IAAI,CAAC,IAAI,EAAyC,CAAC;AAC5D,CAAC;AAED;;GAEG;AACH,SAAS,YAAY,CAAC,QAAoC;IACxD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACvB,MAAM,QAAQ,GAAG,QAAQ,CAAC,gBAAgB,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,gBAAgB,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAEzF,IAAI,QAAQ,GAAG,GAAG,EAAE,CAAC;QACnB,4DAA4D;QAC5D,OAAO,mBAAmB,CAAC,MAAM,CAAC;IACpC,CAAC;IAED,IAAI,QAAQ,CAAC,YAAY,KAAK,SAAS;QAAE,OAAO,mBAAmB,CAAC,QAAQ,CAAC;IAE7E,OAAO,mBAAmB,CAAC,OAAO,CAAC;AACrC,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,OAAe,EACf,SAAiB,EACjB,MAAoB;IAEpB,IAAI,CAAC,MAAM,CAAC,iBAAiB,EAAE,CAAC;QAC9B,OAAO,CAAC,IAAI,CAAC,sEAAsE,CAAC,CAAC;QACrF,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,QAAoC,CAAC;IACzC,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,MAAM,oBAAoB,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC;QACnE,QAAQ,GAAG,MAAM,yBAAyB,CAAC,MAAM,CAAC,WAAW,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;IAC5F,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CAAC,4CAA4C,EAAE,GAAG,CAAC,CAAC;QACjE,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,MAAM,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;IACtC,MAAM,QAAQ,GAAG,QAAQ,CAAC,gBAAgB,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,gBAAgB,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC;IAClG,MAAM,OAAO,GAAG,QAAQ,CAAC,eAAe,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,eAAe,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC;IAE/F,OAAO;QACL,MAAM,EAAE,EAAE,EAAG,uBAAuB;QACpC,SAAS;QACT,QAAQ,EAAE,QAAQ;QAClB,MAAM;QACN,SAAS,EAAE,IAAI,IAAI,CAAC,QAAQ,CAAC,CAAC,WAAW,EAAE;QAC3C,qBAAqB,EAAE,QAAQ,CAAC,OAAO,IAAI,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC;QAC/D,WAAW,EAAE,IAAI,IAAI,CAAC,OAAO,CAAC,CAAC,WAAW,EAAE;QAC5C,SAAS,EAAE,QAAQ,CAAC,YAAY,IAAI,KAAK;KAC1C,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,wBAAwB,CAAC,OAAkC;IAMzE,IAAI,YAAyC,CAAC;IAE9C,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QAC3E,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAgC,CAAC;IACjE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,CAAC,YAAY,CAAC,wBAAwB,EAAE,CAAC;QAC3C,wCAAwC;QACxC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,EAAE,gBAAgB,EAAE,aAAa,EAAE,cAAc,EAAE,GAAG,YAAY,CAAC,wBAAwB,CAAC;IAElG,OAAO;QACL,gBAAgB;QAChB,aAAa;QACb,cAAc;QACd,WAAW,EAAE,YAAY,CAAC,WAAW;KACtC,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,0BAA0B,CAAC,gBAAwC;IACjF,OAAO,CACL,gBAAgB,KAAK,wBAAwB,CAAC,sBAAsB;QACpE,gBAAgB,KAAK,wBAAwB,CAAC,oBAAoB;QAClE,gBAAgB,KAAK,wBAAwB,CAAC,sBAAsB;QACpE,gBAAgB,KAAK,wBAAwB,CAAC,sBAAsB;QACpE,gBAAgB,KAAK,wBAAwB,CAAC,4BAA4B,CAC3E,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,4BAA4B,CAAC,gBAAwC;IACnF,OAAO,CACL,gBAAgB,KAAK,wBAAwB,CAAC,qBAAqB;QACnE,gBAAgB,KAAK,wBAAwB,CAAC,oBAAoB,CACnE,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,2BAA2B,CAAC,gBAAwC;IAClF,OAAO,gBAAgB,KAAK,wBAAwB,CAAC,oBAAoB,CAAC;AAC5E,CAAC"}
1
+ {"version":3,"file":"google.js","sourceRoot":"","sources":["../../src/providers/google.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"purchase.d.ts","sourceRoot":"","sources":["../../src/routes/purchase.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAGjC,OAAO,KAAK,EAIV,kBAAkB,EACnB,MAAM,gBAAgB,CAAC;AAExB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAiBjD,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,kBAAkB,EAC1B,aAAa,EAAE,aAAa,GAC3B,MAAM,CA2KR"}
1
+ {"version":3,"file":"purchase.d.ts","sourceRoot":"","sources":["../../src/routes/purchase.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAGjC,OAAO,KAAK,EAIV,kBAAkB,EACnB,MAAM,gBAAgB,CAAC;AAExB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAiBjD,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,kBAAkB,EAC1B,aAAa,EAAE,aAAa,GAC3B,MAAM,CAwLR"}
@@ -1,8 +1,8 @@
1
1
  import { Router } from 'express';
2
2
  import { z } from 'zod';
3
3
  import { ROUTES, PURCHASE_TYPE } from '@onesub/shared';
4
- import { validateAppleReceipt } from '../providers/apple.js';
5
- import { validateGoogleReceipt } from '../providers/google.js';
4
+ import { validateAppleConsumableReceipt } from '../providers/apple.js';
5
+ import { validateGoogleProductReceipt } from '../providers/google.js';
6
6
  const validatePurchaseSchema = z.object({
7
7
  platform: z.enum(['apple', 'google']),
8
8
  receipt: z.string().min(1).max(10000),
@@ -22,6 +22,13 @@ export function createPurchaseRouter(config, purchaseStore) {
22
22
  * Validate a receipt for a consumable or non-consumable purchase.
23
23
  * - Non-consumables: rejected if the user already owns the product.
24
24
  * - Consumables: always recorded (multiple purchases allowed).
25
+ *
26
+ * Uses platform-specific product validators (purchases.products API) instead
27
+ * of the subscription validator. Key differences:
28
+ * - Apple: validates tx.type === 'Consumable'|'NonConsumable', uses transactionId
29
+ * (not originalTransactionId) as the dedup key, enforces 72h receipt age
30
+ * - Google: checks consumptionState for consumables (replay prevention),
31
+ * enforces 72h receipt age, uses orderId as the dedup key
25
32
  */
26
33
  router.post(ROUTES.VALIDATE_PURCHASE, async (req, res) => {
27
34
  let body;
@@ -42,7 +49,7 @@ export function createPurchaseRouter(config, purchaseStore) {
42
49
  }
43
50
  const { platform, receipt, userId, productId, type } = body;
44
51
  try {
45
- // Non-consumable duplicate check
52
+ // Non-consumable duplicate check (before receipt validation to fail fast)
46
53
  if (type === PURCHASE_TYPE.NON_CONSUMABLE) {
47
54
  const alreadyOwned = await purchaseStore.hasPurchased(userId, productId);
48
55
  if (alreadyOwned) {
@@ -55,9 +62,10 @@ export function createPurchaseRouter(config, purchaseStore) {
55
62
  return;
56
63
  }
57
64
  }
58
- // Validate receipt via the appropriate provider.
59
- // Both providers return a SubscriptionInfo-shaped object; we extract only
60
- // the fields we need for PurchaseInfo (transactionId, purchasedAt).
65
+ // Validate receipt via the appropriate platform-specific product validator.
66
+ // Note: these are separate from the subscription validators — they call
67
+ // purchases.products (not purchases.subscriptions) and apply product-specific
68
+ // security checks (type validation, consumptionState, receipt age).
61
69
  let transactionId = null;
62
70
  let purchasedAt = new Date().toISOString();
63
71
  if (platform === 'apple') {
@@ -70,10 +78,10 @@ export function createPurchaseRouter(config, purchaseStore) {
70
78
  res.status(500).json(response);
71
79
  return;
72
80
  }
73
- const sub = await validateAppleReceipt(receipt, config.apple);
74
- if (sub) {
75
- transactionId = sub.originalTransactionId;
76
- purchasedAt = sub.purchasedAt;
81
+ const result = await validateAppleConsumableReceipt(receipt, config.apple, productId);
82
+ if (result) {
83
+ transactionId = result.transactionId;
84
+ purchasedAt = result.purchasedAt;
77
85
  }
78
86
  }
79
87
  else {
@@ -86,10 +94,10 @@ export function createPurchaseRouter(config, purchaseStore) {
86
94
  res.status(500).json(response);
87
95
  return;
88
96
  }
89
- const sub = await validateGoogleReceipt(receipt, productId, config.google);
90
- if (sub) {
91
- transactionId = sub.originalTransactionId;
92
- purchasedAt = sub.purchasedAt;
97
+ const result = await validateGoogleProductReceipt(receipt, productId, config.google, type === PURCHASE_TYPE.CONSUMABLE ? 'consumable' : 'non_consumable');
98
+ if (result) {
99
+ transactionId = result.transactionId;
100
+ purchasedAt = result.purchasedAt;
93
101
  }
94
102
  }
95
103
  if (!transactionId) {
@@ -101,7 +109,7 @@ export function createPurchaseRouter(config, purchaseStore) {
101
109
  res.status(422).json(response);
102
110
  return;
103
111
  }
104
- // Check for duplicate transaction (idempotency)
112
+ // Idempotency check same transaction submitted twice returns the existing record
105
113
  const existing = await purchaseStore.getPurchaseByTransactionId(transactionId);
106
114
  if (existing) {
107
115
  const response = {
@@ -1 +1 @@
1
- {"version":3,"file":"purchase.js","sourceRoot":"","sources":["../../src/routes/purchase.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"purchase.js","sourceRoot":"","sources":["../../src/routes/purchase.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAEjC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAOxB,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAEvD,OAAO,EAAE,8BAA8B,EAAE,MAAM,uBAAuB,CAAC;AACvE,OAAO,EAAE,4BAA4B,EAAE,MAAM,wBAAwB,CAAC;AAEtE,MAAM,sBAAsB,GAAG,CAAC,CAAC,MAAM,CAAC;IACtC,QAAQ,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IACrC,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC;IACrC,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC;IAClC,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC;IACrC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,aAAa,CAAC,UAAU,EAAE,aAAa,CAAC,cAAc,CAAC,CAAC;CACvE,CAAC,CAAC;AAEH,MAAM,yBAAyB,GAAG,CAAC,CAAC,MAAM,CAAC;IACzC,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC;IAClC,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE;CACjD,CAAC,CAAC;AAEH,MAAM,UAAU,oBAAoB,CAClC,MAA0B,EAC1B,aAA4B;IAE5B,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC;IAExB;;;;;;;;;;;;;OAaG;IACH,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,iBAAiB,EAAE,KAAK,EAAE,GAAY,EAAE,GAAa,EAAE,EAAE;QAC1E,IAAI,IAA4C,CAAC;QAEjD,IAAI,CAAC;YACH,IAAI,GAAG,sBAAsB,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAChD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,GAAG,YAAY,CAAC,CAAC,QAAQ,EAAE,CAAC;gBAC9B,MAAM,QAAQ,GAA6B;oBACzC,KAAK,EAAE,KAAK;oBACZ,QAAQ,EAAE,IAAI;oBACd,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAsB,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;iBACxE,CAAC;gBACF,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;gBAC/B,OAAO;YACT,CAAC;YACD,MAAM,GAAG,CAAC;QACZ,CAAC;QAED,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC;QAE5D,IAAI,CAAC;YACH,0EAA0E;YAC1E,IAAI,IAAI,KAAK,aAAa,CAAC,cAAc,EAAE,CAAC;gBAC1C,MAAM,YAAY,GAAG,MAAM,aAAa,CAAC,YAAY,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;gBACzE,IAAI,YAAY,EAAE,CAAC;oBACjB,MAAM,QAAQ,GAA6B;wBACzC,KAAK,EAAE,KAAK;wBACZ,QAAQ,EAAE,IAAI;wBACd,KAAK,EAAE,8BAA8B;qBACtC,CAAC;oBACF,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;oBAC/B,OAAO;gBACT,CAAC;YACH,CAAC;YAED,4EAA4E;YAC5E,wEAAwE;YACxE,8EAA8E;YAC9E,oEAAoE;YACpE,IAAI,aAAa,GAAkB,IAAI,CAAC;YACxC,IAAI,WAAW,GAAW,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;YAEnD,IAAI,QAAQ,KAAK,OAAO,EAAE,CAAC;gBACzB,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;oBAClB,MAAM,QAAQ,GAA6B;wBACzC,KAAK,EAAE,KAAK;wBACZ,QAAQ,EAAE,IAAI;wBACd,KAAK,EAAE,kCAAkC;qBAC1C,CAAC;oBACF,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;oBAC/B,OAAO;gBACT,CAAC;gBACD,MAAM,MAAM,GAAG,MAAM,8BAA8B,CAAC,OAAO,EAAE,MAAM,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;gBACtF,IAAI,MAAM,EAAE,CAAC;oBACX,aAAa,GAAG,MAAM,CAAC,aAAa,CAAC;oBACrC,WAAW,GAAG,MAAM,CAAC,WAAW,CAAC;gBACnC,CAAC;YACH,CAAC;iBAAM,CAAC;gBACN,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;oBACnB,MAAM,QAAQ,GAA6B;wBACzC,KAAK,EAAE,KAAK;wBACZ,QAAQ,EAAE,IAAI;wBACd,KAAK,EAAE,mCAAmC;qBAC3C,CAAC;oBACF,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;oBAC/B,OAAO;gBACT,CAAC;gBACD,MAAM,MAAM,GAAG,MAAM,4BAA4B,CAC/C,OAAO,EACP,SAAS,EACT,MAAM,CAAC,MAAM,EACb,IAAI,KAAK,aAAa,CAAC,UAAU,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,gBAAgB,CACpE,CAAC;gBACF,IAAI,MAAM,EAAE,CAAC;oBACX,aAAa,GAAG,MAAM,CAAC,aAAa,CAAC;oBACrC,WAAW,GAAG,MAAM,CAAC,WAAW,CAAC;gBACnC,CAAC;YACH,CAAC;YAED,IAAI,CAAC,aAAa,EAAE,CAAC;gBACnB,MAAM,QAAQ,GAA6B;oBACzC,KAAK,EAAE,KAAK;oBACZ,QAAQ,EAAE,IAAI;oBACd,KAAK,EAAE,2BAA2B;iBACnC,CAAC;gBACF,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;gBAC/B,OAAO;YACT,CAAC;YAED,mFAAmF;YACnF,MAAM,QAAQ,GAAG,MAAM,aAAa,CAAC,0BAA0B,CAAC,aAAa,CAAC,CAAC;YAC/E,IAAI,QAAQ,EAAE,CAAC;gBACb,MAAM,QAAQ,GAA6B;oBACzC,KAAK,EAAE,IAAI;oBACX,QAAQ,EAAE,QAAQ;iBACnB,CAAC;gBACF,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;gBAC/B,OAAO;YACT,CAAC;YAED,MAAM,QAAQ,GAAiB;gBAC7B,MAAM;gBACN,SAAS;gBACT,QAAQ;gBACR,IAAI;gBACJ,aAAa;gBACb,WAAW;gBACX,QAAQ,EAAE,CAAC;aACZ,CAAC;YAEF,MAAM,aAAa,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC;YAE3C,MAAM,QAAQ,GAA6B;gBACzC,KAAK,EAAE,IAAI;gBACX,QAAQ;aACT,CAAC;YACF,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACjC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CAAC,8CAA8C,EAAE,GAAG,CAAC,CAAC;YACnE,MAAM,QAAQ,GAA6B;gBACzC,KAAK,EAAE,KAAK;gBACZ,QAAQ,EAAE,IAAI;gBACd,KAAK,EAAE,kDAAkD;aAC1D,CAAC;YACF,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACjC,CAAC;IACH,CAAC,CAAC,CAAC;IAEH;;;;OAIG;IACH,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,eAAe,EAAE,KAAK,EAAE,GAAY,EAAE,GAAa,EAAE,EAAE;QACvE,IAAI,KAAgD,CAAC;QAErD,IAAI,CAAC;YACH,KAAK,GAAG,yBAAyB,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACrD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,GAAG,YAAY,CAAC,CAAC,QAAQ,EAAE,CAAC;gBAC9B,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;oBACnB,SAAS,EAAE,EAAE;oBACb,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAsB,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;iBACxE,CAAC,CAAC;gBACH,OAAO;YACT,CAAC;YACD,MAAM,GAAG,CAAC;QACZ,CAAC;QAED,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,KAAK,CAAC;QAEpC,IAAI,CAAC;YACH,IAAI,SAAS,GAAG,MAAM,aAAa,CAAC,oBAAoB,CAAC,MAAM,CAAC,CAAC;YAEjE,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;gBAC5B,SAAS,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC;YACjE,CAAC;YAED,MAAM,QAAQ,GAA2B,EAAE,SAAS,EAAE,CAAC;YACvD,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACjC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CAAC,uCAAuC,EAAE,GAAG,CAAC,CAAC;YAC5D,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,EAAE,EAAE,KAAK,EAAE,uBAAuB,EAAE,CAAC,CAAC;QAC1E,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC;AAChB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onesub/server",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Server-side receipt validation middleware for react-native-iap. Apple StoreKit 2 + Google Play Billing. One line.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -40,7 +40,7 @@
40
40
  },
41
41
  "license": "MIT",
42
42
  "dependencies": {
43
- "@onesub/shared": "0.1.0",
43
+ "@onesub/shared": "0.2.0",
44
44
  "express": "^4.19.2",
45
45
  "jose": "^5.9.6",
46
46
  "zod": "^3.23.0"