@misterhomer1992/miit-bot-payment 1.1.7 → 2.0.4
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/config/ConfigurationManager.d.ts +64 -0
- package/dist/config/ConfigurationManager.d.ts.map +1 -0
- package/dist/config/ConfigurationManager.js +144 -0
- package/dist/config/ConfigurationManager.js.map +1 -0
- package/dist/config/defaults.d.ts +18 -0
- package/dist/config/defaults.d.ts.map +1 -0
- package/dist/config/defaults.js +26 -0
- package/dist/config/defaults.js.map +1 -0
- package/dist/config/environment.d.ts +38 -0
- package/dist/config/environment.d.ts.map +1 -0
- package/dist/config/environment.js +91 -0
- package/dist/config/environment.js.map +1 -0
- package/dist/config/index.d.ts +5 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +18 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/types.d.ts +53 -0
- package/dist/config/types.d.ts.map +1 -0
- package/dist/config/types.js +3 -0
- package/dist/config/types.js.map +1 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +23 -0
- package/dist/index.js.map +1 -1
- package/dist/modules/cache/InMemoryCache.d.ts +17 -0
- package/dist/modules/cache/InMemoryCache.d.ts.map +1 -0
- package/dist/modules/cache/InMemoryCache.js +77 -0
- package/dist/modules/cache/InMemoryCache.js.map +1 -0
- package/dist/modules/cache/index.d.ts +3 -0
- package/dist/modules/cache/index.d.ts.map +1 -0
- package/dist/modules/cache/index.js +19 -0
- package/dist/modules/cache/index.js.map +1 -0
- package/dist/modules/cache/types.d.ts +52 -0
- package/dist/modules/cache/types.d.ts.map +1 -0
- package/dist/modules/cache/types.js +3 -0
- package/dist/modules/cache/types.js.map +1 -0
- package/dist/modules/errors/index.d.ts +2 -0
- package/dist/modules/errors/index.d.ts.map +1 -0
- package/dist/modules/errors/index.js +19 -0
- package/dist/modules/errors/index.js.map +1 -0
- package/dist/modules/errors/types.d.ts +112 -0
- package/dist/modules/errors/types.d.ts.map +1 -0
- package/dist/modules/errors/types.js +174 -0
- package/dist/modules/errors/types.js.map +1 -0
- package/dist/modules/payments/api.d.ts +63 -1
- package/dist/modules/payments/api.d.ts.map +1 -1
- package/dist/modules/payments/api.js +103 -1
- package/dist/modules/payments/api.js.map +1 -1
- package/dist/modules/payments/const.d.ts.map +1 -1
- package/dist/modules/payments/const.js +1 -0
- package/dist/modules/payments/const.js.map +1 -1
- package/dist/modules/payments/index.d.ts +8 -0
- package/dist/modules/payments/index.d.ts.map +1 -1
- package/dist/modules/payments/index.js +8 -0
- package/dist/modules/payments/index.js.map +1 -1
- package/dist/modules/payments/service.d.ts +42 -2
- package/dist/modules/payments/service.d.ts.map +1 -1
- package/dist/modules/payments/service.js +132 -3
- package/dist/modules/payments/service.js.map +1 -1
- package/dist/modules/payments/subscription-check-webhook.handler.d.ts +85 -0
- package/dist/modules/payments/subscription-check-webhook.handler.d.ts.map +1 -0
- package/dist/modules/payments/subscription-check-webhook.handler.js +155 -0
- package/dist/modules/payments/subscription-check-webhook.handler.js.map +1 -0
- package/dist/modules/payments/subscription-check-webhook.service.d.ts +59 -0
- package/dist/modules/payments/subscription-check-webhook.service.d.ts.map +1 -0
- package/dist/modules/payments/subscription-check-webhook.service.js +330 -0
- package/dist/modules/payments/subscription-check-webhook.service.js.map +1 -0
- package/dist/modules/payments/subscription-check-webhook.types.d.ts +25 -0
- package/dist/modules/payments/subscription-check-webhook.types.d.ts.map +1 -0
- package/dist/modules/payments/subscription-check-webhook.types.js +3 -0
- package/dist/modules/payments/subscription-check-webhook.types.js.map +1 -0
- package/dist/modules/payments/types.d.ts +69 -2
- package/dist/modules/payments/types.d.ts.map +1 -1
- package/dist/modules/payments/utils.d.ts +151 -5
- package/dist/modules/payments/utils.d.ts.map +1 -1
- package/dist/modules/payments/utils.js +253 -9
- package/dist/modules/payments/utils.js.map +1 -1
- package/dist/modules/payments/wayforpay.service.d.ts +39 -0
- package/dist/modules/payments/wayforpay.service.d.ts.map +1 -0
- package/dist/modules/payments/wayforpay.service.js +217 -0
- package/dist/modules/payments/wayforpay.service.js.map +1 -0
- package/dist/modules/payments/wayforpay.types.d.ts +115 -0
- package/dist/modules/payments/wayforpay.types.d.ts.map +1 -0
- package/dist/modules/payments/wayforpay.types.js +3 -0
- package/dist/modules/payments/wayforpay.types.js.map +1 -0
- package/dist/modules/payments/webhook.handler.d.ts +98 -0
- package/dist/modules/payments/webhook.handler.d.ts.map +1 -0
- package/dist/modules/payments/webhook.handler.js +153 -0
- package/dist/modules/payments/webhook.handler.js.map +1 -0
- package/dist/modules/payments/webhook.service.d.ts +99 -0
- package/dist/modules/payments/webhook.service.d.ts.map +1 -0
- package/dist/modules/payments/webhook.service.js +672 -0
- package/dist/modules/payments/webhook.service.js.map +1 -0
- package/dist/modules/payments/webhook.types.d.ts +35 -0
- package/dist/modules/payments/webhook.types.d.ts.map +1 -0
- package/dist/modules/payments/webhook.types.js +3 -0
- package/dist/modules/payments/webhook.types.js.map +1 -0
- package/dist/modules/subscription/change.service.d.ts +80 -0
- package/dist/modules/subscription/change.service.d.ts.map +1 -0
- package/dist/modules/subscription/change.service.js +226 -0
- package/dist/modules/subscription/change.service.js.map +1 -0
- package/dist/modules/subscription/index.d.ts +2 -0
- package/dist/modules/subscription/index.d.ts.map +1 -1
- package/dist/modules/subscription/index.js +2 -0
- package/dist/modules/subscription/index.js.map +1 -1
- package/dist/modules/subscription/service.d.ts +8 -1
- package/dist/modules/subscription/service.d.ts.map +1 -1
- package/dist/modules/subscription/service.js +58 -1
- package/dist/modules/subscription/service.js.map +1 -1
- package/dist/modules/subscription/status-check.handler.d.ts +117 -0
- package/dist/modules/subscription/status-check.handler.d.ts.map +1 -0
- package/dist/modules/subscription/status-check.handler.js +164 -0
- package/dist/modules/subscription/status-check.handler.js.map +1 -0
- package/dist/modules/subscription/types.d.ts +37 -1
- package/dist/modules/subscription/types.d.ts.map +1 -1
- package/dist/modules/subscriptionPlan/const.d.ts +5 -0
- package/dist/modules/subscriptionPlan/const.d.ts.map +1 -0
- package/dist/modules/subscriptionPlan/const.js +106 -0
- package/dist/modules/subscriptionPlan/const.js.map +1 -0
- package/dist/modules/subscriptionPlan/index.d.ts +5 -0
- package/dist/modules/subscriptionPlan/index.d.ts.map +1 -0
- package/dist/modules/subscriptionPlan/index.js +21 -0
- package/dist/modules/subscriptionPlan/index.js.map +1 -0
- package/dist/modules/subscriptionPlan/repository.d.ts +22 -0
- package/dist/modules/subscriptionPlan/repository.d.ts.map +1 -0
- package/dist/modules/subscriptionPlan/repository.js +95 -0
- package/dist/modules/subscriptionPlan/repository.js.map +1 -0
- package/dist/modules/subscriptionPlan/service.d.ts +21 -0
- package/dist/modules/subscriptionPlan/service.d.ts.map +1 -0
- package/dist/modules/subscriptionPlan/service.js +128 -0
- package/dist/modules/subscriptionPlan/service.js.map +1 -0
- package/dist/modules/subscriptionPlan/types.d.ts +40 -0
- package/dist/modules/subscriptionPlan/types.d.ts.map +1 -0
- package/dist/modules/subscriptionPlan/types.js +3 -0
- package/dist/modules/subscriptionPlan/types.js.map +1 -0
- package/dist/modules/token/const.d.ts +7 -0
- package/dist/modules/token/const.d.ts.map +1 -0
- package/dist/modules/token/const.js +66 -0
- package/dist/modules/token/const.js.map +1 -0
- package/dist/modules/token/index.d.ts +4 -0
- package/dist/modules/token/index.d.ts.map +1 -0
- package/dist/modules/token/index.js +20 -0
- package/dist/modules/token/index.js.map +1 -0
- package/dist/modules/token/service.d.ts +46 -0
- package/dist/modules/token/service.d.ts.map +1 -0
- package/dist/modules/token/service.js +249 -0
- package/dist/modules/token/service.js.map +1 -0
- package/dist/modules/token/types.d.ts +109 -0
- package/dist/modules/token/types.d.ts.map +1 -0
- package/dist/modules/token/types.js +3 -0
- package/dist/modules/token/types.js.map +1 -0
- package/dist/modules/tokenPack/const.d.ts +4 -0
- package/dist/modules/tokenPack/const.d.ts.map +1 -0
- package/dist/modules/tokenPack/const.js +10 -0
- package/dist/modules/tokenPack/const.js.map +1 -0
- package/dist/modules/tokenPack/index.d.ts +5 -0
- package/dist/modules/tokenPack/index.d.ts.map +1 -0
- package/dist/modules/tokenPack/index.js +21 -0
- package/dist/modules/tokenPack/index.js.map +1 -0
- package/dist/modules/tokenPack/repository.d.ts +32 -0
- package/dist/modules/tokenPack/repository.d.ts.map +1 -0
- package/dist/modules/tokenPack/repository.js +103 -0
- package/dist/modules/tokenPack/repository.js.map +1 -0
- package/dist/modules/tokenPack/service.d.ts +28 -0
- package/dist/modules/tokenPack/service.d.ts.map +1 -0
- package/dist/modules/tokenPack/service.js +106 -0
- package/dist/modules/tokenPack/service.js.map +1 -0
- package/dist/modules/tokenPack/types.d.ts +124 -0
- package/dist/modules/tokenPack/types.d.ts.map +1 -0
- package/dist/modules/tokenPack/types.js +3 -0
- package/dist/modules/tokenPack/types.js.map +1 -0
- package/package.json +9 -5
- package/src/config/ConfigurationManager.ts +159 -0
- package/src/config/defaults.ts +27 -0
- package/src/config/environment.ts +94 -0
- package/src/config/index.ts +22 -0
- package/src/config/types.ts +56 -0
- package/src/index.ts +29 -0
- package/src/modules/cache/InMemoryCache.ts +98 -0
- package/src/modules/cache/index.ts +2 -0
- package/src/modules/cache/types.ts +60 -0
- package/src/modules/cancellableAPI/utils.ts +60 -0
- package/src/modules/errors/index.ts +16 -0
- package/src/modules/errors/types.ts +201 -0
- package/src/modules/invoice/const.ts +7 -0
- package/src/modules/invoice/index.ts +4 -0
- package/src/modules/invoice/repository.ts +52 -0
- package/src/modules/invoice/service.ts +44 -0
- package/src/modules/invoice/types.ts +47 -0
- package/src/modules/logger/types.ts +8 -0
- package/src/modules/network/utils.ts +24 -0
- package/src/modules/payments/api.ts +289 -0
- package/src/modules/payments/const.ts +11 -0
- package/src/modules/payments/index.ts +14 -0
- package/src/modules/payments/repository.ts +125 -0
- package/src/modules/payments/service.test.ts +400 -0
- package/src/modules/payments/service.ts +365 -0
- package/src/modules/payments/subscription-check-webhook.handler.integration.test.ts +935 -0
- package/src/modules/payments/subscription-check-webhook.handler.ts +211 -0
- package/src/modules/payments/subscription-check-webhook.service.ts +398 -0
- package/src/modules/payments/subscription-check-webhook.types.ts +29 -0
- package/src/modules/payments/types.ts +193 -0
- package/src/modules/payments/utils.ts +428 -0
- package/src/modules/payments/wayforpay.service.test.ts +375 -0
- package/src/modules/payments/wayforpay.service.ts +284 -0
- package/src/modules/payments/wayforpay.types.ts +138 -0
- package/src/modules/payments/webhook.handler.integration.test.ts +975 -0
- package/src/modules/payments/webhook.handler.ts +219 -0
- package/src/modules/payments/webhook.service.ts +812 -0
- package/src/modules/payments/webhook.types.ts +38 -0
- package/src/modules/subscription/change.service.ts +317 -0
- package/src/modules/subscription/const.ts +9 -0
- package/src/modules/subscription/index.ts +5 -0
- package/src/modules/subscription/repository.ts +277 -0
- package/src/modules/subscription/service.test.ts +665 -0
- package/src/modules/subscription/service.ts +328 -0
- package/src/modules/subscription/status-check.handler.ts +254 -0
- package/src/modules/subscription/types.ts +267 -0
- package/src/modules/subscription/utils.ts +5 -0
- package/src/modules/subscriptionPlan/const.ts +106 -0
- package/src/modules/subscriptionPlan/index.ts +4 -0
- package/src/modules/subscriptionPlan/repository.ts +129 -0
- package/src/modules/subscriptionPlan/service.test.ts +401 -0
- package/src/modules/subscriptionPlan/service.ts +148 -0
- package/src/modules/subscriptionPlan/types.ts +67 -0
- package/src/modules/token/const.ts +64 -0
- package/src/modules/token/index.ts +3 -0
- package/src/modules/token/service.test.ts +499 -0
- package/src/modules/token/service.ts +297 -0
- package/src/modules/token/types.ts +124 -0
- package/src/modules/tokenPack/const.ts +9 -0
- package/src/modules/tokenPack/index.ts +4 -0
- package/src/modules/tokenPack/repository.ts +144 -0
- package/src/modules/tokenPack/service.ts +119 -0
- package/src/modules/tokenPack/types.ts +131 -0
- package/src/modules/user/index.ts +3 -0
- package/src/modules/user/types.ts +143 -0
- package/src/modules/user/userRepository.ts +64 -0
- package/src/modules/user/userService.ts +68 -0
- package/src/types/extend-express.d.ts +16 -0
- package/src/types/function.ts +5 -0
- package/src/types/utilities.ts +22 -0
- package/src/utils.ts +53 -0
- package/tsconfig.json +29 -0
- package/dist/modules/subscription/subscriptionPlan.d.ts +0 -4
- package/dist/modules/subscription/subscriptionPlan.d.ts.map +0 -1
- package/dist/modules/subscription/subscriptionPlan.js +0 -67
- package/dist/modules/subscription/subscriptionPlan.js.map +0 -1
|
@@ -0,0 +1,672 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.WebhookHandlerService = void 0;
|
|
7
|
+
const moment_1 = __importDefault(require("moment"));
|
|
8
|
+
const APPROVED_STATUS = 'Approved';
|
|
9
|
+
const DECLINED_STATUS = 'Declined';
|
|
10
|
+
const EXPIRED_STATUS = 'Expired';
|
|
11
|
+
const wayforpay_service_1 = require("./wayforpay.service");
|
|
12
|
+
const service_1 = require("./service");
|
|
13
|
+
const service_2 = require("../invoice/service");
|
|
14
|
+
const service_3 = require("../subscription/service");
|
|
15
|
+
const service_4 = require("../subscriptionPlan/service");
|
|
16
|
+
const service_5 = require("../tokenPack/service");
|
|
17
|
+
const utils_1 = require("./utils");
|
|
18
|
+
const const_1 = require("../token/const");
|
|
19
|
+
const config_1 = require("../../config");
|
|
20
|
+
/**
|
|
21
|
+
* WebhookHandlerService processes payment confirmation webhooks from WayForPay.
|
|
22
|
+
* It handles signature verification, payment status updates, subscription activation,
|
|
23
|
+
* and user token balance initialization.
|
|
24
|
+
*/
|
|
25
|
+
class WebhookHandlerService {
|
|
26
|
+
constructor({ logger, wayForPayService, paymentService, invoiceService, subscriptionService, subscriptionPlanService, tokenPackService, tokenPackPlans, gracePeriodConfig, }) {
|
|
27
|
+
this.logger = logger;
|
|
28
|
+
this.wayForPayService = wayForPayService || new wayforpay_service_1.WayForPayService({ logger });
|
|
29
|
+
this.paymentService = paymentService || new service_1.PaymentService({ logger });
|
|
30
|
+
this.invoiceService = invoiceService || new service_2.InvoiceService({ logger });
|
|
31
|
+
this.subscriptionService = subscriptionService || new service_3.SubscriptionService({ logger });
|
|
32
|
+
this.subscriptionPlanService = subscriptionPlanService || new service_4.SubscriptionPlanService({ logger });
|
|
33
|
+
this.tokenPackService = tokenPackService || new service_5.TokenPackService({ logger });
|
|
34
|
+
this.tokenPackPlans = tokenPackPlans || const_1.DEFAULT_TOKEN_PACK_PLANS;
|
|
35
|
+
this.gracePeriodConfig = gracePeriodConfig || this.loadGracePeriodConfig();
|
|
36
|
+
}
|
|
37
|
+
loadGracePeriodConfig() {
|
|
38
|
+
try {
|
|
39
|
+
const configManager = config_1.ConfigurationManager.getInstance();
|
|
40
|
+
return configManager.getGracePeriodConfig();
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
return config_1.DEFAULT_GRACE_PERIOD_CONFIG;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Processes a payment confirmation webhook from WayForPay.
|
|
48
|
+
* This method handles the complete webhook flow:
|
|
49
|
+
* 1. Verifies the signature to validate request authenticity
|
|
50
|
+
* 2. Creates an invoice record
|
|
51
|
+
* 3. Updates payment status
|
|
52
|
+
* 4. Activates subscription or renews existing one
|
|
53
|
+
* 5. Initializes user token balance based on plan
|
|
54
|
+
*
|
|
55
|
+
* @param callbackData - The callback data from WayForPay
|
|
56
|
+
* @returns Processing result with response to send back to WayForPay
|
|
57
|
+
*/
|
|
58
|
+
async processPaymentWebhook(callbackData) {
|
|
59
|
+
const { orderReference, transactionStatus } = callbackData;
|
|
60
|
+
this.logger.info({
|
|
61
|
+
message: 'Processing WayForPay webhook',
|
|
62
|
+
payload: {
|
|
63
|
+
orderReference,
|
|
64
|
+
transactionStatus,
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
try {
|
|
68
|
+
// Step 1: Verify signature to validate request authenticity
|
|
69
|
+
const signatureResult = this.wayForPayService.verifyCallbackSignature(callbackData);
|
|
70
|
+
if (!signatureResult.isValid) {
|
|
71
|
+
this.logger.warning({
|
|
72
|
+
message: 'Invalid webhook signature',
|
|
73
|
+
payload: {
|
|
74
|
+
orderReference,
|
|
75
|
+
error: signatureResult.error,
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
return {
|
|
79
|
+
success: false,
|
|
80
|
+
response: this.wayForPayService.buildWebhookResponse(orderReference, 'decline'),
|
|
81
|
+
error: signatureResult.error || 'Invalid signature',
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
// Step 2: Check if this is an approved transaction
|
|
85
|
+
if (transactionStatus !== APPROVED_STATUS) {
|
|
86
|
+
this.logger.info({
|
|
87
|
+
message: 'Webhook received for non-approved transaction',
|
|
88
|
+
payload: {
|
|
89
|
+
orderReference,
|
|
90
|
+
transactionStatus,
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
// Handle declined or other statuses
|
|
94
|
+
if (transactionStatus === DECLINED_STATUS || transactionStatus === EXPIRED_STATUS) {
|
|
95
|
+
await this.handleFailedPayment(callbackData);
|
|
96
|
+
}
|
|
97
|
+
return {
|
|
98
|
+
success: true,
|
|
99
|
+
response: this.wayForPayService.buildWebhookResponse(orderReference, 'accept'),
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
// Step 3: Process based on payment type
|
|
103
|
+
if ((0, utils_1.isUpgradeOrderReference)(orderReference)) {
|
|
104
|
+
// Process upgrade payment
|
|
105
|
+
await this.processApprovedUpgradePayment(callbackData);
|
|
106
|
+
}
|
|
107
|
+
else if ((0, utils_1.isSubscriptionOrderReference)(orderReference)) {
|
|
108
|
+
// Process subscription payment
|
|
109
|
+
await this.processApprovedSubscriptionPayment(callbackData);
|
|
110
|
+
}
|
|
111
|
+
else if ((0, utils_1.isTokenPackOrderReference)(orderReference)) {
|
|
112
|
+
// Process token pack payment
|
|
113
|
+
await this.processApprovedTokenPackPayment(callbackData);
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
this.logger.info({
|
|
117
|
+
message: 'Webhook received for unknown payment type',
|
|
118
|
+
payload: { orderReference },
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
return {
|
|
122
|
+
success: true,
|
|
123
|
+
response: this.wayForPayService.buildWebhookResponse(orderReference, 'accept'),
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
catch (error) {
|
|
127
|
+
this.logger.error({
|
|
128
|
+
message: 'Error processing webhook',
|
|
129
|
+
payload: {
|
|
130
|
+
orderReference,
|
|
131
|
+
error: JSON.stringify(error),
|
|
132
|
+
},
|
|
133
|
+
});
|
|
134
|
+
return {
|
|
135
|
+
success: false,
|
|
136
|
+
response: this.wayForPayService.buildWebhookResponse(orderReference, 'decline'),
|
|
137
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Processes an approved subscription payment.
|
|
143
|
+
* Creates invoice, updates payment status, activates subscription,
|
|
144
|
+
* and initializes token balance.
|
|
145
|
+
*/
|
|
146
|
+
async processApprovedSubscriptionPayment(callbackData) {
|
|
147
|
+
const { orderReference, merchantAccount, merchantSignature, reasonCode, reason, amount, currency } = callbackData;
|
|
148
|
+
// Parse order reference to extract user and plan info
|
|
149
|
+
const parsedReference = (0, utils_1.parseOrderReference)(orderReference);
|
|
150
|
+
if (!parsedReference) {
|
|
151
|
+
throw new Error(`Invalid order reference format: ${orderReference}`);
|
|
152
|
+
}
|
|
153
|
+
const userId = (0, utils_1.getOrderReferenceUserId)(orderReference);
|
|
154
|
+
const planId = (0, utils_1.getOrderReferenceItemId)(orderReference);
|
|
155
|
+
const { platform } = parsedReference;
|
|
156
|
+
if (!userId || !planId) {
|
|
157
|
+
throw new Error(`Could not extract userId or planId from order reference: ${orderReference}`);
|
|
158
|
+
}
|
|
159
|
+
// Check for duplicate webhook (idempotency)
|
|
160
|
+
const existingInvoice = await this.invoiceService.getByOrderReference(orderReference);
|
|
161
|
+
if (existingInvoice) {
|
|
162
|
+
this.logger.info({
|
|
163
|
+
message: 'Duplicate webhook received, already processed',
|
|
164
|
+
payload: { orderReference },
|
|
165
|
+
});
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
// Create invoice record
|
|
169
|
+
// reasonCode from WayForPay is a string like '1100', convert to number for storage
|
|
170
|
+
const reasonCodeNumber = typeof reasonCode === 'string' ? parseInt(reasonCode, 10) : Number(reasonCode);
|
|
171
|
+
const invoice = await this.invoiceService.create({
|
|
172
|
+
merchantAccount,
|
|
173
|
+
orderReference,
|
|
174
|
+
merchantSignature,
|
|
175
|
+
reasonCode: reasonCodeNumber,
|
|
176
|
+
reason,
|
|
177
|
+
createdDate: moment_1.default.unix(callbackData.createdDate).toISOString(),
|
|
178
|
+
processingDate: moment_1.default.unix(callbackData.processingDate).toISOString(),
|
|
179
|
+
currency,
|
|
180
|
+
amount,
|
|
181
|
+
});
|
|
182
|
+
this.logger.info({
|
|
183
|
+
message: 'Invoice created',
|
|
184
|
+
payload: { invoiceId: invoice.id, orderReference },
|
|
185
|
+
});
|
|
186
|
+
// Update payment status to completed
|
|
187
|
+
const payment = await this.paymentService.getByOrderReference(orderReference);
|
|
188
|
+
if (payment && payment.id) {
|
|
189
|
+
await this.paymentService.changeStatus({
|
|
190
|
+
id: payment.id,
|
|
191
|
+
status: 'completed',
|
|
192
|
+
});
|
|
193
|
+
this.logger.info({
|
|
194
|
+
message: 'Payment status updated to completed',
|
|
195
|
+
payload: { paymentId: payment.id, orderReference },
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
// Get subscription plan to calculate expiration and tokens
|
|
199
|
+
const subscriptionPlan = await this.subscriptionPlanService.getById(planId);
|
|
200
|
+
if (!subscriptionPlan) {
|
|
201
|
+
throw new Error(`Subscription plan not found: ${planId}`);
|
|
202
|
+
}
|
|
203
|
+
// Calculate subscription expiration date
|
|
204
|
+
const startedAt = moment_1.default.utc().toISOString();
|
|
205
|
+
const expiresAt = this.calculateExpirationDate(subscriptionPlan.regularMode, subscriptionPlan.count);
|
|
206
|
+
// Check for existing active subscription
|
|
207
|
+
const existingSubscription = await this.subscriptionService.getByUser({
|
|
208
|
+
userId,
|
|
209
|
+
platform,
|
|
210
|
+
status: 'active',
|
|
211
|
+
});
|
|
212
|
+
if (existingSubscription) {
|
|
213
|
+
// Renew existing subscription
|
|
214
|
+
await this.renewSubscription(existingSubscription, expiresAt);
|
|
215
|
+
}
|
|
216
|
+
else {
|
|
217
|
+
// Activate new subscription
|
|
218
|
+
await this.activateNewSubscription({
|
|
219
|
+
userId,
|
|
220
|
+
platform,
|
|
221
|
+
planId,
|
|
222
|
+
startedAt,
|
|
223
|
+
expiresAt,
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
// Initialize user token balance based on plan credits
|
|
227
|
+
await this.initializeTokenBalance({
|
|
228
|
+
userId,
|
|
229
|
+
platform,
|
|
230
|
+
planId,
|
|
231
|
+
tokens: subscriptionPlan.credits,
|
|
232
|
+
});
|
|
233
|
+
this.logger.info({
|
|
234
|
+
message: 'Subscription activated successfully',
|
|
235
|
+
payload: {
|
|
236
|
+
userId,
|
|
237
|
+
platform,
|
|
238
|
+
planId,
|
|
239
|
+
expiresAt,
|
|
240
|
+
tokens: subscriptionPlan.credits,
|
|
241
|
+
},
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Processes an approved token pack payment.
|
|
246
|
+
* Creates invoice, updates payment status, and adds tokens to user balance.
|
|
247
|
+
*/
|
|
248
|
+
async processApprovedTokenPackPayment(callbackData) {
|
|
249
|
+
const { orderReference, merchantAccount, merchantSignature, reasonCode, reason, amount, currency } = callbackData;
|
|
250
|
+
// Parse order reference to extract user and pack info
|
|
251
|
+
const parsedReference = (0, utils_1.parseOrderReference)(orderReference);
|
|
252
|
+
if (!parsedReference) {
|
|
253
|
+
throw new Error(`Invalid order reference format: ${orderReference}`);
|
|
254
|
+
}
|
|
255
|
+
const userId = (0, utils_1.getOrderReferenceUserId)(orderReference);
|
|
256
|
+
const packId = (0, utils_1.getOrderReferenceItemId)(orderReference);
|
|
257
|
+
const { platform } = parsedReference;
|
|
258
|
+
if (!userId || !packId) {
|
|
259
|
+
throw new Error(`Could not extract userId or packId from order reference: ${orderReference}`);
|
|
260
|
+
}
|
|
261
|
+
// Check for duplicate webhook (idempotency)
|
|
262
|
+
const existingInvoice = await this.invoiceService.getByOrderReference(orderReference);
|
|
263
|
+
if (existingInvoice) {
|
|
264
|
+
this.logger.info({
|
|
265
|
+
message: 'Duplicate webhook received for token pack, already processed',
|
|
266
|
+
payload: { orderReference },
|
|
267
|
+
});
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
// Create invoice record
|
|
271
|
+
const reasonCodeNumber = typeof reasonCode === 'string' ? parseInt(reasonCode, 10) : Number(reasonCode);
|
|
272
|
+
const invoice = await this.invoiceService.create({
|
|
273
|
+
merchantAccount,
|
|
274
|
+
orderReference,
|
|
275
|
+
merchantSignature,
|
|
276
|
+
reasonCode: reasonCodeNumber,
|
|
277
|
+
reason,
|
|
278
|
+
createdDate: moment_1.default.unix(callbackData.createdDate).toISOString(),
|
|
279
|
+
processingDate: moment_1.default.unix(callbackData.processingDate).toISOString(),
|
|
280
|
+
currency,
|
|
281
|
+
amount,
|
|
282
|
+
});
|
|
283
|
+
this.logger.info({
|
|
284
|
+
message: 'Invoice created for token pack',
|
|
285
|
+
payload: { invoiceId: invoice.id, orderReference },
|
|
286
|
+
});
|
|
287
|
+
// Update payment status to completed
|
|
288
|
+
const payment = await this.paymentService.getByOrderReference(orderReference);
|
|
289
|
+
if (payment && payment.id) {
|
|
290
|
+
await this.paymentService.changeStatus({
|
|
291
|
+
id: payment.id,
|
|
292
|
+
status: 'completed',
|
|
293
|
+
});
|
|
294
|
+
this.logger.info({
|
|
295
|
+
message: 'Token pack payment status updated to completed',
|
|
296
|
+
payload: { paymentId: payment.id, orderReference },
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
// Get token pack plan to determine token count
|
|
300
|
+
const tokenPackPlan = this.tokenPackPlans.find((p) => p.id === packId);
|
|
301
|
+
if (!tokenPackPlan) {
|
|
302
|
+
throw new Error(`Token pack plan not found: ${packId}`);
|
|
303
|
+
}
|
|
304
|
+
// Create token pack for user
|
|
305
|
+
await this.tokenPackService.create({
|
|
306
|
+
userId,
|
|
307
|
+
platform,
|
|
308
|
+
packId,
|
|
309
|
+
tokens: tokenPackPlan.tokens,
|
|
310
|
+
purchasedAt: moment_1.default.utc().toISOString(),
|
|
311
|
+
provider: 'wayforpay',
|
|
312
|
+
});
|
|
313
|
+
this.logger.info({
|
|
314
|
+
message: 'Token pack activated successfully',
|
|
315
|
+
payload: {
|
|
316
|
+
userId,
|
|
317
|
+
platform,
|
|
318
|
+
packId,
|
|
319
|
+
tokens: tokenPackPlan.tokens,
|
|
320
|
+
},
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* Processes an approved subscription upgrade payment.
|
|
325
|
+
* Updates subscription's planId, resets expiresAt, and adds new plan's tokens to existing balance.
|
|
326
|
+
*/
|
|
327
|
+
async processApprovedUpgradePayment(callbackData) {
|
|
328
|
+
const { orderReference, merchantAccount, merchantSignature, reasonCode, reason, amount, currency } = callbackData;
|
|
329
|
+
// Parse upgrade order reference to extract both plan IDs
|
|
330
|
+
const upgradeParsed = (0, utils_1.parseUpgradeOrderReference)(orderReference);
|
|
331
|
+
if (!upgradeParsed) {
|
|
332
|
+
throw new Error(`Invalid upgrade order reference format: ${orderReference}`);
|
|
333
|
+
}
|
|
334
|
+
const { userId, platform, currentPlanId, newPlanId } = upgradeParsed;
|
|
335
|
+
if (!userId || !currentPlanId || !newPlanId) {
|
|
336
|
+
throw new Error(`Could not extract userId or plan IDs from upgrade order reference: ${orderReference}`);
|
|
337
|
+
}
|
|
338
|
+
// Check for duplicate webhook (idempotency)
|
|
339
|
+
const existingInvoice = await this.invoiceService.getByOrderReference(orderReference);
|
|
340
|
+
if (existingInvoice) {
|
|
341
|
+
this.logger.info({
|
|
342
|
+
message: 'Duplicate webhook received for upgrade, already processed',
|
|
343
|
+
payload: { orderReference },
|
|
344
|
+
});
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
// Create invoice record
|
|
348
|
+
const reasonCodeNumber = typeof reasonCode === 'string' ? parseInt(reasonCode, 10) : Number(reasonCode);
|
|
349
|
+
const invoice = await this.invoiceService.create({
|
|
350
|
+
merchantAccount,
|
|
351
|
+
orderReference,
|
|
352
|
+
merchantSignature,
|
|
353
|
+
reasonCode: reasonCodeNumber,
|
|
354
|
+
reason,
|
|
355
|
+
createdDate: moment_1.default.unix(callbackData.createdDate).toISOString(),
|
|
356
|
+
processingDate: moment_1.default.unix(callbackData.processingDate).toISOString(),
|
|
357
|
+
currency,
|
|
358
|
+
amount,
|
|
359
|
+
});
|
|
360
|
+
this.logger.info({
|
|
361
|
+
message: 'Invoice created for upgrade',
|
|
362
|
+
payload: { invoiceId: invoice.id, orderReference },
|
|
363
|
+
});
|
|
364
|
+
// Update payment status to completed
|
|
365
|
+
const payment = await this.paymentService.getByOrderReference(orderReference);
|
|
366
|
+
if (payment && payment.id) {
|
|
367
|
+
await this.paymentService.changeStatus({
|
|
368
|
+
id: payment.id,
|
|
369
|
+
status: 'completed',
|
|
370
|
+
});
|
|
371
|
+
this.logger.info({
|
|
372
|
+
message: 'Upgrade payment status updated to completed',
|
|
373
|
+
payload: { paymentId: payment.id, orderReference },
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
// Get the new subscription plan
|
|
377
|
+
const newPlan = await this.subscriptionPlanService.getById(newPlanId);
|
|
378
|
+
if (!newPlan) {
|
|
379
|
+
throw new Error(`New subscription plan not found: ${newPlanId}`);
|
|
380
|
+
}
|
|
381
|
+
// Get existing active subscription
|
|
382
|
+
const existingSubscription = await this.subscriptionService.getByUser({
|
|
383
|
+
userId,
|
|
384
|
+
platform,
|
|
385
|
+
status: 'active',
|
|
386
|
+
});
|
|
387
|
+
if (!existingSubscription) {
|
|
388
|
+
this.logger.warning({
|
|
389
|
+
message: 'No active subscription found for upgrade, treating as new subscription',
|
|
390
|
+
payload: { userId, platform, orderReference },
|
|
391
|
+
});
|
|
392
|
+
// Fall back to creating a new subscription
|
|
393
|
+
const startedAt = moment_1.default.utc().toISOString();
|
|
394
|
+
const expiresAt = this.calculateExpirationDate(newPlan.regularMode, newPlan.count);
|
|
395
|
+
await this.activateNewSubscription({
|
|
396
|
+
userId,
|
|
397
|
+
platform,
|
|
398
|
+
planId: newPlanId,
|
|
399
|
+
startedAt,
|
|
400
|
+
expiresAt,
|
|
401
|
+
});
|
|
402
|
+
// Initialize token balance with new plan's credits
|
|
403
|
+
await this.initializeTokenBalance({
|
|
404
|
+
userId,
|
|
405
|
+
platform,
|
|
406
|
+
planId: newPlanId,
|
|
407
|
+
tokens: newPlan.credits,
|
|
408
|
+
});
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
411
|
+
// Calculate new expiration date (full new period from now)
|
|
412
|
+
const startedAt = moment_1.default.utc().toISOString();
|
|
413
|
+
const expiresAt = this.calculateExpirationDate(newPlan.regularMode, newPlan.count);
|
|
414
|
+
// Update subscription: change planId, reset expiresAt and startedAt, ensure active status
|
|
415
|
+
await this.subscriptionService.updateFieldsById({
|
|
416
|
+
subscriptionId: existingSubscription.id,
|
|
417
|
+
fields: [
|
|
418
|
+
['planId', newPlanId],
|
|
419
|
+
['expiresAt', expiresAt],
|
|
420
|
+
['startedAt', startedAt],
|
|
421
|
+
['status', 'active'],
|
|
422
|
+
],
|
|
423
|
+
});
|
|
424
|
+
this.logger.info({
|
|
425
|
+
message: 'Subscription upgraded',
|
|
426
|
+
payload: {
|
|
427
|
+
subscriptionId: existingSubscription.id,
|
|
428
|
+
previousPlanId: existingSubscription.planId,
|
|
429
|
+
newPlanId,
|
|
430
|
+
previousExpiresAt: existingSubscription.expiresAt,
|
|
431
|
+
newExpiresAt: expiresAt,
|
|
432
|
+
},
|
|
433
|
+
});
|
|
434
|
+
// Add new plan's tokens to existing balance (keep existing + add new)
|
|
435
|
+
await this.initializeTokenBalance({
|
|
436
|
+
userId,
|
|
437
|
+
platform,
|
|
438
|
+
planId: newPlanId,
|
|
439
|
+
tokens: newPlan.credits,
|
|
440
|
+
});
|
|
441
|
+
this.logger.info({
|
|
442
|
+
message: 'Subscription upgrade completed successfully',
|
|
443
|
+
payload: {
|
|
444
|
+
userId,
|
|
445
|
+
platform,
|
|
446
|
+
previousPlanId: currentPlanId,
|
|
447
|
+
newPlanId,
|
|
448
|
+
expiresAt,
|
|
449
|
+
tokensAdded: newPlan.credits,
|
|
450
|
+
},
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
/**
|
|
454
|
+
* Activates a new subscription for the user.
|
|
455
|
+
*/
|
|
456
|
+
async activateNewSubscription(params) {
|
|
457
|
+
const subscriptionData = {
|
|
458
|
+
userId: params.userId,
|
|
459
|
+
platform: params.platform,
|
|
460
|
+
planId: params.planId,
|
|
461
|
+
startedAt: params.startedAt,
|
|
462
|
+
expiresAt: params.expiresAt,
|
|
463
|
+
provider: 'wayforpay',
|
|
464
|
+
};
|
|
465
|
+
await this.subscriptionService.activateSubscription(subscriptionData);
|
|
466
|
+
this.logger.info({
|
|
467
|
+
message: 'New subscription activated',
|
|
468
|
+
payload: {
|
|
469
|
+
userId: params.userId,
|
|
470
|
+
platform: params.platform,
|
|
471
|
+
planId: params.planId,
|
|
472
|
+
},
|
|
473
|
+
});
|
|
474
|
+
}
|
|
475
|
+
/**
|
|
476
|
+
* Renews an existing subscription by extending its expiration date.
|
|
477
|
+
*/
|
|
478
|
+
async renewSubscription(existingSubscription, newExpiresAt) {
|
|
479
|
+
// If the subscription hasn't expired yet, extend from current expiration
|
|
480
|
+
const currentExpiration = moment_1.default.utc(existingSubscription.expiresAt);
|
|
481
|
+
const now = moment_1.default.utc();
|
|
482
|
+
let extendedExpiresAt;
|
|
483
|
+
if (currentExpiration.isAfter(now)) {
|
|
484
|
+
// Extend from current expiration date
|
|
485
|
+
const durationToAdd = moment_1.default.utc(newExpiresAt).diff(moment_1.default.utc(), 'milliseconds');
|
|
486
|
+
extendedExpiresAt = currentExpiration.add(durationToAdd, 'milliseconds').toISOString();
|
|
487
|
+
}
|
|
488
|
+
else {
|
|
489
|
+
// Subscription has expired, use the new expiration date
|
|
490
|
+
extendedExpiresAt = newExpiresAt;
|
|
491
|
+
}
|
|
492
|
+
await this.subscriptionService.renewSubscription({
|
|
493
|
+
userId: existingSubscription.userId,
|
|
494
|
+
platform: existingSubscription.platform,
|
|
495
|
+
expiresAt: extendedExpiresAt,
|
|
496
|
+
});
|
|
497
|
+
this.logger.info({
|
|
498
|
+
message: 'Subscription renewed',
|
|
499
|
+
payload: {
|
|
500
|
+
subscriptionId: existingSubscription.id,
|
|
501
|
+
previousExpiresAt: existingSubscription.expiresAt,
|
|
502
|
+
newExpiresAt: extendedExpiresAt,
|
|
503
|
+
},
|
|
504
|
+
});
|
|
505
|
+
}
|
|
506
|
+
/**
|
|
507
|
+
* Initializes user token balance by creating a token pack.
|
|
508
|
+
*/
|
|
509
|
+
async initializeTokenBalance(params) {
|
|
510
|
+
const { userId, platform, planId, tokens } = params;
|
|
511
|
+
await this.tokenPackService.create({
|
|
512
|
+
userId,
|
|
513
|
+
platform,
|
|
514
|
+
packId: planId,
|
|
515
|
+
tokens,
|
|
516
|
+
purchasedAt: moment_1.default.utc().toISOString(),
|
|
517
|
+
provider: 'wayforpay',
|
|
518
|
+
});
|
|
519
|
+
this.logger.info({
|
|
520
|
+
message: 'Token balance initialized',
|
|
521
|
+
payload: {
|
|
522
|
+
userId,
|
|
523
|
+
platform,
|
|
524
|
+
packId: planId,
|
|
525
|
+
tokens,
|
|
526
|
+
},
|
|
527
|
+
});
|
|
528
|
+
}
|
|
529
|
+
/**
|
|
530
|
+
* Calculates the subscription expiration date based on the regular mode.
|
|
531
|
+
*/
|
|
532
|
+
calculateExpirationDate(regularMode, count) {
|
|
533
|
+
const now = moment_1.default.utc();
|
|
534
|
+
switch (regularMode) {
|
|
535
|
+
case 'daily':
|
|
536
|
+
return now.add(count, 'days').toISOString();
|
|
537
|
+
case 'monthly':
|
|
538
|
+
return now.add(count, 'months').toISOString();
|
|
539
|
+
case 'yearly':
|
|
540
|
+
return now.add(count, 'years').toISOString();
|
|
541
|
+
default:
|
|
542
|
+
return now.add(count, 'months').toISOString();
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
/**
|
|
546
|
+
* Handles failed or declined payments.
|
|
547
|
+
* For recurring subscription renewals, this triggers deactivation of the subscription.
|
|
548
|
+
*/
|
|
549
|
+
async handleFailedPayment(callbackData) {
|
|
550
|
+
const { orderReference } = callbackData;
|
|
551
|
+
const payment = await this.paymentService.getByOrderReference(orderReference);
|
|
552
|
+
if (payment && payment.id) {
|
|
553
|
+
await this.paymentService.changeStatus({
|
|
554
|
+
id: payment.id,
|
|
555
|
+
status: 'failed',
|
|
556
|
+
});
|
|
557
|
+
this.logger.info({
|
|
558
|
+
message: 'Payment status updated to failed',
|
|
559
|
+
payload: {
|
|
560
|
+
paymentId: payment.id,
|
|
561
|
+
orderReference,
|
|
562
|
+
reason: callbackData.reason,
|
|
563
|
+
},
|
|
564
|
+
});
|
|
565
|
+
}
|
|
566
|
+
// Handle failed recurring renewal - deactivate subscription
|
|
567
|
+
if (this.isRecurringRenewal(callbackData) && (0, utils_1.isSubscriptionOrderReference)(orderReference)) {
|
|
568
|
+
await this.handleFailedRenewal(callbackData);
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
/**
|
|
572
|
+
* Checks if the payment callback is for a recurring renewal payment.
|
|
573
|
+
* WayForPay sets regularOn field for recurring payments.
|
|
574
|
+
*/
|
|
575
|
+
isRecurringRenewal(callbackData) {
|
|
576
|
+
// regularOn is set by WayForPay for recurring payment callbacks
|
|
577
|
+
return !!callbackData.regularOn;
|
|
578
|
+
}
|
|
579
|
+
/**
|
|
580
|
+
* Handles a failed recurring renewal by cancelling or deactivating the subscription.
|
|
581
|
+
* Respects grace period configuration before full deactivation.
|
|
582
|
+
*/
|
|
583
|
+
async handleFailedRenewal(callbackData) {
|
|
584
|
+
const { orderReference, reason, reasonCode } = callbackData;
|
|
585
|
+
const parsedReference = (0, utils_1.parseOrderReference)(orderReference);
|
|
586
|
+
if (!parsedReference) {
|
|
587
|
+
this.logger.warning({
|
|
588
|
+
message: 'Could not parse order reference for failed renewal',
|
|
589
|
+
payload: { orderReference },
|
|
590
|
+
});
|
|
591
|
+
return;
|
|
592
|
+
}
|
|
593
|
+
const userId = (0, utils_1.getOrderReferenceUserId)(orderReference);
|
|
594
|
+
const { platform } = parsedReference;
|
|
595
|
+
if (!userId) {
|
|
596
|
+
this.logger.warning({
|
|
597
|
+
message: 'Could not extract userId from order reference for failed renewal',
|
|
598
|
+
payload: { orderReference },
|
|
599
|
+
});
|
|
600
|
+
return;
|
|
601
|
+
}
|
|
602
|
+
// Find active subscription for user
|
|
603
|
+
const existingSubscription = await this.subscriptionService.getByUser({
|
|
604
|
+
userId,
|
|
605
|
+
platform,
|
|
606
|
+
status: 'active',
|
|
607
|
+
});
|
|
608
|
+
if (!existingSubscription) {
|
|
609
|
+
this.logger.info({
|
|
610
|
+
message: 'No active subscription found for failed renewal',
|
|
611
|
+
payload: { userId, platform, orderReference },
|
|
612
|
+
});
|
|
613
|
+
return;
|
|
614
|
+
}
|
|
615
|
+
// Check if subscription has expired past the grace period
|
|
616
|
+
const now = moment_1.default.utc();
|
|
617
|
+
const expiresAt = moment_1.default.utc(existingSubscription.expiresAt);
|
|
618
|
+
const gracePeriodMs = this.gracePeriodConfig.enabled ? this.gracePeriodConfig.durationMs : 0;
|
|
619
|
+
const gracePeriodEnd = expiresAt.clone().add(gracePeriodMs, 'milliseconds');
|
|
620
|
+
if (now.isAfter(gracePeriodEnd)) {
|
|
621
|
+
// Subscription has expired past grace period - deactivate it
|
|
622
|
+
await this.subscriptionService.deactivateSubscription({ id: existingSubscription.id });
|
|
623
|
+
this.logger.info({
|
|
624
|
+
message: 'Subscription deactivated due to failed renewal (past grace period)',
|
|
625
|
+
payload: {
|
|
626
|
+
subscriptionId: existingSubscription.id,
|
|
627
|
+
userId,
|
|
628
|
+
platform,
|
|
629
|
+
orderReference,
|
|
630
|
+
reason,
|
|
631
|
+
reasonCode,
|
|
632
|
+
expiresAt: existingSubscription.expiresAt,
|
|
633
|
+
gracePeriodMs,
|
|
634
|
+
gracePeriodEnd: gracePeriodEnd.toISOString(),
|
|
635
|
+
},
|
|
636
|
+
});
|
|
637
|
+
}
|
|
638
|
+
else if (expiresAt.isBefore(now)) {
|
|
639
|
+
// Subscription has expired but within grace period - cancel (mark for future deactivation)
|
|
640
|
+
await this.subscriptionService.cancelSubscription({ id: existingSubscription.id });
|
|
641
|
+
this.logger.info({
|
|
642
|
+
message: 'Subscription cancelled due to failed renewal (within grace period)',
|
|
643
|
+
payload: {
|
|
644
|
+
subscriptionId: existingSubscription.id,
|
|
645
|
+
userId,
|
|
646
|
+
platform,
|
|
647
|
+
orderReference,
|
|
648
|
+
expiresAt: existingSubscription.expiresAt,
|
|
649
|
+
gracePeriodEnd: gracePeriodEnd.toISOString(),
|
|
650
|
+
reason,
|
|
651
|
+
reasonCode,
|
|
652
|
+
},
|
|
653
|
+
});
|
|
654
|
+
}
|
|
655
|
+
else {
|
|
656
|
+
this.logger.info({
|
|
657
|
+
message: 'Failed renewal for subscription that has not yet expired',
|
|
658
|
+
payload: {
|
|
659
|
+
subscriptionId: existingSubscription.id,
|
|
660
|
+
userId,
|
|
661
|
+
platform,
|
|
662
|
+
orderReference,
|
|
663
|
+
expiresAt: existingSubscription.expiresAt,
|
|
664
|
+
reason,
|
|
665
|
+
reasonCode,
|
|
666
|
+
},
|
|
667
|
+
});
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
exports.WebhookHandlerService = WebhookHandlerService;
|
|
672
|
+
//# sourceMappingURL=webhook.service.js.map
|