payment-kit 1.18.30 → 1.18.31

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.
Files changed (51) hide show
  1. package/api/src/crons/metering-subscription-detection.ts +9 -0
  2. package/api/src/integrations/arcblock/nft.ts +1 -0
  3. package/api/src/integrations/blocklet/passport.ts +1 -1
  4. package/api/src/integrations/stripe/handlers/invoice.ts +2 -2
  5. package/api/src/integrations/stripe/handlers/setup-intent.ts +29 -1
  6. package/api/src/integrations/stripe/handlers/subscription.ts +19 -15
  7. package/api/src/integrations/stripe/resource.ts +81 -1
  8. package/api/src/libs/audit.ts +42 -0
  9. package/api/src/libs/invoice.ts +54 -7
  10. package/api/src/libs/notification/index.ts +72 -4
  11. package/api/src/libs/notification/template/base.ts +2 -0
  12. package/api/src/libs/notification/template/subscription-renew-failed.ts +1 -5
  13. package/api/src/libs/notification/template/subscription-renewed.ts +1 -5
  14. package/api/src/libs/notification/template/subscription-succeeded.ts +8 -18
  15. package/api/src/libs/notification/template/subscription-trial-start.ts +2 -10
  16. package/api/src/libs/notification/template/subscription-upgraded.ts +1 -5
  17. package/api/src/libs/payment.ts +47 -14
  18. package/api/src/libs/product.ts +1 -4
  19. package/api/src/libs/session.ts +600 -8
  20. package/api/src/libs/setting.ts +172 -0
  21. package/api/src/libs/subscription.ts +7 -69
  22. package/api/src/libs/ws.ts +5 -0
  23. package/api/src/queues/checkout-session.ts +42 -36
  24. package/api/src/queues/notification.ts +3 -2
  25. package/api/src/queues/payment.ts +33 -6
  26. package/api/src/queues/usage-record.ts +2 -10
  27. package/api/src/routes/checkout-sessions.ts +324 -187
  28. package/api/src/routes/connect/shared.ts +160 -38
  29. package/api/src/routes/connect/subscribe.ts +123 -64
  30. package/api/src/routes/payment-currencies.ts +3 -6
  31. package/api/src/routes/payment-links.ts +11 -1
  32. package/api/src/routes/payment-stats.ts +2 -2
  33. package/api/src/routes/payouts.ts +2 -1
  34. package/api/src/routes/settings.ts +45 -0
  35. package/api/src/routes/subscriptions.ts +1 -2
  36. package/api/src/store/migrations/20250408-subscription-grouping.ts +39 -0
  37. package/api/src/store/migrations/20250419-subscription-grouping.ts +69 -0
  38. package/api/src/store/models/checkout-session.ts +52 -0
  39. package/api/src/store/models/index.ts +1 -0
  40. package/api/src/store/models/payment-link.ts +6 -0
  41. package/api/src/store/models/subscription.ts +8 -6
  42. package/api/src/store/models/types.ts +31 -1
  43. package/api/tests/libs/session.spec.ts +423 -0
  44. package/api/tests/libs/subscription.spec.ts +0 -110
  45. package/blocklet.yml +3 -1
  46. package/package.json +20 -19
  47. package/scripts/sdk.js +486 -155
  48. package/src/locales/en.tsx +1 -1
  49. package/src/locales/zh.tsx +1 -1
  50. package/src/pages/admin/settings/vault-config/edit-form.tsx +1 -1
  51. package/src/pages/customer/subscription/change-payment.tsx +8 -3
package/scripts/sdk.js CHANGED
@@ -1,161 +1,492 @@
1
- // run sdk test: # blocklet exec /scripts/sdk.js --app-id=appid
1
+ /* eslint-disable @typescript-eslint/no-unused-vars */
2
+ // run sdk test: # pnpm run sdk-test
2
3
 
3
4
  /* eslint-disable import/no-extraneous-dependencies */
4
5
  /* eslint-disable no-console */
5
- const payment = require('@blocklet/payment-js').default;
6
+ const { spawn } = require('child_process');
7
+ const payment = require('../../../packages/client/lib').default;
6
8
 
7
- (async () => {
9
+ function getAppId() {
10
+ const args = process.argv.slice(2);
11
+ let appId = '';
12
+
13
+ for (let i = 0; i < args.length; i++) {
14
+ if (args[i] === '--app-id' && i + 1 < args.length) {
15
+ appId = args[i + 1];
16
+ break;
17
+ }
18
+ }
19
+
20
+ if (!appId) {
21
+ appId =
22
+ process.env.BLOCKLET_DEV_APP_DID?.split(':').pop() ||
23
+ process.env.BLOCKLET_APP_ID ||
24
+ 'zNKuN3XwXN7xq2NsJQjjfwujyqCxx26DhwgV';
25
+ }
26
+
27
+ return appId;
28
+ }
29
+
30
+ const checkoutModule = {
31
+ // 订阅级别管理通知
32
+ async createWithNotification() {
33
+ const checkoutSession = await payment.checkout.sessions.create({
34
+ mode: 'subscription',
35
+ line_items: [{ price_id: 'price_fQFIS12yi0JR3KePLmitjrhA', quantity: 1 }],
36
+ subscription_data: {
37
+ notification_settings: {
38
+ self_handle: true,
39
+ include_events: ['customer.subscription.started'],
40
+ },
41
+ no_stake: true,
42
+ },
43
+ });
44
+ console.log('createWithNotification', checkoutSession);
45
+ return checkoutSession;
46
+ },
47
+
48
+ // 批量订阅 + 免质押
49
+ async createBatchSubscription() {
50
+ const checkoutSession = await payment.checkout.sessions.create({
51
+ mode: 'subscription',
52
+ line_items: [
53
+ {
54
+ price_id: 'price_fQFIS12yi0JR3KePLmitjrhA',
55
+ quantity: 1,
56
+ subscription_data: {
57
+ metadata: { test: 'test price_fQFIS12yi0JR3KePLmitjrhA' },
58
+ },
59
+ },
60
+ {
61
+ price_id: 'price_PXyI9Duz99eqty1AqbaEc73u',
62
+ quantity: 1,
63
+ subscription_data: {
64
+ metadata: { test: 'test price_PXyI9Duz99eqty1AqbaEc73u' },
65
+ },
66
+ },
67
+ ],
68
+ enable_subscription_grouping: true,
69
+ subscription_data: {
70
+ no_stake: true,
71
+ },
72
+ });
73
+ console.log('createBatchSubscription', checkoutSession);
74
+ return checkoutSession;
75
+ },
76
+
77
+ // 批量订阅 + 试用 + 免质押
78
+ async createWithTrial() {
79
+ const checkoutSession = await payment.checkout.sessions.create({
80
+ mode: 'subscription',
81
+ line_items: [
82
+ { price_id: 'price_dsjm7m1qzxEg370uLAjma4zS', quantity: 2 },
83
+ { price_id: 'price_n9xjC3M2cILR3iWez7ToMRjd', quantity: 1 },
84
+ { price_id: 'price_6P2bJXtWx8Awk65NC4f1PAsf', quantity: 1 },
85
+ ],
86
+ enable_subscription_grouping: true,
87
+ subscription_data: {
88
+ no_stake: true,
89
+ },
90
+ });
91
+ console.log('createWithTrial', checkoutSession);
92
+ return checkoutSession;
93
+ },
94
+
95
+ // 创建通知设置
96
+ async createNotificationSetting() {
97
+ const setting = await payment.settings.create({
98
+ type: 'notification',
99
+ mountLocation: 'core.sdk.test',
100
+ description: 'test',
101
+ settings: {
102
+ self_handle: true,
103
+ include_events: ['customer.subscription.started'],
104
+ },
105
+ });
106
+ console.log('createNotificationSetting', setting);
107
+ return setting;
108
+ },
109
+
110
+ // 应用级别管理通知
111
+ async createCheckoutWithSettingId() {
112
+ // 先创建设置
113
+ const NOTIFICATION_MOUNT_LOCATION = 'core.sdk.test';
114
+ const setting = await payment.settings.create({
115
+ type: 'notification',
116
+ mountLocation: NOTIFICATION_MOUNT_LOCATION,
117
+ description: 'test',
118
+ settings: {
119
+ self_handle: true,
120
+ include_events: ['customer.subscription.started'],
121
+ },
122
+ });
123
+ console.log('createNotificationSetting', setting);
124
+ // 使用设置ID创建结账会话
125
+ const checkoutSession = await payment.checkout.sessions.create({
126
+ mode: 'subscription',
127
+ line_items: [{ price_id: 'price_fQFIS12yi0JR3KePLmitjrhA', quantity: 1 }],
128
+ metadata: {
129
+ setting_id: NOTIFICATION_MOUNT_LOCATION, // 也可以是 setting.id
130
+ },
131
+ });
132
+ console.log('createCheckoutWithSettingId', checkoutSession);
133
+ return { setting, checkoutSession };
134
+ },
135
+ };
136
+
137
+ const paymentModule = {
138
+ // 获取支付意图
139
+ async retrievePaymentIntent() {
140
+ const paymentIntent = await payment.paymentIntents.retrieve('pi_ybTOCWweEnb9grWZsTH7MCVi');
141
+ console.log('retrievePaymentIntent', paymentIntent);
142
+ return paymentIntent;
143
+ },
144
+
145
+ // === 退款相关测试 ===
146
+
147
+ // 创建退款
148
+ async createRefund() {
149
+ const paymentIntentId = 'pi_ybTOCWweEnb9grWZsTH7MCVi';
150
+
151
+ const refundResult = await payment.paymentIntents.refund(paymentIntentId, {
152
+ amount: '0.001',
153
+ reason: 'requested_by_customer',
154
+ description: 'Refund Test',
155
+ });
156
+ console.log('createRefund', refundResult);
157
+ return refundResult;
158
+ },
159
+
160
+ // 获取退款详情
161
+ async retrieveRefund() {
162
+ const refund = await payment.refunds.retrieve('re_loHv143R78cSe38uGjxRBsfv');
163
+ console.log('retrieveRefund', refund);
164
+ return refund;
165
+ },
166
+
167
+ // 获取退款列表
168
+ async listRefunds() {
169
+ const paymentIntentId = 'pi_ybTOCWweEnb9grWZsTH7MCVi';
170
+ const paymentIntent = await payment.paymentIntents.retrieve(paymentIntentId);
171
+
172
+ const refunds = await payment.refunds.list({
173
+ invoice_id: paymentIntent.invoice_id,
174
+ });
175
+ console.log('listRefunds', refunds);
176
+ return refunds;
177
+ },
178
+
179
+ // 创建自定义退款
180
+ async createCustomRefund() {
181
+ const paymentIntentId = 'pi_ybTOCWweEnb9grWZsTH7MCVi';
182
+ const paymentIntent = await payment.paymentIntents.retrieve(paymentIntentId);
183
+
184
+ const refundData = {
185
+ amount: '0.001',
186
+ reason: 'requested_by_admin',
187
+ description: 'Custom Refund Test',
188
+ invoice_id: paymentIntent.invoice_id,
189
+ payment_intent_id: paymentIntent.id,
190
+ currency_id: paymentIntent.currency_id,
191
+ payment_method_id: paymentIntent.payment_method_id,
192
+ customer_id: paymentIntent.customer_id,
193
+ };
194
+
195
+ const customRefund = await payment.refunds.create(refundData);
196
+ console.log('createCustomRefund', customRefund);
197
+ return customRefund;
198
+ },
199
+
200
+ // 完整支付和退款流程
201
+ async paymentAndRefundFlow() {
202
+ // 注意:测试中使用已有的支付意图,实际应用中应先创建支付意图
203
+ const paymentIntentId = 'pi_ybTOCWweEnb9grWZsTH7MCVi';
204
+ const paymentIntent = await payment.paymentIntents.retrieve(paymentIntentId);
205
+
206
+ const partialRefund = await payment.paymentIntents.refund(paymentIntentId, {
207
+ amount: '0.0005', // 部分金额
208
+ reason: 'requested_by_customer',
209
+ description: 'Partial Refund Test',
210
+ });
211
+
212
+ const refunds = await payment.refunds.list({
213
+ invoice_id: paymentIntent.invoice_id,
214
+ });
215
+
216
+ console.log('paymentAndRefundFlow', {
217
+ paymentIntent,
218
+ partialRefund,
219
+ refundsList: refunds,
220
+ });
221
+ return {
222
+ paymentIntent,
223
+ partialRefund,
224
+ refundsList: refunds,
225
+ };
226
+ },
227
+ };
228
+
229
+ const productModule = {
230
+ // 创建单个产品
231
+ async createProduct() {
232
+ const product = await payment.products.create({
233
+ name: 'Test SDK product',
234
+ description: 'test',
235
+ });
236
+ console.log('createProduct', product);
237
+ return product;
238
+ },
239
+
240
+ // === 价格相关测试 ===
241
+
242
+ // 方式1:创建产品时一并创建价格
243
+ async createProductWithPrice() {
244
+ const product = await payment.products.create({
245
+ name: 'Test One-time Price Product',
246
+ description: 'Product for one-time price test',
247
+ type: 'good',
248
+ prices: [
249
+ {
250
+ type: 'one_time',
251
+ unit_amount: '1',
252
+ currency_id: 'pc_9l5sh8bcjbLU',
253
+ lookup_key: 'test_one_time_price',
254
+ },
255
+ ],
256
+ });
257
+ console.log('createProductWithPrice', product);
258
+ return product;
259
+ },
260
+
261
+ // 方式2:先创建产品,再创建价格
262
+ async createOneTimePrice() {
263
+ const product = await payment.products.create({
264
+ name: 'Test Separate Price Product',
265
+ description: 'Product for separate price creation test',
266
+ });
267
+
268
+ const price = await payment.prices.create({
269
+ product_id: product.id,
270
+ type: 'one_time',
271
+ unit_amount: '1',
272
+ currency_id: 'pc_9l5sh8bcjbLU',
273
+ });
274
+ console.log('createOneTimePrice', { product, price });
275
+ return { product, price };
276
+ },
277
+
278
+ // 创建循环订阅价格
279
+ async createRecurringPrice() {
280
+ const productId = 'prod_Acj9sSjFaABVQm'; // 使用已存在的产品ID
281
+
282
+ const price = await payment.prices.create({
283
+ locked: false,
284
+ model: 'standard',
285
+ billing_scheme: '',
286
+ currency_id: 'pc_aW2zy2y8yoi7',
287
+ nickname: '',
288
+ type: 'recurring',
289
+ unit_amount: '0.001',
290
+ lookup_key: '',
291
+ recurring: {
292
+ interval_config: 'month_1',
293
+ interval: 'month',
294
+ interval_count: 1,
295
+ usage_type: 'licensed',
296
+ aggregate_usage: 'sum',
297
+ },
298
+ transform_quantity: { divide_by: 1, round: 'up' },
299
+ tiers: [],
300
+ metadata: [],
301
+ custom_unit_amount: null,
302
+ currency_options: [],
303
+ tiers_mode: null,
304
+ quantity_available: 10,
305
+ quantity_limit_per_checkout: '2',
306
+ product_id: productId,
307
+ });
308
+ console.log('createRecurringPrice', price);
309
+ return price;
310
+ },
311
+
312
+ // 价格库存管理
313
+ async updateInventory() {
314
+ const inventoryPrice = await payment.prices.inventory('price_xUd1QXTwhoIdOfHa9WT6MdBe', {
315
+ quantity: 1,
316
+ action: 'decrement',
317
+ });
318
+ console.log('updateInventory', inventoryPrice);
319
+ return inventoryPrice;
320
+ },
321
+
322
+ // 完整流程:展示两种创建方式
323
+ async createProductWithPrices() {
324
+ // 方式1:内联价格
325
+ const productWithInlinePrices = await payment.products.create({
326
+ name: 'Product with Inline Prices',
327
+ description: 'Product with prices defined inline',
328
+ prices: [
329
+ {
330
+ type: 'one_time',
331
+ unit_amount: '15',
332
+ currency_id: 'pc_9l5sh8bcjbLU',
333
+ },
334
+ {
335
+ type: 'recurring',
336
+ unit_amount: '7.5',
337
+ currency_id: 'pc_aW2zy2y8yoi7',
338
+ recurring: {
339
+ interval_config: 'month_1',
340
+ interval: 'month',
341
+ interval_count: 1,
342
+ usage_type: 'licensed',
343
+ },
344
+ },
345
+ ],
346
+ });
347
+
348
+ // 方式2:分步创建
349
+ const product = await payment.products.create({
350
+ name: 'Product with Separate Prices',
351
+ description: 'Product with separately defined prices',
352
+ });
353
+
354
+ const oneTimePrice = await payment.prices.create({
355
+ product_id: product.id,
356
+ type: 'one_time',
357
+ unit_amount: '10',
358
+ currency_id: 'pc_9l5sh8bcjbLU',
359
+ });
360
+
361
+ const subscriptionPrice = await payment.prices.create({
362
+ product_id: product.id,
363
+ type: 'recurring',
364
+ unit_amount: '5',
365
+ currency_id: 'pc_aW2zy2y8yoi7',
366
+ recurring: {
367
+ interval_config: 'month_1',
368
+ interval: 'month',
369
+ interval_count: 1,
370
+ usage_type: 'licensed',
371
+ },
372
+ });
373
+ console.log('createProductWithPrices', {
374
+ inlineMethod: productWithInlinePrices,
375
+ separateMethod: {
376
+ product,
377
+ prices: {
378
+ oneTime: oneTimePrice,
379
+ subscription: subscriptionPrice,
380
+ },
381
+ },
382
+ });
383
+ return {
384
+ inlineMethod: productWithInlinePrices,
385
+ separateMethod: {
386
+ product,
387
+ prices: {
388
+ oneTime: oneTimePrice,
389
+ subscription: subscriptionPrice,
390
+ },
391
+ },
392
+ };
393
+ },
394
+ };
395
+
396
+ const subscriptionModule = {
397
+ // 获取所有订阅
398
+ async listSubscriptions() {
399
+ const subscriptions = await payment.subscriptions.list({
400
+ order: 'updated_at:ASC',
401
+ // 支持Sequelize排序格式
402
+ // order: [
403
+ // ['status', 'DESC'],
404
+ // ['updated_at', 'ASC'],
405
+ // ],
406
+ activeFirst: true,
407
+ });
408
+ console.log('listSubscriptions', subscriptions);
409
+ return subscriptions;
410
+ },
411
+
412
+ // 上报用量
413
+ async createUsageRecord() {
414
+ const record = {
415
+ subscription_item_id: 'si_Tq4c2KsC40uPq7',
416
+ quantity: 1,
417
+ action: 'increment',
418
+ livemode: false,
419
+ timestamp: Math.floor(Date.now() / 1000),
420
+ };
421
+
422
+ const result = await payment.subscriptionItems.createUsageRecord(record);
423
+ console.log('createUsageRecord', result);
424
+ return result;
425
+ },
426
+
427
+ // 并发上报
428
+ async createBatchUsageRecords() {
429
+ const records = [
430
+ {
431
+ subscription_item_id: 'si_Tq4c2KsC40uPq7',
432
+ quantity: 1,
433
+ action: 'increment',
434
+ livemode: false,
435
+ timestamp: Math.floor(Date.now() / 1000),
436
+ },
437
+ {
438
+ subscription_item_id: 'si_Tq4c2KsC40uPq7',
439
+ quantity: 2,
440
+ action: 'increment',
441
+ livemode: false,
442
+ timestamp: Math.floor(Date.now() / 1000),
443
+ },
444
+ ];
445
+
446
+ const results = await Promise.allSettled(
447
+ records.map((record) => payment.subscriptionItems.createUsageRecord(record))
448
+ );
449
+ console.log('createBatchUsageRecords', results);
450
+ return results;
451
+ },
452
+ };
453
+
454
+ // 测试模块注册
455
+ const testModules = {
456
+ checkout: checkoutModule,
457
+ payment: paymentModule,
458
+ product: productModule,
459
+ subscription: subscriptionModule,
460
+ };
461
+
462
+ async function runTest() {
8
463
  payment.environments.setTestMode(true);
9
- const subcriptions = await payment.subscriptions.list({
10
- order: 'updated_at:ASC',
11
- // order: [
12
- // ['status', 'DESC'],
13
- // ['updated_at', 'ASC'],
14
- // ], // also support sequelize order
15
- activeFirst: true,
464
+ await testModules.checkout.createBatchSubscription();
465
+ }
466
+
467
+ async function main() {
468
+ if (process.env.BLOCKLET_APP_ID) {
469
+ await runTest();
470
+ process.exit(0);
471
+ }
472
+
473
+ const appId = getAppId();
474
+ const args = process.argv.slice(2).filter((arg) => arg !== '--app-id' && arg !== appId);
475
+
476
+ console.log(`Running with blocklet exec, app-id: ${appId}`);
477
+
478
+ const child = spawn('blocklet', ['exec', '/scripts/sdk.js', '--app-id', appId, ...args], {
479
+ stdio: 'inherit',
480
+ });
481
+
482
+ child.on('exit', (code) => {
483
+ process.exit(code || 0);
484
+ });
485
+ }
486
+
487
+ if (require.main === module) {
488
+ main().catch((error) => {
489
+ console.error('Error:', error);
490
+ process.exit(1);
16
491
  });
17
- console.log('🚀 ~ subcriptions:', subcriptions);
18
-
19
- // const paymentIntent = await payment.paymentIntents.retrieve('pi_ybTOCWweEnb9grWZsTH7MCVi');
20
-
21
- // console.log('paymentIntent', paymentIntent);
22
-
23
- // const refundResult = await payment.paymentIntents.refund('pi_ybTOCWweEnb9grWZsTH7MCVi', {
24
- // amount: '0.001',
25
- // reason: 'requested_by_customer',
26
- // description: 'Refund Test',
27
- // });
28
- // console.log('refundResult', refundResult);
29
-
30
- // const refund = await payment.refunds.retrieve('re_loHv143R78cSe38uGjxRBsfv');
31
- // console.log('🚀 ~ refund:', refund);
32
-
33
- // const refunds = await payment.refunds.list({
34
- // invoice_id: paymentIntent.invoice_id,
35
- // });
36
- // console.log('🚀 ~ refunds:', refunds);
37
-
38
- // const customRefundResult = await payment.refunds.create({
39
- // amount: '0.001',
40
- // reason: 'requested_by_admin',
41
- // description: 'Custom Refund Test',
42
- // invoice_id: paymentIntent.invoice_id,
43
- // payment_intent_id: paymentIntent.id,
44
- // currency_id: paymentIntent.currency_id,
45
- // payment_method_id: paymentIntent.payment_method_id,
46
- // customer_id: paymentIntent.customer_id,
47
- // });
48
- // console.log('🚀 ~ customRefundResult:', customRefundResult);
49
-
50
- // const price = await payment.prices.create({
51
- // locked: false,
52
- // model: 'standard',
53
- // billing_scheme: '',
54
- // currency_id: 'pc_aW2zy2y8yoi7',
55
- // nickname: '',
56
- // type: 'recurring',
57
- // unit_amount: '0.001',
58
- // lookup_key: '',
59
- // recurring: {
60
- // interval_config: 'month_1',
61
- // interval: 'month',
62
- // interval_count: 1,
63
- // usage_type: 'licensed',
64
- // aggregate_usage: 'sum',
65
- // },
66
- // transform_quantity: { divide_by: 1, round: 'up' },
67
- // tiers: [],
68
- // metadata: [],
69
- // custom_unit_amount: null,
70
- // currency_options: [],
71
- // tiers_mode: null,
72
- // quantity_available: 10,
73
- // quantity_limit_per_checkout: '2',
74
- // product_id: 'prod_Acj9sSjFaABVQm',
75
- // });
76
- // console.log('🚀 ~ price:', price);
77
- // const inventoryPrice = await payment.prices.inventory('price_xUd1QXTwhoIdOfHa9WT6MdBe', {
78
- // quantity: 1,
79
- // action: 'decrement',
80
- // });
81
- // console.log('inventoryPrice', inventoryPrice);
82
-
83
- // const checkoutSession = await payment.checkout.sessions.create({
84
- // success_url:
85
- // 'https://bbqa2t5pfyfroyobmzknmktshckzto4btkfagxyjqwy.did.abtnet.io/store/api/payment/success?redirect=https://bbqa2t5pfyfroyobmzknmktshckzto4btkfagxyjqwy.did.abtnet.io/maker/mint/z3CtLCSchoZj4H5gqwyATSAEdvfd1m88VnDUZ',
86
- // cancel_url:
87
- // 'https://bbqa2t5pfyfroyobmzknmktshckzto4btkfagxyjqwy.did.abtnet.io/store/api/payment/cancel?redirect=https://bbqa2t5pfyfroyobmzknmktshckzto4btkfagxyjqwy.did.abtnet.io/maker/mint/z3CtLCSchoZj4H5gqwyATSAEdvfd1m88VnDUZ',
88
- // mode: 'payment',
89
- // line_items: [
90
- // { price_id: 'price_G7TE6QZbvqkIaqzDJVb2afws', quantity: 2 },
91
- // { price_id: 'price_rOUdD9fQrBGqn6M3YywXdDeK', quantity: 2 },
92
- // ],
93
- // subscription_data: {
94
- // service_actions: [
95
- // {
96
- // type: 'notification',
97
- // text: {
98
- // zh: '查看文档',
99
- // en: 'View Documentation',
100
- // },
101
- // link: 'https://www.arcblock.io/docs/createblocklet/en/quick-start',
102
- // triggerEvents: ['customer.subscription.started', 'customer.subscription.deleted'],
103
- // },
104
- // {
105
- // type: 'notification',
106
- // text: {
107
- // zh: '社区提问',
108
- // en: 'Ask in Community',
109
- // },
110
- // link: 'https://community.arcblock.io/?locale=en',
111
- // triggerEvents: ['customer.subscription.started', 'customer.subscription.renewed'],
112
- // },
113
- // {
114
- // type: 'custom',
115
- // text: {
116
- // zh: '查看',
117
- // en: 'View',
118
- // },
119
- // link: 'https://www.arcblock.io/docs/createblocklet/en/quick-start',
120
- // color: 'primary',
121
- // variant: 'outlined',
122
- // },
123
- // ],
124
- // },
125
- // expires_at: 1729243800,
126
- // });
127
- // console.log('checkoutSession', checkoutSession);
128
- // const product = await payment.products.create({
129
- // name: 'Test SDK product',
130
- // description: 'test',
131
- // });
132
- // console.log('🚀 ~ product:', product);
133
- // const paymentPrice = await payment.prices.create({
134
- // product_id: product.id,
135
- // type: 'one_time',
136
- // unit_amount: '1',
137
- // currency_id: 'pc_9l5sh8bcjbLU',
138
- // });
139
- // console.log('🚀 ~ paymentPrice:', paymentPrice);
140
-
141
- // 测试下并发上报
142
- // const promises = [
143
- // {
144
- // subscription_item_id: 'si_Tq4c2KsC40uPq7',
145
- // quantity: 1,
146
- // action: 'increment',
147
- // livemode: false,
148
- // timestamp: 1730692388,
149
- // },
150
- // // {
151
- // // subscription_item_id: 'si_Tq4c2KsC40uPq7',
152
- // // quantity: 2,
153
- // // action: 'increment',
154
- // // livemode: false,
155
- // // timestamp: 1730692388,
156
- // // },
157
- // ];
158
- // const results = await Promise.allSettled(promises.map((p) => payment.subscriptionItems.createUsageRecord(p)));
159
- // console.log('🚀 ~ results:', results);
160
- process.exit(0);
161
- })();
492
+ }
@@ -762,7 +762,7 @@ export default flat({
762
762
  bufferThreshold: 'Buffer Threshold',
763
763
  bufferThresholdHelp:
764
764
  'Only when the amount exceeding the deposit threshold reaches the buffer threshold will the collection operation be triggered.',
765
- bufferThresholdInvalid: 'Buffer threshold must be greater than 0',
765
+ bufferThresholdInvalid: 'Buffer threshold must be greater than or equal to 0',
766
766
  edit: 'Configure',
767
767
  enable: 'Enable',
768
768
  editTitle: 'Configure {currency} Vault Settings',
@@ -743,7 +743,7 @@ export default flat({
743
743
  withdrawThreshold: '提取阈值',
744
744
  bufferThreshold: '差额阈值',
745
745
  bufferThresholdHelp: '只有当超出存入阈值的金额达到差额阈值时,才会触发归集操作.',
746
- bufferThresholdInvalid: '差额阈值必须大于0',
746
+ bufferThresholdInvalid: '差额阈值不能小于0',
747
747
  edit: '配置',
748
748
  enable: '启用',
749
749
  editTitle: '配置 {currency} 冷钱包设置',
@@ -220,7 +220,7 @@ function EditForm({ item, onClose, onSuccess, isOwner }: EditFormProps) {
220
220
  rules={{
221
221
  required: true,
222
222
  validate: (value) => {
223
- if (Number(value) <= 0) {
223
+ if (Number(value) < 0) {
224
224
  return t('admin.vaultConfig.bufferThresholdInvalid');
225
225
  }
226
226
  const validPrecision = formatAmountPrecisionLimit(value.toString(), locale, item.decimal || 6);
@@ -242,11 +242,16 @@ function CustomerSubscriptionChangePayment({ subscription, customer, onComplete
242
242
  <Controller
243
243
  name="payment_currency"
244
244
  control={control}
245
- render={() => (
245
+ render={({ field }) => (
246
246
  <CurrencySelector
247
- value={selectedCurrencyIndex}
247
+ value={field.value}
248
248
  currencies={currencies}
249
- onChange={setSelectedCurrencyIndex}
249
+ onChange={(currencyId: string) => {
250
+ const index = currencies.findIndex((x) => x.id === currencyId);
251
+ if (index >= 0) {
252
+ setSelectedCurrencyIndex(index);
253
+ }
254
+ }}
250
255
  />
251
256
  )}
252
257
  />