import { AdyenCheckout, Dropin, Card, PayPal, GooglePay, ApplePay } from '@adyen/adyen-web'
From Drop-in v6, it is possible to import individual payment methods or all at once. We use importing of the individual payment methods in our example.
For example
import { AdyenCheckout, Dropin, Card, PayPal, GooglePay, ApplePay } from '@adyen/adyen-web'
For the styles use import '@adyen/adyen-web/styles/adyen.css';
From v6 onwards, creating an instance of Drop-in is as follows:
const checkout = await AdyenCheckout({
// configuration
});
const dropin = new Dropin(checkout, {
paymentMethodComponents: [Card, PayPal, GooglePay], // payment method classes that are going to be used as part of the Drop-in
paymentMethodsConfiguration: { /** Configurations **/ } // configure each payment method displayed on the Drop-in
}).mount('#dropin-container')
|
Note
|
paymentMethodsConfiguration is moved to the Drop-in configuration
|
onSubmit and onAdditionalDetailsThese events handlers now have access to the new actions parameter, which lets you continue the payment flow actions.resolve() or stop it actions.reject().
const checkout = await AdyenCheckout({
// Insert your global configuration here
onSubmit: async (state: SubmitData, component: UIElement, actions: SubmitActions) => {
try {
// create payment
const paymentSummary = await handleSubmitPaymentInfo(paymentRequest);
// submit the cart
const checkoutResponse = await submitCart({
sensitivePaymentMethodData: [
{
paymentId: paymentSummary.paymentId,
paymentMethodProperties: {
ADYEN_PAYMENT_DATA: {
...state.data,
shopperEmail: authState.isAuthenticated
? authState.customerEmail
: cart.emailAddress,
lineItems,
countryCode: countryCode,
},
},
},
],
});
if (checkoutResponse?.success) {
// At this point, the payment is successful
actions.resolve({
resultCode: 'Authorised',
});
}
} catch (error) {
actions.reject();
}
},
});
const [submitActions, setSubmitActions] = useState<SubmitActions>();
useEffect(() => {
if (!submitActions) {
return;
}
const errorType = get(error, 'failureType');
if (
errorType === 'PAYMENT_REQUIRES_3DS_VERIFICATION' ||
errorType === 'PAYMENT_REQUIRES_EXTERNAL_INTERACTION' ||
errorType === 'PAYMENT_REQUIRES_HOSTED_PAYMENT_PAGE_INTERACTION'
) {
const errorDetails = find(
get(error, 'paymentTransactionFailureDetails'),
({ failureType }) =>
failureType === 'REQUIRES_3DS_VERIFICATION' ||
failureType === 'REQUIRES_EXTERNAL_INTERACTION' ||
failureType === 'REQUIRES_HOSTED_PAYMENT_PAGE_INTERACTION'
);
// get result code from the action
const resultCode = get(errorDetails, 'nextAction.attributes.resultCode');
const action = omit(
get(errorDetails, 'nextAction.attributes'),
'resultCode'
);
if (!resultCode) {
submitActions.reject();
} else {
submitActions.resolve({
resultCode,
action,
});
}
} else if (errorType && error) {
setError(error);
submitActions.reject();
}
}, [error, submitActions, setError]);
onPaymentCompletedThis event now works both in the Sessions Flow and Advanced Flow.
This event is no longer triggered for failed payments. For failed payments, we now use the onPaymentFailed event handler instead.
Once this event is triggered, initiate the successful transaction flow on your web page.
onPaymentFailedThis is a new event, which is triggered when a payment fails. Previously, this event was part of onPaymentCompleted.
This event is triggered when the resultCode of a payment is Cancelled, Error, or Refused.
Once this event is triggered, initiate the failed transaction flow on your web page.
const onPaymentFailed = async (data?: PaymentFailedData, component?: UIElement) => {
const resultCode = data?.resultCode;
component?.unmount();
// redirect to
await router.push({
pathname: '/checkout/payment',
query: {
payment_finalization_status: 'REQUIRES_PAYMENT_MODIFICATION',
payment_result_status:
'Cancelled' === resultCode ? 'PAYMENT_CANCELED' : 'PAYMENT_FAILED',
gateway_type: 'ADYEN',
},
});
}
To align with the latest changes, a new generic hook useAdyenGetAvailablePaymentMethods was added to read the available payment methods.
This new hook allows you to specify the generic response type from Adyen.
import {
AdyenCheckout,
PaymentMethodsResponse,
GooglePay,
ApplePay,
PayPal
} from '@adyen/adyen-web';
import { useAdyenGetAvailablePaymentMethods } from '@broadleaf/adyen-payment-services-react';
const {
getAvailablePaymentMethods,
} = useAdyenGetAvailablePaymentMethods<PaymentMethodsResponse>({
authState,
adyenPaymentServicesClient,
});
...
const shopperReference = authState.isAuthenticated
? authState.customerId
: undefined;
const paymentMethodsResponse = await getAvailablePaymentMethods({
amount,
shopperLocale: locale,
countryCode: 'US',
shopperReference,
});
const checkout = await AdyenCheckout({
environment: 'test',
...
paymentMethodsResponse,
});
// Add this ref to the component where the express checkout button will be rendered.
const container = useRef(null);
// get the payment type to mount
const type = 'applepay'; // googlepay or paypal
// get configuration for the payment type
const configuration = {};
if (checkout.paymentMethodsResponse?.has(type)) {
if (type === 'googlepay' || type === 'applepay') {
const expressCheckoutComponent =
type === 'googlepay'
? new GooglePay(checkout, configuration)
: new ApplePay(checkout, configuration);
expressCheckoutComponent
.isAvailable()
.then(() => {
expressCheckoutComponent.mount(container.current);
})
.catch(() => {
// handle error
});
} else if (type === 'paypal') {
new PayPal(checkout, configuration).mount(container.current);
}
}
The event handlers onShippingContactSelected() and onShippingMethodSelected() now work only when the Apple Pay Component configuration isExpress is set to true
onAuthorizedThis event is now triggered before the onSubmit event, and must be resolved or rejected to continue the payment flow.
onAuthorized returns an object with the authorizedEvent (raw data from Apple Pay) along with the formatted billingAddress and deliveryAddress.
import {
AddressData,
ApplePay,
} from '@adyen/adyen-web';
const applepay = new ApplePay(checkout, {
onAuthorized: (paymentData: {
authorizedEvent: ApplePayJS.ApplePayPaymentAuthorizedEvent;
billingAddress?: Partial<AddressData>;
deliveryAddress?: Partial<AddressData>;
}, actions: {
resolve: () => void;
reject: (error?: ApplePayJS.ApplePayError) => void;
}) => {
setPaymentData(paymentData);
actions.resolve();
},
});
The event handler onPaymentDataChanged() now works only when the Google Pay Component configuration isExpress is set to true.
onAuthorizedThis event is now triggered before the onSubmit event, and must be resolved or rejected to continue the payment flow.
onAuthorized returns an object with the authorizedEvent (raw data from Google Pay) along with the formatted billingAddress and deliveryAddress.
import {
AddressData,
GooglePay,
} from '@adyen/adyen-web';
const googlepay = new GooglePay(checkout, {
onAuthorized: (paymentData: {
authorizedEvent: google.payments.api.PaymentData;
billingAddress?: Partial<AddressData>;
deliveryAddress?: Partial<AddressData>;
}, actions: {
resolve: () => void;
reject: (error?: google.payments.api.PaymentDataError | string) => void;
}) => {
setPaymentData(paymentData);
actions.resolve();
},
});
The onShopperDetails(shopperDetails, rawData, actions) event handler is renamed to onAuthorized({authorizedEvent, billingAddress, deliveryAddress}, actions).
The `onShippingChange() event handler is no longer supported. Implement onShippingAddressChange() and onShippingOptionsChange() instead
import {
AddressData,
PayPal,
} from '@adyen/adyen-web';
import { OrderResponseBody as PayPalOrderResponseBody } from '@paypal/paypal-js';
const payPal = new PayPal(checkout, {
onAuthorized: (paymentData: {
authorizedEvent: PayPalOrderResponseBody;
billingAddress?: Partial<AddressData>;
deliveryAddress?: Partial<AddressData>;
}, actions: {
resolve: () => void;
reject: () => void;
}) => {
setPaymentData(paymentData);
actions.resolve();
},
});
In the express checkout flow, when the PayPal button is clicked, we create a payment for a specific amount.
When the shipping address/option changes, the Cart can be repriced, and the new price is reallocated to the payment.
Due to this, the payment version can be changed and when you update the payment you need to use the latest payment version.
In CartOperationServices, we added the CartAlert in the response to notify the caller about the updated payment.
See CartOperationServices 3.0.0 Release Notes for information about this change.
type PaymentSummaryStoreValueType = {
paymentId: string;
version: number;
};
const createPaymentSummaryStore = () => {
let value: PaymentSummaryStoreValueType = null;
return {
setValue(newValue: PaymentSummaryStoreValueType) {
value = newValue;
},
getValue() {
return value;
},
};
};
const paymentSummaryStore = createPaymentSummaryStore();
const useHandleShippingAddressChange = ({ setErrorMsg }) => {
const { cart } = useCartContext();
const updateFulfillmentAddress = useUpdateFulfillmentAddress({
buildFulfillmentAddress,
});
const buildShippingOptionParameters = useBuildShippingOptionParameters();
const getPaymentAmount = useGetPaymentAmount({
gatewayType: AdyenPaymentForm.TYPE,
});
const authState = usePaymentAuthState();
const paymentClient = usePaymentClient();
const { handleUpdatePaymentInfo } = useSubmitPaymentRequest({
authState,
// set "undefined" to prevent updating existing payments
payments: undefined,
ownerId: cart?.id,
owningUserEmailAddress: cart?.emailAddress,
paymentClient,
multiplePaymentsAllowed: false,
rejectOnError: true,
});
return useEventCallback(async (data, actions, component: PayPal) => {
const { fulfillmentOptions, cart } = await updateFulfillmentAddress(
get(data, 'shippingAddress')
);
const shippingOptions = buildShippingOptionParameters(
fulfillmentOptions,
cart
);
const paymentAmount = getPaymentAmount(cart);
const { paymentId, version } = paymentSummaryStore.getValue();
const paymentUpdateRequest = {
paymentId,
amount: paymentAmount,
taxTotal: cart.cartPricing.totalTax,
includedTaxTotal: cart.cartPricing.includedTaxAmount,
gatewayUpdateParams: {
paymentData: component.paymentData,
deliveryMethods: shippingOptions,
},
} as UpdatePaymentRequest;
try {
const paymentVersionToUpdate = getLatestPaymentVersionByPaymentId(
paymentId,
version,
cart.cartAlerts
);
const updatedPaymentSummary = await handleUpdatePaymentInfo(
paymentUpdateRequest,
paymentVersionToUpdate
);
paymentSummaryStore.setValue({
paymentId: updatedPaymentSummary.paymentId,
version: updatedPaymentSummary.version,
});
component.updatePaymentData(
get(updatedPaymentSummary.gatewayUpdateResponse, 'paymentData')
);
} catch (err) {
console.error('EXCEPTION FOR PAYMENT UPDATE:', err);
component.setStatus('error');
component.unmount();
}
}, []);
};
const getLatestPaymentVersionByPaymentId = (
paymentId: string,
currentVersion: number,
alerts?: CartAlert[]
): number => {
if (isEmpty(alerts)) {
return currentVersion;
}
let latestVersion = currentVersion;
for (const alert of alerts) {
const updates = alert.additionalAttributes?.['UPDATED_PAYMENTS'];
if (alert.type !== 'PRICING_CHANGE' || isEmpty(updates)) {
continue;
}
const updatedPayment = find(updates, update => {
return update.paymentId === paymentId;
});
if (updatedPayment?.version > latestVersion) {
latestVersion = updatedPayment.version;
}
}
return latestVersion;
};