Skip to content

Commit 3e3f3c2

Browse files
authored
Adding dataSdkIntegrationSource to the script tag for analytics purpose (#822)
* Adding dataSdkIntegrationSource to the script tag for analytics purpose * updating changeset * removing merchant control on the dataSdkIntegrationSource
1 parent 0cb1a60 commit 3e3f3c2

File tree

7 files changed

+74
-4
lines changed

7 files changed

+74
-4
lines changed

.changeset/eighty-yaks-juggle.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@paypal/paypal-js": minor
3+
---
4+
5+
Adding dataSdkIntegrationSource to the script tag for analytics purpose

.changeset/forty-pots-open.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@paypal/react-paypal-js": minor
3+
---
4+
5+
Adding dataSdkIntegrationSource to the script tag for analytics purpose

packages/paypal-js/src/v6/index.test.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,43 @@ describe("loadCoreSdkScript()", () => {
9494
});
9595
});
9696

97+
describe("dataSdkIntegrationSource option", () => {
98+
test("should support custom data-sdk-integration-source attribute", async () => {
99+
const integrationSource = "react-paypal-js";
100+
101+
const result = await loadCoreSdkScript({
102+
dataSdkIntegrationSource: integrationSource,
103+
});
104+
105+
expect(mockedInsertScriptElement.mock.calls[0][0].url).toEqual(
106+
"https://www.sandbox.paypal.com/web-sdk/v6/core",
107+
);
108+
expect(
109+
mockedInsertScriptElement.mock.calls[0][0].attributes,
110+
).toEqual({
111+
"data-sdk-integration-source": integrationSource,
112+
});
113+
expect(mockedInsertScriptElement).toHaveBeenCalledTimes(1);
114+
expect(result).toBeDefined();
115+
});
116+
117+
test("should error when dataSdkIntegrationSource is an empty string", async () => {
118+
expect(async () => {
119+
await loadCoreSdkScript({ dataSdkIntegrationSource: "" });
120+
}).rejects.toThrowError(
121+
'The "dataSdkIntegrationSource" option cannot be an empty string',
122+
);
123+
});
124+
125+
test("should error when dataSdkIntegrationSource is only whitespace", async () => {
126+
expect(async () => {
127+
await loadCoreSdkScript({ dataSdkIntegrationSource: " " });
128+
}).rejects.toThrowError(
129+
'The "dataSdkIntegrationSource" option cannot be an empty string',
130+
);
131+
});
132+
});
133+
97134
test("should return PayPal namespace with version property", async () => {
98135
const result = await loadCoreSdkScript();
99136
expect(result).toBeDefined();

packages/paypal-js/src/v6/index.ts

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ function loadCoreSdkScript(options: LoadCoreSdkScriptOptions = {}) {
2222
return Promise.resolve(window.paypal as unknown as PayPalV6Namespace);
2323
}
2424

25-
const { environment, debug, dataNamespace } = options;
25+
const { environment, debug, dataNamespace, dataSdkIntegrationSource } =
26+
options;
2627
const attributes: Record<string, string> = {};
2728

2829
const baseURL =
@@ -39,6 +40,10 @@ function loadCoreSdkScript(options: LoadCoreSdkScriptOptions = {}) {
3940
attributes["data-namespace"] = dataNamespace;
4041
}
4142

43+
if (dataSdkIntegrationSource) {
44+
attributes["data-sdk-integration-source"] = dataSdkIntegrationSource;
45+
}
46+
4247
return new Promise<PayPalV6Namespace>((resolve, reject) => {
4348
insertScriptElement({
4449
url: url.toString(),
@@ -71,7 +76,8 @@ function validateArguments(options: unknown) {
7176
if (typeof options !== "object" || options === null) {
7277
throw new Error("Expected an options object");
7378
}
74-
const { environment, dataNamespace } = options as LoadCoreSdkScriptOptions;
79+
const { environment, dataNamespace, dataSdkIntegrationSource } =
80+
options as LoadCoreSdkScriptOptions;
7581

7682
if (
7783
environment &&
@@ -86,6 +92,15 @@ function validateArguments(options: unknown) {
8692
if (dataNamespace !== undefined && dataNamespace.trim() === "") {
8793
throw new Error('The "dataNamespace" option cannot be an empty string');
8894
}
95+
96+
if (
97+
dataSdkIntegrationSource !== undefined &&
98+
dataSdkIntegrationSource.trim() === ""
99+
) {
100+
throw new Error(
101+
'The "dataSdkIntegrationSource" option cannot be an empty string',
102+
);
103+
}
89104
}
90105

91106
export { loadCoreSdkScript, version };

packages/paypal-js/types/v6/index.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,7 @@ export interface BaseInstance {
223223

224224
interface CoreSdkScriptDataAttributes {
225225
dataNamespace?: string;
226+
dataSdkIntegrationSource?: string;
226227
}
227228

228229
export interface LoadCoreSdkScriptOptions extends CoreSdkScriptDataAttributes {

packages/react-paypal-js/src/v6/components/PayPalProvider.test.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ describe("PayPalProvider", () => {
138138
expect(loadCoreSdkScript).toHaveBeenCalledWith({
139139
environment: "sandbox",
140140
debug: false,
141+
dataSdkIntegrationSource: "react-paypal-js",
141142
});
142143

143144
await waitFor(() => expectResolvedState(state));
@@ -180,6 +181,7 @@ describe("PayPalProvider", () => {
180181
expect(loadCoreSdkScript).toHaveBeenCalledWith({
181182
environment,
182183
debug: false,
184+
dataSdkIntegrationSource: "react-paypal-js",
183185
});
184186

185187
await waitFor(() => {

packages/react-paypal-js/src/v6/components/PayPalProvider.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ type PayPalProviderProps = Omit<
2929
CreateInstanceOptions<readonly [Components, ...Components[]]>,
3030
"components" | "clientToken"
3131
> &
32-
LoadCoreSdkScriptOptions & {
32+
Omit<LoadCoreSdkScriptOptions, "dataSdkIntegrationSource"> & {
3333
components?: Components[];
3434
eligibleMethodsResponse?: FindEligiblePaymentMethodsResponse;
3535
children: React.ReactNode;
@@ -118,7 +118,10 @@ export const PayPalProvider: React.FC<PayPalProviderProps> = ({
118118
>(undefined);
119119
const [isHydrated, setIsHydrated] = useState(false);
120120
// Ref to hold script options to avoid re-running effect
121-
const loadCoreScriptOptions = useRef(scriptOptions);
121+
const loadCoreScriptOptions = useRef({
122+
...scriptOptions,
123+
dataSdkIntegrationSource: "react-paypal-js",
124+
});
122125

123126
// Set hydrated state after initial client render to prevent hydration mismatch
124127
useIsomorphicLayoutEffect(() => {
@@ -170,6 +173,8 @@ export const PayPalProvider: React.FC<PayPalProviderProps> = ({
170173
environment: loadCoreScriptOptions.current.environment,
171174
debug: loadCoreScriptOptions.current.debug,
172175
dataNamespace: loadCoreScriptOptions.current.dataNamespace,
176+
dataSdkIntegrationSource:
177+
loadCoreScriptOptions.current.dataSdkIntegrationSource,
173178
});
174179

175180
if (paypal) {

0 commit comments

Comments
 (0)