@paypal/checkout-components 5.0.344 → 5.0.345-alpha-714509c.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
package/src/lib/security.js
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
import { isSameDomain } from "@krakenjs/cross-domain-utils/src";
|
|
4
4
|
import { supportsPopups } from "@krakenjs/belter/src";
|
|
5
|
-
import { isPayPalDomain } from "@paypal/sdk-client/src";
|
|
5
|
+
import { getEnv, isPayPalDomain } from "@paypal/sdk-client/src";
|
|
6
|
+
import { ENV } from "@paypal/sdk-constants/src";
|
|
6
7
|
|
|
7
8
|
export function allowIframe(): boolean {
|
|
8
9
|
if (!isPayPalDomain()) {
|
|
@@ -28,3 +29,13 @@ export function allowIframe(): boolean {
|
|
|
28
29
|
export const protectedExport = (unprotectedExport) =>
|
|
29
30
|
isPayPalDomain() ? unprotectedExport : undefined;
|
|
30
31
|
/* eslint-enable no-confusing-arrow */
|
|
32
|
+
|
|
33
|
+
// $FlowIssue
|
|
34
|
+
export const localOrStageExport = (unprotectedExport) => {
|
|
35
|
+
const env = getEnv();
|
|
36
|
+
if (env === ENV.LOCAL || env === ENV.STAGE) {
|
|
37
|
+
return unprotectedExport;
|
|
38
|
+
} else {
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
};
|
|
@@ -1,17 +1,81 @@
|
|
|
1
1
|
/* @flow */
|
|
2
|
+
/* eslint-disable eslint-comments/disable-enable-pair */
|
|
3
|
+
/* eslint-disable no-restricted-globals, promise/no-native */
|
|
2
4
|
import { type LoggerType } from "@krakenjs/beaver-logger/src";
|
|
3
|
-
import { ZalgoPromise } from "@krakenjs/zalgo-promise/src";
|
|
4
5
|
import { create, type ZoidComponent } from "@krakenjs/zoid/src";
|
|
5
6
|
import { FPTI_KEY } from "@paypal/sdk-constants/src";
|
|
6
7
|
|
|
7
8
|
import { ValidationError } from "../lib";
|
|
8
9
|
|
|
10
|
+
type MerchantPayloadData = {|
|
|
11
|
+
amount: string,
|
|
12
|
+
currency: string,
|
|
13
|
+
nonce: string,
|
|
14
|
+
threeDSRequested?: boolean, // do we want to keep this name or align it with other 3DS documentation
|
|
15
|
+
transactionContext?: Object,
|
|
16
|
+
// experience context
|
|
17
|
+
|};
|
|
18
|
+
|
|
19
|
+
// eslint-disable-next-line no-undef
|
|
20
|
+
type Request = <TRequestData, TResponse>({|
|
|
21
|
+
method?: string,
|
|
22
|
+
url: string,
|
|
23
|
+
// eslint-disable-next-line no-undef
|
|
24
|
+
data: TRequestData,
|
|
25
|
+
accessToken: ?string,
|
|
26
|
+
// eslint-disable-next-line no-undef
|
|
27
|
+
|}) => Promise<TResponse>;
|
|
28
|
+
|
|
29
|
+
type requestData = {|
|
|
30
|
+
intent: "THREE_DS_VERIFICATION",
|
|
31
|
+
payment_source: {|
|
|
32
|
+
card: {|
|
|
33
|
+
single_use_token: string,
|
|
34
|
+
verification_method: string,
|
|
35
|
+
|},
|
|
36
|
+
|},
|
|
37
|
+
amount: {|
|
|
38
|
+
currency_code: string,
|
|
39
|
+
value: string,
|
|
40
|
+
|},
|
|
41
|
+
transaction_context?: {|
|
|
42
|
+
soft_descriptor?: string,
|
|
43
|
+
|},
|
|
44
|
+
|};
|
|
45
|
+
|
|
46
|
+
type responseBody = {|
|
|
47
|
+
payment_id: string,
|
|
48
|
+
status: string,
|
|
49
|
+
intent: string,
|
|
50
|
+
payment_source: {|
|
|
51
|
+
card: {|
|
|
52
|
+
last_digits: string,
|
|
53
|
+
type: string,
|
|
54
|
+
name: string,
|
|
55
|
+
expiry: string,
|
|
56
|
+
|},
|
|
57
|
+
|},
|
|
58
|
+
amount: {|
|
|
59
|
+
currency_code: string,
|
|
60
|
+
value: string,
|
|
61
|
+
|},
|
|
62
|
+
transaction_context: {|
|
|
63
|
+
soft_descriptor: string,
|
|
64
|
+
|},
|
|
65
|
+
links: $ReadOnlyArray<{|
|
|
66
|
+
href: string,
|
|
67
|
+
rel: string,
|
|
68
|
+
method: string,
|
|
69
|
+
|}>,
|
|
70
|
+
|};
|
|
71
|
+
|
|
9
72
|
type SdkConfig = {|
|
|
10
|
-
|
|
73
|
+
authenticationToken: ?string,
|
|
74
|
+
paypalApiDomain: string,
|
|
11
75
|
|};
|
|
12
76
|
|
|
13
77
|
const parseSdkConfig = ({ sdkConfig, logger }): SdkConfig => {
|
|
14
|
-
if (!sdkConfig.
|
|
78
|
+
if (!sdkConfig.authenticationToken) {
|
|
15
79
|
throw new ValidationError(
|
|
16
80
|
`script data attribute sdk-client-token is required but was not passed`
|
|
17
81
|
);
|
|
@@ -23,29 +87,99 @@ const parseSdkConfig = ({ sdkConfig, logger }): SdkConfig => {
|
|
|
23
87
|
|
|
24
88
|
return sdkConfig;
|
|
25
89
|
};
|
|
90
|
+
|
|
91
|
+
const parseMerchantPayload = ({
|
|
92
|
+
merchantPayload,
|
|
93
|
+
}: {|
|
|
94
|
+
merchantPayload: MerchantPayloadData,
|
|
95
|
+
|}): requestData => {
|
|
96
|
+
// what validation on merchant input should we do here?
|
|
97
|
+
// empty object
|
|
98
|
+
const { threeDSRequested, amount, currency, nonce, transactionContext } =
|
|
99
|
+
merchantPayload;
|
|
100
|
+
|
|
101
|
+
// amount - validate that it's a string
|
|
102
|
+
// currency - validate that it's a string
|
|
103
|
+
// what validations are done on the API end - what client side validation is the API expecting
|
|
104
|
+
|
|
105
|
+
return {
|
|
106
|
+
intent: "THREE_DS_VERIFICATION",
|
|
107
|
+
payment_source: {
|
|
108
|
+
card: {
|
|
109
|
+
single_use_token: nonce,
|
|
110
|
+
verification_method: threeDSRequested
|
|
111
|
+
? "SCA_ALWAYS"
|
|
112
|
+
: "SCA_WHEN_REQUIRED",
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
amount: {
|
|
116
|
+
currency_code: currency,
|
|
117
|
+
value: amount,
|
|
118
|
+
},
|
|
119
|
+
...transactionContext,
|
|
120
|
+
};
|
|
121
|
+
};
|
|
122
|
+
|
|
26
123
|
export interface ThreeDomainSecureComponentInterface {
|
|
27
|
-
isEligible():
|
|
124
|
+
isEligible(): Promise<boolean>;
|
|
28
125
|
show(): ZoidComponent<void>;
|
|
29
126
|
}
|
|
30
127
|
export class ThreeDomainSecureComponent {
|
|
31
128
|
logger: LoggerType;
|
|
129
|
+
request: Request;
|
|
32
130
|
sdkConfig: SdkConfig;
|
|
131
|
+
authenticationURL: string;
|
|
33
132
|
|
|
34
133
|
constructor({
|
|
35
134
|
logger,
|
|
135
|
+
request,
|
|
36
136
|
sdkConfig,
|
|
37
137
|
}: {|
|
|
38
138
|
logger: LoggerType,
|
|
139
|
+
request: Request,
|
|
39
140
|
sdkConfig: SdkConfig,
|
|
40
141
|
|}) {
|
|
41
142
|
this.logger = logger;
|
|
143
|
+
this.request = request;
|
|
42
144
|
this.sdkConfig = parseSdkConfig({ sdkConfig, logger });
|
|
43
145
|
}
|
|
44
146
|
|
|
45
|
-
isEligible():
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
147
|
+
async isEligible(merchantPayload: MerchantPayloadData): Promise<boolean> {
|
|
148
|
+
const data = parseMerchantPayload({ merchantPayload });
|
|
149
|
+
|
|
150
|
+
try {
|
|
151
|
+
const { status, links } = await this.request<requestData, responseBody>({
|
|
152
|
+
method: "POST",
|
|
153
|
+
url: `${this.sdkConfig.paypalApiDomain}/v2/payments/payment`,
|
|
154
|
+
data,
|
|
155
|
+
accessToken: this.sdkConfig.authenticationToken,
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
let responseStatus = false;
|
|
159
|
+
if (status === "PAYER_ACTION_REQUIRED") {
|
|
160
|
+
this.authenticationURL = links[0].href;
|
|
161
|
+
// check for rel = payer action inside the object
|
|
162
|
+
responseStatus = true;
|
|
163
|
+
}
|
|
164
|
+
return responseStatus;
|
|
165
|
+
} catch (error) {
|
|
166
|
+
this.logger.warn(error);
|
|
167
|
+
return false;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// change name to isContingent??
|
|
171
|
+
// will return true or false
|
|
172
|
+
// if payer action required, return true. obtain link from response for show method - check length of links
|
|
173
|
+
|
|
174
|
+
// if payer action not required, return false
|
|
175
|
+
|
|
176
|
+
// will make API request to v2/payments/pamyment endpoint with merchant payload an grab sdktoken as
|
|
177
|
+
// bearer token
|
|
178
|
+
|
|
179
|
+
// will need to handle errors from API response
|
|
180
|
+
// What are the other options for status response and how do we handle them from a compliance standpoint
|
|
181
|
+
// What do we do if we get a 500 error from the API?
|
|
182
|
+
// do we throw an error or return false?
|
|
49
183
|
}
|
|
50
184
|
|
|
51
185
|
show() {
|
|
@@ -4,11 +4,13 @@ import { describe, expect, vi } from "vitest";
|
|
|
4
4
|
import { ThreeDomainSecureComponent } from "./component";
|
|
5
5
|
|
|
6
6
|
const defaultSdkConfig = {
|
|
7
|
-
|
|
7
|
+
authenticationToken: "sdk-client-token",
|
|
8
8
|
};
|
|
9
9
|
|
|
10
10
|
const createThreeDomainSecureComponent = ({
|
|
11
11
|
sdkConfig = defaultSdkConfig,
|
|
12
|
+
// $FlowFixMe
|
|
13
|
+
request,
|
|
12
14
|
logger = {
|
|
13
15
|
info: vi.fn().mockReturnThis(),
|
|
14
16
|
warn: vi.fn().mockReturnThis(),
|
|
@@ -18,7 +20,9 @@ const createThreeDomainSecureComponent = ({
|
|
|
18
20
|
},
|
|
19
21
|
} = {}) =>
|
|
20
22
|
new ThreeDomainSecureComponent({
|
|
23
|
+
// $FlowFixMe
|
|
21
24
|
sdkConfig,
|
|
25
|
+
request,
|
|
22
26
|
// $FlowIssue
|
|
23
27
|
logger,
|
|
24
28
|
});
|
|
@@ -28,8 +32,17 @@ afterEach(() => {
|
|
|
28
32
|
});
|
|
29
33
|
|
|
30
34
|
describe("three domain secure component - isEligible method", () => {
|
|
31
|
-
test("should return false", async () => {
|
|
35
|
+
test.skip("should return false", async () => {
|
|
36
|
+
// successful response
|
|
37
|
+
// true for payer_action - false for Completed
|
|
38
|
+
|
|
39
|
+
// parameter validation
|
|
40
|
+
// testing for negative parameter such as null or invalid value
|
|
41
|
+
// error handling for API response
|
|
42
|
+
|
|
43
|
+
// mock the getpaypalapidomain so that it always returns the value that we expect
|
|
32
44
|
const threeDomainSecuretClient = createThreeDomainSecureComponent();
|
|
45
|
+
// $FlowFixMe
|
|
33
46
|
const eligibility = await threeDomainSecuretClient.isEligible();
|
|
34
47
|
expect(eligibility).toEqual(false);
|
|
35
48
|
});
|
|
@@ -49,7 +62,7 @@ describe("three domain secure component - initialization", () => {
|
|
|
49
62
|
createThreeDomainSecureComponent({
|
|
50
63
|
sdkConfig: {
|
|
51
64
|
...defaultSdkConfig,
|
|
52
|
-
|
|
65
|
+
authenticationToken: "",
|
|
53
66
|
},
|
|
54
67
|
})
|
|
55
68
|
).toThrowError(
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
/* @flow */
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
getLogger,
|
|
4
|
+
getPayPalAPIDomain,
|
|
5
|
+
getUserIDToken,
|
|
6
|
+
} from "@paypal/sdk-client/src";
|
|
3
7
|
|
|
8
|
+
import { callRestAPI, localOrStageExport } from "../lib";
|
|
4
9
|
import type { LazyExport } from "../types";
|
|
5
|
-
import { protectedExport } from "../lib";
|
|
6
10
|
|
|
7
11
|
import {
|
|
8
12
|
ThreeDomainSecureComponent,
|
|
@@ -14,12 +18,15 @@ export const ThreeDomainSecureClient: LazyExport<ThreeDomainSecureComponentInter
|
|
|
14
18
|
__get__: () => {
|
|
15
19
|
const threeDomainSecureInstance = new ThreeDomainSecureComponent({
|
|
16
20
|
logger: getLogger(),
|
|
21
|
+
// $FlowIssue ZalgoPromise vs Promise
|
|
22
|
+
request: callRestAPI,
|
|
17
23
|
sdkConfig: {
|
|
18
|
-
|
|
24
|
+
authenticationToken: getUserIDToken(),
|
|
25
|
+
paypalApiDomain: getPayPalAPIDomain(),
|
|
19
26
|
},
|
|
20
27
|
});
|
|
21
|
-
return
|
|
22
|
-
isEligible: () => threeDomainSecureInstance.isEligible(),
|
|
28
|
+
return localOrStageExport({
|
|
29
|
+
isEligible: (payload) => threeDomainSecureInstance.isEligible(payload),
|
|
23
30
|
show: () => threeDomainSecureInstance.show(),
|
|
24
31
|
});
|
|
25
32
|
},
|