@paypal/checkout-components 5.0.329 → 5.0.331

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@paypal/checkout-components",
3
- "version": "5.0.329",
3
+ "version": "5.0.331",
4
4
  "description": "PayPal Checkout components, for integrating checkout products.",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -136,6 +136,7 @@ export type FundingSourceConfig = {|
136
136
  labelText?:
137
137
  | string
138
138
  | (({|
139
+ buyerCountry: $Values<typeof COUNTRY>,
139
140
  content: ?ContentType,
140
141
  fundingEligibility: ?FundingEligibilityType,
141
142
  label?: string,
@@ -45,18 +45,21 @@ export function getPayPalConfig(): FundingSourceConfig {
45
45
 
46
46
  labelText: ({ content, label, period }) => {
47
47
  let text = `${FUNDING_BRAND_LABEL.PAYPAL}`;
48
+
48
49
  if (content && label === BUTTON_LABEL.INSTALLMENT) {
49
50
  if (period) {
50
- text = content["label.installment.withPeriod"].replace(
51
- "{period}",
52
- String(period)
53
- );
51
+ const rawLabel = content["label.installment.withPeriod"];
52
+
53
+ if (typeof rawLabel === "string") {
54
+ text = rawLabel.replace("{period}", String(period));
55
+ }
54
56
  } else {
55
57
  text = content["label.installment.withoutPeriod"];
56
58
  }
57
59
  } else if (content && label && content[`label.${label}`]) {
58
60
  text = content[`label.${label}`];
59
61
  }
62
+
60
63
  return text;
61
64
  },
62
65
  Logo,
@@ -2,7 +2,12 @@
2
2
  /** @jsx node */
3
3
 
4
4
  import type { FundingEligibilityType } from "@paypal/sdk-client/src";
5
- import { FUNDING, ENV, type LocaleType } from "@paypal/sdk-constants/src";
5
+ import {
6
+ COUNTRY,
7
+ FUNDING,
8
+ ENV,
9
+ type LocaleType,
10
+ } from "@paypal/sdk-constants/src";
6
11
  import { node, type ElementNode } from "@krakenjs/jsx-pragmatic/src";
7
12
  import { LOGO_COLOR, LOGO_CLASS } from "@paypal/sdk-logos/src";
8
13
  import {
@@ -37,12 +42,13 @@ import type {
37
42
  } from "./props";
38
43
  import { Spinner } from "./spinner";
39
44
  import { MenuButton } from "./menu-button";
40
- import { isBorderRadiusNumber } from "./util";
45
+ import { isBorderRadiusNumber, checkLabelEligibility } from "./util";
41
46
 
42
47
  type IndividualButtonProps = {|
43
48
  style: ButtonStyle,
44
49
  fundingSource: $Values<typeof FUNDING>,
45
50
  multiple: boolean,
51
+ buyerCountry: $Values<typeof COUNTRY>,
46
52
  locale: LocaleType,
47
53
  onClick?: Function,
48
54
  env: $Values<typeof ENV>,
@@ -68,26 +74,27 @@ type IndividualButtonProps = {|
68
74
  |};
69
75
 
70
76
  export function Button({
71
- fundingSource,
72
- style,
73
- multiple,
74
- locale,
77
+ buyerCountry,
78
+ commit,
79
+ content,
80
+ customerId,
75
81
  env,
82
+ experiment,
83
+ flow,
76
84
  fundingEligibility,
85
+ fundingSource,
77
86
  i,
87
+ instrument,
88
+ locale,
89
+ multiple,
78
90
  nonce,
79
- flow,
80
- vault,
81
- userIDToken,
82
- customerId,
83
- personalization,
84
91
  onClick = noop,
85
- content,
86
- tagline,
87
- commit,
88
- experiment,
89
- instrument,
92
+ personalization,
90
93
  showPayLabel,
94
+ style,
95
+ tagline,
96
+ userIDToken,
97
+ vault,
91
98
  }: IndividualButtonProps): ElementNode {
92
99
  const { layout, shape, borderRadius } = style;
93
100
 
@@ -142,13 +149,16 @@ export function Button({
142
149
  }
143
150
  };
144
151
 
152
+ const eligibleLabel = checkLabelEligibility(label, buyerCountry);
153
+
145
154
  function getAriaLabel(): string {
146
155
  let labelText =
147
156
  typeof fundingConfig.labelText === "function"
148
157
  ? fundingConfig.labelText({
158
+ buyerCountry,
149
159
  content,
150
160
  fundingEligibility,
151
- label,
161
+ label: eligibleLabel,
152
162
  period,
153
163
  })
154
164
  : fundingConfig.labelText || fundingSource;
@@ -166,7 +176,7 @@ export function Button({
166
176
 
167
177
  const logoNode = (
168
178
  <Logo
169
- label={label}
179
+ label={eligibleLabel}
170
180
  locale={locale}
171
181
  logoColor={logoColor}
172
182
  fundingEligibility={fundingEligibility}
@@ -182,7 +192,7 @@ export function Button({
182
192
  <Label
183
193
  i={i}
184
194
  logo={logoNode}
185
- label={label}
195
+ label={eligibleLabel}
186
196
  nonce={nonce}
187
197
  locale={locale}
188
198
  logoColor={logoColor}
@@ -136,36 +136,37 @@ export function validateButtonProps(props: ButtonPropsInputs) {
136
136
  export function Buttons(props: ButtonsProps): ElementNode {
137
137
  const { onClick = noop } = props;
138
138
  const {
139
- wallet,
140
- fundingSource,
141
- style,
142
- locale,
143
- remembered,
139
+ applePaySupport,
140
+ buyerCountry,
141
+ commit,
142
+ components,
143
+ content,
144
+ customerId,
145
+ displayOnly,
146
+ enableFunding,
144
147
  env,
148
+ experiment,
149
+ flow,
145
150
  fundingEligibility,
146
- platform,
147
- commit,
148
- vault,
151
+ fundingSource,
152
+ hasShippingCallback,
153
+ locale,
154
+ message,
155
+ messageMarkup,
149
156
  nonce,
150
- enableFunding,
151
- components,
152
- onShippingChange,
153
157
  onShippingAddressChange,
158
+ onShippingChange,
154
159
  onShippingOptionsChange,
155
- hasShippingCallback,
156
160
  personalization,
157
- userIDToken,
158
- customerId,
159
- content,
160
- flow,
161
- experiment,
162
- applePaySupport,
163
- supportsPopups,
164
- supportedNativeBrowser,
161
+ platform,
162
+ remembered,
165
163
  showPayLabel,
166
- displayOnly,
167
- message,
168
- messageMarkup,
164
+ style,
165
+ supportedNativeBrowser,
166
+ supportsPopups,
167
+ userIDToken,
168
+ vault,
169
+ wallet,
169
170
  } = normalizeButtonProps(props);
170
171
  const { layout, shape, tagline } = style;
171
172
 
@@ -268,6 +269,7 @@ export function Buttons(props: ButtonsProps): ElementNode {
268
269
  fundingSource={source}
269
270
  multiple={multiple}
270
271
  env={env}
272
+ buyerCountry={buyerCountry}
271
273
  locale={locale}
272
274
  nonce={nonce}
273
275
  fundingEligibility={fundingEligibility}
@@ -433,7 +433,7 @@ export type ApplePaySessionConfigRequest = (
433
433
 
434
434
  export type ButtonMessage = {|
435
435
  amount?: number,
436
- offer?: $ReadOnlyArray<$Values<typeof MESSAGE_OFFER>>,
436
+ offer?: string,
437
437
  color: $Values<typeof MESSAGE_COLOR>,
438
438
  position: $Values<typeof MESSAGE_POSITION>,
439
439
  align: $Values<typeof MESSAGE_ALIGN>,
@@ -450,6 +450,7 @@ export type ButtonMessageInputs = {|
450
450
  export type RenderButtonProps = {|
451
451
  style: ButtonStyle,
452
452
  locale: LocaleType,
453
+ buyerCountry: $Values<typeof COUNTRY>,
453
454
  commit: boolean,
454
455
  fundingSource: ?$Values<typeof FUNDING>,
455
456
  env: $Values<typeof ENV>,
@@ -555,6 +556,7 @@ export type ButtonPropsInputs = {
555
556
  clientID: string,
556
557
  fundingSource?: ?$Values<typeof FUNDING>,
557
558
  style?: ButtonStyleInputs | void,
559
+ buyerCountry: $Values<typeof COUNTRY>,
558
560
  locale?: $PropertyType<ButtonProps, "locale"> | void,
559
561
  commit?: $PropertyType<ButtonProps, "commit"> | void,
560
562
  env?: $PropertyType<ButtonProps, "env"> | void,
@@ -780,12 +782,11 @@ export function normalizeButtonMessage(
780
782
  ): ButtonMessage {
781
783
  const {
782
784
  amount,
783
- offer,
784
785
  color = MESSAGE_COLOR.BLACK,
785
786
  position,
786
787
  align = MESSAGE_ALIGN.CENTER,
787
788
  } = message;
788
-
789
+ let offer = message.offer;
789
790
  if (typeof amount !== "undefined") {
790
791
  if (typeof amount !== "number") {
791
792
  throw new TypeError(
@@ -800,6 +801,9 @@ export function normalizeButtonMessage(
800
801
  }
801
802
 
802
803
  if (typeof offer !== "undefined") {
804
+ if (typeof offer === "string") {
805
+ offer = offer.split(",");
806
+ }
803
807
  if (!Array.isArray(offer)) {
804
808
  throw new TypeError(
805
809
  `Expected message.offer to be an array of strings, got: ${String(
@@ -813,6 +817,7 @@ export function normalizeButtonMessage(
813
817
  if (invalidOffers.length > 0) {
814
818
  throw new Error(`Invalid offer(s): ${invalidOffers.join(",")}`);
815
819
  }
820
+ offer = offer.join(",");
816
821
  }
817
822
 
818
823
  if (typeof color !== "undefined" && !values(MESSAGE_COLOR).includes(color)) {
@@ -868,6 +873,7 @@ export function normalizeButtonProps(
868
873
  );
869
874
 
870
875
  let {
876
+ buyerCountry,
871
877
  clientID,
872
878
  fundingSource,
873
879
  style = getDefaultStyle(),
@@ -970,6 +976,7 @@ export function normalizeButtonProps(
970
976
  : undefined;
971
977
 
972
978
  return {
979
+ buyerCountry,
973
980
  clientID,
974
981
  fundingSource,
975
982
  style,
@@ -1,8 +1,7 @@
1
1
  /* @flow */
2
- import { FUNDING } from "@paypal/sdk-constants/src";
2
+ import { COUNTRY, FUNDING } from "@paypal/sdk-constants/src";
3
3
 
4
- import { BUTTON_LAYOUT, MESSAGE_POSITION } from "../../constants";
5
- import { ValidationError } from "../../lib";
4
+ import { BUTTON_LABEL, BUTTON_LAYOUT, MESSAGE_POSITION } from "../../constants";
6
5
 
7
6
  export function isBorderRadiusNumber(borderRadius?: number): boolean {
8
7
  return typeof borderRadius === "number";
@@ -25,8 +24,9 @@ export function calculateMessagePosition(
25
24
  const showPoweredBy = calculateShowPoweredBy(layout, fundingSources);
26
25
 
27
26
  if (showPoweredBy && position === MESSAGE_POSITION.BOTTOM) {
28
- throw new ValidationError(
29
- "Message position must be 'top' when Debit and/or Credit Card button is present"
27
+ // eslint-disable-next-line no-console
28
+ console.warn(
29
+ "PayPal Button Message cannot be positioned at bottom when displaying the Debit or Credit Card button."
30
30
  );
31
31
  }
32
32
 
@@ -39,3 +39,20 @@ export function calculateMessagePosition(
39
39
  }
40
40
  return MESSAGE_POSITION.BOTTOM;
41
41
  }
42
+
43
+ export function checkLabelEligibility(
44
+ label?: $Values<typeof BUTTON_LABEL>,
45
+ buyerCountry: $Values<typeof COUNTRY>
46
+ ): $Values<typeof BUTTON_LABEL> | typeof undefined {
47
+ const eligibleCountriesForInstallmentLabel = ["BR", "MX"];
48
+
49
+ // Don't render the installment label if buyerCountry is not eligible for installment product
50
+ if (
51
+ label === BUTTON_LABEL.INSTALLMENT &&
52
+ !eligibleCountriesForInstallmentLabel.includes(buyerCountry)
53
+ ) {
54
+ return BUTTON_LABEL.PAYPAL;
55
+ }
56
+
57
+ return label;
58
+ }
@@ -743,7 +743,11 @@ export const getButtonsComponent: () => ButtonsComponent = memoize(() => {
743
743
  })
744
744
  .flush();
745
745
 
746
- const modalInstance = await getModal(clientID, merchantID);
746
+ const modalInstance = await getModal(
747
+ clientID,
748
+ merchantID,
749
+ buttonSessionID
750
+ );
747
751
  return modalInstance?.show({
748
752
  amount,
749
753
  offer: offerType,
@@ -758,11 +762,10 @@ export const getButtonsComponent: () => ButtonsComponent = memoize(() => {
758
762
  required: false,
759
763
  value: ({ props }) => {
760
764
  return () => {
761
- const { clientID, merchantID } = props;
762
765
  // offerType, messageType, offerCountryCode, and creditProductIdentifier are passed in and may be used in an upcoming message hover logging feature
763
-
764
766
  // lazy loads the modal, to be memoized and executed onMessageClick
765
- return getModal(clientID, merchantID);
767
+ const { buttonSessionID, clientID, merchantID } = props;
768
+ return getModal(clientID, merchantID, buttonSessionID);
766
769
  };
767
770
  },
768
771
  },
@@ -14,7 +14,6 @@ import {
14
14
  once,
15
15
  memoize,
16
16
  } from "@krakenjs/belter/src";
17
- import { FUNDING } from "@paypal/sdk-constants/src";
18
17
  import {
19
18
  getEnableFunding,
20
19
  getLogger,
@@ -25,6 +24,7 @@ import {
25
24
  getEnv,
26
25
  getNamespace,
27
26
  } from "@paypal/sdk-client/src";
27
+ import { FUNDING, FPTI_KEY } from "@paypal/sdk-constants/src";
28
28
  import { getRefinedFundingEligibility } from "@paypal/funding-components/src";
29
29
 
30
30
  import type { Experiment as EligibilityExperiment } from "../../types";
@@ -371,8 +371,9 @@ function buildModalBundleUrl(): string {
371
371
 
372
372
  export const getModal: (
373
373
  clientID: string,
374
- merchantID?: $ReadOnlyArray<string> | void
375
- ) => Object = memoize(async (clientID, merchantID) => {
374
+ merchantID: $ReadOnlyArray<string> | void,
375
+ buttonSessionID: string
376
+ ) => Object = memoize(async (clientID, merchantID, buttonSessionID) => {
376
377
  try {
377
378
  const namespace = getNamespace();
378
379
  if (!window[namespace].MessagesModal) {
@@ -393,6 +394,19 @@ export const getModal: (
393
394
  }
394
395
 
395
396
  return window[namespace].MessagesModal({
397
+ buttonSessionId: buttonSessionID,
398
+ onApply: () =>
399
+ getLogger()
400
+ .info("button_message_modal_apply")
401
+ .track({
402
+ [FPTI_KEY.TRANSITION]: "button_message_modal_apply",
403
+ [FPTI_KEY.STATE]: "BUTTON_MESSAGE",
404
+ [FPTI_KEY.BUTTON_SESSION_UID]: buttonSessionID,
405
+ [FPTI_KEY.CONTEXT_ID]: buttonSessionID,
406
+ [FPTI_KEY.CONTEXT_TYPE]: "button_session_id",
407
+ [FPTI_KEY.EVENT_NAME]: "modal_apply",
408
+ })
409
+ .flush(),
396
410
  account: `client-id:${clientID}`,
397
411
  merchantId: merchantID?.join(",") || undefined,
398
412
  });
@@ -50,6 +50,7 @@ type CardFieldsProps = {|
50
50
  clientID: string,
51
51
  style?: {|
52
52
  height: number,
53
+ input: {| height: number |},
53
54
  |},
54
55
  env?: string,
55
56
  locale?: string,
@@ -128,9 +129,10 @@ const url = () =>
128
129
  `${getPayPalDomain()}${__PAYPAL_CHECKOUT__.__URI__.__CARD_FIELD__}`;
129
130
 
130
131
  const prerenderTemplate = ({ props, doc }) => {
131
- return (
132
- <CardPrerender nonce={props.nonce} height={props.style?.height} />
133
- ).render(dom({ doc }));
132
+ const height = props.style?.height ?? props.style?.input?.height ?? null;
133
+ return (<CardPrerender nonce={props.nonce} height={height} />).render(
134
+ dom({ doc })
135
+ );
134
136
  };
135
137
 
136
138
  export type CardFieldsComponent = ZoidComponent<
@@ -295,6 +295,12 @@ export function getVenmoCheckoutComponent(): VenmoCheckoutComponent {
295
295
  queryParam: true,
296
296
  required: false,
297
297
  },
298
+
299
+ venmoVaultEnabled: {
300
+ type: "boolean",
301
+ queryParam: true,
302
+ required: false,
303
+ },
298
304
  },
299
305
 
300
306
  dimensions: ({ props }) => {