Skip to content

Commit

Permalink
Merge branch 'main' into v6
Browse files Browse the repository at this point in the history
# Conflicts:
#	packages/e2e-playwright/package.json
#	packages/e2e/package.json
#	packages/lib/.eslintrc.cjs
#	packages/lib/package.json
#	packages/lib/src/components/Ach/components/AchInput/AchInput.test.tsx
#	packages/lib/src/components/Ach/components/AchInput/components/AchSFInput.tsx
#	packages/lib/src/components/ApplePay/ApplePay.tsx
#	packages/lib/src/components/Card/Card.Analytics.test.tsx
#	packages/lib/src/components/Card/Card.test.tsx
#	packages/lib/src/components/Card/Card.tsx
#	packages/lib/src/components/Card/components/CardInput/CardInput.tsx
#	packages/lib/src/components/Card/components/CardInput/components/CardFieldsWrapper.tsx
#	packages/lib/src/components/Card/components/CardInput/components/types.ts
#	packages/lib/src/components/Card/components/CardInput/types.ts
#	packages/lib/src/components/Card/types.ts
#	packages/lib/src/components/CustomCard/CustomCard.tsx
#	packages/lib/src/components/Donation/Donation.tsx
#	packages/lib/src/components/Donation/components/types.ts
#	packages/lib/src/components/Donation/types.ts
#	packages/lib/src/components/Dropin/components/DropinComponent.tsx
#	packages/lib/src/components/GooglePay/GooglePay.test.ts
#	packages/lib/src/components/GooglePay/GooglePay.tsx
#	packages/lib/src/components/ThreeDS2/ThreeDS2Challenge.test.tsx
#	packages/lib/src/components/ThreeDS2/ThreeDS2Challenge.tsx
#	packages/lib/src/components/ThreeDS2/ThreeDS2DeviceFingerprint.test.tsx
#	packages/lib/src/components/ThreeDS2/ThreeDS2DeviceFingerprint.tsx
#	packages/lib/src/components/ThreeDS2/components/Challenge/DoChallenge3DS2.tsx
#	packages/lib/src/components/ThreeDS2/components/Challenge/PrepareChallenge3DS2.tsx
#	packages/lib/src/components/ThreeDS2/components/Challenge/types.ts
#	packages/lib/src/components/ThreeDS2/components/DeviceFingerprint/PrepareFingerprint3DS2.test.tsx
#	packages/lib/src/components/ThreeDS2/components/DeviceFingerprint/PrepareFingerprint3DS2.tsx
#	packages/lib/src/components/ThreeDS2/components/DeviceFingerprint/types.ts
#	packages/lib/src/components/ThreeDS2/components/Form/ThreeDS2Form.test.tsx
#	packages/lib/src/components/ThreeDS2/types.ts
#	packages/lib/src/components/UIElement.tsx
#	packages/lib/src/components/WeChat/WeChat.ts
#	packages/lib/src/components/helpers/IssuerListContainer/IssuerListContainer.tsx
#	packages/lib/src/components/internal/Address/types.ts
#	packages/lib/src/components/internal/BaseElement/BaseElement.ts
#	packages/lib/src/components/internal/IssuerList/IssuerList.test.tsx
#	packages/lib/src/components/internal/IssuerList/types.ts
#	packages/lib/src/components/internal/SecuredFields/SFP/SecuredFieldsProvider.ts
#	packages/lib/src/components/internal/SecuredFields/lib/configuration/constants.ts
#	packages/lib/src/components/internal/SecuredFields/utils.ts
#	packages/lib/src/components/types.ts
#	packages/lib/src/core/Analytics/Analytics.test.ts
#	packages/lib/src/core/Analytics/Analytics.ts
#	packages/lib/src/core/Analytics/EventsQueue.test.ts
#	packages/lib/src/core/Analytics/analyticsPreProcessor.ts
#	packages/lib/src/core/Analytics/constants.ts
#	packages/lib/src/core/Analytics/types.ts
#	packages/lib/src/core/Analytics/utils.ts
#	packages/lib/src/core/ProcessResponse/PaymentAction/actionTypes.ts
#	packages/lib/src/core/Services/analytics/collect-id.test.ts
#	packages/lib/src/core/Services/analytics/collect-id.ts
#	packages/lib/src/core/Services/analytics/types.ts
#	packages/lib/src/core/core.ts
#	packages/lib/src/core/types.ts
#	packages/lib/src/utils/textUtils.ts
#	packages/lib/tsconfig.json
#	packages/playground/package.json
#	packages/playground/src/pages/Cards/Cards.js
#	packages/playground/src/pages/Dropin/session.js
#	packages/playground/src/pages/Helpers/Helpers.js
#	yarn.lock
  • Loading branch information
sponglord committed Mar 12, 2024
2 parents 0dcb3b6 + 3ce12ef commit b0702c5
Show file tree
Hide file tree
Showing 45 changed files with 426 additions and 76 deletions.
5 changes: 0 additions & 5 deletions .changeset/heavy-forks-peel.md

This file was deleted.

4 changes: 2 additions & 2 deletions .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ jobs:
MERCHANT_ACCOUNT: ${{secrets.ADYEN_CHECKOUT_MERCHANT_ACCOUNT}}
CLIENT_KEY: ${{secrets.ADYEN_CHECKOUT_CLIENT_KEY}}
CLIENT_ENV: test
TESTING_ENVIRONMENT: https://checkout-test.adyen.com/checkout/v70
API_VERSION: v70
TESTING_ENVIRONMENT: https://checkout-test.adyen.com/checkout/v71
API_VERSION: v71
- name: Archive test result artifacts
if: always()
uses: actions/upload-artifact@v4
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Dropin } from '../dropin';
import { getImageCount } from '../../tests/utils/image';

export const getCreditCardPM = (dropin: Dropin) => {
const creditCard = dropin.getPaymentMethodItem('Credit Card');
const creditCard = dropin.getPaymentMethodItem('Cards');

const brandsHolder = creditCard.locator('.adyen-checkout__payment-method__brands');

Expand Down
4 changes: 1 addition & 3 deletions packages/e2e/tests/vouchers/boleto/boleto.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,7 @@ const mockData = {
houseNumberOrName: '123',
city: 'Sao Paulo',
postalCode: '11111555',
stateOrProvince: 'SP',
firstName: 'N/A',
lastName: 'N/A'
stateOrProvince: 'SP'
},
shopperEmail: '[email protected]'
};
Expand Down
26 changes: 26 additions & 0 deletions packages/lib/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,31 @@
# @adyen/adyen-web

## 5.60.0

### Minor Changes

- Add support for new Donation properties. ([#2572](https://github.com/Adyen/adyen-web/pull/2572))

- Starting using /checkoutanalytics endpoint to retrieve "checkoutAttemptId" log "submit" and "action-handled" events ([#2234](https://github.com/Adyen/adyen-web/pull/2234))

### Patch Changes

- Sanitize `billingAddress` when making a `/payments` call. Remove `firstName` and `lastName` in the `billingAddress` for non Riverty payments. ([#2591](https://github.com/Adyen/adyen-web/pull/2591))

- For regular card, zero auth payments, we store the payment method only if the configuration says we should ([#2589](https://github.com/Adyen/adyen-web/pull/2589))

- Updating applepay typescript types and fixing challengeWindowSize prop ([#2584](https://github.com/Adyen/adyen-web/pull/2584))

## 5.59.0

### Minor Changes

- Adding support for PayPal Express Flow ([#2551](https://github.com/Adyen/adyen-web/pull/2551))

### Patch Changes

- For zero auth payments, we always send the `storePaymentMethod` to save the payment details. ([#2571](https://github.com/Adyen/adyen-web/pull/2571))

## 5.58.0

### Minor Changes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ describe('AchInput', () => {
});

test('should render StoreDetails when enabled', async () => {
withCoreProvider(<AchInput enableStoreDetails resources={global.resources} />);
withCoreProvider(<AchInput enableStoreDetails={true} resources={global.resources} />);
expect(await screen.findByText(/save for my next payment/i)).toBeTruthy();
});

Expand Down
13 changes: 9 additions & 4 deletions packages/lib/src/components/ApplePay/ApplePay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import { APPLEPAY_SESSION_ENDPOINT } from './config';
import { preparePaymentRequest } from './payment-request';
import { resolveSupportedVersion, mapBrands, formatApplePayContactToAdyenAddressFormat } from './utils';
import AdyenCheckoutError from '../../core/Errors/AdyenCheckoutError';
import { ANALYTICS_INSTANT_PAYMENT_BUTTON, ANALYTICS_SELECTED_STR } from '../../core/Analytics/constants';
import { DecodeObject } from '../types';
import { TxVariants } from '../tx-variants';
import { sanitizeResponse, verifyPaymentDidNotFail } from '../internal/UIElement/utils';
import { ANALYTICS_INSTANT_PAYMENT_BUTTON, ANALYTICS_SELECTED_STR } from '../../core/Analytics/constants';
Expand Down Expand Up @@ -222,10 +224,13 @@ class ApplePayElement extends UIElement<ApplePayConfiguration> {

try {
const response = await httpPost(options, request);
const decodedData = base64.decode(response.data);
if (!decodedData) reject('Could not decode Apple Pay session');
const session = JSON.parse(decodedData as string);
resolve(session);
const decodedData: DecodeObject = base64.decode(response.data);
if (!decodedData.success) {
reject('Could not decode Apple Pay session');
} else {
const session = JSON.parse(decodedData.data);
resolve(session);
}
} catch (e) {
reject('Could not get Apple Pay session');
}
Expand Down
1 change: 0 additions & 1 deletion packages/lib/src/components/ApplePay/payment-request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ export const preparePaymentRequest = (paymentRequest): ApplePayJS.ApplePayPaymen
shippingMethods: props.shippingMethods,
shippingType: props.shippingType,

// @ts-ignore 'recurringPaymentRequest' isn't defined in the @types/applepayjs
recurringPaymentRequest: props.recurringPaymentRequest,

merchantCapabilities: props.merchantCapabilities,
Expand Down
9 changes: 1 addition & 8 deletions packages/lib/src/components/ApplePay/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,14 +103,7 @@ export interface ApplePayConfiguration extends UIElementProps {
* ApplePayRecurringPaymentRequest - Represents a request to set up a recurring payment, typically a subscription.
* {@link https://developer.apple.com/documentation/apple_pay_on_the_web/applepayrecurringpaymentrequest}
*/
recurringPaymentRequest?: {
paymentDescription: string;
regularBilling: ApplePayJS.ApplePayLineItem;
trialBilling?: ApplePayJS.ApplePayLineItem;
billingAgreement?: string;
managementURL: string;
tokenNotificationURL?: string;
};
recurringPaymentRequest?: ApplePayJS.ApplePayRecurringPaymentRequest;

// Requested Billing and Shipping Contact Information

Expand Down
54 changes: 52 additions & 2 deletions packages/lib/src/components/Card/Card.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,10 @@ describe('Card', () => {
expect(card.props.countryCode).toEqual('kr');
});

test('should return false for enableStoreDetails in case of zero-auto transaction', () => {
test('should return false for showStoreDetailsCheckbox in case of zero-auto transaction, whilst preserving the original value of enableStoreDetail', () => {
const card = new CardElement(global.core, { amount: { value: 0, currency: 'eur' }, enableStoreDetails: true });
expect(card.props.enableStoreDetails).toEqual(false);
expect(card.props.enableStoreDetails).toEqual(true);
expect(card.props.showStoreDetailsCheckbox).toEqual(false);
});
});

Expand Down Expand Up @@ -72,15 +73,64 @@ describe('Card', () => {

test('should return storePaymentMethod if enableStoreDetails is enabled', () => {
const card = new CardElement(global.core, { enableStoreDetails: true });
expect(card.props.showStoreDetailsCheckbox).toEqual(true);
card.setState({ storePaymentMethod: true });
expect(card.data.storePaymentMethod).toBe(true);
});

test('should not return storePaymentMethod if enableStoreDetails is disabled', () => {
const card = new CardElement(global.core, { enableStoreDetails: false });
expect(card.props.showStoreDetailsCheckbox).toEqual(false);
card.setState({ storePaymentMethod: true });
expect(card.data.storePaymentMethod).not.toBeDefined();
});

test('should only return storePaymentMethod:true for regular card, zero auth payments, *if* the conditions are right', () => {
// Manual flow
expect(new CardElement(global.core, { amount: { value: 0 }, enableStoreDetails: true }).data.storePaymentMethod).toBe(true);
expect(new CardElement(global.core, { amount: { value: 0 }, enableStoreDetails: false }).data.storePaymentMethod).toBe(undefined);

// Session flow - session configuration should override merchant configuration
let cardElement = new CardElement(global.core, {
amount: { value: 0 },
enableStoreDetails: false,
session: { configuration: { enableStoreDetails: true } }
});
expect(cardElement.data.storePaymentMethod).toBe(true);

cardElement = new CardElement(global.core, {
amount: { value: 0 },
enableStoreDetails: true,
session: { configuration: { enableStoreDetails: false } }
});
expect(cardElement.data.storePaymentMethod).toBe(undefined);
});

test('should return storePaymentMethod based on the checkbox value, for regular card, non-zero auth payments', () => {
const card = new CardElement(global.core, { amount: { value: 10 }, enableStoreDetails: true });
card.setState({ storePaymentMethod: true });
expect(card.data.storePaymentMethod).toBe(true);
card.setState({ storePaymentMethod: false });
expect(card.data.storePaymentMethod).toBe(false);
});

test('should not return storePaymentMethod for stored card, non-zero auth payments', () => {
expect(
new CardElement(global.core, { amount: { value: 10 }, storedPaymentMethodId: 'xxx', enableStoreDetails: true }).data.storePaymentMethod
).not.toBeDefined();
expect(
new CardElement(global.core, { amount: { value: 10 }, storedPaymentMethodId: 'xxx', enableStoreDetails: false }).data.storePaymentMethod
).not.toBeDefined();
});

test('should not return storePaymentMethod for stored card, zero auth payments', () => {
expect(
new CardElement(global.core, { amount: { value: 0 }, storedPaymentMethodId: 'xxx', enableStoreDetails: true }).data.storePaymentMethod
).not.toBeDefined();
expect(
new CardElement(global.core, { amount: { value: 0 }, storedPaymentMethodId: 'xxx', enableStoreDetails: false }).data.storePaymentMethod
).not.toBeDefined();
});
});

describe('isValid', () => {
Expand Down
36 changes: 32 additions & 4 deletions packages/lib/src/components/Card/Card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,11 @@ export class CardElement extends UIElement<CardConfiguration> {
};

formatProps(props: CardConfiguration) {
// The value from a session should be used, before falling back to the merchant configuration
const enableStoreDetails = props.session?.configuration?.enableStoreDetails ?? props.enableStoreDetails;

const isZeroAuth = props.amount?.value === 0;
const enableStoreDetails = isZeroAuth ? false : props.session?.configuration?.enableStoreDetails || props.enableStoreDetails;
const showStoreDetailsCheckbox = isZeroAuth ? false : enableStoreDetails;

const storedCardID = props.storedPaymentMethodId || props.id; // check if we've been passed a (checkout) processed storedCard or one that merchant has pulled from the PMs response
const isEcommerceStoredCard = storedCardID && props?.supportedShopperInteractions?.includes('Ecommerce'); // If we have a storedCard does it support Ecommerce (it might not if the merchant has pulled it from the PMs response)
Expand Down Expand Up @@ -110,6 +113,7 @@ export class CardElement extends UIElement<CardConfiguration> {
// installmentOptions of a session should be used before falling back to the merchant configuration
installmentOptions: props.session?.configuration?.installmentOptions || props.installmentOptions,
enableStoreDetails,
showStoreDetailsCheckbox,
/**
* Click to Pay configuration
* - If email is set explicitly in the configuration, then it can override the one used in the session creation
Expand All @@ -135,8 +139,7 @@ export class CardElement extends UIElement<CardConfiguration> {
* - when /binLookup detects a dual-branded card and the shopper makes a brand selection
* - or, in the case of a storedCard
*/
const cardBrand = this.state.selectedBrandValue;
const includeStorePaymentMethod = this.props.enableStoreDetails && typeof this.state.storePaymentMethod !== 'undefined';
const cardBrand = this.state.selectedBrandValue; // || this.props.brand; v5_into_v6

return {
paymentMethod: {
Expand All @@ -148,7 +151,7 @@ export class CardElement extends UIElement<CardConfiguration> {
},
...(this.state.billingAddress && { billingAddress: this.state.billingAddress }),
...(this.state.socialSecurityNumber && { socialSecurityNumber: this.state.socialSecurityNumber }),
...(includeStorePaymentMethod && { storePaymentMethod: Boolean(this.state.storePaymentMethod) }),
...this.storePaymentMethodPayload,
...(hasValidInstallmentsObject(this.state.installments) && { installments: this.state.installments }),
browserInfo: this.browserInfo,
origin: !!window && window.location.origin
Expand Down Expand Up @@ -255,6 +258,31 @@ export class CardElement extends UIElement<CardConfiguration> {

public onBinValue = triggerBinLookUp(this);

get storePaymentMethodPayload() {
const isStoredCard = this.props.storedPaymentMethodId?.length > 0;
if (isStoredCard) {
return {};
}

/**
* For regular card, zero auth payments, we store the payment method, *if* the configuration says we should:
* - For sessions, this means if the session has been created with storePaymentMethodMode: 'askForConsent'
* - For the advanced flow, this means if the merchant has still set enableStoreDetails: true
*
* What we are doing is.. if for a normal payment we would show the "Save for my next payment" checkbox,
* for a zero-auth payment we effectively click the checkbox on behalf of the shopper.
*/
const isZeroAuth = this.props.amount?.value === 0;
if (isZeroAuth) {
return this.props.enableStoreDetails ? { storePaymentMethod: true } : {};
}

// For regular card, non-zero auth payments, we store the payment method based on the checkbox value.
// const includeStorePaymentMethod = this.props.enableStoreDetails && typeof this.state.storePaymentMethod !== 'undefined';
const includeStorePaymentMethod = this.props.showStoreDetailsCheckbox && typeof this.state.storePaymentMethod !== 'undefined';
return includeStorePaymentMethod ? { storePaymentMethod: Boolean(this.state.storePaymentMethod) } : {};
}

get isValid() {
return !!this.state.isValid;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export const CardFieldsWrapper = ({
billingAddressAllowedCountries,
billingAddressValidationRules = null,
brandsConfiguration,
enableStoreDetails,
showStoreDetailsCheckbox,
hasCVC,
hasHolderName,
holderNameRequired,
Expand Down Expand Up @@ -147,7 +147,7 @@ export const CardFieldsWrapper = ({
</div>
)}

{enableStoreDetails && <StoreDetails onChange={handleOnStoreDetails} />}
{showStoreDetailsCheckbox && <StoreDetails onChange={handleOnStoreDetails} />}

{hasInstallments && (
<Installments
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ export interface CardInputProps {
showFormInstruction?: boolean;
showInstallmentAmounts?: boolean;
showPayButton?: boolean;
showStoreDetailsCheckbox?: boolean;
showWarnings?: boolean;
showContextualElement?: boolean;
specifications?: Specifications;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ export const extractPropsForCardFields = (props: CardInputProps) => {
billingAddressRequiredFields: props.billingAddressRequiredFields,
billingAddressAllowedCountries: props.billingAddressAllowedCountries,
brandsConfiguration: props.brandsConfiguration,
enableStoreDetails: props.enableStoreDetails,
showStoreDetailsCheckbox: props.showStoreDetailsCheckbox,
hasCVC: props.hasCVC,
hasHolderName: props.hasHolderName,
holderNameRequired: props.holderNameRequired,
Expand Down
11 changes: 9 additions & 2 deletions packages/lib/src/components/Card/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,17 @@ export interface CardConfiguration extends UIElementProps {
*/
showContextualElement?: boolean;

/** Show/hide the "store details" checkbox */
/** Config option related to whether we set storePaymentMethod in the card data, and showing/hiding the "store details" checkbox */
enableStoreDetails?: boolean;

/** Show/hide the Security Code field - merchant set config option */
/**
* Show/hide the "store details" checkbox
* @internal
*/
showStoreDetailsCheckbox?: boolean;

/** Show/hide the CVC field - merchant set config option */
hideCVC?: boolean;

/**
Expand Down Expand Up @@ -151,7 +158,7 @@ export interface CardConfiguration extends UIElementProps {
onFocus?: (event: CbObjOnFocus | ComponentFocusObject) => void;

/**
* Called when a field gains loses focus.
* Called when a field loses focus.
*/
onBlur?: (event: CbObjOnFocus | ComponentFocusObject) => void;

Expand Down
Loading

0 comments on commit b0702c5

Please sign in to comment.