payment-kit 1.20.10 → 1.20.11
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 +25 -24
- package/api/src/libs/invoice.ts +6 -6
- package/api/src/locales/en.ts +38 -38
- package/blocklet.yml +1 -1
- package/doc/vendor_fulfillment_system.md +38 -38
- package/package.json +5 -5
- package/src/locales/en.tsx +26 -26
package/README.md
CHANGED
|
@@ -1,46 +1,47 @@
|
|
|
1
1
|
# Payment Kit
|
|
2
2
|
|
|
3
|
-
The decentralized
|
|
3
|
+
The decentralized Stripe for the Blocklet platform.
|
|
4
4
|
|
|
5
5
|
## Contribution
|
|
6
6
|
|
|
7
7
|
### Development
|
|
8
8
|
|
|
9
|
-
1.
|
|
10
|
-
2.
|
|
11
|
-
3.
|
|
9
|
+
1. Clone the repository
|
|
10
|
+
2. Run `make build`
|
|
11
|
+
3. Run `cd blocklets/core && blocklet dev`
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
#### Troubleshooting
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
**Error: "pre-start error component xxx is not running or unreachable"**
|
|
16
16
|
|
|
17
|
-
-
|
|
18
|
-
-
|
|
19
|
-
-
|
|
20
|
-
-
|
|
21
|
-
-
|
|
17
|
+
- Create a `.env.local` file in the project 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 the `/core` directory
|
|
21
|
+
- Edit `BLOCKLET_DEV_MOUNT_POINT="/"`
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
**Error: "Insufficient funds to pay for transaction cost from xxx, expected 1.0020909, got 0"**
|
|
24
|
+
|
|
25
|
+
- Copy the `BLOCKLET_DEV_APP_DID`
|
|
26
|
+
- Transfer 2 TBA from your DID Wallet to the copied address
|
|
26
27
|
|
|
27
28
|
### Debug Stripe
|
|
28
29
|
|
|
29
|
-
1. Install and
|
|
30
|
-
2. Start your local
|
|
30
|
+
1. Install and log in following the instructions at: https://stripe.com/docs/stripe-cli
|
|
31
|
+
2. Start your local Payment Kit server and note its port
|
|
31
32
|
3. Run `stripe listen --forward-to http://127.0.0.1:8188/api/integrations/stripe/webhook --log-level=debug --latest`
|
|
32
33
|
|
|
33
34
|
### Test Stripe
|
|
34
35
|
|
|
35
|
-
Invoices for subscriptions are not finalized automatically. You can use
|
|
36
|
+
Invoices for subscriptions are not finalized automatically. You can use the Stripe Postman collection to finalize them and then confirm the payment.
|
|
36
37
|
|
|
37
38
|
### Easter Eggs
|
|
38
39
|
|
|
39
|
-
|
|
40
|
+
Non-public environment variables used in the code that can change the behavior of Payment Kit:
|
|
40
41
|
|
|
41
|
-
- PAYMENT_CHANGE_LOCKED_PRICE
|
|
42
|
-
- PAYMENT_RELOAD_SUBSCRIPTION_JOBS
|
|
43
|
-
- PAYMENT_BILLING_THRESHOLD
|
|
44
|
-
- PAYMENT_MIN_STAKE_AMOUNT
|
|
45
|
-
- PAYMENT_DAYS_UNTIL_DUE
|
|
46
|
-
- PAYMENT_DAYS_UNTIL_CANCEL
|
|
42
|
+
- `PAYMENT_CHANGE_LOCKED_PRICE`: Allows changing locked price (must be set to "1" to enable)
|
|
43
|
+
- `PAYMENT_RELOAD_SUBSCRIPTION_JOBS`: Reloads subscription jobs on start (must be set to "1" to enable)
|
|
44
|
+
- `PAYMENT_BILLING_THRESHOLD`: Global default billing threshold (must be a number)
|
|
45
|
+
- `PAYMENT_MIN_STAKE_AMOUNT`: Global minimum stake amount limit (must be a number)
|
|
46
|
+
- `PAYMENT_DAYS_UNTIL_DUE`: Global default days until due (must be a number)
|
|
47
|
+
- `PAYMENT_DAYS_UNTIL_CANCEL`: Global default days until cancellation (must be a number)
|
package/api/src/libs/invoice.ts
CHANGED
|
@@ -67,7 +67,7 @@ export async function getOneTimeProductInfo(invoiceId: string, paymentCurrency:
|
|
|
67
67
|
include: [{ model: InvoiceItem, as: 'lines' }],
|
|
68
68
|
});
|
|
69
69
|
if (!doc) {
|
|
70
|
-
throw new Error(`Invoice not found
|
|
70
|
+
throw new Error(`Invoice not found: ${invoiceId}`);
|
|
71
71
|
}
|
|
72
72
|
const json = doc.toJSON();
|
|
73
73
|
const products = (await Product.findAll()).map((x) => x.toJSON());
|
|
@@ -733,19 +733,19 @@ export async function ensureOverdraftProtectionInvoiceAndItems({
|
|
|
733
733
|
const invoicePrice = price?.currency_options?.find((x: any) => x.currency_id === paymentIntent?.currency_id);
|
|
734
734
|
|
|
735
735
|
if (!subscription.overdraft_protection?.enabled) {
|
|
736
|
-
throw new Error('
|
|
736
|
+
throw new Error('Overdraft protection invoice creation skipped: overdraft protection is not enabled');
|
|
737
737
|
}
|
|
738
738
|
|
|
739
739
|
if (!invoicePrice) {
|
|
740
|
-
throw new Error('
|
|
740
|
+
throw new Error('Overdraft protection invoice price not found');
|
|
741
741
|
}
|
|
742
742
|
const currency = await PaymentCurrency.findByPk(invoicePrice.currency_id);
|
|
743
743
|
if (!currency) {
|
|
744
|
-
throw new Error('
|
|
744
|
+
throw new Error('Overdraft protection invoice currency not found');
|
|
745
745
|
}
|
|
746
746
|
const paymentMethod = await PaymentMethod.findByPk(currency?.payment_method_id);
|
|
747
747
|
if (!paymentMethod) {
|
|
748
|
-
throw new Error('
|
|
748
|
+
throw new Error('Overdraft protection invoice payment method not found');
|
|
749
749
|
}
|
|
750
750
|
|
|
751
751
|
if (paymentMethod.type !== 'arcblock') {
|
|
@@ -754,7 +754,7 @@ export async function ensureOverdraftProtectionInvoiceAndItems({
|
|
|
754
754
|
|
|
755
755
|
const { unused } = await isSubscriptionOverdraftProtectionEnabled(subscription, paymentIntent?.currency_id);
|
|
756
756
|
if (new BN(unused).lt(new BN(invoicePrice.unit_amount))) {
|
|
757
|
-
throw new Error('
|
|
757
|
+
throw new Error('Overdraft protection invoice creation skipped: insufficient overdraft protection funds');
|
|
758
758
|
}
|
|
759
759
|
|
|
760
760
|
const result = await createInvoiceWithItems({
|
package/api/src/locales/en.ts
CHANGED
|
@@ -54,45 +54,45 @@ export default flat({
|
|
|
54
54
|
|
|
55
55
|
billingDiscrepancy: {
|
|
56
56
|
title: '{productName} billing discrepancy',
|
|
57
|
-
body: '
|
|
57
|
+
body: 'A billing discrepancy has been detected for {productName}. Please review your billing details.',
|
|
58
58
|
},
|
|
59
59
|
|
|
60
60
|
sendTo: 'Sent to',
|
|
61
61
|
mintNFT: {
|
|
62
62
|
title: '{collection} NFT minted',
|
|
63
|
-
message: 'A new {collection} NFT
|
|
63
|
+
message: 'A new {collection} NFT has been minted and sent to your wallet. Please check your wallet to view it.',
|
|
64
64
|
},
|
|
65
65
|
|
|
66
66
|
usageReportEmpty: {
|
|
67
67
|
title: 'No usage report for {productName}',
|
|
68
|
-
body: 'No usage report for {productName}
|
|
68
|
+
body: 'No usage report has been detected for {productName}. Please verify your usage reporting configuration.',
|
|
69
69
|
},
|
|
70
70
|
|
|
71
71
|
meteringSubscriptionDetection: {
|
|
72
72
|
title: '[{appName}] Metering subscription detection',
|
|
73
|
-
body: 'During {startTimeStr} - {endTimeStr}, a total of {totalCount} subscriptions with usage-based billing were scanned
|
|
73
|
+
body: 'During {startTimeStr} - {endTimeStr}, a total of {totalCount} subscriptions with usage-based billing were scanned.\nOf these, {normalCount} were normal, while {abnormalCount} had anomalies, including {unreportedCount} unreported subscriptions and {discrepantCount} with billing discrepancies.\n\nAbnormal subscriptions:',
|
|
74
74
|
healthyBody:
|
|
75
|
-
'During {startTimeStr} - {endTimeStr}, a total of {totalCount} subscriptions with usage-based billing were scanned, and all subscriptions were
|
|
75
|
+
'During {startTimeStr} - {endTimeStr}, a total of {totalCount} subscriptions with usage-based billing were scanned, and all subscriptions were functioning normally.',
|
|
76
76
|
view: 'Manage subscriptions',
|
|
77
77
|
},
|
|
78
78
|
|
|
79
79
|
subscriptionTrialStart: {
|
|
80
80
|
title: 'Welcome to the start of your {productName} trial',
|
|
81
|
-
body: 'Congratulations on your {productName} trial!
|
|
81
|
+
body: 'Congratulations on starting your {productName} trial! Your trial period is {trialDuration} and will end on {subscriptionTrialEnd}. Enjoy exploring {productName}!',
|
|
82
82
|
},
|
|
83
83
|
|
|
84
84
|
subscriptionTrialWillEnd: {
|
|
85
85
|
title: 'The {productName} trial will end soon',
|
|
86
86
|
body: 'Your trial for {productName} will end in {willRenewDuration}. Please ensure your account balance is sufficient for automatic billing after the trial ends. Thank you for your support and trust!',
|
|
87
87
|
unableToPayBody:
|
|
88
|
-
'Your trial for {productName} will end in {willRenewDuration}.Your current balance is {balance}, which is less than {price}
|
|
88
|
+
'Your trial for {productName} will end in {willRenewDuration}. Your current balance is {balance}, which is less than {price}. Please ensure your balance is sufficient for automatic billing. Thank you for your support and trust!',
|
|
89
89
|
unableToPayReason:
|
|
90
|
-
'The estimated payment amount is {price}, but
|
|
90
|
+
'The estimated payment amount is {price}, but your current balance is insufficient ({balance}). Please ensure your account has enough balance to avoid payment failure.',
|
|
91
91
|
},
|
|
92
92
|
|
|
93
93
|
subscriptionSucceed: {
|
|
94
94
|
title: "Congratulations! You've successfully subscribed to {productName}",
|
|
95
|
-
body: 'Thank you for successfully subscribing to {productName} on {at}. We
|
|
95
|
+
body: 'Thank you for successfully subscribing to {productName} on {at}. We are happy to provide you with excellent service, and we wish you a pleasant experience.',
|
|
96
96
|
},
|
|
97
97
|
|
|
98
98
|
oneTimePaymentSucceeded: {
|
|
@@ -102,7 +102,7 @@ export default flat({
|
|
|
102
102
|
|
|
103
103
|
subscriptionUpgraded: {
|
|
104
104
|
title: 'Congratulations! Your subscription plan for {productName} has been successfully upgraded',
|
|
105
|
-
body: 'Your subscription plan for {productName} has been successfully upgraded
|
|
105
|
+
body: 'Your subscription plan for {productName} has been successfully upgraded on {at}. Thank you for your support, and we wish you a pleasant experience!',
|
|
106
106
|
},
|
|
107
107
|
|
|
108
108
|
subscriptionWillRenew: {
|
|
@@ -111,53 +111,53 @@ export default flat({
|
|
|
111
111
|
unableToPayBody:
|
|
112
112
|
'Your subscription to {productName} is scheduled for automatic payment on {at}({willRenewDuration} later). If you have any questions or need assistance, please feel free to contact us.',
|
|
113
113
|
unableToPayReason:
|
|
114
|
-
'The estimated payment amount is {price}, but
|
|
114
|
+
'The estimated payment amount is {price}, but your current balance is insufficient ({balance}). Please ensure your account has enough balance to avoid payment failure.',
|
|
115
115
|
renewAmount: 'Payment amount',
|
|
116
116
|
estimatedAmountNote: 'Estimate {amount}, billed based on final usage',
|
|
117
117
|
},
|
|
118
118
|
|
|
119
119
|
subscriptionRenewed: {
|
|
120
120
|
title: '{productName} payment successful',
|
|
121
|
-
body: 'Payment for your
|
|
122
|
-
noExpenseIncurred: 'No expenses incurred during the service ',
|
|
121
|
+
body: 'Payment for your {productName} subscription was successfully collected on {at}. Thanks for your continued support and trust. We wish you a pleasant experience using this service!',
|
|
122
|
+
noExpenseIncurred: 'No expenses incurred during the service period',
|
|
123
123
|
},
|
|
124
124
|
|
|
125
125
|
subscriptionRenewFailed: {
|
|
126
126
|
title: '{productName} automatic payment failed',
|
|
127
|
-
body: 'We are sorry to inform you that your {productName}
|
|
127
|
+
body: 'We are sorry to inform you that your {productName} automatic payment failed on {at}. If you have any questions, please contact us promptly. Thank you!',
|
|
128
128
|
reason: {
|
|
129
|
-
noDidWallet: 'You have not
|
|
130
|
-
noDelegation: 'Your DID Wallet has not been authorized
|
|
129
|
+
noDidWallet: 'You have not connected a DID Wallet. Please connect your DID Wallet to ensure sufficient balance',
|
|
130
|
+
noDelegation: 'Your DID Wallet has not been authorized. Please update authorization',
|
|
131
131
|
noTransferPermission:
|
|
132
|
-
'Your DID Wallet has not granted transfer permission to the application
|
|
132
|
+
'Your DID Wallet has not granted transfer permission to the application. Please update authorization',
|
|
133
133
|
noTokenPermission:
|
|
134
|
-
'Your DID Wallet has not granted token transfer permission to the application
|
|
134
|
+
'Your DID Wallet has not granted token transfer permission to the application. Please update authorization',
|
|
135
135
|
noTransferTo:
|
|
136
|
-
'Your DID Wallet has not granted
|
|
137
|
-
noEnoughAllowance: 'The deduction amount exceeds the single transfer limit
|
|
138
|
-
noToken: 'Your account has no tokens
|
|
139
|
-
noEnoughToken: 'Your account token balance is {balance}, insufficient for {price}
|
|
140
|
-
noSupported: '
|
|
136
|
+
'Your DID Wallet has not granted automatic payment permission to the application. Please update authorization',
|
|
137
|
+
noEnoughAllowance: 'The deduction amount exceeds the single transfer limit. Please update authorization',
|
|
138
|
+
noToken: 'Your account has no tokens. Please add funds',
|
|
139
|
+
noEnoughToken: 'Your account token balance is {balance}, which is insufficient for {price}. Please add funds',
|
|
140
|
+
noSupported: 'Automatic payment with tokens is not supported. Please check your package',
|
|
141
141
|
txSendFailed: 'Failed to send automatic payment transaction',
|
|
142
142
|
},
|
|
143
143
|
},
|
|
144
144
|
|
|
145
145
|
autoRechargeFailed: {
|
|
146
146
|
title: 'Auto Top-Up payment failed',
|
|
147
|
-
body: 'We are sorry to inform you that your {creditCurrencyName} auto top-up
|
|
147
|
+
body: 'We are sorry to inform you that your {creditCurrencyName} auto top-up automatic payment failed on {at}. If you have any questions, please contact us promptly. Thank you!',
|
|
148
148
|
reason: {
|
|
149
|
-
noDidWallet: 'You have not
|
|
150
|
-
noDelegation: 'Your DID Wallet has not been authorized
|
|
149
|
+
noDidWallet: 'You have not connected a DID Wallet. Please connect your DID Wallet to ensure sufficient balance',
|
|
150
|
+
noDelegation: 'Your DID Wallet has not been authorized. Please update authorization',
|
|
151
151
|
noTransferPermission:
|
|
152
|
-
'Your DID Wallet has not granted transfer permission to the application
|
|
152
|
+
'Your DID Wallet has not granted transfer permission to the application. Please update authorization',
|
|
153
153
|
noTokenPermission:
|
|
154
|
-
'Your DID Wallet has not granted token transfer permission to the application
|
|
154
|
+
'Your DID Wallet has not granted token transfer permission to the application. Please update authorization',
|
|
155
155
|
noTransferTo:
|
|
156
|
-
'Your DID Wallet has not granted
|
|
157
|
-
noEnoughAllowance: 'The deduction amount exceeds the single transfer limit
|
|
158
|
-
noToken: 'Your account has no tokens
|
|
159
|
-
noEnoughToken: 'Your account token balance is {balance}, insufficient for {price}
|
|
160
|
-
noSupported: '
|
|
156
|
+
'Your DID Wallet has not granted automatic payment permission to the application. Please update authorization',
|
|
157
|
+
noEnoughAllowance: 'The deduction amount exceeds the single transfer limit. Please update authorization',
|
|
158
|
+
noToken: 'Your account has no tokens. Please add funds',
|
|
159
|
+
noEnoughToken: 'Your account token balance is {balance}, which is insufficient for {price}. Please add funds',
|
|
160
|
+
noSupported: 'Automatic payment with tokens is not supported. Please check your package',
|
|
161
161
|
txSendFailed: 'Failed to send automatic payment transaction',
|
|
162
162
|
},
|
|
163
163
|
},
|
|
@@ -187,7 +187,7 @@ export default flat({
|
|
|
187
187
|
|
|
188
188
|
customerRewardSucceeded: {
|
|
189
189
|
title: 'Thanks for your reward of {amount}',
|
|
190
|
-
body: '
|
|
190
|
+
body: 'Thank you for your reward on {at}. The reward amount is {amount}. Your support is our driving force. Thank you for your generous support!',
|
|
191
191
|
received: '{address} has received {amount}',
|
|
192
192
|
viewDetail: 'View Reference',
|
|
193
193
|
},
|
|
@@ -204,14 +204,14 @@ export default flat({
|
|
|
204
204
|
},
|
|
205
205
|
sender: 'Payer',
|
|
206
206
|
viewDetail: 'View Details',
|
|
207
|
-
|
|
207
|
+
sent: '{address} has sent {amount}',
|
|
208
208
|
},
|
|
209
209
|
subscriptWillCanceled: {
|
|
210
|
-
title: '{productName} subscription is about to be cancelled
|
|
210
|
+
title: '{productName} subscription is about to be cancelled',
|
|
211
211
|
pastDue:
|
|
212
|
-
'Your
|
|
212
|
+
'Your {productName} subscription will be automatically cancelled by the system after {at} ({willCancelDuration} later) due to repeated automatic payment failures. Please resolve the automatic payment issue manually to avoid service interruption. If you have any questions, please feel free to contact us.',
|
|
213
213
|
body: 'Your subscription to {productName} will be automatically canceled on {at} ({willCancelDuration} later). If you have any questions, please feel free to contact us.',
|
|
214
|
-
renewAmount: '
|
|
214
|
+
renewAmount: 'Deduction amount',
|
|
215
215
|
cancelReason: 'Cancel reason',
|
|
216
216
|
revokeStake: 'Revoke stake',
|
|
217
217
|
adminCanceled: 'Admin canceled',
|
package/blocklet.yml
CHANGED
|
@@ -1,63 +1,63 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Multi-Vendor Fulfillment System Complete Implementation
|
|
2
2
|
|
|
3
|
-
## 📋
|
|
3
|
+
## 📋 Project Overview
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
This document summarizes the complete implementation of the multi-vendor fulfillment system in Payment Kit, including architecture design, process logic, technical implementation, and problem-solving solutions.
|
|
6
6
|
|
|
7
|
-
## 🎯
|
|
7
|
+
## 🎯 System Goals
|
|
8
8
|
|
|
9
|
-
###
|
|
9
|
+
### Core Requirements
|
|
10
10
|
|
|
11
|
-
1.
|
|
12
|
-
2.
|
|
13
|
-
3.
|
|
14
|
-
4.
|
|
15
|
-
5.
|
|
16
|
-
6.
|
|
11
|
+
1. **Multi-vendor Support**: A single order can contain products from multiple vendors
|
|
12
|
+
2. **Asynchronous Fulfillment**: Different vendors can fulfill orders independently and in parallel
|
|
13
|
+
3. **State Coordination**: Unified management of fulfillment status across all vendors
|
|
14
|
+
4. **Fault Tolerance**: Handle fulfillment failures, retries, timeouts, and other exceptional situations
|
|
15
|
+
5. **Commission Processing**: Automatic commission distribution after all vendors complete fulfillment
|
|
16
|
+
6. **Refund Protection**: Full refund if any vendor fails, ensuring user rights
|
|
17
17
|
|
|
18
|
-
###
|
|
18
|
+
### Business Rules
|
|
19
19
|
|
|
20
|
-
-
|
|
21
|
-
-
|
|
22
|
-
-
|
|
23
|
-
-
|
|
24
|
-
-
|
|
20
|
+
- **All Success**: All vendors fulfill successfully → Process commission distribution
|
|
21
|
+
- **Any Failure**: If any vendor fails, times out, or exceeds retry limit → **Initiate return requests for successful vendors** → Full refund to user
|
|
22
|
+
- **Timeout Period**: 5 minutes of no response is considered timeout
|
|
23
|
+
- **Retry Limit**: Maximum 3 retries
|
|
24
|
+
- **Return Mechanism**: Initiate return requests for vendors that have completed fulfillment
|
|
25
25
|
|
|
26
|
-
## 🏗️
|
|
26
|
+
## 🏗️ System Architecture
|
|
27
27
|
|
|
28
|
-
###
|
|
28
|
+
### Architecture Pattern: Coordinator Pattern
|
|
29
29
|
|
|
30
|
-
#### 🔄
|
|
30
|
+
#### 🔄 **Core Process Flow (Text Version)**
|
|
31
31
|
|
|
32
32
|
```
|
|
33
33
|
1. Payment Success
|
|
34
34
|
↓
|
|
35
35
|
2. vendor-commission.ts
|
|
36
|
-
├─
|
|
37
|
-
├─
|
|
38
|
-
└─
|
|
36
|
+
├─ Check vendor configuration
|
|
37
|
+
├─ No vendors → Direct cold wallet transfer ✅
|
|
38
|
+
└─ Has vendors → Call coordinator
|
|
39
39
|
|
|
40
40
|
3. vendor-fulfillment-coordinator.ts
|
|
41
|
-
├─
|
|
42
|
-
├─
|
|
43
|
-
├─
|
|
44
|
-
└─
|
|
41
|
+
├─ Start fulfillment process
|
|
42
|
+
├─ Initialize vendor_info status
|
|
43
|
+
├─ Create fulfillment tasks for each vendor
|
|
44
|
+
└─ Wait passively for notifications
|
|
45
45
|
|
|
46
|
-
4. vendor-fulfillment.ts (
|
|
47
|
-
├─
|
|
48
|
-
├─
|
|
49
|
-
├─
|
|
50
|
-
└─
|
|
46
|
+
4. vendor-fulfillment.ts (parallel execution)
|
|
47
|
+
├─ Vendor A fulfills → Notify coordinator ✅
|
|
48
|
+
├─ Vendor B fulfills → Notify coordinator ✅
|
|
49
|
+
├─ Vendor C fulfills → Notify coordinator ❌
|
|
50
|
+
└─ Retry mechanism (max 3 times)
|
|
51
51
|
|
|
52
52
|
5. vendor-fulfillment-coordinator.ts
|
|
53
|
-
├─
|
|
54
|
-
├─
|
|
55
|
-
├─
|
|
56
|
-
├─
|
|
57
|
-
└─
|
|
53
|
+
├─ Receive all notifications
|
|
54
|
+
├─ Update vendor_info status (transaction + lock)
|
|
55
|
+
├─ Check overall status
|
|
56
|
+
├─ All success → Commission distribution + cold wallet ✅
|
|
57
|
+
└─ Any failure → 🔄 Initiate return requests for successful vendors → Full refund ❌
|
|
58
58
|
```
|
|
59
59
|
|
|
60
|
-
#### 📊
|
|
60
|
+
#### 📊 **Detailed Architecture Diagram**
|
|
61
61
|
|
|
62
62
|
```mermaid
|
|
63
63
|
graph TD
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "payment-kit",
|
|
3
|
-
"version": "1.20.
|
|
3
|
+
"version": "1.20.11",
|
|
4
4
|
"scripts": {
|
|
5
5
|
"dev": "blocklet dev --open",
|
|
6
6
|
"lint": "tsc --noEmit && eslint src api/src --ext .mjs,.js,.jsx,.ts,.tsx",
|
|
@@ -56,8 +56,8 @@
|
|
|
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.11",
|
|
60
|
+
"@blocklet/payment-vendor": "1.20.11",
|
|
61
61
|
"@blocklet/sdk": "^1.16.52-beta-20250912-112002-e3499e9c",
|
|
62
62
|
"@blocklet/ui-react": "^3.1.40",
|
|
63
63
|
"@blocklet/uploader": "^0.2.10",
|
|
@@ -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.11",
|
|
130
130
|
"@types/cookie-parser": "^1.4.9",
|
|
131
131
|
"@types/cors": "^2.8.19",
|
|
132
132
|
"@types/debug": "^4.1.12",
|
|
@@ -173,5 +173,5 @@
|
|
|
173
173
|
"parser": "typescript"
|
|
174
174
|
}
|
|
175
175
|
},
|
|
176
|
-
"gitHead": "
|
|
176
|
+
"gitHead": "b69becabf1721f6238cf24004537bee1e4ade860"
|
|
177
177
|
}
|
package/src/locales/en.tsx
CHANGED
|
@@ -11,11 +11,11 @@ export default flat({
|
|
|
11
11
|
add: 'Add more metadata',
|
|
12
12
|
edit: 'Edit metadata',
|
|
13
13
|
empty: 'No metadata',
|
|
14
|
-
emptyTip: "You haven't added any metadata yet
|
|
14
|
+
emptyTip: "You haven't added any metadata yet. You can add it now",
|
|
15
15
|
formMode: 'Switch to form mode',
|
|
16
16
|
jsonMode: 'Switch to JSON mode',
|
|
17
17
|
jsonPlaceholder: 'Enter JSON data...',
|
|
18
|
-
invalidJson: 'Invalid JSON format
|
|
18
|
+
invalidJson: 'Invalid JSON format. Please check your input',
|
|
19
19
|
formatJson: 'Format JSON',
|
|
20
20
|
},
|
|
21
21
|
price: 'Price',
|
|
@@ -26,7 +26,7 @@ export default flat({
|
|
|
26
26
|
minLength: 'Min {len} characters',
|
|
27
27
|
invalidCharacters: 'Invalid characters',
|
|
28
28
|
latinOnly:
|
|
29
|
-
'
|
|
29
|
+
'Must contain at least one letter and cannot include Chinese characters or special characters such as <, >, ", \' or \\',
|
|
30
30
|
loading: 'Loading...',
|
|
31
31
|
rechargeTime: 'Recharge Time',
|
|
32
32
|
submit: 'Submit',
|
|
@@ -41,8 +41,8 @@ export default flat({
|
|
|
41
41
|
quickStarts: 'Quick Starts',
|
|
42
42
|
copy: 'Copy',
|
|
43
43
|
copied: 'Copied',
|
|
44
|
-
copySuccess: '
|
|
45
|
-
copyFailed: 'Copy
|
|
44
|
+
copySuccess: 'Copied successfully',
|
|
45
|
+
copyFailed: 'Copy failed',
|
|
46
46
|
copyTip: 'Please copy manually',
|
|
47
47
|
save: 'Save',
|
|
48
48
|
cancel: 'Cancel',
|
|
@@ -254,7 +254,7 @@ export default flat({
|
|
|
254
254
|
'Credit products are used to provide consumable credits to users, supporting both one-time purchase and package pricing models.',
|
|
255
255
|
creditAmount: {
|
|
256
256
|
label: 'Credit Amount',
|
|
257
|
-
placeholder: 'Amount of credits users
|
|
257
|
+
placeholder: 'Amount of credits users receive when purchasing this price',
|
|
258
258
|
help: 'Leave empty for pay-per-unit pricing, enter a number for fixed package deals',
|
|
259
259
|
description: 'Purchase quantity determines the amount of Credits received',
|
|
260
260
|
},
|
|
@@ -367,14 +367,14 @@ export default flat({
|
|
|
367
367
|
archiveTip: 'Archiving will hide this product from new purchases. Are you sure you want to archive this product?',
|
|
368
368
|
unarchive: 'Unarchive product',
|
|
369
369
|
unarchiveTip:
|
|
370
|
-
'Unarchiving will enable this product
|
|
370
|
+
'Unarchiving will enable this product for new purchases. Are you sure you want to unarchive this product?',
|
|
371
371
|
remove: 'Remove product',
|
|
372
372
|
removeTip: 'Removing will hide this product from new purchases. Are you sure you want to remove this product?',
|
|
373
373
|
archived: 'This product has been archived',
|
|
374
374
|
archivedTip:
|
|
375
375
|
"This product can't be added to new invoices, subscriptions, payment links, or pricing tables. Any existing subscriptions with this product remain active until canceled and any existing payment links or pricing tables are deactivated.",
|
|
376
376
|
locked: 'This product is locked because at least one of its prices is used by a subscription or a payment.',
|
|
377
|
-
currencyNotAligned: '
|
|
377
|
+
currencyNotAligned: 'All prices must have the same currency settings',
|
|
378
378
|
image: {
|
|
379
379
|
label: 'Image',
|
|
380
380
|
add: 'Add image',
|
|
@@ -431,7 +431,7 @@ export default flat({
|
|
|
431
431
|
vendorConfig: {
|
|
432
432
|
title: 'Vendor Configuration',
|
|
433
433
|
add: 'Add Vendor',
|
|
434
|
-
empty: 'No vendors configured. Click "Add Vendor" to configure vendor services
|
|
434
|
+
empty: 'No vendors configured. Click "Add Vendor" to configure vendor services',
|
|
435
435
|
vendor: 'Vendor',
|
|
436
436
|
vendorRequired: 'Vendor is required',
|
|
437
437
|
productCode: 'Product Code',
|
|
@@ -508,7 +508,7 @@ export default flat({
|
|
|
508
508
|
packageDesc: 'Price by number of units',
|
|
509
509
|
graduated: 'Graduated Pricing',
|
|
510
510
|
volume: 'Volume pricing',
|
|
511
|
-
custom: 'Customer
|
|
511
|
+
custom: 'Customer chooses price',
|
|
512
512
|
usageBased: 'Usage-based',
|
|
513
513
|
creditMetered: 'Credit metered',
|
|
514
514
|
},
|
|
@@ -520,7 +520,7 @@ export default flat({
|
|
|
520
520
|
selectMeter: 'Select meter',
|
|
521
521
|
pricingNote: 'For metered products, pricing is automatically based on usage tracked by the selected meter.',
|
|
522
522
|
description:
|
|
523
|
-
'Credit metered billing requires
|
|
523
|
+
'Credit metered billing requires corresponding credit consumption. If credits are consumed, the corresponding service will stop.',
|
|
524
524
|
},
|
|
525
525
|
credit: {
|
|
526
526
|
saveAsBasePrice: 'Set as Top-up Package',
|
|
@@ -557,7 +557,7 @@ export default flat({
|
|
|
557
557
|
tip: '',
|
|
558
558
|
},
|
|
559
559
|
quantity: {
|
|
560
|
-
tip: 'Quantity must be equal or greater than 0',
|
|
560
|
+
tip: 'Quantity must be equal to or greater than 0',
|
|
561
561
|
},
|
|
562
562
|
quantityAvailable: {
|
|
563
563
|
label: 'Available quantity',
|
|
@@ -597,10 +597,10 @@ export default flat({
|
|
|
597
597
|
afterPay: 'After payment',
|
|
598
598
|
products: 'Products',
|
|
599
599
|
addProduct: 'Add another product',
|
|
600
|
-
requireBillingAddress: 'Collect
|
|
601
|
-
requirePhoneNumber: 'Collect
|
|
600
|
+
requireBillingAddress: 'Collect customer billing addresses',
|
|
601
|
+
requirePhoneNumber: 'Collect customer phone numbers',
|
|
602
602
|
allowPromotionCodes: 'Allow promotion codes',
|
|
603
|
-
requireCrossSell: 'Require cross
|
|
603
|
+
requireCrossSell: 'Require cross-sell products to be selected if eligible',
|
|
604
604
|
includeFreeTrial: 'Include a free trial',
|
|
605
605
|
noStakeRequired: 'No stake required',
|
|
606
606
|
showProductFeatures: 'Show product features',
|
|
@@ -684,7 +684,7 @@ export default flat({
|
|
|
684
684
|
expiredUncapturedCharge: 'Expired uncaptured charge',
|
|
685
685
|
amountRange: 'Refund amount must be between {min} and {max} {symbol}',
|
|
686
686
|
amountHelper: 'Refund amount must be less than or equal to {max} {symbol}',
|
|
687
|
-
required: '
|
|
687
|
+
required: 'Please fill in the refund information',
|
|
688
688
|
empty: 'The current order has been fully refunded',
|
|
689
689
|
},
|
|
690
690
|
},
|
|
@@ -718,11 +718,11 @@ export default flat({
|
|
|
718
718
|
},
|
|
719
719
|
name: {
|
|
720
720
|
label: 'Name',
|
|
721
|
-
tip: '
|
|
721
|
+
tip: 'Customer-facing',
|
|
722
722
|
},
|
|
723
723
|
description: {
|
|
724
724
|
label: 'Description',
|
|
725
|
-
tip: 'Not
|
|
725
|
+
tip: 'Not customer-facing',
|
|
726
726
|
},
|
|
727
727
|
stripe: {
|
|
728
728
|
dashboard: {
|
|
@@ -749,11 +749,11 @@ export default flat({
|
|
|
749
749
|
},
|
|
750
750
|
api_host: {
|
|
751
751
|
label: 'API Host',
|
|
752
|
-
tip: 'The
|
|
752
|
+
tip: 'The GraphQL endpoint to send transactions to',
|
|
753
753
|
},
|
|
754
754
|
explorer_host: {
|
|
755
755
|
label: 'Explorer Host',
|
|
756
|
-
tip: 'The
|
|
756
|
+
tip: 'The web app endpoint to view transaction details',
|
|
757
757
|
},
|
|
758
758
|
},
|
|
759
759
|
ethereum: {
|
|
@@ -763,11 +763,11 @@ export default flat({
|
|
|
763
763
|
},
|
|
764
764
|
api_host: {
|
|
765
765
|
label: 'RPC Endpoint',
|
|
766
|
-
tip: 'The RPC endpoint to send
|
|
766
|
+
tip: 'The RPC endpoint to send transactions to',
|
|
767
767
|
},
|
|
768
768
|
explorer_host: {
|
|
769
769
|
label: 'Explorer Host',
|
|
770
|
-
tip: 'The
|
|
770
|
+
tip: 'The web app endpoint to view transaction details',
|
|
771
771
|
},
|
|
772
772
|
native_symbol: {
|
|
773
773
|
label: 'Native Symbol',
|
|
@@ -775,7 +775,7 @@ export default flat({
|
|
|
775
775
|
},
|
|
776
776
|
confirmation: {
|
|
777
777
|
label: 'Confirmation Count',
|
|
778
|
-
tip: '
|
|
778
|
+
tip: 'Number of blocks required since transaction execution',
|
|
779
779
|
},
|
|
780
780
|
},
|
|
781
781
|
evm: {
|
|
@@ -792,11 +792,11 @@ export default flat({
|
|
|
792
792
|
},
|
|
793
793
|
api_host: {
|
|
794
794
|
label: 'RPC Endpoint',
|
|
795
|
-
tip: 'The RPC endpoint to send
|
|
795
|
+
tip: 'The RPC endpoint to send transactions to',
|
|
796
796
|
},
|
|
797
797
|
explorer_host: {
|
|
798
798
|
label: 'Explorer Host',
|
|
799
|
-
tip: 'The
|
|
799
|
+
tip: 'The web app endpoint to view transaction details',
|
|
800
800
|
},
|
|
801
801
|
native_symbol: {
|
|
802
802
|
label: 'Native Symbol',
|
|
@@ -804,7 +804,7 @@ export default flat({
|
|
|
804
804
|
},
|
|
805
805
|
confirmation: {
|
|
806
806
|
label: 'Confirmation Count',
|
|
807
|
-
tip: '
|
|
807
|
+
tip: 'Number of blocks required since transaction execution',
|
|
808
808
|
},
|
|
809
809
|
},
|
|
810
810
|
},
|