@onesub/server 0.17.1 → 0.18.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/README.md +60 -1
- package/dist/__tests__/apps.test.d.ts +2 -0
- package/dist/__tests__/apps.test.d.ts.map +1 -0
- package/dist/apps.d.ts +41 -0
- package/dist/apps.d.ts.map +1 -0
- package/dist/index.cjs +145 -44
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +145 -44
- package/dist/index.js.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-apple.d.ts.map +1 -1
- package/dist/routes/webhook-google.d.ts.map +1 -1
- package/dist/stores/redis.d.ts +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1166,23 +1166,95 @@ function sendZodError(res, err2, extra = {}) {
|
|
|
1166
1166
|
);
|
|
1167
1167
|
}
|
|
1168
1168
|
|
|
1169
|
+
// src/apps.ts
|
|
1170
|
+
function appName(app) {
|
|
1171
|
+
return app.id || app.apple?.bundleId || app.google?.packageName || "default";
|
|
1172
|
+
}
|
|
1173
|
+
var registryCache = /* @__PURE__ */ new WeakMap();
|
|
1174
|
+
function getAppRegistry(config) {
|
|
1175
|
+
const cached = registryCache.get(config);
|
|
1176
|
+
if (cached) return cached;
|
|
1177
|
+
const registry = buildAppRegistry(config);
|
|
1178
|
+
registryCache.set(config, registry);
|
|
1179
|
+
return registry;
|
|
1180
|
+
}
|
|
1181
|
+
function buildAppRegistry(config) {
|
|
1182
|
+
const apps = [...config.apps ?? []];
|
|
1183
|
+
if (config.apple || config.google) {
|
|
1184
|
+
const id = config.defaultAppId || config.apple?.bundleId || config.google?.packageName || "default";
|
|
1185
|
+
if (!apps.some((a) => a.id === id)) {
|
|
1186
|
+
apps.unshift({ id, apple: config.apple, google: config.google });
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
const defaultApp = apps.find((a) => a.id === config.defaultAppId) ?? // No explicit default: fall back to the top-level config, which unshifted to
|
|
1190
|
+
// the front above. Never silently pick an arbitrary app when several exist —
|
|
1191
|
+
// that would validate one app's receipt against another's credentials.
|
|
1192
|
+
(config.apple || config.google || apps.length === 1 ? apps[0] : void 0);
|
|
1193
|
+
if (apps.length > 1) {
|
|
1194
|
+
log.info("[onesub] Multi-app mode:", apps.map(appName).join(", "), "| default:", defaultApp ? appName(defaultApp) : "(none)");
|
|
1195
|
+
}
|
|
1196
|
+
function match(hint) {
|
|
1197
|
+
if (hint.appId) {
|
|
1198
|
+
const byId = apps.find(
|
|
1199
|
+
(a) => a.id === hint.appId || a.apple?.bundleId === hint.appId || a.google?.packageName === hint.appId
|
|
1200
|
+
);
|
|
1201
|
+
if (byId) return byId;
|
|
1202
|
+
log.warn("[onesub] Unknown appId:", hint.appId);
|
|
1203
|
+
return void 0;
|
|
1204
|
+
}
|
|
1205
|
+
if (hint.bundleId) {
|
|
1206
|
+
const byBundle = apps.find((a) => a.apple?.bundleId === hint.bundleId);
|
|
1207
|
+
if (byBundle) return byBundle;
|
|
1208
|
+
log.warn("[onesub] No app configured for bundleId:", hint.bundleId);
|
|
1209
|
+
return void 0;
|
|
1210
|
+
}
|
|
1211
|
+
return defaultApp;
|
|
1212
|
+
}
|
|
1213
|
+
return {
|
|
1214
|
+
apps,
|
|
1215
|
+
defaultApp,
|
|
1216
|
+
configFor(hint) {
|
|
1217
|
+
const app = match(hint);
|
|
1218
|
+
if (!app) {
|
|
1219
|
+
return { ...config, apple: void 0, google: void 0 };
|
|
1220
|
+
}
|
|
1221
|
+
return { ...config, apple: app.apple, google: app.google };
|
|
1222
|
+
}
|
|
1223
|
+
};
|
|
1224
|
+
}
|
|
1225
|
+
function peekAppleBundleId(jws) {
|
|
1226
|
+
try {
|
|
1227
|
+
const payload = jws.split(".")[1];
|
|
1228
|
+
if (!payload) return void 0;
|
|
1229
|
+
const json = Buffer.from(payload, "base64url").toString("utf-8");
|
|
1230
|
+
const claims = JSON.parse(json);
|
|
1231
|
+
return typeof claims.bundleId === "string" ? claims.bundleId : void 0;
|
|
1232
|
+
} catch {
|
|
1233
|
+
return void 0;
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
|
|
1169
1237
|
// src/routes/validate.ts
|
|
1170
1238
|
var NO_SUB = { valid: false, subscription: null };
|
|
1171
1239
|
var validateSchema = z.object({
|
|
1172
1240
|
platform: z.enum(["apple", "google"]),
|
|
1173
1241
|
receipt: z.string().min(1).max(1e4),
|
|
1174
1242
|
userId: z.string().min(1).max(256),
|
|
1175
|
-
productId: z.string().min(1).max(256)
|
|
1243
|
+
productId: z.string().min(1).max(256),
|
|
1244
|
+
/** Which app this receipt belongs to. Optional — see OneSubServerConfig.apps. */
|
|
1245
|
+
appId: z.string().min(1).max(256).optional()
|
|
1176
1246
|
});
|
|
1177
1247
|
function createValidateRouter(config, store) {
|
|
1178
1248
|
const router = Router();
|
|
1249
|
+
const registry = getAppRegistry(config);
|
|
1179
1250
|
router.post(ROUTES.VALIDATE, async (req, res) => {
|
|
1180
1251
|
let platform;
|
|
1181
1252
|
let receipt;
|
|
1182
1253
|
let userId;
|
|
1183
1254
|
let productId;
|
|
1255
|
+
let appId;
|
|
1184
1256
|
try {
|
|
1185
|
-
({ platform, receipt, userId, productId } = validateSchema.parse(req.body));
|
|
1257
|
+
({ platform, receipt, userId, productId, appId } = validateSchema.parse(req.body));
|
|
1186
1258
|
} catch (err2) {
|
|
1187
1259
|
if (err2 instanceof z.ZodError) {
|
|
1188
1260
|
sendZodError(res, err2, NO_SUB);
|
|
@@ -1192,18 +1264,22 @@ function createValidateRouter(config, store) {
|
|
|
1192
1264
|
}
|
|
1193
1265
|
try {
|
|
1194
1266
|
let sub = null;
|
|
1267
|
+
const appConfig = registry.configFor({
|
|
1268
|
+
appId,
|
|
1269
|
+
bundleId: platform === "apple" ? peekAppleBundleId(receipt) : void 0
|
|
1270
|
+
});
|
|
1195
1271
|
if (platform === "apple") {
|
|
1196
|
-
if (!
|
|
1272
|
+
if (!appConfig.apple) {
|
|
1197
1273
|
sendError(res, 500, ONESUB_ERROR_CODE.APPLE_CONFIG_MISSING, "Apple configuration not provided", NO_SUB);
|
|
1198
1274
|
return;
|
|
1199
1275
|
}
|
|
1200
|
-
sub = await validateAppleReceipt(receipt,
|
|
1276
|
+
sub = await validateAppleReceipt(receipt, appConfig.apple);
|
|
1201
1277
|
} else {
|
|
1202
|
-
if (!
|
|
1278
|
+
if (!appConfig.google) {
|
|
1203
1279
|
sendError(res, 500, ONESUB_ERROR_CODE.GOOGLE_CONFIG_MISSING, "Google configuration not provided", NO_SUB);
|
|
1204
1280
|
return;
|
|
1205
1281
|
}
|
|
1206
|
-
sub = await validateGoogleReceipt(receipt, productId,
|
|
1282
|
+
sub = await validateGoogleReceipt(receipt, productId, appConfig.google);
|
|
1207
1283
|
}
|
|
1208
1284
|
if (!sub) {
|
|
1209
1285
|
sendError(res, 422, ONESUB_ERROR_CODE.RECEIPT_VALIDATION_FAILED, "Receipt validation failed", NO_SUB);
|
|
@@ -1227,8 +1303,8 @@ function createValidateRouter(config, store) {
|
|
|
1227
1303
|
}
|
|
1228
1304
|
sub.userId = userId;
|
|
1229
1305
|
await store.save(sub);
|
|
1230
|
-
if (platform === "google" &&
|
|
1231
|
-
void acknowledgeGoogleSubscription(receipt, productId,
|
|
1306
|
+
if (platform === "google" && appConfig.google) {
|
|
1307
|
+
void acknowledgeGoogleSubscription(receipt, productId, appConfig.google);
|
|
1232
1308
|
}
|
|
1233
1309
|
const response = { valid: true, subscription: sub };
|
|
1234
1310
|
res.status(200).json(response);
|
|
@@ -1352,11 +1428,12 @@ async function processAppleNotification(work, config, store, purchaseStore) {
|
|
|
1352
1428
|
appAccountToken,
|
|
1353
1429
|
inAppOwnershipType
|
|
1354
1430
|
} = work.decoded;
|
|
1431
|
+
const appleConfigForApp = getAppRegistry(config).configFor({ bundleId }).apple;
|
|
1355
1432
|
const mapped = mapAppleNotificationStatus(notificationType, subtype);
|
|
1356
1433
|
const finalStatus = mapped ?? status;
|
|
1357
|
-
if (notificationType === "CONSUMPTION_REQUEST" &&
|
|
1358
|
-
const provider =
|
|
1359
|
-
const appleConfig =
|
|
1434
|
+
if (notificationType === "CONSUMPTION_REQUEST" && appleConfigForApp?.consumptionInfoProvider && transactionId && productId) {
|
|
1435
|
+
const provider = appleConfigForApp.consumptionInfoProvider;
|
|
1436
|
+
const appleConfig = appleConfigForApp;
|
|
1360
1437
|
void (async () => {
|
|
1361
1438
|
try {
|
|
1362
1439
|
const info = await provider({
|
|
@@ -1405,8 +1482,8 @@ async function processAppleNotification(work, config, store, purchaseStore) {
|
|
|
1405
1482
|
expiresAt: expiresAt ?? existing.expiresAt
|
|
1406
1483
|
};
|
|
1407
1484
|
await store.save(updated);
|
|
1408
|
-
} else if (
|
|
1409
|
-
const fresh = await fetchAppleSubscriptionStatus(originalTransactionId,
|
|
1485
|
+
} else if (appleConfigForApp?.issuerId && appleConfigForApp?.keyId && appleConfigForApp?.privateKey) {
|
|
1486
|
+
const fresh = await fetchAppleSubscriptionStatus(originalTransactionId, appleConfigForApp, {
|
|
1410
1487
|
sandbox: environment === "Sandbox"
|
|
1411
1488
|
});
|
|
1412
1489
|
if (fresh) {
|
|
@@ -1461,8 +1538,9 @@ async function handleAppleWebhook(req, res, config, store, purchaseStore, webhoo
|
|
|
1461
1538
|
res.status(200).json({ received: true });
|
|
1462
1539
|
return;
|
|
1463
1540
|
}
|
|
1464
|
-
|
|
1465
|
-
|
|
1541
|
+
const notifiedApp = getAppRegistry(config).configFor({ bundleId: decoded.bundleId });
|
|
1542
|
+
if (decoded.bundleId && !notifiedApp.apple) {
|
|
1543
|
+
log.warn("[onesub/webhook/apple] No app configured for bundleId:", decoded.bundleId);
|
|
1466
1544
|
sendError(res, 400, ONESUB_ERROR_CODE.BUNDLE_ID_MISMATCH, "Bundle ID mismatch");
|
|
1467
1545
|
return;
|
|
1468
1546
|
}
|
|
@@ -1539,7 +1617,17 @@ var GOOGLE_FAILURE_MESSAGES = {
|
|
|
1539
1617
|
message: "Failed to process notification"
|
|
1540
1618
|
}
|
|
1541
1619
|
};
|
|
1620
|
+
function googleResolver(config) {
|
|
1621
|
+
const registry = getAppRegistry(config);
|
|
1622
|
+
const restricted = registry.apps.some((app) => !!app.google?.packageName);
|
|
1623
|
+
return {
|
|
1624
|
+
registry,
|
|
1625
|
+
serves: (packageName) => !restricted || !!registry.configFor({ appId: packageName }).google,
|
|
1626
|
+
googleFor: (packageName) => restricted ? registry.configFor({ appId: packageName }).google : registry.defaultApp?.google ?? config.google
|
|
1627
|
+
};
|
|
1628
|
+
}
|
|
1542
1629
|
async function processGoogleNotification(work, config, store, purchaseStore) {
|
|
1630
|
+
const { googleFor } = googleResolver(config);
|
|
1543
1631
|
if (work.kind === "voided") {
|
|
1544
1632
|
const { voided } = work;
|
|
1545
1633
|
if (voided.productType === 1) {
|
|
@@ -1562,8 +1650,9 @@ async function processGoogleNotification(work, config, store, purchaseStore) {
|
|
|
1562
1650
|
const { notificationType: notificationType2, purchaseToken: purchaseToken2, sku } = work.oneTimeProduct;
|
|
1563
1651
|
if (notificationType2 === 1) {
|
|
1564
1652
|
log.info("[onesub/webhook/google] oneTimeProduct PURCHASED:", sku);
|
|
1565
|
-
|
|
1566
|
-
|
|
1653
|
+
const googleCfg = googleFor(work.oneTimeProduct.packageName);
|
|
1654
|
+
if (googleCfg?.serviceAccountKey && googleCfg.packageName) {
|
|
1655
|
+
void acknowledgeGoogleProduct(purchaseToken2, sku, googleCfg).catch(
|
|
1567
1656
|
(err2) => log.warn(`[onesub/webhook/google] oneTimeProduct ack failed for SKU ${sku} \u2014 3-day auto-refund risk:`, err2)
|
|
1568
1657
|
);
|
|
1569
1658
|
}
|
|
@@ -1592,14 +1681,15 @@ async function processGoogleNotification(work, config, store, purchaseStore) {
|
|
|
1592
1681
|
} else {
|
|
1593
1682
|
finalStatus = existing?.status ?? SUBSCRIPTION_STATUS.ACTIVE;
|
|
1594
1683
|
}
|
|
1595
|
-
|
|
1596
|
-
|
|
1684
|
+
const subGoogleCfg = googleFor(packageName);
|
|
1685
|
+
if (isGooglePriceChangeConfirmedNotification(notificationType) && subGoogleCfg?.onPriceChangeConfirmed) {
|
|
1686
|
+
const hook = subGoogleCfg.onPriceChangeConfirmed;
|
|
1597
1687
|
void Promise.resolve().then(() => hook({ purchaseToken, subscriptionId, packageName })).catch((err2) => log.warn("[onesub/webhook/google] onPriceChangeConfirmed hook failed:", err2));
|
|
1598
1688
|
}
|
|
1599
1689
|
if (existing) {
|
|
1600
1690
|
let updated = { ...existing, status: finalStatus };
|
|
1601
|
-
if (
|
|
1602
|
-
const fresh = await validateGoogleReceipt(purchaseToken, subscriptionId,
|
|
1691
|
+
if (subGoogleCfg?.serviceAccountKey) {
|
|
1692
|
+
const fresh = await validateGoogleReceipt(purchaseToken, subscriptionId, subGoogleCfg);
|
|
1603
1693
|
if (fresh) {
|
|
1604
1694
|
const preserveNotificationStatus = isGoogleGracePeriodNotification(notificationType) || isGoogleOnHoldNotification(notificationType);
|
|
1605
1695
|
updated = {
|
|
@@ -1614,8 +1704,8 @@ async function processGoogleNotification(work, config, store, purchaseStore) {
|
|
|
1614
1704
|
}
|
|
1615
1705
|
await store.save(updated);
|
|
1616
1706
|
} else {
|
|
1617
|
-
if (
|
|
1618
|
-
const fresh = await validateGoogleReceipt(purchaseToken, subscriptionId,
|
|
1707
|
+
if (subGoogleCfg?.serviceAccountKey) {
|
|
1708
|
+
const fresh = await validateGoogleReceipt(purchaseToken, subscriptionId, subGoogleCfg);
|
|
1619
1709
|
if (fresh) {
|
|
1620
1710
|
const boundAccountId = fresh.boundAccountId;
|
|
1621
1711
|
delete fresh.boundAccountId;
|
|
@@ -1636,12 +1726,15 @@ async function processGoogleNotification(work, config, store, purchaseStore) {
|
|
|
1636
1726
|
}
|
|
1637
1727
|
}
|
|
1638
1728
|
async function handleGoogleWebhook(req, res, config, store, purchaseStore, webhookEventStore, webhookQueue) {
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1729
|
+
const pushIdentities = getAppRegistry(config).apps.map((app) => app.google).filter((g) => !!g?.pushAudience);
|
|
1730
|
+
if (pushIdentities.length > 0) {
|
|
1731
|
+
let authenticated = false;
|
|
1732
|
+
for (const google of pushIdentities) {
|
|
1733
|
+
if (await verifyGooglePushToken(req, google.pushAudience, google.pushServiceAccountEmail)) {
|
|
1734
|
+
authenticated = true;
|
|
1735
|
+
break;
|
|
1736
|
+
}
|
|
1737
|
+
}
|
|
1645
1738
|
if (!authenticated) {
|
|
1646
1739
|
sendError(res, 401, ONESUB_ERROR_CODE.UNAUTHORIZED, "Unauthorized");
|
|
1647
1740
|
return;
|
|
@@ -1663,10 +1756,11 @@ async function handleGoogleWebhook(req, res, config, store, purchaseStore, webho
|
|
|
1663
1756
|
markedEventId = body.message.messageId;
|
|
1664
1757
|
}
|
|
1665
1758
|
let work;
|
|
1759
|
+
const { serves: servesPackage } = googleResolver(config);
|
|
1666
1760
|
const voided = decodeGoogleVoidedNotification(body);
|
|
1667
1761
|
if (voided) {
|
|
1668
|
-
if (
|
|
1669
|
-
log.warn("[onesub/webhook/google] voided package name
|
|
1762
|
+
if (!servesPackage(voided.packageName)) {
|
|
1763
|
+
log.warn("[onesub/webhook/google] voided package name not served:", voided.packageName);
|
|
1670
1764
|
sendError(res, 400, ONESUB_ERROR_CODE.PACKAGE_NAME_MISMATCH, "Package name mismatch");
|
|
1671
1765
|
return;
|
|
1672
1766
|
}
|
|
@@ -1674,8 +1768,8 @@ async function handleGoogleWebhook(req, res, config, store, purchaseStore, webho
|
|
|
1674
1768
|
} else {
|
|
1675
1769
|
const oneTimeProduct = decodeGoogleOneTimeProductNotification(body);
|
|
1676
1770
|
if (oneTimeProduct) {
|
|
1677
|
-
if (
|
|
1678
|
-
log.warn("[onesub/webhook/google] oneTimeProduct package name
|
|
1771
|
+
if (!servesPackage(oneTimeProduct.packageName)) {
|
|
1772
|
+
log.warn("[onesub/webhook/google] oneTimeProduct package name not served:", oneTimeProduct.packageName);
|
|
1679
1773
|
sendError(res, 400, ONESUB_ERROR_CODE.PACKAGE_NAME_MISMATCH, "Package name mismatch");
|
|
1680
1774
|
return;
|
|
1681
1775
|
}
|
|
@@ -1686,8 +1780,8 @@ async function handleGoogleWebhook(req, res, config, store, purchaseStore, webho
|
|
|
1686
1780
|
res.status(200).json({ received: true });
|
|
1687
1781
|
return;
|
|
1688
1782
|
}
|
|
1689
|
-
if (
|
|
1690
|
-
log.warn("[onesub/webhook/google] Package name
|
|
1783
|
+
if (!servesPackage(notification.packageName)) {
|
|
1784
|
+
log.warn("[onesub/webhook/google] Package name not served:", notification.packageName);
|
|
1691
1785
|
sendError(res, 400, ONESUB_ERROR_CODE.PACKAGE_NAME_MISMATCH, "Package name mismatch");
|
|
1692
1786
|
return;
|
|
1693
1787
|
}
|
|
@@ -1750,7 +1844,9 @@ var validatePurchaseSchema = z.object({
|
|
|
1750
1844
|
receipt: z.string().min(1).max(1e4),
|
|
1751
1845
|
userId: z.string().min(1).max(256),
|
|
1752
1846
|
productId: z.string().min(1).max(256),
|
|
1753
|
-
type: z.enum([PURCHASE_TYPE.CONSUMABLE, PURCHASE_TYPE.NON_CONSUMABLE])
|
|
1847
|
+
type: z.enum([PURCHASE_TYPE.CONSUMABLE, PURCHASE_TYPE.NON_CONSUMABLE]),
|
|
1848
|
+
/** Which app this receipt belongs to. Optional — see OneSubServerConfig.apps. */
|
|
1849
|
+
appId: z.string().min(1).max(256).optional()
|
|
1754
1850
|
});
|
|
1755
1851
|
var purchaseStatusQuerySchema = z.object({
|
|
1756
1852
|
userId: z.string().min(1).max(256),
|
|
@@ -1758,6 +1854,7 @@ var purchaseStatusQuerySchema = z.object({
|
|
|
1758
1854
|
});
|
|
1759
1855
|
function createPurchaseRouter(config, purchaseStore) {
|
|
1760
1856
|
const router = Router();
|
|
1857
|
+
const registry = getAppRegistry(config);
|
|
1761
1858
|
router.post(ROUTES.VALIDATE_PURCHASE, async (req, res) => {
|
|
1762
1859
|
let body;
|
|
1763
1860
|
try {
|
|
@@ -1769,7 +1866,11 @@ function createPurchaseRouter(config, purchaseStore) {
|
|
|
1769
1866
|
}
|
|
1770
1867
|
throw err2;
|
|
1771
1868
|
}
|
|
1772
|
-
const { platform, receipt, userId, productId, type } = body;
|
|
1869
|
+
const { platform, receipt, userId, productId, type, appId } = body;
|
|
1870
|
+
const appConfig = registry.configFor({
|
|
1871
|
+
appId,
|
|
1872
|
+
bundleId: platform === "apple" ? peekAppleBundleId(receipt) : void 0
|
|
1873
|
+
});
|
|
1773
1874
|
try {
|
|
1774
1875
|
if (type === PURCHASE_TYPE.NON_CONSUMABLE) {
|
|
1775
1876
|
const owned = (await purchaseStore.getPurchasesByUserId(userId)).find(
|
|
@@ -1789,25 +1890,25 @@ function createPurchaseRouter(config, purchaseStore) {
|
|
|
1789
1890
|
let purchasedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1790
1891
|
let boundAccountId = null;
|
|
1791
1892
|
if (platform === "apple") {
|
|
1792
|
-
if (!
|
|
1893
|
+
if (!appConfig.apple) {
|
|
1793
1894
|
sendError(res, 500, ONESUB_ERROR_CODE.APPLE_CONFIG_MISSING, "Apple configuration not provided", NO_PURCHASE);
|
|
1794
1895
|
return;
|
|
1795
1896
|
}
|
|
1796
|
-
const result = await validateAppleConsumableReceipt(receipt,
|
|
1897
|
+
const result = await validateAppleConsumableReceipt(receipt, appConfig.apple, productId);
|
|
1797
1898
|
if (result) {
|
|
1798
1899
|
transactionId = result.transactionId;
|
|
1799
1900
|
purchasedAt = result.purchasedAt;
|
|
1800
1901
|
boundAccountId = result.appAccountToken ?? null;
|
|
1801
1902
|
}
|
|
1802
1903
|
} else {
|
|
1803
|
-
if (!
|
|
1904
|
+
if (!appConfig.google) {
|
|
1804
1905
|
sendError(res, 500, ONESUB_ERROR_CODE.GOOGLE_CONFIG_MISSING, "Google configuration not provided", NO_PURCHASE);
|
|
1805
1906
|
return;
|
|
1806
1907
|
}
|
|
1807
1908
|
const result = await validateGoogleProductReceipt(
|
|
1808
1909
|
receipt,
|
|
1809
1910
|
productId,
|
|
1810
|
-
|
|
1911
|
+
appConfig.google,
|
|
1811
1912
|
type === PURCHASE_TYPE.CONSUMABLE ? "consumable" : "non_consumable"
|
|
1812
1913
|
);
|
|
1813
1914
|
if (result) {
|
|
@@ -1873,11 +1974,11 @@ function createPurchaseRouter(config, purchaseStore) {
|
|
|
1873
1974
|
quantity: 1
|
|
1874
1975
|
};
|
|
1875
1976
|
await purchaseStore.savePurchase(purchase);
|
|
1876
|
-
if (platform === "google" &&
|
|
1977
|
+
if (platform === "google" && appConfig.google) {
|
|
1877
1978
|
if (type === PURCHASE_TYPE.CONSUMABLE) {
|
|
1878
|
-
void consumeGoogleProductReceipt(receipt, productId,
|
|
1979
|
+
void consumeGoogleProductReceipt(receipt, productId, appConfig.google);
|
|
1879
1980
|
} else {
|
|
1880
|
-
void acknowledgeGoogleProduct(receipt, productId,
|
|
1981
|
+
void acknowledgeGoogleProduct(receipt, productId, appConfig.google);
|
|
1881
1982
|
}
|
|
1882
1983
|
}
|
|
1883
1984
|
const response = {
|