@onesub/server 0.21.0 → 0.21.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/index.js CHANGED
@@ -1271,6 +1271,16 @@ function sendZodError(res, err2, extra = {}) {
1271
1271
  extra
1272
1272
  );
1273
1273
  }
1274
+ function parseOrSend(res, schema, input, opts = {}) {
1275
+ const result = schema.safeParse(input);
1276
+ if (result.success) return result.data;
1277
+ if (opts.message !== void 0) {
1278
+ sendError(res, 400, ONESUB_ERROR_CODE.INVALID_INPUT, opts.message, opts.extra ?? {});
1279
+ } else {
1280
+ sendZodError(res, result.error, opts.extra ?? {});
1281
+ }
1282
+ return void 0;
1283
+ }
1274
1284
 
1275
1285
  // src/apps.ts
1276
1286
  function appName(app) {
@@ -1369,20 +1379,9 @@ function createValidateRouter(config, store) {
1369
1379
  const router = Router();
1370
1380
  const registry = getAppRegistry(config);
1371
1381
  router.post(ROUTES.VALIDATE, async (req, res) => {
1372
- let platform;
1373
- let receipt;
1374
- let userId;
1375
- let productId;
1376
- let appId;
1377
- try {
1378
- ({ platform, receipt, userId, productId, appId } = validateSchema.parse(req.body));
1379
- } catch (err2) {
1380
- if (err2 instanceof z.ZodError) {
1381
- sendZodError(res, err2, NO_SUB);
1382
- return;
1383
- }
1384
- throw err2;
1385
- }
1382
+ const body = parseOrSend(res, validateSchema, req.body, { extra: NO_SUB });
1383
+ if (!body) return;
1384
+ const { platform, receipt, userId, productId, appId } = body;
1386
1385
  try {
1387
1386
  let sub = null;
1388
1387
  const appConfig = registry.configFor({
@@ -1991,16 +1990,8 @@ function createPurchaseRouter(config, purchaseStore) {
1991
1990
  const router = Router();
1992
1991
  const registry = getAppRegistry(config);
1993
1992
  router.post(ROUTES.VALIDATE_PURCHASE, async (req, res) => {
1994
- let body;
1995
- try {
1996
- body = validatePurchaseSchema.parse(req.body);
1997
- } catch (err2) {
1998
- if (err2 instanceof z.ZodError) {
1999
- sendZodError(res, err2, NO_PURCHASE);
2000
- return;
2001
- }
2002
- throw err2;
2003
- }
1993
+ const body = parseOrSend(res, validatePurchaseSchema, req.body, { extra: NO_PURCHASE });
1994
+ if (!body) return;
2004
1995
  const { platform, receipt, userId, productId, type, appId } = body;
2005
1996
  const appConfig = registry.configFor({
2006
1997
  appId,
@@ -2126,16 +2117,10 @@ function createPurchaseRouter(config, purchaseStore) {
2126
2117
  }
2127
2118
  });
2128
2119
  router.get(ROUTES.PURCHASE_STATUS, async (req, res) => {
2129
- let query;
2130
- try {
2131
- query = purchaseStatusQuerySchema.parse(req.query);
2132
- } catch (err2) {
2133
- if (err2 instanceof z.ZodError) {
2134
- sendZodError(res, err2, { purchases: [] });
2135
- return;
2136
- }
2137
- throw err2;
2138
- }
2120
+ const query = parseOrSend(res, purchaseStatusQuerySchema, req.query, {
2121
+ extra: { purchases: [] }
2122
+ });
2123
+ if (!query) return;
2139
2124
  const { userId, productId } = query;
2140
2125
  try {
2141
2126
  const purchases = productId !== void 0 ? await purchasesForProduct(purchaseStore, userId, productId) : await purchaseStore.getPurchasesByUserId(userId);
@@ -2183,6 +2168,8 @@ async function evaluateEntitlement(userId, entitlement, store, purchaseStore) {
2183
2168
  }
2184
2169
  var userIdSchema = z.string().min(1).max(256);
2185
2170
  var entitlementIdSchema = z.string().min(1).max(128);
2171
+ var entitlementQuerySchema = z.object({ userId: userIdSchema, id: entitlementIdSchema });
2172
+ var entitlementsQuerySchema = z.object({ userId: userIdSchema });
2186
2173
  function createEntitlementRouter(config, store, purchaseStore) {
2187
2174
  if (!config.entitlements || Object.keys(config.entitlements).length === 0) {
2188
2175
  return null;
@@ -2190,15 +2177,11 @@ function createEntitlementRouter(config, store, purchaseStore) {
2190
2177
  const entitlements = config.entitlements;
2191
2178
  const router = Router();
2192
2179
  router.get(ROUTES.ENTITLEMENT, async (req, res) => {
2193
- let userId;
2194
- let id;
2195
- try {
2196
- userId = userIdSchema.parse(req.query["userId"]);
2197
- id = entitlementIdSchema.parse(req.query["id"]);
2198
- } catch {
2199
- sendError(res, 400, ONESUB_ERROR_CODE.INVALID_INPUT, "userId and id are required");
2200
- return;
2201
- }
2180
+ const query = parseOrSend(res, entitlementQuerySchema, req.query, {
2181
+ message: "userId and id are required"
2182
+ });
2183
+ if (!query) return;
2184
+ const { userId, id } = query;
2202
2185
  const entitlement = entitlements[id];
2203
2186
  if (!entitlement) {
2204
2187
  sendError(res, 404, ONESUB_ERROR_CODE.ENTITLEMENT_NOT_FOUND, `Unknown entitlement: ${id}`);
@@ -2214,13 +2197,11 @@ function createEntitlementRouter(config, store, purchaseStore) {
2214
2197
  }
2215
2198
  });
2216
2199
  router.get(ROUTES.ENTITLEMENTS, async (req, res) => {
2217
- let userId;
2218
- try {
2219
- userId = userIdSchema.parse(req.query["userId"]);
2220
- } catch {
2221
- sendError(res, 400, ONESUB_ERROR_CODE.INVALID_INPUT, "userId is required");
2222
- return;
2223
- }
2200
+ const query = parseOrSend(res, entitlementsQuerySchema, req.query, {
2201
+ message: "userId is required"
2202
+ });
2203
+ if (!query) return;
2204
+ const { userId } = query;
2224
2205
  try {
2225
2206
  const [subs, purchases] = await Promise.all([
2226
2207
  store.getAllByUserId(userId),
@@ -2269,13 +2250,10 @@ function createAdminRouter(config, purchaseStore, store, webhookQueue) {
2269
2250
  productId: z.string().min(1).max(256)
2270
2251
  });
2271
2252
  router.delete("/onesub/purchase/admin/:userId/:productId", async (req, res) => {
2272
- let params;
2273
- try {
2274
- params = resetParamsSchema.parse(req.params);
2275
- } catch {
2276
- sendError(res, 400, ONESUB_ERROR_CODE.INVALID_INPUT, "userId and productId required");
2277
- return;
2278
- }
2253
+ const params = parseOrSend(res, resetParamsSchema, req.params, {
2254
+ message: "userId and productId required"
2255
+ });
2256
+ if (!params) return;
2279
2257
  const deleted = await purchaseStore.deletePurchases(params.userId, params.productId);
2280
2258
  res.json({ ok: true, deleted });
2281
2259
  });
@@ -2284,16 +2262,8 @@ function createAdminRouter(config, purchaseStore, store, webhookQueue) {
2284
2262
  newUserId: z.string().min(1).max(256)
2285
2263
  });
2286
2264
  router.post("/onesub/purchase/admin/transfer", async (req, res) => {
2287
- let body;
2288
- try {
2289
- body = transferSchema.parse(req.body);
2290
- } catch (err2) {
2291
- if (err2 instanceof z.ZodError) {
2292
- sendZodError(res, err2);
2293
- return;
2294
- }
2295
- throw err2;
2296
- }
2265
+ const body = parseOrSend(res, transferSchema, req.body);
2266
+ if (!body) return;
2297
2267
  const existing = await purchaseStore.getPurchaseByTransactionId(body.transactionId);
2298
2268
  if (!existing) {
2299
2269
  sendError(res, 404, ONESUB_ERROR_CODE.TRANSACTION_NOT_FOUND, "TRANSACTION_NOT_FOUND");
@@ -2315,16 +2285,8 @@ function createAdminRouter(config, purchaseStore, store, webhookQueue) {
2315
2285
  transactionId: z.string().min(1).max(256).optional()
2316
2286
  });
2317
2287
  router.post("/onesub/purchase/admin/grant", async (req, res) => {
2318
- let body;
2319
- try {
2320
- body = grantSchema.parse(req.body);
2321
- } catch (err2) {
2322
- if (err2 instanceof z.ZodError) {
2323
- sendZodError(res, err2);
2324
- return;
2325
- }
2326
- throw err2;
2327
- }
2288
+ const body = parseOrSend(res, grantSchema, req.body);
2289
+ if (!body) return;
2328
2290
  const transactionId = body.transactionId ?? `admin_grant_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
2329
2291
  const purchase = {
2330
2292
  transactionId,
@@ -2356,16 +2318,8 @@ function createAdminRouter(config, purchaseStore, store, webhookQueue) {
2356
2318
  offset: z.coerce.number().int().nonnegative().optional()
2357
2319
  });
2358
2320
  router.get(ROUTES.ADMIN_SUBSCRIPTIONS, async (req, res) => {
2359
- let query;
2360
- try {
2361
- query = listQuerySchema.parse(req.query);
2362
- } catch (err2) {
2363
- if (err2 instanceof z.ZodError) {
2364
- sendZodError(res, err2);
2365
- return;
2366
- }
2367
- throw err2;
2368
- }
2321
+ const query = parseOrSend(res, listQuerySchema, req.query);
2322
+ if (!query) return;
2369
2323
  try {
2370
2324
  const result = await store.listFiltered(query);
2371
2325
  const response = {
@@ -2376,20 +2330,18 @@ function createAdminRouter(config, purchaseStore, store, webhookQueue) {
2376
2330
  };
2377
2331
  res.status(200).json(response);
2378
2332
  } catch (err2) {
2379
- sendError(res, 500, ONESUB_ERROR_CODE.STORE_ERROR, err2.message ?? "list error");
2333
+ log.error("[onesub/admin/subscriptions] list error:", err2);
2334
+ sendError(res, 500, ONESUB_ERROR_CODE.STORE_ERROR, "Internal server error");
2380
2335
  }
2381
2336
  });
2382
2337
  const detailParamsSchema = z.object({
2383
2338
  transactionId: z.string().min(1).max(256)
2384
2339
  });
2385
2340
  router.get("/onesub/admin/subscriptions/:transactionId", async (req, res) => {
2386
- let params;
2387
- try {
2388
- params = detailParamsSchema.parse(req.params);
2389
- } catch {
2390
- sendError(res, 400, ONESUB_ERROR_CODE.INVALID_INPUT, "transactionId required");
2391
- return;
2392
- }
2341
+ const params = parseOrSend(res, detailParamsSchema, req.params, {
2342
+ message: "transactionId required"
2343
+ });
2344
+ if (!params) return;
2393
2345
  try {
2394
2346
  const sub = await store.getByTransactionId(params.transactionId);
2395
2347
  if (!sub) {
@@ -2398,20 +2350,18 @@ function createAdminRouter(config, purchaseStore, store, webhookQueue) {
2398
2350
  }
2399
2351
  res.status(200).json(sub);
2400
2352
  } catch (err2) {
2401
- sendError(res, 500, ONESUB_ERROR_CODE.STORE_ERROR, err2.message ?? "detail error");
2353
+ log.error("[onesub/admin/subscriptions/:transactionId] detail error:", err2);
2354
+ sendError(res, 500, ONESUB_ERROR_CODE.STORE_ERROR, "Internal server error");
2402
2355
  }
2403
2356
  });
2404
2357
  const customerParamsSchema = z.object({
2405
2358
  userId: z.string().min(1).max(256)
2406
2359
  });
2407
2360
  router.get("/onesub/admin/customers/:userId", async (req, res) => {
2408
- let params;
2409
- try {
2410
- params = customerParamsSchema.parse(req.params);
2411
- } catch {
2412
- sendError(res, 400, ONESUB_ERROR_CODE.INVALID_INPUT, "userId required");
2413
- return;
2414
- }
2361
+ const params = parseOrSend(res, customerParamsSchema, req.params, {
2362
+ message: "userId required"
2363
+ });
2364
+ if (!params) return;
2415
2365
  try {
2416
2366
  const [subscriptions, purchases] = await Promise.all([
2417
2367
  store.getAllByUserId(params.userId),
@@ -2433,20 +2383,18 @@ function createAdminRouter(config, purchaseStore, store, webhookQueue) {
2433
2383
  };
2434
2384
  res.status(200).json(response);
2435
2385
  } catch (err2) {
2436
- sendError(res, 500, ONESUB_ERROR_CODE.STORE_ERROR, err2.message ?? "customer error");
2386
+ log.error("[onesub/admin/customers/:userId] error:", err2);
2387
+ sendError(res, 500, ONESUB_ERROR_CODE.STORE_ERROR, "Internal server error");
2437
2388
  }
2438
2389
  });
2439
2390
  const syncAppleParamsSchema = z.object({
2440
2391
  originalTransactionId: z.string().min(1).max(256)
2441
2392
  });
2442
2393
  router.post(ROUTES.ADMIN_SYNC_APPLE, async (req, res) => {
2443
- let params;
2444
- try {
2445
- params = syncAppleParamsSchema.parse(req.params);
2446
- } catch {
2447
- sendError(res, 400, ONESUB_ERROR_CODE.INVALID_INPUT, "originalTransactionId required");
2448
- return;
2449
- }
2394
+ const params = parseOrSend(res, syncAppleParamsSchema, req.params, {
2395
+ message: "originalTransactionId required"
2396
+ });
2397
+ if (!params) return;
2450
2398
  if (!config.apple?.issuerId || !config.apple?.keyId || !config.apple?.privateKey) {
2451
2399
  sendError(res, 400, ONESUB_ERROR_CODE.APPLE_CONFIG_MISSING, "Apple API credentials not configured");
2452
2400
  return;
@@ -2465,7 +2413,8 @@ function createAdminRouter(config, purchaseStore, store, webhookQueue) {
2465
2413
  await store.save(fresh);
2466
2414
  res.status(200).json({ ok: true, subscription: fresh });
2467
2415
  } catch (err2) {
2468
- sendError(res, 500, ONESUB_ERROR_CODE.INTERNAL_ERROR, err2.message ?? "sync error");
2416
+ log.error("[onesub/admin/sync-apple] error:", err2);
2417
+ sendError(res, 500, ONESUB_ERROR_CODE.INTERNAL_ERROR, "Internal server error");
2469
2418
  }
2470
2419
  });
2471
2420
  const overrideParamsSchema = z.object({ userId: z.string().min(1).max(256) });
@@ -2474,19 +2423,10 @@ function createAdminRouter(config, purchaseStore, store, webhookQueue) {
2474
2423
  res.json({ overrides: listTestOverrides() });
2475
2424
  });
2476
2425
  router.put(ROUTES.ADMIN_TEST_OVERRIDE, (req, res) => {
2477
- let params;
2478
- let body;
2479
- try {
2480
- params = overrideParamsSchema.parse(req.params);
2481
- body = overrideBodySchema.parse(req.body);
2482
- } catch (err2) {
2483
- if (err2 instanceof z.ZodError) {
2484
- sendZodError(res, err2);
2485
- } else {
2486
- sendError(res, 400, ONESUB_ERROR_CODE.INVALID_INPUT, "userId and { entitled: boolean } required");
2487
- }
2488
- return;
2489
- }
2426
+ const params = parseOrSend(res, overrideParamsSchema, req.params);
2427
+ if (!params) return;
2428
+ const body = parseOrSend(res, overrideBodySchema, req.body);
2429
+ if (!body) return;
2490
2430
  setTestOverride(params.userId, body.entitled);
2491
2431
  log.warn(
2492
2432
  `[onesub/admin] sandbox test override set for ${params.userId}: entitled=${body.entitled}`
@@ -2494,13 +2434,10 @@ function createAdminRouter(config, purchaseStore, store, webhookQueue) {
2494
2434
  res.json({ ok: true, userId: params.userId, entitled: body.entitled, sandboxOnly: true });
2495
2435
  });
2496
2436
  router.delete(ROUTES.ADMIN_TEST_OVERRIDE, (req, res) => {
2497
- let params;
2498
- try {
2499
- params = overrideParamsSchema.parse(req.params);
2500
- } catch {
2501
- sendError(res, 400, ONESUB_ERROR_CODE.INVALID_INPUT, "userId required");
2502
- return;
2503
- }
2437
+ const params = parseOrSend(res, overrideParamsSchema, req.params, {
2438
+ message: "userId required"
2439
+ });
2440
+ if (!params) return;
2504
2441
  const cleared = clearTestOverride(params.userId);
2505
2442
  res.json({ ok: true, userId: params.userId, cleared });
2506
2443
  });
@@ -2510,25 +2447,22 @@ function createAdminRouter(config, purchaseStore, store, webhookQueue) {
2510
2447
  const items = await webhookQueue.listDeadLetters();
2511
2448
  res.status(200).json({ items });
2512
2449
  } catch (err2) {
2513
- sendError(res, 500, ONESUB_ERROR_CODE.STORE_ERROR, err2.message ?? "list error");
2450
+ log.error("[onesub/admin/webhook-deadletters] error:", err2);
2451
+ sendError(res, 500, ONESUB_ERROR_CODE.STORE_ERROR, "Internal server error");
2514
2452
  }
2515
2453
  });
2516
2454
  }
2517
2455
  if (webhookQueue?.replayDeadLetter) {
2518
2456
  const replayParamsSchema = z.object({ id: z.string().min(1).max(256) });
2519
2457
  router.post("/onesub/admin/webhook-replay/:id", async (req, res) => {
2520
- let params;
2521
- try {
2522
- params = replayParamsSchema.parse(req.params);
2523
- } catch {
2524
- sendError(res, 400, ONESUB_ERROR_CODE.INVALID_INPUT, "id required");
2525
- return;
2526
- }
2458
+ const params = parseOrSend(res, replayParamsSchema, req.params, { message: "id required" });
2459
+ if (!params) return;
2527
2460
  try {
2528
2461
  await webhookQueue.replayDeadLetter(params.id);
2529
2462
  res.status(200).json({ ok: true });
2530
2463
  } catch (err2) {
2531
- sendError(res, 500, ONESUB_ERROR_CODE.STORE_ERROR, err2.message ?? "replay error");
2464
+ log.error("[onesub/admin/webhook-replay] error:", err2);
2465
+ sendError(res, 500, ONESUB_ERROR_CODE.STORE_ERROR, "Internal server error");
2532
2466
  }
2533
2467
  });
2534
2468
  }
@@ -2787,16 +2721,8 @@ function createAppleOfferRouter(config) {
2787
2721
  return;
2788
2722
  }
2789
2723
  }
2790
- let body;
2791
- try {
2792
- body = offerBodySchema.parse(req.body);
2793
- } catch (err2) {
2794
- if (err2 instanceof z.ZodError) {
2795
- sendZodError(res, err2);
2796
- return;
2797
- }
2798
- throw err2;
2799
- }
2724
+ const body = parseOrSend(res, offerBodySchema, req.body);
2725
+ if (!body) return;
2800
2726
  if (!apple.bundleId) {
2801
2727
  sendError(res, 400, ONESUB_ERROR_CODE.APPLE_CONFIG_MISSING, "config.apple.bundleId is required for offer signing");
2802
2728
  return;
@@ -2813,7 +2739,8 @@ function createAppleOfferRouter(config) {
2813
2739
  );
2814
2740
  res.status(200).json(result);
2815
2741
  } catch (err2) {
2816
- sendError(res, 500, ONESUB_ERROR_CODE.INTERNAL_ERROR, err2.message ?? "Offer signing failed");
2742
+ log.error("[onesub/apple/offer] signing error:", err2);
2743
+ sendError(res, 500, ONESUB_ERROR_CODE.INTERNAL_ERROR, "Offer signing failed");
2817
2744
  }
2818
2745
  });
2819
2746
  return router;