payment-kit 1.20.11 → 1.20.12
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 +2 -0
- package/api/src/integrations/stripe/handlers/invoice.ts +63 -5
- package/api/src/integrations/stripe/handlers/payment-intent.ts +1 -0
- package/api/src/integrations/stripe/resource.ts +253 -2
- package/api/src/libs/currency.ts +31 -0
- package/api/src/libs/discount/coupon.ts +1061 -0
- package/api/src/libs/discount/discount.ts +349 -0
- package/api/src/libs/discount/nft.ts +239 -0
- package/api/src/libs/discount/redemption.ts +636 -0
- package/api/src/libs/discount/vc.ts +73 -0
- package/api/src/libs/invoice.ts +44 -10
- package/api/src/libs/math-utils.ts +6 -0
- package/api/src/libs/price.ts +43 -0
- package/api/src/libs/session.ts +242 -57
- package/api/src/libs/subscription.ts +2 -6
- package/api/src/queues/auto-recharge.ts +1 -1
- package/api/src/queues/discount-status.ts +200 -0
- package/api/src/queues/subscription.ts +98 -5
- package/api/src/queues/usage-record.ts +1 -1
- package/api/src/routes/auto-recharge-configs.ts +5 -3
- package/api/src/routes/checkout-sessions.ts +755 -64
- package/api/src/routes/connect/change-payment.ts +6 -1
- package/api/src/routes/connect/change-plan.ts +6 -1
- package/api/src/routes/connect/setup.ts +6 -1
- package/api/src/routes/connect/shared.ts +80 -9
- package/api/src/routes/connect/subscribe.ts +12 -2
- package/api/src/routes/coupons.ts +518 -0
- package/api/src/routes/index.ts +4 -0
- package/api/src/routes/invoices.ts +44 -3
- package/api/src/routes/meter-events.ts +2 -1
- package/api/src/routes/payment-currencies.ts +1 -0
- package/api/src/routes/promotion-codes.ts +482 -0
- package/api/src/routes/subscriptions.ts +23 -2
- package/api/src/store/migrations/20250904-discount.ts +136 -0
- package/api/src/store/migrations/20250910-timestamp-fields.ts +116 -0
- package/api/src/store/migrations/20250916-add-description-fields.ts +30 -0
- package/api/src/store/models/checkout-session.ts +12 -0
- package/api/src/store/models/coupon.ts +144 -4
- package/api/src/store/models/discount.ts +23 -10
- package/api/src/store/models/index.ts +13 -2
- package/api/src/store/models/promotion-code.ts +295 -18
- package/api/src/store/models/types.ts +30 -1
- package/api/tests/libs/session.spec.ts +48 -27
- package/blocklet.yml +1 -1
- package/package.json +20 -20
- package/src/app.tsx +2 -0
- package/src/components/customer/link.tsx +1 -1
- package/src/components/discount/discount-info.tsx +178 -0
- package/src/components/invoice/table.tsx +140 -48
- package/src/components/invoice-pdf/styles.ts +6 -0
- package/src/components/invoice-pdf/template.tsx +59 -33
- package/src/components/metadata/form.tsx +14 -5
- package/src/components/payment-link/actions.tsx +42 -0
- package/src/components/price/form.tsx +91 -65
- package/src/components/product/vendor-config.tsx +5 -3
- package/src/components/promotion/active-redemptions.tsx +534 -0
- package/src/components/promotion/currency-multi-select.tsx +350 -0
- package/src/components/promotion/currency-restrictions.tsx +117 -0
- package/src/components/promotion/product-select.tsx +292 -0
- package/src/components/promotion/promotion-code-form.tsx +534 -0
- package/src/components/subscription/portal/list.tsx +6 -1
- package/src/components/subscription/vendor-service-list.tsx +13 -2
- package/src/locales/en.tsx +227 -0
- package/src/locales/zh.tsx +222 -1
- package/src/pages/admin/billing/subscriptions/detail.tsx +5 -0
- package/src/pages/admin/products/coupons/applicable-products.tsx +166 -0
- package/src/pages/admin/products/coupons/create.tsx +612 -0
- package/src/pages/admin/products/coupons/detail.tsx +538 -0
- package/src/pages/admin/products/coupons/edit.tsx +127 -0
- package/src/pages/admin/products/coupons/index.tsx +210 -3
- package/src/pages/admin/products/index.tsx +22 -3
- package/src/pages/admin/products/products/detail.tsx +12 -2
- package/src/pages/admin/products/promotion-codes/actions.tsx +103 -0
- package/src/pages/admin/products/promotion-codes/create.tsx +235 -0
- package/src/pages/admin/products/promotion-codes/detail.tsx +416 -0
- package/src/pages/admin/products/promotion-codes/list.tsx +247 -0
- package/src/pages/admin/products/promotion-codes/verification-config.tsx +327 -0
- package/src/pages/admin/products/vendors/index.tsx +17 -5
- package/src/pages/customer/subscription/detail.tsx +5 -0
- package/vite.config.ts +4 -3
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import pAll from 'p-all';
|
|
2
|
+
import createQueue from '../libs/queue';
|
|
3
|
+
import { Coupon, PromotionCode } from '../store/models';
|
|
4
|
+
import logger from '../libs/logger';
|
|
5
|
+
import { events } from '../libs/event';
|
|
6
|
+
import { validCoupon, validPromotionCode } from '../libs/discount/coupon';
|
|
7
|
+
|
|
8
|
+
export type DiscountType = 'coupon' | 'promotion-code';
|
|
9
|
+
|
|
10
|
+
export interface DiscountStatusJobData {
|
|
11
|
+
type: DiscountType;
|
|
12
|
+
id: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Process discount status check for both coupons and promotion codes
|
|
17
|
+
*/
|
|
18
|
+
export async function processDiscountStatus(job: DiscountStatusJobData) {
|
|
19
|
+
logger.info('Processing discount status check job', { job });
|
|
20
|
+
const { type, id } = job;
|
|
21
|
+
|
|
22
|
+
if (type === 'coupon') {
|
|
23
|
+
await processCouponStatus(id);
|
|
24
|
+
} else if (type === 'promotion-code') {
|
|
25
|
+
await processPromotionCodeStatus(id);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Process coupon status check
|
|
31
|
+
*/
|
|
32
|
+
async function processCouponStatus(couponId: string) {
|
|
33
|
+
const coupon = await Coupon.findByPk(couponId);
|
|
34
|
+
if (!coupon) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Skip if already invalid
|
|
39
|
+
if (!coupon.valid) {
|
|
40
|
+
logger.info('Coupon is already invalid, skipping', { couponId });
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Use the existing validCoupon function to check validity
|
|
45
|
+
const validation = validCoupon(coupon);
|
|
46
|
+
|
|
47
|
+
if (!validation.valid) {
|
|
48
|
+
// Mark coupon as invalid
|
|
49
|
+
await coupon.update({ valid: false, metadata: { ...coupon.metadata, invalid_reason: validation.reason } });
|
|
50
|
+
logger.info('Coupon marked as invalid', {
|
|
51
|
+
couponId,
|
|
52
|
+
reason: validation.reason,
|
|
53
|
+
redeem_by: coupon.redeem_by,
|
|
54
|
+
max_redemptions: coupon.max_redemptions,
|
|
55
|
+
times_redeemed: coupon.times_redeemed,
|
|
56
|
+
});
|
|
57
|
+
const promotionCodes = await PromotionCode.findAll({ where: { coupon_id: couponId, active: true } });
|
|
58
|
+
await pAll(
|
|
59
|
+
promotionCodes.map(
|
|
60
|
+
(promotionCode) => () =>
|
|
61
|
+
promotionCode.update({
|
|
62
|
+
active: false,
|
|
63
|
+
metadata: { ...promotionCode.metadata, invalid_reason: validation.reason },
|
|
64
|
+
})
|
|
65
|
+
),
|
|
66
|
+
{ concurrency: 5 }
|
|
67
|
+
);
|
|
68
|
+
logger.info('Promotion codes marked as inactive', {
|
|
69
|
+
promotionCodeIds: promotionCodes.map((pc) => pc.id),
|
|
70
|
+
reason: validation.reason,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Process promotion code status check
|
|
77
|
+
*/
|
|
78
|
+
async function processPromotionCodeStatus(promotionCodeId: string) {
|
|
79
|
+
const promotionCode = await PromotionCode.findByPk(promotionCodeId);
|
|
80
|
+
if (!promotionCode) {
|
|
81
|
+
logger.warn('Promotion code not found for status check', { promotionCodeId });
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Skip if already inactive
|
|
86
|
+
if (!promotionCode.active) {
|
|
87
|
+
logger.info('Promotion code is already inactive, skipping', { promotionCodeId });
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Use the existing validPromotionCode function to check validity
|
|
92
|
+
const validation = await validPromotionCode(promotionCode, {});
|
|
93
|
+
|
|
94
|
+
if (!validation.valid) {
|
|
95
|
+
// Mark promotion code as inactive
|
|
96
|
+
await promotionCode.update({
|
|
97
|
+
active: false,
|
|
98
|
+
metadata: { ...promotionCode.metadata, invalid_reason: validation.reason },
|
|
99
|
+
});
|
|
100
|
+
logger.info('Promotion code marked as inactive', {
|
|
101
|
+
promotionCodeId,
|
|
102
|
+
reason: validation.reason,
|
|
103
|
+
expires_at: promotionCode.expires_at,
|
|
104
|
+
max_redemptions: promotionCode.max_redemptions,
|
|
105
|
+
times_redeemed: promotionCode.times_redeemed,
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Schedule discount status monitoring jobs
|
|
112
|
+
*/
|
|
113
|
+
export async function addDiscountStatusJob(
|
|
114
|
+
record: Coupon | PromotionCode,
|
|
115
|
+
type: DiscountType = 'coupon',
|
|
116
|
+
replace: boolean = false,
|
|
117
|
+
options: { delay?: number; runAt?: number } = {}
|
|
118
|
+
) {
|
|
119
|
+
const { id } = record;
|
|
120
|
+
const jobId = `discount-status-${type}-${id}`;
|
|
121
|
+
|
|
122
|
+
// Remove existing job if present
|
|
123
|
+
const existingJob = await discountStatusQueue.get(jobId);
|
|
124
|
+
if (existingJob && !replace) {
|
|
125
|
+
logger.info('Discount job already exists, skipping', { type, id, jobId });
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
if (existingJob && replace) {
|
|
129
|
+
await discountStatusQueue.delete(jobId);
|
|
130
|
+
logger.info('Removed existing discount status job for update', { type, id, jobId });
|
|
131
|
+
}
|
|
132
|
+
discountStatusQueue.push({ id: jobId, job: { type, id }, ...options });
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Create unified discount status queue
|
|
136
|
+
export const discountStatusQueue = createQueue<DiscountStatusJobData>({
|
|
137
|
+
name: 'discount-status',
|
|
138
|
+
onJob: processDiscountStatus,
|
|
139
|
+
options: {
|
|
140
|
+
concurrency: 5,
|
|
141
|
+
maxRetries: 3,
|
|
142
|
+
enableScheduledJob: true,
|
|
143
|
+
},
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
export const startDiscountStatusQueue = async () => {
|
|
147
|
+
logger.info('Starting discount status queue...');
|
|
148
|
+
|
|
149
|
+
const coupons = await Coupon.findAll({ where: { valid: true } });
|
|
150
|
+
const promotionCodes = await PromotionCode.findAll({ where: { active: true } });
|
|
151
|
+
await pAll(
|
|
152
|
+
coupons.map(
|
|
153
|
+
(coupon) => () =>
|
|
154
|
+
addDiscountStatusJob(coupon, 'coupon', false, {
|
|
155
|
+
runAt: coupon.redeem_by,
|
|
156
|
+
})
|
|
157
|
+
),
|
|
158
|
+
{ concurrency: 5 }
|
|
159
|
+
);
|
|
160
|
+
await pAll(
|
|
161
|
+
promotionCodes.map(
|
|
162
|
+
(promotionCode) => () =>
|
|
163
|
+
addDiscountStatusJob(promotionCode, 'promotion-code', false, {
|
|
164
|
+
runAt: promotionCode.expires_at,
|
|
165
|
+
})
|
|
166
|
+
),
|
|
167
|
+
{ concurrency: 5 }
|
|
168
|
+
);
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
events.on('coupon.created', (coupon: Coupon) => {
|
|
172
|
+
logger.info('Received coupon.created event', { couponId: coupon.id });
|
|
173
|
+
addDiscountStatusJob(coupon, 'coupon', true, {
|
|
174
|
+
runAt: coupon.redeem_by,
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
events.on('promotion-code.created', (promotionCode: PromotionCode) => {
|
|
179
|
+
addDiscountStatusJob(promotionCode, 'promotion-code', true, {
|
|
180
|
+
runAt: promotionCode.expires_at,
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
events.on('promotion-code.updated', (promotionCode: PromotionCode) => {
|
|
185
|
+
if (promotionCode.active) {
|
|
186
|
+
addDiscountStatusJob(promotionCode, 'promotion-code', true, {
|
|
187
|
+
runAt: promotionCode.expires_at,
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
events.on('discount-status.queued', (record, type, replace) => {
|
|
193
|
+
try {
|
|
194
|
+
addDiscountStatusJob(record, type, replace);
|
|
195
|
+
events.emit('discount-status.queued.done', record, type, replace);
|
|
196
|
+
} catch (error) {
|
|
197
|
+
logger.error('Error in discount-status.queued', { recordId: record.id, type, replace });
|
|
198
|
+
events.emit('discount-status.queued.error', record, type, replace);
|
|
199
|
+
}
|
|
200
|
+
});
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { LiteralUnion } from 'type-fest';
|
|
2
2
|
|
|
3
|
+
import { BN } from '@ocap/util';
|
|
3
4
|
import { Op } from 'sequelize';
|
|
4
5
|
import { createEvent } from '../libs/audit';
|
|
5
6
|
import { ensurePassportRevoked } from '../integrations/blocklet/passport';
|
|
@@ -34,8 +35,10 @@ import { Invoice } from '../store/models/invoice';
|
|
|
34
35
|
import { Price } from '../store/models/price';
|
|
35
36
|
import { Subscription } from '../store/models/subscription';
|
|
36
37
|
import { SubscriptionItem } from '../store/models/subscription-item';
|
|
38
|
+
import { getValidDiscountsForSubscriptionBilling } from '../libs/discount/coupon';
|
|
37
39
|
import { invoiceQueue } from './invoice';
|
|
38
40
|
import { SubscriptionWillCanceledSchedule } from '../crons/subscription-will-canceled';
|
|
41
|
+
import { applySubscriptionDiscount } from '../libs/discount/discount';
|
|
39
42
|
|
|
40
43
|
type SubscriptionJob = {
|
|
41
44
|
subscriptionId: string;
|
|
@@ -190,7 +193,90 @@ const doHandleSubscriptionInvoice = async ({
|
|
|
190
193
|
return null;
|
|
191
194
|
}
|
|
192
195
|
|
|
193
|
-
|
|
196
|
+
// Get valid discounts for this subscription billing period
|
|
197
|
+
const { validDiscounts, expiredDiscounts } = await getValidDiscountsForSubscriptionBilling({
|
|
198
|
+
subscriptionId: subscription.id,
|
|
199
|
+
customerId: subscription.customer_id,
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
logger.info('Valid discounts for subscription billing', {
|
|
203
|
+
subscriptionId: subscription.id,
|
|
204
|
+
validDiscounts: validDiscounts.map((d) => d.id),
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
// Log expired discounts for monitoring
|
|
208
|
+
if (expiredDiscounts.length > 0) {
|
|
209
|
+
logger.info('Found expired discounts during subscription billing', {
|
|
210
|
+
subscriptionId: subscription.id,
|
|
211
|
+
expiredDiscounts: expiredDiscounts.map((d) => d.id),
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Calculate subscription amount with discounts
|
|
216
|
+
const baseAmount = await getSubscriptionCycleAmount(expandedItems, currency.id);
|
|
217
|
+
|
|
218
|
+
// Apply discounts - simplified to use only one discount (first valid one)
|
|
219
|
+
const firstValidDiscount = validDiscounts?.[0] || null;
|
|
220
|
+
logger.info('First valid discount', {
|
|
221
|
+
subscriptionId: subscription.id,
|
|
222
|
+
firstValidDiscount: firstValidDiscount?.id,
|
|
223
|
+
});
|
|
224
|
+
let enhancedLineItems = expandedItems;
|
|
225
|
+
let discountSummary: {
|
|
226
|
+
appliedCoupon: string | null;
|
|
227
|
+
discountAmount: string;
|
|
228
|
+
totalDiscountAmount: string;
|
|
229
|
+
finalTotal: string;
|
|
230
|
+
} = {
|
|
231
|
+
appliedCoupon: null,
|
|
232
|
+
discountAmount: '0',
|
|
233
|
+
totalDiscountAmount: '0',
|
|
234
|
+
finalTotal: baseAmount.total,
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
if (firstValidDiscount) {
|
|
238
|
+
// Check if subscription is still in trial period for accurate discount calculation
|
|
239
|
+
const now = Math.floor(Date.now() / 1000);
|
|
240
|
+
const isTrialing = !!(subscription.trial_end && subscription.trial_end > now);
|
|
241
|
+
|
|
242
|
+
const discountApplication = await applySubscriptionDiscount({
|
|
243
|
+
lineItems: expandedItems,
|
|
244
|
+
discount: firstValidDiscount,
|
|
245
|
+
currency,
|
|
246
|
+
totalAmount: baseAmount.total,
|
|
247
|
+
billingContext: {
|
|
248
|
+
trialing: isTrialing,
|
|
249
|
+
},
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
logger.info('Discount application', {
|
|
253
|
+
subscriptionId: subscription.id,
|
|
254
|
+
discountApplication,
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
enhancedLineItems = discountApplication.enhancedLineItems;
|
|
258
|
+
discountSummary = discountApplication.discountSummary;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
const { appliedCoupon, totalDiscountAmount, finalTotal } = discountSummary;
|
|
262
|
+
|
|
263
|
+
// Additional safety check to ensure final total is never negative
|
|
264
|
+
const safeFinalTotal = new BN(finalTotal || '0').lt(new BN('0')) ? '0' : finalTotal || '0';
|
|
265
|
+
|
|
266
|
+
// Prepare data for invoice creation
|
|
267
|
+
const appliedDiscounts = appliedCoupon && firstValidDiscount ? [firstValidDiscount.id] : [];
|
|
268
|
+
const discountBreakdownForInvoice =
|
|
269
|
+
appliedCoupon && firstValidDiscount ? [{ amount: totalDiscountAmount, discount: firstValidDiscount.id }] : [];
|
|
270
|
+
|
|
271
|
+
if (validDiscounts.length > 0) {
|
|
272
|
+
logger.info('Subscription billing discount calculation completed', {
|
|
273
|
+
subscriptionId: subscription.id,
|
|
274
|
+
originalTotal: baseAmount.total,
|
|
275
|
+
totalDiscount: totalDiscountAmount,
|
|
276
|
+
finalTotal,
|
|
277
|
+
appliedDiscounts: appliedDiscounts.length,
|
|
278
|
+
});
|
|
279
|
+
}
|
|
194
280
|
|
|
195
281
|
const { invoice } = await ensureInvoiceAndItems({
|
|
196
282
|
customer,
|
|
@@ -198,7 +284,7 @@ const doHandleSubscriptionInvoice = async ({
|
|
|
198
284
|
subscription,
|
|
199
285
|
trialing: false,
|
|
200
286
|
metered: true,
|
|
201
|
-
lineItems:
|
|
287
|
+
lineItems: enhancedLineItems,
|
|
202
288
|
props: {
|
|
203
289
|
livemode: subscription.livemode,
|
|
204
290
|
description: `Subscription ${reason}`,
|
|
@@ -209,11 +295,18 @@ const doHandleSubscriptionInvoice = async ({
|
|
|
209
295
|
status,
|
|
210
296
|
billing_reason: `subscription_${reason}`,
|
|
211
297
|
currency_id: subscription.currency_id,
|
|
212
|
-
|
|
298
|
+
// Set correct subtotal (original amount) and total (after discount)
|
|
299
|
+
subtotal: baseAmount.total,
|
|
300
|
+
total: safeFinalTotal,
|
|
213
301
|
payment_settings: subscription.payment_settings,
|
|
214
302
|
default_payment_method_id: subscription.default_payment_method_id,
|
|
215
|
-
|
|
216
|
-
|
|
303
|
+
// Discount information for Invoice
|
|
304
|
+
discounts: appliedDiscounts,
|
|
305
|
+
total_discount_amounts: discountBreakdownForInvoice,
|
|
306
|
+
metadata: {
|
|
307
|
+
...metadata,
|
|
308
|
+
},
|
|
309
|
+
} as unknown as Invoice,
|
|
217
310
|
});
|
|
218
311
|
|
|
219
312
|
logger.info('Invoice created for subscription', { invoice: invoice.id, subscription: subscription.id });
|
|
@@ -4,7 +4,7 @@ import dayjs from '../libs/dayjs';
|
|
|
4
4
|
import { getLock } from '../libs/lock';
|
|
5
5
|
import logger from '../libs/logger';
|
|
6
6
|
import createQueue from '../libs/queue';
|
|
7
|
-
import { getPriceUintAmountByCurrency } from '../libs/
|
|
7
|
+
import { getPriceUintAmountByCurrency } from '../libs/price';
|
|
8
8
|
import { Invoice, PaymentCurrency, Price, SubscriptionItem, TLineItemExpanded, UsageRecord } from '../store/models';
|
|
9
9
|
import { Subscription } from '../store/models/subscription';
|
|
10
10
|
import { invoiceQueue } from './invoice';
|
|
@@ -5,6 +5,7 @@ import { CustomError } from '@blocklet/error';
|
|
|
5
5
|
import { Op } from 'sequelize';
|
|
6
6
|
import sessionMiddleware from '@blocklet/sdk/lib/middlewares/session';
|
|
7
7
|
import { BN, fromTokenToUnit, fromUnitToToken } from '@ocap/util';
|
|
8
|
+
import { trimDecimals } from '../libs/math-utils';
|
|
8
9
|
import {
|
|
9
10
|
AutoRechargeConfig,
|
|
10
11
|
Customer,
|
|
@@ -16,7 +17,8 @@ import {
|
|
|
16
17
|
TPriceExpanded,
|
|
17
18
|
} from '../store/models';
|
|
18
19
|
import { isDelegationSufficientForPayment } from '../libs/payment';
|
|
19
|
-
import {
|
|
20
|
+
import { validateStripePaymentAmounts } from '../libs/session';
|
|
21
|
+
import { getPriceUintAmountByCurrency } from '../libs/price';
|
|
20
22
|
import { ensureStripeSetupIntentForAutoRecharge } from '../integrations/stripe/resource';
|
|
21
23
|
import logger from '../libs/logger';
|
|
22
24
|
|
|
@@ -349,13 +351,13 @@ router.post('/submit', async (req, res) => {
|
|
|
349
351
|
}
|
|
350
352
|
let { threshold, daily_limits: dailyLimits } = configData;
|
|
351
353
|
if (threshold) {
|
|
352
|
-
threshold = fromTokenToUnit(
|
|
354
|
+
threshold = fromTokenToUnit(trimDecimals(threshold, currency.decimal), currency.decimal).toString();
|
|
353
355
|
}
|
|
354
356
|
if (dailyLimits) {
|
|
355
357
|
dailyLimits = {
|
|
356
358
|
max_attempts: dailyLimits.max_attempts || 0,
|
|
357
359
|
max_amount: fromTokenToUnit(
|
|
358
|
-
|
|
360
|
+
trimDecimals(dailyLimits.max_amount || '0', rechargeCurrency.decimal),
|
|
359
361
|
rechargeCurrency.decimal
|
|
360
362
|
).toString(),
|
|
361
363
|
};
|