payment-kit 1.13.25 → 1.13.27

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 (73) hide show
  1. package/api/src/index.ts +8 -1
  2. package/api/src/integrations/blockchain/nft.ts +125 -0
  3. package/api/src/integrations/blockchain/stake.ts +55 -0
  4. package/api/src/integrations/blocklet/notification.ts +101 -0
  5. package/api/src/integrations/blocklet/passport.ts +139 -0
  6. package/api/src/integrations/stripe/handlers/invoice.ts +1 -1
  7. package/api/src/integrations/stripe/resource.ts +7 -7
  8. package/api/src/integrations/stripe/setup.ts +1 -1
  9. package/api/src/jobs/checkout-session.ts +23 -0
  10. package/api/src/jobs/payment.ts +1 -2
  11. package/api/src/libs/audit.ts +44 -2
  12. package/api/src/libs/payment.ts +3 -4
  13. package/api/src/locales/en.ts +9 -1
  14. package/api/src/locales/zh.ts +9 -1
  15. package/api/src/routes/checkout-sessions.ts +44 -14
  16. package/api/src/routes/connect/collect.ts +1 -2
  17. package/api/src/routes/connect/pay.ts +1 -2
  18. package/api/src/routes/connect/setup.ts +1 -2
  19. package/api/src/routes/connect/shared.ts +7 -3
  20. package/api/src/routes/connect/subscribe.ts +2 -3
  21. package/api/src/routes/index.ts +4 -0
  22. package/api/src/routes/integrations/stripe.ts +1 -1
  23. package/api/src/routes/passports.ts +74 -0
  24. package/api/src/routes/payment-links.ts +12 -2
  25. package/api/src/routes/pricing-table.ts +17 -3
  26. package/api/src/routes/products.ts +3 -3
  27. package/api/src/routes/redirect.ts +18 -0
  28. package/api/src/routes/subscriptions.ts +2 -5
  29. package/api/src/store/migrations/20231021-nft.ts +22 -0
  30. package/api/src/store/models/checkout-session.ts +76 -20
  31. package/api/src/store/models/invoice.ts +2 -0
  32. package/api/src/store/models/payment-intent.ts +2 -0
  33. package/api/src/store/models/payment-link.ts +26 -15
  34. package/api/src/store/models/payment-method.ts +22 -1
  35. package/api/src/store/models/price.ts +2 -0
  36. package/api/src/store/models/subscription.ts +26 -4
  37. package/api/src/store/models/types.ts +32 -1
  38. package/api/third.d.ts +2 -0
  39. package/blocklet.yml +1 -1
  40. package/package.json +7 -5
  41. package/src/components/customer/actions.tsx +15 -17
  42. package/src/components/customer/form.tsx +1 -1
  43. package/src/components/invoice/list.tsx +2 -1
  44. package/src/components/passport/actions.tsx +62 -0
  45. package/src/components/passport/assign.tsx +82 -0
  46. package/src/components/payment-intent/list.tsx +5 -1
  47. package/src/components/payment-link/actions.tsx +14 -1
  48. package/src/components/payment-link/after-pay.tsx +33 -1
  49. package/src/components/payment-link/preview.tsx +3 -6
  50. package/src/components/price/form.tsx +22 -23
  51. package/src/components/pricing-table/actions.tsx +14 -1
  52. package/src/components/pricing-table/payment-settings.tsx +33 -1
  53. package/src/components/pricing-table/preview.tsx +3 -7
  54. package/src/components/pricing-table/product-settings.tsx +4 -0
  55. package/src/components/pricing-table/product-skeleton.tsx +39 -0
  56. package/src/components/product/actions.tsx +14 -1
  57. package/src/components/status.tsx +1 -1
  58. package/src/components/subscription/status.tsx +3 -3
  59. package/src/components/table.tsx +14 -4
  60. package/src/global.css +7 -5
  61. package/src/libs/util.ts +6 -0
  62. package/src/locales/en.tsx +53 -2
  63. package/src/locales/zh.tsx +272 -116
  64. package/src/pages/admin/payments/links/create.tsx +4 -0
  65. package/src/pages/admin/payments/links/detail.tsx +9 -4
  66. package/src/pages/admin/products/index.tsx +2 -0
  67. package/src/pages/admin/products/passports/index.tsx +154 -0
  68. package/src/pages/admin/products/pricing-tables/create.tsx +1 -1
  69. package/src/pages/admin/settings/index.tsx +1 -1
  70. package/src/pages/admin/settings/payment-methods/index.tsx +17 -7
  71. package/src/pages/checkout/pay.tsx +15 -13
  72. package/src/pages/checkout/pricing-table.tsx +127 -91
  73. package/api/src/libs/chain/arcblock.ts +0 -13
@@ -11,9 +11,18 @@ import {
11
11
  } from 'sequelize';
12
12
  import type { LiteralUnion } from 'type-fest';
13
13
 
14
- import { createStatusEvent } from '../../libs/audit';
14
+ import { createCustomEvent, createStatusEvent } from '../../libs/audit';
15
15
  import { createIdGenerator } from '../../libs/util';
16
- import type { CurrencyConversion, CustomField, CustomerDetail, InvoiceData, LineItem, PaymentDetails } from './types';
16
+ import type {
17
+ CurrencyConversion,
18
+ CustomField,
19
+ CustomerDetail,
20
+ InvoiceData,
21
+ LineItem,
22
+ NftMintDetails,
23
+ NftMintSettings,
24
+ PaymentDetails,
25
+ } from './types';
17
26
 
18
27
  const nextId = createIdGenerator('cs', 58);
19
28
 
@@ -162,13 +171,24 @@ export class CheckoutSession extends Model<InferAttributes<CheckoutSession>, Inf
162
171
  // 3rd party payment tx hash
163
172
  declare payment_details?: PaymentDetails;
164
173
 
174
+ // A subset of parameters to be passed to PaymentIntent creation for Checkout Sessions in payment mode.
175
+ declare payment_intent_data?: {
176
+ description?: string;
177
+ statement_descriptor?: string;
178
+ metadata?: Record<string, any>;
179
+ };
180
+
181
+ // Whether should we mint an nft to the customer on checkout complete
182
+ // Will use setting from payment method
183
+ declare nft_mint_status: LiteralUnion<'disabled' | 'pending' | 'minted' | 'sent' | 'error', string>;
184
+ declare nft_mint_settings?: NftMintSettings;
185
+ declare nft_mint_details?: NftMintDetails;
186
+
165
187
  // FIXME: Only exist on creation
166
188
  // declare discounts?: {
167
189
  // coupon?: string;
168
190
  // promotion_code?: string;
169
191
  // };
170
- // A subset of parameters to be passed to PaymentIntent creation for Checkout Sessions in payment mode.
171
- // declare payment_intent_data?: any;
172
192
 
173
193
  // TODO: following fields not supported yet
174
194
  // automatic_tax
@@ -236,12 +256,19 @@ export class CheckoutSession extends Model<InferAttributes<CheckoutSession>, Inf
236
256
  },
237
257
  mode: {
238
258
  type: DataTypes.ENUM('payment', 'setup', 'subscription'),
259
+ allowNull: false,
239
260
  },
240
261
  status: {
241
262
  type: DataTypes.ENUM('complete', 'open', 'expired'),
263
+ allowNull: false,
242
264
  },
243
265
  payment_status: {
244
266
  type: DataTypes.ENUM('paid', 'unpaid', 'no_payment_required'),
267
+ allowNull: false,
268
+ },
269
+ nft_mint_status: {
270
+ type: DataTypes.ENUM('disabled', 'pending', 'minted', 'sent', 'error'),
271
+ allowNull: false,
245
272
  },
246
273
  line_items: {
247
274
  type: DataTypes.JSON,
@@ -364,24 +391,53 @@ export class CheckoutSession extends Model<InferAttributes<CheckoutSession>, Inf
364
391
  };
365
392
 
366
393
  public static initialize(sequelize: any) {
367
- this.init(CheckoutSession.GENESIS_ATTRIBUTES, {
368
- sequelize,
369
- modelName: 'CheckoutSession',
370
- tableName: 'checkout_sessions',
371
- createdAt: 'created_at',
372
- updatedAt: 'updated_at',
373
- hooks: {
374
- afterUpdate: (model: CheckoutSession, options) => {
375
- createStatusEvent(
376
- 'CheckoutSession',
377
- 'checkout.session',
378
- { complete: 'completed', expired: 'expired' },
379
- model,
380
- options
381
- ).catch(console.error);
394
+ this.init(
395
+ {
396
+ ...CheckoutSession.GENESIS_ATTRIBUTES,
397
+ payment_intent_data: {
398
+ type: DataTypes.JSON,
399
+ allowNull: true,
400
+ },
401
+ nft_mint_settings: {
402
+ type: DataTypes.JSON,
403
+ allowNull: true,
404
+ },
405
+ nft_mint_details: {
406
+ type: DataTypes.JSON,
407
+ allowNull: true,
382
408
  },
383
409
  },
384
- });
410
+ {
411
+ sequelize,
412
+ modelName: 'CheckoutSession',
413
+ tableName: 'checkout_sessions',
414
+ createdAt: 'created_at',
415
+ updatedAt: 'updated_at',
416
+ hooks: {
417
+ afterUpdate: (model: CheckoutSession, options) => {
418
+ createStatusEvent(
419
+ 'CheckoutSession',
420
+ 'checkout.session',
421
+ { complete: 'completed', expired: 'expired' },
422
+ model,
423
+ options
424
+ ).catch(console.error);
425
+ createCustomEvent(
426
+ 'CheckoutSession',
427
+ 'checkout.session',
428
+ (current, previous) => {
429
+ if (previous.nft_mint_status === 'pending' && current.nft_mint_status === 'minted') {
430
+ return 'nft_minted';
431
+ }
432
+ return '';
433
+ },
434
+ model,
435
+ options
436
+ ).catch(console.error);
437
+ },
438
+ },
439
+ }
440
+ );
385
441
  }
386
442
 
387
443
  public static associate(models: any) {
@@ -173,6 +173,7 @@ export class Invoice extends Model<InferAttributes<Invoice>, InferCreationAttrib
173
173
  },
174
174
  collection_method: {
175
175
  type: DataTypes.ENUM('charge_automatically', 'send_invoice'),
176
+ allowNull: false,
176
177
  },
177
178
  currency_id: {
178
179
  type: DataTypes.STRING(15),
@@ -204,6 +205,7 @@ export class Invoice extends Model<InferAttributes<Invoice>, InferCreationAttrib
204
205
  },
205
206
  status: {
206
207
  type: DataTypes.ENUM('draft', 'open', 'void', 'paid', 'uncollectible'),
208
+ allowNull: false,
207
209
  },
208
210
  checkout_session_id: {
209
211
  type: DataTypes.STRING(64),
@@ -196,9 +196,11 @@ export class PaymentIntent extends Model<InferAttributes<PaymentIntent>, InferCr
196
196
  },
197
197
  capture_method: {
198
198
  type: DataTypes.ENUM('automatic', 'manual'),
199
+ allowNull: false,
199
200
  },
200
201
  confirmation_method: {
201
202
  type: DataTypes.ENUM('automatic', 'manual'),
203
+ allowNull: false,
202
204
  },
203
205
  payment_method_types: {
204
206
  type: DataTypes.JSON,
@@ -4,7 +4,7 @@ import type { LiteralUnion } from 'type-fest';
4
4
 
5
5
  import { createEvent } from '../../libs/audit';
6
6
  import { createIdGenerator } from '../../libs/util';
7
- import type { AfterPayment, CustomField, LineItem } from './types';
7
+ import type { AfterPayment, CustomField, LineItem, NftMintSettings } from './types';
8
8
 
9
9
  const nextId = createIdGenerator('plink', 24);
10
10
 
@@ -71,6 +71,8 @@ export class PaymentLink extends Model<InferAttributes<PaymentLink>, InferCreati
71
71
  trial_period_days: number;
72
72
  };
73
73
 
74
+ declare nft_mint_settings?: NftMintSettings;
75
+
74
76
  declare metadata?: Record<string, any>;
75
77
 
76
78
  // TODO: following fields not supported
@@ -190,21 +192,30 @@ export class PaymentLink extends Model<InferAttributes<PaymentLink>, InferCreati
190
192
  };
191
193
 
192
194
  public static initialize(sequelize: any) {
193
- this.init(PaymentLink.GENESIS_ATTRIBUTES, {
194
- sequelize,
195
- modelName: 'PaymentLink',
196
- tableName: 'payment_links',
197
- createdAt: 'created_at',
198
- updatedAt: 'updated_at',
199
- hooks: {
200
- afterCreate: (model: PaymentLink, options) =>
201
- createEvent('PaymentLink', 'payment_link.created', model, options).catch(console.error),
202
- afterUpdate: (model: PaymentLink, options) =>
203
- createEvent('PaymentLink', 'payment_link.updated', model, options).catch(console.error),
204
- afterDestroy: (model: PaymentLink, options) =>
205
- createEvent('PaymentLink', 'payment_link.deleted', model, options).catch(console.error),
195
+ this.init(
196
+ {
197
+ ...PaymentLink.GENESIS_ATTRIBUTES,
198
+ nft_mint_settings: {
199
+ type: DataTypes.JSON,
200
+ allowNull: true,
201
+ },
206
202
  },
207
- });
203
+ {
204
+ sequelize,
205
+ modelName: 'PaymentLink',
206
+ tableName: 'payment_links',
207
+ createdAt: 'created_at',
208
+ updatedAt: 'updated_at',
209
+ hooks: {
210
+ afterCreate: (model: PaymentLink, options) =>
211
+ createEvent('PaymentLink', 'payment_link.created', model, options).catch(console.error),
212
+ afterUpdate: (model: PaymentLink, options) =>
213
+ createEvent('PaymentLink', 'payment_link.updated', model, options).catch(console.error),
214
+ afterDestroy: (model: PaymentLink, options) =>
215
+ createEvent('PaymentLink', 'payment_link.deleted', model, options).catch(console.error),
216
+ },
217
+ }
218
+ );
208
219
  }
209
220
 
210
221
  public static associate(models: any) {
@@ -1,5 +1,6 @@
1
1
  /* eslint-disable @typescript-eslint/lines-between-class-members */
2
2
  import security from '@blocklet/sdk/lib/security';
3
+ import OcapClient from '@ocap/client';
3
4
  import cloneDeep from 'lodash/cloneDeep';
4
5
  import { CreationOptional, DataTypes, InferAttributes, InferCreationAttributes, Model } from 'sequelize';
5
6
  import Stripe from 'stripe';
@@ -12,6 +13,7 @@ import type { PaymentMethodSettings } from './types';
12
13
  const nextId = createIdGenerator('pm', 24);
13
14
 
14
15
  const stripeClients = new Map<string, Stripe>();
16
+ const ocapClients = new Map<string, OcapClient>();
15
17
 
16
18
  // eslint-disable-next-line prettier/prettier
17
19
  export class PaymentMethod extends Model<InferAttributes<PaymentMethod>, InferCreationAttributes<PaymentMethod>> {
@@ -162,7 +164,7 @@ export class PaymentMethod extends Model<InferAttributes<PaymentMethod>, InferCr
162
164
  return tmp;
163
165
  }
164
166
 
165
- getStripe() {
167
+ getStripeClient() {
166
168
  if (this.type !== 'stripe') {
167
169
  throw new Error('payment method is not stripe');
168
170
  }
@@ -181,6 +183,25 @@ export class PaymentMethod extends Model<InferAttributes<PaymentMethod>, InferCr
181
183
  return client as Stripe;
182
184
  }
183
185
 
186
+ getOcapClient() {
187
+ if (this.type !== 'arcblock') {
188
+ throw new Error('payment method is not arcblock');
189
+ }
190
+ if (!this.settings.arcblock) {
191
+ throw new Error('payment method config insufficient to create ocap client');
192
+ }
193
+
194
+ const host = this.settings.arcblock?.api_host;
195
+ const cached = ocapClients.has(host);
196
+ if (!cached) {
197
+ const created = new OcapClient(host);
198
+ ocapClients.set(host, created);
199
+ return created;
200
+ }
201
+
202
+ return ocapClients.get(host) as OcapClient;
203
+ }
204
+
184
205
  public static async supportAutoCharge(id: string) {
185
206
  const method = await PaymentMethod.findByPk(id);
186
207
  return method && ['arcblock', 'ethereum'].includes(method.type);
@@ -103,9 +103,11 @@ export class Price extends Model<InferAttributes<Price>, InferCreationAttributes
103
103
  },
104
104
  type: {
105
105
  type: DataTypes.ENUM('one_time', 'recurring'),
106
+ allowNull: false,
106
107
  },
107
108
  billing_scheme: {
108
109
  type: DataTypes.ENUM('per_unit', 'tiered'),
110
+ allowNull: false,
109
111
  },
110
112
  unit_amount: {
111
113
  type: DataTypes.STRING(32),
@@ -3,7 +3,7 @@
3
3
  import { CreationOptional, DataTypes, InferAttributes, InferCreationAttributes, Model } from 'sequelize';
4
4
  import type { LiteralUnion } from 'type-fest';
5
5
 
6
- import { createEvent, createStatusEvent } from '../../libs/audit';
6
+ import { createCustomEvent, createEvent, createStatusEvent } from '../../libs/audit';
7
7
  import { createIdGenerator } from '../../libs/util';
8
8
  import type { PaymentDetails, PaymentSettings, PriceRecurring } from './types';
9
9
 
@@ -188,6 +188,7 @@ export class Subscription extends Model<InferAttributes<Subscription>, InferCrea
188
188
  },
189
189
  collection_method: {
190
190
  type: DataTypes.ENUM('charge_automatically', 'send_invoice'),
191
+ allowNull: false,
191
192
  },
192
193
  days_until_due: {
193
194
  type: DataTypes.NUMBER,
@@ -269,12 +270,13 @@ export class Subscription extends Model<InferAttributes<Subscription>, InferCrea
269
270
  createEvent('Subscription', 'customer.subscription.created', model, options).catch(console.error),
270
271
  afterUpdate: (model: Subscription, options) => {
271
272
  createEvent('Subscription', 'customer.subscription.updated', model, options).catch(console.error);
272
- createStatusEvent('Subscription', 'customer.subscription', { canceled: 'canceled' }, model, options).catch(
273
+ createStatusEvent('Subscription', 'customer.subscription', { canceled: 'deleted' }, model, options).catch(
274
+ console.error
275
+ );
276
+ createCustomEvent('Subscription', 'customer.subscription', getSubscriptionEventType, model, options).catch(
273
277
  console.error
274
278
  );
275
279
  },
276
- afterDestroy: (model: Subscription, options) =>
277
- createEvent('Subscription', 'customer.subscription.deleted', model, options).catch(console.error),
278
280
  },
279
281
  });
280
282
  }
@@ -303,3 +305,23 @@ export class Subscription extends Model<InferAttributes<Subscription>, InferCrea
303
305
  }
304
306
 
305
307
  export type TSubscription = InferAttributes<Subscription>;
308
+
309
+ export function getSubscriptionEventType(current: TSubscription, previous: Partial<TSubscription>) {
310
+ if (current.pause_collection && !previous.pause_collection) {
311
+ return 'paused';
312
+ }
313
+ if (!current.pause_collection && previous.pause_collection) {
314
+ return 'resumed';
315
+ }
316
+ if (
317
+ current.status === 'active' &&
318
+ current.current_period_end &&
319
+ current.current_period_end !== previous.current_period_end &&
320
+ current.current_period_start &&
321
+ current.current_period_start !== previous.current_period_start
322
+ ) {
323
+ return 'renewed';
324
+ }
325
+
326
+ return '';
327
+ }
@@ -152,7 +152,12 @@ export type LineItem = {
152
152
  // tax_rates?: any;
153
153
  };
154
154
 
155
- export type InvoiceData = Record<string, any>;
155
+ export type InvoiceData = {
156
+ description?: string;
157
+ footer?: string;
158
+ metadata?: Record<string, any>;
159
+ custom_fields?: SimpleCustomField[];
160
+ };
156
161
 
157
162
  // @see https://stripe.com/docs/currencies/conversions
158
163
  // If presentment currency is different from settlement currency, this is the exchange rate snapshot used.
@@ -262,6 +267,28 @@ export type PaymentDetails = {
262
267
  };
263
268
  };
264
269
 
270
+ export type NftMintSettings = {
271
+ enabled: boolean;
272
+ behavior?: LiteralUnion<'per_customer' | 'per_checkout_session', string>;
273
+ factory?: string;
274
+ inputs?: Record<string, string>;
275
+ };
276
+
277
+ export type NftMintDetails = {
278
+ arcblock?: {
279
+ tx_hash?: string;
280
+ address: string;
281
+ owner: string;
282
+ error?: string;
283
+ };
284
+ ethereum?: {
285
+ tx_hash?: string;
286
+ address: string;
287
+ owner: string;
288
+ error?: string;
289
+ };
290
+ };
291
+
265
292
  // Very similar to PaymentLink
266
293
  export type PricingTableItem = {
267
294
  price_id: string;
@@ -306,6 +333,8 @@ export type PricingTableItem = {
306
333
  description: string;
307
334
  trial_period_days: number;
308
335
  };
336
+
337
+ nft_mint_settings?: NftMintSettings;
309
338
  };
310
339
 
311
340
  export type BrandSettings = {
@@ -346,6 +375,7 @@ export type EventType = LiteralUnion<
346
375
  | 'charge.updated'
347
376
  | 'checkout.session.async_payment_failed'
348
377
  | 'checkout.session.async_payment_succeeded'
378
+ | 'checkout.session.nft_minted'
349
379
  | 'checkout.session.completed'
350
380
  | 'checkout.session.expired'
351
381
  | 'coupon.created'
@@ -370,6 +400,7 @@ export type EventType = LiteralUnion<
370
400
  | 'customer.subscription.pending_update_applied'
371
401
  | 'customer.subscription.pending_update_expired'
372
402
  | 'customer.subscription.resumed'
403
+ | 'customer.subscription.renewed'
373
404
  | 'customer.subscription.trial_will_end'
374
405
  | 'customer.subscription.updated'
375
406
  | 'customer.tax_id.created'
package/api/third.d.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  declare module 'vite-plugin-blocklet';
2
2
 
3
+ declare module '@blocklet/sdk/service/notification';
4
+
3
5
  declare module 'express-history-api-fallback';
4
6
 
5
7
  declare module 'express-async-errors';
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.13.25
17
+ version: 1.13.27
18
18
  logo: logo.png
19
19
  files:
20
20
  - dist
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "payment-kit",
3
- "version": "1.13.25",
3
+ "version": "1.13.27",
4
4
  "scripts": {
5
5
  "dev": "blocklet dev",
6
6
  "eject": "vite eject",
@@ -45,8 +45,8 @@
45
45
  "@arcblock/did-connect": "^2.7.28",
46
46
  "@arcblock/did-util": "^1.18.92",
47
47
  "@arcblock/ux": "^2.7.28",
48
- "@blocklet/logger": "^1.16.16",
49
- "@blocklet/sdk": "^1.16.16",
48
+ "@blocklet/logger": "1.16.17-beta-703fb879",
49
+ "@blocklet/sdk": "1.16.17-beta-703fb879",
50
50
  "@blocklet/ui-react": "^2.7.28",
51
51
  "@blocklet/uploader": "^0.0.26",
52
52
  "@mui/icons-material": "^5.14.13",
@@ -54,6 +54,7 @@
54
54
  "@mui/material": "^5.14.13",
55
55
  "@mui/styles": "^5.14.13",
56
56
  "@mui/system": "^5.14.13",
57
+ "@ocap/asset": "^1.18.92",
57
58
  "@ocap/client": "^1.18.92",
58
59
  "@ocap/mcrypto": "^1.18.92",
59
60
  "@ocap/util": "^1.18.92",
@@ -99,9 +100,10 @@
99
100
  "validator": "^13.11.0"
100
101
  },
101
102
  "devDependencies": {
103
+ "@abtnode/types": "1.16.17-beta-703fb879",
102
104
  "@arcblock/eslint-config": "^0.2.4",
103
105
  "@arcblock/eslint-config-ts": "^0.2.4",
104
- "@did-pay/types": "1.13.25",
106
+ "@did-pay/types": "1.13.27",
105
107
  "@types/cookie-parser": "^1.4.4",
106
108
  "@types/cors": "^2.8.14",
107
109
  "@types/dotenv-flow": "^3.3.1",
@@ -138,5 +140,5 @@
138
140
  "parser": "typescript"
139
141
  }
140
142
  },
141
- "gitHead": "4032a368047c93d3fc52ecbaa4cf37221924f45c"
143
+ "gitHead": "7f11915be75b7419085508377566b02495b423a1"
142
144
  }
@@ -8,7 +8,7 @@ import api from '../../libs/api';
8
8
  import { formatError } from '../../libs/util';
9
9
  import Actions from '../actions';
10
10
  import ClickBoundary from '../click-boundary';
11
- import ConfirmDialog from '../confirm';
11
+ import EditCustomer from './edit';
12
12
 
13
13
  type Props = {
14
14
  data: TCustomerExpanded;
@@ -27,12 +27,12 @@ export default function CustomerActions({ data, onChange, variant }: Props) {
27
27
  loading: false,
28
28
  });
29
29
 
30
- const onRefund = async () => {
30
+ const onUpdateInfo = async (updates: TCustomerExpanded) => {
31
31
  try {
32
32
  setState({ loading: true });
33
- await api.put(`/api/customers/${data.id}/archive`).then((res) => res.data);
33
+ await api.put(`/api/customers/${data.id}`, updates).then((res) => res.data);
34
34
  Toast.success(t('common.saved'));
35
- onChange(state.action);
35
+ onChange('update');
36
36
  } catch (err) {
37
37
  console.error(err);
38
38
  Toast.error(formatError(err));
@@ -46,27 +46,25 @@ export default function CustomerActions({ data, onChange, variant }: Props) {
46
46
  <Actions
47
47
  variant={variant}
48
48
  actions={[
49
- {
50
- label: t('admin.payments'),
51
- handler: () => setState({ action: 'refund' }),
52
- color: 'primary',
53
- disabled: true,
54
- },
49
+ // {
50
+ // label: t('admin.payments'),
51
+ // handler: () => setState({ action: 'refund' }),
52
+ // color: 'primary',
53
+ // disabled: true,
54
+ // },
55
55
  {
56
56
  label: t('admin.customer.edit'),
57
57
  handler: () => setState({ action: 'edit' }),
58
58
  color: 'primary',
59
- disabled: true,
60
59
  },
61
60
  ]}
62
61
  />
63
- {state.action === 'refund' && (
64
- <ConfirmDialog
65
- onConfirm={onRefund}
66
- onCancel={() => setState({ action: '' })}
67
- title={t('admin.customer.refund')}
68
- message={t('admin.customer.refundTip')}
62
+ {state.action === 'edit' && (
63
+ <EditCustomer
64
+ data={data}
69
65
  loading={state.loading}
66
+ onSave={onUpdateInfo}
67
+ onCancel={() => setState({ action: '' })}
70
68
  />
71
69
  )}
72
70
  </ClickBoundary>
@@ -64,7 +64,7 @@ export default function CustomerForm() {
64
64
  }}
65
65
  />
66
66
 
67
- <Typography component="h6" sx={{ mb: 1, color: 'text.primary', fontWeight: 600 }}>
67
+ <Typography component="h6" sx={{ pt: 2, color: 'text.primary', fontWeight: 600 }}>
68
68
  {t('checkout.billing.required')}
69
69
  </Typography>
70
70
  <Controller
@@ -94,11 +94,12 @@ export default function InvoiceList({ customer_id, subscription_id, features, st
94
94
  label: t('common.amount'),
95
95
  name: 'total',
96
96
  width: 60,
97
+ align: 'right',
97
98
  options: {
98
99
  customBodyRenderLite: (_: string, index: number) => {
99
100
  const item = data.list[index];
100
101
  return (
101
- <Typography component="span">
102
+ <Typography component="strong" fontWeight={600}>
102
103
  {fromUnitToToken(item?.total, item?.paymentCurrency.decimal)}&nbsp;
103
104
  {item?.paymentCurrency.symbol}
104
105
  </Typography>
@@ -0,0 +1,62 @@
1
+ import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
2
+ import Toast from '@arcblock/ux/lib/Toast';
3
+ import { useSetState } from 'ahooks';
4
+
5
+ import api from '../../libs/api';
6
+ import { formatError } from '../../libs/util';
7
+ import Actions from '../actions';
8
+ import ClickBoundary from '../click-boundary';
9
+ import ConfirmDialog from '../confirm';
10
+
11
+ type Props = {
12
+ data: any;
13
+ variant?: string;
14
+ };
15
+
16
+ PassportActions.defaultProps = {
17
+ variant: 'compact',
18
+ };
19
+
20
+ export default function PassportActions(props: Props) {
21
+ const { t } = useLocaleContext();
22
+
23
+ const [state, setState] = useSetState({
24
+ action: '',
25
+ loading: false,
26
+ });
27
+
28
+ const onUnassign = async () => {
29
+ try {
30
+ setState({ loading: true });
31
+ await api.delete(`/api/passports/assign/${props.data.name}`).then((res) => res.data);
32
+ Toast.success(t('common.saved'));
33
+ } catch (err) {
34
+ console.error(err);
35
+ Toast.error(formatError(err));
36
+ } finally {
37
+ setState({ loading: false, action: '' });
38
+ }
39
+ };
40
+
41
+ const actions = [
42
+ {
43
+ label: t('admin.passport.unassign'),
44
+ handler: () => setState({ action: 'unassign' }),
45
+ color: 'error',
46
+ },
47
+ ];
48
+
49
+ return (
50
+ <ClickBoundary>
51
+ <Actions variant={props.variant} actions={actions} />
52
+ {state.action === 'unassign' && (
53
+ <ConfirmDialog
54
+ onConfirm={onUnassign}
55
+ onCancel={() => setState({ action: '' })}
56
+ title={t('admin.passport.unassign')}
57
+ message={t('admin.passport.unassignTip')}
58
+ />
59
+ )}
60
+ </ClickBoundary>
61
+ );
62
+ }