payment-kit 1.14.3 → 1.14.5
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/README.md +10 -5
- package/api/src/libs/session.ts +16 -0
- package/api/src/routes/checkout-sessions.ts +9 -4
- package/api/src/routes/connect/setup.ts +2 -1
- package/api/src/routes/connect/shared.ts +4 -2
- package/api/src/routes/connect/subscribe.ts +2 -1
- package/api/src/routes/pricing-table.ts +2 -1
- package/api/src/store/models/subscription.ts +1 -0
- package/api/src/store/models/types.ts +1 -0
- package/blocklet.yml +1 -1
- package/package.json +4 -4
- package/src/pages/customer/subscription/embed.tsx +31 -1
package/README.md
CHANGED
|
@@ -11,15 +11,19 @@ The decentralized stripe for blocklet platform.
|
|
|
11
11
|
3. run `cd blocklets/core && blocklet dev`
|
|
12
12
|
|
|
13
13
|
##### when error
|
|
14
|
+
|
|
14
15
|
1. pre-start error component xxx is not running or unreachable
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
-
|
|
19
|
-
|
|
16
|
+
|
|
17
|
+
- create .env.local file in this root
|
|
18
|
+
- add BLOCKLET_DEV_APP_DID="did:abt:your payment kit server did"
|
|
19
|
+
- add BLOCKLET_DEV_MOUNT_POINT="/example"
|
|
20
|
+
- copy .env.local to be under the /core
|
|
21
|
+
- edit BLOCKLET_DEV_MOUNT_POINT="/"
|
|
22
|
+
|
|
20
23
|
2. Insufficient fund to pay for tx cost from xxx, expected 1.0020909, got 0
|
|
21
24
|
- copy BLOCKLET_DEV_APP_DID
|
|
22
25
|
- transfer 2 TBA in your DID Wallet to your copied address
|
|
26
|
+
|
|
23
27
|
### Debug Stripe
|
|
24
28
|
|
|
25
29
|
1. Install and login with instructions from: https://stripe.com/docs/stripe-cli
|
|
@@ -37,5 +41,6 @@ None public environment variables used in the code, can change the behavior of t
|
|
|
37
41
|
- PAYMENT_CHANGE_LOCKED_PRICE: allow change locked price, must be set to "1" to work
|
|
38
42
|
- PAYMENT_RELOAD_SUBSCRIPTION_JOBS: reload subscription jobs on start, must be set to "1" to work
|
|
39
43
|
- PAYMENT_BILLING_THRESHOLD: global default billing threshold, must be a number
|
|
44
|
+
- PAYMENT_MIN_STAKE_AMOUNT: global min stake amount limit, must be a number
|
|
40
45
|
- PAYMENT_DAYS_UNTIL_DUE: global default days until due, must be a number
|
|
41
46
|
- PAYMENT_DAYS_UNTIL_CANCEL: global default days until cancel, must be a number
|
package/api/src/libs/session.ts
CHANGED
|
@@ -299,6 +299,22 @@ export function getBillingThreshold(config: Record<string, any> = {}) {
|
|
|
299
299
|
return 0;
|
|
300
300
|
}
|
|
301
301
|
|
|
302
|
+
export function getMinStakeAmount(config: Record<string, any> = {}) {
|
|
303
|
+
if (config?.min_stake_amount) {
|
|
304
|
+
const threshold = +(config.min_stake_amount as string);
|
|
305
|
+
if (threshold > 0) {
|
|
306
|
+
return threshold;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
const threshold = +(process.env.PAYMENT_MIN_STAKE_AMOUNT as string);
|
|
311
|
+
if (threshold > 0) {
|
|
312
|
+
return threshold;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
return 0;
|
|
316
|
+
}
|
|
317
|
+
|
|
302
318
|
export function canPayWithDelegation(beneficiaries: PaymentBeneficiary[]) {
|
|
303
319
|
return beneficiaries.length === 0 || beneficiaries.every((x) => x.address === wallet.address);
|
|
304
320
|
}
|
|
@@ -30,6 +30,7 @@ import {
|
|
|
30
30
|
getCheckoutAmount,
|
|
31
31
|
getCheckoutMode,
|
|
32
32
|
getFastCheckoutAmount,
|
|
33
|
+
getMinStakeAmount,
|
|
33
34
|
getStatementDescriptor,
|
|
34
35
|
getSupportedPaymentCurrencies,
|
|
35
36
|
getSupportedPaymentMethods,
|
|
@@ -146,6 +147,7 @@ export const formatCheckoutSession = async (payload: any, throwOnEmptyItems = tr
|
|
|
146
147
|
description: '',
|
|
147
148
|
trial_period_days: 0,
|
|
148
149
|
billing_threshold_amount: 0,
|
|
150
|
+
min_stake_amount: 0,
|
|
149
151
|
trial_end: 0,
|
|
150
152
|
},
|
|
151
153
|
payment_intent_data: {},
|
|
@@ -373,6 +375,7 @@ export async function startCheckoutSessionFromPaymentLink(id: string, req: Reque
|
|
|
373
375
|
raw.payment_link_id = link.id;
|
|
374
376
|
raw.subscription_data = merge(link.subscription_data, getDataObjectFromQuery(req.query, 'subscription_data'), {
|
|
375
377
|
billing_threshold_amount: getBillingThreshold(link.subscription_data),
|
|
378
|
+
min_stake_amount: getMinStakeAmount(link.subscription_data),
|
|
376
379
|
});
|
|
377
380
|
|
|
378
381
|
if (link.after_completion?.hosted_confirmation?.custom_message) {
|
|
@@ -559,6 +562,7 @@ router.put('/:id/submit', user, ensureCheckoutSessionOpen, async (req, res) => {
|
|
|
559
562
|
const trialInDays = Number(checkoutSession.subscription_data?.trial_period_days || 0);
|
|
560
563
|
const trialEnds = Number(checkoutSession.subscription_data?.trial_end || 0);
|
|
561
564
|
const billingThreshold = Number(checkoutSession.subscription_data?.billing_threshold_amount || 0);
|
|
565
|
+
const minStakeAmount = Number(checkoutSession.subscription_data?.min_stake_amount || 0);
|
|
562
566
|
const amount = getCheckoutAmount(lineItems, paymentCurrency.id, trialInDays > 0 || trialEnds > now);
|
|
563
567
|
await checkoutSession.update({
|
|
564
568
|
amount_subtotal: amount.subtotal,
|
|
@@ -809,10 +813,11 @@ router.put('/:id/submit', user, ensureCheckoutSessionOpen, async (req, res) => {
|
|
|
809
813
|
missing_payment_method: 'create_invoice',
|
|
810
814
|
},
|
|
811
815
|
},
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
:
|
|
816
|
+
billing_thresholds: {
|
|
817
|
+
amount_gte: billingThreshold,
|
|
818
|
+
stake_gte: minStakeAmount,
|
|
819
|
+
reset_billing_cycle_anchor: false,
|
|
820
|
+
},
|
|
816
821
|
pending_invoice_item_interval: setup.recurring,
|
|
817
822
|
pending_setup_intent: setupIntent?.id,
|
|
818
823
|
default_payment_method_id: paymentMethod.id,
|
|
@@ -43,6 +43,7 @@ export default {
|
|
|
43
43
|
const trialEnds = Number(checkoutSession.subscription_data?.trial_end || 0);
|
|
44
44
|
const trialing = trialInDays > 0 || trialEnds > now;
|
|
45
45
|
const billingThreshold = Number(checkoutSession.subscription_data?.billing_threshold_amount || 0);
|
|
46
|
+
const minStakeAmount = Number(checkoutSession.subscription_data?.min_stake_amount || 0);
|
|
46
47
|
const fastCheckoutAmount = getFastCheckoutAmount(items, checkoutSession.mode, paymentCurrency.id, trialing);
|
|
47
48
|
|
|
48
49
|
if (paymentMethod.type === 'arcblock') {
|
|
@@ -65,7 +66,7 @@ export default {
|
|
|
65
66
|
paymentCurrency,
|
|
66
67
|
paymentMethod,
|
|
67
68
|
trialing,
|
|
68
|
-
billingThreshold,
|
|
69
|
+
billingThreshold: Math.max(minStakeAmount, billingThreshold),
|
|
69
70
|
items,
|
|
70
71
|
}),
|
|
71
72
|
];
|
|
@@ -732,8 +732,10 @@ export async function getStakeTxClaim({
|
|
|
732
732
|
paymentMethod: PaymentMethod;
|
|
733
733
|
}) {
|
|
734
734
|
// create staking amount
|
|
735
|
-
const billingThreshold =
|
|
736
|
-
const
|
|
735
|
+
const billingThreshold = Number(subscription.billing_thresholds?.amount_gte || 0);
|
|
736
|
+
const minStakeAmount = Number(subscription.billing_thresholds?.stake_gte || 0);
|
|
737
|
+
const threshold = fromTokenToUnit(Math.max(billingThreshold, minStakeAmount), paymentCurrency.decimal);
|
|
738
|
+
const staking = getSubscriptionStakeSetup(items, paymentCurrency.id, threshold.toString());
|
|
737
739
|
const amount = staking.licensed.add(staking.metered).toString();
|
|
738
740
|
|
|
739
741
|
if (paymentMethod.type === 'arcblock') {
|
|
@@ -44,6 +44,7 @@ export default {
|
|
|
44
44
|
const trialEnds = Number(checkoutSession.subscription_data?.trial_end || 0);
|
|
45
45
|
const trialing = trialInDays > 0 || trialEnds > now;
|
|
46
46
|
const billingThreshold = Number(checkoutSession.subscription_data?.billing_threshold_amount || 0);
|
|
47
|
+
const minStakeAmount = Number(checkoutSession.subscription_data?.min_stake_amount || 0);
|
|
47
48
|
const fastCheckoutAmount = getFastCheckoutAmount(items, checkoutSession.mode, paymentCurrency.id, trialing);
|
|
48
49
|
|
|
49
50
|
if (paymentMethod.type === 'arcblock') {
|
|
@@ -67,7 +68,7 @@ export default {
|
|
|
67
68
|
paymentCurrency,
|
|
68
69
|
paymentMethod,
|
|
69
70
|
trialing,
|
|
70
|
-
billingThreshold,
|
|
71
|
+
billingThreshold: Math.max(minStakeAmount, billingThreshold),
|
|
71
72
|
items,
|
|
72
73
|
}),
|
|
73
74
|
];
|
|
@@ -11,7 +11,7 @@ import { checkPassportForPricingTable } from '../integrations/blocklet/passport'
|
|
|
11
11
|
import { createListParamSchema } from '../libs/api';
|
|
12
12
|
import logger from '../libs/logger';
|
|
13
13
|
import { authenticate } from '../libs/security';
|
|
14
|
-
import { getBillingThreshold, isLineItemCurrencyAligned } from '../libs/session';
|
|
14
|
+
import { getBillingThreshold, getMinStakeAmount, isLineItemCurrencyAligned } from '../libs/session';
|
|
15
15
|
import { getDaysUntilCancel, getDaysUntilDue } from '../libs/subscription';
|
|
16
16
|
import { getDataObjectFromQuery } from '../libs/util';
|
|
17
17
|
import { CheckoutSession } from '../store/models/checkout-session';
|
|
@@ -224,6 +224,7 @@ router.post('/:id/checkout/:priceId', async (req, res) => {
|
|
|
224
224
|
]),
|
|
225
225
|
subscription_data: merge(price.subscription_data || {}, getDataObjectFromQuery(req.query, 'subscription_data'), {
|
|
226
226
|
billing_threshold_amount: getBillingThreshold(price.subscription_data),
|
|
227
|
+
min_stake_amount: getMinStakeAmount(price.subscription_data),
|
|
227
228
|
}),
|
|
228
229
|
metadata: {
|
|
229
230
|
...doc.metadata,
|
package/blocklet.yml
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "payment-kit",
|
|
3
|
-
"version": "1.14.
|
|
3
|
+
"version": "1.14.5",
|
|
4
4
|
"scripts": {
|
|
5
5
|
"dev": "blocklet dev --open",
|
|
6
6
|
"eject": "vite eject",
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"@arcblock/validator": "^1.18.124",
|
|
53
53
|
"@blocklet/js-sdk": "1.16.28",
|
|
54
54
|
"@blocklet/logger": "1.16.28",
|
|
55
|
-
"@blocklet/payment-react": "1.14.
|
|
55
|
+
"@blocklet/payment-react": "1.14.5",
|
|
56
56
|
"@blocklet/sdk": "1.16.28",
|
|
57
57
|
"@blocklet/ui-react": "^2.10.3",
|
|
58
58
|
"@blocklet/uploader": "^0.1.20",
|
|
@@ -118,7 +118,7 @@
|
|
|
118
118
|
"devDependencies": {
|
|
119
119
|
"@abtnode/types": "1.16.28",
|
|
120
120
|
"@arcblock/eslint-config-ts": "^0.3.2",
|
|
121
|
-
"@blocklet/payment-types": "1.14.
|
|
121
|
+
"@blocklet/payment-types": "1.14.5",
|
|
122
122
|
"@types/cookie-parser": "^1.4.7",
|
|
123
123
|
"@types/cors": "^2.8.17",
|
|
124
124
|
"@types/debug": "^4.1.12",
|
|
@@ -160,5 +160,5 @@
|
|
|
160
160
|
"parser": "typescript"
|
|
161
161
|
}
|
|
162
162
|
},
|
|
163
|
-
"gitHead": "
|
|
163
|
+
"gitHead": "e4715805a87b2e4e3622564f4bc26578f19211e4"
|
|
164
164
|
}
|
|
@@ -5,9 +5,11 @@ import {
|
|
|
5
5
|
api,
|
|
6
6
|
formatBNStr,
|
|
7
7
|
formatError,
|
|
8
|
+
formatPrettyMsLocale,
|
|
8
9
|
formatSubscriptionProduct,
|
|
9
10
|
formatTime,
|
|
10
11
|
formatToDate,
|
|
12
|
+
formatToDatetime,
|
|
11
13
|
getDidConnectQueryParams,
|
|
12
14
|
getInvoiceStatusColor,
|
|
13
15
|
getPrefix,
|
|
@@ -25,11 +27,13 @@ import {
|
|
|
25
27
|
ListItem,
|
|
26
28
|
ListSubheader,
|
|
27
29
|
Stack,
|
|
30
|
+
Tooltip,
|
|
28
31
|
Typography,
|
|
29
32
|
} from '@mui/material';
|
|
30
33
|
import { useRequest } from 'ahooks';
|
|
31
34
|
import { useMemo } from 'react';
|
|
32
35
|
import { joinURL, withQuery } from 'ufo';
|
|
36
|
+
import prettyMs from 'pretty-ms-i18n';
|
|
33
37
|
|
|
34
38
|
const fetchInvoiceData = (params: Record<string, any> = {}): Promise<Paginated<TInvoiceExpanded>> => {
|
|
35
39
|
const search = new URLSearchParams();
|
|
@@ -112,7 +116,33 @@ export default function SubscriptionEmbed() {
|
|
|
112
116
|
},
|
|
113
117
|
];
|
|
114
118
|
|
|
115
|
-
if (subscription.status === '
|
|
119
|
+
if (subscription.status === 'trialing') {
|
|
120
|
+
const endTimestamp = subscription.current_period_end * 1000;
|
|
121
|
+
const remainingTime = endTimestamp - Date.now();
|
|
122
|
+
|
|
123
|
+
infoList.push({
|
|
124
|
+
name: t('payment.customer.subscriptions.trialLeft'),
|
|
125
|
+
value: (
|
|
126
|
+
<Typography
|
|
127
|
+
sx={{
|
|
128
|
+
color: 'success.main',
|
|
129
|
+
fontWeight: 'bold',
|
|
130
|
+
marginRight: '10px',
|
|
131
|
+
}}>
|
|
132
|
+
<Tooltip title={formatToDatetime(endTimestamp, locale)} placement="top-end" enterTouchDelay={0}>
|
|
133
|
+
<Typography component="span" sx={{ cursor: 'pointer' }}>
|
|
134
|
+
{prettyMs(remainingTime, {
|
|
135
|
+
locale: formatPrettyMsLocale(locale),
|
|
136
|
+
compact: true,
|
|
137
|
+
})}
|
|
138
|
+
</Typography>
|
|
139
|
+
</Tooltip>
|
|
140
|
+
</Typography>
|
|
141
|
+
),
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (subscription.status === 'active') {
|
|
116
146
|
infoList.push({
|
|
117
147
|
name: t('payment.customer.subscriptions.nextInvoice'),
|
|
118
148
|
value: (
|