Skip to content

Commit

Permalink
Support New displayOnly Confg Option (#2252)
Browse files Browse the repository at this point in the history
* add new vaultable logic to eligible funding

* add displayOnly to button props

* remove unneeded prop

* fix lint

* refactor. thanks shane

* add some basic unit tests for funding eligibility

* clean up mock funding options

* change const name & update constants

* fix lint

* make flow types, lint, and logic work together

* add new test

* update sdk-constants version

---------

Co-authored-by: Spencer Sablan <[email protected]>
  • Loading branch information
spencersablan and Spencer Sablan authored Oct 17, 2023
1 parent 3a76edc commit bcd4e50
Show file tree
Hide file tree
Showing 6 changed files with 211 additions and 3 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@
"@paypal/common-components": "^1.0.35",
"@paypal/funding-components": "^1.0.31",
"@paypal/sdk-client": "^4.0.176",
"@paypal/sdk-constants": "^1.0.128",
"@paypal/sdk-constants": "^1.0.133",
"@paypal/sdk-logos": "^2.2.6"
},
"lint-staged": {
Expand Down
54 changes: 52 additions & 2 deletions src/funding/funding.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
/* @flow */

import type { FundingEligibilityType } from "@paypal/sdk-client/src";
import { PLATFORM, FUNDING, COMPONENTS } from "@paypal/sdk-constants/src";
import type {
FundingEligibilityType,
CardEligibility,
} from "@paypal/sdk-client/src";
import {
PLATFORM,
FUNDING,
COMPONENTS,
DISPLAY_ONLY_VALUES,
} from "@paypal/sdk-constants/src";
import { SUPPORTED_FUNDING_SOURCES } from "@paypal/funding-components/src";

import type { Wallet, Experiment } from "../types";
Expand Down Expand Up @@ -30,8 +38,36 @@ type IsFundingEligibleOptions = {|
supportsPopups: boolean,
supportedNativeBrowser: boolean,
experiment?: Experiment,
displayOnly?: $ReadOnlyArray<$Values<typeof DISPLAY_ONLY_VALUES>>,
|};

function isFundingVaultable({
fundingEligibility,
fundingSource,
}: {|
fundingEligibility: FundingEligibilityType,
fundingSource: $Values<typeof FUNDING>,
|}): boolean {
// fundingEligibility.card doesn't give vaultable property like other funding sources
if (
fundingSource === FUNDING.CARD &&
fundingEligibility[fundingSource]?.vendors
) {
const { vendors } = (fundingEligibility[fundingSource]: CardEligibility);

// If any vendors are both eligible & vaultable, card is vaultable
return Object.keys(vendors).some(
(vendor) => vendors[vendor]?.eligible && vendors[vendor]?.vaultable
);
}

if (!fundingEligibility[fundingSource]?.vaultable) {
return false;
}

return true;
}

export function isFundingEligible(
source: $Values<typeof FUNDING>,
{
Expand All @@ -50,6 +86,7 @@ export function isFundingEligible(
supportsPopups,
supportedNativeBrowser,
experiment,
displayOnly,
}: IsFundingEligibleOptions
): boolean {
if (!fundingEligibility[source] || !fundingEligibility[source].eligible) {
Expand All @@ -70,6 +107,16 @@ export function isFundingEligible(
return false;
}

const shouldDisplayOnlyVaultableButtons =
displayOnly && displayOnly.includes("vaultable");

if (
shouldDisplayOnlyVaultableButtons &&
!isFundingVaultable({ fundingEligibility, fundingSource: source })
) {
return false;
}

if (
fundingConfig.eligible &&
!fundingConfig.eligible({
Expand Down Expand Up @@ -152,6 +199,7 @@ export function determineEligibleFunding({
supportsPopups,
supportedNativeBrowser,
experiment,
displayOnly = [],
}: {|
fundingSource: ?$Values<typeof FUNDING>,
remembered: $ReadOnlyArray<$Values<typeof FUNDING>>,
Expand All @@ -169,6 +217,7 @@ export function determineEligibleFunding({
supportsPopups: boolean,
supportedNativeBrowser: boolean,
experiment: Experiment,
displayOnly?: $ReadOnlyArray<$Values<typeof DISPLAY_ONLY_VALUES>>,
|}): $ReadOnlyArray<$Values<typeof FUNDING>> {
if (fundingSource) {
return [fundingSource];
Expand All @@ -191,6 +240,7 @@ export function determineEligibleFunding({
supportsPopups,
supportedNativeBrowser,
experiment,
displayOnly,
})
);

Expand Down
140 changes: 140 additions & 0 deletions src/funding/funding.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
/* @flow */
import { COMPONENTS, FUNDING } from "@paypal/sdk-constants/src";
import { describe, expect } from "vitest";

import { BUTTON_FLOW } from "../constants";

import { isFundingEligible } from "./funding";

const defaultMockFundingOptions = {
platform: "desktop",
components: [COMPONENTS.BUTTONS],
flow: BUTTON_FLOW.PURCHASE,
fundingSource: FUNDING.SEPA,
fundingEligibility: {
paylater: {
eligible: true,
vaultable: false,
},
venmo: {
eligible: true,
vaultable: true,
branded: false,
},
sepa: {
eligible: false,
branded: false,
},
oxxo: {
eligible: false,
vaultable: true,
branded: false,
},
card: {
eligible: true,
branded: false,
vendors: {
visa: {
eligible: true,
vaultable: false,
},
mastercard: {
eligible: true,
vaultable: false,
},
amex: {
eligible: true,
vaultable: false,
},
},
},
},
applePaySupport: false,
supportsPopups: true,
supportedNativeBrowser: true,
onShippingChange: null,
onShippingAddressChange: null,
onShippingOptionsChange: null,
};

describe("Funding eligibility", () => {
test("should not be eligible if funding source is missing from fundingEligibility", () => {
const fundingEligible = isFundingEligible(
FUNDING.WECHATPAY,
defaultMockFundingOptions
);

expect(fundingEligible).toBe(false);
});

test("should not be eligible if displayOnly includes 'vaultable' and vaultable is false", () => {
const options = {
...defaultMockFundingOptions,
displayOnly: ["vaultable"],
fundingSource: FUNDING.PAYLATER,
};
const fundingEligible = isFundingEligible(FUNDING.PAYLATER, options);

expect(fundingEligible).toBe(false);
});

test("card should not be eligible if displayOnly includes 'vaultable' and no vendors are vaultable", () => {
const options = {
...defaultMockFundingOptions,
displayOnly: ["vaultable"],
components: [COMPONENTS.BUTTONS],
fundingSource: FUNDING.CARD,
};
const fundingEligible = isFundingEligible(FUNDING.CARD, options);

expect(fundingEligible).toBe(false);
});

test("card should be eligible if displayOnly includes 'vaultable' and any vendor is vaultable", () => {
const options = {
...defaultMockFundingOptions,
displayOnly: ["vaultable"],
fundingSource: FUNDING.CARD,
components: [COMPONENTS.BUTTONS],
platform: "desktop",
fundingEligibility: {
card: {
eligible: true,
branded: false,
vendors: {
visa: {
eligible: true,
vaultable: true,
},
mastercard: {
eligible: true,
vaultable: false,
},
},
},
},
};

const fundingEligible = isFundingEligible(FUNDING.CARD, options);

expect(fundingEligible).toBe(true);
});

test("should not be eligible if fundingSource.eligible is false", () => {
const fundingEligible = isFundingEligible(
FUNDING.SEPA,
defaultMockFundingOptions
);

expect(fundingEligible).toBe(false);
});

test("should not be eligible if fundingSource.eligible is false and fundingSource.vaultable is true", () => {
const fundingEligible = isFundingEligible(
FUNDING.OXXO,
defaultMockFundingOptions
);

expect(fundingEligible).toBe(false);
});
});
2 changes: 2 additions & 0 deletions src/ui/buttons/buttons.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ export function Buttons(props: ButtonsProps): ElementNode {
supportsPopups,
supportedNativeBrowser,
showPayLabel,
displayOnly,
} = normalizeButtonProps(props);
const { layout, shape, tagline } = style;

Expand All @@ -196,6 +197,7 @@ export function Buttons(props: ButtonsProps): ElementNode {
supportsPopups,
supportedNativeBrowser,
experiment,
displayOnly,
});
const multiple = fundingSources.length > 1;

Expand Down
7 changes: 7 additions & 0 deletions src/ui/buttons/props.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
type LocaleType,
CARD,
COMPONENTS,
DISPLAY_ONLY_VALUES,
} from "@paypal/sdk-constants/src";
import { type CrossDomainWindowType } from "@krakenjs/cross-domain-utils/src";
import { LOGO_COLOR } from "@paypal/sdk-logos/src";
Expand Down Expand Up @@ -461,6 +462,7 @@ export type RenderButtonProps = {|
supportsPopups: boolean,
supportedNativeBrowser: boolean,
showPayLabel: boolean,
displayOnly?: $ReadOnlyArray<$Values<typeof DISPLAY_ONLY_VALUES>>,
|};

export type PrerenderDetails = {|
Expand Down Expand Up @@ -518,6 +520,7 @@ export type ButtonProps = {|
meta: {||},
renderedButtons: $ReadOnlyArray<$Values<typeof FUNDING>>,
createVaultSetupToken: CreateVaultSetupToken,
displayOnly?: $ReadOnlyArray<$Values<typeof DISPLAY_ONLY_VALUES>>,
|};

// eslint-disable-next-line flowtype/require-exact-type
Expand Down Expand Up @@ -559,6 +562,7 @@ export type ButtonPropsInputs = {
supportsPopups: boolean,
supportedNativeBrowser: boolean,
showPayLabel: boolean,
displayOnly: $ReadOnlyArray<$Values<typeof DISPLAY_ONLY_VALUES>>,
};

export const DEFAULT_STYLE = {
Expand Down Expand Up @@ -744,6 +748,7 @@ export function normalizeButtonProps(
supportsPopups = false,
supportedNativeBrowser = false,
showPayLabel = true,
displayOnly = [],
} = props;

const { country, lang } = locale;
Expand Down Expand Up @@ -794,6 +799,7 @@ export function normalizeButtonProps(
applePaySupport,
supportsPopups,
supportedNativeBrowser,
displayOnly,
})
) {
throw new Error(`Funding Source not eligible: ${fundingSource}`);
Expand Down Expand Up @@ -833,5 +839,6 @@ export function normalizeButtonProps(
supportsPopups,
supportedNativeBrowser,
showPayLabel,
displayOnly,
};
}
9 changes: 9 additions & 0 deletions src/zoid/buttons/component.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -833,6 +833,15 @@ export const getButtonsComponent: () => ButtonsComponent = memoize(() => {
required: false,
value: getExperimentation,
},

displayOnly: {
type: "array",
queryParam: true,
required: false,
value: ({ props }) => {
return props?.displayOnly || [];
},
},
},
});
});

0 comments on commit bcd4e50

Please sign in to comment.