Skip to content

Commit 74aa351

Browse files
committed
feat(payment): PAYPAL-4937 updated PaymentMethodRequestSender by adding an additional method
1 parent 6de92bf commit 74aa351

10 files changed

+202
-2
lines changed

packages/core/src/common/http-request/responses.mock.ts

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ import { Response } from '@bigcommerce/request-sender';
22

33
import { ErrorResponseBody } from '@bigcommerce/checkout-sdk/payment-integration-api';
44

5-
import { PaymentResponse } from '../../payment';
5+
import { HeadlessPaymentMethodResponse, PaymentResponse } from '../../payment';
6+
import HeadlessPaymentMethod from '../../payment/headless-payment-method';
67

78
export function getResponse<T>(
89
body: T,
@@ -38,6 +39,27 @@ export function getPaymentResponse<T>(
3839
};
3940
}
4041

42+
export function getHeadlessPaymentResponse<T>(
43+
site: HeadlessPaymentMethod<T>,
44+
headers = {},
45+
status = 200,
46+
statusText = 'OK',
47+
): Response<HeadlessPaymentMethodResponse<T>> {
48+
return {
49+
body: {
50+
data: {
51+
site,
52+
},
53+
},
54+
status,
55+
statusText,
56+
headers: {
57+
'content-type': 'application/json',
58+
...headers,
59+
},
60+
};
61+
}
62+
4163
export function getErrorResponse(
4264
body = getErrorResponseBody(),
4365
headers = {},
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { HeadlessPaymentMethodType } from './headless-payment-method-type';
2+
3+
const HeadlessPaymentMethodConfig: Record<string, HeadlessPaymentMethodType> = {
4+
paypalcommerce: HeadlessPaymentMethodType.PAYPALCOMMERCE,
5+
paypalcommercecredit: HeadlessPaymentMethodType.PAYPALCOMMERCECREDIT,
6+
};
7+
8+
export default HeadlessPaymentMethodConfig;
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import HeadlessPaymentMethod from './headless-payment-method';
2+
3+
export interface HeadlessPaymentMethodResponse<T = any> {
4+
data: {
5+
site: HeadlessPaymentMethod<T>;
6+
};
7+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export enum HeadlessPaymentMethodType {
2+
PAYPALCOMMERCE = 'paypalcommerce.paypal',
3+
PAYPALCOMMERCECREDIT = 'paypalcommerce.paypalcredit',
4+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export default interface HeadlessPaymentMethod<T = any> {
2+
paymentWalletWithInitializationData: {
3+
clientToken?: string;
4+
initializationData?: T;
5+
};
6+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import HeadlessPaymentMethod from './headless-payment-method';
2+
3+
export const initializationData = {
4+
merchantId: '100000',
5+
paymentButtonStyles: {
6+
checkoutTopButtonStyles: { color: 'blue', label: 'checkout', height: '36' },
7+
},
8+
};
9+
10+
export const encodedInitializationData = btoa(JSON.stringify(initializationData));
11+
12+
export function getHeadlessPaymentMethod(): HeadlessPaymentMethod {
13+
return {
14+
paymentWalletWithInitializationData: {
15+
clientToken: 'clientToken',
16+
initializationData: encodedInitializationData,
17+
},
18+
};
19+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { RequestOptions } from '../common/http-request';
2+
3+
export default interface HeadlessPaymentRequestOptions extends RequestOptions {
4+
body?: { query: string };
5+
headers: { Authorization: string; [key: string]: string };
6+
}

packages/core/src/payment/index.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ export {
66
export * from './payment-method-actions';
77
export * from './payment-method-types';
88
export * from './payment-status-types';
9+
export * from './headless-payment-method-type';
910

1011
export {
1112
default as PaymentAdditionalAction,
@@ -37,12 +38,15 @@ export {
3738
WithMollieIssuerInstrument,
3839
} from './payment';
3940
export { default as PaymentMethod } from './payment-method';
41+
export { default as HeadlessPaymentMethod } from './headless-payment-method';
4042
export { default as PaymentMethodMeta } from './payment-method-meta';
4143
export { default as PaymentMethodConfig } from './payment-method-config';
44+
export { default as HeadlessPaymentMethodConfig } from './headless-payment-method-config';
4245
export { default as InitializationStrategy } from './payment-method-initialization-strategy';
4346
export { default as PaymentMethodActionCreator } from './payment-method-action-creator';
4447
export { default as paymentMethodReducer } from './payment-method-reducer';
4548
export { default as PaymentMethodRequestSender } from './payment-method-request-sender';
49+
export { default as HeadlessPaymentRequestOptions } from './headless-payment-request-options';
4650
export {
4751
default as PaymentMethodSelector,
4852
PaymentMethodSelectorFactory,
@@ -55,6 +59,7 @@ export { default as PaymentRequestSender } from './payment-request-sender';
5559
export { default as PaymentRequestTransformer } from './payment-request-transformer';
5660
export { default as PaymentResponse } from './payment-response';
5761
export { default as PaymentResponseBody } from './payment-response-body';
62+
export { HeadlessPaymentMethodResponse } from './headless-payment-method-response';
5863
export {
5964
default as PaymentSelector,
6065
PaymentSelectorFactory,

packages/core/src/payment/payment-method-request-sender.spec.ts

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@ import {
66
} from '@bigcommerce/request-sender';
77

88
import { ContentType, INTERNAL_USE_ONLY, SDK_VERSION_HEADERS } from '../common/http-request';
9-
import { getResponse } from '../common/http-request/responses.mock';
9+
import { getHeadlessPaymentResponse, getResponse } from '../common/http-request/responses.mock';
1010

11+
import { HeadlessPaymentMethodResponse } from './headless-payment-method-response';
12+
import { getHeadlessPaymentMethod, initializationData } from './headless-payment-methods.mock';
1113
import PaymentMethod from './payment-method';
1214
import PaymentMethodRequestSender from './payment-method-request-sender';
1315
import { getPaymentMethod, getPaymentMethods } from './payment-methods.mock';
@@ -136,4 +138,46 @@ describe('PaymentMethodRequestSender', () => {
136138
});
137139
});
138140
});
141+
142+
describe('#loadPaymentWalletWithInitializationData()', () => {
143+
let response: Response<HeadlessPaymentMethodResponse>;
144+
const authorization = 'authorization-1234';
145+
146+
beforeEach(() => {
147+
response = getHeadlessPaymentResponse(getHeadlessPaymentMethod());
148+
jest.spyOn(requestSender, 'post').mockReturnValue(Promise.resolve(response));
149+
});
150+
151+
it('loads headless payment method', async () => {
152+
const walletInitData =
153+
await paymentMethodRequestSender.loadPaymentWalletWithInitializationData(
154+
'paypalcommerce',
155+
{ headers: { Authorization: authorization } },
156+
);
157+
158+
expect(requestSender.post).toHaveBeenCalledWith(
159+
'/graphql',
160+
expect.objectContaining({
161+
headers: {
162+
Authorization: authorization,
163+
'Content-Type': 'application/json',
164+
},
165+
}),
166+
);
167+
168+
expect(walletInitData).toEqual(
169+
expect.objectContaining({
170+
body: {
171+
initializationData,
172+
clientToken: 'clientToken',
173+
id: 'paypalcommerce',
174+
config: {},
175+
method: '',
176+
supportedCards: [],
177+
type: 'PAYMENT_TYPE_API',
178+
},
179+
}),
180+
);
181+
});
182+
});
139183
});

packages/core/src/payment/payment-method-request-sender.ts

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ import {
77
SDK_VERSION_HEADERS,
88
} from '../common/http-request';
99

10+
import HeadlessPaymentMethodConfig from './headless-payment-method-config';
11+
import { HeadlessPaymentMethodResponse } from './headless-payment-method-response';
12+
import { HeadlessPaymentMethodType } from './headless-payment-method-type';
13+
import HeadlessPaymentRequestOptions from './headless-payment-request-options';
1014
import PaymentMethod from './payment-method';
1115

1216
export default class PaymentMethodRequestSender {
@@ -44,4 +48,79 @@ export default class PaymentMethodRequestSender {
4448
params,
4549
});
4650
}
51+
52+
/**
53+
* GraphQL payment requests
54+
*/
55+
loadPaymentWalletWithInitializationData(
56+
methodId: string,
57+
options: HeadlessPaymentRequestOptions,
58+
): Promise<Response<PaymentMethod>> {
59+
const url = `/graphql`;
60+
61+
const entityId = this.getPaymentEntityId(methodId);
62+
63+
const graphQLQuery = `
64+
query {
65+
site {
66+
paymentWalletWithInitializationData(filter: {paymentWalletEntityId: "${entityId}"}) {
67+
clientToken
68+
initializationData
69+
}
70+
}
71+
}
72+
`;
73+
74+
const requestOptions: HeadlessPaymentRequestOptions = {
75+
headers: {
76+
...options.headers,
77+
'Content-Type': 'application/json',
78+
},
79+
body: {
80+
query: graphQLQuery,
81+
},
82+
};
83+
84+
return this._requestSender
85+
.post<HeadlessPaymentMethodResponse<string>>(url, requestOptions)
86+
.then((response) => this.transformToPaymentMethodResponse(response, methodId));
87+
}
88+
89+
private transformToPaymentMethodResponse(
90+
response: Response<HeadlessPaymentMethodResponse>,
91+
methodId: string,
92+
): Response<PaymentMethod> {
93+
const {
94+
body: {
95+
data: {
96+
site: { paymentWalletWithInitializationData },
97+
},
98+
},
99+
} = response;
100+
101+
return {
102+
...response,
103+
body: {
104+
initializationData: JSON.parse(
105+
atob(paymentWalletWithInitializationData.initializationData),
106+
),
107+
clientToken: paymentWalletWithInitializationData.clientToken,
108+
id: methodId,
109+
config: {},
110+
method: '',
111+
supportedCards: [],
112+
type: 'PAYMENT_TYPE_API',
113+
},
114+
};
115+
}
116+
117+
private getPaymentEntityId(methodId: string): HeadlessPaymentMethodType {
118+
const entityId = HeadlessPaymentMethodConfig[methodId];
119+
120+
if (!entityId) {
121+
throw new Error('Unable to get payment entity id.');
122+
}
123+
124+
return entityId;
125+
}
47126
}

0 commit comments

Comments
 (0)