payment-kit 1.18.11 → 1.18.13
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/notification/template/one-time-payment-succeeded.ts +5 -3
- package/api/src/libs/notification/template/subscription-canceled.ts +3 -3
- package/api/src/libs/notification/template/subscription-refund-succeeded.ts +4 -3
- package/api/src/libs/notification/template/subscription-renew-failed.ts +5 -4
- package/api/src/libs/notification/template/subscription-renewed.ts +2 -1
- package/api/src/libs/notification/template/subscription-stake-slash-succeeded.ts +3 -4
- package/api/src/libs/notification/template/subscription-succeeded.ts +2 -1
- package/api/src/libs/notification/template/subscription-upgraded.ts +6 -4
- package/api/src/libs/notification/template/subscription-will-canceled.ts +6 -3
- package/api/src/libs/notification/template/subscription-will-renew.ts +1 -1
- package/api/src/routes/connect/change-payment.ts +1 -0
- package/api/src/routes/connect/change-plan.ts +1 -0
- package/api/src/routes/connect/setup.ts +1 -0
- package/api/src/routes/connect/shared.ts +39 -33
- package/api/src/routes/connect/subscribe.ts +14 -8
- package/api/src/routes/customers.ts +79 -5
- package/api/src/routes/subscriptions.ts +13 -1
- package/api/src/store/models/invoice.ts +4 -2
- package/blocklet.yml +3 -3
- package/package.json +15 -15
- package/src/app.tsx +17 -17
- package/src/components/actions.tsx +32 -9
- package/src/components/copyable.tsx +2 -2
- package/src/components/layout/user.tsx +37 -0
- package/src/components/subscription/portal/actions.tsx +26 -5
- package/src/components/subscription/portal/list.tsx +24 -6
- package/src/components/subscription/status.tsx +2 -2
- package/src/libs/util.ts +15 -0
- package/src/pages/admin/payments/payouts/detail.tsx +6 -1
- package/src/pages/customer/index.tsx +247 -154
- package/src/pages/customer/invoice/detail.tsx +1 -1
- package/src/pages/customer/payout/detail.tsx +9 -2
- package/src/pages/customer/recharge.tsx +6 -2
- package/src/pages/customer/subscription/change-payment.tsx +1 -1
- package/src/pages/customer/subscription/change-plan.tsx +1 -1
- package/src/pages/customer/subscription/detail.tsx +8 -3
- package/src/pages/customer/subscription/embed.tsx +142 -84
|
@@ -87,9 +87,6 @@ export class OneTimePaymentSucceededEmailTemplate
|
|
|
87
87
|
const productName = await getMainProductNameByCheckoutSession(checkoutSession);
|
|
88
88
|
const at: string = formatTime(checkoutSession.created_at);
|
|
89
89
|
|
|
90
|
-
const paymentInfo: string = `${fromUnitToToken(checkoutSession?.amount_total, paymentCurrency.decimal)} ${
|
|
91
|
-
paymentCurrency.symbol
|
|
92
|
-
}`;
|
|
93
90
|
const hasNft: boolean = checkoutSession?.nft_mint_status === 'minted';
|
|
94
91
|
const nftMintItem: NftMintItem | undefined = hasNft
|
|
95
92
|
? // @ts-expect-error
|
|
@@ -98,6 +95,11 @@ export class OneTimePaymentSucceededEmailTemplate
|
|
|
98
95
|
|
|
99
96
|
const paymentIntent = await PaymentIntent.findByPk(checkoutSession!.payment_intent_id);
|
|
100
97
|
const paymentMethod: PaymentMethod | null = await PaymentMethod.findByPk(paymentIntent!.payment_method_id);
|
|
98
|
+
|
|
99
|
+
const paymentInfo: string = `${fromUnitToToken(checkoutSession?.amount_total, paymentCurrency.decimal)} ${
|
|
100
|
+
paymentCurrency.symbol
|
|
101
|
+
}${paymentMethod ? ` (${paymentMethod.name})` : ''}`;
|
|
102
|
+
|
|
101
103
|
// @ts-expect-error
|
|
102
104
|
const chainHost: string | undefined = paymentMethod?.settings?.[paymentMethod.type]?.api_host;
|
|
103
105
|
const viewSubscriptionLink = '';
|
|
@@ -76,9 +76,6 @@ export class SubscriptionCanceledEmailTemplate implements BaseEmailTemplate<Subs
|
|
|
76
76
|
const locale = await getUserLocale(userDid);
|
|
77
77
|
const productName = await getMainProductName(subscription.id);
|
|
78
78
|
const at: string = formatTime(subscription.canceled_at * 1000);
|
|
79
|
-
|
|
80
|
-
// @ts-ignore
|
|
81
|
-
const paymentInfo: string = `${fromUnitToToken(invoice.total, invoice?.paymentCurrency?.decimal)} ${invoice?.paymentCurrency?.symbol}`;
|
|
82
79
|
const currentPeriodStart: string = formatTime(invoice.period_start * 1000);
|
|
83
80
|
const currentPeriodEnd: string = formatTime(invoice.period_end * 1000);
|
|
84
81
|
const duration: string = prettyMsI18n(
|
|
@@ -89,6 +86,9 @@ export class SubscriptionCanceledEmailTemplate implements BaseEmailTemplate<Subs
|
|
|
89
86
|
);
|
|
90
87
|
const paymentMethod: PaymentMethod | null = await PaymentMethod.findByPk(invoice.default_payment_method_id);
|
|
91
88
|
|
|
89
|
+
// @ts-ignore
|
|
90
|
+
const paymentInfo: string = `${fromUnitToToken(invoice.total, invoice?.paymentCurrency?.decimal)} ${invoice?.paymentCurrency?.symbol}${paymentMethod ? ` (${paymentMethod.name})` : ''}`;
|
|
91
|
+
|
|
92
92
|
const customerCancelRequest = subscription.cancelation_details?.reason === 'cancellation_requested';
|
|
93
93
|
let cancellationReason = '';
|
|
94
94
|
const { stakeEnough, stakeReturn, stakeSlash, hasStake } = await getSubscriptionStakeCancellation(
|
|
@@ -75,9 +75,6 @@ export class SubscriptionRefundSucceededEmailTemplate
|
|
|
75
75
|
const productName = await getMainProductName(refund.subscription_id!);
|
|
76
76
|
const at: string = formatTime(refund.created_at);
|
|
77
77
|
|
|
78
|
-
const paymentInfo: string = `${fromUnitToToken(paymentIntent?.amount_received || '0', paymentCurrency.decimal)} ${paymentCurrency.symbol}`;
|
|
79
|
-
const refundInfo: string = `${fromUnitToToken(refund.amount, paymentCurrency.decimal)} ${paymentCurrency.symbol}`;
|
|
80
|
-
|
|
81
78
|
let invoice = null;
|
|
82
79
|
let currentPeriodStart;
|
|
83
80
|
let currentPeriodEnd;
|
|
@@ -105,6 +102,10 @@ export class SubscriptionRefundSucceededEmailTemplate
|
|
|
105
102
|
const paymentMethod: PaymentMethod | null = await PaymentMethod.findByPk(
|
|
106
103
|
refund.payment_method_id || invoice?.default_payment_method_id
|
|
107
104
|
);
|
|
105
|
+
|
|
106
|
+
const paymentInfo: string = `${fromUnitToToken(paymentIntent?.amount_received || '0', paymentCurrency.decimal)} ${paymentCurrency.symbol}${paymentMethod ? ` (${paymentMethod.name})` : ''}`;
|
|
107
|
+
const refundInfo: string = `${fromUnitToToken(refund.amount, paymentCurrency.decimal)} ${paymentCurrency.symbol}${paymentMethod ? ` (${paymentMethod.name})` : ''}`;
|
|
108
|
+
|
|
108
109
|
// @ts-expect-error
|
|
109
110
|
const chainHost: string | undefined = paymentMethod?.settings?.[paymentMethod.type]?.api_host;
|
|
110
111
|
const viewSubscriptionLink = getCustomerSubscriptionPageUrl({
|
|
@@ -112,9 +112,6 @@ export class SubscriptionRenewFailedEmailTemplate
|
|
|
112
112
|
const nftMintItem: NftMintItem | undefined = hasNft
|
|
113
113
|
? checkoutSession?.nft_mint_details?.[checkoutSession?.nft_mint_details?.type as NFTMintChainType]
|
|
114
114
|
: undefined;
|
|
115
|
-
const paymentInfo: string = `${fromUnitToToken(invoice.amount_remaining, paymentCurrency.decimal)} ${
|
|
116
|
-
paymentCurrency.symbol
|
|
117
|
-
}`;
|
|
118
115
|
|
|
119
116
|
const currentPeriodStart: string = formatTime(invoice.period_start * 1000);
|
|
120
117
|
const currentPeriodEnd: string = formatTime(invoice.period_end * 1000);
|
|
@@ -124,8 +121,12 @@ export class SubscriptionRenewFailedEmailTemplate
|
|
|
124
121
|
locale: getPrettyMsI18nLocale(locale),
|
|
125
122
|
}
|
|
126
123
|
);
|
|
127
|
-
|
|
128
124
|
const paymentMethod: PaymentMethod | null = await PaymentMethod.findByPk(invoice.default_payment_method_id);
|
|
125
|
+
|
|
126
|
+
const paymentInfo: string = `${fromUnitToToken(invoice.amount_remaining, paymentCurrency.decimal)} ${
|
|
127
|
+
paymentCurrency.symbol
|
|
128
|
+
}${paymentMethod ? ` (${paymentMethod.name})` : ''}`;
|
|
129
|
+
|
|
129
130
|
const chainHost: string | undefined =
|
|
130
131
|
paymentMethod?.settings?.[checkoutSession?.nft_mint_details?.type as NFTMintChainType]?.api_host;
|
|
131
132
|
const viewSubscriptionLink = getCustomerSubscriptionPageUrl({
|
|
@@ -102,7 +102,6 @@ export class SubscriptionRenewedEmailTemplate implements BaseEmailTemplate<Subsc
|
|
|
102
102
|
? checkoutSession?.nft_mint_details?.[checkoutSession?.nft_mint_details?.type as NFTMintChainType]
|
|
103
103
|
: undefined;
|
|
104
104
|
const amountPaid = +fromUnitToToken(invoice.total, paymentCurrency.decimal);
|
|
105
|
-
const paymentInfo: string = `${fromUnitToToken(invoice.total, paymentCurrency.decimal)} ${paymentCurrency.symbol}`;
|
|
106
105
|
const currentPeriodStart: string = formatTime(invoice.period_start * 1000);
|
|
107
106
|
const currentPeriodEnd: string = formatTime(invoice.period_end * 1000);
|
|
108
107
|
const duration: string = prettyMsI18n(
|
|
@@ -113,6 +112,8 @@ export class SubscriptionRenewedEmailTemplate implements BaseEmailTemplate<Subsc
|
|
|
113
112
|
);
|
|
114
113
|
|
|
115
114
|
const paymentMethod: PaymentMethod | null = await PaymentMethod.findByPk(invoice.default_payment_method_id);
|
|
115
|
+
const paymentInfo: string = `${fromUnitToToken(invoice.total, paymentCurrency.decimal)} ${paymentCurrency.symbol}${paymentMethod ? ` (${paymentMethod.name})` : ''}`;
|
|
116
|
+
|
|
116
117
|
const chainHost: string | undefined =
|
|
117
118
|
paymentMethod?.settings?.[checkoutSession?.nft_mint_details?.type as NFTMintChainType]?.api_host;
|
|
118
119
|
const viewSubscriptionLink = getCustomerSubscriptionPageUrl({
|
|
@@ -10,7 +10,6 @@ import { getCustomerSubscriptionPageUrl } from '../../subscription';
|
|
|
10
10
|
import { formatTime } from '../../time';
|
|
11
11
|
import { getExplorerLink, getSubscriptionNotificationCustomActions } from '../../util';
|
|
12
12
|
import type { BaseEmailTemplate, BaseEmailTemplateType } from './base';
|
|
13
|
-
import logger from '../../logger';
|
|
14
13
|
|
|
15
14
|
export interface SubscriptionStakeSlashSucceededEmailTemplateOptions {
|
|
16
15
|
paymentIntentId: string;
|
|
@@ -83,9 +82,10 @@ export class SubscriptionStakeSlashSucceededEmailTemplate
|
|
|
83
82
|
const productName = await getMainProductName(this.options.subscriptionId);
|
|
84
83
|
const at: string = formatTime(paymentIntent.created_at);
|
|
85
84
|
|
|
86
|
-
const slashInfo: string = `${fromUnitToToken(paymentIntent?.amount_received || '0', paymentCurrency.decimal)} ${paymentCurrency.symbol}`;
|
|
87
|
-
|
|
88
85
|
const paymentMethod: PaymentMethod | null = await PaymentMethod.findByPk(paymentIntent.payment_method_id);
|
|
86
|
+
|
|
87
|
+
const slashInfo: string = `${fromUnitToToken(paymentIntent?.amount_received || '0', paymentCurrency.decimal)} ${paymentCurrency.symbol}${paymentMethod ? ` (${paymentMethod.name})` : ''}`;
|
|
88
|
+
|
|
89
89
|
// @ts-expect-error
|
|
90
90
|
const chainHost: string | undefined = paymentMethod?.settings?.[paymentMethod.type]?.api_host;
|
|
91
91
|
const viewSubscriptionLink = getCustomerSubscriptionPageUrl({
|
|
@@ -140,7 +140,6 @@ export class SubscriptionStakeSlashSucceededEmailTemplate
|
|
|
140
140
|
customActions,
|
|
141
141
|
} = await this.getContext();
|
|
142
142
|
|
|
143
|
-
logger.info('SubscriptionStakeSlashSucceededEmailTemplate getTemplate', { productName, at, userDid, slashInfo, viewSubscriptionLink, viewTxHashLink });
|
|
144
143
|
const template: BaseEmailTemplateType = {
|
|
145
144
|
title: `${translate('notification.subscriptionStakeSlashSucceeded.title', locale, {
|
|
146
145
|
productName,
|
|
@@ -120,7 +120,6 @@ export class SubscriptionSucceededEmailTemplate
|
|
|
120
120
|
const oneTimeProductInfo = await getOneTimeProductInfo(subscription.latest_invoice_id as string, paymentCurrency);
|
|
121
121
|
const paymentAmount = await getPaymentAmountForCycleSubscription(subscription, paymentCurrency);
|
|
122
122
|
|
|
123
|
-
const paymentInfo: string = `${paymentAmount} ${paymentCurrency.symbol}`;
|
|
124
123
|
const hasNft: boolean = checkoutSession?.nft_mint_status === 'minted';
|
|
125
124
|
const nftMintItem: NftMintItem | undefined = hasNft
|
|
126
125
|
? checkoutSession?.nft_mint_details?.[checkoutSession?.nft_mint_details?.type as NFTMintChainType]
|
|
@@ -135,6 +134,8 @@ export class SubscriptionSucceededEmailTemplate
|
|
|
135
134
|
);
|
|
136
135
|
|
|
137
136
|
const paymentMethod: PaymentMethod | null = await PaymentMethod.findByPk(invoice.default_payment_method_id);
|
|
137
|
+
const paymentInfo: string = `${paymentAmount} ${paymentCurrency.symbol}${paymentMethod ? ` (${paymentMethod.name})` : ''}`;
|
|
138
|
+
|
|
138
139
|
// @FIXME: 获取 chainHost 困难的一批?
|
|
139
140
|
const chainHost: string | undefined =
|
|
140
141
|
paymentMethod?.settings?.[checkoutSession?.nft_mint_details?.type as NFTMintChainType]?.api_host;
|
|
@@ -88,10 +88,6 @@ export class SubscriptionUpgradedEmailTemplate implements BaseEmailTemplate<Subs
|
|
|
88
88
|
const productName = await getMainProductName(subscription.id);
|
|
89
89
|
const at: string = formatTime(subscription.created_at);
|
|
90
90
|
|
|
91
|
-
const paymentInfo: string = `${fromUnitToToken(
|
|
92
|
-
paymentIntent?.amount || invoice.amount_paid,
|
|
93
|
-
paymentCurrency.decimal
|
|
94
|
-
)} ${paymentCurrency.symbol}`;
|
|
95
91
|
const hasNft: boolean = checkoutSession?.nft_mint_status === 'minted';
|
|
96
92
|
const nftMintItem: NftMintItem | undefined = hasNft
|
|
97
93
|
? checkoutSession?.nft_mint_details?.[checkoutSession?.nft_mint_details?.type as NFTMintChainType]
|
|
@@ -110,6 +106,12 @@ export class SubscriptionUpgradedEmailTemplate implements BaseEmailTemplate<Subs
|
|
|
110
106
|
);
|
|
111
107
|
|
|
112
108
|
const paymentMethod: PaymentMethod | null = await PaymentMethod.findByPk(invoice.default_payment_method_id);
|
|
109
|
+
|
|
110
|
+
const paymentInfo: string = `${fromUnitToToken(
|
|
111
|
+
paymentIntent?.amount || invoice.amount_paid,
|
|
112
|
+
paymentCurrency.decimal
|
|
113
|
+
)} ${paymentCurrency.symbol}${paymentMethod ? ` (${paymentMethod.name})` : ''}`;
|
|
114
|
+
|
|
113
115
|
// @ts-expect-error
|
|
114
116
|
const chainHost: string | undefined = paymentMethod?.settings?.[paymentMethod.type]?.api_host;
|
|
115
117
|
const viewSubscriptionLink = getCustomerSubscriptionPageUrl({
|
|
@@ -6,7 +6,7 @@ import type { ManipulateType } from 'dayjs';
|
|
|
6
6
|
import dayjs from '../../dayjs';
|
|
7
7
|
import { getUserLocale } from '../../../integrations/blocklet/notification';
|
|
8
8
|
import { translate } from '../../../locales';
|
|
9
|
-
import { Customer, Invoice, Subscription } from '../../../store/models';
|
|
9
|
+
import { Customer, Invoice, PaymentMethod, Subscription } from '../../../store/models';
|
|
10
10
|
import { PaymentCurrency } from '../../../store/models/payment-currency';
|
|
11
11
|
import { getCustomerInvoicePageUrl } from '../../invoice';
|
|
12
12
|
import logger from '../../logger';
|
|
@@ -82,7 +82,10 @@ export class SubscriptionWillCanceledEmailTemplate
|
|
|
82
82
|
where: {
|
|
83
83
|
id: subscription.latest_invoice_id,
|
|
84
84
|
},
|
|
85
|
-
include: [
|
|
85
|
+
include: [
|
|
86
|
+
{ model: PaymentCurrency, as: 'paymentCurrency' },
|
|
87
|
+
{ model: PaymentMethod, as: 'paymentMethod' },
|
|
88
|
+
],
|
|
86
89
|
});
|
|
87
90
|
if (!invoice) {
|
|
88
91
|
throw new Error(`Invoice(${subscription.latest_invoice_id}) not found`);
|
|
@@ -94,7 +97,7 @@ export class SubscriptionWillCanceledEmailTemplate
|
|
|
94
97
|
const at: string = formatTime(cancelAt * 1000);
|
|
95
98
|
const willCancelDuration: string = getSimplifyDuration((cancelAt - now) * 1000, locale);
|
|
96
99
|
// @ts-ignore
|
|
97
|
-
const paymentInfo: string = `${fromUnitToToken(+invoice.total, invoice?.paymentCurrency?.decimal)} ${invoice?.paymentCurrency?.symbol}`;
|
|
100
|
+
const paymentInfo: string = `${fromUnitToToken(+invoice.total, invoice?.paymentCurrency?.decimal)} ${invoice?.paymentCurrency?.symbol}${invoice?.paymentMethod ? `(${invoice?.paymentMethod.name})` : ''}`;
|
|
98
101
|
|
|
99
102
|
let body: string = translate('notification.subscriptWillCanceled.body', locale, {
|
|
100
103
|
productName,
|
|
@@ -117,7 +117,7 @@ export class SubscriptionWillRenewEmailTemplate
|
|
|
117
117
|
const paidType: string = isPrePaid
|
|
118
118
|
? translate('notification.common.prepaid', locale)
|
|
119
119
|
: translate('notification.common.postpaid', locale);
|
|
120
|
-
const paymentInfo: string = `${paymentDetail?.price || '0'} ${paymentCurrency.symbol}`;
|
|
120
|
+
const paymentInfo: string = `${paymentDetail?.price || '0'} ${paymentCurrency.symbol}${paymentMethod ? ` (${paymentMethod.name})` : ''}`;
|
|
121
121
|
const currentPeriodStart: string = isPrePaid
|
|
122
122
|
? formatTime(invoice.period_end * 1000)
|
|
123
123
|
: formatTime(invoice.period_start * 1000);
|
|
@@ -17,6 +17,7 @@ import logger from '../../libs/logger';
|
|
|
17
17
|
export default {
|
|
18
18
|
action: 'change-payment',
|
|
19
19
|
authPrincipal: false,
|
|
20
|
+
persistentDynamicClaims: true,
|
|
20
21
|
claims: {
|
|
21
22
|
authPrincipal: async ({ extraParams }: CallbackArgs) => {
|
|
22
23
|
const { paymentMethod } = await ensureChangePaymentContext(extraParams.subscriptionId);
|
|
@@ -21,6 +21,7 @@ import logger from '../../libs/logger';
|
|
|
21
21
|
export default {
|
|
22
22
|
action: 'change-plan',
|
|
23
23
|
authPrincipal: false,
|
|
24
|
+
persistentDynamicClaims: true,
|
|
24
25
|
claims: {
|
|
25
26
|
authPrincipal: async ({ extraParams }: CallbackArgs) => {
|
|
26
27
|
const { paymentMethod } = await ensureSubscription(extraParams.subscriptionId);
|
|
@@ -22,6 +22,7 @@ import { EVM_CHAIN_TYPES } from '../../libs/constants';
|
|
|
22
22
|
export default {
|
|
23
23
|
action: 'setup',
|
|
24
24
|
authPrincipal: false,
|
|
25
|
+
persistentDynamicClaims: true,
|
|
25
26
|
claims: {
|
|
26
27
|
authPrincipal: async ({ extraParams }: CallbackArgs) => {
|
|
27
28
|
const { paymentMethod } = await ensureSetupIntent(extraParams.checkoutSessionId);
|
|
@@ -1011,6 +1011,7 @@ export async function executeOcapTransactions(
|
|
|
1011
1011
|
nonce?: string
|
|
1012
1012
|
) {
|
|
1013
1013
|
const client = paymentMethod.getOcapClient();
|
|
1014
|
+
logger.info('start executeOcapTransactions', claims);
|
|
1014
1015
|
const delegation = claims.find((x) => x.type === 'signature' && x.meta?.purpose === 'delegation');
|
|
1015
1016
|
const staking = claims.find((x) => x.type === 'prepareTx' && x.meta?.purpose === 'staking');
|
|
1016
1017
|
const transactions = [
|
|
@@ -1021,40 +1022,45 @@ export async function executeOcapTransactions(
|
|
|
1021
1022
|
const stakingAmount =
|
|
1022
1023
|
staking?.requirement?.tokens?.find((x: any) => x.address === paymentCurrencyContract)?.value || '0';
|
|
1023
1024
|
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1025
|
+
try {
|
|
1026
|
+
const [delegationTxHash, stakingTxHash] = await Promise.all(
|
|
1027
|
+
transactions.map(async ([claim, type]) => {
|
|
1028
|
+
if (!claim) {
|
|
1029
|
+
return '';
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
const tx: Partial<Transaction> = client.decodeTx(claim.finalTx || claim.origin);
|
|
1033
|
+
if (claim.sig) {
|
|
1034
|
+
tx.signature = claim.sig;
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
// @ts-ignore
|
|
1038
|
+
const { buffer } = await client[`encode${type}Tx`]({ tx });
|
|
1039
|
+
// @ts-ignore
|
|
1040
|
+
const txHash = await client[`send${type}Tx`](
|
|
1041
|
+
// @ts-ignore
|
|
1042
|
+
{ tx, wallet: fromPublicKey(userPk, toTypeInfo(userDid)) },
|
|
1043
|
+
getGasPayerExtra(buffer, client.pickGasPayerHeaders(request))
|
|
1044
|
+
);
|
|
1045
|
+
|
|
1046
|
+
return txHash;
|
|
1047
|
+
})
|
|
1042
1048
|
);
|
|
1043
|
-
|
|
1044
|
-
return
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1049
|
+
|
|
1050
|
+
return {
|
|
1051
|
+
tx_hash: delegationTxHash,
|
|
1052
|
+
payer: userDid,
|
|
1053
|
+
type: 'delegate',
|
|
1054
|
+
staking: {
|
|
1055
|
+
tx_hash: stakingTxHash,
|
|
1056
|
+
address: await getCustomerStakeAddress(userDid, nonce || subscriptionId || ''),
|
|
1057
|
+
},
|
|
1058
|
+
stakingAmount,
|
|
1059
|
+
};
|
|
1060
|
+
} catch (err) {
|
|
1061
|
+
logger.error('executeOcapTransactions failed', err);
|
|
1062
|
+
throw err;
|
|
1063
|
+
}
|
|
1058
1064
|
}
|
|
1059
1065
|
|
|
1060
1066
|
|
|
@@ -23,6 +23,7 @@ import { EVM_CHAIN_TYPES } from '../../libs/constants';
|
|
|
23
23
|
export default {
|
|
24
24
|
action: 'subscription',
|
|
25
25
|
authPrincipal: false,
|
|
26
|
+
persistentDynamicClaims: true,
|
|
26
27
|
claims: {
|
|
27
28
|
authPrincipal: async ({ extraParams }: CallbackArgs) => {
|
|
28
29
|
const { paymentMethod } = await ensurePaymentIntent(extraParams.checkoutSessionId);
|
|
@@ -151,15 +152,15 @@ export default {
|
|
|
151
152
|
if (!subscription) {
|
|
152
153
|
throw new Error('Subscription for checkoutSession not found');
|
|
153
154
|
}
|
|
154
|
-
|
|
155
|
+
const paymentSettings = {
|
|
156
|
+
payment_method_types: [paymentMethod.type],
|
|
157
|
+
payment_method_options: {
|
|
158
|
+
[paymentMethod.type]: { payer: userDid },
|
|
159
|
+
},
|
|
160
|
+
};
|
|
155
161
|
const prepareTxExecution = async () => {
|
|
156
162
|
await subscription.update({
|
|
157
|
-
payment_settings:
|
|
158
|
-
payment_method_types: [paymentMethod.type],
|
|
159
|
-
payment_method_options: {
|
|
160
|
-
[paymentMethod.type]: { payer: userDid },
|
|
161
|
-
},
|
|
162
|
-
},
|
|
163
|
+
payment_settings: paymentSettings,
|
|
163
164
|
});
|
|
164
165
|
};
|
|
165
166
|
|
|
@@ -179,7 +180,9 @@ export default {
|
|
|
179
180
|
if (paymentMethod.type === 'arcblock') {
|
|
180
181
|
await prepareTxExecution();
|
|
181
182
|
const { invoice } = await ensureInvoiceForCheckout({ checkoutSession, customer, subscription });
|
|
182
|
-
|
|
183
|
+
if (invoice) {
|
|
184
|
+
await invoice.update({ payment_settings: paymentSettings });
|
|
185
|
+
}
|
|
183
186
|
const { stakingAmount, ...paymentDetails } = await executeOcapTransactions(
|
|
184
187
|
userDid,
|
|
185
188
|
userPk,
|
|
@@ -217,6 +220,9 @@ export default {
|
|
|
217
220
|
if (EVM_CHAIN_TYPES.includes(paymentMethod.type)) {
|
|
218
221
|
await prepareTxExecution();
|
|
219
222
|
const { invoice } = await ensureInvoiceForCheckout({ checkoutSession, customer, subscription });
|
|
223
|
+
if (invoice) {
|
|
224
|
+
await invoice.update({ payment_settings: paymentSettings });
|
|
225
|
+
}
|
|
220
226
|
broadcastEvmTransaction(checkoutSessionId, 'pending', claimsList);
|
|
221
227
|
|
|
222
228
|
const paymentDetails = await executeEvmTransaction('approve', userDid, claimsList, paymentMethod);
|
|
@@ -11,6 +11,7 @@ import { formatMetadata } from '../libs/util';
|
|
|
11
11
|
import { Customer } from '../store/models/customer';
|
|
12
12
|
import { blocklet } from '../libs/auth';
|
|
13
13
|
import logger from '../libs/logger';
|
|
14
|
+
import { Invoice } from '../store/models';
|
|
14
15
|
|
|
15
16
|
const router = Router();
|
|
16
17
|
const auth = authenticate<Customer>({ component: true, roles: ['owner', 'admin'] });
|
|
@@ -86,26 +87,99 @@ router.get('/me', sessionMiddleware(), async (req, res) => {
|
|
|
86
87
|
}
|
|
87
88
|
|
|
88
89
|
try {
|
|
89
|
-
|
|
90
|
+
let doc = await Customer.findByPkOrDid(req.user.did as string);
|
|
90
91
|
const livemode = req.query.livemode ? !!req?.livemode : !!doc?.livemode;
|
|
91
92
|
if (!doc) {
|
|
92
93
|
if (req.query.fallback) {
|
|
93
94
|
const result = await blocklet.getUser(req.user.did);
|
|
94
|
-
res.json({ ...result.user, address: {}, livemode });
|
|
95
|
+
return res.json({ ...result.user, address: {}, livemode });
|
|
96
|
+
}
|
|
97
|
+
if (req.query.create) {
|
|
98
|
+
// create customer
|
|
99
|
+
const { user } = await blocklet.getUser(req.user.did);
|
|
100
|
+
const customer = await Customer.create({
|
|
101
|
+
livemode: true,
|
|
102
|
+
did: req.user.did,
|
|
103
|
+
name: user.fullName,
|
|
104
|
+
email: user.email,
|
|
105
|
+
phone: '',
|
|
106
|
+
address: {},
|
|
107
|
+
description: user.remark,
|
|
108
|
+
metadata: {},
|
|
109
|
+
balance: '0',
|
|
110
|
+
next_invoice_sequence: 1,
|
|
111
|
+
delinquent: false,
|
|
112
|
+
invoice_prefix: Customer.getInvoicePrefix(),
|
|
113
|
+
});
|
|
114
|
+
logger.info('customer created', {
|
|
115
|
+
customerId: customer.id,
|
|
116
|
+
did: customer.did,
|
|
117
|
+
});
|
|
118
|
+
doc = customer;
|
|
95
119
|
} else {
|
|
96
|
-
res.json({ error: 'Customer not found' });
|
|
120
|
+
return res.json({ error: 'Customer not found' });
|
|
97
121
|
}
|
|
98
|
-
}
|
|
122
|
+
}
|
|
123
|
+
try {
|
|
99
124
|
const [summary, stake, token] = await Promise.all([
|
|
100
125
|
doc.getSummary(livemode),
|
|
101
126
|
getStakeSummaryByDid(doc.did, livemode),
|
|
102
127
|
getTokenSummaryByDid(doc.did, livemode),
|
|
103
128
|
]);
|
|
104
129
|
res.json({ ...doc.toJSON(), summary: { ...summary, stake, token }, livemode });
|
|
130
|
+
} catch (summaryErr) {
|
|
131
|
+
logger.error('get customer summary failed', summaryErr);
|
|
132
|
+
if (req.query.skipError) {
|
|
133
|
+
return res.json({
|
|
134
|
+
...doc.toJSON(),
|
|
135
|
+
summary: { stake: {}, token: {} },
|
|
136
|
+
livemode,
|
|
137
|
+
summaryError: summaryErr.message,
|
|
138
|
+
error: `Failed to get customer: ${summaryErr.message}`,
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
throw summaryErr;
|
|
105
142
|
}
|
|
106
143
|
} catch (err) {
|
|
107
144
|
logger.error('get customer failed', err);
|
|
108
|
-
|
|
145
|
+
if (req.query.skipError) {
|
|
146
|
+
return res.json({
|
|
147
|
+
error: `Failed to get customer: ${err.message}`,
|
|
148
|
+
did: req.user?.did,
|
|
149
|
+
name: req.user?.fullName,
|
|
150
|
+
address: {},
|
|
151
|
+
livemode: !!req.query.livemode,
|
|
152
|
+
summary: { stake: {}, token: {} },
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
return res.status(500).json({ error: `Failed to get customer: ${err.message}` });
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
// get overdue invoices
|
|
160
|
+
router.get('/:id/overdue/invoices', auth, async (req, res) => {
|
|
161
|
+
if (!req.user) {
|
|
162
|
+
return res.status(403).json({ error: 'Unauthorized' });
|
|
163
|
+
}
|
|
164
|
+
try {
|
|
165
|
+
const doc = await Customer.findByPkOrDid(req.params.id as string);
|
|
166
|
+
if (!doc) {
|
|
167
|
+
return res.status(404).json({ error: 'Customer not found' });
|
|
168
|
+
}
|
|
169
|
+
const [summary, detail, invoices] = await Invoice!.getUncollectibleAmount({
|
|
170
|
+
customerId: doc.id,
|
|
171
|
+
livemode: req.query.livemode ? !!req.query.livemode : doc.livemode,
|
|
172
|
+
});
|
|
173
|
+
const subscriptionCount = new Set(invoices.map((x) => x.subscription_id)).size;
|
|
174
|
+
return res.json({
|
|
175
|
+
summary,
|
|
176
|
+
invoices,
|
|
177
|
+
subscriptionCount,
|
|
178
|
+
detail,
|
|
179
|
+
});
|
|
180
|
+
} catch (err) {
|
|
181
|
+
logger.error(err);
|
|
182
|
+
return res.status(500).json({ error: `Failed to get overdue invoices: ${err.message}` });
|
|
109
183
|
}
|
|
110
184
|
});
|
|
111
185
|
|
|
@@ -97,6 +97,7 @@ const schema = createListParamSchema<{
|
|
|
97
97
|
activeFirst?: boolean;
|
|
98
98
|
price_id?: string;
|
|
99
99
|
order?: string | string[] | OrderItem | OrderItem[];
|
|
100
|
+
showTotalCount?: boolean;
|
|
100
101
|
}>({
|
|
101
102
|
status: Joi.string().empty(''),
|
|
102
103
|
customer_id: Joi.string().empty(''),
|
|
@@ -110,6 +111,7 @@ const schema = createListParamSchema<{
|
|
|
110
111
|
Joi.array().items(Joi.array().ordered(Joi.string(), Joi.string().valid('ASC', 'DESC').insensitive()))
|
|
111
112
|
)
|
|
112
113
|
.optional(),
|
|
114
|
+
showTotalCount: Joi.boolean().optional(),
|
|
113
115
|
});
|
|
114
116
|
|
|
115
117
|
const parseOrder = (orderStr: string): OrderItem => {
|
|
@@ -195,7 +197,17 @@ router.get('/', authMine, async (req, res) => {
|
|
|
195
197
|
// @ts-ignore
|
|
196
198
|
docs.forEach((x) => expandLineItems(x.items, products, prices));
|
|
197
199
|
|
|
198
|
-
|
|
200
|
+
if (query.showTotalCount) {
|
|
201
|
+
const totalCount = await Subscription.count({
|
|
202
|
+
where: {
|
|
203
|
+
customer_id: where.customer_id,
|
|
204
|
+
},
|
|
205
|
+
distinct: true,
|
|
206
|
+
});
|
|
207
|
+
res.json({ count, list: docs, paging: { page, pageSize }, totalCount });
|
|
208
|
+
} else {
|
|
209
|
+
res.json({ count, list: docs, paging: { page, pageSize } });
|
|
210
|
+
}
|
|
199
211
|
} catch (err) {
|
|
200
212
|
logger.error(err);
|
|
201
213
|
res.json({ count: 0, list: [], paging: { page, pageSize } });
|
|
@@ -530,7 +530,9 @@ export class Invoice extends Model<InferAttributes<Invoice>, InferCreationAttrib
|
|
|
530
530
|
return ['paid', 'void'].includes(this.status);
|
|
531
531
|
}
|
|
532
532
|
|
|
533
|
-
private static async _getUncollectibleAmount(
|
|
533
|
+
private static async _getUncollectibleAmount(
|
|
534
|
+
where: WhereOptions<Invoice>
|
|
535
|
+
): Promise<[GroupedBN, GroupedStrList, Invoice[]]> {
|
|
534
536
|
const invoices = await Invoice.findAll({ where });
|
|
535
537
|
const summary: GroupedBN = {};
|
|
536
538
|
const detail: GroupedStrList = {};
|
|
@@ -548,7 +550,7 @@ export class Invoice extends Model<InferAttributes<Invoice>, InferCreationAttrib
|
|
|
548
550
|
detail[key]!.push(invoice.id);
|
|
549
551
|
});
|
|
550
552
|
|
|
551
|
-
return [summary, detail];
|
|
553
|
+
return [summary, detail, invoices];
|
|
552
554
|
}
|
|
553
555
|
|
|
554
556
|
public static getUncollectibleAmount({
|
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.18.
|
|
17
|
+
version: 1.18.13
|
|
18
18
|
logo: logo.png
|
|
19
19
|
files:
|
|
20
20
|
- dist
|
|
@@ -113,7 +113,7 @@ navigation:
|
|
|
113
113
|
icon: ion:receipt-outline
|
|
114
114
|
link: /customer
|
|
115
115
|
section:
|
|
116
|
-
-
|
|
116
|
+
- userCenter
|
|
117
117
|
- sessionManager
|
|
118
118
|
role:
|
|
119
119
|
- owner
|
|
@@ -165,4 +165,4 @@ events:
|
|
|
165
165
|
- type: invoice.paid
|
|
166
166
|
description: Invoice has been fully paid and settled
|
|
167
167
|
- type: refund.succeeded
|
|
168
|
-
description: Refund has been successfully processed and completed
|
|
168
|
+
description: Refund has been successfully processed and completed
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "payment-kit",
|
|
3
|
-
"version": "1.18.
|
|
3
|
+
"version": "1.18.13",
|
|
4
4
|
"scripts": {
|
|
5
5
|
"dev": "blocklet dev --open",
|
|
6
6
|
"eject": "vite eject",
|
|
@@ -44,29 +44,29 @@
|
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
46
|
"@abtnode/cron": "^1.16.39",
|
|
47
|
-
"@arcblock/did": "^1.19.
|
|
47
|
+
"@arcblock/did": "^1.19.14",
|
|
48
48
|
"@arcblock/did-auth-storage-nedb": "^1.7.1",
|
|
49
49
|
"@arcblock/did-connect": "^2.11.48",
|
|
50
|
-
"@arcblock/did-util": "^1.19.
|
|
51
|
-
"@arcblock/jwt": "^1.19.
|
|
50
|
+
"@arcblock/did-util": "^1.19.14",
|
|
51
|
+
"@arcblock/jwt": "^1.19.14",
|
|
52
52
|
"@arcblock/ux": "^2.11.48",
|
|
53
|
-
"@arcblock/validator": "^1.19.
|
|
53
|
+
"@arcblock/validator": "^1.19.14",
|
|
54
54
|
"@blocklet/js-sdk": "^1.16.39",
|
|
55
55
|
"@blocklet/logger": "^1.16.39",
|
|
56
|
-
"@blocklet/payment-react": "1.18.
|
|
56
|
+
"@blocklet/payment-react": "1.18.13",
|
|
57
57
|
"@blocklet/sdk": "^1.16.39",
|
|
58
58
|
"@blocklet/ui-react": "^2.11.48",
|
|
59
|
-
"@blocklet/uploader": "^0.1.
|
|
60
|
-
"@blocklet/xss": "^0.1.
|
|
59
|
+
"@blocklet/uploader": "^0.1.71",
|
|
60
|
+
"@blocklet/xss": "^0.1.27",
|
|
61
61
|
"@mui/icons-material": "^5.16.6",
|
|
62
62
|
"@mui/lab": "^5.0.0-alpha.173",
|
|
63
63
|
"@mui/material": "^5.16.6",
|
|
64
64
|
"@mui/system": "^5.16.6",
|
|
65
|
-
"@ocap/asset": "^1.19.
|
|
66
|
-
"@ocap/client": "^1.19.
|
|
67
|
-
"@ocap/mcrypto": "^1.19.
|
|
68
|
-
"@ocap/util": "^1.19.
|
|
69
|
-
"@ocap/wallet": "^1.19.
|
|
65
|
+
"@ocap/asset": "^1.19.14",
|
|
66
|
+
"@ocap/client": "^1.19.14",
|
|
67
|
+
"@ocap/mcrypto": "^1.19.14",
|
|
68
|
+
"@ocap/util": "^1.19.14",
|
|
69
|
+
"@ocap/wallet": "^1.19.14",
|
|
70
70
|
"@stripe/react-stripe-js": "^2.7.3",
|
|
71
71
|
"@stripe/stripe-js": "^2.4.0",
|
|
72
72
|
"ahooks": "^3.8.0",
|
|
@@ -121,7 +121,7 @@
|
|
|
121
121
|
"devDependencies": {
|
|
122
122
|
"@abtnode/types": "^1.16.39",
|
|
123
123
|
"@arcblock/eslint-config-ts": "^0.3.3",
|
|
124
|
-
"@blocklet/payment-types": "1.18.
|
|
124
|
+
"@blocklet/payment-types": "1.18.13",
|
|
125
125
|
"@types/cookie-parser": "^1.4.7",
|
|
126
126
|
"@types/cors": "^2.8.17",
|
|
127
127
|
"@types/debug": "^4.1.12",
|
|
@@ -167,5 +167,5 @@
|
|
|
167
167
|
"parser": "typescript"
|
|
168
168
|
}
|
|
169
169
|
},
|
|
170
|
-
"gitHead": "
|
|
170
|
+
"gitHead": "b26aa859ba5961c7a8dedfc238a31476cf16036c"
|
|
171
171
|
}
|