@onesub/server 0.21.0 → 0.21.2
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__/errors.test.d.ts +12 -0
- package/dist/__tests__/errors.test.d.ts.map +1 -0
- package/dist/__tests__/google-webhook-open-warning.test.d.ts +12 -0
- package/dist/__tests__/google-webhook-open-warning.test.d.ts.map +1 -0
- package/dist/errors.d.ts +34 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/index.cjs +95 -152
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +95 -152
- package/dist/index.js.map +1 -1
- package/dist/routes/admin.d.ts.map +1 -1
- package/dist/routes/apple-offer.d.ts.map +1 -1
- package/dist/routes/entitlements.d.ts.map +1 -1
- package/dist/routes/purchase.d.ts.map +1 -1
- package/dist/routes/validate.d.ts.map +1 -1
- package/dist/routes/webhook-google.d.ts +18 -0
- package/dist/routes/webhook-google.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,EAAE,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAC1C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAGzD,OAAO,KAAK,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,EAAE,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAC1C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAGzD,OAAO,KAAK,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAWnE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEvD;;GAEG;AACH,MAAM,WAAW,sBAAuB,SAAQ,kBAAkB;IAChE;;;OAGG;IACH,KAAK,CAAC,EAAE,iBAAiB,CAAC;IAC1B;;;;OAIG;IACH,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B;;;;OAIG;IACH,KAAK,CAAC,EAAE,YAAY,CAAC;IACrB;;;;;;OAMG;IACH,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;IACtC;;;;;;;;;;;;;;;OAeG;IACH,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,sBAAsB,GAAG,MAAM,CAwD7E;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,sBAAsB,GAAG,UAAU,CAAC,OAAO,OAAO,CAAC,CAW7F;AAGD,OAAO,EAAE,yBAAyB,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAC9E,YAAY,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AACnE,OAAO,EAAE,yBAAyB,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AACxF,OAAO,EACL,sBAAsB,EACtB,kBAAkB,EAClB,iBAAiB,EACjB,sBAAsB,GACvB,MAAM,mBAAmB,CAAC;AAG3B,YAAY,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,EAAE,oBAAoB,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAGpF,YAAY,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAAE,yBAAyB,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAC5G,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACrG,OAAO,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAK/E,OAAO,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AACrE,YAAY,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAClE,OAAO,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAC;AACvE,YAAY,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAIpE,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9D,YAAY,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAI/C,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAGxC,OAAO,EACL,oBAAoB,EACpB,4BAA4B,EAC5B,4BAA4B,EAC5B,yBAAyB,GAC1B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAO9D,OAAO,EAAE,mBAAmB,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAC;AAIxF,OAAO,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC"}
|
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
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
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({
|
|
@@ -1745,6 +1744,21 @@ var GOOGLE_FAILURE_MESSAGES = {
|
|
|
1745
1744
|
message: "Failed to process notification"
|
|
1746
1745
|
}
|
|
1747
1746
|
};
|
|
1747
|
+
function warnIfGoogleWebhookOpen(config) {
|
|
1748
|
+
if (process.env["NODE_ENV"] !== "production") return;
|
|
1749
|
+
const apps = getAppRegistry(config).apps;
|
|
1750
|
+
const googleApps = apps.map((a) => a.google).filter((g) => !!g);
|
|
1751
|
+
if (googleApps.length > 0 && !googleApps.some((g) => g.pushAudience)) {
|
|
1752
|
+
log.warn(
|
|
1753
|
+
"[onesub] SECURITY: POST /onesub/webhook/google accepts unauthenticated requests \u2014 no configured app sets google.pushAudience, so the Pub/Sub OIDC token is never verified. A caller who knows a purchaseToken or orderId can cancel a subscription or delete a one-time purchase. Set google.pushAudience (and google.pushServiceAccountEmail)."
|
|
1754
|
+
);
|
|
1755
|
+
}
|
|
1756
|
+
if (!apps.some((a) => a.google?.packageName)) {
|
|
1757
|
+
log.warn(
|
|
1758
|
+
"[onesub] POST /onesub/webhook/google is in open mode \u2014 no configured app declares google.packageName, so notifications for ANY package are accepted. The route is mounted whether or not Google is configured; set google.packageName, or block the path at your proxy if this deployment does not serve Google Play."
|
|
1759
|
+
);
|
|
1760
|
+
}
|
|
1761
|
+
}
|
|
1748
1762
|
function googleResolver(config) {
|
|
1749
1763
|
const registry = getAppRegistry(config);
|
|
1750
1764
|
const restricted = registry.apps.some((app) => !!app.google?.packageName);
|
|
@@ -1991,16 +2005,8 @@ function createPurchaseRouter(config, purchaseStore) {
|
|
|
1991
2005
|
const router = Router();
|
|
1992
2006
|
const registry = getAppRegistry(config);
|
|
1993
2007
|
router.post(ROUTES.VALIDATE_PURCHASE, async (req, res) => {
|
|
1994
|
-
|
|
1995
|
-
|
|
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
|
-
}
|
|
2008
|
+
const body = parseOrSend(res, validatePurchaseSchema, req.body, { extra: NO_PURCHASE });
|
|
2009
|
+
if (!body) return;
|
|
2004
2010
|
const { platform, receipt, userId, productId, type, appId } = body;
|
|
2005
2011
|
const appConfig = registry.configFor({
|
|
2006
2012
|
appId,
|
|
@@ -2126,16 +2132,10 @@ function createPurchaseRouter(config, purchaseStore) {
|
|
|
2126
2132
|
}
|
|
2127
2133
|
});
|
|
2128
2134
|
router.get(ROUTES.PURCHASE_STATUS, async (req, res) => {
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
if (err2 instanceof z.ZodError) {
|
|
2134
|
-
sendZodError(res, err2, { purchases: [] });
|
|
2135
|
-
return;
|
|
2136
|
-
}
|
|
2137
|
-
throw err2;
|
|
2138
|
-
}
|
|
2135
|
+
const query = parseOrSend(res, purchaseStatusQuerySchema, req.query, {
|
|
2136
|
+
extra: { purchases: [] }
|
|
2137
|
+
});
|
|
2138
|
+
if (!query) return;
|
|
2139
2139
|
const { userId, productId } = query;
|
|
2140
2140
|
try {
|
|
2141
2141
|
const purchases = productId !== void 0 ? await purchasesForProduct(purchaseStore, userId, productId) : await purchaseStore.getPurchasesByUserId(userId);
|
|
@@ -2183,6 +2183,8 @@ async function evaluateEntitlement(userId, entitlement, store, purchaseStore) {
|
|
|
2183
2183
|
}
|
|
2184
2184
|
var userIdSchema = z.string().min(1).max(256);
|
|
2185
2185
|
var entitlementIdSchema = z.string().min(1).max(128);
|
|
2186
|
+
var entitlementQuerySchema = z.object({ userId: userIdSchema, id: entitlementIdSchema });
|
|
2187
|
+
var entitlementsQuerySchema = z.object({ userId: userIdSchema });
|
|
2186
2188
|
function createEntitlementRouter(config, store, purchaseStore) {
|
|
2187
2189
|
if (!config.entitlements || Object.keys(config.entitlements).length === 0) {
|
|
2188
2190
|
return null;
|
|
@@ -2190,15 +2192,11 @@ function createEntitlementRouter(config, store, purchaseStore) {
|
|
|
2190
2192
|
const entitlements = config.entitlements;
|
|
2191
2193
|
const router = Router();
|
|
2192
2194
|
router.get(ROUTES.ENTITLEMENT, async (req, res) => {
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
} catch {
|
|
2199
|
-
sendError(res, 400, ONESUB_ERROR_CODE.INVALID_INPUT, "userId and id are required");
|
|
2200
|
-
return;
|
|
2201
|
-
}
|
|
2195
|
+
const query = parseOrSend(res, entitlementQuerySchema, req.query, {
|
|
2196
|
+
message: "userId and id are required"
|
|
2197
|
+
});
|
|
2198
|
+
if (!query) return;
|
|
2199
|
+
const { userId, id } = query;
|
|
2202
2200
|
const entitlement = entitlements[id];
|
|
2203
2201
|
if (!entitlement) {
|
|
2204
2202
|
sendError(res, 404, ONESUB_ERROR_CODE.ENTITLEMENT_NOT_FOUND, `Unknown entitlement: ${id}`);
|
|
@@ -2214,13 +2212,11 @@ function createEntitlementRouter(config, store, purchaseStore) {
|
|
|
2214
2212
|
}
|
|
2215
2213
|
});
|
|
2216
2214
|
router.get(ROUTES.ENTITLEMENTS, async (req, res) => {
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
return;
|
|
2223
|
-
}
|
|
2215
|
+
const query = parseOrSend(res, entitlementsQuerySchema, req.query, {
|
|
2216
|
+
message: "userId is required"
|
|
2217
|
+
});
|
|
2218
|
+
if (!query) return;
|
|
2219
|
+
const { userId } = query;
|
|
2224
2220
|
try {
|
|
2225
2221
|
const [subs, purchases] = await Promise.all([
|
|
2226
2222
|
store.getAllByUserId(userId),
|
|
@@ -2269,13 +2265,10 @@ function createAdminRouter(config, purchaseStore, store, webhookQueue) {
|
|
|
2269
2265
|
productId: z.string().min(1).max(256)
|
|
2270
2266
|
});
|
|
2271
2267
|
router.delete("/onesub/purchase/admin/:userId/:productId", async (req, res) => {
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
sendError(res, 400, ONESUB_ERROR_CODE.INVALID_INPUT, "userId and productId required");
|
|
2277
|
-
return;
|
|
2278
|
-
}
|
|
2268
|
+
const params = parseOrSend(res, resetParamsSchema, req.params, {
|
|
2269
|
+
message: "userId and productId required"
|
|
2270
|
+
});
|
|
2271
|
+
if (!params) return;
|
|
2279
2272
|
const deleted = await purchaseStore.deletePurchases(params.userId, params.productId);
|
|
2280
2273
|
res.json({ ok: true, deleted });
|
|
2281
2274
|
});
|
|
@@ -2284,16 +2277,8 @@ function createAdminRouter(config, purchaseStore, store, webhookQueue) {
|
|
|
2284
2277
|
newUserId: z.string().min(1).max(256)
|
|
2285
2278
|
});
|
|
2286
2279
|
router.post("/onesub/purchase/admin/transfer", async (req, res) => {
|
|
2287
|
-
|
|
2288
|
-
|
|
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
|
-
}
|
|
2280
|
+
const body = parseOrSend(res, transferSchema, req.body);
|
|
2281
|
+
if (!body) return;
|
|
2297
2282
|
const existing = await purchaseStore.getPurchaseByTransactionId(body.transactionId);
|
|
2298
2283
|
if (!existing) {
|
|
2299
2284
|
sendError(res, 404, ONESUB_ERROR_CODE.TRANSACTION_NOT_FOUND, "TRANSACTION_NOT_FOUND");
|
|
@@ -2315,16 +2300,8 @@ function createAdminRouter(config, purchaseStore, store, webhookQueue) {
|
|
|
2315
2300
|
transactionId: z.string().min(1).max(256).optional()
|
|
2316
2301
|
});
|
|
2317
2302
|
router.post("/onesub/purchase/admin/grant", async (req, res) => {
|
|
2318
|
-
|
|
2319
|
-
|
|
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
|
-
}
|
|
2303
|
+
const body = parseOrSend(res, grantSchema, req.body);
|
|
2304
|
+
if (!body) return;
|
|
2328
2305
|
const transactionId = body.transactionId ?? `admin_grant_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
|
|
2329
2306
|
const purchase = {
|
|
2330
2307
|
transactionId,
|
|
@@ -2356,16 +2333,8 @@ function createAdminRouter(config, purchaseStore, store, webhookQueue) {
|
|
|
2356
2333
|
offset: z.coerce.number().int().nonnegative().optional()
|
|
2357
2334
|
});
|
|
2358
2335
|
router.get(ROUTES.ADMIN_SUBSCRIPTIONS, async (req, res) => {
|
|
2359
|
-
|
|
2360
|
-
|
|
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
|
-
}
|
|
2336
|
+
const query = parseOrSend(res, listQuerySchema, req.query);
|
|
2337
|
+
if (!query) return;
|
|
2369
2338
|
try {
|
|
2370
2339
|
const result = await store.listFiltered(query);
|
|
2371
2340
|
const response = {
|
|
@@ -2376,20 +2345,18 @@ function createAdminRouter(config, purchaseStore, store, webhookQueue) {
|
|
|
2376
2345
|
};
|
|
2377
2346
|
res.status(200).json(response);
|
|
2378
2347
|
} catch (err2) {
|
|
2379
|
-
|
|
2348
|
+
log.error("[onesub/admin/subscriptions] list error:", err2);
|
|
2349
|
+
sendError(res, 500, ONESUB_ERROR_CODE.STORE_ERROR, "Internal server error");
|
|
2380
2350
|
}
|
|
2381
2351
|
});
|
|
2382
2352
|
const detailParamsSchema = z.object({
|
|
2383
2353
|
transactionId: z.string().min(1).max(256)
|
|
2384
2354
|
});
|
|
2385
2355
|
router.get("/onesub/admin/subscriptions/:transactionId", async (req, res) => {
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
sendError(res, 400, ONESUB_ERROR_CODE.INVALID_INPUT, "transactionId required");
|
|
2391
|
-
return;
|
|
2392
|
-
}
|
|
2356
|
+
const params = parseOrSend(res, detailParamsSchema, req.params, {
|
|
2357
|
+
message: "transactionId required"
|
|
2358
|
+
});
|
|
2359
|
+
if (!params) return;
|
|
2393
2360
|
try {
|
|
2394
2361
|
const sub = await store.getByTransactionId(params.transactionId);
|
|
2395
2362
|
if (!sub) {
|
|
@@ -2398,20 +2365,18 @@ function createAdminRouter(config, purchaseStore, store, webhookQueue) {
|
|
|
2398
2365
|
}
|
|
2399
2366
|
res.status(200).json(sub);
|
|
2400
2367
|
} catch (err2) {
|
|
2401
|
-
|
|
2368
|
+
log.error("[onesub/admin/subscriptions/:transactionId] detail error:", err2);
|
|
2369
|
+
sendError(res, 500, ONESUB_ERROR_CODE.STORE_ERROR, "Internal server error");
|
|
2402
2370
|
}
|
|
2403
2371
|
});
|
|
2404
2372
|
const customerParamsSchema = z.object({
|
|
2405
2373
|
userId: z.string().min(1).max(256)
|
|
2406
2374
|
});
|
|
2407
2375
|
router.get("/onesub/admin/customers/:userId", async (req, res) => {
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
sendError(res, 400, ONESUB_ERROR_CODE.INVALID_INPUT, "userId required");
|
|
2413
|
-
return;
|
|
2414
|
-
}
|
|
2376
|
+
const params = parseOrSend(res, customerParamsSchema, req.params, {
|
|
2377
|
+
message: "userId required"
|
|
2378
|
+
});
|
|
2379
|
+
if (!params) return;
|
|
2415
2380
|
try {
|
|
2416
2381
|
const [subscriptions, purchases] = await Promise.all([
|
|
2417
2382
|
store.getAllByUserId(params.userId),
|
|
@@ -2433,20 +2398,18 @@ function createAdminRouter(config, purchaseStore, store, webhookQueue) {
|
|
|
2433
2398
|
};
|
|
2434
2399
|
res.status(200).json(response);
|
|
2435
2400
|
} catch (err2) {
|
|
2436
|
-
|
|
2401
|
+
log.error("[onesub/admin/customers/:userId] error:", err2);
|
|
2402
|
+
sendError(res, 500, ONESUB_ERROR_CODE.STORE_ERROR, "Internal server error");
|
|
2437
2403
|
}
|
|
2438
2404
|
});
|
|
2439
2405
|
const syncAppleParamsSchema = z.object({
|
|
2440
2406
|
originalTransactionId: z.string().min(1).max(256)
|
|
2441
2407
|
});
|
|
2442
2408
|
router.post(ROUTES.ADMIN_SYNC_APPLE, async (req, res) => {
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
sendError(res, 400, ONESUB_ERROR_CODE.INVALID_INPUT, "originalTransactionId required");
|
|
2448
|
-
return;
|
|
2449
|
-
}
|
|
2409
|
+
const params = parseOrSend(res, syncAppleParamsSchema, req.params, {
|
|
2410
|
+
message: "originalTransactionId required"
|
|
2411
|
+
});
|
|
2412
|
+
if (!params) return;
|
|
2450
2413
|
if (!config.apple?.issuerId || !config.apple?.keyId || !config.apple?.privateKey) {
|
|
2451
2414
|
sendError(res, 400, ONESUB_ERROR_CODE.APPLE_CONFIG_MISSING, "Apple API credentials not configured");
|
|
2452
2415
|
return;
|
|
@@ -2465,7 +2428,8 @@ function createAdminRouter(config, purchaseStore, store, webhookQueue) {
|
|
|
2465
2428
|
await store.save(fresh);
|
|
2466
2429
|
res.status(200).json({ ok: true, subscription: fresh });
|
|
2467
2430
|
} catch (err2) {
|
|
2468
|
-
|
|
2431
|
+
log.error("[onesub/admin/sync-apple] error:", err2);
|
|
2432
|
+
sendError(res, 500, ONESUB_ERROR_CODE.INTERNAL_ERROR, "Internal server error");
|
|
2469
2433
|
}
|
|
2470
2434
|
});
|
|
2471
2435
|
const overrideParamsSchema = z.object({ userId: z.string().min(1).max(256) });
|
|
@@ -2474,19 +2438,10 @@ function createAdminRouter(config, purchaseStore, store, webhookQueue) {
|
|
|
2474
2438
|
res.json({ overrides: listTestOverrides() });
|
|
2475
2439
|
});
|
|
2476
2440
|
router.put(ROUTES.ADMIN_TEST_OVERRIDE, (req, res) => {
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
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
|
-
}
|
|
2441
|
+
const params = parseOrSend(res, overrideParamsSchema, req.params);
|
|
2442
|
+
if (!params) return;
|
|
2443
|
+
const body = parseOrSend(res, overrideBodySchema, req.body);
|
|
2444
|
+
if (!body) return;
|
|
2490
2445
|
setTestOverride(params.userId, body.entitled);
|
|
2491
2446
|
log.warn(
|
|
2492
2447
|
`[onesub/admin] sandbox test override set for ${params.userId}: entitled=${body.entitled}`
|
|
@@ -2494,13 +2449,10 @@ function createAdminRouter(config, purchaseStore, store, webhookQueue) {
|
|
|
2494
2449
|
res.json({ ok: true, userId: params.userId, entitled: body.entitled, sandboxOnly: true });
|
|
2495
2450
|
});
|
|
2496
2451
|
router.delete(ROUTES.ADMIN_TEST_OVERRIDE, (req, res) => {
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
sendError(res, 400, ONESUB_ERROR_CODE.INVALID_INPUT, "userId required");
|
|
2502
|
-
return;
|
|
2503
|
-
}
|
|
2452
|
+
const params = parseOrSend(res, overrideParamsSchema, req.params, {
|
|
2453
|
+
message: "userId required"
|
|
2454
|
+
});
|
|
2455
|
+
if (!params) return;
|
|
2504
2456
|
const cleared = clearTestOverride(params.userId);
|
|
2505
2457
|
res.json({ ok: true, userId: params.userId, cleared });
|
|
2506
2458
|
});
|
|
@@ -2510,25 +2462,22 @@ function createAdminRouter(config, purchaseStore, store, webhookQueue) {
|
|
|
2510
2462
|
const items = await webhookQueue.listDeadLetters();
|
|
2511
2463
|
res.status(200).json({ items });
|
|
2512
2464
|
} catch (err2) {
|
|
2513
|
-
|
|
2465
|
+
log.error("[onesub/admin/webhook-deadletters] error:", err2);
|
|
2466
|
+
sendError(res, 500, ONESUB_ERROR_CODE.STORE_ERROR, "Internal server error");
|
|
2514
2467
|
}
|
|
2515
2468
|
});
|
|
2516
2469
|
}
|
|
2517
2470
|
if (webhookQueue?.replayDeadLetter) {
|
|
2518
2471
|
const replayParamsSchema = z.object({ id: z.string().min(1).max(256) });
|
|
2519
2472
|
router.post("/onesub/admin/webhook-replay/:id", async (req, res) => {
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
params = replayParamsSchema.parse(req.params);
|
|
2523
|
-
} catch {
|
|
2524
|
-
sendError(res, 400, ONESUB_ERROR_CODE.INVALID_INPUT, "id required");
|
|
2525
|
-
return;
|
|
2526
|
-
}
|
|
2473
|
+
const params = parseOrSend(res, replayParamsSchema, req.params, { message: "id required" });
|
|
2474
|
+
if (!params) return;
|
|
2527
2475
|
try {
|
|
2528
2476
|
await webhookQueue.replayDeadLetter(params.id);
|
|
2529
2477
|
res.status(200).json({ ok: true });
|
|
2530
2478
|
} catch (err2) {
|
|
2531
|
-
|
|
2479
|
+
log.error("[onesub/admin/webhook-replay] error:", err2);
|
|
2480
|
+
sendError(res, 500, ONESUB_ERROR_CODE.STORE_ERROR, "Internal server error");
|
|
2532
2481
|
}
|
|
2533
2482
|
});
|
|
2534
2483
|
}
|
|
@@ -2787,16 +2736,8 @@ function createAppleOfferRouter(config) {
|
|
|
2787
2736
|
return;
|
|
2788
2737
|
}
|
|
2789
2738
|
}
|
|
2790
|
-
|
|
2791
|
-
|
|
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
|
-
}
|
|
2739
|
+
const body = parseOrSend(res, offerBodySchema, req.body);
|
|
2740
|
+
if (!body) return;
|
|
2800
2741
|
if (!apple.bundleId) {
|
|
2801
2742
|
sendError(res, 400, ONESUB_ERROR_CODE.APPLE_CONFIG_MISSING, "config.apple.bundleId is required for offer signing");
|
|
2802
2743
|
return;
|
|
@@ -2813,7 +2754,8 @@ function createAppleOfferRouter(config) {
|
|
|
2813
2754
|
);
|
|
2814
2755
|
res.status(200).json(result);
|
|
2815
2756
|
} catch (err2) {
|
|
2816
|
-
|
|
2757
|
+
log.error("[onesub/apple/offer] signing error:", err2);
|
|
2758
|
+
sendError(res, 500, ONESUB_ERROR_CODE.INTERNAL_ERROR, "Offer signing failed");
|
|
2817
2759
|
}
|
|
2818
2760
|
});
|
|
2819
2761
|
return router;
|
|
@@ -4215,6 +4157,7 @@ function createOneSubMiddleware(config) {
|
|
|
4215
4157
|
"[onesub] apple.mockMode / google.mockMode cannot be enabled when NODE_ENV=production \u2014 these modes accept any receipt as valid."
|
|
4216
4158
|
);
|
|
4217
4159
|
}
|
|
4160
|
+
warnIfGoogleWebhookOpen(config);
|
|
4218
4161
|
const store = config.store ?? new InMemorySubscriptionStore();
|
|
4219
4162
|
const purchaseStore = config.purchaseStore ?? new InMemoryPurchaseStore();
|
|
4220
4163
|
if (config.cache) setDefaultCache(config.cache);
|