Skip to content

Commit 00f3cc9

Browse files
authored
Merge pull request #179 from mt-ytaher/pmb-1165
[PMB-1165] Add `affiliate_code` in authorize url
2 parents 027b208 + 129e607 commit 00f3cc9

File tree

4 files changed

+154
-4
lines changed

4 files changed

+154
-4
lines changed

src/api/__tests__/authorize-url.test.ts

+61
Original file line numberDiff line numberDiff line change
@@ -96,5 +96,66 @@ describe('api', () => {
9696
}
9797
expectUrlToMatchWithPKCE(url, {baseUrl: MY_ACCOUNT_DOMAINS.production, path: '/oauth/authorize', query})
9898
});
99+
100+
test('includes affiliate_code when provided', () => {
101+
mockedStorage.set.mockClear();
102+
103+
const state = 'state';
104+
const scopes = 'points_read';
105+
const affiliateCode = 'mtb_hoge';
106+
107+
const mtLinkSdk = new MtLinkSdk();
108+
mtLinkSdk.init(clientId, { redirectUri });
109+
110+
const url = authorizeUrl(mtLinkSdk.storedOptions, {
111+
state,
112+
redirectUri,
113+
scopes,
114+
affiliateCode
115+
});
116+
117+
const urlObj = new URL(url);
118+
expect(urlObj.searchParams.get('affiliate_code')).toBe(affiliateCode);
119+
});
120+
121+
test('does not include affiliate_code when affiliateCode is undefined', () => {
122+
mockedStorage.set.mockClear();
123+
124+
const state = 'state';
125+
const scopes = 'points_read';
126+
127+
const mtLinkSdk = new MtLinkSdk();
128+
mtLinkSdk.init(clientId, { redirectUri });
129+
130+
const url = authorizeUrl(mtLinkSdk.storedOptions, {
131+
state,
132+
redirectUri,
133+
scopes,
134+
affiliateCode: undefined
135+
});
136+
137+
const urlObj = new URL(url);
138+
expect(urlObj.searchParams.has('affiliate_code')).toBe(false);
139+
});
140+
141+
test('does not include affiliate_code when affiliateCode is empty string', () => {
142+
mockedStorage.set.mockClear();
143+
144+
const state = 'state';
145+
const scopes = 'points_read';
146+
147+
const mtLinkSdk = new MtLinkSdk();
148+
mtLinkSdk.init(clientId, { redirectUri });
149+
150+
const url = authorizeUrl(mtLinkSdk.storedOptions, {
151+
state,
152+
redirectUri,
153+
scopes,
154+
affiliateCode: null as any
155+
});
156+
157+
const urlObj = new URL(url);
158+
expect(urlObj.searchParams.has('affiliate_code')).toBe(false);
159+
});
99160
});
100161
});

src/api/__tests__/authorize.test.ts

+81
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,87 @@ describe('api', () => {
107107
expectUrlToMatchWithPKCE(url, {baseUrl: MY_ACCOUNT_DOMAINS.production, path: '/oauth/authorize', query })
108108
});
109109

110+
test('includes affiliate_code when provided', () => {
111+
mockedStorage.set.mockClear();
112+
open.mockClear();
113+
114+
const state = 'state';
115+
const scopes = 'points_read';
116+
const samlSubjectId = 'mySubject';
117+
const affiliateCode = 'mtb_hoge';
118+
119+
const mtLinkSdk = new MtLinkSdk();
120+
mtLinkSdk.init(clientId, { samlSubjectId });
121+
122+
authorize(mtLinkSdk.storedOptions, {
123+
state,
124+
redirectUri,
125+
scopes,
126+
affiliateCode
127+
});
128+
129+
expect(open).toBeCalledTimes(1);
130+
expect(open).toBeCalledWith(expect.any(String), '_self', 'noreferrer');
131+
const url = open.mock.calls[0][0]
132+
133+
const parsed = new URL(url);
134+
expect(parsed.searchParams.has('affiliate_code')).toBe(true);
135+
});
136+
137+
test('does not include affiliate_code when affiliateCode is undefined', () => {
138+
mockedStorage.set.mockClear();
139+
open.mockClear();
140+
141+
const state = 'state';
142+
const scopes = 'points_read';
143+
const samlSubjectId = 'mySubject';
144+
const affiliateCode = undefined;
145+
146+
const mtLinkSdk = new MtLinkSdk();
147+
mtLinkSdk.init(clientId, { samlSubjectId });
148+
149+
authorize(mtLinkSdk.storedOptions, {
150+
state,
151+
redirectUri,
152+
scopes,
153+
affiliateCode
154+
});
155+
156+
expect(open).toBeCalledTimes(1);
157+
expect(open).toBeCalledWith(expect.any(String), '_self', 'noreferrer');
158+
const url = open.mock.calls[0][0]
159+
160+
const parsed = new URL(url);
161+
expect(parsed.searchParams.has('affiliate_code')).toBe(false);
162+
});
163+
164+
test('does not include affiliate_code when affiliateCode is null', () => {
165+
mockedStorage.set.mockClear();
166+
open.mockClear();
167+
168+
const state = 'state';
169+
const scopes = 'points_read';
170+
const samlSubjectId = 'mySubject';
171+
const affiliateCode = null as any;
172+
173+
const mtLinkSdk = new MtLinkSdk();
174+
mtLinkSdk.init(clientId, { samlSubjectId });
175+
176+
authorize(mtLinkSdk.storedOptions, {
177+
state,
178+
redirectUri,
179+
scopes,
180+
affiliateCode
181+
});
182+
183+
expect(open).toBeCalledTimes(1);
184+
expect(open).toBeCalledWith(expect.any(String), '_self', 'noreferrer');
185+
const url = open.mock.calls[0][0]
186+
187+
const parsed = new URL(url);
188+
expect(parsed.searchParams.has('affiliate_code')).toBe(false);
189+
});
190+
110191
test('without window', () => {
111192
const windowSpy = jest.spyOn(global, 'window', 'get');
112193
// @ts-ignore: mocking window object to undefined

src/api/authorize-url.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ export default function authorize(storedOptions: StoredOptions, options: Authori
2020
throw new Error('[mt-link-sdk] Make sure to call `init` before calling `authorizeUrl/authorize`.');
2121
}
2222

23-
const { scopes = defaultScopes, redirectUri = defaultRedirectUri, codeChallenge, state, ...rest } = options;
23+
const { scopes = defaultScopes, redirectUri = defaultRedirectUri, codeChallenge, state, affiliateCode, ...rest } = options;
2424

2525
if (!redirectUri) {
2626
throw new Error(
@@ -44,7 +44,8 @@ export default function authorize(storedOptions: StoredOptions, options: Authori
4444
country: 'JP',
4545
locale,
4646
saml_subject_id: samlSubjectId,
47-
configs: generateConfigs(mergeConfigs(storedOptions, rest))
47+
configs: generateConfigs(mergeConfigs(storedOptions, rest)),
48+
...(affiliateCode ? { affiliate_code: affiliateCode } : {})
4849
});
4950

5051
return `${MY_ACCOUNT_DOMAINS[mode]}/oauth/authorize?${queryString}`;

src/typings.ts

+9-2
Original file line numberDiff line numberDiff line change
@@ -233,8 +233,15 @@ export interface OAuthSharedParams {
233233
*/
234234
redirectUri?: string;
235235
}
236-
237-
export interface AuthorizeOptions extends OAuthSharedParams, ConfigsOptions, AuthorizeConfigsOptions {
236+
export interface AffiliateTrackingParams {
237+
/**
238+
* An optional affiliate code for attributing user referrals.
239+
*
240+
* If not provided, no affiliate attribution will be applied.
241+
*/
242+
affiliateCode?: string;
243+
}
244+
export interface AuthorizeOptions extends OAuthSharedParams, ConfigsOptions, AuthorizeConfigsOptions, AffiliateTrackingParams {
238245
/**
239246
* Access scopes you're requesting. This can be a single scope, or an array of scopes.
240247
*

0 commit comments

Comments
 (0)