@rnw-community/react-native-payments
Version:
Accept Payments with Apple Pay and Android Pay using the Payment Request API.
251 lines • 16 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.PaymentRequest = void 0;
/* eslint-disable max-lines */
const react_native_1 = require("react-native");
const react_native_uuid_1 = __importDefault(require("react-native-uuid"));
const shared_1 = require("@rnw-community/shared");
const android_payment_method_tokenization_type_enum_1 = require("../../@standard/android/enum/android-payment-method-tokenization-type.enum");
const android_payment_data_request_1 = require("../../@standard/android/request/android-payment-data-request");
const android_payment_method_1 = require("../../@standard/android/request/android-payment-method");
const android_transaction_info_1 = require("../../@standard/android/request/android-transaction-info");
const ios_pk_contact_field_enum_1 = require("../../@standard/ios/enum/ios-pk-contact-field.enum");
const ios_pk_merchant_capability_enum_1 = require("../../@standard/ios/enum/ios-pk-merchant-capability.enum");
const ios_pk_payment_networks_enum_1 = require("../../@standard/ios/enum/ios-pk-payment-networks.enum");
const payment_method_name_enum_1 = require("../../enum/payment-method-name.enum");
const payments_error_enum_1 = require("../../enum/payments-error.enum");
const supported_networks_enum_1 = require("../../enum/supported-networks.enum");
const constructor_error_1 = require("../../error/constructor.error");
const dom_exception_1 = require("../../error/dom.exception");
const payments_error_1 = require("../../error/payments.error");
const validate_display_items_util_1 = require("../../util/validate-display-items.util");
const validate_payment_methods_util_1 = require("../../util/validate-payment-methods.util");
const validate_total_util_1 = require("../../util/validate-total.util");
const native_payments_1 = require("../native-payments/native-payments");
const android_payment_response_1 = require("../payment-response/android-payment-response");
const ios_payment_response_1 = require("../payment-response/ios-payment-response");
/*
* HINT: Troubleshooting: https://developers.google.com/pay/api/android/support/troubleshooting
* HINT: Google Pay API Errors: https://developers.google.com/pay/api/web/reference/error-objects
*/
class PaymentRequest {
// eslint-disable-next-line max-statements
constructor(methodData, details) {
this.methodData = methodData;
this.details = details;
this.updating = false;
this.state = 'created';
this.acceptPromiseRejecter = shared_1.emptyFn;
// 3. Establish the request's id:
if (!(0, shared_1.isNotEmptyString)(details.id)) {
// TODO: Can we avoid using external lib? Use Math.random?
details.id = react_native_uuid_1.default.v4();
}
this.id = details.id;
// 4. Process payment methods
(0, validate_payment_methods_util_1.validatePaymentMethods)(methodData);
// 5. Process the total
(0, validate_total_util_1.validateTotal)(details.total, constructor_error_1.ConstructorError);
// 6. If the displayItems member of details is present, then for each item in details.displayItems:
(0, validate_display_items_util_1.validateDisplayItems)(details.displayItems, constructor_error_1.ConstructorError);
// 17. Set request.[[serializedMethodData]] to serializedMethodData. */
this.platformMethodData = this.findPlatformPaymentMethodData();
const nativePlatformMethodData = react_native_1.Platform.OS === 'android'
? this.getAndroidPaymentMethodData(this.platformMethodData, details)
: this.getIosPaymentMethodData(this.platformMethodData);
this.serializedMethodData = JSON.stringify(nativePlatformMethodData);
}
// https://www.w3.org/TR/payment-request/#canmakepayment-method
async canMakePayment() {
if (this.state !== 'created') {
throw new dom_exception_1.DOMException(payments_error_enum_1.PaymentsErrorEnum.InvalidStateError);
}
return native_payments_1.NativePayments.canMakePayments(this.serializedMethodData);
}
// https://www.w3.org/TR/payment-request/#show-method
show() {
return new Promise((resolve, reject) => {
this.acceptPromiseRejecter = reject;
if (this.state === 'created') {
this.state = 'interactive';
// HINT: We need to pass Android environment configuration to native module via details
const details = react_native_1.Platform.OS === 'android'
? {
...this.details,
environment: this.platformMethodData.environment,
}
: this.details;
native_payments_1.NativePayments.show(this.serializedMethodData, details)
.then(jsonDetails => {
resolve(this.handleAccept(jsonDetails));
return void 0;
})
// eslint-disable-next-line @typescript-eslint/use-unknown-in-catch-callback-variable
.catch(reject);
}
else {
reject(new dom_exception_1.DOMException(payments_error_enum_1.PaymentsErrorEnum.InvalidStateError));
}
});
}
// https://www.w3.org/TR/payment-request/#abort-method
async abort() {
if (this.state !== 'interactive') {
throw new dom_exception_1.DOMException(payments_error_enum_1.PaymentsErrorEnum.InvalidStateError);
}
await native_payments_1.NativePayments.abort().catch(() => {
throw new payments_error_1.PaymentsError(`Failed aborting PaymentRequest`);
});
this.state = 'closed';
this.acceptPromiseRejecter(new dom_exception_1.DOMException(payments_error_enum_1.PaymentsErrorEnum.AbortError));
}
handleAccept(details) {
try {
return react_native_1.Platform.OS === 'android'
? new android_payment_response_1.AndroidPaymentResponse(this.id, payment_method_name_enum_1.PaymentMethodNameEnum.AndroidPay, details)
: new ios_payment_response_1.IosPaymentResponse(this.id, payment_method_name_enum_1.PaymentMethodNameEnum.ApplePay, details);
}
catch (_e) {
// TODO: Is there an standard exception for this?
throw new payments_error_1.PaymentsError(`Failed parsing PaymentRequest details`);
}
}
findPlatformPaymentMethodData() {
const platformSupportedMethod = react_native_1.Platform.OS === 'ios' ? payment_method_name_enum_1.PaymentMethodNameEnum.ApplePay : payment_method_name_enum_1.PaymentMethodNameEnum.AndroidPay;
const platformMethod = this.methodData.find(paymentMethodData => paymentMethodData.supportedMethods === platformSupportedMethod);
if (!(0, shared_1.isDefined)(platformMethod)) {
throw new dom_exception_1.DOMException(payments_error_enum_1.PaymentsErrorEnum.NotSupportedError);
}
return platformMethod.data;
}
// eslint-disable-next-line class-methods-use-this,@typescript-eslint/class-methods-use-this
getAndroidPaymentMethodData(methodData, details) {
const isBillingRequired = methodData.requestBillingAddress === true ||
methodData.requestPayerName === true ||
methodData.requestPayerPhone === true;
return {
...android_payment_data_request_1.defaultAndroidPaymentDataRequest,
merchantInfo: {
merchantName: details.total.label,
},
transactionInfo: {
...android_transaction_info_1.defaultAndroidTransactionInfo,
currencyCode: methodData.currencyCode,
totalPrice: details.total.amount.value,
totalPriceLabel: details.total.label,
countryCode: methodData.countryCode,
},
allowedPaymentMethods: [
{
...android_payment_method_1.defaultAndroidPaymentMethod,
parameters: {
...android_payment_method_1.defaultAndroidPaymentMethod.parameters,
allowedCardNetworks: methodData.supportedNetworks.map(network => network.toUpperCase()),
allowedAuthMethods: methodData.allowedAuthMethods ?? android_payment_method_1.defaultAndroidPaymentMethod.parameters.allowedAuthMethods,
...(isBillingRequired && {
billingAddressRequired: true,
billingAddressParameters: {
format: methodData.requestBillingAddress === true ? 'FULL' : 'MIN',
phoneNumberRequired: methodData.requestPayerPhone === true,
},
}),
},
...((0, shared_1.isDefined)(methodData.gatewayConfig) && {
tokenizationSpecification: {
parameters: methodData.gatewayConfig,
type: android_payment_method_tokenization_type_enum_1.AndroidPaymentMethodTokenizationType.PAYMENT_GATEWAY,
},
}),
...((0, shared_1.isDefined)(methodData.directConfig) && {
tokenizationSpecification: {
parameters: methodData.directConfig,
type: android_payment_method_tokenization_type_enum_1.AndroidPaymentMethodTokenizationType.DIRECT,
},
}),
},
],
...(methodData.requestPayerEmail === true && { emailRequired: true }),
...(methodData.requestShipping === true && {
shippingAddressRequired: true,
shippingAddressParameters: {
phoneNumberRequired: methodData.requestPayerPhone === true,
},
}),
};
}
// eslint-disable-next-line class-methods-use-this,@typescript-eslint/class-methods-use-this
getIosPaymentMethodData(methodData) {
// TODO: Add mappings for other systems if needed
const supportedNetworkMap = {
[supported_networks_enum_1.SupportedNetworkEnum.Amex]: ios_pk_payment_networks_enum_1.IosPKPaymentNetworksEnum.PKPaymentNetworkAmex,
[supported_networks_enum_1.SupportedNetworkEnum.Mastercard]: ios_pk_payment_networks_enum_1.IosPKPaymentNetworksEnum.PKPaymentNetworkMasterCard,
[supported_networks_enum_1.SupportedNetworkEnum.Visa]: ios_pk_payment_networks_enum_1.IosPKPaymentNetworksEnum.PKPaymentNetworkVisa,
[supported_networks_enum_1.SupportedNetworkEnum.Discover]: ios_pk_payment_networks_enum_1.IosPKPaymentNetworksEnum.PKPaymentNetworkDiscover,
[supported_networks_enum_1.SupportedNetworkEnum.Bancontact]: ios_pk_payment_networks_enum_1.IosPKPaymentNetworksEnum.PKPaymentNetworkBancontact,
[supported_networks_enum_1.SupportedNetworkEnum.CartesBancaires]: ios_pk_payment_networks_enum_1.IosPKPaymentNetworksEnum.PKPaymentNetworkCartesBancaires,
[supported_networks_enum_1.SupportedNetworkEnum.ChinaUnionPay]: ios_pk_payment_networks_enum_1.IosPKPaymentNetworksEnum.PKPaymentNetworkChinaUnionPay,
[supported_networks_enum_1.SupportedNetworkEnum.Dankort]: ios_pk_payment_networks_enum_1.IosPKPaymentNetworksEnum.PKPaymentNetworkDankort,
[supported_networks_enum_1.SupportedNetworkEnum.Eftpos]: ios_pk_payment_networks_enum_1.IosPKPaymentNetworksEnum.PKPaymentNetworkEftpos,
[supported_networks_enum_1.SupportedNetworkEnum.Electron]: ios_pk_payment_networks_enum_1.IosPKPaymentNetworksEnum.PKPaymentNetworkElectron,
[supported_networks_enum_1.SupportedNetworkEnum.Elo]: ios_pk_payment_networks_enum_1.IosPKPaymentNetworksEnum.PKPaymentNetworkElo,
[supported_networks_enum_1.SupportedNetworkEnum.Girocard]: ios_pk_payment_networks_enum_1.IosPKPaymentNetworksEnum.PKPaymentNetworkGirocard,
[supported_networks_enum_1.SupportedNetworkEnum.Interac]: ios_pk_payment_networks_enum_1.IosPKPaymentNetworksEnum.PKPaymentNetworkInterac,
[supported_networks_enum_1.SupportedNetworkEnum.Jcb]: ios_pk_payment_networks_enum_1.IosPKPaymentNetworksEnum.PKPaymentNetworkJCB,
[supported_networks_enum_1.SupportedNetworkEnum.Mada]: ios_pk_payment_networks_enum_1.IosPKPaymentNetworksEnum.PKPaymentNetworkMada,
[supported_networks_enum_1.SupportedNetworkEnum.Maestro]: ios_pk_payment_networks_enum_1.IosPKPaymentNetworksEnum.PKPaymentNetworkMaestro,
[supported_networks_enum_1.SupportedNetworkEnum.Mir]: ios_pk_payment_networks_enum_1.IosPKPaymentNetworksEnum.PKPaymentNetworkMir,
[supported_networks_enum_1.SupportedNetworkEnum.PrivateLabel]: ios_pk_payment_networks_enum_1.IosPKPaymentNetworksEnum.PKPaymentNetworkPrivateLabel,
[supported_networks_enum_1.SupportedNetworkEnum.Vpay]: ios_pk_payment_networks_enum_1.IosPKPaymentNetworksEnum.PKPaymentNetworkVPay,
};
const defaultMerchantCapabilities = [
ios_pk_merchant_capability_enum_1.IosPKMerchantCapability.PKMerchantCapability3DS,
ios_pk_merchant_capability_enum_1.IosPKMerchantCapability.PKMerchantCapabilityDebit,
ios_pk_merchant_capability_enum_1.IosPKMerchantCapability.PKMerchantCapabilityCredit,
];
const requestedShippingFields = this.getRequestedShippingFields(methodData);
const isShippingRequested = requestedShippingFields.length > 0;
return {
countryCode: methodData.countryCode,
currencyCode: methodData.currencyCode,
merchantIdentifier: methodData.merchantIdentifier,
supportedNetworks: methodData.supportedNetworks.map(network => supportedNetworkMap[network]),
merchantCapabilities: (0, shared_1.isNotEmptyArray)(methodData.merchantCapabilities)
? methodData.merchantCapabilities
: defaultMerchantCapabilities,
...(methodData.requestBillingAddress === true && {
requiredBillingContactFields: this.getRequestedBillingFields(methodData),
}),
...(isShippingRequested && { requiredShippingContactFields: requestedShippingFields }),
};
}
// eslint-disable-next-line class-methods-use-this,@typescript-eslint/class-methods-use-this
getRequestedBillingFields(methodData) {
const requiredBillingFields = [];
if (methodData.requestBillingAddress ?? false) {
requiredBillingFields.push(ios_pk_contact_field_enum_1.IOSPKContactField.PKContactFieldPostalAddress);
}
return requiredBillingFields;
}
// eslint-disable-next-line class-methods-use-this,@typescript-eslint/class-methods-use-this
getRequestedShippingFields(methodData) {
const requiredShippingFields = [];
if (methodData.requestPayerEmail ?? false) {
requiredShippingFields.push(ios_pk_contact_field_enum_1.IOSPKContactField.PKContactFieldEmailAddress);
}
if (methodData.requestPayerName ?? false) {
requiredShippingFields.push(ios_pk_contact_field_enum_1.IOSPKContactField.PKContactFieldName);
}
if (methodData.requestPayerPhone ?? false) {
requiredShippingFields.push(ios_pk_contact_field_enum_1.IOSPKContactField.PKContactFieldPhoneNumber);
}
if (methodData.requestShipping ?? false) {
requiredShippingFields.push(ios_pk_contact_field_enum_1.IOSPKContactField.PKContactFieldPostalAddress);
}
return requiredShippingFields;
}
}
exports.PaymentRequest = PaymentRequest;
//# sourceMappingURL=payment-request.js.map