/**
* External dependencies
*/
import { store as noticesStore } from '@wordpress/notices';
import deprecated from '@wordpress/deprecated';
import type { BillingAddress, ShippingAddress } from '@woocommerce/settings';
import { isObject, isString, objectHasProp } from '@woocommerce/types';
* Internal dependencies
import {
emitEventWithAbort,
isErrorResponse,
isFailResponse,
isSuccessResponse,
noticeContexts,
ObserverResponse,
} from '../../base/context/event-emit';
import { EMIT_TYPES } from '../../base/context/providers/cart-checkout/payment-events/event-emit';
import type { emitProcessingEventType } from './types';
import { CART_STORE_KEY } from '../cart';
isBillingAddress,
isShippingAddress,
} from '../../types/type-guards/address';
import { isObserverResponse } from '../../types/type-guards/observers';
import { isValidValidationErrorsObject } from '../../types/type-guards/validation';
export const __internalSetExpressPaymentError = ( message?: string ) => {
return ( { registry } ) => {
const { createErrorNotice, removeNotice } =
registry.dispatch( noticesStore );
if ( message ) {
createErrorNotice( message, {
id: 'wc-express-payment-error',
context: noticeContexts.EXPRESS_PAYMENTS,
} );
} else {
removeNotice(
'wc-express-payment-error',
noticeContexts.EXPRESS_PAYMENTS
);
}
};
* Emit the payment_processing event
export const __internalEmitPaymentProcessingEvent: emitProcessingEventType = (
currentObserver,
setValidationErrors
) => {
return ( { dispatch, registry } ) => {
registry.dispatch( 'core/notices' );
removeNotice( 'wc-payment-error', noticeContexts.PAYMENTS );
return emitEventWithAbort(
EMIT_TYPES.PAYMENT_PROCESSING,
{}
).then( ( observerResponses ) => {
let successResponse: ObserverResponse | undefined,
errorResponse: ObserverResponse | undefined,
billingAddress: BillingAddress | undefined,
shippingAddress: ShippingAddress | undefined;
observerResponses.forEach( ( response ) => {
if ( isSuccessResponse( response ) ) {
// The last observer response always "wins" for success.
successResponse = response;
// We consider both failed and error responses as an error.
if (
isErrorResponse( response ) ||
isFailResponse( response )
) {
errorResponse = response;
// Extensions may return shippingData, shippingAddress, billingData, and billingAddress in the response,
// so we need to check for all. If we detect either shippingData or billingData we need to show a
// deprecated warning for it, but also apply the changes to the wc/store/cart store.
const {
billingAddress: billingAddressFromResponse,
// Deprecated, but keeping it for now, for compatibility with extensions returning it.
billingData: billingDataFromResponse,
shippingAddress: shippingAddressFromResponse,
shippingData: shippingDataFromResponse,
} = response?.meta || {};
billingAddress = billingAddressFromResponse as BillingAddress;
shippingAddress =
shippingAddressFromResponse as ShippingAddress;
if ( billingDataFromResponse ) {
// Set this here so that old extensions still using billingData can set the billingAddress.
billingAddress = billingDataFromResponse as BillingAddress;
deprecated(
'returning billingData from an onPaymentProcessing observer in WooCommerce Blocks',
{
version: '9.5.0',
alternative: 'billingAddress',
link: 'https://github.com/woocommerce/woocommerce-blocks/pull/6369',
if ( shippingDataFromResponse ) {
// Set this here so that old extensions still using shippingData can set the shippingAddress.
shippingDataFromResponse as ShippingAddress;
'returning shippingData from an onPaymentProcessing observer in WooCommerce Blocks',
alternative: 'shippingAddress',
link: 'https://github.com/woocommerce/woocommerce-blocks/pull/8163',
const { setBillingAddress, setShippingAddress } =
registry.dispatch( CART_STORE_KEY );
// Observer returned success, we sync the payment method data and billing address.
if ( isObserverResponse( successResponse ) && ! errorResponse ) {
const { paymentMethodData } = successResponse?.meta || {};
if ( isBillingAddress( billingAddress ) ) {
setBillingAddress( billingAddress );
if ( isShippingAddress( shippingAddress ) ) {
setShippingAddress( shippingAddress );
dispatch.__internalSetPaymentMethodData(
isObject( paymentMethodData ) ? paymentMethodData : {}
dispatch.__internalSetPaymentReady();
} else if ( isFailResponse( errorResponse ) ) {
const { paymentMethodData } = errorResponse?.meta || {};
objectHasProp( errorResponse, 'message' ) &&
isString( errorResponse.message ) &&
errorResponse.message.length
let context: string = noticeContexts.PAYMENTS;
objectHasProp( errorResponse, 'messageContext' ) &&
isString( errorResponse.messageContext ) &&
errorResponse.messageContext.length
context = errorResponse.messageContext;
createErrorNotice( errorResponse.message, {
id: 'wc-payment-error',
isDismissible: false,
context,
dispatch.__internalSetPaymentError();
} else if ( isErrorResponse( errorResponse ) ) {
isValidValidationErrorsObject(
errorResponse.validationErrors
)
setValidationErrors( errorResponse.validationErrors );
// Otherwise there are no payment methods doing anything so just assume payment method is ready.