File "ApplepayButton.js"
Full Path: /home/siazco/grocery.siazco.se/wp-content/plugins/woocommerce-paypal-payments/modules/ppcp-applepay/resources/js/ApplepayButton.js
File size: 26.96 KB
MIME-type: text/x-java
Charset: utf-8
/* global ApplePaySession */
/* global PayPalCommerceGateway */
import { createAppleErrors } from './Helper/applePayError';
import FormValidator from '../../../ppcp-button/resources/js/modules/Helper/FormValidator';
import ErrorHandler from '../../../ppcp-button/resources/js/modules/ErrorHandler';
import widgetBuilder from '../../../ppcp-button/resources/js/modules/Renderer/WidgetBuilder';
import PaymentButton from '../../../ppcp-button/resources/js/modules/Renderer/PaymentButton';
import {
PaymentContext,
PaymentMethods,
} from '../../../ppcp-button/resources/js/modules/Helper/CheckoutMethodState';
import {
combineStyles,
combineWrapperIds,
} from '../../../ppcp-button/resources/js/modules/Helper/PaymentButtonHelpers';
/**
* Plugin-specific styling.
*
* Note that most properties of this object do not apply to the Apple Pay button.
*
* @typedef {Object} PPCPStyle
* @property {string} shape - Outline shape.
* @property {?number} height - Button height in pixel.
*/
/**
* Style options that are defined by the Apple Pay SDK and are required to render the button.
*
* @typedef {Object} ApplePayStyle
* @property {string} type - Defines the button label.
* @property {string} color - Button color
* @property {string} lang - The locale; an empty string will apply the user-agent's language.
*/
/**
* This object describes the transaction details.
*
* @typedef {Object} TransactionInfo
* @property {string} countryCode - The ISO country code
* @property {string} currencyCode - The ISO currency code
* @property {string} totalPriceStatus - Usually 'FINAL', can also be 'DRAFT'
* @property {string} totalPrice - Total monetary value of the transaction.
* @property {Array} chosenShippingMethods - Selected shipping method.
* @property {string} shippingPackages - A list of available shipping methods, defined by WooCommerce.
*/
/**
* A payment button for Apple Pay.
*
* On a single page, multiple Apple Pay buttons can be displayed, which also means multiple
* ApplePayButton instances exist. A typical case is on the product page, where one Apple Pay button
* is located inside the minicart-popup, and another pay-now button is in the product context.
*/
class ApplePayButton extends PaymentButton {
/**
* @inheritDoc
*/
static methodId = PaymentMethods.APPLEPAY;
/**
* @inheritDoc
*/
static cssClass = 'ppcp-button-applepay';
#formData = null;
#updatedContactInfo = [];
#selectedShippingMethod = [];
/**
* Initialization data sent to the button.
*/
#initialPaymentRequest = null;
/**
* Details about the processed transaction, provided to the Apple SDK.
*
* @type {?TransactionInfo}
*/
#transactionInfo = null;
/**
* Apple Pay specific API configuration.
*/
#applePayConfig = null;
/**
* Details about the product (relevant on product page)
*
* @type {{quantity: ?number, items: []}}
*/
#product = {};
/**
* @inheritDoc
*/
static getWrappers( buttonConfig, ppcpConfig ) {
return combineWrapperIds(
buttonConfig?.button?.wrapper || '',
buttonConfig?.button?.mini_cart_wrapper || '',
ppcpConfig?.button?.wrapper || '',
'ppc-button-applepay-container',
'ppc-button-ppcp-applepay'
);
}
/**
* @inheritDoc
*/
static getStyles( buttonConfig, ppcpConfig ) {
const { color, lang, type } = buttonConfig?.button || {};
const buttonStyle = { color, lang, type };
const buttonStyles = {
style: buttonStyle,
mini_cart_style: buttonStyle,
};
return combineStyles( ppcpConfig?.button || {}, buttonStyles );
}
constructor(
context,
externalHandler,
buttonConfig,
ppcpConfig,
contextHandler
) {
// Disable debug output in the browser console:
// buttonConfig.is_debug = false;
super(
context,
externalHandler,
buttonConfig,
ppcpConfig,
contextHandler
);
this.init = this.init.bind( this );
this.onPaymentAuthorized = this.onPaymentAuthorized.bind( this );
this.onButtonClick = this.onButtonClick.bind( this );
this.#product = {
quantity: null,
items: [],
};
this.log( 'Create instance' );
}
/**
* @inheritDoc
*/
get requiresShipping() {
if ( ! super.requiresShipping ) {
return false;
}
if ( ! this.buttonConfig.product?.needShipping ) {
return false;
}
return (
PaymentContext.Checkout !== this.context ||
this.shouldUpdateButtonWithFormData
);
}
/**
* Details about the processed transaction.
*
* This object defines the price that is charged, and text that is displayed inside the
* payment sheet.
*
* @return {?TransactionInfo} The TransactionInfo object.
*/
get transactionInfo() {
return this.#transactionInfo;
}
/**
* Assign the new transaction details to the payment button.
*
* @param {TransactionInfo} newTransactionInfo - Transaction details.
*/
set transactionInfo( newTransactionInfo ) {
this.#transactionInfo = newTransactionInfo;
this.refresh();
}
/**
* The nonce for ajax requests.
*
* @return {string} The nonce value
*/
get nonce() {
const input = document.getElementById(
'woocommerce-process-checkout-nonce'
);
return input?.value || this.buttonConfig.nonce;
}
/**
* @inheritDoc
*/
registerValidationRules( invalidIf, validIf ) {
validIf( () => this.isPreview );
invalidIf(
() => ! this.#applePayConfig,
'No API configuration - missing configure() call?'
);
invalidIf(
() => ! this.#transactionInfo,
'No transactionInfo - missing configure() call?'
);
invalidIf(
() => ! this.contextHandler?.validateContext(),
`Invalid context handler.`
);
}
/**
* Configures the button instance. Must be called before the initial `init()`.
*
* @param {Object} apiConfig - API configuration.
* @param {TransactionInfo} transactionInfo - Transaction details.
*/
configure( apiConfig, transactionInfo ) {
this.#applePayConfig = apiConfig;
this.#transactionInfo = transactionInfo;
}
init() {
// Use `reinit()` to force a full refresh of an initialized button.
if ( this.isInitialized ) {
return;
}
// Stop, if configuration is invalid.
if ( ! this.validateConfiguration() ) {
return;
}
super.init();
this.checkEligibility();
}
reinit() {
// Missing (invalid) configuration indicates, that the first `init()` call did not happen yet.
if ( ! this.validateConfiguration( true ) ) {
return;
}
super.reinit();
this.init();
}
/**
* Re-check if the current session is eligible for Apple Pay.
*/
checkEligibility() {
if ( this.isPreview ) {
this.isEligible = true;
return;
}
try {
if ( ! window.ApplePaySession?.canMakePayments() ) {
this.isEligible = false;
return;
}
this.isEligible = !! this.#applePayConfig.isEligible;
} catch ( error ) {
this.isEligible = false;
}
}
/**
* Starts an Apple Pay session, which means that the user interacted with the Apple Pay button.
*
* @param {Object} paymentRequest The payment request object.
*/
applePaySession( paymentRequest ) {
this.log( 'applePaySession', paymentRequest );
const session = new ApplePaySession( 4, paymentRequest );
if ( this.requiresShipping ) {
session.onshippingmethodselected =
this.onShippingMethodSelected( session );
session.onshippingcontactselected =
this.onShippingContactSelected( session );
}
session.onvalidatemerchant = this.onValidateMerchant( session );
session.onpaymentauthorized = this.onPaymentAuthorized( session );
/**
* This starts the merchant validation process and displays the payment sheet
* {@see https://developer.apple.com/documentation/apple_pay_on_the_web/applepaysession/1778001-begin}
*
* After calling the `begin` method, the browser invokes your `onvalidatemerchant` handler
* {@see https://applepaydemo.apple.com/apple-pay-js-api}
*/
session.begin();
return session;
}
/**
* Applies CSS classes and inline styling to the payment button wrapper.
*/
applyWrapperStyles() {
super.applyWrapperStyles();
const { height } = this.style;
if ( height ) {
const wrapper = this.wrapperElement;
wrapper.style.setProperty(
'--apple-pay-button-height',
`${ height }px`
);
wrapper.style.height = `${ height }px`;
}
}
/**
* Creates the payment button and calls `this.insertButton()` to make the button visible in the
* correct wrapper.
*/
addButton() {
const { color, type, language } = this.style;
const button = document.createElement( 'apple-pay-button' );
button.id = 'apple-' + this.wrapperId;
button.setAttribute( 'buttonstyle', color );
button.setAttribute( 'type', type );
button.setAttribute( 'locale', language );
button.addEventListener( 'click', ( evt ) => {
evt.preventDefault();
this.onButtonClick();
} );
this.insertButton( button );
}
//------------------------
// Button click
//------------------------
/**
* Show Apple Pay payment sheet when Apple Pay payment button is clicked
*/
async onButtonClick() {
this.log( 'onButtonClick' );
const paymentRequest = this.paymentRequest();
// Do this on another place like on create order endpoint handler.
window.ppcpFundingSource = 'apple_pay';
// Trigger woocommerce validation if we are in the checkout page.
if ( PaymentContext.Checkout === this.context ) {
const checkoutFormSelector = 'form.woocommerce-checkout';
const errorHandler = new ErrorHandler(
PayPalCommerceGateway.labels.error.generic,
document.querySelector( '.woocommerce-notices-wrapper' )
);
try {
const formData = new FormData(
document.querySelector( checkoutFormSelector )
);
this.#formData = Object.fromEntries( formData.entries() );
this.updateRequestDataWithForm( paymentRequest );
} catch ( error ) {
console.error( error );
}
this.log( '=== paymentRequest', paymentRequest );
const session = this.applePaySession( paymentRequest );
const formValidator =
PayPalCommerceGateway.early_checkout_validation_enabled
? new FormValidator(
PayPalCommerceGateway.ajax.validate_checkout.endpoint,
PayPalCommerceGateway.ajax.validate_checkout.nonce
)
: null;
if ( formValidator ) {
try {
const errors = await formValidator.validate(
document.querySelector( checkoutFormSelector )
);
if ( errors.length > 0 ) {
errorHandler.messages( errors );
jQuery( document.body ).trigger( 'checkout_error', [
errorHandler.currentHtml(),
] );
session.abort();
return;
}
} catch ( error ) {
console.error( error );
}
}
return;
}
// Default session initialization.
this.applePaySession( paymentRequest );
}
/**
* If the button should be updated with the form addresses.
*
* @return {boolean} True, when Apple Pay data should be submitted to WooCommerce.
*/
get shouldUpdateButtonWithFormData() {
if ( PaymentContext.Checkout !== this.context ) {
return false;
}
return (
this.buttonConfig?.preferences?.checkout_data_mode ===
'use_applepay'
);
}
/**
* Indicates how payment completion should be handled if with the context handler default
* actions. Or with Apple Pay module specific completion.
*
* @return {boolean} True, when the Apple Pay data should be submitted to WooCommerce.
*/
get shouldCompletePaymentWithContextHandler() {
// Data already handled, ex: PayNow
if ( ! this.contextHandler.shippingAllowed() ) {
return true;
}
// Use WC form data mode in Checkout.
return (
PaymentContext.Checkout === this.context &&
! this.shouldUpdateButtonWithFormData
);
}
/**
* Updates Apple Pay paymentRequest with form data.
*
* @param {Object} paymentRequest Object to extend with form data.
*/
updateRequestDataWithForm( paymentRequest ) {
if ( ! this.shouldUpdateButtonWithFormData ) {
return;
}
// Add billing address.
paymentRequest.billingContact = this.fillBillingContact(
this.#formData
);
if ( ! this.requiresShipping ) {
return;
}
// Add shipping address.
paymentRequest.shippingContact = this.fillShippingContact(
this.#formData
);
// Get shipping methods.
const rate = this.transactionInfo.chosenShippingMethods[ 0 ];
paymentRequest.shippingMethods = [];
// Add selected shipping method.
for ( const shippingPackage of this.transactionInfo.shippingPackages ) {
if ( rate === shippingPackage.id ) {
const shippingMethod = {
label: shippingPackage.label,
detail: '',
amount: shippingPackage.cost_str,
identifier: shippingPackage.id,
};
// Remember this shipping method as the selected one.
this.#selectedShippingMethod = shippingMethod;
paymentRequest.shippingMethods.push( shippingMethod );
break;
}
}
// Add other shipping methods.
for ( const shippingPackage of this.transactionInfo.shippingPackages ) {
if ( rate !== shippingPackage.id ) {
paymentRequest.shippingMethods.push( {
label: shippingPackage.label,
detail: '',
amount: shippingPackage.cost_str,
identifier: shippingPackage.id,
} );
}
}
// Store for reuse in case this data is not provided by ApplePay on authorization.
this.#initialPaymentRequest = paymentRequest;
this.log(
'=== paymentRequest.shippingMethods',
paymentRequest.shippingMethods
);
}
paymentRequest() {
const applepayConfig = this.#applePayConfig;
const buttonConfig = this.buttonConfig;
const baseRequest = {
countryCode: applepayConfig.countryCode,
merchantCapabilities: applepayConfig.merchantCapabilities,
supportedNetworks: applepayConfig.supportedNetworks,
requiredShippingContactFields: [
'postalAddress',
'email',
'phone',
],
// ApplePay does not implement billing email and phone fields.
requiredBillingContactFields: [ 'postalAddress' ],
};
if ( ! this.requiresShipping ) {
if ( this.shouldCompletePaymentWithContextHandler ) {
// Data is handled externally.
baseRequest.requiredShippingContactFields = [];
} else {
// Minimum data required to create order.
baseRequest.requiredShippingContactFields = [
'email',
'phone',
];
}
}
const paymentRequest = Object.assign( {}, baseRequest );
paymentRequest.currencyCode = buttonConfig.shop.currencyCode;
paymentRequest.total = {
label: buttonConfig.shop.totalLabel,
type: 'final',
amount: this.transactionInfo.totalPrice,
};
return paymentRequest;
}
refreshProductContextData() {
if ( PaymentContext.Product !== this.context ) {
return;
}
// Refresh product data that makes the price change.
this.#product.quantity = document.querySelector( 'input.qty' )?.value;
// Always an array; grouped products can return multiple items.
this.#product.items = this.contextHandler.products();
this.log( 'Products updated', this.#product );
}
//------------------------
// Payment process
//------------------------
/**
* Make ajax call to change the verification-status of the current domain.
*
* @param {boolean} isValid
*/
adminValidation( isValid ) {
// eslint-disable-next-line no-unused-vars
const ignored = fetch( this.buttonConfig.ajax_url, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams( {
action: 'ppcp_validate',
'woocommerce-process-checkout-nonce': this.nonce,
validation: isValid,
} ).toString(),
} );
}
/**
* Returns an event handler that Apple Pay calls when displaying the payment sheet.
*
* @see https://developer.apple.com/documentation/apple_pay_on_the_web/applepaysession/1778021-onvalidatemerchant
*
* @param {Object} session The ApplePaySession object.
*
* @return {(function(*): void)|*} Callback that runs after the merchant validation
*/
onValidateMerchant( session ) {
return ( applePayValidateMerchantEvent ) => {
this.log( 'onvalidatemerchant call' );
widgetBuilder.paypal
.Applepay()
.validateMerchant( {
validationUrl: applePayValidateMerchantEvent.validationURL,
} )
.then( ( validateResult ) => {
session.completeMerchantValidation(
validateResult.merchantSession
);
this.adminValidation( true );
} )
.catch( ( validateError ) => {
console.error( validateError );
this.adminValidation( false );
this.log( 'onvalidatemerchant session abort' );
session.abort();
} );
};
}
onShippingMethodSelected( session ) {
this.log( 'onshippingmethodselected', this.buttonConfig.ajax_url );
const ajaxUrl = this.buttonConfig.ajax_url;
return ( event ) => {
this.log( 'onshippingmethodselected call' );
const data = this.getShippingMethodData( event );
jQuery.ajax( {
url: ajaxUrl,
method: 'POST',
data,
success: ( applePayShippingMethodUpdate ) => {
this.log( 'onshippingmethodselected ok' );
const response = applePayShippingMethodUpdate.data;
if ( applePayShippingMethodUpdate.success === false ) {
response.errors = createAppleErrors( response.errors );
}
this.#selectedShippingMethod = event.shippingMethod;
// Sort the response shipping methods, so that the selected shipping method is
// the first one.
response.newShippingMethods =
response.newShippingMethods.sort( ( a ) => {
if (
a.label === this.#selectedShippingMethod.label
) {
return -1;
}
return 1;
} );
if ( applePayShippingMethodUpdate.success === false ) {
response.errors = createAppleErrors( response.errors );
}
session.completeShippingMethodSelection( response );
},
error: ( jqXHR, textStatus, errorThrown ) => {
this.log( 'onshippingmethodselected error', textStatus );
console.warn( textStatus, errorThrown );
session.abort();
},
} );
};
}
onShippingContactSelected( session ) {
this.log( 'onshippingcontactselected', this.buttonConfig.ajax_url );
const ajaxUrl = this.buttonConfig.ajax_url;
return ( event ) => {
this.log( 'onshippingcontactselected call' );
const data = this.getShippingContactData( event );
jQuery.ajax( {
url: ajaxUrl,
method: 'POST',
data,
success: ( applePayShippingContactUpdate ) => {
this.log( 'onshippingcontactselected ok' );
const response = applePayShippingContactUpdate.data;
this.#updatedContactInfo = event.shippingContact;
if ( applePayShippingContactUpdate.success === false ) {
response.errors = createAppleErrors( response.errors );
}
if ( response.newShippingMethods ) {
this.#selectedShippingMethod =
response.newShippingMethods[ 0 ];
}
session.completeShippingContactSelection( response );
},
error: ( jqXHR, textStatus, errorThrown ) => {
this.log( 'onshippingcontactselected error', textStatus );
console.warn( textStatus, errorThrown );
session.abort();
},
} );
};
}
getShippingContactData( event ) {
const productId = this.buttonConfig.product.id;
this.refreshProductContextData();
switch ( this.context ) {
case PaymentContext.Product:
return {
action: 'ppcp_update_shipping_contact',
product_id: productId,
products: JSON.stringify( this.#product.items ),
caller_page: 'productDetail',
product_quantity: this.#product.quantity,
simplified_contact: event.shippingContact,
need_shipping: this.requiresShipping,
'woocommerce-process-checkout-nonce': this.nonce,
};
case PaymentContext.Cart:
case PaymentContext.Checkout:
case PaymentContext.BlockCart:
case PaymentContext.BlockCheckout:
case PaymentContext.MiniCart:
return {
action: 'ppcp_update_shipping_contact',
simplified_contact: event.shippingContact,
caller_page: 'cart',
need_shipping: this.requiresShipping,
'woocommerce-process-checkout-nonce': this.nonce,
};
}
}
getShippingMethodData( event ) {
const productId = this.buttonConfig.product.id;
this.refreshProductContextData();
switch ( this.context ) {
case PaymentContext.Product:
return {
action: 'ppcp_update_shipping_method',
shipping_method: event.shippingMethod,
simplified_contact: this.hasValidContactInfo(
this.#updatedContactInfo
)
? this.#updatedContactInfo
: this.#initialPaymentRequest?.shippingContact ??
this.#initialPaymentRequest?.billingContact,
product_id: productId,
products: JSON.stringify( this.#product.items ),
caller_page: 'productDetail',
product_quantity: this.#product.quantity,
'woocommerce-process-checkout-nonce': this.nonce,
};
case PaymentContext.Cart:
case PaymentContext.Checkout:
case PaymentContext.BlockCart:
case PaymentContext.BlockCheckout:
case PaymentContext.MiniCart:
return {
action: 'ppcp_update_shipping_method',
shipping_method: event.shippingMethod,
simplified_contact: this.hasValidContactInfo(
this.#updatedContactInfo
)
? this.#updatedContactInfo
: this.#initialPaymentRequest?.shippingContact ??
this.#initialPaymentRequest?.billingContact,
caller_page: 'cart',
'woocommerce-process-checkout-nonce': this.nonce,
};
}
}
onPaymentAuthorized( session ) {
this.log( 'onpaymentauthorized' );
return async ( event ) => {
this.log( 'onpaymentauthorized call' );
const processInWooAndCapture = async ( data ) => {
return new Promise( ( resolve, reject ) => {
try {
const billingContact =
data.billing_contact ||
this.#initialPaymentRequest.billingContact;
const shippingContact =
data.shipping_contact ||
this.#initialPaymentRequest.shippingContact;
const shippingMethod =
this.#selectedShippingMethod ||
( this.#initialPaymentRequest.shippingMethods ||
[] )[ 0 ];
const requestData = {
action: 'ppcp_create_order',
caller_page: this.context,
product_id: this.buttonConfig.product.id ?? null,
products: JSON.stringify( this.#product.items ),
product_quantity: this.#product.quantity,
shipping_contact: shippingContact,
billing_contact: billingContact,
token: event.payment.token,
shipping_method: shippingMethod,
'woocommerce-process-checkout-nonce': this.nonce,
funding_source: 'applepay',
_wp_http_referer: '/?wc-ajax=update_order_review',
paypal_order_id: data.paypal_order_id,
};
this.log(
'onpaymentauthorized request',
this.buttonConfig.ajax_url,
data
);
jQuery.ajax( {
url: this.buttonConfig.ajax_url,
method: 'POST',
data: requestData,
complete: () => {
this.log( 'onpaymentauthorized complete' );
},
success: ( authorizationResult ) => {
this.log( 'onpaymentauthorized ok' );
resolve( authorizationResult );
},
error: ( jqXHR, textStatus, errorThrown ) => {
this.log(
'onpaymentauthorized error',
textStatus
);
reject( new Error( errorThrown ) );
},
} );
} catch ( error ) {
this.error( 'onpaymentauthorized catch', error );
}
} );
};
const id = await this.contextHandler.createOrder();
this.log(
'onpaymentauthorized paypal order ID',
id,
event.payment.token,
event.payment.billingContact
);
try {
const confirmOrderResponse = await widgetBuilder.paypal
.Applepay()
.confirmOrder( {
orderId: id,
token: event.payment.token,
billingContact: event.payment.billingContact,
} );
this.log(
'onpaymentauthorized confirmOrderResponse',
confirmOrderResponse
);
if (
confirmOrderResponse &&
confirmOrderResponse.approveApplePayPayment
) {
if (
confirmOrderResponse.approveApplePayPayment.status ===
'APPROVED'
) {
try {
if (
this.shouldCompletePaymentWithContextHandler
) {
// No shipping, expect immediate capture, ex: PayNow, Checkout with
// form data.
let approveFailed = false;
await this.contextHandler.approveOrder(
{
orderID: id,
},
{
// actions mock object.
restart: () =>
new Promise( ( resolve ) => {
approveFailed = true;
resolve();
} ),
order: {
get: () =>
new Promise( ( resolve ) => {
resolve( null );
} ),
},
}
);
if ( ! approveFailed ) {
this.log(
'onpaymentauthorized approveOrder OK'
);
session.completePayment(
ApplePaySession.STATUS_SUCCESS
);
} else {
this.error(
'onpaymentauthorized approveOrder FAIL'
);
session.completePayment(
ApplePaySession.STATUS_FAILURE
);
session.abort();
}
} else {
// Default payment.
const data = {
billing_contact:
event.payment.billingContact,
shipping_contact:
event.payment.shippingContact,
paypal_order_id: id,
};
const authorizationResult =
await processInWooAndCapture( data );
if (
authorizationResult.result === 'success'
) {
session.completePayment(
ApplePaySession.STATUS_SUCCESS
);
window.location.href =
authorizationResult.redirect;
} else {
session.completePayment(
ApplePaySession.STATUS_FAILURE
);
}
}
} catch ( error ) {
session.completePayment(
ApplePaySession.STATUS_FAILURE
);
session.abort();
console.error( error );
}
} else {
console.error( 'Error status is not APPROVED' );
session.completePayment(
ApplePaySession.STATUS_FAILURE
);
}
} else {
console.error( 'Invalid confirmOrderResponse' );
session.completePayment( ApplePaySession.STATUS_FAILURE );
}
} catch ( error ) {
console.error(
'Error confirming order with applepay token',
error
);
session.completePayment( ApplePaySession.STATUS_FAILURE );
session.abort();
}
};
}
#extractContactInfo( data, primaryPrefix, fallbackPrefix ) {
if ( ! data || typeof data !== 'object' ) {
data = {};
}
const getValue = ( key ) =>
data[ `${ primaryPrefix }_${ key }` ] ||
data[ `${ fallbackPrefix }_${ key }` ] ||
'';
return {
givenName: getValue( 'first_name' ),
familyName: getValue( 'last_name' ),
emailAddress: getValue( 'email' ),
phoneNumber: getValue( 'phone' ),
addressLines: [ getValue( 'address_1' ), getValue( 'address_2' ) ],
locality: getValue( 'city' ),
postalCode: getValue( 'postcode' ),
countryCode: getValue( 'country' ),
administrativeArea: getValue( 'state' ),
};
}
fillBillingContact( data ) {
return this.#extractContactInfo( data, 'billing', '' );
}
fillShippingContact( data ) {
if ( ! data?.shipping_first_name ) {
return this.fillBillingContact( data );
}
return this.#extractContactInfo( data, 'shipping', 'billing' );
}
hasValidContactInfo( value ) {
return Array.isArray( value )
? value.length > 0
: Object.keys( value || {} ).length > 0;
}
}
export default ApplePayButton;