@blocklet/payment-react 1.13.121 → 1.13.122

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.
@@ -4,11 +4,11 @@ import { useLocaleContext } from "@arcblock/ux/lib/Locale/context";
4
4
  import { useTheme } from "@arcblock/ux/lib/Theme";
5
5
  import Toast from "@arcblock/ux/lib/Toast";
6
6
  import { LoadingButton } from "@mui/lab";
7
- import { Avatar, Fade, InputAdornment, MenuItem, Select, Stack, Typography } from "@mui/material";
7
+ import { Avatar, Card, Fade, InputAdornment, Stack, Typography } from "@mui/material";
8
8
  import { useCreation, useSetState, useSize } from "ahooks";
9
9
  import { PhoneNumberUtil } from "google-libphonenumber";
10
10
  import pWaitFor from "p-wait-for";
11
- import { useEffect } from "react";
11
+ import { useEffect, useState } from "react";
12
12
  import { Controller, useFormContext, useWatch } from "react-hook-form";
13
13
  import { dispatch } from "use-bus";
14
14
  import isEmail from "validator/es/lib/isEmail";
@@ -36,6 +36,19 @@ const waitForCheckoutComplete = async (sessionId) => {
36
36
  );
37
37
  return result;
38
38
  };
39
+ const flatPaymentMethods = (methods = []) => {
40
+ const out = [];
41
+ methods.forEach((method) => {
42
+ const currencies = method.paymentCurrencies || method.payment_currencies || [];
43
+ currencies.forEach((currency) => {
44
+ out.push({
45
+ ...currency,
46
+ method
47
+ });
48
+ });
49
+ });
50
+ return out;
51
+ };
39
52
  PaymentForm.defaultProps = {};
40
53
  export default function PaymentForm({
41
54
  checkoutSession,
@@ -59,6 +72,8 @@ export default function PaymentForm({
59
72
  customer,
60
73
  stripePaying: false
61
74
  });
75
+ const currencies = flatPaymentMethods(paymentMethods);
76
+ const [paymentCurrencyIndex, setPaymentCurrencyIndex] = useState(0);
62
77
  useEffect(() => {
63
78
  if (session?.user) {
64
79
  const values = getValues();
@@ -73,9 +88,11 @@ export default function PaymentForm({
73
88
  }
74
89
  }
75
90
  }, [session?.user, getValues, setValue]);
91
+ useEffect(() => {
92
+ setValue("payment_method", currencies[paymentCurrencyIndex].method.id);
93
+ setValue("payment_currency", currencies[paymentCurrencyIndex].id);
94
+ }, [paymentCurrencyIndex, currencies, setValue]);
76
95
  const paymentMethod = useWatch({ control, name: "payment_method" });
77
- const paymentCurrency = useWatch({ control, name: "payment_currency" });
78
- const paymentCurrencies = paymentMethods.find((x) => x.id === paymentMethod)?.payment_currencies || [];
79
96
  const domSize = useSize(document.body);
80
97
  const isColumnLayout = useCreation(() => {
81
98
  if (domSize) {
@@ -88,12 +105,8 @@ export default function PaymentForm({
88
105
  const payee = getStatementDescriptor(checkoutSession.line_items);
89
106
  const buttonText = session?.user ? t(`payment.checkout.${checkoutSession.mode}`) : t("payment.checkout.connect", { action: t(`payment.checkout.${checkoutSession.mode}`) });
90
107
  const method = paymentMethods.find((x) => x.id === paymentMethod);
91
- const handleMethodChange = (e) => {
92
- setValue("payment_method", e.target.value);
93
- const currencies = paymentMethods.find((x) => x.id === e.target.value)?.payment_currencies || [];
94
- if (currencies.some((x) => x.id === paymentCurrency) === false) {
95
- setValue("payment_currency", currencies[0]?.id);
96
- }
108
+ const handleCurrencyChange = (index) => {
109
+ setPaymentCurrencyIndex(index);
97
110
  };
98
111
  const handleConnected = async () => {
99
112
  try {
@@ -267,36 +280,42 @@ export default function PaymentForm({
267
280
  /* @__PURE__ */ jsx(AddressForm, { mode: checkoutSession.billing_address_collection, stripe: method?.type === "stripe" }),
268
281
  /* @__PURE__ */ jsx(Fade, { in: true, children: /* @__PURE__ */ jsxs(Stack, { direction: "column", alignItems: "flex-start", className: "cko-payment-methods", children: [
269
282
  /* @__PURE__ */ jsx(Typography, { sx: { mb: 2, color: "text.primary", fontWeight: 600 }, children: t("payment.checkout.method") }),
270
- /* @__PURE__ */ jsxs(Stack, { direction: "row", sx: { width: 1 }, spacing: 1, children: [
271
- /* @__PURE__ */ jsx(
272
- Controller,
273
- {
274
- name: "payment_method",
275
- control,
276
- render: ({ field }) => /* @__PURE__ */ jsx(Select, { ...field, onChange: handleMethodChange, sx: { flex: 1 }, size: "small", children: paymentMethods.map((x) => {
277
- const selected = x.id === paymentMethod;
278
- return /* @__PURE__ */ jsx(MenuItem, { value: x.id, children: /* @__PURE__ */ jsxs(Stack, { direction: "row", spacing: 1, children: [
279
- /* @__PURE__ */ jsx(Avatar, { src: x.logo, alt: x.name, sx: { width: 20, height: 20 } }),
280
- /* @__PURE__ */ jsx(Typography, { color: selected ? "text.primary" : "text.secondary", children: x.name })
281
- ] }) }, x.id);
282
- }) })
283
- }
284
- ),
285
- /* @__PURE__ */ jsx(
286
- Controller,
287
- {
288
- name: "payment_currency",
289
- control,
290
- render: ({ field }) => /* @__PURE__ */ jsx(Select, { ...field, sx: { flex: 1 }, size: "small", children: paymentCurrencies.map((x) => {
291
- const selected = x.id === paymentCurrency;
292
- return /* @__PURE__ */ jsx(MenuItem, { value: x.id, children: /* @__PURE__ */ jsxs(Stack, { direction: "row", spacing: 1, children: [
293
- /* @__PURE__ */ jsx(Avatar, { src: x.logo, alt: x.name, sx: { width: 20, height: 20 } }),
294
- /* @__PURE__ */ jsx(Typography, { color: selected ? "text.primary" : "text.secondary", children: x.symbol })
295
- ] }) }, x.id);
296
- }) })
297
- }
298
- )
299
- ] }),
283
+ /* @__PURE__ */ jsx(Stack, { direction: "row", sx: { width: "100%" }, children: /* @__PURE__ */ jsx(
284
+ Controller,
285
+ {
286
+ name: "payment_currency",
287
+ control,
288
+ render: () => /* @__PURE__ */ jsx(
289
+ "section",
290
+ {
291
+ style: {
292
+ display: currencies.length > 1 ? "grid" : "block",
293
+ gridTemplateColumns: "50% 50%",
294
+ width: "100%"
295
+ },
296
+ children: currencies.map((x, i) => {
297
+ const selected = i === paymentCurrencyIndex;
298
+ return /* @__PURE__ */ jsx(
299
+ Card,
300
+ {
301
+ variant: "outlined",
302
+ onClick: () => handleCurrencyChange(i),
303
+ className: selected ? "cko-payment-card" : "cko-payment-card-unselect",
304
+ children: /* @__PURE__ */ jsxs(Stack, { direction: "row", alignItems: "center", children: [
305
+ /* @__PURE__ */ jsx(Avatar, { src: x.logo, alt: x.name, sx: { width: 30, height: 30, marginRight: "10px" } }),
306
+ /* @__PURE__ */ jsxs("div", { children: [
307
+ /* @__PURE__ */ jsx(Typography, { variant: "h5", component: "div", children: x.symbol }),
308
+ /* @__PURE__ */ jsx(Typography, { sx: { fontSize: 14 }, color: "text.secondary", gutterBottom: true, children: x.method.name })
309
+ ] })
310
+ ] })
311
+ },
312
+ x.id
313
+ );
314
+ })
315
+ }
316
+ )
317
+ }
318
+ ) }),
300
319
  state.stripePaying && state.stripeContext && /* @__PURE__ */ jsx(
301
320
  StripeCheckout,
302
321
  {
@@ -319,7 +338,6 @@ export default function PaymentForm({
319
338
  size: "large",
320
339
  onClick: onAction,
321
340
  fullWidth: true,
322
- loadingPosition: "end",
323
341
  disabled: state.submitting || state.paying || state.stripePaying,
324
342
  loading: state.submitting || state.paying,
325
343
  children: state.submitting || state.paying ? t("payment.checkout.processing") : buttonText
@@ -286,6 +286,52 @@ export const Root = styled(Box)`
286
286
  .cko-payment-methods {
287
287
  }
288
288
 
289
+ .cko-payment-card {
290
+ position: relative;
291
+ border: 2px solid #3773f2;
292
+ padding: 5px 10px;
293
+ margin: 5px 0;
294
+ cursor: pointer;
295
+ }
296
+
297
+ .cko-payment-card::before {
298
+ content: '';
299
+ position: absolute;
300
+ right: 0;
301
+ top: 0;
302
+ border: 12px solid #3773f2;
303
+ border-bottom-color: transparent;
304
+ border-left-color: transparent;
305
+ }
306
+
307
+ .cko-payment-card-unselect {
308
+ border: 2px solid #bbb;
309
+ padding: 5px 10px;
310
+ margin: 5px 0;
311
+ cursor: pointer;
312
+ }
313
+
314
+ .cko-payment-card:nth-child(odd) {
315
+ margin-right: 5px;
316
+ }
317
+
318
+ .cko-payment-card-unselect:nth-child(odd) {
319
+ margin-right: 5px;
320
+ }
321
+
322
+ .cko-payment-card::after {
323
+ content: '';
324
+ width: 6px;
325
+ height: 10px;
326
+ position: absolute;
327
+ right: 3px;
328
+ top: 0px;
329
+ border: 2px solid #fff;
330
+ border-top-color: transparent;
331
+ border-left-color: transparent;
332
+ transform: rotate(35deg);
333
+ }
334
+
289
335
  .cko-payment-submit {
290
336
  .MuiButtonBase-root {
291
337
  border-radius: 0;
@@ -45,6 +45,19 @@ const waitForCheckoutComplete = async sessionId => {
45
45
  });
46
46
  return result;
47
47
  };
48
+ const flatPaymentMethods = (methods = []) => {
49
+ const out = [];
50
+ methods.forEach(method => {
51
+ const currencies = method.paymentCurrencies || method.payment_currencies || [];
52
+ currencies.forEach(currency => {
53
+ out.push({
54
+ ...currency,
55
+ method
56
+ });
57
+ });
58
+ });
59
+ return out;
60
+ };
48
61
  PaymentForm.defaultProps = {};
49
62
  function PaymentForm({
50
63
  checkoutSession,
@@ -78,6 +91,8 @@ function PaymentForm({
78
91
  customer,
79
92
  stripePaying: false
80
93
  });
94
+ const currencies = flatPaymentMethods(paymentMethods);
95
+ const [paymentCurrencyIndex, setPaymentCurrencyIndex] = (0, _react.useState)(0);
81
96
  (0, _react.useEffect)(() => {
82
97
  if (session?.user) {
83
98
  const values = getValues();
@@ -92,15 +107,14 @@ function PaymentForm({
92
107
  }
93
108
  }
94
109
  }, [session?.user, getValues, setValue]);
110
+ (0, _react.useEffect)(() => {
111
+ setValue("payment_method", currencies[paymentCurrencyIndex].method.id);
112
+ setValue("payment_currency", currencies[paymentCurrencyIndex].id);
113
+ }, [paymentCurrencyIndex, currencies, setValue]);
95
114
  const paymentMethod = (0, _reactHookForm.useWatch)({
96
115
  control,
97
116
  name: "payment_method"
98
117
  });
99
- const paymentCurrency = (0, _reactHookForm.useWatch)({
100
- control,
101
- name: "payment_currency"
102
- });
103
- const paymentCurrencies = paymentMethods.find(x => x.id === paymentMethod)?.payment_currencies || [];
104
118
  const domSize = (0, _ahooks.useSize)(document.body);
105
119
  const isColumnLayout = (0, _ahooks.useCreation)(() => {
106
120
  if (domSize) {
@@ -115,12 +129,8 @@ function PaymentForm({
115
129
  action: t(`payment.checkout.${checkoutSession.mode}`)
116
130
  });
117
131
  const method = paymentMethods.find(x => x.id === paymentMethod);
118
- const handleMethodChange = e => {
119
- setValue("payment_method", e.target.value);
120
- const currencies = paymentMethods.find(x => x.id === e.target.value)?.payment_currencies || [];
121
- if (currencies.some(x => x.id === paymentCurrency) === false) {
122
- setValue("payment_currency", currencies[0]?.id);
123
- }
132
+ const handleCurrencyChange = index => {
133
+ setPaymentCurrencyIndex(index);
124
134
  };
125
135
  const handleConnected = async () => {
126
136
  try {
@@ -348,80 +358,56 @@ function PaymentForm({
348
358
  fontWeight: 600
349
359
  },
350
360
  children: t("payment.checkout.method")
351
- }), /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Stack, {
361
+ }), /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Stack, {
352
362
  direction: "row",
353
363
  sx: {
354
- width: 1
364
+ width: "100%"
355
365
  },
356
- spacing: 1,
357
- children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_reactHookForm.Controller, {
358
- name: "payment_method",
359
- control,
360
- render: ({
361
- field
362
- }) => /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Select, {
363
- ...field,
364
- onChange: handleMethodChange,
365
- sx: {
366
- flex: 1
367
- },
368
- size: "small",
369
- children: paymentMethods.map(x => {
370
- const selected = x.id === paymentMethod;
371
- return /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.MenuItem, {
372
- value: x.id,
373
- children: /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Stack, {
374
- direction: "row",
375
- spacing: 1,
376
- children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Avatar, {
377
- src: x.logo,
378
- alt: x.name,
379
- sx: {
380
- width: 20,
381
- height: 20
382
- }
383
- }), /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
384
- color: selected ? "text.primary" : "text.secondary",
385
- children: x.name
386
- })]
387
- })
388
- }, x.id);
389
- })
390
- })
391
- }), /* @__PURE__ */(0, _jsxRuntime.jsx)(_reactHookForm.Controller, {
366
+ children: /* @__PURE__ */(0, _jsxRuntime.jsx)(_reactHookForm.Controller, {
392
367
  name: "payment_currency",
393
368
  control,
394
- render: ({
395
- field
396
- }) => /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Select, {
397
- ...field,
398
- sx: {
399
- flex: 1
369
+ render: () => /* @__PURE__ */(0, _jsxRuntime.jsx)("section", {
370
+ style: {
371
+ display: currencies.length > 1 ? "grid" : "block",
372
+ gridTemplateColumns: "50% 50%",
373
+ width: "100%"
400
374
  },
401
- size: "small",
402
- children: paymentCurrencies.map(x => {
403
- const selected = x.id === paymentCurrency;
404
- return /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.MenuItem, {
405
- value: x.id,
375
+ children: currencies.map((x, i) => {
376
+ const selected = i === paymentCurrencyIndex;
377
+ return /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Card, {
378
+ variant: "outlined",
379
+ onClick: () => handleCurrencyChange(i),
380
+ className: selected ? "cko-payment-card" : "cko-payment-card-unselect",
406
381
  children: /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Stack, {
407
382
  direction: "row",
408
- spacing: 1,
383
+ alignItems: "center",
409
384
  children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Avatar, {
410
385
  src: x.logo,
411
386
  alt: x.name,
412
387
  sx: {
413
- width: 20,
414
- height: 20
388
+ width: 30,
389
+ height: 30,
390
+ marginRight: "10px"
415
391
  }
416
- }), /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
417
- color: selected ? "text.primary" : "text.secondary",
418
- children: x.symbol
392
+ }), /* @__PURE__ */(0, _jsxRuntime.jsxs)("div", {
393
+ children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
394
+ variant: "h5",
395
+ component: "div",
396
+ children: x.symbol
397
+ }), /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
398
+ sx: {
399
+ fontSize: 14
400
+ },
401
+ color: "text.secondary",
402
+ gutterBottom: true,
403
+ children: x.method.name
404
+ })]
419
405
  })]
420
406
  })
421
407
  }, x.id);
422
408
  })
423
409
  })
424
- })]
410
+ })
425
411
  }), state.stripePaying && state.stripeContext && /* @__PURE__ */(0, _jsxRuntime.jsx)(_stripe.default, {
426
412
  clientSecret: state.stripeContext.client_secret,
427
413
  intentType: state.stripeContext.intent_type,
@@ -442,7 +428,6 @@ function PaymentForm({
442
428
  size: "large",
443
429
  onClick: onAction,
444
430
  fullWidth: true,
445
- loadingPosition: "end",
446
431
  disabled: state.submitting || state.paying || state.stripePaying,
447
432
  loading: state.submitting || state.paying,
448
433
  children: state.submitting || state.paying ? t("payment.checkout.processing") : buttonText
@@ -344,6 +344,52 @@ const Root = exports.Root = (0, _system.styled)(_material.Box)`
344
344
  .cko-payment-methods {
345
345
  }
346
346
 
347
+ .cko-payment-card {
348
+ position: relative;
349
+ border: 2px solid #3773f2;
350
+ padding: 5px 10px;
351
+ margin: 5px 0;
352
+ cursor: pointer;
353
+ }
354
+
355
+ .cko-payment-card::before {
356
+ content: '';
357
+ position: absolute;
358
+ right: 0;
359
+ top: 0;
360
+ border: 12px solid #3773f2;
361
+ border-bottom-color: transparent;
362
+ border-left-color: transparent;
363
+ }
364
+
365
+ .cko-payment-card-unselect {
366
+ border: 2px solid #bbb;
367
+ padding: 5px 10px;
368
+ margin: 5px 0;
369
+ cursor: pointer;
370
+ }
371
+
372
+ .cko-payment-card:nth-child(odd) {
373
+ margin-right: 5px;
374
+ }
375
+
376
+ .cko-payment-card-unselect:nth-child(odd) {
377
+ margin-right: 5px;
378
+ }
379
+
380
+ .cko-payment-card::after {
381
+ content: '';
382
+ width: 6px;
383
+ height: 10px;
384
+ position: absolute;
385
+ right: 3px;
386
+ top: 0px;
387
+ border: 2px solid #fff;
388
+ border-top-color: transparent;
389
+ border-left-color: transparent;
390
+ transform: rotate(35deg);
391
+ }
392
+
347
393
  .cko-payment-submit {
348
394
  .MuiButtonBase-root {
349
395
  border-radius: 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blocklet/payment-react",
3
- "version": "1.13.121",
3
+ "version": "1.13.122",
4
4
  "description": "Reusable react components for payment kit v2",
5
5
  "keywords": [
6
6
  "react",
@@ -89,7 +89,7 @@
89
89
  "@babel/core": "^7.19.3",
90
90
  "@babel/preset-env": "^7.19.3",
91
91
  "@babel/preset-react": "^7.18.6",
92
- "@blocklet/payment-types": "1.13.121",
92
+ "@blocklet/payment-types": "1.13.122",
93
93
  "@storybook/addon-essentials": "^7.6.10",
94
94
  "@storybook/addon-interactions": "^7.6.10",
95
95
  "@storybook/addon-links": "^7.6.10",
@@ -118,5 +118,5 @@
118
118
  "vite-plugin-babel": "^1.2.0",
119
119
  "vite-plugin-node-polyfills": "^0.19.0"
120
120
  },
121
- "gitHead": "c02f337493cf7dac11324447534cf8684b763064"
121
+ "gitHead": "015a834e0971d7d0d9947953daeb96a240248c2f"
122
122
  }
@@ -3,13 +3,13 @@ import 'react-international-phone/style.css';
3
3
  import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
4
4
  import { useTheme } from '@arcblock/ux/lib/Theme';
5
5
  import Toast from '@arcblock/ux/lib/Toast';
6
- import type { TCustomer, TPaymentIntent, TPaymentMethodExpanded } from '@blocklet/payment-types';
6
+ import type { PaymentCurrency, TCustomer, TPaymentIntent, TPaymentMethodExpanded } from '@blocklet/payment-types';
7
7
  import { LoadingButton } from '@mui/lab';
8
- import { Avatar, Fade, InputAdornment, MenuItem, Select, Stack, Typography } from '@mui/material';
8
+ import { Avatar, Card, Fade, InputAdornment, Stack, Typography } from '@mui/material';
9
9
  import { useCreation, useSetState, useSize } from 'ahooks';
10
10
  import { PhoneNumberUtil } from 'google-libphonenumber';
11
11
  import pWaitFor from 'p-wait-for';
12
- import { useEffect } from 'react';
12
+ import { useEffect, useState } from 'react';
13
13
  import { Controller, useFormContext, useWatch } from 'react-hook-form';
14
14
  import { dispatch } from 'use-bus';
15
15
  import isEmail from 'validator/es/lib/isEmail';
@@ -56,6 +56,22 @@ const waitForCheckoutComplete = async (sessionId: string) => {
56
56
 
57
57
  type PageData = CheckoutContext & CheckoutCallbacks;
58
58
 
59
+ const flatPaymentMethods = (methods: TPaymentMethodExpanded[] = []) => {
60
+ const out: PaymentCurrency[] = [];
61
+
62
+ methods.forEach((method: any) => {
63
+ const currencies = method.paymentCurrencies || method.payment_currencies || [];
64
+ currencies.forEach((currency: any) => {
65
+ out.push({
66
+ ...currency,
67
+ method,
68
+ });
69
+ });
70
+ });
71
+
72
+ return out;
73
+ };
74
+
59
75
  PaymentForm.defaultProps = {};
60
76
 
61
77
  // FIXME: https://stripe.com/docs/elements/address-element
@@ -98,6 +114,9 @@ export default function PaymentForm({
98
114
  stripePaying: false,
99
115
  });
100
116
 
117
+ const currencies = flatPaymentMethods(paymentMethods);
118
+ const [paymentCurrencyIndex, setPaymentCurrencyIndex] = useState(0);
119
+
101
120
  useEffect(() => {
102
121
  if (session?.user) {
103
122
  const values = getValues();
@@ -113,9 +132,12 @@ export default function PaymentForm({
113
132
  }
114
133
  }, [session?.user, getValues, setValue]);
115
134
 
135
+ useEffect(() => {
136
+ setValue('payment_method', (currencies[paymentCurrencyIndex] as any).method.id);
137
+ setValue('payment_currency', currencies[paymentCurrencyIndex].id);
138
+ }, [paymentCurrencyIndex, currencies, setValue]);
139
+
116
140
  const paymentMethod = useWatch({ control, name: 'payment_method' });
117
- const paymentCurrency = useWatch({ control, name: 'payment_currency' });
118
- const paymentCurrencies = paymentMethods.find((x) => x.id === paymentMethod)?.payment_currencies || [];
119
141
 
120
142
  const domSize = useSize(document.body);
121
143
 
@@ -135,12 +157,8 @@ export default function PaymentForm({
135
157
 
136
158
  const method = paymentMethods.find((x) => x.id === paymentMethod) as TPaymentMethodExpanded;
137
159
 
138
- const handleMethodChange = (e: any) => {
139
- setValue('payment_method', e.target.value);
140
- const currencies = paymentMethods.find((x) => x.id === e.target.value)?.payment_currencies || [];
141
- if (currencies.some((x) => x.id === paymentCurrency) === false) {
142
- setValue('payment_currency', currencies[0]?.id);
143
- }
160
+ const handleCurrencyChange = (index: number) => {
161
+ setPaymentCurrencyIndex(index);
144
162
  };
145
163
 
146
164
  const handleConnected = async () => {
@@ -322,43 +340,40 @@ export default function PaymentForm({
322
340
  <Fade in>
323
341
  <Stack direction="column" alignItems="flex-start" className="cko-payment-methods">
324
342
  <Typography sx={{ mb: 2, color: 'text.primary', fontWeight: 600 }}>{t('payment.checkout.method')}</Typography>
325
- <Stack direction="row" sx={{ width: 1 }} spacing={1}>
326
- <Controller
327
- name="payment_method"
328
- control={control}
329
- render={({ field }) => (
330
- <Select {...field} onChange={handleMethodChange} sx={{ flex: 1 }} size="small">
331
- {paymentMethods.map((x) => {
332
- const selected = x.id === paymentMethod;
333
- return (
334
- <MenuItem key={x.id} value={x.id}>
335
- <Stack direction="row" spacing={1}>
336
- <Avatar src={x.logo} alt={x.name} sx={{ width: 20, height: 20 }} />
337
- <Typography color={selected ? 'text.primary' : 'text.secondary'}>{x.name}</Typography>
338
- </Stack>
339
- </MenuItem>
340
- );
341
- })}
342
- </Select>
343
- )}
344
- />
343
+ <Stack direction="row" sx={{ width: '100%' }}>
345
344
  <Controller
346
345
  name="payment_currency"
347
346
  control={control}
348
- render={({ field }) => (
349
- <Select {...field} sx={{ flex: 1 }} size="small">
350
- {paymentCurrencies.map((x) => {
351
- const selected = x.id === paymentCurrency;
347
+ render={() => (
348
+ <section
349
+ style={{
350
+ display: currencies.length > 1 ? 'grid' : 'block',
351
+ gridTemplateColumns: '50% 50%',
352
+ width: '100%',
353
+ }}>
354
+ {currencies.map((x, i) => {
355
+ const selected = i === paymentCurrencyIndex;
352
356
  return (
353
- <MenuItem key={x.id} value={x.id}>
354
- <Stack direction="row" spacing={1}>
355
- <Avatar src={x.logo} alt={x.name} sx={{ width: 20, height: 20 }} />
356
- <Typography color={selected ? 'text.primary' : 'text.secondary'}>{x.symbol}</Typography>
357
+ <Card
358
+ key={x.id}
359
+ variant="outlined"
360
+ onClick={() => handleCurrencyChange(i)}
361
+ className={selected ? 'cko-payment-card' : 'cko-payment-card-unselect'}>
362
+ <Stack direction="row" alignItems="center">
363
+ <Avatar src={x.logo} alt={x.name} sx={{ width: 30, height: 30, marginRight: '10px' }} />
364
+ <div>
365
+ <Typography variant="h5" component="div">
366
+ {x.symbol}
367
+ </Typography>
368
+ <Typography sx={{ fontSize: 14 }} color="text.secondary" gutterBottom>
369
+ {(x as any).method.name}
370
+ </Typography>
371
+ </div>
357
372
  </Stack>
358
- </MenuItem>
373
+ </Card>
359
374
  );
360
375
  })}
361
- </Select>
376
+ </section>
362
377
  )}
363
378
  />
364
379
  </Stack>
@@ -383,7 +398,6 @@ export default function PaymentForm({
383
398
  size="large"
384
399
  onClick={onAction}
385
400
  fullWidth
386
- loadingPosition="end"
387
401
  disabled={state.submitting || state.paying || state.stripePaying}
388
402
  loading={state.submitting || state.paying}>
389
403
  {state.submitting || state.paying ? t('payment.checkout.processing') : buttonText}
@@ -331,6 +331,52 @@ export const Root = styled(Box)<{ mode: LiteralUnion<'standalone' | 'inline' | '
331
331
  .cko-payment-methods {
332
332
  }
333
333
 
334
+ .cko-payment-card {
335
+ position: relative;
336
+ border: 2px solid #3773f2;
337
+ padding: 5px 10px;
338
+ margin: 5px 0;
339
+ cursor: pointer;
340
+ }
341
+
342
+ .cko-payment-card::before {
343
+ content: '';
344
+ position: absolute;
345
+ right: 0;
346
+ top: 0;
347
+ border: 12px solid #3773f2;
348
+ border-bottom-color: transparent;
349
+ border-left-color: transparent;
350
+ }
351
+
352
+ .cko-payment-card-unselect {
353
+ border: 2px solid #bbb;
354
+ padding: 5px 10px;
355
+ margin: 5px 0;
356
+ cursor: pointer;
357
+ }
358
+
359
+ .cko-payment-card:nth-child(odd) {
360
+ margin-right: 5px;
361
+ }
362
+
363
+ .cko-payment-card-unselect:nth-child(odd) {
364
+ margin-right: 5px;
365
+ }
366
+
367
+ .cko-payment-card::after {
368
+ content: '';
369
+ width: 6px;
370
+ height: 10px;
371
+ position: absolute;
372
+ right: 3px;
373
+ top: 0px;
374
+ border: 2px solid #fff;
375
+ border-top-color: transparent;
376
+ border-left-color: transparent;
377
+ transform: rotate(35deg);
378
+ }
379
+
334
380
  .cko-payment-submit {
335
381
  .MuiButtonBase-root {
336
382
  border-radius: 0;