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
JavaScript
"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