1 import { c } from 'ttag';
3 import { type IconName } from '@proton/components/components/icon/Icon';
4 import useApi from '@proton/components/hooks/useApi';
5 import useAuthentication from '@proton/components/hooks/useAuthentication';
6 import type { AvailablePaymentMethod, PaymentMethodFlows, SavedPaymentMethod } from '@proton/payments';
7 import { PAYMENT_METHOD_TYPES, type PaymentMethodSepa, isSignupFlow } from '@proton/payments';
9 import type { MethodsHook, Props } from '../react-extensions/useMethods';
10 import { useMethods as _useMethods } from '../react-extensions/useMethods';
12 export interface ViewPaymentMethod extends AvailablePaymentMethod {
13 readonly icon: IconName | undefined;
14 readonly text: string;
17 interface ClientMethodsHook extends MethodsHook {
18 usedMethods: ViewPaymentMethod[];
19 newMethods: ViewPaymentMethod[];
20 allMethods: ViewPaymentMethod[];
21 lastUsedMethod: ViewPaymentMethod | undefined;
24 const getIcon = (paymentMethod: SavedPaymentMethod): IconName | undefined => {
26 paymentMethod.Type === PAYMENT_METHOD_TYPES.PAYPAL ||
27 paymentMethod.Type === PAYMENT_METHOD_TYPES.CHARGEBEE_PAYPAL
29 return 'brand-paypal';
33 paymentMethod.Type === PAYMENT_METHOD_TYPES.CARD ||
34 paymentMethod.Type === PAYMENT_METHOD_TYPES.CHARGEBEE_CARD
36 switch (paymentMethod.Details.Brand.toLowerCase()) {
37 case 'american express':
42 return 'brand-mastercard';
44 return 'brand-discover';
50 if (paymentMethod.Type === PAYMENT_METHOD_TYPES.CHARGEBEE_SEPA_DIRECT_DEBIT) {
55 export function formattedSavedSepaDetails(method: PaymentMethodSepa): string {
56 const NBSP_HTML = '\u00A0';
58 Details: { Country, Last4 },
61 return c('Info').t`IBAN${NBSP_HTML}${Country}${NBSP_HTML}••••${NBSP_HTML}${Last4}`;
64 const getMethod = (paymentMethod: SavedPaymentMethod): string => {
65 switch (paymentMethod.Type) {
66 case PAYMENT_METHOD_TYPES.CARD:
67 case PAYMENT_METHOD_TYPES.CHARGEBEE_CARD:
68 const brand = paymentMethod.Details.Brand;
69 const last4 = paymentMethod.Details.Last4;
70 // translator: example would be: "Mastercard" ending in "7777"
71 return c('new_plans: info').t`${brand} ending in ${last4}`;
72 case PAYMENT_METHOD_TYPES.PAYPAL:
73 case PAYMENT_METHOD_TYPES.CHARGEBEE_PAYPAL:
74 return `PayPal - ${paymentMethod.Details.PayerID}`;
75 case PAYMENT_METHOD_TYPES.CHARGEBEE_SEPA_DIRECT_DEBIT:
76 return `Bank transfer - ${formattedSavedSepaDetails(paymentMethod)}`;
83 * Transform the payment method object from the react-extensions package to a view model that can be used in the UI.
85 export function convertMethod(
86 method: AvailablePaymentMethod,
87 getSavedMethodById: MethodsHook['getSavedMethodByID'],
88 flow: PaymentMethodFlows
89 ): ViewPaymentMethod {
90 if (method.paymentMethodId) {
91 const savedMethod = getSavedMethodById(method.paymentMethodId) as SavedPaymentMethod;
93 icon: getIcon(savedMethod),
94 text: [getMethod(savedMethod), method.isExpired && `(${c('Info').t`Expired`})`].filter(Boolean).join(' '),
99 if (method.type === PAYMENT_METHOD_TYPES.PAYPAL || method.type === PAYMENT_METHOD_TYPES.PAYPAL_CREDIT) {
101 icon: 'brand-paypal' as const,
102 text: c('Payment method option').t`PayPal`,
105 } else if (method.type === PAYMENT_METHOD_TYPES.BITCOIN || method.type === PAYMENT_METHOD_TYPES.CHARGEBEE_BITCOIN) {
107 icon: 'brand-bitcoin' as const,
108 text: c('Payment method option').t`Bitcoin`,
111 } else if (method.type === PAYMENT_METHOD_TYPES.CASH) {
113 icon: 'money-bills' as const,
114 text: c('Label').t`Cash`,
117 } else if (method.type === PAYMENT_METHOD_TYPES.CHARGEBEE_CARD) {
119 icon: 'credit-card' as const,
120 text: c('Payment method option').t`Credit/debit card`,
123 } else if (method.type === PAYMENT_METHOD_TYPES.CHARGEBEE_PAYPAL) {
125 icon: 'brand-paypal' as const,
126 text: c('Payment method option').t`PayPal`,
129 } else if (method.type === PAYMENT_METHOD_TYPES.CHARGEBEE_SEPA_DIRECT_DEBIT) {
131 icon: 'bank' as const,
132 text: c('Payment method option').t`Bank transfer`,
138 icon: 'credit-card' as const,
139 text: isSignupFlow(flow)
140 ? c('Payment method option').t`Credit/debit card`
141 : c('Payment method option').t`New credit/debit card`,
147 * Enhance the methods hook with client specific data like icons and text.
148 * @param methodsHook - output of the useMethods hook from the react-extensions package
149 * @param flow – current payment flow. Might modify the text of the payment methods
152 export const wrapMethods = (methodsHook: MethodsHook, flow: PaymentMethodFlows): ClientMethodsHook => {
153 const { getSavedMethodByID, usedMethods, newMethods, allMethods, lastUsedMethod } = methodsHook;
157 usedMethods: usedMethods.map((method) => convertMethod(method, getSavedMethodByID, flow)),
158 newMethods: newMethods.map((method) => convertMethod(method, getSavedMethodByID, flow)),
159 allMethods: allMethods.map((method) => convertMethod(method, getSavedMethodByID, flow)),
160 lastUsedMethod: lastUsedMethod && convertMethod(lastUsedMethod, getSavedMethodByID, flow),
165 * A preconfigured version of the useMethods hook from the react-extensions package.
166 * Returns view models of methods that can be used in the UI.
168 export const useMethods = (props: Props): ClientMethodsHook => {
169 const api = useApi();
170 const { UID } = useAuthentication();
171 const isAuthenticated = !!UID;
173 const internalResult = _useMethods(props, { api, isAuthenticated });
175 return wrapMethods(internalResult, props.flow);