@exodus/react-native-payments
Version:
[](http://facebook.github.io/react-native/releases/0.40) [](https://www.np
201 lines (200 loc) • 8.37 kB
JavaScript
import { DeviceEventEmitter, Platform } from 'react-native';
import { randomUUID } from '@exodus/crypto/randomUUID';
import NativePayments from './NativePayments';
import PaymentResponse from './PaymentResponse';
import PaymentRequestUpdateEvent from './PaymentRequestUpdateEvent';
import { ConstructorError } from './errors';
import { convertDetailAmountsToString, getPlatformMethodData, validateTotal, validatePaymentMethods, validateDisplayItems, validateShippingOptions, getSelectedShippingOption, transformMerchantCapabilities } from './helpers';
import { SHIPPING_ADDRESS_CHANGE_EVENT, SHIPPING_OPTION_CHANGE_EVENT, INTERNAL_SHIPPING_ADDRESS_CHANGE_EVENT, INTERNAL_SHIPPING_OPTION_CHANGE_EVENT, USER_DISMISS_EVENT, USER_ACCEPT_EVENT } from './constants';
const noop = () => { };
const IS_ANDROID = Platform.OS === 'android';
const IS_IOS = Platform.OS === 'ios';
export default class PaymentRequest {
_id;
_shippingAddress;
_shippingOption;
_shippingType;
_acceptPromiseResolver;
_acceptPromiseRejecter;
_serializedMethodData;
_details;
_options;
_state;
_updating;
_shippingAddressChangesCount;
_shippingAddressChangeFn;
_shippingOptionChangeFn;
_shippingAddressChangeSubscription;
_shippingOptionChangeSubscription;
_userDismissSubscription;
_userAcceptSubscription;
static canMakePaymentsUsingNetworks = NativePayments.canMakePaymentsUsingNetworks;
static MerchantCapabilities = {
debit: 'debit',
credit: 'credit',
emv: 'emv'
};
constructor(methodData = [], details, options = Object.create(null)) {
options = { ...options };
options.merchantCapabilities = transformMerchantCapabilities(options.merchantCapabilities);
noop();
if (!details.id) {
details.id = randomUUID();
}
validatePaymentMethods(methodData);
validateTotal(details.total, ConstructorError);
validateDisplayItems(details.displayItems, ConstructorError);
let selectedShippingOption = null;
validateShippingOptions(details, ConstructorError);
if (IS_IOS) {
selectedShippingOption = getSelectedShippingOption(details.shippingOptions);
}
noop();
noop();
this._options = options;
this._state = 'created';
this._updating = false;
this._details = details;
this._serializedMethodData = JSON.stringify(methodData);
this._id = details.id;
this._shippingOption = selectedShippingOption;
this._shippingAddress = null;
this._shippingType =
IS_IOS && options.shippingType && options.requestShipping === true ? options.shippingType : null;
this._setupEventListeners();
this._shippingAddressChangesCount = 0;
const platformMethodData = getPlatformMethodData(methodData, Platform.OS);
const normalizedDetails = convertDetailAmountsToString(details);
NativePayments.createPaymentRequest(platformMethodData, normalizedDetails, options);
}
_setupEventListeners() {
this._userDismissSubscription = DeviceEventEmitter.addListener(USER_DISMISS_EVENT, this._closePaymentRequest.bind(this));
this._userAcceptSubscription = DeviceEventEmitter.addListener(USER_ACCEPT_EVENT, this._handleUserAccept.bind(this));
if (IS_IOS) {
this._shippingOptionChangeSubscription = DeviceEventEmitter.addListener(INTERNAL_SHIPPING_OPTION_CHANGE_EVENT, this._handleShippingOptionChange.bind(this));
this._shippingAddressChangeSubscription = DeviceEventEmitter.addListener(INTERNAL_SHIPPING_ADDRESS_CHANGE_EVENT, this._handleShippingAddressChange.bind(this));
}
}
_handleShippingAddressChange(postalAddress) {
this._shippingAddress = postalAddress;
const event = new PaymentRequestUpdateEvent(SHIPPING_ADDRESS_CHANGE_EVENT, this);
this._shippingAddressChangesCount++;
if (IS_IOS && this._shippingAddressChangesCount === 1) {
return event.updateWith(this._details);
}
this._shippingAddressChangeFn?.(event);
}
_handleShippingOptionChange(value) {
this._shippingOption = value.selectedShippingOptionId;
const event = new PaymentRequestUpdateEvent(SHIPPING_OPTION_CHANGE_EVENT, this);
this._shippingOptionChangeFn?.(event);
}
_getPlatformDetails(details) {
const { paymentData: serializedPaymentData, billingContact: serializedBillingContact, shippingContact: serializedShippingContact, paymentToken, transactionIdentifier, paymentMethod } = details;
const isSimulator = transactionIdentifier === 'Simulated Identifier';
let billingContact = null;
let shippingContact = null;
if (serializedBillingContact && serializedBillingContact !== '') {
try {
billingContact = JSON.parse(serializedBillingContact);
}
catch (e) { }
}
if (serializedShippingContact && serializedShippingContact !== '') {
try {
shippingContact = JSON.parse(serializedShippingContact);
}
catch (e) { }
}
return {
paymentData: isSimulator ? null : JSON.parse(serializedPaymentData),
billingContact,
shippingContact,
paymentToken,
transactionIdentifier,
paymentMethod
};
}
_handleUserAccept(details) {
if (IS_ANDROID) {
const { shippingAddress } = details;
this._shippingAddress = shippingAddress;
}
const paymentResponse = new PaymentResponse({
requestId: this.id,
methodName: IS_IOS ? 'apple-pay' : 'android-pay',
shippingAddress: this._options.requestShipping
? this._shippingAddress
: null,
details: this._getPlatformDetails(details),
shippingOption: IS_IOS ? this._shippingOption ?? null : null,
payerName: this._options.requestPayerName ? this._shippingAddress?.recipient ?? null : null,
payerPhone: this._options.requestPayerPhone ? this._shippingAddress?.phone ?? null : null,
payerEmail: IS_ANDROID && this._options.requestPayerEmail
? details.payerEmail
: null
});
return this._acceptPromiseResolver?.(paymentResponse);
}
_closePaymentRequest(reject = true) {
this._state = 'closed';
if (reject)
this._acceptPromiseRejecter?.(new Error('AbortError'));
this._removeEventListeners();
}
_removeEventListeners() {
this._userDismissSubscription?.remove();
this._userAcceptSubscription?.remove();
if (IS_IOS) {
this._shippingAddressChangeSubscription?.remove();
this._shippingOptionChangeSubscription?.remove();
}
}
stopRequest() {
if (this._state !== 'closed')
this._closePaymentRequest(false);
}
addEventListener(eventName, fn) {
if (eventName === SHIPPING_ADDRESS_CHANGE_EVENT) {
return (this._shippingAddressChangeFn = fn.bind(this));
}
if (eventName === SHIPPING_OPTION_CHANGE_EVENT) {
return (this._shippingOptionChangeFn = fn.bind(this));
}
}
get id() {
return this._id;
}
get shippingAddress() {
return this._shippingAddress;
}
get shippingOption() {
return this._shippingOption;
}
show() {
return new Promise((resolve, reject) => {
this._acceptPromiseResolver = resolve;
this._acceptPromiseRejecter = reject;
if (this._state !== 'created') {
return reject(new Error('InvalidStateError'));
}
this._state = 'interactive';
NativePayments.show().catch(reject);
});
}
async abort() {
if (this._state !== 'interactive') {
throw new Error('InvalidStateError');
}
try {
await NativePayments.abort();
this._closePaymentRequest();
}
catch (error) {
throw new Error('InvalidStateError');
}
}
canMakePayments() {
return NativePayments.canMakePayments();
}
}