@onesub/server 0.12.0 → 0.12.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/__tests__/webhook-apple.test.d.ts +12 -0
- package/dist/__tests__/webhook-apple.test.d.ts.map +1 -0
- package/dist/__tests__/webhook-google.test.d.ts +13 -0
- package/dist/__tests__/webhook-google.test.d.ts.map +1 -0
- package/dist/index.cjs +258 -288
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +259 -286
- package/dist/index.js.map +1 -1
- package/dist/providers/google.d.ts.map +1 -1
- package/dist/routes/webhook-apple.d.ts +6 -0
- package/dist/routes/webhook-apple.d.ts.map +1 -0
- package/dist/routes/webhook-google.d.ts +7 -0
- package/dist/routes/webhook-google.d.ts.map +1 -0
- package/dist/routes/webhook.d.ts +4 -17
- package/dist/routes/webhook.d.ts.map +1 -1
- package/package.json +2 -2
- package/dist/__tests__/admin-customer-detail.test.js +0 -146
- package/dist/__tests__/admin-customer-detail.test.js.map +0 -1
- package/dist/__tests__/admin-list-subscriptions.test.js +0 -192
- package/dist/__tests__/admin-list-subscriptions.test.js.map +0 -1
- package/dist/__tests__/admin-subscription-detail.test.js +0 -112
- package/dist/__tests__/admin-subscription-detail.test.js.map +0 -1
- package/dist/__tests__/apple-root-ca.test.js +0 -41
- package/dist/__tests__/apple-root-ca.test.js.map +0 -1
- package/dist/__tests__/apple-status-api.test.js +0 -345
- package/dist/__tests__/apple-status-api.test.js.map +0 -1
- package/dist/__tests__/cache.test.js +0 -34
- package/dist/__tests__/cache.test.js.map +0 -1
- package/dist/__tests__/entitlements.test.js +0 -268
- package/dist/__tests__/entitlements.test.js.map +0 -1
- package/dist/__tests__/error-codes.test.js +0 -168
- package/dist/__tests__/error-codes.test.js.map +0 -1
- package/dist/__tests__/fetch-hardening.test.js +0 -192
- package/dist/__tests__/fetch-hardening.test.js.map +0 -1
- package/dist/__tests__/google-paused.test.js +0 -126
- package/dist/__tests__/google-paused.test.js.map +0 -1
- package/dist/__tests__/google-price-change.test.js +0 -186
- package/dist/__tests__/google-price-change.test.js.map +0 -1
- package/dist/__tests__/google-subscriptions-v2.test.js +0 -206
- package/dist/__tests__/google-subscriptions-v2.test.js.map +0 -1
- package/dist/__tests__/lifecycle-scenarios.test.js +0 -579
- package/dist/__tests__/lifecycle-scenarios.test.js.map +0 -1
- package/dist/__tests__/lifecycle-states.test.js +0 -423
- package/dist/__tests__/lifecycle-states.test.js.map +0 -1
- package/dist/__tests__/linked-purchase-token.test.js +0 -227
- package/dist/__tests__/linked-purchase-token.test.js.map +0 -1
- package/dist/__tests__/metrics.test.js +0 -340
- package/dist/__tests__/metrics.test.js.map +0 -1
- package/dist/__tests__/mock-provider.test.js +0 -188
- package/dist/__tests__/mock-provider.test.js.map +0 -1
- package/dist/__tests__/openapi.test.js +0 -32
- package/dist/__tests__/openapi.test.js.map +0 -1
- package/dist/__tests__/paused-auto-resume.test.js +0 -162
- package/dist/__tests__/paused-auto-resume.test.js.map +0 -1
- package/dist/__tests__/providers.test.js +0 -321
- package/dist/__tests__/providers.test.js.map +0 -1
- package/dist/__tests__/redis-store.test.js +0 -117
- package/dist/__tests__/redis-store.test.js.map +0 -1
- package/dist/__tests__/refund-policy.test.js +0 -317
- package/dist/__tests__/refund-policy.test.js.map +0 -1
- package/dist/__tests__/schema.test.js +0 -46
- package/dist/__tests__/schema.test.js.map +0 -1
- package/dist/__tests__/store.test.js +0 -172
- package/dist/__tests__/store.test.js.map +0 -1
- package/dist/__tests__/test-utils.js +0 -30
- package/dist/__tests__/test-utils.js.map +0 -1
- package/dist/__tests__/webhook-events.test.js +0 -36
- package/dist/__tests__/webhook-events.test.js.map +0 -1
- package/dist/__tests__/webhook-queue.test.js +0 -26
- package/dist/__tests__/webhook-queue.test.js.map +0 -1
- package/dist/__tests__/webhook-refund.test.js +0 -313
- package/dist/__tests__/webhook-refund.test.js.map +0 -1
- package/dist/cache.js +0 -43
- package/dist/cache.js.map +0 -1
- package/dist/errors.js +0 -19
- package/dist/errors.js.map +0 -1
- package/dist/http.js +0 -36
- package/dist/http.js.map +0 -1
- package/dist/logger.js +0 -20
- package/dist/logger.js.map +0 -1
- package/dist/openapi.js +0 -196
- package/dist/openapi.js.map +0 -1
- package/dist/providers/apple-root-ca.js +0 -38
- package/dist/providers/apple-root-ca.js.map +0 -1
- package/dist/providers/apple.js +0 -510
- package/dist/providers/apple.js.map +0 -1
- package/dist/providers/google.js +0 -522
- package/dist/providers/google.js.map +0 -1
- package/dist/providers/mock.js +0 -93
- package/dist/providers/mock.js.map +0 -1
- package/dist/routes/admin.js +0 -287
- package/dist/routes/admin.js.map +0 -1
- package/dist/routes/entitlements.js +0 -130
- package/dist/routes/entitlements.js.map +0 -1
- package/dist/routes/metrics.js +0 -281
- package/dist/routes/metrics.js.map +0 -1
- package/dist/routes/purchase.js +0 -187
- package/dist/routes/purchase.js.map +0 -1
- package/dist/routes/status.js +0 -55
- package/dist/routes/status.js.map +0 -1
- package/dist/routes/validate.js +0 -70
- package/dist/routes/validate.js.map +0 -1
- package/dist/routes/webhook.js +0 -461
- package/dist/routes/webhook.js.map +0 -1
- package/dist/store.js +0 -145
- package/dist/store.js.map +0 -1
- package/dist/stores/postgres.js +0 -329
- package/dist/stores/postgres.js.map +0 -1
- package/dist/stores/redis.js +0 -255
- package/dist/stores/redis.js.map +0 -1
- package/dist/stores/schema.js +0 -67
- package/dist/stores/schema.js.map +0 -1
- package/dist/tracing.js +0 -74
- package/dist/tracing.js.map +0 -1
- package/dist/webhook-events.js +0 -57
- package/dist/webhook-events.js.map +0 -1
- package/dist/webhook-queue.js +0 -108
- package/dist/webhook-queue.js.map +0 -1
package/dist/index.d.ts
CHANGED
|
@@ -85,5 +85,4 @@ export { validateAppleReceipt, fetchAppleSubscriptionStatus } from './providers/
|
|
|
85
85
|
export { validateGoogleReceipt } from './providers/google.js';
|
|
86
86
|
export { evaluateEntitlement } from './routes/entitlements.js';
|
|
87
87
|
export { setLogger, log } from './logger.js';
|
|
88
|
-
export default createOneSubMiddleware;
|
|
89
88
|
//# sourceMappingURL=index.d.ts.map
|
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;AASnE,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;;;;;OAKG;IACH,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,sBAAsB,GAAG,MAAM,CA6C7E;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,MAAM,qBAAqB,CAAC;AACxF,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACrG,OAAO,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAI/E,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,EAAE,oBAAoB,EAAE,4BAA4B,EAAE,MAAM,sBAAsB,CAAC;AAC1F,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAI9D,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAI/D,OAAO,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,aAAa,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;AASnE,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;;;;;OAKG;IACH,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,sBAAsB,GAAG,MAAM,CA6C7E;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,MAAM,qBAAqB,CAAC;AACxF,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACrG,OAAO,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAI/E,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,EAAE,oBAAoB,EAAE,4BAA4B,EAAE,MAAM,sBAAsB,CAAC;AAC1F,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAI9D,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAI/D,OAAO,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -723,6 +723,7 @@ async function fetchProductPurchase(packageName, productId, purchaseToken, acces
|
|
|
723
723
|
async function acknowledgeGoogleSubscription(purchaseToken, productId, config) {
|
|
724
724
|
if (config.mockMode) return;
|
|
725
725
|
if (!config.serviceAccountKey) return;
|
|
726
|
+
if (!config.packageName) return;
|
|
726
727
|
let accessToken;
|
|
727
728
|
try {
|
|
728
729
|
accessToken = await getCachedAccessToken(config.serviceAccountKey);
|
|
@@ -748,6 +749,7 @@ async function acknowledgeGoogleSubscription(purchaseToken, productId, config) {
|
|
|
748
749
|
async function acknowledgeGoogleProduct(purchaseToken, productId, config) {
|
|
749
750
|
if (config.mockMode) return;
|
|
750
751
|
if (!config.serviceAccountKey) return;
|
|
752
|
+
if (!config.packageName) return;
|
|
751
753
|
let accessToken;
|
|
752
754
|
try {
|
|
753
755
|
accessToken = await getCachedAccessToken(config.serviceAccountKey);
|
|
@@ -772,6 +774,7 @@ async function acknowledgeGoogleProduct(purchaseToken, productId, config) {
|
|
|
772
774
|
}
|
|
773
775
|
async function consumeGoogleProductReceipt(purchaseToken, productId, config) {
|
|
774
776
|
if (!config.serviceAccountKey) return;
|
|
777
|
+
if (!config.packageName) return;
|
|
775
778
|
let accessToken;
|
|
776
779
|
try {
|
|
777
780
|
accessToken = await getCachedAccessToken(config.serviceAccountKey);
|
|
@@ -819,6 +822,10 @@ async function validateGoogleReceipt(receipt, productId, config) {
|
|
|
819
822
|
log.warn("[onesub/google] No serviceAccountKey provided \u2014 cannot call Play API");
|
|
820
823
|
return null;
|
|
821
824
|
}
|
|
825
|
+
if (!config.packageName) {
|
|
826
|
+
log.warn("[onesub/google] No packageName provided \u2014 cannot call Play API");
|
|
827
|
+
return null;
|
|
828
|
+
}
|
|
822
829
|
let purchase;
|
|
823
830
|
try {
|
|
824
831
|
const token = await getCachedAccessToken(config.serviceAccountKey);
|
|
@@ -871,6 +878,10 @@ async function validateGoogleProductReceipt(purchaseToken, productId, config, ty
|
|
|
871
878
|
log.warn("[onesub/google] No serviceAccountKey \u2014 cannot validate product receipt");
|
|
872
879
|
return null;
|
|
873
880
|
}
|
|
881
|
+
if (!config.packageName) {
|
|
882
|
+
log.warn("[onesub/google] No packageName \u2014 cannot validate product receipt");
|
|
883
|
+
return null;
|
|
884
|
+
}
|
|
874
885
|
let purchase;
|
|
875
886
|
try {
|
|
876
887
|
const token = await getCachedAccessToken(config.serviceAccountKey);
|
|
@@ -1063,41 +1074,9 @@ function createStatusRouter(store) {
|
|
|
1063
1074
|
});
|
|
1064
1075
|
return router;
|
|
1065
1076
|
}
|
|
1066
|
-
var
|
|
1067
|
-
var
|
|
1068
|
-
|
|
1069
|
-
if (!googleJwks) {
|
|
1070
|
-
googleJwks = createRemoteJWKSet(new URL(GOOGLE_JWKS_URL));
|
|
1071
|
-
}
|
|
1072
|
-
return googleJwks;
|
|
1073
|
-
}
|
|
1074
|
-
async function verifyGooglePushToken(req, expectedAudience) {
|
|
1075
|
-
const authHeader = req.headers["authorization"];
|
|
1076
|
-
if (typeof authHeader !== "string" || !authHeader.startsWith("Bearer ")) {
|
|
1077
|
-
return false;
|
|
1078
|
-
}
|
|
1079
|
-
const token = authHeader.slice("Bearer ".length).trim();
|
|
1080
|
-
try {
|
|
1081
|
-
await jwtVerify(token, getGoogleJwks(), { audience: expectedAudience });
|
|
1082
|
-
return true;
|
|
1083
|
-
} catch {
|
|
1084
|
-
return false;
|
|
1085
|
-
}
|
|
1086
|
-
}
|
|
1087
|
-
var APPLE_ACTIVE_TYPES = /* @__PURE__ */ new Set([
|
|
1088
|
-
"SUBSCRIBED",
|
|
1089
|
-
"DID_RENEW",
|
|
1090
|
-
"DID_RECOVER",
|
|
1091
|
-
"OFFER_REDEEMED"
|
|
1092
|
-
]);
|
|
1093
|
-
var APPLE_CANCELED_TYPES = /* @__PURE__ */ new Set([
|
|
1094
|
-
"REVOKE",
|
|
1095
|
-
"REFUND",
|
|
1096
|
-
"CONSUMPTION_REQUEST"
|
|
1097
|
-
]);
|
|
1098
|
-
var APPLE_EXPIRED_TYPES = /* @__PURE__ */ new Set([
|
|
1099
|
-
"EXPIRED"
|
|
1100
|
-
]);
|
|
1077
|
+
var APPLE_ACTIVE_TYPES = /* @__PURE__ */ new Set(["SUBSCRIBED", "DID_RENEW", "DID_RECOVER", "OFFER_REDEEMED"]);
|
|
1078
|
+
var APPLE_CANCELED_TYPES = /* @__PURE__ */ new Set(["REVOKE", "REFUND", "CONSUMPTION_REQUEST"]);
|
|
1079
|
+
var APPLE_EXPIRED_TYPES = /* @__PURE__ */ new Set(["EXPIRED"]);
|
|
1101
1080
|
function mapAppleNotificationStatus(notificationType, subtype) {
|
|
1102
1081
|
if (notificationType === "DID_FAIL_TO_RENEW") {
|
|
1103
1082
|
return subtype === "GRACE_PERIOD" ? SUBSCRIPTION_STATUS.GRACE_PERIOD : SUBSCRIPTION_STATUS.ON_HOLD;
|
|
@@ -1108,283 +1087,278 @@ function mapAppleNotificationStatus(notificationType, subtype) {
|
|
|
1108
1087
|
if (APPLE_ACTIVE_TYPES.has(notificationType)) return SUBSCRIPTION_STATUS.ACTIVE;
|
|
1109
1088
|
return null;
|
|
1110
1089
|
}
|
|
1111
|
-
function
|
|
1112
|
-
const
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
if (
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
log.info("[onesub/webhook/apple] dedupe: already processed", payload.notificationUUID);
|
|
1134
|
-
res.status(200).json({ received: true, deduped: true });
|
|
1135
|
-
return;
|
|
1136
|
-
}
|
|
1137
|
-
}
|
|
1138
|
-
const decoded = await decodeAppleNotification(payload, config.apple?.skipJwsVerification);
|
|
1139
|
-
if (!decoded) {
|
|
1140
|
-
res.status(200).json({ received: true });
|
|
1090
|
+
async function handleAppleWebhook(req, res, config, store, purchaseStore, webhookEventStore) {
|
|
1091
|
+
const body = req.body;
|
|
1092
|
+
if (!body.signedPayload) {
|
|
1093
|
+
sendError(res, 400, ONESUB_ERROR_CODE.MISSING_SIGNED_PAYLOAD, "Missing signedPayload");
|
|
1094
|
+
return;
|
|
1095
|
+
}
|
|
1096
|
+
let payload;
|
|
1097
|
+
try {
|
|
1098
|
+
payload = await decodeJws(
|
|
1099
|
+
body.signedPayload,
|
|
1100
|
+
config.apple?.skipJwsVerification
|
|
1101
|
+
);
|
|
1102
|
+
} catch (err) {
|
|
1103
|
+
log.error("[onesub/webhook/apple] Failed to decode signedPayload:", err);
|
|
1104
|
+
sendError(res, 400, ONESUB_ERROR_CODE.INVALID_SIGNED_PAYLOAD, "Invalid signedPayload");
|
|
1105
|
+
return;
|
|
1106
|
+
}
|
|
1107
|
+
if (webhookEventStore && typeof payload.notificationUUID === "string") {
|
|
1108
|
+
const fresh = await webhookEventStore.markIfNew("apple", payload.notificationUUID);
|
|
1109
|
+
if (!fresh) {
|
|
1110
|
+
log.info("[onesub/webhook/apple] dedupe: already processed", payload.notificationUUID);
|
|
1111
|
+
res.status(200).json({ received: true, deduped: true });
|
|
1141
1112
|
return;
|
|
1142
1113
|
}
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1114
|
+
}
|
|
1115
|
+
const decoded = await decodeAppleNotification(payload, config.apple?.skipJwsVerification);
|
|
1116
|
+
if (!decoded) {
|
|
1117
|
+
res.status(200).json({ received: true });
|
|
1118
|
+
return;
|
|
1119
|
+
}
|
|
1120
|
+
const {
|
|
1121
|
+
originalTransactionId,
|
|
1122
|
+
transactionId,
|
|
1123
|
+
type,
|
|
1124
|
+
productId,
|
|
1125
|
+
bundleId,
|
|
1126
|
+
environment,
|
|
1127
|
+
status,
|
|
1128
|
+
willRenew,
|
|
1129
|
+
expiresAt
|
|
1130
|
+
} = decoded;
|
|
1131
|
+
const notificationType = payload.notificationType;
|
|
1132
|
+
const subtype = payload.subtype;
|
|
1133
|
+
const mapped = mapAppleNotificationStatus(notificationType, subtype);
|
|
1134
|
+
const finalStatus = mapped ?? status;
|
|
1135
|
+
if (notificationType === "CONSUMPTION_REQUEST" && config.apple?.consumptionInfoProvider && transactionId && productId) {
|
|
1136
|
+
const provider = config.apple.consumptionInfoProvider;
|
|
1137
|
+
const appleConfig = config.apple;
|
|
1138
|
+
void (async () => {
|
|
1139
|
+
try {
|
|
1140
|
+
const info = await provider({
|
|
1141
|
+
transactionId,
|
|
1142
|
+
originalTransactionId,
|
|
1143
|
+
productId,
|
|
1144
|
+
bundleId: bundleId ?? appleConfig.bundleId,
|
|
1145
|
+
environment
|
|
1146
|
+
});
|
|
1147
|
+
if (info) {
|
|
1148
|
+
await sendAppleConsumptionResponse(transactionId, info, appleConfig, {
|
|
1149
|
+
sandbox: environment === "Sandbox"
|
|
1169
1150
|
});
|
|
1170
|
-
if (info) {
|
|
1171
|
-
await sendAppleConsumptionResponse(transactionId, info, appleConfig, {
|
|
1172
|
-
sandbox: environment === "Sandbox"
|
|
1173
|
-
});
|
|
1174
|
-
}
|
|
1175
|
-
} catch (err) {
|
|
1176
|
-
log.warn("[onesub/webhook/apple] consumptionInfoProvider failed:", err);
|
|
1177
1151
|
}
|
|
1178
|
-
}
|
|
1179
|
-
|
|
1180
|
-
const isOneTimePurchase = type === "Consumable" || type === "Non-Consumable";
|
|
1181
|
-
const isRefundOrRevoke = APPLE_CANCELED_TYPES.has(notificationType);
|
|
1182
|
-
try {
|
|
1183
|
-
if (isOneTimePurchase && isRefundOrRevoke) {
|
|
1184
|
-
const lookupId = transactionId ?? originalTransactionId;
|
|
1185
|
-
const removed = await purchaseStore.deletePurchaseByTransactionId(lookupId);
|
|
1186
|
-
if (!removed) {
|
|
1187
|
-
log.warn(
|
|
1188
|
-
"[onesub/webhook/apple] IAP refund for unknown transaction:",
|
|
1189
|
-
lookupId
|
|
1190
|
-
);
|
|
1191
|
-
}
|
|
1192
|
-
res.status(200).json({ received: true });
|
|
1193
|
-
return;
|
|
1152
|
+
} catch (err) {
|
|
1153
|
+
log.warn("[onesub/webhook/apple] consumptionInfoProvider failed:", err);
|
|
1194
1154
|
}
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
expiresAt: expiresAt ?? existing.expiresAt
|
|
1206
|
-
};
|
|
1207
|
-
await store.save(updated);
|
|
1208
|
-
} else if (config.apple?.issuerId && config.apple?.keyId && config.apple?.privateKey) {
|
|
1209
|
-
const fresh = await fetchAppleSubscriptionStatus(originalTransactionId, config.apple, {
|
|
1210
|
-
sandbox: environment === "Sandbox"
|
|
1211
|
-
});
|
|
1212
|
-
if (fresh) {
|
|
1213
|
-
fresh.userId = originalTransactionId;
|
|
1214
|
-
await store.save(fresh);
|
|
1215
|
-
} else {
|
|
1216
|
-
log.warn(
|
|
1217
|
-
"[onesub/webhook/apple] Unknown transaction and Status API returned no record:",
|
|
1218
|
-
originalTransactionId
|
|
1219
|
-
);
|
|
1220
|
-
}
|
|
1221
|
-
} else {
|
|
1222
|
-
log.warn(
|
|
1223
|
-
"[onesub/webhook/apple] Received notification for unknown transaction (no API creds to recover):",
|
|
1224
|
-
originalTransactionId
|
|
1225
|
-
);
|
|
1155
|
+
})();
|
|
1156
|
+
}
|
|
1157
|
+
const isOneTimePurchase = type === "Consumable" || type === "Non-Consumable";
|
|
1158
|
+
const isRefundOrRevoke = APPLE_CANCELED_TYPES.has(notificationType);
|
|
1159
|
+
try {
|
|
1160
|
+
if (isOneTimePurchase && isRefundOrRevoke) {
|
|
1161
|
+
const lookupId = transactionId ?? originalTransactionId;
|
|
1162
|
+
const removed = await purchaseStore.deletePurchaseByTransactionId(lookupId);
|
|
1163
|
+
if (!removed) {
|
|
1164
|
+
log.warn("[onesub/webhook/apple] IAP refund for unknown transaction:", lookupId);
|
|
1226
1165
|
}
|
|
1227
1166
|
res.status(200).json({ received: true });
|
|
1228
|
-
} catch (err) {
|
|
1229
|
-
log.error("[onesub/webhook/apple] Store update error:", err);
|
|
1230
|
-
sendError(res, 500, ONESUB_ERROR_CODE.WEBHOOK_PROCESSING_FAILED, "Failed to update subscription");
|
|
1231
|
-
}
|
|
1232
|
-
});
|
|
1233
|
-
router.post(ROUTES.WEBHOOK_GOOGLE, async (req, res) => {
|
|
1234
|
-
if (config.google?.pushAudience) {
|
|
1235
|
-
const authenticated = await verifyGooglePushToken(req, config.google.pushAudience);
|
|
1236
|
-
if (!authenticated) {
|
|
1237
|
-
sendError(res, 401, ONESUB_ERROR_CODE.UNAUTHORIZED, "Unauthorized");
|
|
1238
|
-
return;
|
|
1239
|
-
}
|
|
1240
|
-
}
|
|
1241
|
-
const body = req.body;
|
|
1242
|
-
if (!body.message?.data) {
|
|
1243
|
-
sendError(res, 400, ONESUB_ERROR_CODE.MISSING_MESSAGE_DATA, "Missing message.data");
|
|
1244
1167
|
return;
|
|
1245
1168
|
}
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1169
|
+
const existing = await store.getByTransactionId(originalTransactionId);
|
|
1170
|
+
if (existing) {
|
|
1171
|
+
const isSubscriptionRefund = isRefundOrRevoke && !isOneTimePurchase && notificationType !== "CONSUMPTION_REQUEST";
|
|
1172
|
+
const keepEntitlement = isSubscriptionRefund && config.refundPolicy === "until_expiry";
|
|
1173
|
+
const updated = keepEntitlement ? { ...existing, willRenew: false } : {
|
|
1174
|
+
...existing,
|
|
1175
|
+
status: finalStatus,
|
|
1176
|
+
willRenew,
|
|
1177
|
+
expiresAt: expiresAt ?? existing.expiresAt
|
|
1178
|
+
};
|
|
1179
|
+
await store.save(updated);
|
|
1180
|
+
} else if (config.apple?.issuerId && config.apple?.keyId && config.apple?.privateKey) {
|
|
1181
|
+
const fresh = await fetchAppleSubscriptionStatus(originalTransactionId, config.apple, {
|
|
1182
|
+
sandbox: environment === "Sandbox"
|
|
1183
|
+
});
|
|
1184
|
+
if (fresh) {
|
|
1185
|
+
fresh.userId = originalTransactionId;
|
|
1186
|
+
await store.save(fresh);
|
|
1187
|
+
} else {
|
|
1257
1188
|
log.warn(
|
|
1258
|
-
"[onesub/webhook/
|
|
1259
|
-
|
|
1260
|
-
"!==",
|
|
1261
|
-
config.google.packageName
|
|
1189
|
+
"[onesub/webhook/apple] Unknown transaction and Status API returned no record:",
|
|
1190
|
+
originalTransactionId
|
|
1262
1191
|
);
|
|
1263
|
-
sendError(res, 400, ONESUB_ERROR_CODE.PACKAGE_NAME_MISMATCH, "Package name mismatch");
|
|
1264
|
-
return;
|
|
1265
|
-
}
|
|
1266
|
-
try {
|
|
1267
|
-
if (voided.productType === 1) {
|
|
1268
|
-
const existing = await store.getByTransactionId(voided.purchaseToken);
|
|
1269
|
-
if (existing) {
|
|
1270
|
-
const updated = config.refundPolicy === "until_expiry" ? { ...existing, willRenew: false } : { ...existing, status: SUBSCRIPTION_STATUS.CANCELED };
|
|
1271
|
-
await store.save(updated);
|
|
1272
|
-
} else {
|
|
1273
|
-
log.warn(
|
|
1274
|
-
"[onesub/webhook/google] voided subscription for unknown purchaseToken:",
|
|
1275
|
-
voided.purchaseToken
|
|
1276
|
-
);
|
|
1277
|
-
}
|
|
1278
|
-
} else {
|
|
1279
|
-
const removed = await purchaseStore.deletePurchaseByTransactionId(voided.orderId);
|
|
1280
|
-
if (!removed) {
|
|
1281
|
-
log.warn(
|
|
1282
|
-
"[onesub/webhook/google] voided IAP for unknown orderId:",
|
|
1283
|
-
voided.orderId
|
|
1284
|
-
);
|
|
1285
|
-
}
|
|
1286
|
-
}
|
|
1287
|
-
res.status(200).json({ received: true });
|
|
1288
|
-
} catch (err) {
|
|
1289
|
-
log.error("[onesub/webhook/google] voided notification error:", err);
|
|
1290
|
-
sendError(res, 500, ONESUB_ERROR_CODE.WEBHOOK_PROCESSING_FAILED, "Failed to process voided notification");
|
|
1291
1192
|
}
|
|
1193
|
+
} else {
|
|
1194
|
+
log.warn(
|
|
1195
|
+
"[onesub/webhook/apple] Received notification for unknown transaction (no API creds to recover):",
|
|
1196
|
+
originalTransactionId
|
|
1197
|
+
);
|
|
1198
|
+
}
|
|
1199
|
+
res.status(200).json({ received: true });
|
|
1200
|
+
} catch (err) {
|
|
1201
|
+
log.error("[onesub/webhook/apple] Store update error:", err);
|
|
1202
|
+
sendError(res, 500, ONESUB_ERROR_CODE.WEBHOOK_PROCESSING_FAILED, "Failed to update subscription");
|
|
1203
|
+
}
|
|
1204
|
+
}
|
|
1205
|
+
var GOOGLE_JWKS_URL = "https://www.googleapis.com/oauth2/v3/certs";
|
|
1206
|
+
var googleJwks = null;
|
|
1207
|
+
function getGoogleJwks() {
|
|
1208
|
+
if (!googleJwks) googleJwks = createRemoteJWKSet(new URL(GOOGLE_JWKS_URL));
|
|
1209
|
+
return googleJwks;
|
|
1210
|
+
}
|
|
1211
|
+
async function verifyGooglePushToken(req, expectedAudience) {
|
|
1212
|
+
const authHeader = req.headers["authorization"];
|
|
1213
|
+
if (typeof authHeader !== "string" || !authHeader.startsWith("Bearer ")) return false;
|
|
1214
|
+
try {
|
|
1215
|
+
await jwtVerify(authHeader.slice("Bearer ".length).trim(), getGoogleJwks(), {
|
|
1216
|
+
audience: expectedAudience
|
|
1217
|
+
});
|
|
1218
|
+
return true;
|
|
1219
|
+
} catch {
|
|
1220
|
+
return false;
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1223
|
+
async function handleGoogleWebhook(req, res, config, store, purchaseStore, webhookEventStore) {
|
|
1224
|
+
if (config.google?.pushAudience) {
|
|
1225
|
+
const authenticated = await verifyGooglePushToken(req, config.google.pushAudience);
|
|
1226
|
+
if (!authenticated) {
|
|
1227
|
+
sendError(res, 401, ONESUB_ERROR_CODE.UNAUTHORIZED, "Unauthorized");
|
|
1292
1228
|
return;
|
|
1293
1229
|
}
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1230
|
+
}
|
|
1231
|
+
const body = req.body;
|
|
1232
|
+
if (!body.message?.data) {
|
|
1233
|
+
sendError(res, 400, ONESUB_ERROR_CODE.MISSING_MESSAGE_DATA, "Missing message.data");
|
|
1234
|
+
return;
|
|
1235
|
+
}
|
|
1236
|
+
if (webhookEventStore && typeof body.message.messageId === "string") {
|
|
1237
|
+
const fresh = await webhookEventStore.markIfNew("google", body.message.messageId);
|
|
1238
|
+
if (!fresh) {
|
|
1239
|
+
log.info("[onesub/webhook/google] dedupe: already processed", body.message.messageId);
|
|
1240
|
+
res.status(200).json({ received: true, deduped: true });
|
|
1297
1241
|
return;
|
|
1298
1242
|
}
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
"!==",
|
|
1305
|
-
config.google.packageName
|
|
1306
|
-
);
|
|
1243
|
+
}
|
|
1244
|
+
const voided = decodeGoogleVoidedNotification(body);
|
|
1245
|
+
if (voided) {
|
|
1246
|
+
if (config.google?.packageName && voided.packageName !== config.google.packageName) {
|
|
1247
|
+
log.warn("[onesub/webhook/google] voided package name mismatch:", voided.packageName, "!==", config.google.packageName);
|
|
1307
1248
|
sendError(res, 400, ONESUB_ERROR_CODE.PACKAGE_NAME_MISMATCH, "Package name mismatch");
|
|
1308
1249
|
return;
|
|
1309
1250
|
}
|
|
1310
1251
|
try {
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
} else if (isGooglePausedNotification(notificationType)) {
|
|
1320
|
-
finalStatus = SUBSCRIPTION_STATUS.PAUSED;
|
|
1321
|
-
} else if (isGooglePriceChangeConfirmedNotification(notificationType)) {
|
|
1322
|
-
finalStatus = SUBSCRIPTION_STATUS.ACTIVE;
|
|
1323
|
-
} else if (isGoogleCanceledNotification(notificationType)) {
|
|
1324
|
-
finalStatus = SUBSCRIPTION_STATUS.CANCELED;
|
|
1325
|
-
} else if (isGoogleExpiredNotification(notificationType)) {
|
|
1326
|
-
finalStatus = SUBSCRIPTION_STATUS.EXPIRED;
|
|
1252
|
+
if (voided.productType === 1) {
|
|
1253
|
+
const existing = await store.getByTransactionId(voided.purchaseToken);
|
|
1254
|
+
if (existing) {
|
|
1255
|
+
const updated = config.refundPolicy === "until_expiry" ? { ...existing, willRenew: false } : { ...existing, status: SUBSCRIPTION_STATUS.CANCELED };
|
|
1256
|
+
await store.save(updated);
|
|
1257
|
+
} else {
|
|
1258
|
+
log.warn("[onesub/webhook/google] voided subscription for unknown purchaseToken:", voided.purchaseToken);
|
|
1259
|
+
}
|
|
1327
1260
|
} else {
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
void Promise.resolve().then(() => hook({ purchaseToken, subscriptionId, packageName })).catch((err) => log.warn("[onesub/webhook/google] onPriceChangeConfirmed hook failed:", err));
|
|
1261
|
+
const removed = await purchaseStore.deletePurchaseByTransactionId(voided.orderId);
|
|
1262
|
+
if (!removed) {
|
|
1263
|
+
log.warn("[onesub/webhook/google] voided IAP for unknown orderId:", voided.orderId);
|
|
1264
|
+
}
|
|
1333
1265
|
}
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1266
|
+
res.status(200).json({ received: true });
|
|
1267
|
+
} catch (err) {
|
|
1268
|
+
log.error("[onesub/webhook/google] voided notification error:", err);
|
|
1269
|
+
sendError(res, 500, ONESUB_ERROR_CODE.WEBHOOK_PROCESSING_FAILED, "Failed to process voided notification");
|
|
1270
|
+
}
|
|
1271
|
+
return;
|
|
1272
|
+
}
|
|
1273
|
+
const notification = decodeGoogleNotification(body);
|
|
1274
|
+
if (!notification) {
|
|
1275
|
+
res.status(200).json({ received: true });
|
|
1276
|
+
return;
|
|
1277
|
+
}
|
|
1278
|
+
const { notificationType, purchaseToken, subscriptionId, packageName } = notification;
|
|
1279
|
+
if (config.google?.packageName && packageName !== config.google.packageName) {
|
|
1280
|
+
log.warn("[onesub/webhook/google] Package name mismatch:", packageName, "!==", config.google.packageName);
|
|
1281
|
+
sendError(res, 400, ONESUB_ERROR_CODE.PACKAGE_NAME_MISMATCH, "Package name mismatch");
|
|
1282
|
+
return;
|
|
1283
|
+
}
|
|
1284
|
+
try {
|
|
1285
|
+
const existing = await store.getByTransactionId(purchaseToken);
|
|
1286
|
+
let finalStatus;
|
|
1287
|
+
if (isGoogleActiveNotification(notificationType)) {
|
|
1288
|
+
finalStatus = SUBSCRIPTION_STATUS.ACTIVE;
|
|
1289
|
+
} else if (isGoogleGracePeriodNotification(notificationType)) {
|
|
1290
|
+
finalStatus = SUBSCRIPTION_STATUS.GRACE_PERIOD;
|
|
1291
|
+
} else if (isGoogleOnHoldNotification(notificationType)) {
|
|
1292
|
+
finalStatus = SUBSCRIPTION_STATUS.ON_HOLD;
|
|
1293
|
+
} else if (isGooglePausedNotification(notificationType)) {
|
|
1294
|
+
finalStatus = SUBSCRIPTION_STATUS.PAUSED;
|
|
1295
|
+
} else if (isGooglePriceChangeConfirmedNotification(notificationType)) {
|
|
1296
|
+
finalStatus = SUBSCRIPTION_STATUS.ACTIVE;
|
|
1297
|
+
} else if (isGoogleCanceledNotification(notificationType)) {
|
|
1298
|
+
finalStatus = SUBSCRIPTION_STATUS.CANCELED;
|
|
1299
|
+
} else if (isGoogleExpiredNotification(notificationType)) {
|
|
1300
|
+
finalStatus = SUBSCRIPTION_STATUS.EXPIRED;
|
|
1301
|
+
} else {
|
|
1302
|
+
finalStatus = SUBSCRIPTION_STATUS.ACTIVE;
|
|
1303
|
+
}
|
|
1304
|
+
if (isGooglePriceChangeConfirmedNotification(notificationType) && config.google?.onPriceChangeConfirmed) {
|
|
1305
|
+
const hook = config.google.onPriceChangeConfirmed;
|
|
1306
|
+
void Promise.resolve().then(() => hook({ purchaseToken, subscriptionId, packageName })).catch((err) => log.warn("[onesub/webhook/google] onPriceChangeConfirmed hook failed:", err));
|
|
1307
|
+
}
|
|
1308
|
+
if (existing) {
|
|
1309
|
+
let updated = { ...existing, status: finalStatus };
|
|
1310
|
+
if (config.google?.serviceAccountKey) {
|
|
1311
|
+
const fresh = await validateGoogleReceipt(purchaseToken, subscriptionId, config.google);
|
|
1312
|
+
if (fresh) {
|
|
1313
|
+
const preserveNotificationStatus = finalStatus === SUBSCRIPTION_STATUS.GRACE_PERIOD || finalStatus === SUBSCRIPTION_STATUS.ON_HOLD;
|
|
1314
|
+
updated = {
|
|
1315
|
+
...existing,
|
|
1316
|
+
status: preserveNotificationStatus ? finalStatus : fresh.status,
|
|
1317
|
+
expiresAt: fresh.expiresAt,
|
|
1318
|
+
willRenew: fresh.willRenew,
|
|
1319
|
+
autoResumeTime: fresh.autoResumeTime,
|
|
1320
|
+
linkedPurchaseToken: fresh.linkedPurchaseToken ?? existing.linkedPurchaseToken
|
|
1321
|
+
};
|
|
1354
1322
|
}
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
);
|
|
1367
|
-
} else {
|
|
1368
|
-
fresh.userId = purchaseToken;
|
|
1369
|
-
}
|
|
1370
|
-
} else {
|
|
1371
|
-
fresh.userId = purchaseToken;
|
|
1323
|
+
}
|
|
1324
|
+
await store.save(updated);
|
|
1325
|
+
} else {
|
|
1326
|
+
if (config.google?.serviceAccountKey) {
|
|
1327
|
+
const fresh = await validateGoogleReceipt(purchaseToken, subscriptionId, config.google);
|
|
1328
|
+
if (fresh) {
|
|
1329
|
+
if (fresh.linkedPurchaseToken) {
|
|
1330
|
+
const previous = await store.getByTransactionId(fresh.linkedPurchaseToken);
|
|
1331
|
+
fresh.userId = previous ? previous.userId : purchaseToken;
|
|
1332
|
+
if (previous) {
|
|
1333
|
+
log.info(`[onesub/webhook/google] inherited userId ${previous.userId} from linkedPurchaseToken ${fresh.linkedPurchaseToken} \u2192 new token ${purchaseToken}`);
|
|
1372
1334
|
}
|
|
1373
|
-
|
|
1335
|
+
} else {
|
|
1336
|
+
fresh.userId = purchaseToken;
|
|
1374
1337
|
}
|
|
1375
|
-
|
|
1376
|
-
log.warn(
|
|
1377
|
-
"[onesub/webhook/google] Unknown purchase token and no serviceAccountKey to re-fetch:",
|
|
1378
|
-
purchaseToken
|
|
1379
|
-
);
|
|
1338
|
+
await store.save(fresh);
|
|
1380
1339
|
}
|
|
1340
|
+
} else {
|
|
1341
|
+
log.warn("[onesub/webhook/google] Unknown purchase token and no serviceAccountKey to re-fetch:", purchaseToken);
|
|
1381
1342
|
}
|
|
1382
|
-
res.status(200).json({ received: true });
|
|
1383
|
-
} catch (err) {
|
|
1384
|
-
log.error("[onesub/webhook/google] Error handling notification:", err);
|
|
1385
|
-
sendError(res, 500, ONESUB_ERROR_CODE.WEBHOOK_PROCESSING_FAILED, "Failed to process notification");
|
|
1386
1343
|
}
|
|
1387
|
-
|
|
1344
|
+
res.status(200).json({ received: true });
|
|
1345
|
+
} catch (err) {
|
|
1346
|
+
log.error("[onesub/webhook/google] Error handling notification:", err);
|
|
1347
|
+
sendError(res, 500, ONESUB_ERROR_CODE.WEBHOOK_PROCESSING_FAILED, "Failed to process notification");
|
|
1348
|
+
}
|
|
1349
|
+
}
|
|
1350
|
+
|
|
1351
|
+
// src/routes/webhook.ts
|
|
1352
|
+
function createWebhookRouter(config, store, purchaseStore, webhookEventStore) {
|
|
1353
|
+
const router = Router();
|
|
1354
|
+
router.post(
|
|
1355
|
+
ROUTES.WEBHOOK_APPLE,
|
|
1356
|
+
(req, res) => handleAppleWebhook(req, res, config, store, purchaseStore, webhookEventStore)
|
|
1357
|
+
);
|
|
1358
|
+
router.post(
|
|
1359
|
+
ROUTES.WEBHOOK_GOOGLE,
|
|
1360
|
+
(req, res) => handleGoogleWebhook(req, res, config, store, purchaseStore, webhookEventStore)
|
|
1361
|
+
);
|
|
1388
1362
|
return router;
|
|
1389
1363
|
}
|
|
1390
1364
|
var NO_PURCHASE = { valid: false, purchase: null };
|
|
@@ -3048,7 +3022,6 @@ function createOneSubServer(config) {
|
|
|
3048
3022
|
app.use(createOneSubMiddleware(config));
|
|
3049
3023
|
return app;
|
|
3050
3024
|
}
|
|
3051
|
-
var index_default = createOneSubMiddleware;
|
|
3052
3025
|
var isMain = typeof process !== "undefined" && process.argv[1] != null && new URL(import.meta.url).pathname.endsWith(process.argv[1].replace(/\\/g, "/"));
|
|
3053
3026
|
if (isMain) {
|
|
3054
3027
|
const config = {
|
|
@@ -3074,6 +3047,6 @@ if (isMain) {
|
|
|
3074
3047
|
});
|
|
3075
3048
|
}
|
|
3076
3049
|
|
|
3077
|
-
export { BullMQWebhookQueue, CacheWebhookEventStore, InMemoryCacheAdapter, InMemoryPurchaseStore, InMemorySubscriptionStore, InMemoryWebhookEventStore, InProcessWebhookQueue, ONESUB_OPENAPI, PostgresPurchaseStore, PostgresSubscriptionStore, RedisCacheAdapter, RedisPurchaseStore, RedisSubscriptionStore, RedisWebhookEventStore, createOneSubMiddleware, createOneSubServer,
|
|
3050
|
+
export { BullMQWebhookQueue, CacheWebhookEventStore, InMemoryCacheAdapter, InMemoryPurchaseStore, InMemorySubscriptionStore, InMemoryWebhookEventStore, InProcessWebhookQueue, ONESUB_OPENAPI, PostgresPurchaseStore, PostgresSubscriptionStore, RedisCacheAdapter, RedisPurchaseStore, RedisSubscriptionStore, RedisWebhookEventStore, createOneSubMiddleware, createOneSubServer, evaluateEntitlement, fetchAppleSubscriptionStatus, getDefaultCache, log, openapiHandler, setDefaultCache, setLogger, validateAppleReceipt, validateGoogleReceipt, withSpan };
|
|
3078
3051
|
//# sourceMappingURL=index.js.map
|
|
3079
3052
|
//# sourceMappingURL=index.js.map
|