@paypal/checkout-components 5.0.307 → 5.0.308
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/dist/button.js +1 -1
- package/dist/test/button.js +1 -1
- package/globals.js +1 -0
- package/package.json +2 -2
- package/src/constants/button.js +21 -0
- package/src/constants/class.js +5 -0
- package/src/declarations.js +1 -0
- package/src/hosted-buttons/utils.js +6 -1
- package/src/hosted-buttons/utils.test.js +21 -0
- package/src/ui/buttons/buttons.jsx +22 -4
- package/src/ui/buttons/message.jsx +31 -0
- package/src/ui/buttons/props.js +104 -1
- package/src/ui/buttons/util.js +36 -0
- package/src/zoid/buttons/component.jsx +121 -1
- package/src/zoid/buttons/util.js +53 -0
package/globals.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@paypal/checkout-components",
|
|
3
|
-
"version": "5.0.
|
|
3
|
+
"version": "5.0.308",
|
|
4
4
|
"description": "PayPal Checkout components, for integrating checkout products.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -114,7 +114,7 @@
|
|
|
114
114
|
"@paypal/connect-loader-component": "1.1.1",
|
|
115
115
|
"@paypal/funding-components": "^1.0.31",
|
|
116
116
|
"@paypal/sdk-client": "^4.0.184",
|
|
117
|
-
"@paypal/sdk-constants": "^1.0.
|
|
117
|
+
"@paypal/sdk-constants": "^1.0.146",
|
|
118
118
|
"@paypal/sdk-logos": "^2.2.6"
|
|
119
119
|
},
|
|
120
120
|
"lint-staged": {
|
package/src/constants/button.js
CHANGED
|
@@ -63,3 +63,24 @@ export const MENU_PLACEMENT = {
|
|
|
63
63
|
ABOVE: ("above": "above"),
|
|
64
64
|
BELOW: ("below": "below"),
|
|
65
65
|
};
|
|
66
|
+
|
|
67
|
+
export const MESSAGE_OFFER = {
|
|
68
|
+
PAY_LATER_LONG_TERM: ("pay_later_long_term": "pay_later_long_term"),
|
|
69
|
+
PAY_LATER_SHORT_TERM: ("pay_later_short_term": "pay_later_short_term"),
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
export const MESSAGE_COLOR = {
|
|
73
|
+
BLACK: ("black": "black"),
|
|
74
|
+
WHITE: ("white": "white"),
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
export const MESSAGE_POSITION = {
|
|
78
|
+
TOP: ("top": "top"),
|
|
79
|
+
BOTTOM: ("bottom": "bottom"),
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
export const MESSAGE_ALIGN = {
|
|
83
|
+
CENTER: ("center": "center"),
|
|
84
|
+
LEFT: ("left": "left"),
|
|
85
|
+
RIGHT: ("right": "right"),
|
|
86
|
+
};
|
package/src/constants/class.js
CHANGED
|
@@ -42,4 +42,9 @@ export const CLASS = {
|
|
|
42
42
|
HIDDEN: ("hidden": "hidden"),
|
|
43
43
|
|
|
44
44
|
IMMEDIATE: ("immediate": "immediate"),
|
|
45
|
+
|
|
46
|
+
BUTTON_MESSAGE: ("paypal-button-message": "paypal-button-message"),
|
|
47
|
+
|
|
48
|
+
BUTTON_MESSAGE_RESERVE:
|
|
49
|
+
("paypal-button-message-reserved": "paypal-button-message-reserved"),
|
|
45
50
|
};
|
package/src/declarations.js
CHANGED
|
@@ -214,7 +214,12 @@ export const buildHostedButtonOnApprove = ({
|
|
|
214
214
|
// so we need to redirect to the thank you page for buyers who complete
|
|
215
215
|
// a checkout via "Debit or Credit Card".
|
|
216
216
|
if (data.paymentSource === FUNDING.CARD) {
|
|
217
|
-
|
|
217
|
+
let redirectUrl = `${baseUrl}/ncp/payment/${hostedButtonId}/${data.orderID}`;
|
|
218
|
+
// add error messages to the payment confirmation page url
|
|
219
|
+
if (response.body?.details?.[0]?.issue) {
|
|
220
|
+
redirectUrl += `?status=${response.body.details[0].issue}`;
|
|
221
|
+
}
|
|
222
|
+
window.location = redirectUrl;
|
|
218
223
|
}
|
|
219
224
|
return response;
|
|
220
225
|
});
|
|
@@ -285,6 +285,27 @@ describe("buildHostedButtonOnApprove", () => {
|
|
|
285
285
|
"https://example.com/ncp/payment/B1234567890/EC-1234567890"
|
|
286
286
|
);
|
|
287
287
|
});
|
|
288
|
+
|
|
289
|
+
test("redirects with an error message in the status query parameter", async () => {
|
|
290
|
+
// $FlowIssue
|
|
291
|
+
request.mockImplementation(() =>
|
|
292
|
+
// eslint-disable-next-line compat/compat
|
|
293
|
+
Promise.resolve({
|
|
294
|
+
body: {
|
|
295
|
+
details: [
|
|
296
|
+
{
|
|
297
|
+
issue: "DUPLICATE_INVOICE_ID",
|
|
298
|
+
},
|
|
299
|
+
],
|
|
300
|
+
},
|
|
301
|
+
})
|
|
302
|
+
);
|
|
303
|
+
|
|
304
|
+
await onApprove({ orderID, paymentSource: "card" });
|
|
305
|
+
expect(window.location).toBe(
|
|
306
|
+
"https://example.com/ncp/payment/B1234567890/EC-1234567890?status=DUPLICATE_INVOICE_ID"
|
|
307
|
+
);
|
|
308
|
+
});
|
|
288
309
|
});
|
|
289
310
|
});
|
|
290
311
|
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
BUTTON_NUMBER,
|
|
16
16
|
BUTTON_LAYOUT,
|
|
17
17
|
BUTTON_FLOW,
|
|
18
|
+
MESSAGE_POSITION,
|
|
18
19
|
} from "../../constants";
|
|
19
20
|
import {
|
|
20
21
|
determineEligibleFunding,
|
|
@@ -34,6 +35,8 @@ import { Button } from "./button";
|
|
|
34
35
|
import { TagLine } from "./tagline";
|
|
35
36
|
import { Script } from "./script";
|
|
36
37
|
import { PoweredByPayPal } from "./poweredBy";
|
|
38
|
+
import { Message } from "./message";
|
|
39
|
+
import { calculateShowPoweredBy } from "./util";
|
|
37
40
|
|
|
38
41
|
type GetWalletInstrumentOptions = {|
|
|
39
42
|
wallet: ?Wallet,
|
|
@@ -177,6 +180,8 @@ export function Buttons(props: ButtonsProps): ElementNode {
|
|
|
177
180
|
supportedNativeBrowser,
|
|
178
181
|
showPayLabel,
|
|
179
182
|
displayOnly,
|
|
183
|
+
message,
|
|
184
|
+
messageMarkup,
|
|
180
185
|
} = normalizeButtonProps(props);
|
|
181
186
|
const { layout, shape, tagline } = style;
|
|
182
187
|
|
|
@@ -237,6 +242,14 @@ export function Buttons(props: ButtonsProps): ElementNode {
|
|
|
237
242
|
return i;
|
|
238
243
|
};
|
|
239
244
|
|
|
245
|
+
const showTagline =
|
|
246
|
+
tagline &&
|
|
247
|
+
layout === BUTTON_LAYOUT.HORIZONTAL &&
|
|
248
|
+
!fundingSource &&
|
|
249
|
+
!message;
|
|
250
|
+
|
|
251
|
+
const showPoweredBy = calculateShowPoweredBy(layout, fundingSources);
|
|
252
|
+
|
|
240
253
|
return (
|
|
241
254
|
<div
|
|
242
255
|
class={[
|
|
@@ -256,6 +269,10 @@ export function Buttons(props: ButtonsProps): ElementNode {
|
|
|
256
269
|
fundingEligibility={fundingEligibility}
|
|
257
270
|
/>
|
|
258
271
|
|
|
272
|
+
{message && message.position === MESSAGE_POSITION.TOP ? (
|
|
273
|
+
<Message markup={messageMarkup} position={message.position} />
|
|
274
|
+
) : null}
|
|
275
|
+
|
|
259
276
|
{fundingSources.map((source, i) => (
|
|
260
277
|
<Button
|
|
261
278
|
content={content}
|
|
@@ -285,7 +302,7 @@ export function Buttons(props: ButtonsProps): ElementNode {
|
|
|
285
302
|
/>
|
|
286
303
|
))}
|
|
287
304
|
|
|
288
|
-
{
|
|
305
|
+
{showTagline ? (
|
|
289
306
|
<TagLine
|
|
290
307
|
fundingSource={fundingSources[0]}
|
|
291
308
|
style={style}
|
|
@@ -307,9 +324,10 @@ export function Buttons(props: ButtonsProps): ElementNode {
|
|
|
307
324
|
/>
|
|
308
325
|
) : null}
|
|
309
326
|
|
|
310
|
-
{
|
|
311
|
-
|
|
312
|
-
|
|
327
|
+
{showPoweredBy ? <PoweredByPayPal locale={locale} nonce={nonce} /> : null}
|
|
328
|
+
|
|
329
|
+
{message && message.position === MESSAGE_POSITION.BOTTOM ? (
|
|
330
|
+
<Message markup={messageMarkup} position={message.position} />
|
|
313
331
|
) : null}
|
|
314
332
|
|
|
315
333
|
<Script nonce={nonce} />
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/* @flow */
|
|
2
|
+
/** @jsx node */
|
|
3
|
+
|
|
4
|
+
import { node, type ChildType } from "@krakenjs/jsx-pragmatic/src";
|
|
5
|
+
|
|
6
|
+
import { CLASS } from "../../constants";
|
|
7
|
+
|
|
8
|
+
const INITIAL_RESERVED_HEIGHT = "36px";
|
|
9
|
+
|
|
10
|
+
type MessageProps = {|
|
|
11
|
+
markup: ?string,
|
|
12
|
+
position: string,
|
|
13
|
+
|};
|
|
14
|
+
|
|
15
|
+
export function Message({ markup, position }: MessageProps): ChildType {
|
|
16
|
+
const messageClassNames = [
|
|
17
|
+
CLASS.BUTTON_MESSAGE,
|
|
18
|
+
`${CLASS.BUTTON_MESSAGE}-${position}`,
|
|
19
|
+
].join(" ");
|
|
20
|
+
|
|
21
|
+
if (typeof markup !== "string") {
|
|
22
|
+
return (
|
|
23
|
+
<div
|
|
24
|
+
class={`${messageClassNames} ${CLASS.BUTTON_MESSAGE_RESERVE}`}
|
|
25
|
+
style={`height:${INITIAL_RESERVED_HEIGHT}`}
|
|
26
|
+
/>
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return <div class={messageClassNames} innerHTML={markup} />;
|
|
31
|
+
}
|
package/src/ui/buttons/props.js
CHANGED
|
@@ -40,11 +40,15 @@ import {
|
|
|
40
40
|
BUTTON_SIZE,
|
|
41
41
|
BUTTON_FLOW,
|
|
42
42
|
MENU_PLACEMENT,
|
|
43
|
+
MESSAGE_OFFER,
|
|
44
|
+
MESSAGE_COLOR,
|
|
45
|
+
MESSAGE_POSITION,
|
|
46
|
+
MESSAGE_ALIGN,
|
|
43
47
|
} from "../../constants";
|
|
44
48
|
import { getFundingConfig, isFundingEligible } from "../../funding";
|
|
45
49
|
|
|
46
50
|
import { BUTTON_SIZE_STYLE } from "./config";
|
|
47
|
-
import { isBorderRadiusNumber } from "./util";
|
|
51
|
+
import { isBorderRadiusNumber, calculateMessagePosition } from "./util";
|
|
48
52
|
|
|
49
53
|
export type CreateOrderData = {||} | {||};
|
|
50
54
|
|
|
@@ -424,6 +428,22 @@ export type ApplePaySessionConfigRequest = (
|
|
|
424
428
|
request: Object
|
|
425
429
|
) => ApplePaySessionConfig;
|
|
426
430
|
|
|
431
|
+
export type ButtonMessage = {|
|
|
432
|
+
amount?: number,
|
|
433
|
+
offer?: $ReadOnlyArray<$Values<typeof MESSAGE_OFFER>>,
|
|
434
|
+
color: $Values<typeof MESSAGE_COLOR>,
|
|
435
|
+
position: $Values<typeof MESSAGE_POSITION>,
|
|
436
|
+
align: $Values<typeof MESSAGE_ALIGN>,
|
|
437
|
+
|};
|
|
438
|
+
|
|
439
|
+
export type ButtonMessageInputs = {|
|
|
440
|
+
amount?: number | void,
|
|
441
|
+
offer?: $ReadOnlyArray<$Values<typeof MESSAGE_OFFER>> | void,
|
|
442
|
+
color?: $Values<typeof MESSAGE_COLOR> | void,
|
|
443
|
+
position?: $Values<typeof MESSAGE_POSITION> | void,
|
|
444
|
+
align?: $Values<typeof MESSAGE_ALIGN> | void,
|
|
445
|
+
|};
|
|
446
|
+
|
|
427
447
|
export type RenderButtonProps = {|
|
|
428
448
|
style: ButtonStyle,
|
|
429
449
|
locale: LocaleType,
|
|
@@ -458,6 +478,8 @@ export type RenderButtonProps = {|
|
|
|
458
478
|
supportedNativeBrowser: boolean,
|
|
459
479
|
showPayLabel: boolean,
|
|
460
480
|
displayOnly?: $ReadOnlyArray<$Values<typeof DISPLAY_ONLY_VALUES>>,
|
|
481
|
+
message?: ButtonMessage,
|
|
482
|
+
messageMarkup?: string,
|
|
461
483
|
|};
|
|
462
484
|
|
|
463
485
|
export type PrerenderDetails = {|
|
|
@@ -517,6 +539,8 @@ export type ButtonProps = {|
|
|
|
517
539
|
createVaultSetupToken: CreateVaultSetupToken,
|
|
518
540
|
displayOnly?: $ReadOnlyArray<$Values<typeof DISPLAY_ONLY_VALUES>>,
|
|
519
541
|
hostedButtonId?: string,
|
|
542
|
+
message?: ButtonMessage,
|
|
543
|
+
messageMarkup?: string,
|
|
520
544
|
|};
|
|
521
545
|
|
|
522
546
|
// eslint-disable-next-line flowtype/require-exact-type
|
|
@@ -559,6 +583,9 @@ export type ButtonPropsInputs = {
|
|
|
559
583
|
supportedNativeBrowser: boolean,
|
|
560
584
|
showPayLabel: boolean,
|
|
561
585
|
displayOnly: $ReadOnlyArray<$Values<typeof DISPLAY_ONLY_VALUES>>,
|
|
586
|
+
message?: ButtonMessageInputs | void,
|
|
587
|
+
messageMarkup?: string | void,
|
|
588
|
+
renderedButtons: $ReadOnlyArray<$Values<typeof FUNDING>>,
|
|
562
589
|
};
|
|
563
590
|
|
|
564
591
|
export const DEFAULT_STYLE = {
|
|
@@ -707,6 +734,72 @@ export function normalizeButtonStyle(
|
|
|
707
734
|
};
|
|
708
735
|
}
|
|
709
736
|
|
|
737
|
+
export function normalizeButtonMessage(
|
|
738
|
+
message: ButtonMessageInputs,
|
|
739
|
+
layout: $Values<typeof BUTTON_LAYOUT>,
|
|
740
|
+
fundingSources: $ReadOnlyArray<$Values<typeof FUNDING>>
|
|
741
|
+
): ButtonMessage {
|
|
742
|
+
const {
|
|
743
|
+
amount,
|
|
744
|
+
offer,
|
|
745
|
+
color = MESSAGE_COLOR.BLACK,
|
|
746
|
+
position,
|
|
747
|
+
align = MESSAGE_ALIGN.CENTER,
|
|
748
|
+
} = message;
|
|
749
|
+
|
|
750
|
+
if (typeof amount !== "undefined") {
|
|
751
|
+
if (typeof amount !== "number") {
|
|
752
|
+
throw new TypeError(
|
|
753
|
+
`Expected message.amount to be a number, got: ${amount}`
|
|
754
|
+
);
|
|
755
|
+
}
|
|
756
|
+
if (amount < 0) {
|
|
757
|
+
throw new Error(
|
|
758
|
+
`Expected message.amount to be a positive number, got: ${amount}`
|
|
759
|
+
);
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
if (typeof offer !== "undefined") {
|
|
764
|
+
if (!Array.isArray(offer)) {
|
|
765
|
+
throw new TypeError(
|
|
766
|
+
`Expected message.offer to be an array of strings, got: ${String(
|
|
767
|
+
offer
|
|
768
|
+
)}`
|
|
769
|
+
);
|
|
770
|
+
}
|
|
771
|
+
const invalidOffers = offer.filter(
|
|
772
|
+
(o) => !values(MESSAGE_OFFER).includes(o)
|
|
773
|
+
);
|
|
774
|
+
if (invalidOffers.length > 0) {
|
|
775
|
+
throw new Error(`Invalid offer(s): ${invalidOffers.join(",")}`);
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
if (typeof color !== "undefined" && !values(MESSAGE_COLOR).includes(color)) {
|
|
780
|
+
throw new Error(`Invalid color: ${color}`);
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
if (
|
|
784
|
+
typeof position !== "undefined" &&
|
|
785
|
+
!values(MESSAGE_POSITION).includes(position)
|
|
786
|
+
) {
|
|
787
|
+
throw new Error(`Invalid position: ${position}`);
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
if (typeof align !== "undefined" && !values(MESSAGE_ALIGN).includes(align)) {
|
|
791
|
+
throw new Error(`Invalid align: ${align}`);
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
return {
|
|
795
|
+
amount,
|
|
796
|
+
offer,
|
|
797
|
+
color,
|
|
798
|
+
position: calculateMessagePosition(fundingSources, layout, position),
|
|
799
|
+
align,
|
|
800
|
+
};
|
|
801
|
+
}
|
|
802
|
+
|
|
710
803
|
const COUNTRIES = values(COUNTRY);
|
|
711
804
|
const FUNDING_SOURCES = values(FUNDING);
|
|
712
805
|
const ENVS = values(ENV);
|
|
@@ -761,6 +854,9 @@ export function normalizeButtonProps(
|
|
|
761
854
|
supportedNativeBrowser = false,
|
|
762
855
|
showPayLabel = true,
|
|
763
856
|
displayOnly = [],
|
|
857
|
+
message,
|
|
858
|
+
messageMarkup,
|
|
859
|
+
renderedButtons,
|
|
764
860
|
} = props;
|
|
765
861
|
|
|
766
862
|
const { country, lang } = locale;
|
|
@@ -819,6 +915,11 @@ export function normalizeButtonProps(
|
|
|
819
915
|
}
|
|
820
916
|
|
|
821
917
|
style = normalizeButtonStyle(props, style);
|
|
918
|
+
const { layout } = style;
|
|
919
|
+
|
|
920
|
+
message = message
|
|
921
|
+
? normalizeButtonMessage(message, layout, renderedButtons)
|
|
922
|
+
: undefined;
|
|
822
923
|
|
|
823
924
|
return {
|
|
824
925
|
clientID,
|
|
@@ -852,5 +953,7 @@ export function normalizeButtonProps(
|
|
|
852
953
|
supportedNativeBrowser,
|
|
853
954
|
showPayLabel,
|
|
854
955
|
displayOnly,
|
|
956
|
+
message,
|
|
957
|
+
messageMarkup,
|
|
855
958
|
};
|
|
856
959
|
}
|
package/src/ui/buttons/util.js
CHANGED
|
@@ -1,5 +1,41 @@
|
|
|
1
1
|
/* @flow */
|
|
2
|
+
import { FUNDING } from "@paypal/sdk-constants/src";
|
|
3
|
+
|
|
4
|
+
import { BUTTON_LAYOUT, MESSAGE_POSITION } from "../../constants";
|
|
5
|
+
import { ValidationError } from "../../lib";
|
|
2
6
|
|
|
3
7
|
export function isBorderRadiusNumber(borderRadius?: number): boolean {
|
|
4
8
|
return typeof borderRadius === "number";
|
|
5
9
|
}
|
|
10
|
+
|
|
11
|
+
export function calculateShowPoweredBy(
|
|
12
|
+
layout: $Values<typeof BUTTON_LAYOUT>,
|
|
13
|
+
fundingSources: $ReadOnlyArray<$Values<typeof FUNDING>>
|
|
14
|
+
): boolean {
|
|
15
|
+
return (
|
|
16
|
+
layout === BUTTON_LAYOUT.VERTICAL && fundingSources.includes(FUNDING.CARD)
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function calculateMessagePosition(
|
|
21
|
+
fundingSources: $ReadOnlyArray<$Values<typeof FUNDING>>,
|
|
22
|
+
layout: $Values<typeof BUTTON_LAYOUT>,
|
|
23
|
+
position?: $Values<typeof MESSAGE_POSITION>
|
|
24
|
+
): $Values<typeof MESSAGE_POSITION> {
|
|
25
|
+
const showPoweredBy = calculateShowPoweredBy(layout, fundingSources);
|
|
26
|
+
|
|
27
|
+
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"
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (
|
|
34
|
+
showPoweredBy ||
|
|
35
|
+
position === MESSAGE_POSITION.TOP ||
|
|
36
|
+
(layout === BUTTON_LAYOUT.VERTICAL && !position)
|
|
37
|
+
) {
|
|
38
|
+
return MESSAGE_POSITION.TOP;
|
|
39
|
+
}
|
|
40
|
+
return MESSAGE_POSITION.BOTTOM;
|
|
41
|
+
}
|
|
@@ -73,7 +73,11 @@ import {
|
|
|
73
73
|
logLatencyInstrumentationPhase,
|
|
74
74
|
prepareInstrumentationPayload,
|
|
75
75
|
} from "../../lib";
|
|
76
|
-
import {
|
|
76
|
+
import {
|
|
77
|
+
normalizeButtonStyle,
|
|
78
|
+
normalizeButtonMessage,
|
|
79
|
+
type ButtonProps,
|
|
80
|
+
} from "../../ui/buttons/props";
|
|
77
81
|
import { isFundingEligible } from "../../funding";
|
|
78
82
|
|
|
79
83
|
import { containerTemplate } from "./container";
|
|
@@ -86,6 +90,7 @@ import {
|
|
|
86
90
|
getRenderedButtons,
|
|
87
91
|
getButtonSize,
|
|
88
92
|
getButtonExperiments,
|
|
93
|
+
getModal,
|
|
89
94
|
} from "./util";
|
|
90
95
|
|
|
91
96
|
export type ButtonsComponent = ZoidComponent<ButtonProps>;
|
|
@@ -635,6 +640,24 @@ export const getButtonsComponent: () => ButtonsComponent = memoize(() => {
|
|
|
635
640
|
value: getMerchantRequestedPopupsDisabled,
|
|
636
641
|
},
|
|
637
642
|
|
|
643
|
+
message: {
|
|
644
|
+
type: "object",
|
|
645
|
+
queryParam: true,
|
|
646
|
+
required: false,
|
|
647
|
+
decorate: ({ props, value }) => {
|
|
648
|
+
const {
|
|
649
|
+
style: { layout },
|
|
650
|
+
renderedButtons: fundingSources,
|
|
651
|
+
} = props;
|
|
652
|
+
return normalizeButtonMessage(
|
|
653
|
+
// $FlowFixMe
|
|
654
|
+
value,
|
|
655
|
+
layout,
|
|
656
|
+
fundingSources
|
|
657
|
+
);
|
|
658
|
+
},
|
|
659
|
+
},
|
|
660
|
+
|
|
638
661
|
nonce: {
|
|
639
662
|
type: "string",
|
|
640
663
|
default: getCSPNonce,
|
|
@@ -684,6 +707,103 @@ export const getButtonsComponent: () => ButtonsComponent = memoize(() => {
|
|
|
684
707
|
},
|
|
685
708
|
},
|
|
686
709
|
|
|
710
|
+
onMessageClick: {
|
|
711
|
+
type: "function",
|
|
712
|
+
required: false,
|
|
713
|
+
value: ({ props }) => {
|
|
714
|
+
return async ({
|
|
715
|
+
offerType,
|
|
716
|
+
messageType,
|
|
717
|
+
offerCountryCode,
|
|
718
|
+
creditProductIdentifier,
|
|
719
|
+
}) => {
|
|
720
|
+
const { message, clientID, merchantID, currency, buttonSessionID } =
|
|
721
|
+
props;
|
|
722
|
+
const amount = message?.amount;
|
|
723
|
+
|
|
724
|
+
getLogger()
|
|
725
|
+
.info("button_message_click")
|
|
726
|
+
.track({
|
|
727
|
+
[FPTI_KEY.TRANSITION]: "button_message_click",
|
|
728
|
+
[FPTI_KEY.STATE]: "BUTTON_MESSAGE",
|
|
729
|
+
[FPTI_KEY.BUTTON_SESSION_UID]: buttonSessionID,
|
|
730
|
+
[FPTI_KEY.CONTEXT_ID]: buttonSessionID,
|
|
731
|
+
[FPTI_KEY.CONTEXT_TYPE]: "button_session_id",
|
|
732
|
+
[FPTI_KEY.EVENT_NAME]: "message_click",
|
|
733
|
+
[FPTI_KEY.SELLER_ID]: merchantID?.join(","),
|
|
734
|
+
[FPTI_KEY.BUTTON_MESSAGE_OFFER_TYPE]: offerType,
|
|
735
|
+
[FPTI_KEY.BUTTON_MESSAGE_CREDIT_PRODUCT_IDENTIFIER]:
|
|
736
|
+
creditProductIdentifier,
|
|
737
|
+
[FPTI_KEY.BUTTON_MESSAGE_TYPE]: messageType,
|
|
738
|
+
[FPTI_KEY.BUTTON_MESSAGE_POSITION]: message?.position,
|
|
739
|
+
[FPTI_KEY.BUTTON_MESSAGE_ALIGN]: message?.align,
|
|
740
|
+
[FPTI_KEY.BUTTON_MESSAGE_COLOR]: message?.color,
|
|
741
|
+
[FPTI_KEY.BUTTON_MESSAGE_OFFER_COUNTRY]: offerCountryCode,
|
|
742
|
+
[FPTI_KEY.BUTTON_MESSAGE_CURRENCY]: currency,
|
|
743
|
+
[FPTI_KEY.BUTTON_MESSAGE_AMOUNT]: amount,
|
|
744
|
+
});
|
|
745
|
+
|
|
746
|
+
const modalInstance = await getModal(clientID, merchantID);
|
|
747
|
+
return modalInstance?.show({
|
|
748
|
+
amount,
|
|
749
|
+
offer: offerType,
|
|
750
|
+
currency,
|
|
751
|
+
});
|
|
752
|
+
};
|
|
753
|
+
},
|
|
754
|
+
},
|
|
755
|
+
|
|
756
|
+
onMessageHover: {
|
|
757
|
+
type: "function",
|
|
758
|
+
required: false,
|
|
759
|
+
value: ({ props }) => {
|
|
760
|
+
return () => {
|
|
761
|
+
// offerType, messageType, offerCountryCode, and creditProductIdentifier are passed in and may be used in an upcoming message hover logging feature
|
|
762
|
+
|
|
763
|
+
// lazy loads the modal, to be memoized and executed onMessageClick
|
|
764
|
+
const { clientID, merchantID } = props;
|
|
765
|
+
return getModal(clientID, merchantID);
|
|
766
|
+
};
|
|
767
|
+
},
|
|
768
|
+
},
|
|
769
|
+
|
|
770
|
+
onMessageReady: {
|
|
771
|
+
type: "function",
|
|
772
|
+
required: false,
|
|
773
|
+
value: ({ props }) => {
|
|
774
|
+
return ({
|
|
775
|
+
offerType,
|
|
776
|
+
messageType,
|
|
777
|
+
offerCountryCode,
|
|
778
|
+
creditProductIdentifier,
|
|
779
|
+
}) => {
|
|
780
|
+
const { message, buttonSessionID, currency, merchantID } = props;
|
|
781
|
+
|
|
782
|
+
getLogger()
|
|
783
|
+
.info("button_message_render")
|
|
784
|
+
.track({
|
|
785
|
+
[FPTI_KEY.TRANSITION]: "button_message_render",
|
|
786
|
+
[FPTI_KEY.STATE]: "BUTTON_MESSAGE",
|
|
787
|
+
[FPTI_KEY.BUTTON_SESSION_UID]: buttonSessionID,
|
|
788
|
+
[FPTI_KEY.CONTEXT_ID]: buttonSessionID,
|
|
789
|
+
[FPTI_KEY.CONTEXT_TYPE]: "button_session_id",
|
|
790
|
+
[FPTI_KEY.EVENT_NAME]: "message_render",
|
|
791
|
+
[FPTI_KEY.SELLER_ID]: merchantID?.join(","),
|
|
792
|
+
[FPTI_KEY.BUTTON_MESSAGE_OFFER_TYPE]: offerType,
|
|
793
|
+
[FPTI_KEY.BUTTON_MESSAGE_CREDIT_PRODUCT_IDENTIFIER]:
|
|
794
|
+
creditProductIdentifier,
|
|
795
|
+
[FPTI_KEY.BUTTON_MESSAGE_TYPE]: messageType,
|
|
796
|
+
[FPTI_KEY.BUTTON_MESSAGE_POSITION]: message?.position,
|
|
797
|
+
[FPTI_KEY.BUTTON_MESSAGE_ALIGN]: message?.align,
|
|
798
|
+
[FPTI_KEY.BUTTON_MESSAGE_COLOR]: message?.color,
|
|
799
|
+
[FPTI_KEY.BUTTON_MESSAGE_CURRENCY]: currency,
|
|
800
|
+
[FPTI_KEY.BUTTON_MESSAGE_OFFER_COUNTRY]: offerCountryCode,
|
|
801
|
+
[FPTI_KEY.BUTTON_MESSAGE_AMOUNT]: message?.amount,
|
|
802
|
+
});
|
|
803
|
+
};
|
|
804
|
+
},
|
|
805
|
+
},
|
|
806
|
+
|
|
687
807
|
onShippingAddressChange: {
|
|
688
808
|
type: "function",
|
|
689
809
|
required: false,
|
package/src/zoid/buttons/util.js
CHANGED
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
getElement,
|
|
14
14
|
isStandAlone,
|
|
15
15
|
once,
|
|
16
|
+
memoize,
|
|
16
17
|
} from "@krakenjs/belter/src";
|
|
17
18
|
import { FUNDING } from "@paypal/sdk-constants/src";
|
|
18
19
|
import {
|
|
@@ -22,6 +23,8 @@ import {
|
|
|
22
23
|
getFundingEligibility,
|
|
23
24
|
getPlatform,
|
|
24
25
|
getComponents,
|
|
26
|
+
getEnv,
|
|
27
|
+
getNamespace,
|
|
25
28
|
} from "@paypal/sdk-client/src";
|
|
26
29
|
import { getRefinedFundingEligibility } from "@paypal/funding-components/src";
|
|
27
30
|
|
|
@@ -357,3 +360,53 @@ export function getButtonSize(
|
|
|
357
360
|
}
|
|
358
361
|
}
|
|
359
362
|
}
|
|
363
|
+
|
|
364
|
+
function buildModalBundleUrl(): string {
|
|
365
|
+
let url = __PAYPAL_CHECKOUT__.__URI__.__MESSAGE_MODAL__;
|
|
366
|
+
if (getEnv() === "sandbox") {
|
|
367
|
+
url = url.replace("/js/", "/sandbox/");
|
|
368
|
+
} else if (getEnv() === "stage" || getEnv() === "local") {
|
|
369
|
+
url = url.replace("/js/", "/stage/");
|
|
370
|
+
}
|
|
371
|
+
return url;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
export const getModal: (
|
|
375
|
+
clientID: string,
|
|
376
|
+
merchantID: $ReadOnlyArray<string> | void
|
|
377
|
+
) => Object = memoize(async (clientID, merchantID) => {
|
|
378
|
+
try {
|
|
379
|
+
const namespace = getNamespace();
|
|
380
|
+
if (!window[namespace].MessagesModal) {
|
|
381
|
+
// eslint-disable-next-line no-restricted-globals, promise/no-native
|
|
382
|
+
await new Promise((resolve, reject) => {
|
|
383
|
+
const script = document.createElement("script");
|
|
384
|
+
script.setAttribute("data-pp-namespace", namespace);
|
|
385
|
+
script.src = buildModalBundleUrl();
|
|
386
|
+
script.addEventListener("error", (err: Event) => {
|
|
387
|
+
reject(err);
|
|
388
|
+
});
|
|
389
|
+
script.addEventListener("load", () => {
|
|
390
|
+
document.body?.removeChild(script);
|
|
391
|
+
resolve();
|
|
392
|
+
});
|
|
393
|
+
document.body?.appendChild(script);
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
return window[namespace].MessagesModal({
|
|
398
|
+
account: `client-id:${clientID}`,
|
|
399
|
+
merchantId: merchantID?.join(",") || undefined,
|
|
400
|
+
});
|
|
401
|
+
} catch (err) {
|
|
402
|
+
// $FlowFixMe flow doesn't seem to understand that the reset function property exists on the function object itself
|
|
403
|
+
getModal.reset();
|
|
404
|
+
getLogger()
|
|
405
|
+
.error("button_message_modal_fetch_error", { err })
|
|
406
|
+
.track({
|
|
407
|
+
err: err.message || "BUTTON_MESSAGE_MODAL_FETCH_ERROR",
|
|
408
|
+
details: err.details,
|
|
409
|
+
stack: JSON.stringify(err.stack || err),
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
});
|