@onesub/server 0.13.0 → 0.14.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/dist/__tests__/error-codes.test.js +8 -3
- package/dist/__tests__/error-codes.test.js.map +1 -1
- package/dist/__tests__/redis-store.test.js +58 -9
- package/dist/__tests__/redis-store.test.js.map +1 -1
- package/dist/__tests__/webhook-apple.test.js +247 -0
- package/dist/__tests__/webhook-apple.test.js.map +1 -0
- package/dist/__tests__/webhook-google.test.js +255 -0
- package/dist/__tests__/webhook-google.test.js.map +1 -0
- package/dist/cache.js +0 -4
- package/dist/cache.js.map +1 -1
- package/dist/index.cjs +36 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +36 -7
- package/dist/index.js.map +1 -1
- package/dist/providers/apple.d.ts +8 -0
- package/dist/providers/apple.d.ts.map +1 -1
- package/dist/providers/apple.js +121 -1
- package/dist/providers/apple.js.map +1 -1
- package/dist/providers/google.d.ts +8 -0
- package/dist/providers/google.d.ts.map +1 -1
- package/dist/providers/google.js +42 -0
- package/dist/providers/google.js.map +1 -1
- package/dist/providers/mock.d.ts +2 -0
- package/dist/providers/mock.d.ts.map +1 -1
- package/dist/routes/admin.js +40 -0
- package/dist/routes/admin.js.map +1 -1
- package/dist/routes/apple-offer.js +72 -0
- package/dist/routes/apple-offer.js.map +1 -0
- package/dist/routes/purchase.d.ts.map +1 -1
- package/dist/routes/purchase.js +21 -4
- package/dist/routes/purchase.js.map +1 -1
- package/dist/routes/webhook-apple.js +145 -0
- package/dist/routes/webhook-apple.js.map +1 -0
- package/dist/routes/webhook-google.js +204 -0
- package/dist/routes/webhook-google.js.map +1 -0
- package/dist/routes/webhook.js +9 -451
- package/dist/routes/webhook.js.map +1 -1
- package/dist/stores/redis.js +65 -23
- package/dist/stores/redis.js.map +1 -1
- package/dist/webhook-events.js +9 -8
- package/dist/webhook-events.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -248,10 +248,12 @@ function mockValidateAppleProduct(receipt, expectedProductId) {
|
|
|
248
248
|
const outcome = classifyMockReceipt(receipt);
|
|
249
249
|
if (!outcomePasses(outcome, "apple")) return null;
|
|
250
250
|
const productId = expectedProductId ?? "mock_product";
|
|
251
|
+
const tokenMatch = receipt.match(/#token=([^#]+)/);
|
|
251
252
|
return {
|
|
252
253
|
transactionId: deterministicTransactionId(`mock_apple_${productId}`, receipt),
|
|
253
254
|
productId,
|
|
254
|
-
purchasedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
255
|
+
purchasedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
256
|
+
...tokenMatch ? { appAccountToken: tokenMatch[1] } : {}
|
|
255
257
|
};
|
|
256
258
|
}
|
|
257
259
|
function mockValidateGoogleSubscription(receipt, productId) {
|
|
@@ -273,9 +275,11 @@ function mockValidateGoogleSubscription(receipt, productId) {
|
|
|
273
275
|
function mockValidateGoogleProduct(receipt, productId) {
|
|
274
276
|
const outcome = classifyMockReceipt(receipt);
|
|
275
277
|
if (!outcomePasses(outcome, "google")) return null;
|
|
278
|
+
const tokenMatch = receipt.match(/#token=([^#]+)/);
|
|
276
279
|
return {
|
|
277
280
|
transactionId: deterministicTransactionId(`mock_google_${productId}`, receipt),
|
|
278
|
-
purchasedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
281
|
+
purchasedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
282
|
+
...tokenMatch ? { obfuscatedExternalAccountId: tokenMatch[1] } : {}
|
|
279
283
|
};
|
|
280
284
|
}
|
|
281
285
|
|
|
@@ -439,7 +443,8 @@ async function validateAppleConsumableReceipt(signedTransaction, config, expecte
|
|
|
439
443
|
return {
|
|
440
444
|
transactionId,
|
|
441
445
|
productId: tx.productId,
|
|
442
|
-
purchasedAt: tx.purchaseDate ? new Date(tx.purchaseDate).toISOString() : (/* @__PURE__ */ new Date()).toISOString()
|
|
446
|
+
purchasedAt: tx.purchaseDate ? new Date(tx.purchaseDate).toISOString() : (/* @__PURE__ */ new Date()).toISOString(),
|
|
447
|
+
appAccountToken: tx.appAccountToken ?? void 0
|
|
443
448
|
};
|
|
444
449
|
}
|
|
445
450
|
async function decodeAppleNotification(payload, skipJwsVerification = false) {
|
|
@@ -998,7 +1003,8 @@ async function validateGoogleProductReceipt(purchaseToken, productId, config, ty
|
|
|
998
1003
|
}
|
|
999
1004
|
return {
|
|
1000
1005
|
transactionId: purchase.orderId,
|
|
1001
|
-
purchasedAt: purchase.purchaseTimeMillis ? new Date(parseInt(purchase.purchaseTimeMillis, 10)).toISOString() : (/* @__PURE__ */ new Date()).toISOString()
|
|
1006
|
+
purchasedAt: purchase.purchaseTimeMillis ? new Date(parseInt(purchase.purchaseTimeMillis, 10)).toISOString() : (/* @__PURE__ */ new Date()).toISOString(),
|
|
1007
|
+
obfuscatedExternalAccountId: purchase.obfuscatedExternalAccountId
|
|
1002
1008
|
};
|
|
1003
1009
|
}
|
|
1004
1010
|
function decodeGoogleNotification(payload) {
|
|
@@ -1529,14 +1535,22 @@ function createPurchaseRouter(config, purchaseStore) {
|
|
|
1529
1535
|
const { platform, receipt, userId, productId, type } = body;
|
|
1530
1536
|
try {
|
|
1531
1537
|
if (type === PURCHASE_TYPE.NON_CONSUMABLE) {
|
|
1532
|
-
const
|
|
1533
|
-
|
|
1534
|
-
|
|
1538
|
+
const owned = (await purchaseStore.getPurchasesByUserId(userId)).find(
|
|
1539
|
+
(p) => p.productId === productId
|
|
1540
|
+
);
|
|
1541
|
+
if (owned) {
|
|
1542
|
+
const response2 = {
|
|
1543
|
+
valid: true,
|
|
1544
|
+
purchase: { ...owned, userId },
|
|
1545
|
+
action: "restored"
|
|
1546
|
+
};
|
|
1547
|
+
res.status(200).json(response2);
|
|
1535
1548
|
return;
|
|
1536
1549
|
}
|
|
1537
1550
|
}
|
|
1538
1551
|
let transactionId = null;
|
|
1539
1552
|
let purchasedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1553
|
+
let boundAccountId = null;
|
|
1540
1554
|
if (platform === "apple") {
|
|
1541
1555
|
if (!config.apple) {
|
|
1542
1556
|
sendError(res, 500, ONESUB_ERROR_CODE.APPLE_CONFIG_MISSING, "Apple configuration not provided", NO_PURCHASE);
|
|
@@ -1546,6 +1560,7 @@ function createPurchaseRouter(config, purchaseStore) {
|
|
|
1546
1560
|
if (result) {
|
|
1547
1561
|
transactionId = result.transactionId;
|
|
1548
1562
|
purchasedAt = result.purchasedAt;
|
|
1563
|
+
boundAccountId = result.appAccountToken ?? null;
|
|
1549
1564
|
}
|
|
1550
1565
|
} else {
|
|
1551
1566
|
if (!config.google) {
|
|
@@ -1561,12 +1576,26 @@ function createPurchaseRouter(config, purchaseStore) {
|
|
|
1561
1576
|
if (result) {
|
|
1562
1577
|
transactionId = result.transactionId;
|
|
1563
1578
|
purchasedAt = result.purchasedAt;
|
|
1579
|
+
boundAccountId = result.obfuscatedExternalAccountId ?? null;
|
|
1564
1580
|
}
|
|
1565
1581
|
}
|
|
1566
1582
|
if (!transactionId) {
|
|
1567
1583
|
sendError(res, 422, ONESUB_ERROR_CODE.RECEIPT_VALIDATION_FAILED, "Receipt validation failed", NO_PURCHASE);
|
|
1568
1584
|
return;
|
|
1569
1585
|
}
|
|
1586
|
+
if (boundAccountId && boundAccountId.toLowerCase() !== userId.toLowerCase()) {
|
|
1587
|
+
log.warn(
|
|
1588
|
+
`[onesub/purchase] account binding mismatch for transaction ${transactionId}: token does not match userId ${userId}`
|
|
1589
|
+
);
|
|
1590
|
+
sendError(
|
|
1591
|
+
res,
|
|
1592
|
+
409,
|
|
1593
|
+
ONESUB_ERROR_CODE.TRANSACTION_BELONGS_TO_OTHER_USER,
|
|
1594
|
+
"TRANSACTION_BELONGS_TO_OTHER_USER",
|
|
1595
|
+
NO_PURCHASE
|
|
1596
|
+
);
|
|
1597
|
+
return;
|
|
1598
|
+
}
|
|
1570
1599
|
const existing = await purchaseStore.getPurchaseByTransactionId(transactionId);
|
|
1571
1600
|
let action = "new";
|
|
1572
1601
|
if (existing) {
|