@paypal/checkout-components 5.0.436 → 5.0.437-alpha-4d75517.0
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
|
@@ -116,7 +116,7 @@ export type ButtonsComponent = ZoidComponent<
|
|
|
116
116
|
ButtonProps,
|
|
117
117
|
void,
|
|
118
118
|
void,
|
|
119
|
-
ButtonExtensions
|
|
119
|
+
ButtonExtensions
|
|
120
120
|
>;
|
|
121
121
|
|
|
122
122
|
// $FlowIssue
|
|
@@ -195,7 +195,7 @@ export const getButtonsComponent: () => ButtonsComponent = memoize(() => {
|
|
|
195
195
|
try {
|
|
196
196
|
const cplPhases = prepareInstrumentationPayload(
|
|
197
197
|
buttonSessionID,
|
|
198
|
-
"buttons"
|
|
198
|
+
"buttons"
|
|
199
199
|
);
|
|
200
200
|
const cplLatencyMetrics = {
|
|
201
201
|
[FPTI_KEY.STATE]: "CPL_LATENCY_METRICS",
|
|
@@ -239,6 +239,11 @@ export const getButtonsComponent: () => ButtonsComponent = memoize(() => {
|
|
|
239
239
|
return {
|
|
240
240
|
iframe: {
|
|
241
241
|
allowpaymentrequest: "allowpaymentrequest",
|
|
242
|
+
// Named origin, not bare "payment": zoid never sets this iframe's `src`
|
|
243
|
+
// attribute (it navigates the frame via window.location/form-POST after
|
|
244
|
+
// creation instead), so the 'src' keyword that bare `allow="payment"`
|
|
245
|
+
// resolves to can never match the actual checkout origin.
|
|
246
|
+
allow: `payment ${getPayPalDomain()}`,
|
|
242
247
|
scrolling: "no",
|
|
243
248
|
title: `${FUNDING_BRAND_LABEL.PAYPAL}${fundingSource}`,
|
|
244
249
|
role: "presentation",
|
|
@@ -262,11 +267,11 @@ export const getButtonsComponent: () => ButtonsComponent = memoize(() => {
|
|
|
262
267
|
supportsVenmoPopups = supportsVenmoPopupsUtil(
|
|
263
268
|
buttonExperiments,
|
|
264
269
|
userAgentSupportsPopups(),
|
|
265
|
-
getUserAgent()
|
|
270
|
+
getUserAgent()
|
|
266
271
|
),
|
|
267
272
|
supportedNativeVenmoBrowser = isSupportedNativeVenmoBrowser(
|
|
268
273
|
buttonExperiments,
|
|
269
|
-
getUserAgent()
|
|
274
|
+
getUserAgent()
|
|
270
275
|
),
|
|
271
276
|
experiment = buttonExperiments,
|
|
272
277
|
createBillingAgreement,
|
|
@@ -380,7 +385,7 @@ export const getButtonsComponent: () => ButtonsComponent = memoize(() => {
|
|
|
380
385
|
({ props: { buttonSessionID } }) =>
|
|
381
386
|
({ close }) => {
|
|
382
387
|
const overlay = document.getElementsByName(
|
|
383
|
-
`paypal-overlay-${buttonSessionID}
|
|
388
|
+
`paypal-overlay-${buttonSessionID}`
|
|
384
389
|
)?.[0];
|
|
385
390
|
|
|
386
391
|
if (overlay) {
|
|
@@ -456,7 +461,7 @@ export const getButtonsComponent: () => ButtonsComponent = memoize(() => {
|
|
|
456
461
|
() => {
|
|
457
462
|
window.addEventListener(
|
|
458
463
|
"visibilitychange",
|
|
459
|
-
props.visibilityChangeHandler
|
|
464
|
+
props.visibilityChangeHandler
|
|
460
465
|
);
|
|
461
466
|
},
|
|
462
467
|
},
|
|
@@ -469,7 +474,7 @@ export const getButtonsComponent: () => ButtonsComponent = memoize(() => {
|
|
|
469
474
|
() => {
|
|
470
475
|
window.removeEventListener(
|
|
471
476
|
"visibilitychange",
|
|
472
|
-
props.visibilityChangeHandler
|
|
477
|
+
props.visibilityChangeHandler
|
|
473
478
|
);
|
|
474
479
|
},
|
|
475
480
|
},
|
|
@@ -956,7 +961,7 @@ export const getButtonsComponent: () => ButtonsComponent = memoize(() => {
|
|
|
956
961
|
// $FlowFixMe
|
|
957
962
|
value,
|
|
958
963
|
layout,
|
|
959
|
-
fundingSources
|
|
964
|
+
fundingSources
|
|
960
965
|
);
|
|
961
966
|
},
|
|
962
967
|
},
|
|
@@ -1041,7 +1046,7 @@ export const getButtonsComponent: () => ButtonsComponent = memoize(() => {
|
|
|
1041
1046
|
const modalInstance = await getModal(
|
|
1042
1047
|
clientID,
|
|
1043
1048
|
merchantID,
|
|
1044
|
-
buttonSessionID
|
|
1049
|
+
buttonSessionID
|
|
1045
1050
|
);
|
|
1046
1051
|
return modalInstance?.show({
|
|
1047
1052
|
amount,
|
|
@@ -1137,8 +1142,8 @@ export const getButtonsComponent: () => ButtonsComponent = memoize(() => {
|
|
|
1137
1142
|
value: ({ props }) => {
|
|
1138
1143
|
return Boolean(
|
|
1139
1144
|
props.onShippingChange ||
|
|
1140
|
-
|
|
1141
|
-
|
|
1145
|
+
props.onShippingAddressChange ||
|
|
1146
|
+
props.onShippingOptionsChange
|
|
1142
1147
|
);
|
|
1143
1148
|
},
|
|
1144
1149
|
},
|
|
@@ -1372,7 +1377,7 @@ export const getButtonsComponent: () => ButtonsComponent = memoize(() => {
|
|
|
1372
1377
|
value: ({ props }) => {
|
|
1373
1378
|
return isSupportedNativeVenmoBrowser(
|
|
1374
1379
|
props.experiment,
|
|
1375
|
-
props.userAgent
|
|
1380
|
+
props.userAgent
|
|
1376
1381
|
);
|
|
1377
1382
|
},
|
|
1378
1383
|
queryParam: true,
|
|
@@ -1393,7 +1398,7 @@ export const getButtonsComponent: () => ButtonsComponent = memoize(() => {
|
|
|
1393
1398
|
return supportsVenmoPopupsUtil(
|
|
1394
1399
|
props.experiment,
|
|
1395
1400
|
userAgentSupportsPopups(),
|
|
1396
|
-
props.userAgent
|
|
1401
|
+
props.userAgent
|
|
1397
1402
|
);
|
|
1398
1403
|
},
|
|
1399
1404
|
queryParam: true,
|
|
@@ -29,6 +29,7 @@ vi.mock("@paypal/sdk-client/src", async (importOriginal) => ({
|
|
|
29
29
|
error: vi.fn(),
|
|
30
30
|
})),
|
|
31
31
|
getPayPalDomainRegex: vi.fn(() => /paypal\.com/),
|
|
32
|
+
getPayPalDomain: vi.fn(() => "https://www.paypal.com"),
|
|
32
33
|
getEnv: vi.fn(() => "test"),
|
|
33
34
|
}));
|
|
34
35
|
|
|
@@ -43,7 +44,7 @@ describe("getButtonsComponent iframe title", () => {
|
|
|
43
44
|
const { attributes } = createMock.mock.calls[0][0];
|
|
44
45
|
|
|
45
46
|
expect(attributes({ props: {} }).iframe.title).toBe(
|
|
46
|
-
FUNDING_BRAND_LABEL.PAYPAL
|
|
47
|
+
FUNDING_BRAND_LABEL.PAYPAL
|
|
47
48
|
);
|
|
48
49
|
});
|
|
49
50
|
|
|
@@ -52,7 +53,43 @@ describe("getButtonsComponent iframe title", () => {
|
|
|
52
53
|
const { attributes } = createMock.mock.calls[0][0];
|
|
53
54
|
|
|
54
55
|
expect(attributes({ props: { fundingSource: "venmo" } }).iframe.title).toBe(
|
|
55
|
-
`${FUNDING_BRAND_LABEL.PAYPAL}-venmo
|
|
56
|
+
`${FUNDING_BRAND_LABEL.PAYPAL}-venmo`
|
|
56
57
|
);
|
|
57
58
|
});
|
|
58
59
|
});
|
|
60
|
+
|
|
61
|
+
describe("getButtonsComponent iframe payment permissions policy", () => {
|
|
62
|
+
it("delegates the payment permissions policy to the buttons iframe's actual origin", () => {
|
|
63
|
+
getButtonsComponent();
|
|
64
|
+
const { attributes } = createMock.mock.calls[0][0];
|
|
65
|
+
|
|
66
|
+
expect(attributes({ props: {} }).iframe.allow).toBe(
|
|
67
|
+
"payment https://www.paypal.com"
|
|
68
|
+
);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it("keeps the legacy allowpaymentrequest attribute alongside the modern allow attribute", () => {
|
|
72
|
+
getButtonsComponent();
|
|
73
|
+
const { attributes } = createMock.mock.calls[0][0];
|
|
74
|
+
|
|
75
|
+
const iframeAttrs = attributes({ props: {} }).iframe;
|
|
76
|
+
expect(iframeAttrs.allowpaymentrequest).toBe("allowpaymentrequest");
|
|
77
|
+
expect(iframeAttrs.allow).toBe("payment https://www.paypal.com");
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it("names the origin explicitly rather than relying on the 'src' keyword", () => {
|
|
81
|
+
// zoid's openFrame() never sets this iframe's `src` attribute — it creates
|
|
82
|
+
// the iframe bare, then navigates it later via proxyWin.setLocation(), which
|
|
83
|
+
// uses window.location/form-POST, not the `src` attribute. Permissions
|
|
84
|
+
// Policy's 'src' keyword resolves by reading that attribute, so a bare
|
|
85
|
+
// `allow="payment"` (== `payment 'src'`) can never grant the feature here.
|
|
86
|
+
// Regression test for the retest failure after the original 'payment'-only
|
|
87
|
+
// fix (DTINAPPXO-5124).
|
|
88
|
+
getButtonsComponent();
|
|
89
|
+
const { attributes } = createMock.mock.calls[0][0];
|
|
90
|
+
|
|
91
|
+
const iframeAttrs = attributes({ props: {} }).iframe;
|
|
92
|
+
expect(iframeAttrs.allow).not.toBe("payment");
|
|
93
|
+
expect(iframeAttrs.allow).toContain("https://www.paypal.com");
|
|
94
|
+
});
|
|
95
|
+
});
|
|
@@ -68,13 +68,13 @@ function spyOnWindowNameAssignment(): void {
|
|
|
68
68
|
new Error(
|
|
69
69
|
`window.name was overwritten: ${String(value).slice(
|
|
70
70
|
0,
|
|
71
|
-
WINDOW_NAME_LOG_TRUNCATION_LENGTH
|
|
72
|
-
)}
|
|
73
|
-
)
|
|
71
|
+
WINDOW_NAME_LOG_TRUNCATION_LENGTH
|
|
72
|
+
)}`
|
|
73
|
+
)
|
|
74
74
|
),
|
|
75
75
|
originalValue: String(currentWindowName).slice(
|
|
76
76
|
0,
|
|
77
|
-
WINDOW_NAME_LOG_TRUNCATION_LENGTH
|
|
77
|
+
WINDOW_NAME_LOG_TRUNCATION_LENGTH
|
|
78
78
|
),
|
|
79
79
|
});
|
|
80
80
|
currentWindowName = value;
|
|
@@ -97,6 +97,13 @@ export function getCheckoutComponent(): CheckoutComponent {
|
|
|
97
97
|
attributes: {
|
|
98
98
|
iframe: {
|
|
99
99
|
scrolling: "yes",
|
|
100
|
+
// Delegates the Permissions Policy 'payment' feature so PaymentRequest
|
|
101
|
+
// works when this iframe falls back to CONTEXT.IFRAME (supportsPopups() false).
|
|
102
|
+
// zoid never sets this iframe's `src` attribute (it navigates the frame
|
|
103
|
+
// via window.location/form-POST after creation instead), so the 'src'
|
|
104
|
+
// keyword that bare `allow="payment"` resolves to can never match the
|
|
105
|
+
// actual checkout origin. Name the origin explicitly instead.
|
|
106
|
+
allow: `payment ${getPayPalDomain()}`,
|
|
100
107
|
},
|
|
101
108
|
},
|
|
102
109
|
|
|
@@ -343,8 +350,8 @@ export function getCheckoutComponent(): CheckoutComponent {
|
|
|
343
350
|
value: ({ props }) => {
|
|
344
351
|
return Boolean(
|
|
345
352
|
props.onShippingChange ||
|
|
346
|
-
|
|
347
|
-
|
|
353
|
+
props.onShippingAddressChange ||
|
|
354
|
+
props.onShippingOptionsChange
|
|
348
355
|
);
|
|
349
356
|
},
|
|
350
357
|
},
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/* @flow */
|
|
2
|
+
|
|
3
|
+
import { describe, expect, it, vi } from "vitest";
|
|
4
|
+
import { supportsPopups } from "@krakenjs/belter/src";
|
|
5
|
+
|
|
6
|
+
import { getCheckoutComponent } from "./component";
|
|
7
|
+
|
|
8
|
+
// Capture the config object component.jsx passes to zoid's create() so we can
|
|
9
|
+
// assert on the real (un-exported) attributes.iframe config, mirroring the
|
|
10
|
+
// pattern used in ../buttons/component.test.js.
|
|
11
|
+
const { createMock } = vi.hoisted(() => ({
|
|
12
|
+
createMock: vi.fn((options) => ({ ...options, isChild: () => false })),
|
|
13
|
+
}));
|
|
14
|
+
|
|
15
|
+
vi.mock("@krakenjs/zoid/src", () => ({
|
|
16
|
+
create: createMock,
|
|
17
|
+
CONTEXT: { POPUP: "popup", IFRAME: "iframe" },
|
|
18
|
+
EVENT: { FOCUS: "focus" },
|
|
19
|
+
}));
|
|
20
|
+
|
|
21
|
+
vi.mock("@krakenjs/belter/src", async (importOriginal) => ({
|
|
22
|
+
...(await importOriginal()),
|
|
23
|
+
supportsPopups: vi.fn(() => true),
|
|
24
|
+
}));
|
|
25
|
+
|
|
26
|
+
vi.mock("@paypal/sdk-client/src", async (importOriginal) => ({
|
|
27
|
+
...(await importOriginal()),
|
|
28
|
+
getLogger: vi.fn(() => ({
|
|
29
|
+
metricCounter: vi.fn(),
|
|
30
|
+
track: vi.fn(),
|
|
31
|
+
info: vi.fn(),
|
|
32
|
+
error: vi.fn(),
|
|
33
|
+
})),
|
|
34
|
+
getPayPalDomainRegex: vi.fn(() => /paypal\.com/),
|
|
35
|
+
getPayPalDomain: vi.fn(() => "https://www.paypal.com"),
|
|
36
|
+
}));
|
|
37
|
+
|
|
38
|
+
describe("getCheckoutComponent iframe payment permissions policy", () => {
|
|
39
|
+
it("delegates the payment permissions policy to the checkout iframe's actual origin", () => {
|
|
40
|
+
getCheckoutComponent();
|
|
41
|
+
const { attributes } = createMock.mock.calls[0][0];
|
|
42
|
+
|
|
43
|
+
expect(attributes.iframe.allow).toBe("payment https://www.paypal.com");
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("keeps the existing scrolling attribute alongside the new allow attribute", () => {
|
|
47
|
+
getCheckoutComponent();
|
|
48
|
+
const { attributes } = createMock.mock.calls[0][0];
|
|
49
|
+
|
|
50
|
+
expect(attributes.iframe.scrolling).toBe("yes");
|
|
51
|
+
expect(attributes.iframe.allow).toBe("payment https://www.paypal.com");
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it("still delegates the payment permissions policy when the component falls back to CONTEXT.IFRAME", async () => {
|
|
55
|
+
// getCheckoutComponent() is inline-memoized, so the earlier tests' calls
|
|
56
|
+
// never re-invoke create(). Reset the module registry to get a fresh,
|
|
57
|
+
// un-memoized component that actually re-evaluates supportsPopups().
|
|
58
|
+
vi.mocked(supportsPopups).mockReturnValueOnce(false);
|
|
59
|
+
vi.resetModules();
|
|
60
|
+
const { getCheckoutComponent: getFreshCheckoutComponent } = await import(
|
|
61
|
+
"./component"
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
getFreshCheckoutComponent();
|
|
65
|
+
|
|
66
|
+
const { attributes } = createMock.mock.calls.at(-1)[0];
|
|
67
|
+
expect(attributes.iframe.allow).toBe("payment https://www.paypal.com");
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it("names the origin explicitly rather than relying on the 'src' keyword", () => {
|
|
71
|
+
// zoid's openFrame() never sets this iframe's `src` attribute — it creates
|
|
72
|
+
// the iframe bare, then navigates it later via proxyWin.setLocation(), which
|
|
73
|
+
// uses window.location/form-POST, not the `src` attribute. Permissions
|
|
74
|
+
// Policy's 'src' keyword resolves by reading that attribute, so a bare
|
|
75
|
+
// `allow="payment"` (== `payment 'src'`) can never grant the feature here.
|
|
76
|
+
// Regression test for the retest failure after the original 'payment'-only
|
|
77
|
+
// fix (DTINAPPXO-5124).
|
|
78
|
+
getCheckoutComponent();
|
|
79
|
+
const { attributes } = createMock.mock.calls[0][0];
|
|
80
|
+
|
|
81
|
+
expect(attributes.iframe.allow).not.toBe("payment");
|
|
82
|
+
expect(attributes.iframe.allow).toContain("https://www.paypal.com");
|
|
83
|
+
});
|
|
84
|
+
});
|