@onesub/server 0.18.3 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -0
- package/dist/__tests__/test-overrides.test.d.ts +15 -0
- package/dist/__tests__/test-overrides.test.d.ts.map +1 -0
- package/dist/index.cjs +110 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +110 -1
- package/dist/index.js.map +1 -1
- package/dist/openapi.d.ts.map +1 -1
- package/dist/providers/apple.d.ts.map +1 -1
- package/dist/routes/admin.d.ts.map +1 -1
- package/dist/routes/validate.d.ts.map +1 -1
- package/dist/test-overrides.d.ts +36 -0
- package/dist/test-overrides.d.ts.map +1 -0
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -451,7 +451,10 @@ async function validateAppleReceipt(receipt, config) {
|
|
|
451
451
|
purchasedAt: new Date(purchasedAt).toISOString(),
|
|
452
452
|
willRenew: status === SUBSCRIPTION_STATUS.ACTIVE,
|
|
453
453
|
// refined by renewal info in webhook
|
|
454
|
-
...appAccountToken ? { boundAccountId: appAccountToken } : {}
|
|
454
|
+
...appAccountToken ? { boundAccountId: appAccountToken } : {},
|
|
455
|
+
// Transient, like boundAccountId: the validate route uses it to decide
|
|
456
|
+
// whether a sandbox-only test override may apply, then strips it.
|
|
457
|
+
...tx.environment === "Sandbox" ? { sandbox: true } : {}
|
|
455
458
|
};
|
|
456
459
|
}
|
|
457
460
|
async function validateAppleConsumableReceipt(signedTransaction, config, expectedProductId) {
|
|
@@ -1255,6 +1258,21 @@ function peekAppleBundleId(jws) {
|
|
|
1255
1258
|
}
|
|
1256
1259
|
}
|
|
1257
1260
|
|
|
1261
|
+
// src/test-overrides.ts
|
|
1262
|
+
var overrides = /* @__PURE__ */ new Map();
|
|
1263
|
+
function setTestOverride(userId, entitled) {
|
|
1264
|
+
overrides.set(userId, entitled);
|
|
1265
|
+
}
|
|
1266
|
+
function clearTestOverride(userId) {
|
|
1267
|
+
return overrides.delete(userId);
|
|
1268
|
+
}
|
|
1269
|
+
function getTestOverride(userId) {
|
|
1270
|
+
return overrides.get(userId);
|
|
1271
|
+
}
|
|
1272
|
+
function listTestOverrides() {
|
|
1273
|
+
return [...overrides].map(([userId, entitled]) => ({ userId, entitled }));
|
|
1274
|
+
}
|
|
1275
|
+
|
|
1258
1276
|
// src/routes/validate.ts
|
|
1259
1277
|
var NO_SUB = { valid: false, subscription: null };
|
|
1260
1278
|
var validateSchema = z.object({
|
|
@@ -1322,6 +1340,13 @@ function createValidateRouter(config, store) {
|
|
|
1322
1340
|
);
|
|
1323
1341
|
return;
|
|
1324
1342
|
}
|
|
1343
|
+
const isSandbox = sub.sandbox === true;
|
|
1344
|
+
delete sub.sandbox;
|
|
1345
|
+
if (isSandbox && getTestOverride(userId) === false) {
|
|
1346
|
+
log.warn(`[onesub/validate] sandbox test override active for ${userId}: forcing not-entitled`);
|
|
1347
|
+
sub.status = SUBSCRIPTION_STATUS.EXPIRED;
|
|
1348
|
+
sub.willRenew = false;
|
|
1349
|
+
}
|
|
1325
1350
|
sub.userId = userId;
|
|
1326
1351
|
await store.save(sub);
|
|
1327
1352
|
if (platform === "google" && appConfig.google) {
|
|
@@ -2351,6 +2376,42 @@ function createAdminRouter(config, purchaseStore, store, webhookQueue) {
|
|
|
2351
2376
|
sendError(res, 500, ONESUB_ERROR_CODE.INTERNAL_ERROR, err2.message ?? "sync error");
|
|
2352
2377
|
}
|
|
2353
2378
|
});
|
|
2379
|
+
const overrideParamsSchema = z.object({ userId: z.string().min(1).max(256) });
|
|
2380
|
+
const overrideBodySchema = z.object({ entitled: z.boolean() });
|
|
2381
|
+
router.get(ROUTES.ADMIN_TEST_OVERRIDES, (_req, res) => {
|
|
2382
|
+
res.json({ overrides: listTestOverrides() });
|
|
2383
|
+
});
|
|
2384
|
+
router.put(ROUTES.ADMIN_TEST_OVERRIDE, (req, res) => {
|
|
2385
|
+
let params;
|
|
2386
|
+
let body;
|
|
2387
|
+
try {
|
|
2388
|
+
params = overrideParamsSchema.parse(req.params);
|
|
2389
|
+
body = overrideBodySchema.parse(req.body);
|
|
2390
|
+
} catch (err2) {
|
|
2391
|
+
if (err2 instanceof z.ZodError) {
|
|
2392
|
+
sendZodError(res, err2);
|
|
2393
|
+
} else {
|
|
2394
|
+
sendError(res, 400, ONESUB_ERROR_CODE.INVALID_INPUT, "userId and { entitled: boolean } required");
|
|
2395
|
+
}
|
|
2396
|
+
return;
|
|
2397
|
+
}
|
|
2398
|
+
setTestOverride(params.userId, body.entitled);
|
|
2399
|
+
log.warn(
|
|
2400
|
+
`[onesub/admin] sandbox test override set for ${params.userId}: entitled=${body.entitled}`
|
|
2401
|
+
);
|
|
2402
|
+
res.json({ ok: true, userId: params.userId, entitled: body.entitled, sandboxOnly: true });
|
|
2403
|
+
});
|
|
2404
|
+
router.delete(ROUTES.ADMIN_TEST_OVERRIDE, (req, res) => {
|
|
2405
|
+
let params;
|
|
2406
|
+
try {
|
|
2407
|
+
params = overrideParamsSchema.parse(req.params);
|
|
2408
|
+
} catch {
|
|
2409
|
+
sendError(res, 400, ONESUB_ERROR_CODE.INVALID_INPUT, "userId required");
|
|
2410
|
+
return;
|
|
2411
|
+
}
|
|
2412
|
+
const cleared = clearTestOverride(params.userId);
|
|
2413
|
+
res.json({ ok: true, userId: params.userId, cleared });
|
|
2414
|
+
});
|
|
2354
2415
|
if (webhookQueue?.listDeadLetters) {
|
|
2355
2416
|
router.get("/onesub/admin/webhook-deadletters", async (_req, res) => {
|
|
2356
2417
|
try {
|
|
@@ -3514,6 +3575,54 @@ var ONESUB_OPENAPI = {
|
|
|
3514
3575
|
}
|
|
3515
3576
|
},
|
|
3516
3577
|
// ── admin (mounted when config.adminSecret is set) ───────────────────────
|
|
3578
|
+
[ROUTES.ADMIN_TEST_OVERRIDES]: {
|
|
3579
|
+
get: {
|
|
3580
|
+
summary: "List sandbox-only entitlement overrides (admin).",
|
|
3581
|
+
parameters: [ADMIN_SECRET_PARAM],
|
|
3582
|
+
responses: {
|
|
3583
|
+
200: { description: "OK" },
|
|
3584
|
+
401: err("Invalid admin secret (INVALID_ADMIN_SECRET).")
|
|
3585
|
+
}
|
|
3586
|
+
}
|
|
3587
|
+
},
|
|
3588
|
+
"/onesub/admin/test-overrides/{userId}": {
|
|
3589
|
+
put: {
|
|
3590
|
+
summary: "Force an entitlement verdict for one userId. Honoured ONLY for Sandbox receipts \u2014 a Production receipt ignores it. Exists because Apple cannot cancel a sandbox subscription bought with a real Apple Account, which otherwise locks a tester in.",
|
|
3591
|
+
parameters: [
|
|
3592
|
+
ADMIN_SECRET_PARAM,
|
|
3593
|
+
{ in: "path", name: "userId", required: true, schema: { type: "string" } }
|
|
3594
|
+
],
|
|
3595
|
+
requestBody: {
|
|
3596
|
+
required: true,
|
|
3597
|
+
content: {
|
|
3598
|
+
"application/json": {
|
|
3599
|
+
schema: {
|
|
3600
|
+
type: "object",
|
|
3601
|
+
required: ["entitled"],
|
|
3602
|
+
properties: { entitled: { type: "boolean" } }
|
|
3603
|
+
}
|
|
3604
|
+
}
|
|
3605
|
+
}
|
|
3606
|
+
},
|
|
3607
|
+
responses: {
|
|
3608
|
+
200: { description: "OK" },
|
|
3609
|
+
400: err("Invalid input (INVALID_INPUT)."),
|
|
3610
|
+
401: err("Invalid admin secret (INVALID_ADMIN_SECRET).")
|
|
3611
|
+
}
|
|
3612
|
+
},
|
|
3613
|
+
delete: {
|
|
3614
|
+
summary: "Clear the sandbox entitlement override for one userId (admin).",
|
|
3615
|
+
parameters: [
|
|
3616
|
+
ADMIN_SECRET_PARAM,
|
|
3617
|
+
{ in: "path", name: "userId", required: true, schema: { type: "string" } }
|
|
3618
|
+
],
|
|
3619
|
+
responses: {
|
|
3620
|
+
200: { description: "OK" },
|
|
3621
|
+
400: err("Invalid input (INVALID_INPUT)."),
|
|
3622
|
+
401: err("Invalid admin secret (INVALID_ADMIN_SECRET).")
|
|
3623
|
+
}
|
|
3624
|
+
}
|
|
3625
|
+
},
|
|
3517
3626
|
[ROUTES.ADMIN_SUBSCRIPTIONS]: {
|
|
3518
3627
|
get: {
|
|
3519
3628
|
summary: "Filtered, paginated subscription list (admin).",
|