@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.
- package/dist/__tests__/webhook-apple.test.d.ts +12 -0
- package/dist/__tests__/webhook-apple.test.d.ts.map +1 -0
- package/dist/__tests__/webhook-google.test.d.ts +13 -0
- package/dist/__tests__/webhook-google.test.d.ts.map +1 -0
- package/dist/index.cjs +258 -288
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +259 -286
- package/dist/index.js.map +1 -1
- package/dist/providers/google.d.ts.map +1 -1
- package/dist/routes/webhook-apple.d.ts +6 -0
- package/dist/routes/webhook-apple.d.ts.map +1 -0
- package/dist/routes/webhook-google.d.ts +7 -0
- package/dist/routes/webhook-google.d.ts.map +1 -0
- package/dist/routes/webhook.d.ts +4 -17
- package/dist/routes/webhook.d.ts.map +1 -1
- package/package.json +2 -2
- package/dist/__tests__/admin-customer-detail.test.js +0 -146
- package/dist/__tests__/admin-customer-detail.test.js.map +0 -1
- package/dist/__tests__/admin-list-subscriptions.test.js +0 -192
- package/dist/__tests__/admin-list-subscriptions.test.js.map +0 -1
- package/dist/__tests__/admin-subscription-detail.test.js +0 -112
- package/dist/__tests__/admin-subscription-detail.test.js.map +0 -1
- package/dist/__tests__/apple-root-ca.test.js +0 -41
- package/dist/__tests__/apple-root-ca.test.js.map +0 -1
- package/dist/__tests__/apple-status-api.test.js +0 -345
- package/dist/__tests__/apple-status-api.test.js.map +0 -1
- package/dist/__tests__/cache.test.js +0 -34
- package/dist/__tests__/cache.test.js.map +0 -1
- package/dist/__tests__/entitlements.test.js +0 -268
- package/dist/__tests__/entitlements.test.js.map +0 -1
- package/dist/__tests__/error-codes.test.js +0 -168
- package/dist/__tests__/error-codes.test.js.map +0 -1
- package/dist/__tests__/fetch-hardening.test.js +0 -192
- package/dist/__tests__/fetch-hardening.test.js.map +0 -1
- package/dist/__tests__/google-paused.test.js +0 -126
- package/dist/__tests__/google-paused.test.js.map +0 -1
- package/dist/__tests__/google-price-change.test.js +0 -186
- package/dist/__tests__/google-price-change.test.js.map +0 -1
- package/dist/__tests__/google-subscriptions-v2.test.js +0 -206
- package/dist/__tests__/google-subscriptions-v2.test.js.map +0 -1
- package/dist/__tests__/lifecycle-scenarios.test.js +0 -579
- package/dist/__tests__/lifecycle-scenarios.test.js.map +0 -1
- package/dist/__tests__/lifecycle-states.test.js +0 -423
- package/dist/__tests__/lifecycle-states.test.js.map +0 -1
- package/dist/__tests__/linked-purchase-token.test.js +0 -227
- package/dist/__tests__/linked-purchase-token.test.js.map +0 -1
- package/dist/__tests__/metrics.test.js +0 -340
- package/dist/__tests__/metrics.test.js.map +0 -1
- package/dist/__tests__/mock-provider.test.js +0 -188
- package/dist/__tests__/mock-provider.test.js.map +0 -1
- package/dist/__tests__/openapi.test.js +0 -32
- package/dist/__tests__/openapi.test.js.map +0 -1
- package/dist/__tests__/paused-auto-resume.test.js +0 -162
- package/dist/__tests__/paused-auto-resume.test.js.map +0 -1
- package/dist/__tests__/providers.test.js +0 -321
- package/dist/__tests__/providers.test.js.map +0 -1
- package/dist/__tests__/redis-store.test.js +0 -117
- package/dist/__tests__/redis-store.test.js.map +0 -1
- package/dist/__tests__/refund-policy.test.js +0 -317
- package/dist/__tests__/refund-policy.test.js.map +0 -1
- package/dist/__tests__/schema.test.js +0 -46
- package/dist/__tests__/schema.test.js.map +0 -1
- package/dist/__tests__/store.test.js +0 -172
- package/dist/__tests__/store.test.js.map +0 -1
- package/dist/__tests__/test-utils.js +0 -30
- package/dist/__tests__/test-utils.js.map +0 -1
- package/dist/__tests__/webhook-events.test.js +0 -36
- package/dist/__tests__/webhook-events.test.js.map +0 -1
- package/dist/__tests__/webhook-queue.test.js +0 -26
- package/dist/__tests__/webhook-queue.test.js.map +0 -1
- package/dist/__tests__/webhook-refund.test.js +0 -313
- package/dist/__tests__/webhook-refund.test.js.map +0 -1
- package/dist/cache.js +0 -43
- package/dist/cache.js.map +0 -1
- package/dist/errors.js +0 -19
- package/dist/errors.js.map +0 -1
- package/dist/http.js +0 -36
- package/dist/http.js.map +0 -1
- package/dist/logger.js +0 -20
- package/dist/logger.js.map +0 -1
- package/dist/openapi.js +0 -196
- package/dist/openapi.js.map +0 -1
- package/dist/providers/apple-root-ca.js +0 -38
- package/dist/providers/apple-root-ca.js.map +0 -1
- package/dist/providers/apple.js +0 -510
- package/dist/providers/apple.js.map +0 -1
- package/dist/providers/google.js +0 -522
- package/dist/providers/google.js.map +0 -1
- package/dist/providers/mock.js +0 -93
- package/dist/providers/mock.js.map +0 -1
- package/dist/routes/admin.js +0 -287
- package/dist/routes/admin.js.map +0 -1
- package/dist/routes/entitlements.js +0 -130
- package/dist/routes/entitlements.js.map +0 -1
- package/dist/routes/metrics.js +0 -281
- package/dist/routes/metrics.js.map +0 -1
- package/dist/routes/purchase.js +0 -187
- package/dist/routes/purchase.js.map +0 -1
- package/dist/routes/status.js +0 -55
- package/dist/routes/status.js.map +0 -1
- package/dist/routes/validate.js +0 -70
- package/dist/routes/validate.js.map +0 -1
- package/dist/routes/webhook.js +0 -461
- package/dist/routes/webhook.js.map +0 -1
- package/dist/store.js +0 -145
- package/dist/store.js.map +0 -1
- package/dist/stores/postgres.js +0 -329
- package/dist/stores/postgres.js.map +0 -1
- package/dist/stores/redis.js +0 -255
- package/dist/stores/redis.js.map +0 -1
- package/dist/stores/schema.js +0 -67
- package/dist/stores/schema.js.map +0 -1
- package/dist/tracing.js +0 -74
- package/dist/tracing.js.map +0 -1
- package/dist/webhook-events.js +0 -57
- package/dist/webhook-events.js.map +0 -1
- package/dist/webhook-queue.js +0 -108
- package/dist/webhook-queue.js.map +0 -1
package/dist/routes/admin.js
DELETED
|
@@ -1,287 +0,0 @@
|
|
|
1
|
-
import { Router } from 'express';
|
|
2
|
-
import { z } from 'zod';
|
|
3
|
-
import { PURCHASE_TYPE, ONESUB_ERROR_CODE, ROUTES, SUBSCRIPTION_STATUS } from '@onesub/shared';
|
|
4
|
-
import { evaluateEntitlement } from './entitlements.js';
|
|
5
|
-
import { sendError, sendZodError } from '../errors.js';
|
|
6
|
-
const ADMIN_SECRET_HEADER = 'x-admin-secret';
|
|
7
|
-
/**
|
|
8
|
-
* Admin routes for testing / operational tasks. Require `X-Admin-Secret`
|
|
9
|
-
* header matching config.adminSecret. If config.adminSecret is not set, the
|
|
10
|
-
* entire admin router is not mounted (returns 404 from the parent router).
|
|
11
|
-
*
|
|
12
|
-
* Endpoints:
|
|
13
|
-
* DELETE /onesub/purchase/admin/:userId/:productId
|
|
14
|
-
* → reset a non-consumable so the user can test the purchase flow again
|
|
15
|
-
*
|
|
16
|
-
* POST /onesub/purchase/admin/grant
|
|
17
|
-
* → manually insert a purchase record (skips store verification)
|
|
18
|
-
* body: { userId, productId, platform, type?, transactionId? }
|
|
19
|
-
*
|
|
20
|
-
* POST /onesub/purchase/admin/transfer
|
|
21
|
-
* → reassign a transactionId to a new userId (device migration)
|
|
22
|
-
*
|
|
23
|
-
* GET /onesub/admin/subscriptions?userId=&status=&productId=&platform=&limit=&offset=
|
|
24
|
-
* → filtered/paginated subscription list (used by dashboard + scripts)
|
|
25
|
-
*
|
|
26
|
-
* GET /onesub/admin/subscriptions/:transactionId
|
|
27
|
-
* → single subscription record by originalTransactionId (dashboard detail page)
|
|
28
|
-
*
|
|
29
|
-
* GET /onesub/admin/customers/:userId
|
|
30
|
-
* → bundle of every record onesub knows for one user (subs + purchases +
|
|
31
|
-
* entitlements when configured). Backs the dashboard customer detail page.
|
|
32
|
-
*/
|
|
33
|
-
export function createAdminRouter(config, purchaseStore, store, webhookQueue) {
|
|
34
|
-
if (!config.adminSecret)
|
|
35
|
-
return null;
|
|
36
|
-
const router = Router();
|
|
37
|
-
const adminSecret = config.adminSecret;
|
|
38
|
-
// Auth middleware — applied to both admin scopes. Without this guard, a
|
|
39
|
-
// misconfigured mount on the parent root would expose admin endpoints
|
|
40
|
-
// alongside host-app routes (e.g. /health).
|
|
41
|
-
const adminAuth = (req, res, next) => {
|
|
42
|
-
const provided = req.headers[ADMIN_SECRET_HEADER];
|
|
43
|
-
if (typeof provided !== 'string' || provided !== adminSecret) {
|
|
44
|
-
sendError(res, 401, ONESUB_ERROR_CODE.INVALID_ADMIN_SECRET, 'INVALID_ADMIN_SECRET');
|
|
45
|
-
return;
|
|
46
|
-
}
|
|
47
|
-
next();
|
|
48
|
-
};
|
|
49
|
-
router.use('/onesub/purchase/admin', adminAuth);
|
|
50
|
-
router.use('/onesub/admin', adminAuth);
|
|
51
|
-
// DELETE /onesub/purchase/admin/:userId/:productId
|
|
52
|
-
// Express 5 types route params as `string | string[]` — narrow via zod.
|
|
53
|
-
const resetParamsSchema = z.object({
|
|
54
|
-
userId: z.string().min(1).max(256),
|
|
55
|
-
productId: z.string().min(1).max(256),
|
|
56
|
-
});
|
|
57
|
-
router.delete('/onesub/purchase/admin/:userId/:productId', async (req, res) => {
|
|
58
|
-
let params;
|
|
59
|
-
try {
|
|
60
|
-
params = resetParamsSchema.parse(req.params);
|
|
61
|
-
}
|
|
62
|
-
catch {
|
|
63
|
-
sendError(res, 400, ONESUB_ERROR_CODE.INVALID_INPUT, 'userId and productId required');
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
const deleted = await purchaseStore.deletePurchases(params.userId, params.productId);
|
|
67
|
-
res.json({ ok: true, deleted });
|
|
68
|
-
});
|
|
69
|
-
// POST /onesub/purchase/admin/transfer — reassign transactionId to a new userId
|
|
70
|
-
// (legitimate device/account migration)
|
|
71
|
-
const transferSchema = z.object({
|
|
72
|
-
transactionId: z.string().min(1).max(256),
|
|
73
|
-
newUserId: z.string().min(1).max(256),
|
|
74
|
-
});
|
|
75
|
-
router.post('/onesub/purchase/admin/transfer', async (req, res) => {
|
|
76
|
-
let body;
|
|
77
|
-
try {
|
|
78
|
-
body = transferSchema.parse(req.body);
|
|
79
|
-
}
|
|
80
|
-
catch (err) {
|
|
81
|
-
if (err instanceof z.ZodError) {
|
|
82
|
-
sendZodError(res, err);
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
85
|
-
throw err;
|
|
86
|
-
}
|
|
87
|
-
const existing = await purchaseStore.getPurchaseByTransactionId(body.transactionId);
|
|
88
|
-
if (!existing) {
|
|
89
|
-
sendError(res, 404, ONESUB_ERROR_CODE.TRANSACTION_NOT_FOUND, 'TRANSACTION_NOT_FOUND');
|
|
90
|
-
return;
|
|
91
|
-
}
|
|
92
|
-
// Delete the old row, then save under new userId
|
|
93
|
-
await purchaseStore.deletePurchases(existing.userId, existing.productId);
|
|
94
|
-
const migrated = { ...existing, userId: body.newUserId };
|
|
95
|
-
await purchaseStore.savePurchase(migrated);
|
|
96
|
-
res.json({ ok: true, purchase: migrated });
|
|
97
|
-
});
|
|
98
|
-
// POST /onesub/purchase/admin/grant
|
|
99
|
-
const grantSchema = z.object({
|
|
100
|
-
userId: z.string().min(1).max(256),
|
|
101
|
-
productId: z.string().min(1).max(256),
|
|
102
|
-
platform: z.enum(['apple', 'google']),
|
|
103
|
-
type: z.enum([PURCHASE_TYPE.CONSUMABLE, PURCHASE_TYPE.NON_CONSUMABLE]).default(PURCHASE_TYPE.NON_CONSUMABLE),
|
|
104
|
-
transactionId: z.string().min(1).max(256).optional(),
|
|
105
|
-
});
|
|
106
|
-
router.post('/onesub/purchase/admin/grant', async (req, res) => {
|
|
107
|
-
let body;
|
|
108
|
-
try {
|
|
109
|
-
body = grantSchema.parse(req.body);
|
|
110
|
-
}
|
|
111
|
-
catch (err) {
|
|
112
|
-
if (err instanceof z.ZodError) {
|
|
113
|
-
sendZodError(res, err);
|
|
114
|
-
return;
|
|
115
|
-
}
|
|
116
|
-
throw err;
|
|
117
|
-
}
|
|
118
|
-
const transactionId = body.transactionId ?? `admin_grant_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
|
|
119
|
-
const purchase = {
|
|
120
|
-
transactionId,
|
|
121
|
-
userId: body.userId,
|
|
122
|
-
productId: body.productId,
|
|
123
|
-
platform: body.platform,
|
|
124
|
-
type: body.type,
|
|
125
|
-
quantity: 1,
|
|
126
|
-
purchasedAt: new Date().toISOString(),
|
|
127
|
-
};
|
|
128
|
-
await purchaseStore.savePurchase(purchase);
|
|
129
|
-
res.json({ ok: true, purchase });
|
|
130
|
-
});
|
|
131
|
-
// GET /onesub/admin/subscriptions?userId=&status=&productId=&platform=&limit=&offset=
|
|
132
|
-
// Filtered + paginated subscription list. Backs the dashboard's
|
|
133
|
-
// subscriptions page and ad-hoc operational scripts.
|
|
134
|
-
const listQuerySchema = z.object({
|
|
135
|
-
userId: z.string().min(1).max(256).optional(),
|
|
136
|
-
status: z.enum([
|
|
137
|
-
SUBSCRIPTION_STATUS.ACTIVE,
|
|
138
|
-
SUBSCRIPTION_STATUS.GRACE_PERIOD,
|
|
139
|
-
SUBSCRIPTION_STATUS.ON_HOLD,
|
|
140
|
-
SUBSCRIPTION_STATUS.PAUSED,
|
|
141
|
-
SUBSCRIPTION_STATUS.EXPIRED,
|
|
142
|
-
SUBSCRIPTION_STATUS.CANCELED,
|
|
143
|
-
SUBSCRIPTION_STATUS.NONE,
|
|
144
|
-
]).optional(),
|
|
145
|
-
productId: z.string().min(1).max(256).optional(),
|
|
146
|
-
platform: z.enum(['apple', 'google']).optional(),
|
|
147
|
-
// Cap at 200 server-side so a runaway dashboard query can't tip the DB.
|
|
148
|
-
limit: z.coerce.number().int().positive().max(200).optional(),
|
|
149
|
-
offset: z.coerce.number().int().nonnegative().optional(),
|
|
150
|
-
});
|
|
151
|
-
router.get(ROUTES.ADMIN_SUBSCRIPTIONS, async (req, res) => {
|
|
152
|
-
let query;
|
|
153
|
-
try {
|
|
154
|
-
query = listQuerySchema.parse(req.query);
|
|
155
|
-
}
|
|
156
|
-
catch (err) {
|
|
157
|
-
if (err instanceof z.ZodError) {
|
|
158
|
-
sendZodError(res, err);
|
|
159
|
-
return;
|
|
160
|
-
}
|
|
161
|
-
throw err;
|
|
162
|
-
}
|
|
163
|
-
try {
|
|
164
|
-
const result = await store.listFiltered(query);
|
|
165
|
-
const response = {
|
|
166
|
-
items: result.items,
|
|
167
|
-
total: result.total,
|
|
168
|
-
limit: result.limit,
|
|
169
|
-
offset: result.offset,
|
|
170
|
-
};
|
|
171
|
-
res.status(200).json(response);
|
|
172
|
-
}
|
|
173
|
-
catch (err) {
|
|
174
|
-
// Bubble the message up but not the stack — admin clients log status code
|
|
175
|
-
sendError(res, 500, ONESUB_ERROR_CODE.STORE_ERROR, err.message ?? 'list error');
|
|
176
|
-
}
|
|
177
|
-
});
|
|
178
|
-
// GET /onesub/admin/subscriptions/:transactionId — single record by
|
|
179
|
-
// originalTransactionId. Backs the dashboard's subscription detail page.
|
|
180
|
-
const detailParamsSchema = z.object({
|
|
181
|
-
transactionId: z.string().min(1).max(256),
|
|
182
|
-
});
|
|
183
|
-
router.get('/onesub/admin/subscriptions/:transactionId', async (req, res) => {
|
|
184
|
-
let params;
|
|
185
|
-
try {
|
|
186
|
-
params = detailParamsSchema.parse(req.params);
|
|
187
|
-
}
|
|
188
|
-
catch {
|
|
189
|
-
sendError(res, 400, ONESUB_ERROR_CODE.INVALID_INPUT, 'transactionId required');
|
|
190
|
-
return;
|
|
191
|
-
}
|
|
192
|
-
try {
|
|
193
|
-
const sub = await store.getByTransactionId(params.transactionId);
|
|
194
|
-
if (!sub) {
|
|
195
|
-
sendError(res, 404, ONESUB_ERROR_CODE.TRANSACTION_NOT_FOUND, 'TRANSACTION_NOT_FOUND');
|
|
196
|
-
return;
|
|
197
|
-
}
|
|
198
|
-
res.status(200).json(sub);
|
|
199
|
-
}
|
|
200
|
-
catch (err) {
|
|
201
|
-
sendError(res, 500, ONESUB_ERROR_CODE.STORE_ERROR, err.message ?? 'detail error');
|
|
202
|
-
}
|
|
203
|
-
});
|
|
204
|
-
// GET /onesub/admin/customers/:userId — full per-user profile. Combines the
|
|
205
|
-
// store calls a CS dashboard makes for "show me everything about this user"
|
|
206
|
-
// into a single round-trip.
|
|
207
|
-
//
|
|
208
|
-
// Always returns 200 (even for unknown userIds) — empty arrays + no
|
|
209
|
-
// entitlements means "we have no record of this user", which is a valid
|
|
210
|
-
// signal for the dashboard to render rather than an error condition.
|
|
211
|
-
const customerParamsSchema = z.object({
|
|
212
|
-
userId: z.string().min(1).max(256),
|
|
213
|
-
});
|
|
214
|
-
router.get('/onesub/admin/customers/:userId', async (req, res) => {
|
|
215
|
-
let params;
|
|
216
|
-
try {
|
|
217
|
-
params = customerParamsSchema.parse(req.params);
|
|
218
|
-
}
|
|
219
|
-
catch {
|
|
220
|
-
sendError(res, 400, ONESUB_ERROR_CODE.INVALID_INPUT, 'userId required');
|
|
221
|
-
return;
|
|
222
|
-
}
|
|
223
|
-
try {
|
|
224
|
-
const [subscriptions, purchases] = await Promise.all([
|
|
225
|
-
store.getAllByUserId(params.userId),
|
|
226
|
-
purchaseStore.getPurchasesByUserId(params.userId),
|
|
227
|
-
]);
|
|
228
|
-
// Evaluate entitlements only when configured. Hosts that don't use the
|
|
229
|
-
// entitlement abstraction get the field omitted (dashboard hides panel).
|
|
230
|
-
let entitlements;
|
|
231
|
-
if (config.entitlements && Object.keys(config.entitlements).length > 0) {
|
|
232
|
-
entitlements = {};
|
|
233
|
-
for (const [id, def] of Object.entries(config.entitlements)) {
|
|
234
|
-
entitlements[id] = await evaluateEntitlement(params.userId, def, store, purchaseStore);
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
const response = {
|
|
238
|
-
userId: params.userId,
|
|
239
|
-
subscriptions,
|
|
240
|
-
purchases,
|
|
241
|
-
...(entitlements ? { entitlements } : {}),
|
|
242
|
-
};
|
|
243
|
-
res.status(200).json(response);
|
|
244
|
-
}
|
|
245
|
-
catch (err) {
|
|
246
|
-
sendError(res, 500, ONESUB_ERROR_CODE.STORE_ERROR, err.message ?? 'customer error');
|
|
247
|
-
}
|
|
248
|
-
});
|
|
249
|
-
// GET /onesub/admin/webhook-deadletters
|
|
250
|
-
// POST /onesub/admin/webhook-replay/:id
|
|
251
|
-
// Only mounted when the configured queue supports dead-letter inspection
|
|
252
|
-
// (the default in-process queue does not — Apple/Google retries are the
|
|
253
|
-
// durability layer for that mode).
|
|
254
|
-
if (webhookQueue?.listDeadLetters) {
|
|
255
|
-
router.get('/onesub/admin/webhook-deadletters', async (_req, res) => {
|
|
256
|
-
try {
|
|
257
|
-
const items = await webhookQueue.listDeadLetters();
|
|
258
|
-
res.status(200).json({ items });
|
|
259
|
-
}
|
|
260
|
-
catch (err) {
|
|
261
|
-
sendError(res, 500, ONESUB_ERROR_CODE.STORE_ERROR, err.message ?? 'list error');
|
|
262
|
-
}
|
|
263
|
-
});
|
|
264
|
-
}
|
|
265
|
-
if (webhookQueue?.replayDeadLetter) {
|
|
266
|
-
const replayParamsSchema = z.object({ id: z.string().min(1).max(256) });
|
|
267
|
-
router.post('/onesub/admin/webhook-replay/:id', async (req, res) => {
|
|
268
|
-
let params;
|
|
269
|
-
try {
|
|
270
|
-
params = replayParamsSchema.parse(req.params);
|
|
271
|
-
}
|
|
272
|
-
catch {
|
|
273
|
-
sendError(res, 400, ONESUB_ERROR_CODE.INVALID_INPUT, 'id required');
|
|
274
|
-
return;
|
|
275
|
-
}
|
|
276
|
-
try {
|
|
277
|
-
await webhookQueue.replayDeadLetter(params.id);
|
|
278
|
-
res.status(200).json({ ok: true });
|
|
279
|
-
}
|
|
280
|
-
catch (err) {
|
|
281
|
-
sendError(res, 500, ONESUB_ERROR_CODE.STORE_ERROR, err.message ?? 'replay error');
|
|
282
|
-
}
|
|
283
|
-
});
|
|
284
|
-
}
|
|
285
|
-
return router;
|
|
286
|
-
}
|
|
287
|
-
//# sourceMappingURL=admin.js.map
|
package/dist/routes/admin.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"admin.js","sourceRoot":"","sources":["../../src/routes/admin.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,130 +0,0 @@
|
|
|
1
|
-
import { Router } from 'express';
|
|
2
|
-
import { z } from 'zod';
|
|
3
|
-
import { ROUTES, SUBSCRIPTION_STATUS, ONESUB_ERROR_CODE, PURCHASE_TYPE } from '@onesub/shared';
|
|
4
|
-
import { log } from '../logger.js';
|
|
5
|
-
import { sendError } from '../errors.js';
|
|
6
|
-
/**
|
|
7
|
-
* Evaluate a single entitlement for a user.
|
|
8
|
-
*
|
|
9
|
-
* A user is entitled when EITHER condition holds for any productId in
|
|
10
|
-
* `entitlement.productIds`:
|
|
11
|
-
* 1. an active subscription (status === active|grace_period AND
|
|
12
|
-
* expiresAt > now) for that productId exists
|
|
13
|
-
* 2. a non-consumable purchase for that productId exists
|
|
14
|
-
*
|
|
15
|
-
* Consumables are intentionally excluded — they grant a one-time resource,
|
|
16
|
-
* not an ongoing right.
|
|
17
|
-
*
|
|
18
|
-
* Subscription is preferred over purchase when both match (subs carry an
|
|
19
|
-
* expiry; non-consumables are forever) so the source field skews toward the
|
|
20
|
-
* more "interesting" signal for ops/analytics.
|
|
21
|
-
*/
|
|
22
|
-
export async function evaluateEntitlement(userId, entitlement, store, purchaseStore) {
|
|
23
|
-
const productIdSet = new Set(entitlement.productIds);
|
|
24
|
-
const now = Date.now();
|
|
25
|
-
// 1. Check subscriptions first (richer signal — has expiry).
|
|
26
|
-
const subs = await store.getAllByUserId(userId);
|
|
27
|
-
for (const sub of subs) {
|
|
28
|
-
if (!productIdSet.has(sub.productId))
|
|
29
|
-
continue;
|
|
30
|
-
const statusAllows = sub.status === SUBSCRIPTION_STATUS.ACTIVE ||
|
|
31
|
-
sub.status === SUBSCRIPTION_STATUS.GRACE_PERIOD;
|
|
32
|
-
if (!statusAllows)
|
|
33
|
-
continue;
|
|
34
|
-
if (new Date(sub.expiresAt).getTime() <= now)
|
|
35
|
-
continue;
|
|
36
|
-
return {
|
|
37
|
-
active: true,
|
|
38
|
-
source: 'subscription',
|
|
39
|
-
productId: sub.productId,
|
|
40
|
-
expiresAt: sub.expiresAt,
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
// 2. Check non-consumable purchases.
|
|
44
|
-
const purchases = await purchaseStore.getPurchasesByUserId(userId);
|
|
45
|
-
for (const p of purchases) {
|
|
46
|
-
if (p.type !== PURCHASE_TYPE.NON_CONSUMABLE)
|
|
47
|
-
continue;
|
|
48
|
-
if (!productIdSet.has(p.productId))
|
|
49
|
-
continue;
|
|
50
|
-
return {
|
|
51
|
-
active: true,
|
|
52
|
-
source: 'purchase',
|
|
53
|
-
productId: p.productId,
|
|
54
|
-
};
|
|
55
|
-
}
|
|
56
|
-
return { active: false, source: null };
|
|
57
|
-
}
|
|
58
|
-
const userIdSchema = z.string().min(1).max(256);
|
|
59
|
-
const entitlementIdSchema = z.string().min(1).max(128);
|
|
60
|
-
export function createEntitlementRouter(config, store, purchaseStore) {
|
|
61
|
-
// Only mount when entitlements are configured. Without this guard, the
|
|
62
|
-
// routes would always 503 — better to make the absence loud (404 from
|
|
63
|
-
// express) so misconfiguration is obvious during dev.
|
|
64
|
-
if (!config.entitlements || Object.keys(config.entitlements).length === 0) {
|
|
65
|
-
return null;
|
|
66
|
-
}
|
|
67
|
-
const entitlements = config.entitlements;
|
|
68
|
-
const router = Router();
|
|
69
|
-
/**
|
|
70
|
-
* GET /onesub/entitlement?userId=&id=premium
|
|
71
|
-
* Single entitlement check.
|
|
72
|
-
*/
|
|
73
|
-
router.get(ROUTES.ENTITLEMENT, async (req, res) => {
|
|
74
|
-
let userId;
|
|
75
|
-
let id;
|
|
76
|
-
try {
|
|
77
|
-
userId = userIdSchema.parse(req.query['userId']);
|
|
78
|
-
id = entitlementIdSchema.parse(req.query['id']);
|
|
79
|
-
}
|
|
80
|
-
catch {
|
|
81
|
-
sendError(res, 400, ONESUB_ERROR_CODE.INVALID_INPUT, 'userId and id are required');
|
|
82
|
-
return;
|
|
83
|
-
}
|
|
84
|
-
const entitlement = entitlements[id];
|
|
85
|
-
if (!entitlement) {
|
|
86
|
-
sendError(res, 404, ONESUB_ERROR_CODE.ENTITLEMENT_NOT_FOUND, `Unknown entitlement: ${id}`);
|
|
87
|
-
return;
|
|
88
|
-
}
|
|
89
|
-
try {
|
|
90
|
-
const status = await evaluateEntitlement(userId, entitlement, store, purchaseStore);
|
|
91
|
-
const response = { id, ...status };
|
|
92
|
-
res.status(200).json(response);
|
|
93
|
-
}
|
|
94
|
-
catch (err) {
|
|
95
|
-
log.error('[onesub/entitlement] evaluation error:', err);
|
|
96
|
-
sendError(res, 500, ONESUB_ERROR_CODE.STORE_ERROR, 'Internal server error');
|
|
97
|
-
}
|
|
98
|
-
});
|
|
99
|
-
/**
|
|
100
|
-
* GET /onesub/entitlements?userId=
|
|
101
|
-
* Evaluate every configured entitlement in one round-trip — useful on
|
|
102
|
-
* app launch / login when the host wants the full entitlement map.
|
|
103
|
-
*/
|
|
104
|
-
router.get(ROUTES.ENTITLEMENTS, async (req, res) => {
|
|
105
|
-
let userId;
|
|
106
|
-
try {
|
|
107
|
-
userId = userIdSchema.parse(req.query['userId']);
|
|
108
|
-
}
|
|
109
|
-
catch {
|
|
110
|
-
sendError(res, 400, ONESUB_ERROR_CODE.INVALID_INPUT, 'userId is required');
|
|
111
|
-
return;
|
|
112
|
-
}
|
|
113
|
-
try {
|
|
114
|
-
const entries = await Promise.all(Object.entries(entitlements).map(async ([id, entitlement]) => {
|
|
115
|
-
const status = await evaluateEntitlement(userId, entitlement, store, purchaseStore);
|
|
116
|
-
return [id, status];
|
|
117
|
-
}));
|
|
118
|
-
const response = {
|
|
119
|
-
entitlements: Object.fromEntries(entries),
|
|
120
|
-
};
|
|
121
|
-
res.status(200).json(response);
|
|
122
|
-
}
|
|
123
|
-
catch (err) {
|
|
124
|
-
log.error('[onesub/entitlements] evaluation error:', err);
|
|
125
|
-
sendError(res, 500, ONESUB_ERROR_CODE.STORE_ERROR, 'Internal server error', { entitlements: {} });
|
|
126
|
-
}
|
|
127
|
-
});
|
|
128
|
-
return router;
|
|
129
|
-
}
|
|
130
|
-
//# sourceMappingURL=entitlements.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"entitlements.js","sourceRoot":"","sources":["../../src/routes/entitlements.ts"],"names":[],"mappings":"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"}
|