Skip to content

Commit

Permalink
fix(braintree): pass merchant-id through to checkout session (#265)
Browse files Browse the repository at this point in the history
  • Loading branch information
borodovisin committed May 9, 2022
1 parent 39911ae commit 1844fd5
Show file tree
Hide file tree
Showing 6 changed files with 120 additions and 5 deletions.
61 changes: 61 additions & 0 deletions src/components/braintree/BraintreePayPalButtons.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
BRAINTREE_SOURCE,
BRAINTREE_PAYPAL_CHECKOUT_SOURCE,
LOAD_SCRIPT_ERROR,
BRAINTREE_MULTIPLE_MERCHANT_IDS_ERROR_MESSAGE,
} from "../../constants";
import { FUNDING } from "../../index";

Expand Down Expand Up @@ -335,4 +336,64 @@ describe("render Braintree PayPal button component", () => {
});
});
});

test.each(["merchantId", ["merchantId"]])(
"should call paypalCheckout.create with merchantId option",
async (merchant) => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const mockFuntion = (window as any).braintree.paypalCheckout.create;
const options = {
"client-id": "test",
"merchant-id": merchant,
"data-client-token": `${CLIENT_TOKEN}`,
};

render(
<PayPalScriptProvider options={options}>
<BraintreePayPalButtons
style={{ layout: "horizontal" }}
fundingSource={FUNDING.CREDIT}
createOrder={jest.fn()}
onApprove={jest.fn()}
/>
</PayPalScriptProvider>
);

await waitFor(() => {
expect(mockFuntion).toBeCalledWith({
client: expect.any(Object),
merchantAccountId: "merchantId",
});
});
}
);

test("should throw and error when multiple merchantIds are configured", async () => {
const spyConsoleError = jest
.spyOn(console, "error")
.mockImplementation();
const options = {
"client-id": "test",
"merchant-id": ["merchant1", "merchant2"],
"data-client-token": `${CLIENT_TOKEN}`,
};
render(
<PayPalScriptProvider options={options}>
<BraintreePayPalButtons
style={{ layout: "horizontal" }}
fundingSource={FUNDING.CREDIT}
createOrder={jest.fn()}
onApprove={jest.fn()}
/>
</PayPalScriptProvider>,
{ wrapper }
);
await waitFor(() => expect(onError).toBeCalled());
expect(onError.mock.calls[0][0].message).toEqual(
expect.stringContaining(
BRAINTREE_MULTIPLE_MERCHANT_IDS_ERROR_MESSAGE
)
);
spyConsoleError.mockRestore();
});
});
7 changes: 6 additions & 1 deletion src/components/braintree/BraintreePayPalButtons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import React, { useState, useEffect, type FC } from "react";
import { SDK_SETTINGS, LOAD_SCRIPT_ERROR } from "../../constants";
import { PayPalButtons } from "../PayPalButtons";
import { useScriptProviderContext } from "../../hooks/scriptProviderHooks";
import { decorateActions, getBraintreeNamespace } from "./utils";
import { decorateActions, getBraintreeNamespace, getMerchantId } from "./utils";
import {
DISPATCH_ACTION,
type BraintreePayPalButtonsComponentProps,
Expand Down Expand Up @@ -47,6 +47,11 @@ export const BraintreePayPalButtons: FC<
.then((clientInstance) => {
return braintree.paypalCheckout.create({
client: clientInstance,
merchantAccountId: getMerchantId(
providerContext.options[
SDK_SETTINGS.MERCHANT_CLIENT_ID
]
),
});
})
.then((paypalCheckoutInstance) => {
Expand Down
28 changes: 27 additions & 1 deletion src/components/braintree/utils.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { mock } from "jest-mock-extended";

import { decorateActions, getBraintreeNamespace } from "./utils";
import { decorateActions, getBraintreeNamespace, getMerchantId } from "./utils";
import { getBraintreeWindowNamespace } from "../../utils";
import { BRAINTREE_MULTIPLE_MERCHANT_IDS_ERROR_MESSAGE } from "../../constants";

import type { BraintreePayPalCheckout } from "../../types/braintree/paypalCheckout";
import type { CreateBillingAgreementActions } from "../..";
Expand Down Expand Up @@ -195,3 +196,28 @@ describe("getBraintreeNamespace", () => {
}
});
});

describe("getMerchantId", () => {
test("should return undefined", () => {
expect(getMerchantId(undefined)).toBeUndefined();
});

test("should return an empty string", () => {
expect(getMerchantId([])).toBe("");
});

test.each(["merchantId", ["merchantId"]])(
"should return the merchant when is string or an array with one value",
(value) => {
expect(getMerchantId(value)).toBe("merchantId");
}
);

test("should throw an error when source has more than 1 value", () => {
expect(() => {
getMerchantId(["1", "2"]);
}).toThrowError(
new Error(BRAINTREE_MULTIPLE_MERCHANT_IDS_ERROR_MESSAGE)
);
});
});
20 changes: 20 additions & 0 deletions src/components/braintree/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { getBraintreeWindowNamespace } from "../../utils";
import {
BRAINTREE_SOURCE,
BRAINTREE_PAYPAL_CHECKOUT_SOURCE,
BRAINTREE_MULTIPLE_MERCHANT_IDS_ERROR_MESSAGE,
} from "../../constants";

import type { BraintreeNamespace } from "./../../types/braintreePayPalButtonTypes";
Expand Down Expand Up @@ -97,3 +98,22 @@ export const getBraintreeNamespace = (
loadCustomScript({ url: BRAINTREE_PAYPAL_CHECKOUT_SOURCE }),
]).then(() => getBraintreeWindowNamespace());
};

/**
* Get the merchantId from the source list
*
* @param {Array|string} source - the source list with merchant identifiers
* @throws {Error} when the merchant list has more than 1 value
* @returns {string|undefined} the merchantId or an undefined value
*/
export const getMerchantId = (
source?: Array<string> | string
): string | undefined => {
const isSourceArray = Array.isArray(source);

if (isSourceArray && source.length > 1) {
throw new Error(BRAINTREE_MULTIPLE_MERCHANT_IDS_ERROR_MESSAGE);
}

return source?.toString();
};
3 changes: 3 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
*********************************************/
export const SCRIPT_ID = "data-react-paypal-script-id";
export const SDK_SETTINGS = {
MERCHANT_CLIENT_ID: "merchant-id",
DATA_CLIENT_TOKEN: "data-client-token",
DATA_USER_ID_TOKEN: "data-user-id-token",
DATA_SDK_INTEGRATION_SOURCE: "data-sdk-integration-source",
Expand All @@ -16,6 +17,8 @@ export const LOAD_SCRIPT_ERROR = "Failed to load the PayPal JS SDK script.";
****************************/
export const EMPTY_BRAINTREE_AUTHORIZATION_ERROR_MESSAGE =
"Invalid authorization data. Use data-client-token or data-user-id-token to authorize.";
export const BRAINTREE_MULTIPLE_MERCHANT_IDS_ERROR_MESSAGE =
"Braintree does not support passing in multiple merchantId values.";

const braintreeVersion = "3.84.0";
export const BRAINTREE_SOURCE = `https://js.braintreegateway.com/web/${braintreeVersion}/js/client.min.js`;
Expand Down
6 changes: 3 additions & 3 deletions src/types/braintree/paypalCheckout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,9 @@ export interface BraintreePayPalCheckout {
* });
*/
create(options: {
client?: BraintreeClient | undefined;
authorization?: string | undefined;
merchantAccountId?: string | undefined;
client?: BraintreeClient;
authorization?: string;
merchantAccountId?: string;
}): Promise<BraintreePayPalCheckout>;

/**
Expand Down

0 comments on commit 1844fd5

Please sign in to comment.