payment-kit 1.20.15 → 1.20.16
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/libs/discount/coupon.ts +34 -7
- package/api/src/libs/discount/discount.ts +83 -1
- package/api/src/libs/env.ts +1 -0
- package/api/src/libs/payment.ts +1 -1
- package/api/src/libs/url.ts +3 -3
- package/api/src/queues/checkout-session.ts +3 -0
- package/api/src/queues/payment.ts +5 -0
- package/api/src/routes/checkout-sessions.ts +2 -1
- package/api/src/routes/connect/change-plan.ts +23 -12
- package/api/src/routes/connect/collect.ts +1 -0
- package/api/src/routes/connect/delegation.ts +1 -0
- package/api/src/routes/connect/shared.ts +11 -2
- package/api/src/routes/meter-events.ts +8 -1
- package/api/src/routes/products.ts +1 -0
- package/api/src/store/migrations/20250923-add-discount-confirmed.ts +21 -0
- package/api/src/store/models/discount.ts +18 -7
- package/api/src/store/models/index.ts +8 -1
- package/blocklet.yml +7 -1
- package/package.json +17 -17
|
@@ -1,10 +1,19 @@
|
|
|
1
|
-
import { BN } from '@ocap/util';
|
|
1
|
+
import { BN, fromUnitToToken } from '@ocap/util';
|
|
2
2
|
|
|
3
3
|
import pick from 'lodash/pick';
|
|
4
|
-
import {
|
|
5
|
-
|
|
4
|
+
import {
|
|
5
|
+
Coupon,
|
|
6
|
+
Customer,
|
|
7
|
+
Discount,
|
|
8
|
+
PromotionCode,
|
|
9
|
+
Subscription,
|
|
10
|
+
CheckoutSession,
|
|
11
|
+
PaymentCurrency,
|
|
12
|
+
} from '../../store/models';
|
|
13
|
+
import type { TLineItemExpanded } from '../../store/models';
|
|
6
14
|
import logger from '../logger';
|
|
7
15
|
import { emitAsync } from '../event';
|
|
16
|
+
import { formatNumber } from '../util';
|
|
8
17
|
|
|
9
18
|
export function validCoupon(coupon: Coupon, lineItems?: TLineItemExpanded[]) {
|
|
10
19
|
if (!coupon.valid) {
|
|
@@ -75,13 +84,20 @@ export async function validPromotionCode(
|
|
|
75
84
|
? promotionCode.restrictions?.minimum_amount
|
|
76
85
|
: promotionCode.restrictions?.currency_options?.[currencyId]?.minimum_amount;
|
|
77
86
|
|
|
87
|
+
const currency = await PaymentCurrency.findByPk(currencyId);
|
|
78
88
|
if (minimumAmount) {
|
|
79
89
|
const amountBN = new BN(amount);
|
|
80
90
|
const minimumBN = new BN(minimumAmount);
|
|
81
91
|
if (amountBN.lt(minimumBN)) {
|
|
92
|
+
if (!currency) {
|
|
93
|
+
return {
|
|
94
|
+
valid: false,
|
|
95
|
+
reason: 'This promotion requires a minimum purchase amount. Please add more items to your cart.',
|
|
96
|
+
};
|
|
97
|
+
}
|
|
82
98
|
return {
|
|
83
99
|
valid: false,
|
|
84
|
-
reason:
|
|
100
|
+
reason: `This promotion requires a minimum purchase amount of ${formatNumber(fromUnitToToken(minimumBN, currency?.decimal || 2))} ${currency?.symbol}. Please add more items to your cart.`,
|
|
85
101
|
};
|
|
86
102
|
}
|
|
87
103
|
}
|
|
@@ -296,7 +312,7 @@ async function processSubscriptionDiscount({
|
|
|
296
312
|
baseDiscountData: any;
|
|
297
313
|
existingDiscountMap: Map<string, any>;
|
|
298
314
|
checkoutSessionId: string;
|
|
299
|
-
}): Promise<{ discountRecord:
|
|
315
|
+
}): Promise<{ discountRecord: Discount; shouldUpdateUsage: boolean }> {
|
|
300
316
|
try {
|
|
301
317
|
const existingDiscount = existingDiscountMap.get(subscriptionId);
|
|
302
318
|
|
|
@@ -342,6 +358,7 @@ async function processSubscriptionDiscount({
|
|
|
342
358
|
const newDiscount = await Discount.create({
|
|
343
359
|
...baseDiscountData,
|
|
344
360
|
subscription_id: subscriptionId,
|
|
361
|
+
confirmed: false,
|
|
345
362
|
metadata: {
|
|
346
363
|
...baseDiscountData.metadata,
|
|
347
364
|
subscription_id: subscriptionId,
|
|
@@ -375,7 +392,7 @@ async function processNonSubscriptionDiscount(
|
|
|
375
392
|
baseDiscountData: any,
|
|
376
393
|
existingDiscountMap: Map<string, any>,
|
|
377
394
|
checkoutSessionId: string
|
|
378
|
-
): Promise<{ discountRecord:
|
|
395
|
+
): Promise<{ discountRecord: Discount; shouldUpdateUsage: boolean }> {
|
|
379
396
|
try {
|
|
380
397
|
const existingDiscount = existingDiscountMap.get('no_subscription');
|
|
381
398
|
|
|
@@ -412,7 +429,10 @@ async function processNonSubscriptionDiscount(
|
|
|
412
429
|
}
|
|
413
430
|
|
|
414
431
|
// Create new discount record
|
|
415
|
-
const newDiscount = await Discount.create(
|
|
432
|
+
const newDiscount = await Discount.create({
|
|
433
|
+
...baseDiscountData,
|
|
434
|
+
confirmed: false,
|
|
435
|
+
});
|
|
416
436
|
|
|
417
437
|
// Lock coupon and promotion code when discount is created
|
|
418
438
|
await lockDiscountResources(baseDiscountData.coupon_id, baseDiscountData.promotion_code_id);
|
|
@@ -710,6 +730,13 @@ export async function createDiscountRecordsForCheckout({
|
|
|
710
730
|
updatedPromotionCodes,
|
|
711
731
|
checkoutSessionId: checkoutSession.id,
|
|
712
732
|
});
|
|
733
|
+
await Promise.all(
|
|
734
|
+
discountRecords.map(async (discountRecord) => {
|
|
735
|
+
await discountRecord.update({
|
|
736
|
+
confirmed: true,
|
|
737
|
+
});
|
|
738
|
+
})
|
|
739
|
+
);
|
|
713
740
|
} catch (error) {
|
|
714
741
|
logger.error('Failed to update usage counts, but continuing', {
|
|
715
742
|
couponId: coupon.id,
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { BN } from '@ocap/util';
|
|
2
|
-
import { Coupon, Discount, PaymentCurrency, PromotionCode } from '../../store/models';
|
|
2
|
+
import { Coupon, Discount, PaymentCurrency, PromotionCode, CheckoutSession } from '../../store/models';
|
|
3
3
|
import type { TLineItemExpanded } from '../../store/models';
|
|
4
4
|
import { getPriceUintAmountByCurrency } from '../price';
|
|
5
5
|
import { validCoupon, checkPromotionCodeEligibility, calculateDiscountAmount } from './coupon';
|
|
6
|
+
import { emitAsync } from '../event';
|
|
6
7
|
import logger from '../logger';
|
|
7
8
|
|
|
8
9
|
const getItemsTotalAmount = (lineItems: TLineItemExpanded[], currencyId: string, options?: { trialing?: boolean }) => {
|
|
@@ -347,3 +348,84 @@ export async function applySubscriptionDiscount({
|
|
|
347
348
|
|
|
348
349
|
return result;
|
|
349
350
|
}
|
|
351
|
+
|
|
352
|
+
async function rollbackUsageForResource<T extends Coupon | PromotionCode>(
|
|
353
|
+
resource: T,
|
|
354
|
+
resourceType: 'coupon' | 'promotion-code'
|
|
355
|
+
): Promise<void> {
|
|
356
|
+
const currentUsage = resource.times_redeemed || 0;
|
|
357
|
+
if (currentUsage <= 0) return;
|
|
358
|
+
|
|
359
|
+
if (resourceType === 'coupon') {
|
|
360
|
+
await (resource as Coupon).update({
|
|
361
|
+
times_redeemed: currentUsage - 1,
|
|
362
|
+
valid: true,
|
|
363
|
+
});
|
|
364
|
+
} else {
|
|
365
|
+
await (resource as PromotionCode).update({
|
|
366
|
+
times_redeemed: currentUsage - 1,
|
|
367
|
+
active: true,
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
logger.info('Updated usage for resource', {
|
|
372
|
+
resourceId: resource.id,
|
|
373
|
+
resourceType,
|
|
374
|
+
currentUsage: currentUsage - 1,
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
emitAsync('discount-status.queued', resource, resourceType, true);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
export async function rollbackDiscountUsageForCheckoutSession(checkoutSessionId: string): Promise<void> {
|
|
381
|
+
try {
|
|
382
|
+
const checkoutSession = await CheckoutSession.findByPk(checkoutSessionId);
|
|
383
|
+
if (!checkoutSession) {
|
|
384
|
+
logger.error('Checkout session not found', { checkoutSessionId });
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
if (checkoutSession.status === 'complete') {
|
|
389
|
+
logger.error('Cannot rollback completed session', { checkoutSessionId });
|
|
390
|
+
return;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
const discounts = (await Discount.findAll({
|
|
394
|
+
where: { checkout_session_id: checkoutSessionId },
|
|
395
|
+
include: [
|
|
396
|
+
{ model: Coupon, as: 'coupon' },
|
|
397
|
+
{ model: PromotionCode, as: 'promotionCode', required: false },
|
|
398
|
+
],
|
|
399
|
+
})) as (Discount & { coupon: Coupon; promotionCode?: PromotionCode })[];
|
|
400
|
+
|
|
401
|
+
if (discounts.length === 0) {
|
|
402
|
+
logger.info('No discounts found for checkout session', { checkoutSessionId });
|
|
403
|
+
return;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
const uniqueCoupons = new Map<string, Coupon>();
|
|
407
|
+
const uniquePromotionCodes = new Map<string, PromotionCode>();
|
|
408
|
+
|
|
409
|
+
discounts.forEach((discount) => {
|
|
410
|
+
if (discount.confirmed) {
|
|
411
|
+
if (discount.coupon?.id && !uniqueCoupons.has(discount.coupon.id)) {
|
|
412
|
+
uniqueCoupons.set(discount.coupon.id, discount.coupon);
|
|
413
|
+
}
|
|
414
|
+
if (discount.promotionCode?.id && !uniquePromotionCodes.has(discount.promotionCode.id)) {
|
|
415
|
+
uniquePromotionCodes.set(discount.promotionCode.id, discount.promotionCode);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
const rollbackPromises = [
|
|
421
|
+
...Array.from(uniquePromotionCodes.values()).map((pc) => rollbackUsageForResource(pc, 'promotion-code')),
|
|
422
|
+
...Array.from(uniqueCoupons.values()).map((c) => rollbackUsageForResource(c, 'coupon')),
|
|
423
|
+
];
|
|
424
|
+
|
|
425
|
+
await Promise.all(rollbackPromises);
|
|
426
|
+
|
|
427
|
+
await Promise.all(discounts.map((discount) => discount.destroy()));
|
|
428
|
+
} catch (error) {
|
|
429
|
+
logger.error('Discount rollback failed', { checkoutSessionId, error: error.message });
|
|
430
|
+
}
|
|
431
|
+
}
|
package/api/src/libs/env.ts
CHANGED
|
@@ -21,6 +21,7 @@ export const vendorTimeoutMinutes: number = process.env.VENDOR_TIMEOUT_MINUTES
|
|
|
21
21
|
: 10; // 默认 10 分钟超时
|
|
22
22
|
|
|
23
23
|
export const shortUrlApiKey: string = process.env.SHORT_URL_API_KEY || '';
|
|
24
|
+
export const shortUrlDomain: string = process.env.SHORT_URL_DOMAIN || 's.abtnet.io';
|
|
24
25
|
|
|
25
26
|
// sequelize 配置相关
|
|
26
27
|
export const sequelizeOptionsPoolMin: number = process.env.SEQUELIZE_OPTIONS_POOL_MIN
|
package/api/src/libs/payment.ts
CHANGED
|
@@ -368,7 +368,7 @@ export function isCreditSufficientForPayment(args: {
|
|
|
368
368
|
const balance = tokens[paymentCurrency.id] || '0';
|
|
369
369
|
|
|
370
370
|
if (amount === '0') {
|
|
371
|
-
return { sufficient:
|
|
371
|
+
return { sufficient: true, balance };
|
|
372
372
|
}
|
|
373
373
|
|
|
374
374
|
if (new BN(balance).lt(new BN(amount))) {
|
package/api/src/libs/url.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import logger from './logger';
|
|
2
|
-
import { shortUrlApiKey } from './env';
|
|
2
|
+
import { shortUrlApiKey, shortUrlDomain } from './env';
|
|
3
3
|
|
|
4
4
|
interface ShortUrlResponse {
|
|
5
5
|
shortUrl: string;
|
|
@@ -43,14 +43,14 @@ export async function formatToShortUrl({
|
|
|
43
43
|
maxVisits,
|
|
44
44
|
tags: [],
|
|
45
45
|
shortCodeLength: 8,
|
|
46
|
-
domain:
|
|
46
|
+
domain: shortUrlDomain,
|
|
47
47
|
findIfExists: true,
|
|
48
48
|
validateUrl: true,
|
|
49
49
|
forwardQuery: true,
|
|
50
50
|
crawlable: true,
|
|
51
51
|
};
|
|
52
52
|
|
|
53
|
-
const response = await fetch(
|
|
53
|
+
const response = await fetch(`https://${shortUrlDomain}/rest/v3/short-urls`, {
|
|
54
54
|
method: 'POST',
|
|
55
55
|
headers: {
|
|
56
56
|
'Content-Type': 'application/json',
|
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
SubscriptionItem,
|
|
22
22
|
} from '../store/models';
|
|
23
23
|
import { getCheckoutSessionSubscriptionIds } from '../libs/session';
|
|
24
|
+
import { rollbackDiscountUsageForCheckoutSession } from '../libs/discount/discount';
|
|
24
25
|
|
|
25
26
|
type CheckoutSessionJob = {
|
|
26
27
|
id: string;
|
|
@@ -240,6 +241,8 @@ events.on('checkout.session.expired', async (checkoutSession: CheckoutSession) =
|
|
|
240
241
|
);
|
|
241
242
|
}
|
|
242
243
|
|
|
244
|
+
await rollbackDiscountUsageForCheckoutSession(checkoutSession.id);
|
|
245
|
+
|
|
243
246
|
// update price lock status
|
|
244
247
|
for (const item of checkoutSession.line_items) {
|
|
245
248
|
const price = await Price.findByPk(item.price_id);
|
|
@@ -914,6 +914,11 @@ export const handlePayment = async (job: PaymentJob) => {
|
|
|
914
914
|
return;
|
|
915
915
|
}
|
|
916
916
|
|
|
917
|
+
if (paymentIntent.amount === '0') {
|
|
918
|
+
await handlePaymentSucceed(paymentIntent);
|
|
919
|
+
return;
|
|
920
|
+
}
|
|
921
|
+
|
|
917
922
|
await paymentIntent.update({ status: 'processing', last_payment_error: null });
|
|
918
923
|
|
|
919
924
|
if (paymentMethod.type === 'arcblock') {
|
|
@@ -105,8 +105,8 @@ import {
|
|
|
105
105
|
createDiscountRecordsForCheckout,
|
|
106
106
|
updateSubscriptionDiscountReferences,
|
|
107
107
|
} from '../libs/discount/coupon';
|
|
108
|
+
import { rollbackDiscountUsageForCheckoutSession, applyDiscountsToLineItems } from '../libs/discount/discount';
|
|
108
109
|
import { formatToShortUrl } from '../libs/url';
|
|
109
|
-
import { applyDiscountsToLineItems } from '../libs/discount/discount';
|
|
110
110
|
|
|
111
111
|
const router = Router();
|
|
112
112
|
|
|
@@ -2785,6 +2785,7 @@ router.delete('/:id/remove-promotion', user, ensureCheckoutSessionOpen, async (r
|
|
|
2785
2785
|
const trialSetup = getSubscriptionTrialSetup(checkoutSession.subscription_data as any, currency.id);
|
|
2786
2786
|
const isTrialing = trialSetup.trialInDays > 0 || trialSetup.trialEnd > now;
|
|
2787
2787
|
|
|
2788
|
+
await rollbackDiscountUsageForCheckoutSession(checkoutSession.id);
|
|
2788
2789
|
// Calculate original amounts without any discounts
|
|
2789
2790
|
const originalResult = await applyDiscountsToLineItems({
|
|
2790
2791
|
lineItems: originalItems,
|
|
@@ -52,7 +52,9 @@ export default {
|
|
|
52
52
|
amount: fastCheckoutAmount,
|
|
53
53
|
});
|
|
54
54
|
|
|
55
|
-
|
|
55
|
+
const requiredStake = !subscription!.billing_thresholds?.no_stake;
|
|
56
|
+
|
|
57
|
+
if (delegation.sufficient === false || !requiredStake) {
|
|
56
58
|
claimsList.push({
|
|
57
59
|
signature: await getDelegationTxClaim({
|
|
58
60
|
mode: 'subscription',
|
|
@@ -69,16 +71,22 @@ export default {
|
|
|
69
71
|
});
|
|
70
72
|
}
|
|
71
73
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
74
|
+
if (requiredStake) {
|
|
75
|
+
claimsList.push({
|
|
76
|
+
prepareTx: await getStakeTxClaim({
|
|
77
|
+
userDid,
|
|
78
|
+
userPk,
|
|
79
|
+
paymentCurrency,
|
|
80
|
+
paymentMethod,
|
|
81
|
+
items,
|
|
82
|
+
subscription: subscription!,
|
|
83
|
+
}),
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (claimsList.length === 0) {
|
|
88
|
+
throw new Error('No available claims for your subscription at this time.');
|
|
89
|
+
}
|
|
82
90
|
|
|
83
91
|
return claimsList;
|
|
84
92
|
}
|
|
@@ -123,9 +131,12 @@ export default {
|
|
|
123
131
|
},
|
|
124
132
|
});
|
|
125
133
|
|
|
134
|
+
const requiredStake = !subscription!.billing_thresholds?.no_stake;
|
|
135
|
+
|
|
126
136
|
// 判断是否为最后一步
|
|
127
137
|
const staking = result.find((x: any) => x.claim?.type === 'prepareTx' && x.claim?.meta?.purpose === 'staking');
|
|
128
|
-
const isFinalStep =
|
|
138
|
+
const isFinalStep =
|
|
139
|
+
(paymentMethod.type === 'arcblock' && (staking || !requiredStake)) || paymentMethod.type !== 'arcblock';
|
|
129
140
|
|
|
130
141
|
if (!isFinalStep) {
|
|
131
142
|
await updateSession({
|
|
@@ -774,6 +774,7 @@ export async function getDelegationTxClaim({
|
|
|
774
774
|
paymentCurrency,
|
|
775
775
|
requiredStake,
|
|
776
776
|
});
|
|
777
|
+
|
|
777
778
|
if (mode === 'delegation') {
|
|
778
779
|
tokenRequirements = [];
|
|
779
780
|
}
|
|
@@ -1080,9 +1081,15 @@ export async function getTokenRequirements({
|
|
|
1080
1081
|
billingThreshold = 0,
|
|
1081
1082
|
requiredStake,
|
|
1082
1083
|
}: TokenRequirementArgs) {
|
|
1083
|
-
const tokenRequirements = [];
|
|
1084
|
+
const tokenRequirements: { address: string; value: string }[] = [];
|
|
1084
1085
|
let amount = await getFastCheckoutAmount({ items, mode, currencyId: paymentCurrency.id, trialing: !!trialing });
|
|
1085
1086
|
|
|
1087
|
+
const addStakeRequired = requiredStake && ((paymentMethod.type === 'arcblock' && mode !== 'delegation') || mode === 'setup');
|
|
1088
|
+
|
|
1089
|
+
if (!addStakeRequired && amount === '0') {
|
|
1090
|
+
return tokenRequirements;
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1086
1093
|
// If the app has not staked, we need to add the gas fee to the amount
|
|
1087
1094
|
if ((await hasStakedForGas(paymentMethod)) === false) {
|
|
1088
1095
|
const maxGas = await estimateMaxGasForTx(paymentMethod);
|
|
@@ -1106,7 +1113,7 @@ export async function getTokenRequirements({
|
|
|
1106
1113
|
}
|
|
1107
1114
|
|
|
1108
1115
|
// Add stake requirement to token requirement
|
|
1109
|
-
if (
|
|
1116
|
+
if (addStakeRequired) {
|
|
1110
1117
|
const staking = getSubscriptionStakeSetup(
|
|
1111
1118
|
items,
|
|
1112
1119
|
paymentCurrency.id,
|
|
@@ -1115,6 +1122,8 @@ export async function getTokenRequirements({
|
|
|
1115
1122
|
const exist = tokenRequirements.find((x) => x.address === paymentCurrency.contract);
|
|
1116
1123
|
if (exist) {
|
|
1117
1124
|
exist.value = new BN(exist.value).add(staking.licensed).add(staking.metered).toString();
|
|
1125
|
+
} else {
|
|
1126
|
+
tokenRequirements.push({ address: paymentCurrency.contract as string, value: staking.licensed.add(staking.metered).toString() });
|
|
1118
1127
|
}
|
|
1119
1128
|
}
|
|
1120
1129
|
|
|
@@ -320,7 +320,14 @@ router.get('/pending-amount', authMine, async (req, res) => {
|
|
|
320
320
|
where['payload.subscription_id'] = req.query.subscription_id;
|
|
321
321
|
}
|
|
322
322
|
if (req.query.customer_id) {
|
|
323
|
-
|
|
323
|
+
if (typeof req.query.customer_id !== 'string') {
|
|
324
|
+
return res.status(400).json({ error: 'Customer ID must be a string' });
|
|
325
|
+
}
|
|
326
|
+
const customer = await Customer.findByPkOrDid(req.query.customer_id);
|
|
327
|
+
if (!customer) {
|
|
328
|
+
return res.status(404).json({ error: 'Customer not found' });
|
|
329
|
+
}
|
|
330
|
+
where['payload.customer_id'] = customer.id;
|
|
324
331
|
}
|
|
325
332
|
const [summary] = await MeterEvent.getPendingAmounts({
|
|
326
333
|
subscriptionId: req.query.subscription_id as string,
|
|
@@ -44,6 +44,7 @@ const ProductAndPriceSchema = Joi.object({
|
|
|
44
44
|
'string.pattern.base':
|
|
45
45
|
'statement_descriptor should be at least one letter and cannot include Chinese characters and special characters such as <, >、"、’ or \\',
|
|
46
46
|
})
|
|
47
|
+
.allow(null, '')
|
|
47
48
|
.empty('')
|
|
48
49
|
.optional(),
|
|
49
50
|
unit_label: Joi.string().max(12).empty('').optional(),
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { DataTypes } from 'sequelize';
|
|
2
|
+
import { Migration, safeApplyColumnChanges } from '../migrate';
|
|
3
|
+
|
|
4
|
+
export const up: Migration = async ({ context }) => {
|
|
5
|
+
await safeApplyColumnChanges(context, {
|
|
6
|
+
discounts: [
|
|
7
|
+
{
|
|
8
|
+
name: 'confirmed',
|
|
9
|
+
field: {
|
|
10
|
+
type: DataTypes.BOOLEAN,
|
|
11
|
+
defaultValue: true,
|
|
12
|
+
allowNull: false,
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
],
|
|
16
|
+
});
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export const down: Migration = async ({ context }) => {
|
|
20
|
+
await context.removeColumn('discounts', 'confirmed');
|
|
21
|
+
};
|
|
@@ -18,6 +18,7 @@ export class Discount extends Model<InferAttributes<Discount>, InferCreationAttr
|
|
|
18
18
|
declare checkout_session_id?: string;
|
|
19
19
|
declare invoice_id?: string;
|
|
20
20
|
declare invoice_item_id?: string;
|
|
21
|
+
declare confirmed?: boolean;
|
|
21
22
|
|
|
22
23
|
declare start: number;
|
|
23
24
|
declare end: number | null;
|
|
@@ -101,13 +102,23 @@ export class Discount extends Model<InferAttributes<Discount>, InferCreationAttr
|
|
|
101
102
|
};
|
|
102
103
|
|
|
103
104
|
public static initialize(sequelize: any) {
|
|
104
|
-
this.init(
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
105
|
+
this.init(
|
|
106
|
+
{
|
|
107
|
+
...Discount.GENESIS_ATTRIBUTES,
|
|
108
|
+
confirmed: {
|
|
109
|
+
type: DataTypes.BOOLEAN,
|
|
110
|
+
defaultValue: true,
|
|
111
|
+
allowNull: false,
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
sequelize,
|
|
116
|
+
modelName: 'Discount',
|
|
117
|
+
tableName: 'discounts',
|
|
118
|
+
createdAt: 'created_at',
|
|
119
|
+
updatedAt: 'updated_at',
|
|
120
|
+
}
|
|
121
|
+
);
|
|
111
122
|
}
|
|
112
123
|
|
|
113
124
|
public static associate(models: any) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { CheckoutSession, TCheckoutSession } from './checkout-session';
|
|
2
2
|
import { Coupon, TCoupon } from './coupon';
|
|
3
3
|
import { Customer, TCustomer } from './customer';
|
|
4
|
-
import { Discount } from './discount';
|
|
4
|
+
import { Discount, TDiscount } from './discount';
|
|
5
5
|
import { Event, TEvent } from './event';
|
|
6
6
|
import { Invoice, TInvoice } from './invoice';
|
|
7
7
|
import { InvoiceItem, TInvoiceItem } from './invoice-item';
|
|
@@ -326,9 +326,16 @@ export type TCouponExpanded = TCoupon & {
|
|
|
326
326
|
object: 'coupon';
|
|
327
327
|
applied_products?: TProduct[];
|
|
328
328
|
promotion_codes?: TPromotionCode[];
|
|
329
|
+
currency?: TPaymentCurrency;
|
|
329
330
|
};
|
|
330
331
|
|
|
331
332
|
export type TPromotionCodeExpanded = TPromotionCode & {
|
|
332
333
|
object: 'promotion_code';
|
|
333
334
|
coupon?: TCoupon;
|
|
334
335
|
};
|
|
336
|
+
|
|
337
|
+
export type TDiscountExpanded = TDiscount & {
|
|
338
|
+
object: 'discount';
|
|
339
|
+
coupon?: TCouponExpanded;
|
|
340
|
+
promotionCode?: TPromotionCodeExpanded;
|
|
341
|
+
};
|
package/blocklet.yml
CHANGED
|
@@ -14,7 +14,7 @@ repository:
|
|
|
14
14
|
type: git
|
|
15
15
|
url: git+https://github.com/blocklet/payment-kit.git
|
|
16
16
|
specVersion: 1.2.8
|
|
17
|
-
version: 1.20.
|
|
17
|
+
version: 1.20.16
|
|
18
18
|
logo: logo.png
|
|
19
19
|
files:
|
|
20
20
|
- dist
|
|
@@ -73,6 +73,12 @@ environments:
|
|
|
73
73
|
default: ''
|
|
74
74
|
secure: true
|
|
75
75
|
shared: false
|
|
76
|
+
- name: SHORT_URL_DOMAIN
|
|
77
|
+
description: Short URL service domain
|
|
78
|
+
required: false
|
|
79
|
+
default: s.abtnet.io
|
|
80
|
+
secure: false
|
|
81
|
+
shared: false
|
|
76
82
|
capabilities:
|
|
77
83
|
navigation: true
|
|
78
84
|
clusterMode: false
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "payment-kit",
|
|
3
|
-
"version": "1.20.
|
|
3
|
+
"version": "1.20.16",
|
|
4
4
|
"scripts": {
|
|
5
5
|
"dev": "blocklet dev --open",
|
|
6
6
|
"lint": "tsc --noEmit && eslint src api/src --ext .mjs,.js,.jsx,.ts,.tsx",
|
|
@@ -45,32 +45,32 @@
|
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
47
|
"@abtnode/cron": "^1.16.52-beta-20250912-112002-e3499e9c",
|
|
48
|
-
"@arcblock/did": "^1.25.
|
|
48
|
+
"@arcblock/did": "^1.25.3",
|
|
49
49
|
"@arcblock/did-connect-react": "^3.1.41",
|
|
50
50
|
"@arcblock/did-connect-storage-nedb": "^1.8.0",
|
|
51
|
-
"@arcblock/did-util": "^1.25.
|
|
52
|
-
"@arcblock/jwt": "^1.25.
|
|
51
|
+
"@arcblock/did-util": "^1.25.3",
|
|
52
|
+
"@arcblock/jwt": "^1.25.3",
|
|
53
53
|
"@arcblock/ux": "^3.1.41",
|
|
54
|
-
"@arcblock/validator": "^1.25.
|
|
55
|
-
"@blocklet/did-space-js": "^1.1.
|
|
54
|
+
"@arcblock/validator": "^1.25.3",
|
|
55
|
+
"@blocklet/did-space-js": "^1.1.26",
|
|
56
56
|
"@blocklet/error": "^0.2.5",
|
|
57
57
|
"@blocklet/js-sdk": "^1.16.52-beta-20250912-112002-e3499e9c",
|
|
58
58
|
"@blocklet/logger": "^1.16.52-beta-20250912-112002-e3499e9c",
|
|
59
|
-
"@blocklet/payment-react": "1.20.
|
|
60
|
-
"@blocklet/payment-vendor": "1.20.
|
|
59
|
+
"@blocklet/payment-react": "1.20.16",
|
|
60
|
+
"@blocklet/payment-vendor": "1.20.16",
|
|
61
61
|
"@blocklet/sdk": "^1.16.52-beta-20250912-112002-e3499e9c",
|
|
62
62
|
"@blocklet/ui-react": "^3.1.41",
|
|
63
|
-
"@blocklet/uploader": "^0.2.
|
|
63
|
+
"@blocklet/uploader": "^0.2.12",
|
|
64
64
|
"@blocklet/xss": "^0.2.7",
|
|
65
65
|
"@mui/icons-material": "^7.1.2",
|
|
66
66
|
"@mui/lab": "7.0.0-beta.14",
|
|
67
67
|
"@mui/material": "^7.1.2",
|
|
68
68
|
"@mui/system": "^7.1.1",
|
|
69
|
-
"@ocap/asset": "^1.25.
|
|
70
|
-
"@ocap/client": "^1.25.
|
|
71
|
-
"@ocap/mcrypto": "^1.25.
|
|
72
|
-
"@ocap/util": "^1.25.
|
|
73
|
-
"@ocap/wallet": "^1.25.
|
|
69
|
+
"@ocap/asset": "^1.25.3",
|
|
70
|
+
"@ocap/client": "^1.25.3",
|
|
71
|
+
"@ocap/mcrypto": "^1.25.3",
|
|
72
|
+
"@ocap/util": "^1.25.3",
|
|
73
|
+
"@ocap/wallet": "^1.25.3",
|
|
74
74
|
"@stripe/react-stripe-js": "^2.9.0",
|
|
75
75
|
"@stripe/stripe-js": "^2.4.0",
|
|
76
76
|
"ahooks": "^3.8.5",
|
|
@@ -126,7 +126,7 @@
|
|
|
126
126
|
"devDependencies": {
|
|
127
127
|
"@abtnode/types": "^1.16.52-beta-20250912-112002-e3499e9c",
|
|
128
128
|
"@arcblock/eslint-config-ts": "^0.3.3",
|
|
129
|
-
"@blocklet/payment-types": "1.20.
|
|
129
|
+
"@blocklet/payment-types": "1.20.16",
|
|
130
130
|
"@types/cookie-parser": "^1.4.9",
|
|
131
131
|
"@types/cors": "^2.8.19",
|
|
132
132
|
"@types/debug": "^4.1.12",
|
|
@@ -157,7 +157,7 @@
|
|
|
157
157
|
"vite": "^7.0.0",
|
|
158
158
|
"vite-node": "^3.2.4",
|
|
159
159
|
"vite-plugin-babel-import": "^2.0.5",
|
|
160
|
-
"vite-plugin-blocklet": "^0.11.
|
|
160
|
+
"vite-plugin-blocklet": "^0.11.1",
|
|
161
161
|
"vite-plugin-node-polyfills": "^0.23.0",
|
|
162
162
|
"vite-plugin-svgr": "^4.3.0",
|
|
163
163
|
"vite-tsconfig-paths": "^5.1.4",
|
|
@@ -173,5 +173,5 @@
|
|
|
173
173
|
"parser": "typescript"
|
|
174
174
|
}
|
|
175
175
|
},
|
|
176
|
-
"gitHead": "
|
|
176
|
+
"gitHead": "ebf0677dd4414d4abc4129d6663a7e9ddb415281"
|
|
177
177
|
}
|