@onesub/server 0.12.0 → 0.12.1

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.
Files changed (120) hide show
  1. package/dist/__tests__/webhook-apple.test.d.ts +12 -0
  2. package/dist/__tests__/webhook-apple.test.d.ts.map +1 -0
  3. package/dist/__tests__/webhook-google.test.d.ts +13 -0
  4. package/dist/__tests__/webhook-google.test.d.ts.map +1 -0
  5. package/dist/index.cjs +258 -288
  6. package/dist/index.cjs.map +1 -1
  7. package/dist/index.d.ts +0 -1
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +259 -286
  10. package/dist/index.js.map +1 -1
  11. package/dist/providers/google.d.ts.map +1 -1
  12. package/dist/routes/webhook-apple.d.ts +6 -0
  13. package/dist/routes/webhook-apple.d.ts.map +1 -0
  14. package/dist/routes/webhook-google.d.ts +7 -0
  15. package/dist/routes/webhook-google.d.ts.map +1 -0
  16. package/dist/routes/webhook.d.ts +4 -17
  17. package/dist/routes/webhook.d.ts.map +1 -1
  18. package/package.json +2 -2
  19. package/dist/__tests__/admin-customer-detail.test.js +0 -146
  20. package/dist/__tests__/admin-customer-detail.test.js.map +0 -1
  21. package/dist/__tests__/admin-list-subscriptions.test.js +0 -192
  22. package/dist/__tests__/admin-list-subscriptions.test.js.map +0 -1
  23. package/dist/__tests__/admin-subscription-detail.test.js +0 -112
  24. package/dist/__tests__/admin-subscription-detail.test.js.map +0 -1
  25. package/dist/__tests__/apple-root-ca.test.js +0 -41
  26. package/dist/__tests__/apple-root-ca.test.js.map +0 -1
  27. package/dist/__tests__/apple-status-api.test.js +0 -345
  28. package/dist/__tests__/apple-status-api.test.js.map +0 -1
  29. package/dist/__tests__/cache.test.js +0 -34
  30. package/dist/__tests__/cache.test.js.map +0 -1
  31. package/dist/__tests__/entitlements.test.js +0 -268
  32. package/dist/__tests__/entitlements.test.js.map +0 -1
  33. package/dist/__tests__/error-codes.test.js +0 -168
  34. package/dist/__tests__/error-codes.test.js.map +0 -1
  35. package/dist/__tests__/fetch-hardening.test.js +0 -192
  36. package/dist/__tests__/fetch-hardening.test.js.map +0 -1
  37. package/dist/__tests__/google-paused.test.js +0 -126
  38. package/dist/__tests__/google-paused.test.js.map +0 -1
  39. package/dist/__tests__/google-price-change.test.js +0 -186
  40. package/dist/__tests__/google-price-change.test.js.map +0 -1
  41. package/dist/__tests__/google-subscriptions-v2.test.js +0 -206
  42. package/dist/__tests__/google-subscriptions-v2.test.js.map +0 -1
  43. package/dist/__tests__/lifecycle-scenarios.test.js +0 -579
  44. package/dist/__tests__/lifecycle-scenarios.test.js.map +0 -1
  45. package/dist/__tests__/lifecycle-states.test.js +0 -423
  46. package/dist/__tests__/lifecycle-states.test.js.map +0 -1
  47. package/dist/__tests__/linked-purchase-token.test.js +0 -227
  48. package/dist/__tests__/linked-purchase-token.test.js.map +0 -1
  49. package/dist/__tests__/metrics.test.js +0 -340
  50. package/dist/__tests__/metrics.test.js.map +0 -1
  51. package/dist/__tests__/mock-provider.test.js +0 -188
  52. package/dist/__tests__/mock-provider.test.js.map +0 -1
  53. package/dist/__tests__/openapi.test.js +0 -32
  54. package/dist/__tests__/openapi.test.js.map +0 -1
  55. package/dist/__tests__/paused-auto-resume.test.js +0 -162
  56. package/dist/__tests__/paused-auto-resume.test.js.map +0 -1
  57. package/dist/__tests__/providers.test.js +0 -321
  58. package/dist/__tests__/providers.test.js.map +0 -1
  59. package/dist/__tests__/redis-store.test.js +0 -117
  60. package/dist/__tests__/redis-store.test.js.map +0 -1
  61. package/dist/__tests__/refund-policy.test.js +0 -317
  62. package/dist/__tests__/refund-policy.test.js.map +0 -1
  63. package/dist/__tests__/schema.test.js +0 -46
  64. package/dist/__tests__/schema.test.js.map +0 -1
  65. package/dist/__tests__/store.test.js +0 -172
  66. package/dist/__tests__/store.test.js.map +0 -1
  67. package/dist/__tests__/test-utils.js +0 -30
  68. package/dist/__tests__/test-utils.js.map +0 -1
  69. package/dist/__tests__/webhook-events.test.js +0 -36
  70. package/dist/__tests__/webhook-events.test.js.map +0 -1
  71. package/dist/__tests__/webhook-queue.test.js +0 -26
  72. package/dist/__tests__/webhook-queue.test.js.map +0 -1
  73. package/dist/__tests__/webhook-refund.test.js +0 -313
  74. package/dist/__tests__/webhook-refund.test.js.map +0 -1
  75. package/dist/cache.js +0 -43
  76. package/dist/cache.js.map +0 -1
  77. package/dist/errors.js +0 -19
  78. package/dist/errors.js.map +0 -1
  79. package/dist/http.js +0 -36
  80. package/dist/http.js.map +0 -1
  81. package/dist/logger.js +0 -20
  82. package/dist/logger.js.map +0 -1
  83. package/dist/openapi.js +0 -196
  84. package/dist/openapi.js.map +0 -1
  85. package/dist/providers/apple-root-ca.js +0 -38
  86. package/dist/providers/apple-root-ca.js.map +0 -1
  87. package/dist/providers/apple.js +0 -510
  88. package/dist/providers/apple.js.map +0 -1
  89. package/dist/providers/google.js +0 -522
  90. package/dist/providers/google.js.map +0 -1
  91. package/dist/providers/mock.js +0 -93
  92. package/dist/providers/mock.js.map +0 -1
  93. package/dist/routes/admin.js +0 -287
  94. package/dist/routes/admin.js.map +0 -1
  95. package/dist/routes/entitlements.js +0 -130
  96. package/dist/routes/entitlements.js.map +0 -1
  97. package/dist/routes/metrics.js +0 -281
  98. package/dist/routes/metrics.js.map +0 -1
  99. package/dist/routes/purchase.js +0 -187
  100. package/dist/routes/purchase.js.map +0 -1
  101. package/dist/routes/status.js +0 -55
  102. package/dist/routes/status.js.map +0 -1
  103. package/dist/routes/validate.js +0 -70
  104. package/dist/routes/validate.js.map +0 -1
  105. package/dist/routes/webhook.js +0 -461
  106. package/dist/routes/webhook.js.map +0 -1
  107. package/dist/store.js +0 -145
  108. package/dist/store.js.map +0 -1
  109. package/dist/stores/postgres.js +0 -329
  110. package/dist/stores/postgres.js.map +0 -1
  111. package/dist/stores/redis.js +0 -255
  112. package/dist/stores/redis.js.map +0 -1
  113. package/dist/stores/schema.js +0 -67
  114. package/dist/stores/schema.js.map +0 -1
  115. package/dist/tracing.js +0 -74
  116. package/dist/tracing.js.map +0 -1
  117. package/dist/webhook-events.js +0 -57
  118. package/dist/webhook-events.js.map +0 -1
  119. package/dist/webhook-queue.js +0 -108
  120. package/dist/webhook-queue.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"google.d.ts","sourceRoot":"","sources":["../../src/providers/google.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"google.d.ts","sourceRoot":"","sources":["../../src/providers/google.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import type { Request, Response } from 'express';
2
+ import type { OneSubServerConfig } from '@onesub/shared';
3
+ import type { SubscriptionStore, PurchaseStore } from '../store.js';
4
+ import type { WebhookEventStore } from '../webhook-events.js';
5
+ export declare function handleAppleWebhook(req: Request, res: Response, config: OneSubServerConfig, store: SubscriptionStore, purchaseStore: PurchaseStore, webhookEventStore?: WebhookEventStore): Promise<void>;
6
+ //# sourceMappingURL=webhook-apple.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webhook-apple.d.ts","sourceRoot":"","sources":["../../src/routes/webhook-apple.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACjD,OAAO,KAAK,EAEV,kBAAkB,EAEnB,MAAM,gBAAgB,CAAC;AAExB,OAAO,KAAK,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AASpE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAsB9D,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,QAAQ,EACb,MAAM,EAAE,kBAAkB,EAC1B,KAAK,EAAE,iBAAiB,EACxB,aAAa,EAAE,aAAa,EAC5B,iBAAiB,CAAC,EAAE,iBAAiB,GACpC,OAAO,CAAC,IAAI,CAAC,CAsIf"}
@@ -0,0 +1,7 @@
1
+ import type { Request, Response } from 'express';
2
+ import type { OneSubServerConfig } from '@onesub/shared';
3
+ import type { SubscriptionStore, PurchaseStore } from '../store.js';
4
+ import type { WebhookEventStore } from '../webhook-events.js';
5
+ export declare function verifyGooglePushToken(req: Request, expectedAudience: string): Promise<boolean>;
6
+ export declare function handleGoogleWebhook(req: Request, res: Response, config: OneSubServerConfig, store: SubscriptionStore, purchaseStore: PurchaseStore, webhookEventStore?: WebhookEventStore): Promise<void>;
7
+ //# sourceMappingURL=webhook-google.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webhook-google.d.ts","sourceRoot":"","sources":["../../src/routes/webhook-google.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACjD,OAAO,KAAK,EAA6B,kBAAkB,EAAoB,MAAM,gBAAgB,CAAC;AAEtG,OAAO,KAAK,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAepE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAW9D,wBAAsB,qBAAqB,CAAC,GAAG,EAAE,OAAO,EAAE,gBAAgB,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAWpG;AAED,wBAAsB,mBAAmB,CACvC,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,QAAQ,EACb,MAAM,EAAE,kBAAkB,EAC1B,KAAK,EAAE,iBAAiB,EACxB,aAAa,EAAE,aAAa,EAC5B,iBAAiB,CAAC,EAAE,iBAAiB,GACpC,OAAO,CAAC,IAAI,CAAC,CAmJf"}
@@ -5,23 +5,10 @@ import type { WebhookEventStore } from '../webhook-events.js';
5
5
  /**
6
6
  * Retry / durability semantics.
7
7
  *
8
- * Both Apple (App Store Server Notifications V2) and Google (Pub/Sub RTDN)
9
- * retry on any non-2xx response:
10
- * - Apple: up to 5 retries over ~3 days, exponential backoff.
11
- * - Google: per-Pub/Sub-subscription retry policy (default: retries until
12
- * the message is ack'd or the retention window expires, up to 7 days).
13
- *
14
- * This router uses that built-in retry instead of a local dead-letter queue:
15
- *
16
- * 4xx — the payload is unusable (missing signedPayload, bad signature,
17
- * package mismatch). Return 4xx so the sender does NOT retry, since
18
- * the same request would fail again.
19
- * 5xx — a transient failure on our side (DB down, network to Play API).
20
- * Return 5xx so Apple / Google retry the notification for us.
21
- * 2xx — processed, or intentionally ignored (e.g. test notification).
22
- *
23
- * If you need an explicit DLQ, wrap `store.save()` with your own error
24
- * handler before passing the store to `createOneSubMiddleware()`.
8
+ * Both Apple and Google retry on any non-2xx response.
9
+ * 4xx payload is unusable; do NOT retry.
10
+ * 5xx transient failure; source will retry.
11
+ * 2xx processed or intentionally ignored.
25
12
  */
26
13
  export declare function createWebhookRouter(config: OneSubServerConfig, store: SubscriptionStore, purchaseStore: PurchaseStore, webhookEventStore?: WebhookEventStore): Router;
27
14
  //# sourceMappingURL=webhook.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"webhook.d.ts","sourceRoot":"","sources":["../../src/routes/webhook.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAGjC,OAAO,KAAK,EAGV,kBAAkB,EAEnB,MAAM,gBAAgB,CAAC;AAExB,OAAO,KAAK,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAqBpE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAiG9D;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,kBAAkB,EAC1B,KAAK,EAAE,iBAAiB,EACxB,aAAa,EAAE,aAAa,EAC5B,iBAAiB,CAAC,EAAE,iBAAiB,GACpC,MAAM,CAmZR"}
1
+ {"version":3,"file":"webhook.d.ts","sourceRoot":"","sources":["../../src/routes/webhook.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAEjC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAEzD,OAAO,KAAK,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACpE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAI9D;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,kBAAkB,EAC1B,KAAK,EAAE,iBAAiB,EACxB,aAAa,EAAE,aAAa,EAC5B,iBAAiB,CAAC,EAAE,iBAAiB,GACpC,MAAM,CAYR"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onesub/server",
3
- "version": "0.12.0",
3
+ "version": "0.12.1",
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.cjs",
6
6
  "module": "dist/index.js",
@@ -53,7 +53,7 @@
53
53
  },
54
54
  "license": "MIT",
55
55
  "dependencies": {
56
- "@onesub/shared": "0.7.5",
56
+ "@onesub/shared": "0.7.6",
57
57
  "jose": "^6.2.2",
58
58
  "zod": "^4.3.6"
59
59
  },
@@ -1,146 +0,0 @@
1
- /**
2
- * GET /onesub/admin/customers/:userId — per-user profile bundle.
3
- *
4
- * Backs the dashboard's customer detail page. Returns subscriptions +
5
- * purchases + entitlements (when configured) for one userId in a single
6
- * round-trip. Always 200 — unknown userIds yield empty arrays rather than
7
- * 404, since "no records yet" is a normal CS scenario.
8
- */
9
- import { describe, it, expect } from 'vitest';
10
- import express from 'express';
11
- import { createOneSubMiddleware, InMemorySubscriptionStore, InMemoryPurchaseStore } from '../index.js';
12
- function sub(overrides) {
13
- return {
14
- userId: 'u',
15
- productId: 'pro_monthly',
16
- platform: 'apple',
17
- status: 'active',
18
- expiresAt: '2099-01-01T00:00:00.000Z',
19
- purchasedAt: '2026-04-01T00:00:00.000Z',
20
- originalTransactionId: `orig_${Math.random()}`,
21
- willRenew: true,
22
- ...overrides,
23
- };
24
- }
25
- function purchase(overrides) {
26
- return {
27
- userId: 'u',
28
- productId: 'lifetime_pass',
29
- platform: 'apple',
30
- type: 'non_consumable',
31
- transactionId: `tx_${Math.random()}`,
32
- purchasedAt: '2026-04-01T00:00:00.000Z',
33
- quantity: 1,
34
- ...overrides,
35
- };
36
- }
37
- function spinUp(handler) {
38
- return {
39
- async get(path, headers) {
40
- const httpServer = handler.listen(0);
41
- const port = httpServer.address().port;
42
- try {
43
- const resp = await fetch(`http://127.0.0.1:${port}${path}`, { headers });
44
- const text = await resp.text();
45
- let parsed = text;
46
- try {
47
- parsed = JSON.parse(text);
48
- }
49
- catch { /* keep as text */ }
50
- return { status: resp.status, body: parsed };
51
- }
52
- finally {
53
- await new Promise((r) => httpServer.close(() => r()));
54
- }
55
- },
56
- };
57
- }
58
- function buildServer(opts) {
59
- const store = new InMemorySubscriptionStore();
60
- const purchaseStore = new InMemoryPurchaseStore();
61
- const config = {
62
- apple: { bundleId: 'com.example.app', skipJwsVerification: true },
63
- database: { url: '' },
64
- adminSecret: opts.adminSecret,
65
- ...(opts.entitlements ? { entitlements: opts.entitlements } : {}),
66
- };
67
- const app = express();
68
- app.use(createOneSubMiddleware({ ...config, store, purchaseStore }));
69
- return { store, purchaseStore, server: spinUp(app) };
70
- }
71
- const SECRET = 's3cr3t';
72
- const AUTH = { 'x-admin-secret': SECRET };
73
- describe('GET /onesub/admin/customers/:userId auth', () => {
74
- it('returns 404 when adminSecret is unset (router not mounted)', async () => {
75
- const { server } = buildServer({});
76
- const resp = await server.get('/onesub/admin/customers/u_x');
77
- expect(resp.status).toBe(404);
78
- });
79
- it('returns 401 INVALID_ADMIN_SECRET when header is missing or wrong', async () => {
80
- const { server } = buildServer({ adminSecret: SECRET });
81
- const noHeader = await server.get('/onesub/admin/customers/u_x');
82
- expect(noHeader.status).toBe(401);
83
- expect(noHeader.body.errorCode).toBe('INVALID_ADMIN_SECRET');
84
- });
85
- });
86
- describe('GET /onesub/admin/customers/:userId basics', () => {
87
- it('returns empty profile (200) for unknown userId', async () => {
88
- const { server } = buildServer({ adminSecret: SECRET });
89
- const resp = await server.get('/onesub/admin/customers/u_unknown', AUTH);
90
- expect(resp.status).toBe(200);
91
- expect(resp.body.userId).toBe('u_unknown');
92
- expect(resp.body.subscriptions).toEqual([]);
93
- expect(resp.body.purchases).toEqual([]);
94
- expect(resp.body.entitlements).toBeUndefined();
95
- });
96
- it('combines subscriptions and purchases for the user', async () => {
97
- const { store, purchaseStore, server } = buildServer({ adminSecret: SECRET });
98
- await store.save(sub({ userId: 'alice', productId: 'pro_monthly', originalTransactionId: 't1' }));
99
- await store.save(sub({ userId: 'alice', productId: 'pro_yearly', originalTransactionId: 't2' }));
100
- // unrelated user — must NOT leak
101
- await store.save(sub({ userId: 'bob', originalTransactionId: 't_bob' }));
102
- await purchaseStore.savePurchase(purchase({ userId: 'alice', productId: 'lifetime', transactionId: 'p1' }));
103
- const resp = await server.get('/onesub/admin/customers/alice', AUTH);
104
- expect(resp.status).toBe(200);
105
- expect(resp.body.subscriptions).toHaveLength(2);
106
- expect(resp.body.subscriptions.every((s) => s.userId === 'alice')).toBe(true);
107
- expect(resp.body.purchases).toHaveLength(1);
108
- expect(resp.body.purchases[0]?.userId).toBe('alice');
109
- });
110
- });
111
- describe('GET /onesub/admin/customers/:userId entitlements', () => {
112
- it('omits entitlements when config.entitlements is unset', async () => {
113
- const { store, server } = buildServer({ adminSecret: SECRET });
114
- await store.save(sub({ userId: 'alice', originalTransactionId: 't1' }));
115
- const resp = await server.get('/onesub/admin/customers/alice', AUTH);
116
- expect(resp.body.entitlements).toBeUndefined();
117
- });
118
- it('evaluates each configured entitlement when entitlements are set', async () => {
119
- const { store, purchaseStore, server } = buildServer({
120
- adminSecret: SECRET,
121
- entitlements: {
122
- premium: { productIds: ['pro_monthly', 'pro_yearly'] },
123
- lifetime: { productIds: ['lifetime_pass'] },
124
- },
125
- });
126
- // Active sub matches "premium"
127
- await store.save(sub({ userId: 'alice', productId: 'pro_monthly', status: 'active', originalTransactionId: 't1' }));
128
- // Non-consumable matches "lifetime"
129
- await purchaseStore.savePurchase(purchase({ userId: 'alice', productId: 'lifetime_pass', transactionId: 'p1' }));
130
- const resp = await server.get('/onesub/admin/customers/alice', AUTH);
131
- expect(resp.body.entitlements?.premium?.active).toBe(true);
132
- expect(resp.body.entitlements?.premium?.source).toBe('subscription');
133
- expect(resp.body.entitlements?.lifetime?.active).toBe(true);
134
- expect(resp.body.entitlements?.lifetime?.source).toBe('purchase');
135
- });
136
- it('reports inactive entitlements when nothing matches', async () => {
137
- const { server } = buildServer({
138
- adminSecret: SECRET,
139
- entitlements: { premium: { productIds: ['pro_monthly'] } },
140
- });
141
- const resp = await server.get('/onesub/admin/customers/u_empty', AUTH);
142
- expect(resp.body.entitlements?.premium?.active).toBe(false);
143
- expect(resp.body.entitlements?.premium?.source).toBeNull();
144
- });
145
- });
146
- //# sourceMappingURL=admin-customer-detail.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"admin-customer-detail.test.js","sourceRoot":"","sources":["../../src/__tests__/admin-customer-detail.test.ts"],"names":[],"mappings":"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"}
@@ -1,192 +0,0 @@
1
- /**
2
- * GET /onesub/admin/subscriptions — filtered/paginated list endpoint.
3
- *
4
- * Backs the dashboard's subscriptions page; gated behind adminSecret like
5
- * the other /onesub/admin/* and /onesub/metrics/* endpoints.
6
- */
7
- import { describe, it, expect, beforeEach } from 'vitest';
8
- import express from 'express';
9
- import { createOneSubMiddleware, InMemorySubscriptionStore, InMemoryPurchaseStore } from '../index.js';
10
- // ── helpers ─────────────────────────────────────────────────────────────────
11
- function sub(overrides) {
12
- return {
13
- userId: 'u',
14
- productId: 'pro_monthly',
15
- platform: 'apple',
16
- status: 'active',
17
- expiresAt: '2099-01-01T00:00:00.000Z',
18
- purchasedAt: '2026-04-01T00:00:00.000Z',
19
- originalTransactionId: `orig_${Math.random()}`,
20
- willRenew: true,
21
- ...overrides,
22
- };
23
- }
24
- function spinUp(handler) {
25
- return {
26
- async get(path, headers) {
27
- const httpServer = handler.listen(0);
28
- const port = httpServer.address().port;
29
- try {
30
- const resp = await fetch(`http://127.0.0.1:${port}${path}`, { headers });
31
- const text = await resp.text();
32
- let parsed = text;
33
- try {
34
- parsed = JSON.parse(text);
35
- }
36
- catch { /* keep as text */ }
37
- return { status: resp.status, body: parsed };
38
- }
39
- finally {
40
- await new Promise((r) => httpServer.close(() => r()));
41
- }
42
- },
43
- };
44
- }
45
- function buildServer(opts) {
46
- const store = new InMemorySubscriptionStore();
47
- const purchaseStore = new InMemoryPurchaseStore();
48
- const config = {
49
- apple: { bundleId: 'com.example.app', skipJwsVerification: true },
50
- database: { url: '' },
51
- adminSecret: opts.adminSecret,
52
- };
53
- const app = express();
54
- app.use(createOneSubMiddleware({ ...config, store, purchaseStore }));
55
- return { store, server: spinUp(app) };
56
- }
57
- const SECRET = 's3cr3t';
58
- const AUTH = { 'x-admin-secret': SECRET };
59
- // ── auth ────────────────────────────────────────────────────────────────────
60
- describe('GET /onesub/admin/subscriptions auth', () => {
61
- it('returns 404 when adminSecret is unset (router not mounted)', async () => {
62
- const { server } = buildServer({});
63
- const resp = await server.get('/onesub/admin/subscriptions');
64
- expect(resp.status).toBe(404);
65
- });
66
- it('returns 401 INVALID_ADMIN_SECRET when header is missing or wrong', async () => {
67
- const { server } = buildServer({ adminSecret: SECRET });
68
- const noHeader = await server.get('/onesub/admin/subscriptions');
69
- expect(noHeader.status).toBe(401);
70
- expect(noHeader.body.errorCode).toBe('INVALID_ADMIN_SECRET');
71
- const wrongHeader = await server.get('/onesub/admin/subscriptions', { 'x-admin-secret': 'wrong' });
72
- expect(wrongHeader.status).toBe(401);
73
- });
74
- });
75
- // ── basic listing ───────────────────────────────────────────────────────────
76
- describe('GET /onesub/admin/subscriptions basics', () => {
77
- it('returns empty list with total=0 for an empty store', async () => {
78
- const { server } = buildServer({ adminSecret: SECRET });
79
- const resp = await server.get('/onesub/admin/subscriptions', AUTH);
80
- expect(resp.status).toBe(200);
81
- expect(resp.body.items).toEqual([]);
82
- expect(resp.body.total).toBe(0);
83
- expect(resp.body.limit).toBe(50);
84
- expect(resp.body.offset).toBe(0);
85
- });
86
- it('returns all subs when no filter is passed', async () => {
87
- const { store, server } = buildServer({ adminSecret: SECRET });
88
- await store.save(sub({ userId: 'a', originalTransactionId: 't1' }));
89
- await store.save(sub({ userId: 'b', originalTransactionId: 't2' }));
90
- await store.save(sub({ userId: 'c', originalTransactionId: 't3' }));
91
- const resp = await server.get('/onesub/admin/subscriptions', AUTH);
92
- expect(resp.body.total).toBe(3);
93
- expect(resp.body.items).toHaveLength(3);
94
- });
95
- });
96
- // ── filters ─────────────────────────────────────────────────────────────────
97
- describe('GET /onesub/admin/subscriptions filters', () => {
98
- it('filters by userId', async () => {
99
- const { store, server } = buildServer({ adminSecret: SECRET });
100
- await store.save(sub({ userId: 'a', originalTransactionId: 't1' }));
101
- await store.save(sub({ userId: 'b', originalTransactionId: 't2' }));
102
- await store.save(sub({ userId: 'a', originalTransactionId: 't3' }));
103
- const resp = await server.get('/onesub/admin/subscriptions?userId=a', AUTH);
104
- expect(resp.body.total).toBe(2);
105
- expect(resp.body.items.every((s) => s.userId === 'a')).toBe(true);
106
- });
107
- it('filters by status', async () => {
108
- const { store, server } = buildServer({ adminSecret: SECRET });
109
- await store.save(sub({ userId: 'a', status: 'active', originalTransactionId: 't1' }));
110
- await store.save(sub({ userId: 'b', status: 'expired', originalTransactionId: 't2' }));
111
- await store.save(sub({ userId: 'c', status: 'active', originalTransactionId: 't3' }));
112
- await store.save(sub({ userId: 'd', status: 'paused', originalTransactionId: 't4' }));
113
- const active = await server.get('/onesub/admin/subscriptions?status=active', AUTH);
114
- expect(active.body.total).toBe(2);
115
- const paused = await server.get('/onesub/admin/subscriptions?status=paused', AUTH);
116
- expect(paused.body.total).toBe(1);
117
- });
118
- it('filters by productId + platform combined (AND)', async () => {
119
- const { store, server } = buildServer({ adminSecret: SECRET });
120
- await store.save(sub({ userId: 'a', productId: 'pro_monthly', platform: 'apple', originalTransactionId: 't1' }));
121
- await store.save(sub({ userId: 'b', productId: 'pro_yearly', platform: 'apple', originalTransactionId: 't2' }));
122
- await store.save(sub({ userId: 'c', productId: 'pro_monthly', platform: 'google', originalTransactionId: 't3' }));
123
- const resp = await server.get('/onesub/admin/subscriptions?productId=pro_monthly&platform=apple', AUTH);
124
- expect(resp.body.total).toBe(1);
125
- expect(resp.body.items[0].userId).toBe('a');
126
- });
127
- it('rejects unknown status with 400', async () => {
128
- const { server } = buildServer({ adminSecret: SECRET });
129
- const resp = await server.get('/onesub/admin/subscriptions?status=zombie', AUTH);
130
- expect(resp.status).toBe(400);
131
- });
132
- it('rejects unknown platform with 400', async () => {
133
- const { server } = buildServer({ adminSecret: SECRET });
134
- const resp = await server.get('/onesub/admin/subscriptions?platform=amazon', AUTH);
135
- expect(resp.status).toBe(400);
136
- });
137
- });
138
- // ── pagination ──────────────────────────────────────────────────────────────
139
- describe('GET /onesub/admin/subscriptions pagination', () => {
140
- it('respects limit and offset', async () => {
141
- const { store, server } = buildServer({ adminSecret: SECRET });
142
- for (let i = 0; i < 7; i++) {
143
- await store.save(sub({ userId: `u${i}`, originalTransactionId: `t${i}` }));
144
- }
145
- const page1 = await server.get('/onesub/admin/subscriptions?limit=3&offset=0', AUTH);
146
- expect(page1.body.items).toHaveLength(3);
147
- expect(page1.body.total).toBe(7);
148
- expect(page1.body.limit).toBe(3);
149
- const page2 = await server.get('/onesub/admin/subscriptions?limit=3&offset=3', AUTH);
150
- expect(page2.body.items).toHaveLength(3);
151
- expect(page2.body.offset).toBe(3);
152
- const page3 = await server.get('/onesub/admin/subscriptions?limit=3&offset=6', AUTH);
153
- expect(page3.body.items).toHaveLength(1);
154
- });
155
- it('caps limit at 200 (zod refuses larger values)', async () => {
156
- const { server } = buildServer({ adminSecret: SECRET });
157
- const resp = await server.get('/onesub/admin/subscriptions?limit=999', AUTH);
158
- expect(resp.status).toBe(400);
159
- });
160
- it('rejects negative offset', async () => {
161
- const { server } = buildServer({ adminSecret: SECRET });
162
- const resp = await server.get('/onesub/admin/subscriptions?offset=-1', AUTH);
163
- expect(resp.status).toBe(400);
164
- });
165
- });
166
- // ── store.listFiltered direct (bypass HTTP) ─────────────────────────────────
167
- describe('InMemorySubscriptionStore.listFiltered', () => {
168
- let store;
169
- beforeEach(() => {
170
- store = new InMemorySubscriptionStore();
171
- });
172
- it('combines all four filters with AND semantics', async () => {
173
- await store.save(sub({ userId: 'a', productId: 'pro', platform: 'apple', status: 'active', originalTransactionId: 't1' }));
174
- await store.save(sub({ userId: 'a', productId: 'pro', platform: 'google', status: 'active', originalTransactionId: 't2' }));
175
- await store.save(sub({ userId: 'a', productId: 'pro', platform: 'apple', status: 'expired', originalTransactionId: 't3' }));
176
- const result = await store.listFiltered({
177
- userId: 'a',
178
- productId: 'pro',
179
- platform: 'apple',
180
- status: 'active',
181
- });
182
- expect(result.total).toBe(1);
183
- expect(result.items[0].originalTransactionId).toBe('t1');
184
- });
185
- it('defaults to limit=50, offset=0', async () => {
186
- await store.save(sub());
187
- const result = await store.listFiltered({});
188
- expect(result.limit).toBe(50);
189
- expect(result.offset).toBe(0);
190
- });
191
- });
192
- //# sourceMappingURL=admin-list-subscriptions.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"admin-list-subscriptions.test.js","sourceRoot":"","sources":["../../src/__tests__/admin-list-subscriptions.test.ts"],"names":[],"mappings":"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"}
@@ -1,112 +0,0 @@
1
- /**
2
- * GET /onesub/admin/subscriptions/:transactionId — single-record detail.
3
- *
4
- * Backs the dashboard's subscription detail page. Same auth contract as the
5
- * other /onesub/admin/* endpoints (X-Admin-Secret header).
6
- */
7
- import { describe, it, expect } from 'vitest';
8
- import express from 'express';
9
- import { createOneSubMiddleware, InMemorySubscriptionStore, InMemoryPurchaseStore } from '../index.js';
10
- function sub(overrides) {
11
- return {
12
- userId: 'u',
13
- productId: 'pro_monthly',
14
- platform: 'apple',
15
- status: 'active',
16
- expiresAt: '2099-01-01T00:00:00.000Z',
17
- purchasedAt: '2026-04-01T00:00:00.000Z',
18
- originalTransactionId: 'orig_default',
19
- willRenew: true,
20
- ...overrides,
21
- };
22
- }
23
- function spinUp(handler) {
24
- return {
25
- async get(path, headers) {
26
- const httpServer = handler.listen(0);
27
- const port = httpServer.address().port;
28
- try {
29
- const resp = await fetch(`http://127.0.0.1:${port}${path}`, { headers });
30
- const text = await resp.text();
31
- let parsed = text;
32
- try {
33
- parsed = JSON.parse(text);
34
- }
35
- catch { /* keep as text */ }
36
- return { status: resp.status, body: parsed };
37
- }
38
- finally {
39
- await new Promise((r) => httpServer.close(() => r()));
40
- }
41
- },
42
- };
43
- }
44
- function buildServer(opts) {
45
- const store = new InMemorySubscriptionStore();
46
- const purchaseStore = new InMemoryPurchaseStore();
47
- const config = {
48
- apple: { bundleId: 'com.example.app', skipJwsVerification: true },
49
- database: { url: '' },
50
- adminSecret: opts.adminSecret,
51
- };
52
- const app = express();
53
- app.use(createOneSubMiddleware({ ...config, store, purchaseStore }));
54
- return { store, server: spinUp(app) };
55
- }
56
- const SECRET = 's3cr3t';
57
- const AUTH = { 'x-admin-secret': SECRET };
58
- describe('GET /onesub/admin/subscriptions/:transactionId auth', () => {
59
- it('returns 404 when adminSecret is unset (router not mounted)', async () => {
60
- const { server } = buildServer({});
61
- const resp = await server.get('/onesub/admin/subscriptions/orig_xyz');
62
- expect(resp.status).toBe(404);
63
- });
64
- it('returns 401 INVALID_ADMIN_SECRET when header is missing or wrong', async () => {
65
- const { server } = buildServer({ adminSecret: SECRET });
66
- const noHeader = await server.get('/onesub/admin/subscriptions/orig_xyz');
67
- expect(noHeader.status).toBe(401);
68
- expect(noHeader.body.errorCode).toBe('INVALID_ADMIN_SECRET');
69
- const wrongHeader = await server.get('/onesub/admin/subscriptions/orig_xyz', { 'x-admin-secret': 'wrong' });
70
- expect(wrongHeader.status).toBe(401);
71
- });
72
- });
73
- describe('GET /onesub/admin/subscriptions/:transactionId basics', () => {
74
- it('returns the matching subscription', async () => {
75
- const { store, server } = buildServer({ adminSecret: SECRET });
76
- const target = sub({ userId: 'alice', originalTransactionId: 'orig_42' });
77
- await store.save(target);
78
- await store.save(sub({ userId: 'bob', originalTransactionId: 'orig_other' }));
79
- const resp = await server.get('/onesub/admin/subscriptions/orig_42', AUTH);
80
- expect(resp.status).toBe(200);
81
- expect(resp.body.originalTransactionId).toBe('orig_42');
82
- expect(resp.body.userId).toBe('alice');
83
- });
84
- it('returns 404 TRANSACTION_NOT_FOUND when the id is unknown', async () => {
85
- const { server } = buildServer({ adminSecret: SECRET });
86
- const resp = await server.get('/onesub/admin/subscriptions/orig_does_not_exist', AUTH);
87
- expect(resp.status).toBe(404);
88
- expect(resp.body.errorCode).toBe('TRANSACTION_NOT_FOUND');
89
- });
90
- it('does not collide with the list endpoint (no :transactionId)', async () => {
91
- const { store, server } = buildServer({ adminSecret: SECRET });
92
- await store.save(sub({ userId: 'a', originalTransactionId: 'orig_1' }));
93
- const list = await server.get('/onesub/admin/subscriptions', AUTH);
94
- expect(list.status).toBe(200);
95
- expect(list.body.items).toHaveLength(1);
96
- });
97
- it('preserves Google-only fields (linkedPurchaseToken, autoResumeTime)', async () => {
98
- const { store, server } = buildServer({ adminSecret: SECRET });
99
- await store.save(sub({
100
- platform: 'google',
101
- status: 'paused',
102
- originalTransactionId: 'orig_google',
103
- linkedPurchaseToken: 'prev_token',
104
- autoResumeTime: '2026-05-01T00:00:00.000Z',
105
- }));
106
- const resp = await server.get('/onesub/admin/subscriptions/orig_google', AUTH);
107
- expect(resp.status).toBe(200);
108
- expect(resp.body.linkedPurchaseToken).toBe('prev_token');
109
- expect(resp.body.autoResumeTime).toBe('2026-05-01T00:00:00.000Z');
110
- });
111
- });
112
- //# sourceMappingURL=admin-subscription-detail.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"admin-subscription-detail.test.js","sourceRoot":"","sources":["../../src/__tests__/admin-subscription-detail.test.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,OAAO,MAAM,SAAS,CAAC;AAE9B,OAAO,EAAE,sBAAsB,EAAE,yBAAyB,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAEvG,SAAS,GAAG,CAAC,SAAqC;IAChD,OAAO;QACL,MAAM,EAAE,GAAG;QACX,SAAS,EAAE,aAAa;QACxB,QAAQ,EAAE,OAAO;QACjB,MAAM,EAAE,QAAQ;QAChB,SAAS,EAAE,0BAA0B;QACrC,WAAW,EAAE,0BAA0B;QACvC,qBAAqB,EAAE,cAAc;QACrC,SAAS,EAAE,IAAI;QACf,GAAG,SAAS;KACb,CAAC;AACJ,CAAC;AAMD,SAAS,MAAM,CAAC,OAAwB;IACtC,OAAO;QACL,KAAK,CAAC,GAAG,CAAI,IAAY,EAAE,OAAgC;YACzD,MAAM,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;YACrC,MAAM,IAAI,GAAI,UAAU,CAAC,OAAO,EAAuB,CAAC,IAAI,CAAC;YAC7D,IAAI,CAAC;gBACH,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,oBAAoB,IAAI,GAAG,IAAI,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;gBACzE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;gBAC/B,IAAI,MAAM,GAAY,IAAI,CAAC;gBAC3B,IAAI,CAAC;oBAAC,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;gBAAC,CAAC;gBAAC,MAAM,CAAC,CAAC,kBAAkB,CAAC,CAAC;gBAC/D,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,MAAW,EAAE,CAAC;YACpD,CAAC;oBAAS,CAAC;gBACT,MAAM,IAAI,OAAO,CAAO,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;YAC9D,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC;AAED,SAAS,WAAW,CAAC,IAA8B;IACjD,MAAM,KAAK,GAAG,IAAI,yBAAyB,EAAE,CAAC;IAC9C,MAAM,aAAa,GAAG,IAAI,qBAAqB,EAAE,CAAC;IAClD,MAAM,MAAM,GAAuB;QACjC,KAAK,EAAE,EAAE,QAAQ,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,IAAI,EAAE;QACjE,QAAQ,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE;QACrB,WAAW,EAAE,IAAI,CAAC,WAAW;KAC9B,CAAC;IACF,MAAM,GAAG,GAAG,OAAO,EAAE,CAAC;IACtB,GAAG,CAAC,GAAG,CAAC,sBAAsB,CAAC,EAAE,GAAG,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC,CAAC,CAAC;IACrE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;AACxC,CAAC;AAED,MAAM,MAAM,GAAG,QAAQ,CAAC;AACxB,MAAM,IAAI,GAAG,EAAE,gBAAgB,EAAE,MAAM,EAAE,CAAC;AAE1C,QAAQ,CAAC,qDAAqD,EAAE,GAAG,EAAE;IACnE,EAAE,CAAC,4DAA4D,EAAE,KAAK,IAAI,EAAE;QAC1E,MAAM,EAAE,MAAM,EAAE,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC;QACnC,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,GAAG,CAAC,sCAAsC,CAAC,CAAC;QACtE,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAChC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kEAAkE,EAAE,KAAK,IAAI,EAAE;QAChF,MAAM,EAAE,MAAM,EAAE,GAAG,WAAW,CAAC,EAAE,WAAW,EAAE,MAAM,EAAE,CAAC,CAAC;QAExD,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,GAAG,CAAwB,sCAAsC,CAAC,CAAC;QACjG,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAClC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;QAE7D,MAAM,WAAW,GAAG,MAAM,MAAM,CAAC,GAAG,CAClC,sCAAsC,EACtC,EAAE,gBAAgB,EAAE,OAAO,EAAE,CAC9B,CAAC;QACF,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACvC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,uDAAuD,EAAE,GAAG,EAAE;IACrE,EAAE,CAAC,mCAAmC,EAAE,KAAK,IAAI,EAAE;QACjD,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,WAAW,CAAC,EAAE,WAAW,EAAE,MAAM,EAAE,CAAC,CAAC;QAC/D,MAAM,MAAM,GAAG,GAAG,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,qBAAqB,EAAE,SAAS,EAAE,CAAC,CAAC;QAC1E,MAAM,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACzB,MAAM,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,qBAAqB,EAAE,YAAY,EAAE,CAAC,CAAC,CAAC;QAE9E,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,GAAG,CAC3B,qCAAqC,EACrC,IAAI,CACL,CAAC;QACF,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC9B,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACxD,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACzC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0DAA0D,EAAE,KAAK,IAAI,EAAE;QACxE,MAAM,EAAE,MAAM,EAAE,GAAG,WAAW,CAAC,EAAE,WAAW,EAAE,MAAM,EAAE,CAAC,CAAC;QACxD,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,GAAG,CAC3B,iDAAiD,EACjD,IAAI,CACL,CAAC;QACF,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC9B,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;IAC5D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6DAA6D,EAAE,KAAK,IAAI,EAAE;QAC3E,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,WAAW,CAAC,EAAE,WAAW,EAAE,MAAM,EAAE,CAAC,CAAC;QAC/D,MAAM,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,qBAAqB,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC;QAExE,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,GAAG,CAC3B,6BAA6B,EAC7B,IAAI,CACL,CAAC;QACF,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC9B,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oEAAoE,EAAE,KAAK,IAAI,EAAE;QAClF,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,WAAW,CAAC,EAAE,WAAW,EAAE,MAAM,EAAE,CAAC,CAAC;QAC/D,MAAM,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC;YACnB,QAAQ,EAAE,QAAQ;YAClB,MAAM,EAAE,QAAQ;YAChB,qBAAqB,EAAE,aAAa;YACpC,mBAAmB,EAAE,YAAY;YACjC,cAAc,EAAE,0BAA0B;SAC3C,CAAC,CAAC,CAAC;QAEJ,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,GAAG,CAC3B,yCAAyC,EACzC,IAAI,CACL,CAAC;QACF,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC9B,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QACzD,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC;IACpE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -1,41 +0,0 @@
1
- import { X509Certificate } from 'node:crypto';
2
- import { describe, it, expect } from 'vitest';
3
- import { APPLE_ROOT_CA_G3_PEM, APPLE_ROOT_CA_PEMS, } from '../providers/apple-root-ca.js';
4
- /**
5
- * Protects against accidental deletion when Apple publishes Root CA G4 and
6
- * someone "replaces" G3 instead of appending. StoreKit 2 receipts signed
7
- * under the older root must keep validating after G4 ships.
8
- */
9
- describe('Apple Root CA bundle', () => {
10
- it('includes at least one root', () => {
11
- expect(APPLE_ROOT_CA_PEMS.length).toBeGreaterThan(0);
12
- });
13
- it('includes G3 verbatim (retained even after G4 is added)', () => {
14
- expect(APPLE_ROOT_CA_PEMS).toContain(APPLE_ROOT_CA_G3_PEM);
15
- });
16
- it('every bundled root is a parseable X.509 certificate', () => {
17
- for (const pem of APPLE_ROOT_CA_PEMS) {
18
- expect(() => new X509Certificate(pem)).not.toThrow();
19
- }
20
- });
21
- it('every bundled root is currently within its validity window', () => {
22
- const now = Date.now();
23
- for (const pem of APPLE_ROOT_CA_PEMS) {
24
- const cert = new X509Certificate(pem);
25
- const notBefore = Date.parse(cert.validFrom);
26
- const notAfter = Date.parse(cert.validTo);
27
- expect(notBefore).toBeLessThanOrEqual(now);
28
- expect(notAfter).toBeGreaterThan(now);
29
- }
30
- });
31
- it('G3 is self-signed by Apple (issuer matches subject)', () => {
32
- const cert = new X509Certificate(APPLE_ROOT_CA_G3_PEM);
33
- expect(cert.issuer).toBe(cert.subject);
34
- expect(cert.subject).toContain('Apple Root CA - G3');
35
- });
36
- it('no duplicate PEMs in the bundle', () => {
37
- const set = new Set(APPLE_ROOT_CA_PEMS);
38
- expect(set.size).toBe(APPLE_ROOT_CA_PEMS.length);
39
- });
40
- });
41
- //# sourceMappingURL=apple-root-ca.test.js.map