@exodus/react-native-payments
Version:
[](http://facebook.github.io/react-native/releases/0.40) [](https://www.np
162 lines (161 loc) • 6.53 kB
JavaScript
import { DOMException, ConstructorError } from '../errors';
function isNumber(value) {
return typeof value === 'number';
}
function isString(value) {
return typeof value === 'string';
}
const CAPABILITIES = new Set(['emv', 'debit', 'credit']);
export function transformMerchantCapabilities(merchantCapabilities) {
if (!merchantCapabilities)
return undefined;
if (Array.isArray(merchantCapabilities)) {
merchantCapabilities = merchantCapabilities.reduce((acc, capability) => {
acc[capability] = true;
return acc;
}, Object.create(null));
}
for (const key in merchantCapabilities) {
if (!CAPABILITIES.has(key)) {
throw new TypeError(`Invalid merchant capability: ${key}`);
}
}
return merchantCapabilities;
}
export function isValidDecimalMonetaryValue(amountValue) {
if (!isNumber(amountValue) && !isString(amountValue)) {
return false;
}
return isNumber(amountValue) || isValidStringAmount(amountValue);
}
export function isNegative(amountValue) {
return isNumber(amountValue) ? amountValue < 0 : amountValue.startsWith('-');
}
export function isValidStringAmount(stringAmount) {
if (typeof stringAmount !== 'string')
throw new TypeError('Expected a string');
return /^[-+]?(?:\.[0-9]+|[0-9]+(?:\.[0-9]+)?)$/.test(stringAmount);
}
export function toString(amountValue) {
return isNumber(amountValue) ? amountValue.toString() : amountValue;
}
export function convertObjectAmountToString(objectWithAmount) {
return Object.assign(Object.create(null), objectWithAmount, {
amount: Object.assign(Object.create(null), {
value: toString(objectWithAmount.amount.value),
currency: objectWithAmount.amount.currency
})
});
}
export function convertDetailAmountsToString(details) {
const nextDetails = Object.keys(details).reduce((acc, key) => {
if (key === 'total') {
return Object.assign(Object.create(null), acc, {
[key]: convertObjectAmountToString(details[key])
});
}
if (Array.isArray(details[key]) &&
(key === 'displayItems' || key === 'shippingOptions')) {
return Object.assign(Object.create(null), acc, {
[key]: details[key].map(paymentItemOrShippingOption => convertObjectAmountToString(paymentItemOrShippingOption))
});
}
return acc;
}, Object.create(null));
return nextDetails;
}
export function getPlatformMethodData(methodData, platformOS) {
const platformSupportedMethod = platformOS === 'ios' ? 'apple-pay' : 'android-pay';
const platformMethod = methodData.find(paymentMethodData => paymentMethodData.supportedMethods.includes(platformSupportedMethod));
if (!platformMethod) {
throw new DOMException('NotSupportedError');
}
return platformMethod.data;
}
export function validateTotal(total, errorType = Error) {
if (total === undefined) {
throw new errorType(`required member total is undefined.`);
}
const hasTotal = total && total.amount && total.amount.value;
if (!hasTotal) {
throw new errorType(`Missing required member(s): amount, label.`);
}
const totalAmountValue = total.amount.value;
if (!isValidDecimalMonetaryValue(totalAmountValue)) {
throw new errorType(`'${totalAmountValue}' is not a valid amount format for total`);
}
if (isNegative(totalAmountValue)) {
throw new errorType(`Total amount value should be non-negative`);
}
}
export function validatePaymentMethods(methodData) {
if (methodData.length < 1) {
throw new ConstructorError(`At least one payment method is required`);
}
methodData.forEach(paymentMethod => {
if (paymentMethod.supportedMethods === undefined) {
throw new ConstructorError(`required member supportedMethods is undefined.`);
}
if (!Array.isArray(paymentMethod.supportedMethods)) {
throw new ConstructorError(`required member supportedMethods is not iterable.`);
}
if (paymentMethod.supportedMethods.length < 1) {
throw new ConstructorError(`Each payment method needs to include at least one payment method identifier`);
}
});
}
export function validateDisplayItems(displayItems, errorType = Error) {
if (displayItems) {
displayItems.forEach((item) => {
const amountValue = item && item.amount && item.amount.value;
if (!amountValue) {
throw new errorType(`required member value is undefined.`);
}
if (!isValidDecimalMonetaryValue(amountValue)) {
throw new errorType(`'${amountValue}' is not a valid amount format for display items`);
}
});
}
}
export function validateShippingOptions(details, errorType = Error) {
if (details.shippingOptions === undefined) {
return undefined;
}
if (!Array.isArray(details.shippingOptions)) {
throw new errorType(`Iterator getter is not callable.`);
}
if (details.shippingOptions) {
let seenIDs = [];
details.shippingOptions.forEach((shippingOption) => {
if (shippingOption.id === undefined) {
throw new errorType(`required member id is undefined.`);
}
if (shippingOption.id === null) {
shippingOption.id = 'null';
}
const amountValue = shippingOption.amount.value;
if (!isValidDecimalMonetaryValue(amountValue)) {
throw new errorType(`'${amountValue}' is not a valid amount format for shippingOptions`);
}
if (seenIDs.includes(shippingOption.id)) {
details.shippingOptions = [];
console.warn(`[ReactNativePayments] Duplicate shipping option identifier '${shippingOption.id}' is treated as an invalid address indicator.`);
return undefined;
}
seenIDs.push(shippingOption.id);
});
}
}
export function getSelectedShippingOption(shippingOptions) {
if (!Array.isArray(shippingOptions)) {
return null;
}
if (shippingOptions.length === 0) {
return null;
}
const selectedShippingOption = shippingOptions.find(shippingOption => shippingOption.selected);
if (selectedShippingOption) {
return selectedShippingOption.id;
}
return shippingOptions[0]?.id ?? null;
}