@onesub/server 0.11.4 → 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 (127) hide show
  1. package/README.md +119 -2
  2. package/dist/__tests__/cache.test.d.ts +2 -0
  3. package/dist/__tests__/cache.test.d.ts.map +1 -0
  4. package/dist/__tests__/openapi.test.d.ts +2 -0
  5. package/dist/__tests__/openapi.test.d.ts.map +1 -0
  6. package/dist/__tests__/redis-store.test.d.ts +2 -0
  7. package/dist/__tests__/redis-store.test.d.ts.map +1 -0
  8. package/dist/__tests__/webhook-apple.test.d.ts +12 -0
  9. package/dist/__tests__/webhook-apple.test.d.ts.map +1 -0
  10. package/dist/__tests__/webhook-events.test.d.ts +2 -0
  11. package/dist/__tests__/webhook-events.test.d.ts.map +1 -0
  12. package/dist/__tests__/webhook-google.test.d.ts +13 -0
  13. package/dist/__tests__/webhook-google.test.d.ts.map +1 -0
  14. package/dist/__tests__/webhook-queue.test.d.ts +2 -0
  15. package/dist/__tests__/webhook-queue.test.d.ts.map +1 -0
  16. package/dist/cache.d.ts +32 -0
  17. package/dist/cache.d.ts.map +1 -0
  18. package/dist/index.cjs +3084 -0
  19. package/dist/index.cjs.map +1 -0
  20. package/dist/index.d.ts +34 -1
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.js +3045 -118
  23. package/dist/index.js.map +1 -1
  24. package/dist/openapi.d.ts +41 -0
  25. package/dist/openapi.d.ts.map +1 -0
  26. package/dist/providers/apple.d.ts +1 -1
  27. package/dist/providers/apple.d.ts.map +1 -1
  28. package/dist/providers/google.d.ts.map +1 -1
  29. package/dist/routes/admin.d.ts +2 -1
  30. package/dist/routes/admin.d.ts.map +1 -1
  31. package/dist/routes/webhook-apple.d.ts +6 -0
  32. package/dist/routes/webhook-apple.d.ts.map +1 -0
  33. package/dist/routes/webhook-google.d.ts +7 -0
  34. package/dist/routes/webhook-google.d.ts.map +1 -0
  35. package/dist/routes/webhook.d.ts +6 -18
  36. package/dist/routes/webhook.d.ts.map +1 -1
  37. package/dist/stores/redis.d.ts +60 -0
  38. package/dist/stores/redis.d.ts.map +1 -0
  39. package/dist/stores/schema.d.ts.map +1 -1
  40. package/dist/tracing.d.ts +32 -0
  41. package/dist/tracing.d.ts.map +1 -0
  42. package/dist/webhook-events.d.ts +58 -0
  43. package/dist/webhook-events.d.ts.map +1 -0
  44. package/dist/webhook-queue.d.ts +92 -0
  45. package/dist/webhook-queue.d.ts.map +1 -0
  46. package/package.json +33 -4
  47. package/sql/schema.sql +10 -0
  48. package/dist/__tests__/admin-customer-detail.test.js +0 -146
  49. package/dist/__tests__/admin-customer-detail.test.js.map +0 -1
  50. package/dist/__tests__/admin-list-subscriptions.test.js +0 -192
  51. package/dist/__tests__/admin-list-subscriptions.test.js.map +0 -1
  52. package/dist/__tests__/admin-subscription-detail.test.js +0 -112
  53. package/dist/__tests__/admin-subscription-detail.test.js.map +0 -1
  54. package/dist/__tests__/apple-root-ca.test.js +0 -41
  55. package/dist/__tests__/apple-root-ca.test.js.map +0 -1
  56. package/dist/__tests__/apple-status-api.test.js +0 -345
  57. package/dist/__tests__/apple-status-api.test.js.map +0 -1
  58. package/dist/__tests__/entitlements.test.js +0 -268
  59. package/dist/__tests__/entitlements.test.js.map +0 -1
  60. package/dist/__tests__/error-codes.test.js +0 -168
  61. package/dist/__tests__/error-codes.test.js.map +0 -1
  62. package/dist/__tests__/fetch-hardening.test.js +0 -192
  63. package/dist/__tests__/fetch-hardening.test.js.map +0 -1
  64. package/dist/__tests__/google-paused.test.js +0 -126
  65. package/dist/__tests__/google-paused.test.js.map +0 -1
  66. package/dist/__tests__/google-price-change.test.js +0 -186
  67. package/dist/__tests__/google-price-change.test.js.map +0 -1
  68. package/dist/__tests__/google-subscriptions-v2.test.js +0 -206
  69. package/dist/__tests__/google-subscriptions-v2.test.js.map +0 -1
  70. package/dist/__tests__/lifecycle-scenarios.test.js +0 -579
  71. package/dist/__tests__/lifecycle-scenarios.test.js.map +0 -1
  72. package/dist/__tests__/lifecycle-states.test.js +0 -423
  73. package/dist/__tests__/lifecycle-states.test.js.map +0 -1
  74. package/dist/__tests__/linked-purchase-token.test.js +0 -227
  75. package/dist/__tests__/linked-purchase-token.test.js.map +0 -1
  76. package/dist/__tests__/metrics.test.js +0 -340
  77. package/dist/__tests__/metrics.test.js.map +0 -1
  78. package/dist/__tests__/mock-provider.test.js +0 -188
  79. package/dist/__tests__/mock-provider.test.js.map +0 -1
  80. package/dist/__tests__/paused-auto-resume.test.js +0 -162
  81. package/dist/__tests__/paused-auto-resume.test.js.map +0 -1
  82. package/dist/__tests__/providers.test.js +0 -321
  83. package/dist/__tests__/providers.test.js.map +0 -1
  84. package/dist/__tests__/refund-policy.test.js +0 -317
  85. package/dist/__tests__/refund-policy.test.js.map +0 -1
  86. package/dist/__tests__/schema.test.js +0 -46
  87. package/dist/__tests__/schema.test.js.map +0 -1
  88. package/dist/__tests__/store.test.js +0 -172
  89. package/dist/__tests__/store.test.js.map +0 -1
  90. package/dist/__tests__/test-utils.js +0 -30
  91. package/dist/__tests__/test-utils.js.map +0 -1
  92. package/dist/__tests__/webhook-refund.test.js +0 -313
  93. package/dist/__tests__/webhook-refund.test.js.map +0 -1
  94. package/dist/errors.js +0 -19
  95. package/dist/errors.js.map +0 -1
  96. package/dist/http.js +0 -36
  97. package/dist/http.js.map +0 -1
  98. package/dist/logger.js +0 -20
  99. package/dist/logger.js.map +0 -1
  100. package/dist/providers/apple-root-ca.js +0 -38
  101. package/dist/providers/apple-root-ca.js.map +0 -1
  102. package/dist/providers/apple.js +0 -499
  103. package/dist/providers/apple.js.map +0 -1
  104. package/dist/providers/google.js +0 -493
  105. package/dist/providers/google.js.map +0 -1
  106. package/dist/providers/mock.js +0 -93
  107. package/dist/providers/mock.js.map +0 -1
  108. package/dist/routes/admin.js +0 -251
  109. package/dist/routes/admin.js.map +0 -1
  110. package/dist/routes/entitlements.js +0 -130
  111. package/dist/routes/entitlements.js.map +0 -1
  112. package/dist/routes/metrics.js +0 -281
  113. package/dist/routes/metrics.js.map +0 -1
  114. package/dist/routes/purchase.js +0 -187
  115. package/dist/routes/purchase.js.map +0 -1
  116. package/dist/routes/status.js +0 -55
  117. package/dist/routes/status.js.map +0 -1
  118. package/dist/routes/validate.js +0 -70
  119. package/dist/routes/validate.js.map +0 -1
  120. package/dist/routes/webhook.js +0 -441
  121. package/dist/routes/webhook.js.map +0 -1
  122. package/dist/store.js +0 -145
  123. package/dist/store.js.map +0 -1
  124. package/dist/stores/postgres.js +0 -329
  125. package/dist/stores/postgres.js.map +0 -1
  126. package/dist/stores/schema.js +0 -57
  127. package/dist/stores/schema.js.map +0 -1
@@ -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":"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"}
@@ -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
@@ -1 +0,0 @@
1
- {"version":3,"file":"apple-root-ca.test.js","sourceRoot":"","sources":["../../src/__tests__/apple-root-ca.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EACL,oBAAoB,EACpB,kBAAkB,GACnB,MAAM,+BAA+B,CAAC;AAEvC;;;;GAIG;AACH,QAAQ,CAAC,sBAAsB,EAAE,GAAG,EAAE;IACpC,EAAE,CAAC,4BAA4B,EAAE,GAAG,EAAE;QACpC,MAAM,CAAC,kBAAkB,CAAC,MAAM,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;IACvD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wDAAwD,EAAE,GAAG,EAAE;QAChE,MAAM,CAAC,kBAAkB,CAAC,CAAC,SAAS,CAAC,oBAAoB,CAAC,CAAC;IAC7D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qDAAqD,EAAE,GAAG,EAAE;QAC7D,KAAK,MAAM,GAAG,IAAI,kBAAkB,EAAE,CAAC;YACrC,MAAM,CAAC,GAAG,EAAE,CAAC,IAAI,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;QACvD,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4DAA4D,EAAE,GAAG,EAAE;QACpE,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,KAAK,MAAM,GAAG,IAAI,kBAAkB,EAAE,CAAC;YACrC,MAAM,IAAI,GAAG,IAAI,eAAe,CAAC,GAAG,CAAC,CAAC;YACtC,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YAC7C,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAC1C,MAAM,CAAC,SAAS,CAAC,CAAC,mBAAmB,CAAC,GAAG,CAAC,CAAC;YAC3C,MAAM,CAAC,QAAQ,CAAC,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC;QACxC,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qDAAqD,EAAE,GAAG,EAAE;QAC7D,MAAM,IAAI,GAAG,IAAI,eAAe,CAAC,oBAAoB,CAAC,CAAC;QACvD,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACvC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,oBAAoB,CAAC,CAAC;IACvD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iCAAiC,EAAE,GAAG,EAAE;QACzC,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,kBAAkB,CAAC,CAAC;QACxC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,kBAAkB,CAAC,MAAM,CAAC,CAAC;IACnD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}