UNPKG

react-native-moyasar-sdk

Version:

Official React Native Moyasar SDK - Integrate Credit Cards, Apple Pay, Samsung Pay, and STC Pay with simple interfaces for a seamless payment experience in your React Native app

330 lines (327 loc) 13.3 kB
"use strict"; import { View, TextInput, StyleSheet, TouchableOpacity, Text, ScrollView, KeyboardAvoidingView, Platform, ActivityIndicator, useColorScheme } from 'react-native'; import { getConfiguredLocalizations, isArabicLang } from "../localizations/i18n.js"; import { useEffect, useState } from 'react'; import { formatAmount, toMajor } from "../helpers/currency_util.js"; import { CreditCardPaymentService } from "../services/credit_card_payment_service.js"; import { WebviewPaymentAuth } from "./webview_payment_auth.js"; import { Visa } from "../assets/visa.js"; import { Mastercard } from "../assets/mastercard.js"; import { Amex } from "../assets/amex.js"; import { Mada } from "../assets/mada.js"; import { CreditCardNetwork } from "../models/credit_card_network.js"; import { formatCreditCardNumber, formatExpiryDate } from "../helpers/formatters.js"; import { getCreditCardNetworkFromNumber } from "../helpers/credit_card_utils.js"; import { mapArabicNumbers } from "../helpers/arabic_numbers_mapper.js"; import { debugLog } from "../helpers/debug_log.js"; import { SaudiRiyal } from "../assets/saudi_riyal.js"; // TODO: Modify to a better approach rather than global variable import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; const paymentService = new CreditCardPaymentService(); let formattedAmount; function getFormattedAmount(amount, currency) { if (!formattedAmount) { return formattedAmount = formatAmount(amount, currency); } return formattedAmount; } export function CreditCard({ paymentConfig, onPaymentResult, style: customStyle }) { const [isWebviewVisible, setWebviewVisible] = useState(false); useEffect(() => { debugLog('Moyasar SDK: CreditCard view mounted'); return () => { debugLog('Moyasar SDK: CreditCard view unmounted'); formattedAmount = null; }; }, []); return isWebviewVisible ? /*#__PURE__*/_jsx(WebviewPaymentAuth, { transactionUrl: (paymentService.payment?.source).transactionUrl, onWebviewPaymentAuthResult: webviewPaymentResponse => { if (paymentService.payment) { paymentService.payment.status = webviewPaymentResponse.status; paymentService.payment.source.message = webviewPaymentResponse.message; onPaymentResult(paymentService.payment); } }, style: customStyle }) : /*#__PURE__*/_jsx(CreditCardView, { paymentConfig: paymentConfig, onPaymentResult: onPaymentResult, style: customStyle, setWebviewVisible: setWebviewVisible }); } // TODO: Extract to a separate file const CreditCardView = ({ paymentConfig, onPaymentResult, style: customStyle, setWebviewVisible }) => { const { t } = getConfiguredLocalizations(); const isLightMode = useColorScheme() === 'light'; const [name, setName] = useState(''); const [number, setNumber] = useState(''); const [expiry, setExpiry] = useState(''); const [cvc, setCvc] = useState(''); const [nameError, setNameError] = useState(null); const [numberError, setNumberError] = useState(null); const [expiryError, setExpiryError] = useState(null); const [cvcError, setCvcError] = useState(null); const [isButtonDisabled, setIsButtonDisabled] = useState(true); const [isPaymentInProgress, setIsPaymentInProgress] = useState(false); useEffect(() => { setIsButtonDisabled(!paymentService.validateAllFields({ name, number, expiry, cvc }) || isPaymentInProgress); }, [name, number, expiry, cvc, isPaymentInProgress]); return /*#__PURE__*/_jsx(KeyboardAvoidingView, { behavior: Platform.OS === 'ios' ? 'padding' : 'height', children: /*#__PURE__*/_jsx(ScrollView, { contentContainerStyle: defaultStyle.scrollView, children: /*#__PURE__*/_jsxs(View, { style: [defaultStyle.container, customStyle?.container], children: [/*#__PURE__*/_jsxs(View, { style: defaultStyle.inputContainer, children: [/*#__PURE__*/_jsx(View, { style: defaultStyle.inputSubContainer, children: /*#__PURE__*/_jsx(TextInput, { style: [{ ...defaultStyle.input, color: isLightMode ? 'black' : 'white' }, customStyle?.textInputs], value: name, onChangeText: value => { setName(value); setNameError(paymentService.nameValidator.visualValidate(value)); }, placeholder: t('moyasarTranslation:nameOnCard'), placeholderTextColor: customStyle?.textInputsPlaceholderColor, autoCorrect: false, editable: !isPaymentInProgress }) }), /*#__PURE__*/_jsx(Text, { style: [defaultStyle.errorText, customStyle?.errorText], children: nameError }), /*#__PURE__*/_jsxs(View, { style: defaultStyle.inputSubContainer, children: [/*#__PURE__*/_jsx(TextInput, { style: [{ ...defaultStyle.input, color: isLightMode ? 'black' : 'white' }, customStyle?.textInputs], value: formatCreditCardNumber(number), onChangeText: value => { const cleanNumber = value.replace(/\s/g, '').replace(/[^\d٠-٩]/gi, '').slice(0, 16); const mappedCleanNumbers = mapArabicNumbers(cleanNumber); setNumber(mappedCleanNumbers); setNumberError(paymentService.numberValidator.visualValidate(mappedCleanNumbers)); // To better handle Amex card validation setCvcError(paymentService.cvcValidator.visualValidate(cvc, mappedCleanNumbers)); }, placeholder: t('moyasarTranslation:cardNumber'), placeholderTextColor: customStyle?.textInputsPlaceholderColor, keyboardType: "numeric", editable: !isPaymentInProgress, maxLength: getCreditCardNetworkFromNumber(number) === CreditCardNetwork.amex ? 17 : 19 }), /*#__PURE__*/_jsxs(View, { style: defaultStyle.cardNetworkLogoContainer, children: [paymentService.shouldShowNetworkLogo(number, CreditCardNetwork.mada) ? /*#__PURE__*/_jsx(Mada, { style: defaultStyle.cardNetworkLogo }) : null, paymentService.shouldShowNetworkLogo(number, CreditCardNetwork.visa) ? /*#__PURE__*/_jsx(Visa, { style: defaultStyle.cardNetworkLogo }) : null, paymentService.shouldShowNetworkLogo(number, CreditCardNetwork.master) ? /*#__PURE__*/_jsx(Mastercard, { style: defaultStyle.cardNetworkLogo }) : null, paymentService.shouldShowNetworkLogo(number, CreditCardNetwork.amex) ? /*#__PURE__*/_jsx(Amex, { style: defaultStyle.cardNetworkLogo }) : null] })] }), /*#__PURE__*/_jsx(Text, { style: [defaultStyle.errorText, customStyle?.errorText], children: numberError }), /*#__PURE__*/_jsx(View, { style: defaultStyle.inputSubContainer, children: /*#__PURE__*/_jsx(TextInput, { style: [{ ...defaultStyle.input, color: isLightMode ? 'black' : 'white' }, customStyle?.textInputs], value: formatExpiryDate(expiry), onChangeText: value => { const cleanExpiryDate = value.replace(/[\s\/]/g, '').replace(/[^\d٠-٩]/gi, '').slice(0, 6); const mappedCleanExpiryDate = mapArabicNumbers(cleanExpiryDate); setExpiry(mappedCleanExpiryDate); setExpiryError(paymentService.expiryValidator.visualValidate(mappedCleanExpiryDate)); }, placeholder: t('moyasarTranslation:expiry'), placeholderTextColor: customStyle?.textInputsPlaceholderColor, keyboardType: "numeric", editable: !isPaymentInProgress, maxLength: 9 }) }), /*#__PURE__*/_jsx(Text, { style: [defaultStyle.errorText, customStyle?.errorText], children: expiryError }), /*#__PURE__*/_jsx(View, { style: defaultStyle.inputSubContainer, children: /*#__PURE__*/_jsx(TextInput, { style: [{ ...defaultStyle.input, color: isLightMode ? 'black' : 'white' }, customStyle?.textInputs], value: cvc, onChangeText: value => { const cleanCvc = value.replace(/\s/g, '').replace(/[^\d٠-٩]/gi, ''); const mappedCleanCvc = mapArabicNumbers(cleanCvc); setCvc(mappedCleanCvc); setCvcError(paymentService.cvcValidator.visualValidate(mappedCleanCvc, number)); }, placeholder: t('moyasarTranslation:cvc'), placeholderTextColor: customStyle?.textInputsPlaceholderColor, keyboardType: "numeric", maxLength: (() => { const cardNetwork = getCreditCardNetworkFromNumber(number); return cardNetwork === CreditCardNetwork.amex || cardNetwork === CreditCardNetwork.unknown ? 4 : 3; })(), editable: !isPaymentInProgress }) }), /*#__PURE__*/_jsx(Text, { style: [defaultStyle.errorText, customStyle?.errorText], children: cvcError })] }), /*#__PURE__*/_jsx(View, { style: defaultStyle.buttonContainer, children: /*#__PURE__*/_jsx(TouchableOpacity, { style: [defaultStyle.button, customStyle?.paymentButton, isButtonDisabled && { opacity: 0.5 }], onPress: async () => { setIsPaymentInProgress(true); const showAuthWebview = await paymentService.beginTransaction(paymentConfig, { name, number, expiry, cvc }, onPaymentResult); setIsPaymentInProgress(false); setWebviewVisible(showAuthWebview); }, disabled: isButtonDisabled, children: isPaymentInProgress ? /*#__PURE__*/_jsx(ActivityIndicator, { size: "small", color: customStyle?.activityIndicatorColor ?? '#ffffff' }) : paymentConfig.currency === 'SAR' ? /*#__PURE__*/ // TODO: Remove this temp solution when the new symbol is supported by RN dependencies _jsxs(View, { style: [defaultStyle.inputSubContainer, { alignItems: 'center' }], children: [/*#__PURE__*/_jsx(Text, { style: [defaultStyle.buttonText, { marginEnd: 5 }, customStyle?.paymentButtonText], children: `${t('moyasarTranslation:pay')}` }), /*#__PURE__*/_jsxs(View, { style: [defaultStyle.inputSubContainer, { alignItems: 'center', direction: 'ltr' }], children: [/*#__PURE__*/_jsx(SaudiRiyal, { height: "16", width: "16", style: customStyle?.paymentButtonText }), /*#__PURE__*/_jsx(Text, { style: [defaultStyle.buttonText, { marginStart: 4 }, customStyle?.paymentButtonText], children: `${toMajor(paymentConfig.amount, 'SAR')}` })] })] }) : /*#__PURE__*/_jsxs(Text, { style: [defaultStyle.buttonText, customStyle?.paymentButtonText], children: [t('moyasarTranslation:pay'), ' ', getFormattedAmount(paymentConfig.amount, paymentConfig.currency)] }) }) })] }) }) }); }; const defaultStyle = StyleSheet.create({ scrollView: { maxWidth: '100%', flexGrow: 1, justifyContent: 'space-between', flexDirection: 'column' }, container: { flex: 1, padding: 25 }, inputContainer: { justifyContent: 'flex-start' }, inputSubContainer: { flexDirection: 'row', justifyContent: 'center', direction: isArabicLang() ? 'rtl' : 'ltr' }, buttonContainer: { flex: 1, justifyContent: 'flex-end' }, input: { width: '100%', fontSize: 18, textAlign: isArabicLang() ? 'right' : 'left', borderWidth: 1.25, borderColor: '#DCDCDC', borderRadius: 7, margin: 8, padding: 10 }, button: { minWidth: '100%', justifyContent: 'center', backgroundColor: '#235CE1', borderRadius: 9, marginTop: 30, padding: 10, height: 50 }, buttonText: { color: 'white', fontSize: 20, fontWeight: '500', textAlign: 'center' }, errorText: { color: 'red', fontSize: 12, textAlign: 'left', direction: isArabicLang() ? 'rtl' : 'ltr' }, cardNetworkLogoContainer: { flexDirection: 'row', position: 'absolute', alignSelf: 'center', end: 10, justifyContent: 'flex-end' }, cardNetworkLogo: { marginEnd: 8, height: 37, width: 37 } }); //# sourceMappingURL=credit_card.js.map