Skip to content

Commit

Permalink
feat(payment): PAYPAL-4937 updated PaymentMethodRequestSender by addi…
Browse files Browse the repository at this point in the history
…ng an additional method
  • Loading branch information
bc-nick committed Dec 16, 2024
1 parent 6de92bf commit a6ce0d0
Show file tree
Hide file tree
Showing 10 changed files with 204 additions and 2 deletions.
24 changes: 23 additions & 1 deletion packages/core/src/common/http-request/responses.mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import { Response } from '@bigcommerce/request-sender';

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

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

export function getResponse<T>(
body: T,
Expand Down Expand Up @@ -38,6 +39,27 @@ export function getPaymentResponse<T>(
};
}

export function getHeadlessPaymentResponse<T>(
site: HeadlessPaymentMethod<T>,
headers = {},
status = 200,
statusText = 'OK',
): Response<HeadlessPaymentMethodResponse<T>> {
return {
body: {
data: {
site,
},
},
status,
statusText,
headers: {
'content-type': 'application/json',
...headers,
},
};
}

export function getErrorResponse(
body = getErrorResponseBody(),
headers = {},
Expand Down
8 changes: 8 additions & 0 deletions packages/core/src/payment/headless-payment-method-config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { HeadlessPaymentMethodType } from './headless-payment-method-type';

const HeadlessPaymentMethodConfig: Record<string, HeadlessPaymentMethodType> = {
paypalcommerce: HeadlessPaymentMethodType.PAYPALCOMMERCE,
paypalcommercecredit: HeadlessPaymentMethodType.PAYPALCOMMERCECREDIT,
};

export default HeadlessPaymentMethodConfig;
7 changes: 7 additions & 0 deletions packages/core/src/payment/headless-payment-method-response.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import HeadlessPaymentMethod from './headless-payment-method';

export interface HeadlessPaymentMethodResponse<T = any> {
data: {
site: HeadlessPaymentMethod<T>;
}
}
4 changes: 4 additions & 0 deletions packages/core/src/payment/headless-payment-method-type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export enum HeadlessPaymentMethodType {
PAYPALCOMMERCE = 'paypalcommerce.paypal',
PAYPALCOMMERCECREDIT = 'paypalcommerce.paypalcredit',
}
6 changes: 6 additions & 0 deletions packages/core/src/payment/headless-payment-method.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export default interface HeadlessPaymentMethod<T = any> {
paymentWalletWithInitializationData: {
clientToken?: string;
initializationData?: T;
};
}
12 changes: 12 additions & 0 deletions packages/core/src/payment/headless-payment-methods.mock.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import HeadlessPaymentMethod from './headless-payment-method';

export function getHeadlessPaymentMethod(): HeadlessPaymentMethod {
return {
paymentWalletWithInitializationData: {
clientToken: 'clientToken',
// INFO:: base64 decoded to the following object => {"merchantId": "100000","paymentButtonStyles": {"checkoutTopButtonStyles": {"color": "blue","label": "checkout","height": "36"}}}
initializationData:
'eyJtZXJjaGFudElkIjogIjEwMDAwMCIsInBheW1lbnRCdXR0b25TdHlsZXMiOiB7ImNoZWNrb3V0VG9wQnV0dG9uU3R5bGVzIjogeyJjb2xvciI6ICJibHVlIiwibGFiZWwiOiAiY2hlY2tvdXQiLCJoZWlnaHQiOiAiMzYifX19',
},
};
}
6 changes: 6 additions & 0 deletions packages/core/src/payment/headless-payment-request-options.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { RequestOptions } from '../common/http-request';

export default interface HeadlessPaymentRequestOptions extends RequestOptions {
body?: { query: string };
headers: { Authorization: string; [key: string]: string };
}
5 changes: 5 additions & 0 deletions packages/core/src/payment/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export {
export * from './payment-method-actions';
export * from './payment-method-types';
export * from './payment-status-types';
export * from './headless-payment-method-type';

export {
default as PaymentAdditionalAction,
Expand Down Expand Up @@ -37,12 +38,15 @@ export {
WithMollieIssuerInstrument,
} from './payment';
export { default as PaymentMethod } from './payment-method';
export { default as HeadlessPaymentMethod } from './headless-payment-method';
export { default as PaymentMethodMeta } from './payment-method-meta';
export { default as PaymentMethodConfig } from './payment-method-config';
export { default as HeadlessPaymentMethodConfig } from './headless-payment-method-config';
export { default as InitializationStrategy } from './payment-method-initialization-strategy';
export { default as PaymentMethodActionCreator } from './payment-method-action-creator';
export { default as paymentMethodReducer } from './payment-method-reducer';
export { default as PaymentMethodRequestSender } from './payment-method-request-sender';
export { default as HeadlessPaymentRequestOptions } from './headless-payment-request-options';
export {
default as PaymentMethodSelector,
PaymentMethodSelectorFactory,
Expand All @@ -55,6 +59,7 @@ export { default as PaymentRequestSender } from './payment-request-sender';
export { default as PaymentRequestTransformer } from './payment-request-transformer';
export { default as PaymentResponse } from './payment-response';
export { default as PaymentResponseBody } from './payment-response-body';
export { HeadlessPaymentMethodResponse } from './headless-payment-method-response';
export {
default as PaymentSelector,
PaymentSelectorFactory,
Expand Down
55 changes: 54 additions & 1 deletion packages/core/src/payment/payment-method-request-sender.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@ import {
} from '@bigcommerce/request-sender';

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

import { HeadlessPaymentMethodResponse } from './headless-payment-method-response';
import { getHeadlessPaymentMethod } from './headless-payment-methods.mock';
import PaymentMethod from './payment-method';
import PaymentMethodRequestSender from './payment-method-request-sender';
import { getPaymentMethod, getPaymentMethods } from './payment-methods.mock';
Expand Down Expand Up @@ -136,4 +138,55 @@ describe('PaymentMethodRequestSender', () => {
});
});
});

describe('#loadPaymentWalletWithInitializationData()', () => {
let response: Response<HeadlessPaymentMethodResponse>;
const authorization = 'authorization-1234';

beforeEach(() => {
response = getHeadlessPaymentResponse(getHeadlessPaymentMethod());
jest.spyOn(requestSender, 'post').mockReturnValue(Promise.resolve(response));
});

it('loads headless payment method', async () => {
const walletInitData =
await paymentMethodRequestSender.loadPaymentWalletWithInitializationData(
'paypalcommerce',
{ headers: { Authorization: authorization } },
);

expect(requestSender.post).toHaveBeenCalledWith(
'/graphql',
expect.objectContaining({
headers: {
Authorization: authorization,
'Content-Type': 'application/json',
},
}),
);

expect(walletInitData).toEqual(
expect.objectContaining({
body: {
initializationData: {
merchantId: '100000',
paymentButtonStyles: {
checkoutTopButtonStyles: {
color: 'blue',
label: 'checkout',
height: '36',
},
},
},
clientToken: 'clientToken',
id: 'paypalcommerce',
config: {},
method: '',
supportedCards: [],
type: 'PAYMENT_TYPE_API',
},
}),
);
});
});
});
79 changes: 79 additions & 0 deletions packages/core/src/payment/payment-method-request-sender.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ import {
SDK_VERSION_HEADERS,
} from '../common/http-request';

import HeadlessPaymentMethodConfig from './headless-payment-method-config';
import { HeadlessPaymentMethodResponse } from './headless-payment-method-response';
import { HeadlessPaymentMethodType } from './headless-payment-method-type';
import HeadlessPaymentRequestOptions from './headless-payment-request-options';
import PaymentMethod from './payment-method';

export default class PaymentMethodRequestSender {
Expand Down Expand Up @@ -44,4 +48,79 @@ export default class PaymentMethodRequestSender {
params,
});
}

/**
* GraphQL payment requests
*/
loadPaymentWalletWithInitializationData(
methodId: string,
options: HeadlessPaymentRequestOptions,
): Promise<Response<PaymentMethod>> {
const url = `/graphql`;

const entityId = this.getPaymentEntityId(methodId);

const graphQLQuery = `
query {
site {
paymentWalletWithInitializationData(filter: {paymentWalletEntityId: "${entityId}"}) {
clientToken
initializationData
}
}
}
`;

const requestOptions: HeadlessPaymentRequestOptions = {
headers: {
...options.headers,
'Content-Type': 'application/json',
},
body: {
query: graphQLQuery,
},
};

return this._requestSender
.post<HeadlessPaymentMethodResponse<string>>(url, requestOptions)
.then((response) => this.transformToPaymentMethodResponse(response, methodId));
}

private transformToPaymentMethodResponse(
response: Response<HeadlessPaymentMethodResponse>,
methodId: string,
): Response<PaymentMethod> {
const {
body: {
data: {
site: { paymentWalletWithInitializationData },
},
},
} = response;

return {
...response,
body: {
initializationData: JSON.parse(
atob(paymentWalletWithInitializationData.initializationData),
),
clientToken: paymentWalletWithInitializationData.clientToken,
id: methodId,
config: {},
method: '',
supportedCards: [],
type: 'PAYMENT_TYPE_API',
},
};
}

private getPaymentEntityId(methodId: string): HeadlessPaymentMethodType {
const entityId = HeadlessPaymentMethodConfig[methodId];

if (!entityId) {
throw new Error('Unable to get payment entity id.');
}

return entityId;
}
}

0 comments on commit a6ce0d0

Please sign in to comment.