Skip to content

Commit 2ac93df

Browse files
committed
refactor(shopper): CHECKOUT-2951 Define method-specific options for customer initialization.
BREAKING CHANGE: Method-specific options need to be passed in under a key named after the method when calling `initalizeCustomer`.
1 parent a9625b4 commit 2ac93df

9 files changed

+88
-74
lines changed

src/checkout/checkout-service.ts

Lines changed: 17 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { MissingDataError } from '../common/error/errors';
55
import { RequestOptions } from '../common/http-request';
66
import { ConfigActionCreator } from '../config';
77
import { CouponActionCreator, GiftCertificateActionCreator } from '../coupon';
8-
import { CustomerCredentials, CustomerStrategyActionCreator } from '../customer';
8+
import { CustomerCredentials, CustomerInitializeOptions, CustomerRequestOptions, CustomerStrategyActionCreator } from '../customer';
99
import { CountryActionCreator } from '../geography';
1010
import { OrderActionCreator, OrderRequestBody } from '../order';
1111
import { PaymentMethodActionCreator, PaymentStrategyActionCreator } from '../payment';
@@ -160,32 +160,28 @@ export default class CheckoutService {
160160
return this.loadShippingCountries(options);
161161
}
162162

163-
initializeCustomer(options: any = {}): Promise<CheckoutSelectors> {
164-
return this._store.dispatch(
165-
this._customerStrategyActionCreator.initialize(options),
166-
{ queueId: 'customerStrategy' }
167-
);
163+
initializeCustomer(options?: CustomerInitializeOptions): Promise<CheckoutSelectors> {
164+
const action = this._customerStrategyActionCreator.initialize(options);
165+
166+
return this._store.dispatch(action, { queueId: 'customerStrategy' });
168167
}
169168

170-
deinitializeCustomer(options: any = {}): Promise<CheckoutSelectors> {
171-
return this._store.dispatch(
172-
this._customerStrategyActionCreator.deinitialize(options),
173-
{ queueId: 'customerStrategy' }
174-
);
169+
deinitializeCustomer(options?: CustomerRequestOptions): Promise<CheckoutSelectors> {
170+
const action = this._customerStrategyActionCreator.deinitialize(options);
171+
172+
return this._store.dispatch(action, { queueId: 'customerStrategy' });
175173
}
176174

177-
signInCustomer(credentials: CustomerCredentials, options: any = {}): Promise<CheckoutSelectors> {
178-
return this._store.dispatch(
179-
this._customerStrategyActionCreator.signIn(credentials, options),
180-
{ queueId: 'customerStrategy' }
181-
);
175+
signInCustomer(credentials: CustomerCredentials, options?: CustomerRequestOptions): Promise<CheckoutSelectors> {
176+
const action = this._customerStrategyActionCreator.signIn(credentials, options);
177+
178+
return this._store.dispatch(action, { queueId: 'customerStrategy' });
182179
}
183180

184-
signOutCustomer(options: any = {}): Promise<CheckoutSelectors> {
185-
return this._store.dispatch(
186-
this._customerStrategyActionCreator.signOut(options),
187-
{ queueId: 'customerStrategy' }
188-
);
181+
signOutCustomer(options?: CustomerRequestOptions): Promise<CheckoutSelectors> {
182+
const action = this._customerStrategyActionCreator.signOut(options);
183+
184+
return this._store.dispatch(action, { queueId: 'customerStrategy' });
189185
}
190186

191187
loadShippingOptions(options?: RequestOptions): Promise<CheckoutSelectors> {
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { RequestOptions } from '../common/http-request';
2+
3+
import { AmazonPayCustomerInitializeOptions } from './strategies';
4+
5+
export interface CustomerRequestOptions extends RequestOptions {
6+
methodId?: string;
7+
}
8+
9+
export interface CustomerInitializeOptions extends CustomerRequestOptions {
10+
amazon?: AmazonPayCustomerInitializeOptions;
11+
}

src/customer/customer-strategy-action-creator.ts

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { Observer } from 'rxjs/Observer';
55
import { Registry } from '../common/registry';
66

77
import CustomerCredentials from './customer-credentials';
8+
import { CustomerInitializeOptions, CustomerRequestOptions } from './customer-request-options';
89
import {
910
CustomerStrategyActionType,
1011
CustomerStrategyDeinitializeAction,
@@ -19,13 +20,14 @@ export default class CustomerStrategyActionCreator {
1920
private _strategyRegistry: Registry<CustomerStrategy>
2021
) {}
2122

22-
signIn(credentials: CustomerCredentials, options: CustomerActionOptions = {}): Observable<CustomerStrategySignInAction> {
23+
signIn(credentials: CustomerCredentials, options?: CustomerRequestOptions): Observable<CustomerStrategySignInAction> {
2324
return Observable.create((observer: Observer<CustomerStrategySignInAction>) => {
24-
const meta = { methodId: options.methodId };
25+
const methodId = options && options.methodId;
26+
const meta = { methodId };
2527

2628
observer.next(createAction(CustomerStrategyActionType.SignInRequested, undefined, meta));
2729

28-
this._strategyRegistry.get(options.methodId)
30+
this._strategyRegistry.get(methodId)
2931
.signIn(credentials, options)
3032
.then(() => {
3133
observer.next(createAction(CustomerStrategyActionType.SignInSucceeded, undefined, meta));
@@ -37,13 +39,14 @@ export default class CustomerStrategyActionCreator {
3739
});
3840
}
3941

40-
signOut(options: CustomerActionOptions = {}): Observable<CustomerStrategySignOutAction> {
42+
signOut(options?: CustomerRequestOptions): Observable<CustomerStrategySignOutAction> {
4143
return Observable.create((observer: Observer<CustomerStrategySignOutAction>) => {
42-
const meta = { methodId: options.methodId };
44+
const methodId = options && options.methodId;
45+
const meta = { methodId };
4346

4447
observer.next(createAction(CustomerStrategyActionType.SignOutRequested, undefined, meta));
4548

46-
this._strategyRegistry.get(options.methodId)
49+
this._strategyRegistry.get(methodId)
4750
.signOut(options)
4851
.then(() => {
4952
observer.next(createAction(CustomerStrategyActionType.SignOutSucceeded, undefined, meta));
@@ -55,13 +58,14 @@ export default class CustomerStrategyActionCreator {
5558
});
5659
}
5760

58-
initialize(options: CustomerActionOptions = {}): Observable<CustomerStrategyInitializeAction> {
61+
initialize(options?: CustomerInitializeOptions): Observable<CustomerStrategyInitializeAction> {
5962
return Observable.create((observer: Observer<CustomerStrategyInitializeAction>) => {
60-
const meta = { methodId: options.methodId };
63+
const methodId = options && options.methodId;
64+
const meta = { methodId };
6165

6266
observer.next(createAction(CustomerStrategyActionType.InitializeRequested, undefined, meta));
6367

64-
this._strategyRegistry.get(options.methodId)
68+
this._strategyRegistry.get(methodId)
6569
.initialize(options)
6670
.then(() => {
6771
observer.next(createAction(CustomerStrategyActionType.InitializeSucceeded, undefined, meta));
@@ -73,13 +77,14 @@ export default class CustomerStrategyActionCreator {
7377
});
7478
}
7579

76-
deinitialize(options: CustomerActionOptions = {}): Observable<CustomerStrategyDeinitializeAction> {
80+
deinitialize(options?: CustomerRequestOptions): Observable<CustomerStrategyDeinitializeAction> {
7781
return Observable.create((observer: Observer<CustomerStrategyDeinitializeAction>) => {
78-
const meta = { methodId: options.methodId };
82+
const methodId = options && options.methodId;
83+
const meta = { methodId };
7984

8085
observer.next(createAction(CustomerStrategyActionType.DeinitializeRequested, undefined, meta));
8186

82-
this._strategyRegistry.get(options.methodId)
87+
this._strategyRegistry.get(methodId)
8388
.deinitialize(options)
8489
.then(() => {
8590
observer.next(createAction(CustomerStrategyActionType.DeinitializeSucceeded, undefined, meta));
@@ -91,7 +96,3 @@ export default class CustomerStrategyActionCreator {
9196
});
9297
}
9398
}
94-
95-
export interface CustomerActionOptions {
96-
methodId?: string;
97-
}

src/customer/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
export * from './customer-request-options';
2+
13
export { default as InternalCustomer } from './internal-customer';
24
export { default as Shopper } from './shopper';
35

src/customer/strategies/amazon-pay-customer-strategy.spec.ts

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ describe('AmazonPayCustomerStrategy', () => {
110110

111111
jest.spyOn(store, 'dispatch');
112112

113-
await strategy.initialize({ container: 'login', methodId: 'amazon' });
113+
await strategy.initialize({ methodId: 'amazon', amazon: { container: 'login' } });
114114

115115
expect(paymentMethodActionCreator.loadPaymentMethod).toHaveBeenCalledWith('amazon');
116116
expect(store.dispatch).toHaveBeenCalledWith(action);
@@ -123,20 +123,20 @@ describe('AmazonPayCustomerStrategy', () => {
123123
.mockReturnValue(Observable.of(createErrorAction(LOAD_PAYMENT_METHOD_FAILED, response)));
124124

125125
try {
126-
await strategy.initialize({ container: 'login', methodId: 'amazon' });
126+
await strategy.initialize({ methodId: 'amazon', amazon: { container: 'login' } });
127127
} catch (error) {
128128
expect(error).toEqual(response);
129129
}
130130
});
131131

132132
it('loads widget script', async () => {
133-
await strategy.initialize({ container: 'login', methodId: 'amazon' });
133+
await strategy.initialize({ methodId: 'amazon', amazon: { container: 'login' } });
134134

135135
expect(scriptLoader.loadWidget).toHaveBeenCalledWith(paymentMethod, expect.any(Function));
136136
});
137137

138138
it('creates login button', async () => {
139-
await strategy.initialize({ container: 'login', methodId: 'amazon' });
139+
await strategy.initialize({ methodId: 'amazon', amazon: { container: 'login' } });
140140

141141
expect(buttonConstructorSpy).toHaveBeenCalledWith('login', paymentMethod.config.merchantId, {
142142
authorization: expect.any(Function),
@@ -155,26 +155,26 @@ describe('AmazonPayCustomerStrategy', () => {
155155
.mockReturnValue(Observable.of(createAction(LOAD_PAYMENT_METHOD_SUCCEEDED, { paymentMethod })));
156156

157157
try {
158-
await strategy.initialize({ container: 'login', methodId: 'amazon' });
158+
await strategy.initialize({ methodId: 'amazon', amazon: { container: 'login' } });
159159
} catch (error) {
160160
expect(error).toBeInstanceOf(MissingDataError);
161161
}
162162
});
163163

164164
it('only initializes widget once until deinitialization', async () => {
165-
await strategy.initialize({ container: 'login', methodId: 'amazon' });
166-
await strategy.initialize({ container: 'login', methodId: 'amazon' });
165+
await strategy.initialize({ methodId: 'amazon', amazon: { container: 'login' } });
166+
await strategy.initialize({ methodId: 'amazon', amazon: { container: 'login' } });
167167

168168
expect(buttonConstructorSpy).toHaveBeenCalledTimes(1);
169169

170170
await strategy.deinitialize();
171-
await strategy.initialize({ container: 'login', methodId: 'amazon' });
171+
await strategy.initialize({ methodId: 'amazon', amazon: { container: 'login' } });
172172

173173
expect(buttonConstructorSpy).toHaveBeenCalledTimes(2);
174174
});
175175

176176
it('generates request token', async () => {
177-
await strategy.initialize({ container: 'login', methodId: 'amazon' });
177+
await strategy.initialize({ methodId: 'amazon', amazon: { container: 'login' } });
178178

179179
document.getElementById('login')
180180
.dispatchEvent(new CustomEvent('authorize'));
@@ -183,7 +183,7 @@ describe('AmazonPayCustomerStrategy', () => {
183183
});
184184

185185
it('tracks authorization event', async () => {
186-
await strategy.initialize({ container: 'login', methodId: 'amazon' });
186+
await strategy.initialize({ methodId: 'amazon', amazon: { container: 'login' } });
187187

188188
document.getElementById('login')
189189
.dispatchEvent(new CustomEvent('authorize'));
@@ -194,7 +194,7 @@ describe('AmazonPayCustomerStrategy', () => {
194194
});
195195

196196
it('sends authorization request', async () => {
197-
await strategy.initialize({ container: 'login', methodId: 'amazon' });
197+
await strategy.initialize({ methodId: 'amazon', amazon: { container: 'login' } });
198198

199199
document.getElementById('login')
200200
.dispatchEvent(new CustomEvent('authorize'));
@@ -243,7 +243,7 @@ describe('AmazonPayCustomerStrategy', () => {
243243
});
244244

245245
it('throws error if trying to sign in programmatically', async () => {
246-
await strategy.initialize({ container: 'login', methodId: 'amazon' });
246+
await strategy.initialize({ methodId: 'amazon', amazon: { container: 'login' } });
247247

248248
expect(() => strategy.signIn({ email: 'foo@bar.com', password: 'foobar' })).toThrow();
249249
});

src/customer/strategies/amazon-pay-customer-strategy.ts

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
11
/// <reference path="../../remote-checkout/methods/amazon-pay/amazon-login.d.ts" />
22
/// <reference path="../../remote-checkout/methods/amazon-pay/off-amazon-payments.d.ts" />
33

4-
import 'rxjs/add/observable/empty';
5-
64
import { CheckoutSelectors, CheckoutStore } from '../../checkout';
7-
import { MissingDataError, NotImplementedError } from '../../common/error/errors';
5+
import { InvalidArgumentError, MissingDataError, NotImplementedError } from '../../common/error/errors';
86
import { PaymentMethod, PaymentMethodActionCreator } from '../../payment';
97
import { RemoteCheckoutActionCreator, RemoteCheckoutRequestSender } from '../../remote-checkout';
108
import { RemoteCheckoutCustomerError } from '../../remote-checkout/errors';
119
import { AmazonPayScriptLoader } from '../../remote-checkout/methods/amazon-pay';
1210
import CustomerCredentials from '../customer-credentials';
11+
import { CustomerInitializeOptions, CustomerRequestOptions } from '../customer-request-options';
1312

1413
import CustomerStrategy from './customer-strategy';
1514

@@ -26,23 +25,29 @@ export default class AmazonPayCustomerStrategy extends CustomerStrategy {
2625
super(store);
2726
}
2827

29-
initialize(options: InitializeOptions): Promise<CheckoutSelectors> {
28+
initialize(options: CustomerInitializeOptions): Promise<CheckoutSelectors> {
3029
if (this._isInitialized) {
3130
return super.initialize(options);
3231
}
3332

34-
return this._store.dispatch(this._paymentMethodActionCreator.loadPaymentMethod(options.methodId))
33+
const { amazon: amazonOptions, methodId } = options;
34+
35+
if (!amazonOptions || !methodId) {
36+
throw new InvalidArgumentError('Unable to proceed because "options.amazon" argument is not provided.');
37+
}
38+
39+
return this._store.dispatch(this._paymentMethodActionCreator.loadPaymentMethod(methodId))
3540
.then(({ checkout }) => new Promise((resolve, reject) => {
36-
this._paymentMethod = checkout.getPaymentMethod(options.methodId);
41+
this._paymentMethod = checkout.getPaymentMethod(methodId);
3742

3843
if (!this._paymentMethod) {
39-
throw new MissingDataError(`Unable to initialize because "paymentMethod (${options.methodId})" data is missing.`);
44+
throw new MissingDataError(`Unable to initialize because "paymentMethod (${methodId})" data is missing.`);
4045
}
4146

42-
const { onError = () => {} } = options;
47+
const { onError = () => {} } = amazonOptions;
4348
const onReady = () => {
4449
this._createSignInButton({
45-
...options as InitializeWidgetOptions,
50+
...amazonOptions,
4651
onError: error => {
4752
reject(error);
4853
onError(error);
@@ -58,7 +63,7 @@ export default class AmazonPayCustomerStrategy extends CustomerStrategy {
5863
.then(() => super.initialize(options));
5964
}
6065

61-
deinitialize(options?: any): Promise<CheckoutSelectors> {
66+
deinitialize(options?: CustomerRequestOptions): Promise<CheckoutSelectors> {
6267
if (!this._isInitialized) {
6368
return super.deinitialize(options);
6469
}
@@ -68,13 +73,13 @@ export default class AmazonPayCustomerStrategy extends CustomerStrategy {
6873
return super.deinitialize(options);
6974
}
7075

71-
signIn(credentials: CustomerCredentials, options?: any): Promise<CheckoutSelectors> {
76+
signIn(credentials: CustomerCredentials, options?: CustomerRequestOptions): Promise<CheckoutSelectors> {
7277
throw new NotImplementedError(
7378
'In order to sign in via AmazonPay, the shopper must click on "Login with Amazon" button.'
7479
);
7580
}
7681

77-
signOut(options?: any): Promise<CheckoutSelectors> {
82+
signOut(options?: CustomerRequestOptions): Promise<CheckoutSelectors> {
7883
const { checkout } = this._store.getState();
7984
const { remote = { provider: undefined } } = checkout.getCustomer() || {};
8085

@@ -87,7 +92,7 @@ export default class AmazonPayCustomerStrategy extends CustomerStrategy {
8792
);
8893
}
8994

90-
private _createSignInButton(options: InitializeWidgetOptions): OffAmazonPayments.Button {
95+
private _createSignInButton(options: AmazonPayCustomerInitializeOptions): OffAmazonPayments.Button {
9196
if (!this._paymentMethod || !this._paymentMethod.config.merchantId) {
9297
throw new MissingDataError('Unable to create sign-in button because "paymentMethod.config.merchantId" field is missing.');
9398
}
@@ -131,11 +136,7 @@ export default class AmazonPayCustomerStrategy extends CustomerStrategy {
131136
}
132137
}
133138

134-
export interface InitializeOptions extends InitializeWidgetOptions {
135-
methodId: string;
136-
}
137-
138-
export interface InitializeWidgetOptions {
139+
export interface AmazonPayCustomerInitializeOptions {
139140
container: string;
140141
color?: string;
141142
size?: string;

src/customer/strategies/customer-strategy.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,26 @@
11
import { CheckoutSelectors, CheckoutStore } from '../../checkout';
22
import CustomerCredentials from '../customer-credentials';
33

4+
import { CustomerInitializeOptions, CustomerRequestOptions } from '../customer-request-options';
5+
46
export default abstract class CustomerStrategy {
57
protected _isInitialized = false;
68

79
constructor(
810
protected _store: CheckoutStore
911
) {}
1012

11-
abstract signIn(credentials: CustomerCredentials, options?: any): Promise<CheckoutSelectors>;
13+
abstract signIn(credentials: CustomerCredentials, options?: CustomerRequestOptions): Promise<CheckoutSelectors>;
1214

13-
abstract signOut(options?: any): Promise<CheckoutSelectors>;
15+
abstract signOut(options?: CustomerRequestOptions): Promise<CheckoutSelectors>;
1416

15-
initialize(options?: any): Promise<CheckoutSelectors> {
17+
initialize(options?: CustomerInitializeOptions): Promise<CheckoutSelectors> {
1618
this._isInitialized = true;
1719

1820
return Promise.resolve(this._store.getState());
1921
}
2022

21-
deinitialize(options?: any): Promise<CheckoutSelectors> {
23+
deinitialize(options?: CustomerRequestOptions): Promise<CheckoutSelectors> {
2224
this._isInitialized = false;
2325

2426
return Promise.resolve(this._store.getState());

0 commit comments

Comments
 (0)