@paypal/checkout-components 5.0.292 → 5.0.293-alpha.11

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.
@@ -0,0 +1,53 @@
1
+ /* @flow */
2
+
3
+ import { ZalgoPromise } from "@krakenjs/zalgo-promise/src";
4
+
5
+ export type HostedButtonsComponentProps = {|
6
+ hostedButtonId: string,
7
+ |};
8
+
9
+ export type GetCallbackProps = {|
10
+ hostedButtonId: string,
11
+ merchantId: string,
12
+ |};
13
+
14
+ export type HostedButtonsInstance = {|
15
+ render: (string | HTMLElement) => void,
16
+ |};
17
+
18
+ export type HostedButtonDetailsParams =
19
+ (HostedButtonsComponentProps) => ZalgoPromise<{|
20
+ html: string,
21
+ htmlScript: string,
22
+ style: {|
23
+ layout: string,
24
+ shape: string,
25
+ color: string,
26
+ label: string,
27
+ |},
28
+ |}>;
29
+
30
+ export type ButtonVariables = $ReadOnlyArray<{|
31
+ name: string,
32
+ value: string,
33
+ |}>;
34
+
35
+ export type CreateOrder = (data: {|
36
+ paymentSource: string,
37
+ |}) => ZalgoPromise<string>;
38
+
39
+ export type OnApprove = (data: {|
40
+ orderID: string,
41
+ paymentSource: string,
42
+ |}) => ZalgoPromise<void>;
43
+
44
+ export type CreateAccessToken = (clientID: string) => ZalgoPromise<string>;
45
+
46
+ export type HostedButtonsComponent =
47
+ (HostedButtonsComponentProps) => HostedButtonsInstance;
48
+
49
+ export type RenderForm = {|
50
+ html: string,
51
+ htmlScript: string,
52
+ selector: string | HTMLElement,
53
+ |};
@@ -0,0 +1,144 @@
1
+ /* @flow */
2
+
3
+ import { request, memoize, popup } from "@krakenjs/belter/src";
4
+ import { getSDKHost, getClientID } from "@paypal/sdk-client/src";
5
+
6
+ import { DEFAULT_POPUP_SIZE } from "../zoid/checkout";
7
+
8
+ import type {
9
+ ButtonVariables,
10
+ CreateAccessToken,
11
+ CreateOrder,
12
+ GetCallbackProps,
13
+ HostedButtonDetailsParams,
14
+ OnApprove,
15
+ RenderForm,
16
+ } from "./types";
17
+
18
+ const entryPoint = "SDK";
19
+ const baseUrl = `https://${getSDKHost()}`;
20
+ const apiUrl = baseUrl.replace("www", "api");
21
+
22
+ const getHeaders = (accessToken?: string) => ({
23
+ ...(accessToken && { Authorization: `Bearer ${accessToken}` }),
24
+ "Content-Type": "application/json",
25
+ "PayPal-Entry-Point": entryPoint,
26
+ });
27
+
28
+ export const createAccessToken: CreateAccessToken = memoize<CreateAccessToken>(
29
+ (clientId) => {
30
+ return request({
31
+ url: `${apiUrl}/v1/oauth2/token`,
32
+ method: "POST",
33
+ body: "grant_type=client_credentials",
34
+ headers: {
35
+ Authorization: `Basic ${btoa(clientId)}`,
36
+ "Content-Type": "application/json",
37
+ },
38
+ }).then((response) => response.body.access_token);
39
+ }
40
+ );
41
+
42
+ const getButtonVariable = (variables: ButtonVariables, key: string): string =>
43
+ variables?.find((variable) => variable.name === key)?.value ?? "";
44
+
45
+ const getFundingSource = (paymentSource) => {
46
+ if (paymentSource === "credit") {
47
+ return `CARD`;
48
+ }
49
+ return paymentSource.toUpperCase();
50
+ };
51
+
52
+ export const getHostedButtonDetails: HostedButtonDetailsParams = ({
53
+ hostedButtonId,
54
+ }) => {
55
+ return request({
56
+ url: `${baseUrl}/ncp/api/form-fields/${hostedButtonId}`,
57
+ headers: getHeaders(),
58
+ }).then(({ body }) => {
59
+ const variables = body.button_details.link_variables;
60
+ return {
61
+ style: {
62
+ layout: getButtonVariable(variables, "layout"),
63
+ shape: getButtonVariable(variables, "shape"),
64
+ color: getButtonVariable(variables, "color"),
65
+ label: getButtonVariable(variables, "button_text"),
66
+ },
67
+ html: body.html,
68
+ htmlScript: body.html_script,
69
+ };
70
+ });
71
+ };
72
+
73
+ /**
74
+ * Attaches form fields (html) to the given selector, and
75
+ * initializes window.__pp_form_fields (htmlScript).
76
+ */
77
+ export const renderForm = ({
78
+ html,
79
+ htmlScript,
80
+ selector,
81
+ }: RenderForm): void => {
82
+ const elm =
83
+ typeof selector === "string" ? document.querySelector(selector) : selector;
84
+ if (elm) {
85
+ elm.innerHTML = html + htmlScript;
86
+ const newScriptEl = document.createElement("script");
87
+ const oldScriptEl = elm.querySelector("script");
88
+ newScriptEl.innerHTML = oldScriptEl?.innerHTML ?? "";
89
+ oldScriptEl?.parentNode?.replaceChild(newScriptEl, oldScriptEl);
90
+ }
91
+ };
92
+
93
+ export const buildHostedButtonCreateOrder = ({
94
+ hostedButtonId,
95
+ merchantId,
96
+ }: GetCallbackProps): CreateOrder => {
97
+ return (data) => {
98
+ const userInputs =
99
+ window[`__pp_form_fields_${hostedButtonId}`]?.getUserInputs?.() || {};
100
+ return createAccessToken(getClientID()).then((accessToken) => {
101
+ return request({
102
+ url: `${apiUrl}/v1/checkout/links/${hostedButtonId}/create-context`,
103
+ headers: getHeaders(accessToken),
104
+ method: "POST",
105
+ body: JSON.stringify({
106
+ entry_point: entryPoint,
107
+ funding_source: getFundingSource(data.paymentSource),
108
+ merchant_id: merchantId,
109
+ ...userInputs,
110
+ }),
111
+ }).then(({ body }) => {
112
+ return body.context_id;
113
+ });
114
+ });
115
+ };
116
+ };
117
+
118
+ export const buildHostedButtonOnApprove = ({
119
+ hostedButtonId,
120
+ merchantId,
121
+ }: GetCallbackProps): OnApprove => {
122
+ return (data) => {
123
+ return createAccessToken(getClientID()).then((accessToken) => {
124
+ return request({
125
+ url: `${apiUrl}/v1/checkout/links/${hostedButtonId}/pay`,
126
+ headers: getHeaders(accessToken),
127
+ method: "POST",
128
+ body: JSON.stringify({
129
+ entry_point: entryPoint,
130
+ merchant_id: merchantId,
131
+ context_id: data.orderID,
132
+ }),
133
+ }).then(() => {
134
+ if (data.paymentSource === "card") {
135
+ const url = `${baseUrl}/ncp/payment/${hostedButtonId}/${data.orderID}`;
136
+ popup(url, {
137
+ width: DEFAULT_POPUP_SIZE.WIDTH,
138
+ height: DEFAULT_POPUP_SIZE.HEIGHT,
139
+ });
140
+ }
141
+ });
142
+ });
143
+ };
144
+ };
@@ -0,0 +1,121 @@
1
+ /* @flow */
2
+
3
+ import { test, expect, vi } from "vitest";
4
+ import { request } from "@krakenjs/belter/src";
5
+ import { ZalgoPromise } from "@krakenjs/zalgo-promise/src";
6
+
7
+ import {
8
+ getHostedButtonDetails,
9
+ buildHostedButtonCreateOrder,
10
+ buildHostedButtonOnApprove,
11
+ } from "./utils";
12
+
13
+ vi.mock("@krakenjs/belter/src", async () => {
14
+ return {
15
+ ...(await vi.importActual("@krakenjs/belter/src")),
16
+ request: vi.fn(),
17
+ };
18
+ });
19
+
20
+ vi.mock("@paypal/sdk-client/src", async () => {
21
+ return {
22
+ ...(await vi.importActual("@paypal/sdk-client/src")),
23
+ getSDKHost: () => "example.com",
24
+ getClientID: () => "client_id_123",
25
+ getMerchantID: () => ["merchant_id_123"],
26
+ };
27
+ });
28
+
29
+ const getHostedButtonDetailsResponse = {
30
+ body: {
31
+ button_details: {
32
+ link_variables: [
33
+ {
34
+ name: "business",
35
+ value: "M1234567890",
36
+ },
37
+ {
38
+ name: "shape",
39
+ value: "rect",
40
+ },
41
+ {
42
+ name: "layout",
43
+ value: "vertical",
44
+ },
45
+ {
46
+ name: "color",
47
+ value: "gold",
48
+ },
49
+ {
50
+ name: "button_text",
51
+ value: "paypal",
52
+ },
53
+ ],
54
+ },
55
+ },
56
+ };
57
+
58
+ test("getHostedButtonDetails", async () => {
59
+ // $FlowIssue
60
+ request.mockImplementationOnce(() =>
61
+ ZalgoPromise.resolve(getHostedButtonDetailsResponse)
62
+ );
63
+ await getHostedButtonDetails({
64
+ hostedButtonId: "B1234567890",
65
+ }).then(({ style }) => {
66
+ expect(style).toEqual({
67
+ layout: "vertical",
68
+ shape: "rect",
69
+ color: "gold",
70
+ label: "paypal",
71
+ });
72
+ });
73
+ expect.assertions(1);
74
+ });
75
+
76
+ test("buildHostedButtonCreateOrder", async () => {
77
+ const createOrder = buildHostedButtonCreateOrder({
78
+ hostedButtonId: "B1234567890",
79
+ merchantId: "M1234567890",
80
+ });
81
+
82
+ // $FlowIssue
83
+ request.mockImplementation(() =>
84
+ ZalgoPromise.resolve({
85
+ body: {
86
+ link_id: "B1234567890",
87
+ merchant_id: "M1234567890",
88
+ context_id: "EC-1234567890",
89
+ status: "CREATED",
90
+ },
91
+ })
92
+ );
93
+ const orderID = await createOrder({ paymentSource: "paypal" });
94
+ expect(orderID).toBe("EC-1234567890");
95
+ expect.assertions(1);
96
+ });
97
+
98
+ test("buildHostedButtonOnApprove", async () => {
99
+ const onApprove = buildHostedButtonOnApprove({
100
+ hostedButtonId: "B1234567890",
101
+ merchantId: "M1234567890",
102
+ });
103
+
104
+ // $FlowIssue
105
+ request.mockImplementation(() =>
106
+ ZalgoPromise.resolve({
107
+ body: {},
108
+ })
109
+ );
110
+ await onApprove({ orderID: "EC-1234567890", paymentSource: "paypal" });
111
+ expect(request).toHaveBeenCalledWith(
112
+ expect.objectContaining({
113
+ body: JSON.stringify({
114
+ entry_point: "SDK",
115
+ merchant_id: "M1234567890",
116
+ context_id: "EC-1234567890",
117
+ }),
118
+ })
119
+ );
120
+ expect.assertions(1);
121
+ });
@@ -0,0 +1,29 @@
1
+ /* @flow */
2
+
3
+ import { getHostedButtonsComponent } from "../hosted-buttons";
4
+ import type { HostedButtonsComponent } from "../hosted-buttons/types";
5
+ import { getButtonsComponent } from "../zoid/buttons";
6
+ import {
7
+ getCardFormComponent,
8
+ type CardFormComponent,
9
+ } from "../zoid/card-form";
10
+ import { getCheckoutComponent, type CheckoutComponent } from "../zoid/checkout";
11
+ import type { LazyExport, LazyProtectedExport } from "../types";
12
+ import { protectedExport } from "../lib";
13
+
14
+ export const HostedButtons: LazyExport<HostedButtonsComponent> = {
15
+ __get__: () => getHostedButtonsComponent(),
16
+ };
17
+
18
+ export const Checkout: LazyProtectedExport<CheckoutComponent> = {
19
+ __get__: () => protectedExport(getCheckoutComponent()),
20
+ };
21
+
22
+ export const CardForm: LazyProtectedExport<CardFormComponent> = {
23
+ __get__: () => protectedExport(getCardFormComponent()),
24
+ };
25
+
26
+ export function setup() {
27
+ getButtonsComponent();
28
+ getCheckoutComponent();
29
+ }
@@ -719,6 +719,16 @@ export const getButtonsComponent: () => ButtonsComponent = memoize(() => {
719
719
  },
720
720
  },
721
721
 
722
+ referrerDomain: {
723
+ type: "string",
724
+ required: false,
725
+ value: () => {
726
+ if (window.document.referrer) {
727
+ return new URL(window.document.referrer).host || undefined;
728
+ }
729
+ },
730
+ },
731
+
722
732
  userIDToken: {
723
733
  type: "string",
724
734
  default: getUserIDToken,
@@ -730,7 +740,12 @@ export const getButtonsComponent: () => ButtonsComponent = memoize(() => {
730
740
  clientMetadataID: {
731
741
  type: "string",
732
742
  required: false,
733
- default: getClientMetadataID,
743
+ default: () => {
744
+ const clientMetadataId = getClientMetadataID();
745
+ const sessionID = getSessionID();
746
+
747
+ return clientMetadataId || sessionID;
748
+ },
734
749
  queryParam: true,
735
750
  },
736
751
 
@@ -834,6 +849,11 @@ export const getButtonsComponent: () => ButtonsComponent = memoize(() => {
834
849
  value: getExperimentation,
835
850
  },
836
851
 
852
+ hostedButtonId: {
853
+ type: "string",
854
+ required: false,
855
+ },
856
+
837
857
  displayOnly: {
838
858
  type: "array",
839
859
  queryParam: true,