payment-kit 1.19.18 → 1.19.19
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/api/src/index.ts +3 -1
- package/api/src/integrations/ethereum/tx.ts +11 -0
- package/api/src/integrations/stripe/handlers/invoice.ts +26 -6
- package/api/src/integrations/stripe/handlers/setup-intent.ts +34 -2
- package/api/src/integrations/stripe/resource.ts +185 -1
- package/api/src/libs/invoice.ts +2 -1
- package/api/src/libs/session.ts +6 -1
- package/api/src/queues/auto-recharge.ts +343 -0
- package/api/src/queues/credit-consume.ts +15 -1
- package/api/src/queues/credit-grant.ts +15 -0
- package/api/src/queues/payment.ts +14 -1
- package/api/src/queues/space.ts +1 -0
- package/api/src/routes/auto-recharge-configs.ts +454 -0
- package/api/src/routes/connect/auto-recharge-auth.ts +182 -0
- package/api/src/routes/connect/recharge-account.ts +72 -10
- package/api/src/routes/connect/setup.ts +5 -3
- package/api/src/routes/connect/shared.ts +45 -4
- package/api/src/routes/customers.ts +10 -6
- package/api/src/routes/index.ts +2 -0
- package/api/src/routes/invoices.ts +10 -1
- package/api/src/routes/meters.ts +1 -1
- package/api/src/routes/payment-currencies.ts +129 -0
- package/api/src/store/migrate.ts +20 -0
- package/api/src/store/migrations/20250821-auto-recharge-config.ts +38 -0
- package/api/src/store/models/auto-recharge-config.ts +225 -0
- package/api/src/store/models/credit-grant.ts +1 -1
- package/api/src/store/models/customer.ts +1 -0
- package/api/src/store/models/index.ts +3 -0
- package/api/src/store/models/invoice.ts +2 -1
- package/api/src/store/models/payment-currency.ts +10 -2
- package/api/src/store/models/types.ts +11 -0
- package/blocklet.yml +1 -1
- package/package.json +17 -17
- package/src/components/customer/credit-overview.tsx +103 -18
- package/src/components/customer/overdraft-protection.tsx +5 -5
- package/src/components/info-metric.tsx +11 -2
- package/src/components/invoice/recharge.tsx +8 -2
- package/src/components/metadata/form.tsx +29 -27
- package/src/components/meter/form.tsx +1 -2
- package/src/components/price/form.tsx +39 -26
- package/src/components/product/form.tsx +1 -2
- package/src/locales/en.tsx +15 -0
- package/src/locales/zh.tsx +14 -0
- package/src/pages/admin/billing/meters/detail.tsx +18 -0
- package/src/pages/admin/customers/customers/credit-grant/detail.tsx +10 -0
- package/src/pages/admin/products/prices/actions.tsx +42 -2
- package/src/pages/admin/products/products/create.tsx +1 -2
- package/src/pages/admin/settings/vault-config/edit-form.tsx +8 -8
- package/src/pages/customer/credit-grant/detail.tsx +9 -1
- package/src/pages/customer/recharge/account.tsx +14 -7
- package/src/pages/customer/recharge/subscription.tsx +4 -4
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
import { BN } from '@ocap/util';
|
|
2
|
+
|
|
3
|
+
import { CustomError } from '@blocklet/error';
|
|
4
|
+
import { Op } from 'sequelize';
|
|
5
|
+
import createQueue from '../libs/queue';
|
|
6
|
+
import {
|
|
7
|
+
AutoRechargeConfig,
|
|
8
|
+
CreditGrant,
|
|
9
|
+
Customer,
|
|
10
|
+
Invoice,
|
|
11
|
+
PaymentCurrency,
|
|
12
|
+
PaymentMethod,
|
|
13
|
+
Price,
|
|
14
|
+
Product,
|
|
15
|
+
TPriceExpanded,
|
|
16
|
+
} from '../store/models';
|
|
17
|
+
import logger from '../libs/logger';
|
|
18
|
+
import { getPriceUintAmountByCurrency } from '../libs/session';
|
|
19
|
+
import { isDelegationSufficientForPayment } from '../libs/payment';
|
|
20
|
+
import { createStripeInvoiceForAutoRecharge } from '../integrations/stripe/resource';
|
|
21
|
+
import { ensureInvoiceAndItems } from '../libs/invoice';
|
|
22
|
+
import dayjs from '../libs/dayjs';
|
|
23
|
+
import { invoiceQueue } from './invoice';
|
|
24
|
+
|
|
25
|
+
export interface AutoRechargeJobData {
|
|
26
|
+
customer_id: string;
|
|
27
|
+
currency_id: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface AutoRechargeJobResult {
|
|
31
|
+
success: boolean;
|
|
32
|
+
invoice_id?: string;
|
|
33
|
+
error_message?: string;
|
|
34
|
+
recharge_amount?: string;
|
|
35
|
+
credit_amount?: string;
|
|
36
|
+
payment_method_type?: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export async function processAutoRecharge(job: AutoRechargeJobData) {
|
|
40
|
+
logger.info('Processing auto recharge job', { job });
|
|
41
|
+
const { customer_id: customerId, currency_id: currencyId } = job;
|
|
42
|
+
|
|
43
|
+
const customer = await Customer.findByPk(customerId);
|
|
44
|
+
if (!customer) {
|
|
45
|
+
logger.error('Customer not found', { customerId });
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const currency = await PaymentCurrency.findByPk(currencyId);
|
|
50
|
+
if (!currency) {
|
|
51
|
+
logger.error('Currency not found', { currencyId });
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// 1. find auto recharge config
|
|
56
|
+
const config = (await AutoRechargeConfig.findOne({
|
|
57
|
+
where: {
|
|
58
|
+
customer_id: customerId,
|
|
59
|
+
currency_id: currencyId,
|
|
60
|
+
},
|
|
61
|
+
include: [
|
|
62
|
+
{ model: PaymentCurrency, as: 'rechargeCurrency', required: false },
|
|
63
|
+
{ model: Price, as: 'price', include: [{ model: Product, as: 'product' }] },
|
|
64
|
+
{ model: PaymentMethod, as: 'paymentMethod' },
|
|
65
|
+
],
|
|
66
|
+
})) as AutoRechargeConfig & {
|
|
67
|
+
paymentMethod: PaymentMethod;
|
|
68
|
+
price: TPriceExpanded;
|
|
69
|
+
rechargeCurrency: PaymentCurrency;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
if (!config || !config.enabled) {
|
|
73
|
+
logger.info('No auto recharge config found', { customerId, currencyId });
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (!config.rechargeCurrency) {
|
|
78
|
+
logger.error('Recharge currency not found', { customerId, currencyId });
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const availableGrants = await CreditGrant.getAvailableCreditsForCustomer(customerId, currencyId, [config.price_id]);
|
|
83
|
+
|
|
84
|
+
const totalAvailable = availableGrants.reduce((sum, grant) => sum.add(new BN(grant.remaining_amount)), new BN(0));
|
|
85
|
+
|
|
86
|
+
// 2. check if available balance is above threshold
|
|
87
|
+
if (new BN(totalAvailable).gt(new BN(config.threshold))) {
|
|
88
|
+
logger.info('Available balance is above threshold, skipping auto recharge', {
|
|
89
|
+
customerId,
|
|
90
|
+
currencyId,
|
|
91
|
+
totalAvailable: totalAvailable.toString(),
|
|
92
|
+
threshold: config.threshold,
|
|
93
|
+
});
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const priceAmount = await getPriceUintAmountByCurrency(config.price, config.rechargeCurrency.id);
|
|
98
|
+
if (!priceAmount) {
|
|
99
|
+
logger.error('Price amount is not valid', {
|
|
100
|
+
customerId,
|
|
101
|
+
currencyId,
|
|
102
|
+
});
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
const totalAmount = new BN(priceAmount).mul(new BN(config.quantity ?? 0)).toString();
|
|
106
|
+
|
|
107
|
+
// 3. check daily limit
|
|
108
|
+
if (!config.canRechargeToday(totalAmount)) {
|
|
109
|
+
logger.info('Daily recharge limit exceeded, skipping auto recharge', {
|
|
110
|
+
customerId,
|
|
111
|
+
currencyId,
|
|
112
|
+
});
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// 4. execute auto recharge
|
|
117
|
+
await executeAutoRecharge(customer, config, currency);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async function createInvoiceForAutoRecharge({
|
|
121
|
+
customer,
|
|
122
|
+
config,
|
|
123
|
+
currency,
|
|
124
|
+
price,
|
|
125
|
+
totalAmount,
|
|
126
|
+
paymentMethod,
|
|
127
|
+
rechargeCurrency,
|
|
128
|
+
}: {
|
|
129
|
+
customer: Customer;
|
|
130
|
+
config: AutoRechargeConfig;
|
|
131
|
+
currency: PaymentCurrency;
|
|
132
|
+
price: TPriceExpanded;
|
|
133
|
+
totalAmount: BN;
|
|
134
|
+
paymentMethod: PaymentMethod;
|
|
135
|
+
rechargeCurrency: PaymentCurrency;
|
|
136
|
+
}) {
|
|
137
|
+
const now = dayjs().unix();
|
|
138
|
+
const expandedItems = await Price.expand([{ price_id: price.id, quantity: config.quantity }], {
|
|
139
|
+
product: true,
|
|
140
|
+
});
|
|
141
|
+
let status = 'open';
|
|
142
|
+
if (paymentMethod.type === 'stripe') {
|
|
143
|
+
status = 'draft'; // stripe invoice will be finalized and paid automatically
|
|
144
|
+
}
|
|
145
|
+
const existInvoice = await Invoice.findOne({
|
|
146
|
+
where: {
|
|
147
|
+
customer_id: customer.id,
|
|
148
|
+
currency_id: rechargeCurrency.id,
|
|
149
|
+
status: {
|
|
150
|
+
[Op.in]: ['open', 'draft'],
|
|
151
|
+
},
|
|
152
|
+
},
|
|
153
|
+
});
|
|
154
|
+
if (existInvoice) {
|
|
155
|
+
logger.info('Auto recharge invoice already exists, skipping', {
|
|
156
|
+
customerId: customer.id,
|
|
157
|
+
configId: config.id,
|
|
158
|
+
});
|
|
159
|
+
return existInvoice;
|
|
160
|
+
}
|
|
161
|
+
const { invoice } = await ensureInvoiceAndItems({
|
|
162
|
+
customer,
|
|
163
|
+
currency: rechargeCurrency,
|
|
164
|
+
trialing: false,
|
|
165
|
+
metered: false,
|
|
166
|
+
lineItems: expandedItems,
|
|
167
|
+
applyCredit: false,
|
|
168
|
+
props: {
|
|
169
|
+
status,
|
|
170
|
+
total: totalAmount.toString(),
|
|
171
|
+
livemode: rechargeCurrency.livemode,
|
|
172
|
+
description: `Auto Top-up for ${currency.name}`,
|
|
173
|
+
statement_descriptor: '',
|
|
174
|
+
period_start: now,
|
|
175
|
+
period_end: now,
|
|
176
|
+
auto_advance: true,
|
|
177
|
+
billing_reason: 'auto_recharge',
|
|
178
|
+
currency_id: rechargeCurrency.id,
|
|
179
|
+
default_payment_method_id: paymentMethod.id,
|
|
180
|
+
custom_fields: [],
|
|
181
|
+
footer: '',
|
|
182
|
+
payment_settings: config.payment_settings,
|
|
183
|
+
metadata: {
|
|
184
|
+
auto_recharge: {
|
|
185
|
+
config_id: config.id,
|
|
186
|
+
currency_id: rechargeCurrency.id,
|
|
187
|
+
},
|
|
188
|
+
},
|
|
189
|
+
} as unknown as Invoice,
|
|
190
|
+
});
|
|
191
|
+
logger.info('New invoice created for auto recharge', {
|
|
192
|
+
customerId: customer.id,
|
|
193
|
+
configId: config.id,
|
|
194
|
+
invoiceId: invoice.id,
|
|
195
|
+
amount: totalAmount,
|
|
196
|
+
});
|
|
197
|
+
if (status !== 'draft') {
|
|
198
|
+
invoiceQueue.push({
|
|
199
|
+
id: invoice.id,
|
|
200
|
+
job: { invoiceId: invoice.id, retryOnError: true },
|
|
201
|
+
});
|
|
202
|
+
logger.info('Invoice job scheduled for auto recharge', { invoice: invoice.id, customerId: customer.id });
|
|
203
|
+
}
|
|
204
|
+
return invoice;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
async function executeAutoRecharge(
|
|
208
|
+
customer: Customer,
|
|
209
|
+
config: AutoRechargeConfig & {
|
|
210
|
+
paymentMethod: PaymentMethod;
|
|
211
|
+
price: TPriceExpanded;
|
|
212
|
+
rechargeCurrency: PaymentCurrency;
|
|
213
|
+
},
|
|
214
|
+
currency: PaymentCurrency
|
|
215
|
+
) {
|
|
216
|
+
const paymentMethod = config.paymentMethod!;
|
|
217
|
+
const price = config.price! as TPriceExpanded;
|
|
218
|
+
const rechargeCurrency = config.rechargeCurrency!;
|
|
219
|
+
|
|
220
|
+
try {
|
|
221
|
+
const priceAmount = await getPriceUintAmountByCurrency(price, rechargeCurrency.id);
|
|
222
|
+
const totalAmount = new BN(priceAmount).mul(new BN(config.quantity ?? 0));
|
|
223
|
+
const paymentSettings = config.payment_settings;
|
|
224
|
+
if (!paymentSettings) {
|
|
225
|
+
throw new Error('No payment settings found for auto recharge');
|
|
226
|
+
}
|
|
227
|
+
const payer = (paymentSettings.payment_method_options as any)?.[paymentMethod.type]?.payer;
|
|
228
|
+
if (!payer) {
|
|
229
|
+
throw new Error('No payer found for auto recharge');
|
|
230
|
+
}
|
|
231
|
+
if (paymentMethod.type !== 'stripe') {
|
|
232
|
+
const delegationCheck = await isDelegationSufficientForPayment({
|
|
233
|
+
paymentMethod,
|
|
234
|
+
paymentCurrency: rechargeCurrency,
|
|
235
|
+
userDid: payer,
|
|
236
|
+
amount: totalAmount.toString(),
|
|
237
|
+
});
|
|
238
|
+
if (!delegationCheck.sufficient) {
|
|
239
|
+
throw new CustomError(delegationCheck.reason, 'insufficient delegation or balance');
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
const invoice = await createInvoiceForAutoRecharge({
|
|
243
|
+
customer,
|
|
244
|
+
config,
|
|
245
|
+
currency,
|
|
246
|
+
price,
|
|
247
|
+
totalAmount,
|
|
248
|
+
paymentMethod,
|
|
249
|
+
rechargeCurrency,
|
|
250
|
+
});
|
|
251
|
+
if (paymentMethod.type === 'stripe') {
|
|
252
|
+
await createStripeInvoiceForAutoRecharge({
|
|
253
|
+
autoRechargeConfig: config,
|
|
254
|
+
customer,
|
|
255
|
+
paymentMethod,
|
|
256
|
+
currency: rechargeCurrency,
|
|
257
|
+
invoice,
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
} catch (error: any) {
|
|
261
|
+
logger.error('Auto recharge execution failed', {
|
|
262
|
+
customerId: customer.id,
|
|
263
|
+
configId: config.id,
|
|
264
|
+
paymentMethodType: paymentMethod.type,
|
|
265
|
+
error: error.message,
|
|
266
|
+
});
|
|
267
|
+
throw error;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// 创建自动充值队列
|
|
272
|
+
export const autoRechargeQueue = createQueue<AutoRechargeJobData>({
|
|
273
|
+
name: 'auto-recharge',
|
|
274
|
+
onJob: processAutoRecharge,
|
|
275
|
+
options: {
|
|
276
|
+
concurrency: 5,
|
|
277
|
+
maxRetries: 3,
|
|
278
|
+
},
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* 检查并触发自动充值 (添加到队列)
|
|
283
|
+
*/
|
|
284
|
+
export async function checkAndTriggerAutoRecharge(
|
|
285
|
+
customer: Customer,
|
|
286
|
+
currencyId: string,
|
|
287
|
+
currentBalance: string
|
|
288
|
+
): Promise<void> {
|
|
289
|
+
try {
|
|
290
|
+
// 查找自动充值配置
|
|
291
|
+
const config = await AutoRechargeConfig.findOne({
|
|
292
|
+
where: {
|
|
293
|
+
customer_id: customer.id,
|
|
294
|
+
currency_id: currencyId,
|
|
295
|
+
enabled: true,
|
|
296
|
+
livemode: customer.livemode,
|
|
297
|
+
},
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
if (!config) {
|
|
301
|
+
logger.debug('No auto recharge config found', {
|
|
302
|
+
customerId: customer.id,
|
|
303
|
+
currencyId,
|
|
304
|
+
});
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// check if balance is above threshold
|
|
309
|
+
if (new BN(currentBalance).gte(new BN(config.threshold))) {
|
|
310
|
+
logger.debug('Balance above threshold, skipping auto recharge', {
|
|
311
|
+
customerId: customer.id,
|
|
312
|
+
currentBalance,
|
|
313
|
+
threshold: config.threshold,
|
|
314
|
+
});
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
const jobData: AutoRechargeJobData = {
|
|
319
|
+
customer_id: customer.id,
|
|
320
|
+
currency_id: currencyId,
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
await autoRechargeQueue.push({
|
|
324
|
+
job: jobData,
|
|
325
|
+
id: `auto-recharge-${customer.id}-${currencyId}}`,
|
|
326
|
+
persist: true,
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
logger.info('Added auto recharge job to queue', {
|
|
330
|
+
customerId: customer.id,
|
|
331
|
+
currencyId,
|
|
332
|
+
currentBalance,
|
|
333
|
+
threshold: config.threshold,
|
|
334
|
+
});
|
|
335
|
+
} catch (error: any) {
|
|
336
|
+
logger.error('Failed to trigger auto recharge', {
|
|
337
|
+
customerId: customer.id,
|
|
338
|
+
currencyId,
|
|
339
|
+
currentBalance,
|
|
340
|
+
error: error.message,
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
}
|
|
@@ -19,6 +19,7 @@ import { MAX_RETRY_COUNT, getNextRetry } from '../libs/util';
|
|
|
19
19
|
import { getDaysUntilCancel, getDueUnit, getMeterPriceIdsFromSubscription } from '../libs/subscription';
|
|
20
20
|
import { events } from '../libs/event';
|
|
21
21
|
import { handlePastDueSubscriptionRecovery } from './payment';
|
|
22
|
+
import { checkAndTriggerAutoRecharge } from './auto-recharge';
|
|
22
23
|
|
|
23
24
|
type CreditConsumptionJob = {
|
|
24
25
|
meterEventId: string;
|
|
@@ -485,7 +486,20 @@ export async function handleCreditConsumption(job: CreditConsumptionJob) {
|
|
|
485
486
|
|
|
486
487
|
// Consume available credits (handles existing transactions internally)
|
|
487
488
|
const consumptionResult = await consumeAvailableCredits(context, totalRequiredAmount);
|
|
488
|
-
|
|
489
|
+
// Check for auto recharge after successful consumption
|
|
490
|
+
try {
|
|
491
|
+
await checkAndTriggerAutoRecharge(
|
|
492
|
+
context.customer,
|
|
493
|
+
context.meter.currency_id!,
|
|
494
|
+
consumptionResult.available_balance
|
|
495
|
+
);
|
|
496
|
+
} catch (error: any) {
|
|
497
|
+
logger.warn('Auto recharge check failed after credit consumption', {
|
|
498
|
+
meterEventId,
|
|
499
|
+
customerId,
|
|
500
|
+
error: error.message,
|
|
501
|
+
});
|
|
502
|
+
}
|
|
489
503
|
// Update MeterEvent with consumption details
|
|
490
504
|
await context.meterEvent.update({
|
|
491
505
|
credit_consumed: consumptionResult.consumed,
|
|
@@ -5,6 +5,7 @@ import { BN, fromTokenToUnit } from '@ocap/util';
|
|
|
5
5
|
import dayjs from '../libs/dayjs';
|
|
6
6
|
import createQueue from '../libs/queue';
|
|
7
7
|
import {
|
|
8
|
+
AutoRechargeConfig,
|
|
8
9
|
CreditGrant,
|
|
9
10
|
Customer,
|
|
10
11
|
Invoice,
|
|
@@ -285,6 +286,20 @@ async function handleInvoiceCredit(invoiceId: string) {
|
|
|
285
286
|
return;
|
|
286
287
|
}
|
|
287
288
|
|
|
289
|
+
// Handle auto recharge invoice - use standard processing since we now create proper InvoiceItems
|
|
290
|
+
if (invoice.metadata?.auto_recharge) {
|
|
291
|
+
logger.info('Processing auto recharge invoice with standard logic', {
|
|
292
|
+
invoiceId: invoice.id,
|
|
293
|
+
customerId: invoice.customer.id,
|
|
294
|
+
});
|
|
295
|
+
if (invoice.metadata?.auto_recharge?.config_id) {
|
|
296
|
+
const autoRechargeConfig = await AutoRechargeConfig.findByPk(invoice.metadata?.auto_recharge?.config_id);
|
|
297
|
+
if (autoRechargeConfig) {
|
|
298
|
+
await autoRechargeConfig.updateDailyStats(invoice.total);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
288
303
|
const lineItems = invoice.lines || [];
|
|
289
304
|
|
|
290
305
|
if (!Array.isArray(lineItems) || lineItems.length === 0) {
|
|
@@ -36,7 +36,7 @@ import { SubscriptionItem } from '../store/models/subscription-item';
|
|
|
36
36
|
import type { EVMChainType, PaymentError, PaymentSettings } from '../store/models/types';
|
|
37
37
|
import { notificationQueue } from './notification';
|
|
38
38
|
import { ensureOverdraftProtectionInvoiceAndItems } from '../libs/invoice';
|
|
39
|
-
import { Lock, MeterEvent } from '../store/models';
|
|
39
|
+
import { AutoRechargeConfig, Lock, MeterEvent } from '../store/models';
|
|
40
40
|
import { ensureOverdraftProtectionPrice } from '../libs/overdraft-protection';
|
|
41
41
|
import createQueue from '../libs/queue';
|
|
42
42
|
import { CHARGE_SUPPORTED_CHAIN_TYPES, EVM_CHAIN_TYPES } from '../libs/constants';
|
|
@@ -663,6 +663,19 @@ export const handlePaymentFailed = async (
|
|
|
663
663
|
attempt_count: invoice.attempt_count,
|
|
664
664
|
});
|
|
665
665
|
|
|
666
|
+
if (invoice.billing_reason === 'auto_recharge' && invoice.metadata?.recharge_config?.recharge_id) {
|
|
667
|
+
const autoRechargeConfig = await AutoRechargeConfig.findByPk(invoice.metadata?.recharge_config?.recharge_id);
|
|
668
|
+
if (autoRechargeConfig && autoRechargeConfig.enabled) {
|
|
669
|
+
autoRechargeConfig.update({
|
|
670
|
+
enabled: false,
|
|
671
|
+
metadata: {
|
|
672
|
+
...autoRechargeConfig.metadata,
|
|
673
|
+
failReason: 'Payment failed',
|
|
674
|
+
},
|
|
675
|
+
});
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
|
|
666
679
|
return updates.terminate;
|
|
667
680
|
}
|
|
668
681
|
|
package/api/src/queues/space.ts
CHANGED