@rnw-community/react-native-payments 0.83.1 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +1 -1
- package/dist/cjs/app.plugin.d.ts +3 -0
- package/dist/cjs/app.plugin.d.ts.map +1 -0
- package/dist/cjs/app.plugin.js +5 -0
- package/dist/cjs/app.plugin.js.map +1 -0
- package/dist/cjs/class/payment-request/payment-request.d.ts.map +1 -1
- package/dist/cjs/class/payment-request/payment-request.js +4 -3
- package/dist/cjs/class/payment-request/payment-request.js.map +1 -1
- package/dist/cjs/class/payment-request/payment-request.web.d.ts +5 -0
- package/dist/cjs/class/payment-request/payment-request.web.d.ts.map +1 -0
- package/dist/cjs/class/payment-request/payment-request.web.js +5 -0
- package/dist/cjs/class/payment-request/payment-request.web.js.map +1 -0
- package/dist/cjs/class/payment-response/payment-response.d.ts.map +1 -1
- package/dist/cjs/class/payment-response/payment-response.js +4 -3
- package/dist/cjs/class/payment-response/payment-response.js.map +1 -1
- package/dist/cjs/class/payment-response/payment-response.web.d.ts +5 -0
- package/dist/cjs/class/payment-response/payment-response.web.d.ts.map +1 -0
- package/dist/cjs/class/payment-response/payment-response.web.js +5 -0
- package/dist/cjs/class/payment-response/payment-response.web.js.map +1 -0
- package/dist/cjs/expo-plugins/plugin.props.d.ts +4 -0
- package/dist/cjs/expo-plugins/plugin.props.d.ts.map +1 -0
- package/dist/cjs/expo-plugins/plugin.props.js +3 -0
- package/dist/cjs/expo-plugins/plugin.props.js.map +1 -0
- package/dist/cjs/expo-plugins/with-apple-pay.d.ts +4 -0
- package/dist/cjs/expo-plugins/with-apple-pay.d.ts.map +1 -0
- package/dist/cjs/expo-plugins/with-apple-pay.js +24 -0
- package/dist/cjs/expo-plugins/with-apple-pay.js.map +1 -0
- package/dist/cjs/expo-plugins/with-google-pay.d.ts +4 -0
- package/dist/cjs/expo-plugins/with-google-pay.d.ts.map +1 -0
- package/dist/cjs/expo-plugins/with-google-pay.js +26 -0
- package/dist/cjs/expo-plugins/with-google-pay.js.map +1 -0
- package/dist/cjs/expo-plugins/with-payments.d.ts +4 -0
- package/dist/cjs/expo-plugins/with-payments.d.ts.map +1 -0
- package/dist/cjs/expo-plugins/with-payments.js +12 -0
- package/dist/cjs/expo-plugins/with-payments.js.map +1 -0
- package/dist/cjs/util/is-valid-decimal-monetary-value.util.js +2 -2
- package/dist/cjs/util/is-valid-decimal-monetary-value.util.js.map +1 -1
- package/dist/cjs/util/validate-payment-methods.util.js +1 -1
- package/dist/cjs/util/validate-payment-methods.util.js.map +1 -1
- package/dist/esm/app.plugin.d.ts +3 -0
- package/dist/esm/app.plugin.d.ts.map +1 -0
- package/dist/esm/app.plugin.js +3 -0
- package/dist/esm/app.plugin.js.map +1 -0
- package/dist/esm/class/payment-request/payment-request.d.ts.map +1 -1
- package/dist/esm/class/payment-request/payment-request.js +5 -4
- package/dist/esm/class/payment-request/payment-request.js.map +1 -1
- package/dist/esm/class/payment-request/payment-request.web.d.ts +5 -0
- package/dist/esm/class/payment-request/payment-request.web.d.ts.map +1 -0
- package/dist/esm/class/payment-request/payment-request.web.js +2 -0
- package/dist/esm/class/payment-request/payment-request.web.js.map +1 -0
- package/dist/esm/class/payment-response/payment-response.d.ts.map +1 -1
- package/dist/esm/class/payment-response/payment-response.js +4 -3
- package/dist/esm/class/payment-response/payment-response.js.map +1 -1
- package/dist/esm/class/payment-response/payment-response.web.d.ts +5 -0
- package/dist/esm/class/payment-response/payment-response.web.d.ts.map +1 -0
- package/dist/esm/class/payment-response/payment-response.web.js +2 -0
- package/dist/esm/class/payment-response/payment-response.web.js.map +1 -0
- package/dist/esm/expo-plugins/plugin.props.d.ts +4 -0
- package/dist/esm/expo-plugins/plugin.props.d.ts.map +1 -0
- package/dist/esm/expo-plugins/plugin.props.js +2 -0
- package/dist/esm/expo-plugins/plugin.props.js.map +1 -0
- package/dist/esm/expo-plugins/with-apple-pay.d.ts +4 -0
- package/dist/esm/expo-plugins/with-apple-pay.d.ts.map +1 -0
- package/dist/esm/expo-plugins/with-apple-pay.js +20 -0
- package/dist/esm/expo-plugins/with-apple-pay.js.map +1 -0
- package/dist/esm/expo-plugins/with-google-pay.d.ts +4 -0
- package/dist/esm/expo-plugins/with-google-pay.d.ts.map +1 -0
- package/dist/esm/expo-plugins/with-google-pay.js +22 -0
- package/dist/esm/expo-plugins/with-google-pay.js.map +1 -0
- package/dist/esm/expo-plugins/with-payments.d.ts +4 -0
- package/dist/esm/expo-plugins/with-payments.d.ts.map +1 -0
- package/dist/esm/expo-plugins/with-payments.js +8 -0
- package/dist/esm/expo-plugins/with-payments.js.map +1 -0
- package/dist/esm/util/is-valid-decimal-monetary-value.util.js +2 -2
- package/dist/esm/util/is-valid-decimal-monetary-value.util.js.map +1 -1
- package/dist/esm/util/validate-payment-methods.util.js +2 -2
- package/dist/esm/util/validate-payment-methods.util.js.map +1 -1
- package/ios/Payments.mm +9 -1
- package/package.json +11 -13
- package/react-native-payments.podspec +21 -18
- package/readme.md +33 -27
- package/src/app.plugin.ts +3 -0
- package/src/class/payment-request/payment-request.spec.ts +691 -0
- package/src/class/payment-request/payment-request.ts +5 -4
- package/src/class/payment-request/payment-request.web.ts +1 -0
- package/src/class/payment-response/payment-response.spec.ts +142 -0
- package/src/class/payment-response/payment-response.ts +4 -3
- package/src/class/payment-response/payment-response.web.ts +1 -0
- package/src/expo-plugins/plugin.props.ts +3 -0
- package/src/expo-plugins/with-apple-pay.ts +27 -0
- package/src/expo-plugins/with-google-pay.ts +31 -0
- package/src/expo-plugins/with-payments.ts +13 -0
- package/src/util/is-valid-decimal-monetary-value.util.ts +2 -2
- package/src/util/validate-payment-methods.util.ts +2 -2
- package/app.plugin.js +0 -1
- package/plugins/with-apple-pay.js +0 -24
- package/plugins/with-google-pay.js +0 -49
- package/plugins/with-payments.js +0 -12
|
@@ -0,0 +1,691 @@
|
|
|
1
|
+
/* eslint-disable max-lines */
|
|
2
|
+
import { beforeEach, describe, expect, it, jest } from '@jest/globals';
|
|
3
|
+
import { Platform } from 'react-native';
|
|
4
|
+
|
|
5
|
+
import { IosPKPaymentMethodType } from '../../@standard/ios/enum/ios-pk-payment-method-type.enum';
|
|
6
|
+
import { EnvironmentEnum } from '../../enum/environment.enum';
|
|
7
|
+
import { PaymentMethodNameEnum } from '../../enum/payment-method-name.enum';
|
|
8
|
+
import { PaymentsErrorEnum } from '../../enum/payments-error.enum';
|
|
9
|
+
import { SupportedNetworkEnum } from '../../enum/supported-networks.enum';
|
|
10
|
+
import { ConstructorError } from '../../error/constructor.error';
|
|
11
|
+
import { DOMException } from '../../error/dom.exception';
|
|
12
|
+
import { PaymentsError } from '../../error/payments.error';
|
|
13
|
+
import { NativePayments } from '../native-payments/native-payments';
|
|
14
|
+
|
|
15
|
+
import { PaymentRequest } from './payment-request';
|
|
16
|
+
|
|
17
|
+
import type { AndroidPaymentMethodDataInterface } from '../../@standard/android/mapping/android-payment-method-data.interface';
|
|
18
|
+
import type { AndroidPaymentData } from '../../@standard/android/response/android-payment-data';
|
|
19
|
+
import type { IosPaymentMethodDataInterface } from '../../@standard/ios/mapping/ios-payment-method-data.interface';
|
|
20
|
+
import type { IosPKPayment } from '../../@standard/ios/response/ios-pk-payment';
|
|
21
|
+
import type { PaymentDetailsInit } from '../../@standard/w3c/payment-details-init';
|
|
22
|
+
import type { PaymentItem } from '../../@standard/w3c/payment-item';
|
|
23
|
+
import type { PaymentMethodData } from '../../@standard/w3c/payment-method-data';
|
|
24
|
+
|
|
25
|
+
jest.mock('../native-payments/native-payments', () => ({
|
|
26
|
+
NativePayments: {
|
|
27
|
+
canMakePayments: jest.fn(),
|
|
28
|
+
show: jest.fn(),
|
|
29
|
+
abort: jest.fn(),
|
|
30
|
+
},
|
|
31
|
+
}));
|
|
32
|
+
|
|
33
|
+
jest.mock('react-native', () => ({
|
|
34
|
+
Platform: {
|
|
35
|
+
OS: 'android',
|
|
36
|
+
},
|
|
37
|
+
}));
|
|
38
|
+
|
|
39
|
+
// eslint-disable-next-line max-lines-per-function
|
|
40
|
+
describe('PaymentRequest', () => {
|
|
41
|
+
const paymentDetails = {
|
|
42
|
+
total: {
|
|
43
|
+
label: 'Total',
|
|
44
|
+
amount: { currency: 'USD', value: '10.00' },
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
// eslint-disable-next-line jest/no-hooks
|
|
49
|
+
beforeEach(() => {
|
|
50
|
+
jest.clearAllMocks();
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
// eslint-disable-next-line max-lines-per-function
|
|
54
|
+
describe('validation', () => {
|
|
55
|
+
const methodData: AndroidPaymentMethodDataInterface = {
|
|
56
|
+
supportedMethods: PaymentMethodNameEnum.AndroidPay,
|
|
57
|
+
data: {
|
|
58
|
+
currencyCode: 'USD',
|
|
59
|
+
countryCode: 'US',
|
|
60
|
+
supportedNetworks: [SupportedNetworkEnum.Visa, SupportedNetworkEnum.Mastercard],
|
|
61
|
+
environment: EnvironmentEnum.TEST,
|
|
62
|
+
gatewayConfig: {
|
|
63
|
+
gateway: 'exampleGateway',
|
|
64
|
+
gatewayMerchantId: 'exampleMerchantId',
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
it('should throw when payment methods not passed', () => {
|
|
70
|
+
expect.assertions(2);
|
|
71
|
+
|
|
72
|
+
expect(() => new PaymentRequest([], {} as unknown as PaymentDetailsInit)).toThrow(
|
|
73
|
+
new PaymentsError(`Failed to construct 'PaymentRequest': At least one payment method is required`)
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
expect(() => new PaymentRequest(undefined as unknown as PaymentMethodData[], paymentDetails)).toThrow(
|
|
77
|
+
new PaymentsError(`Failed to construct 'PaymentRequest': At least one payment method is required`)
|
|
78
|
+
);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('should throw when payment methods supportedMethods not passed', () => {
|
|
82
|
+
expect.assertions(2);
|
|
83
|
+
|
|
84
|
+
expect(
|
|
85
|
+
() => new PaymentRequest([{ supportedMethods: undefined } as unknown as PaymentMethodData], paymentDetails)
|
|
86
|
+
).toThrow(
|
|
87
|
+
new PaymentsError(`Failed to construct 'PaymentRequest': required member supportedMethods is undefined.`)
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
expect(() => new PaymentRequest([{} as unknown as PaymentMethodData], paymentDetails)).toThrow(
|
|
91
|
+
new PaymentsError(`Failed to construct 'PaymentRequest': required member supportedMethods is undefined.`)
|
|
92
|
+
);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
describe(`payment details total`, () => {
|
|
96
|
+
it('should throw when total is not defined', () => {
|
|
97
|
+
expect.assertions(1);
|
|
98
|
+
|
|
99
|
+
const invalidPaymentDetails = {} as unknown as PaymentDetailsInit;
|
|
100
|
+
|
|
101
|
+
expect(() => new PaymentRequest([methodData], invalidPaymentDetails)).toThrow(
|
|
102
|
+
new PaymentsError(`Failed to construct 'PaymentRequest': required member total is undefined.`)
|
|
103
|
+
);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it('should throw when total.amount is not defined', () => {
|
|
107
|
+
expect.assertions(1);
|
|
108
|
+
|
|
109
|
+
const invalidPaymentDetails = {
|
|
110
|
+
total: {
|
|
111
|
+
label: 'Total',
|
|
112
|
+
},
|
|
113
|
+
} as unknown as PaymentDetailsInit;
|
|
114
|
+
|
|
115
|
+
expect(() => new PaymentRequest([methodData], invalidPaymentDetails)).toThrow(
|
|
116
|
+
new PaymentsError(`Failed to construct 'PaymentRequest': Missing required member(s): amount, label.`)
|
|
117
|
+
);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it('should throw when total.amount.value is not defined', () => {
|
|
121
|
+
expect.assertions(1);
|
|
122
|
+
|
|
123
|
+
const invalidPaymentDetails = {
|
|
124
|
+
total: {
|
|
125
|
+
label: 'Total',
|
|
126
|
+
amount: {},
|
|
127
|
+
},
|
|
128
|
+
} as unknown as PaymentDetailsInit;
|
|
129
|
+
|
|
130
|
+
expect(() => new PaymentRequest([methodData], invalidPaymentDetails)).toThrow(
|
|
131
|
+
new PaymentsError(`Failed to construct 'PaymentRequest': Missing required member(s): amount, label.`)
|
|
132
|
+
);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it('should throw when total.amount.value is not monetary', () => {
|
|
136
|
+
expect.assertions(1);
|
|
137
|
+
|
|
138
|
+
const invalidPaymentDetails = {
|
|
139
|
+
total: {
|
|
140
|
+
label: 'Total',
|
|
141
|
+
amount: {
|
|
142
|
+
currency: 'USD',
|
|
143
|
+
value: true,
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
} as unknown as PaymentDetailsInit;
|
|
147
|
+
|
|
148
|
+
expect(() => new PaymentRequest([methodData], invalidPaymentDetails)).toThrow(
|
|
149
|
+
new PaymentsError(`Failed to construct 'PaymentRequest': 'true' is not a valid amount format for total`)
|
|
150
|
+
);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it('should throw when total.amount.value is negative', () => {
|
|
154
|
+
expect.assertions(1);
|
|
155
|
+
|
|
156
|
+
const invalidPaymentDetails = {
|
|
157
|
+
total: {
|
|
158
|
+
label: 'Total',
|
|
159
|
+
amount: {
|
|
160
|
+
currency: 'USD',
|
|
161
|
+
value: '-10.00',
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
expect(() => new PaymentRequest([methodData], invalidPaymentDetails)).toThrow(
|
|
167
|
+
new PaymentsError(`Failed to construct 'PaymentRequest': Total amount value should be non-negative`)
|
|
168
|
+
);
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it('should throw when total.amount.value ends with dot', () => {
|
|
172
|
+
expect.assertions(1);
|
|
173
|
+
|
|
174
|
+
const invalidPaymentDetails = {
|
|
175
|
+
total: {
|
|
176
|
+
label: 'Total',
|
|
177
|
+
amount: {
|
|
178
|
+
currency: 'USD',
|
|
179
|
+
value: '10.00.',
|
|
180
|
+
},
|
|
181
|
+
},
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
expect(() => new PaymentRequest([methodData], invalidPaymentDetails)).toThrow(
|
|
185
|
+
new PaymentsError(
|
|
186
|
+
`Failed to construct 'PaymentRequest': '10.00.' is not a valid amount format for total`
|
|
187
|
+
)
|
|
188
|
+
);
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
describe(`payment details displayItems`, () => {
|
|
193
|
+
const paymentDetailsWithTotal: PaymentDetailsInit = {
|
|
194
|
+
total: {
|
|
195
|
+
label: 'Total',
|
|
196
|
+
amount: {
|
|
197
|
+
currency: 'USD',
|
|
198
|
+
value: '10.00',
|
|
199
|
+
},
|
|
200
|
+
},
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
it('should NOT throw when displayItems is not defined or empty', () => {
|
|
204
|
+
expect.assertions(2);
|
|
205
|
+
|
|
206
|
+
expect(() => new PaymentRequest([methodData], paymentDetailsWithTotal)).not.toThrow();
|
|
207
|
+
expect(
|
|
208
|
+
() => new PaymentRequest([methodData], { ...paymentDetailsWithTotal, displayItems: [] })
|
|
209
|
+
).not.toThrow();
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
it('should throw when displayItems item has in proper shape', () => {
|
|
213
|
+
expect.assertions(3);
|
|
214
|
+
|
|
215
|
+
expect(
|
|
216
|
+
() =>
|
|
217
|
+
new PaymentRequest([methodData], {
|
|
218
|
+
...paymentDetailsWithTotal,
|
|
219
|
+
displayItems: [undefined as unknown as PaymentItem],
|
|
220
|
+
})
|
|
221
|
+
).toThrow(new ConstructorError(`required member value is undefined.`));
|
|
222
|
+
|
|
223
|
+
expect(
|
|
224
|
+
() =>
|
|
225
|
+
new PaymentRequest([methodData], {
|
|
226
|
+
...paymentDetailsWithTotal,
|
|
227
|
+
displayItems: [{} as unknown as PaymentItem],
|
|
228
|
+
})
|
|
229
|
+
).toThrow(new ConstructorError(`required member value is undefined.`));
|
|
230
|
+
|
|
231
|
+
expect(
|
|
232
|
+
() =>
|
|
233
|
+
new PaymentRequest([methodData], {
|
|
234
|
+
...paymentDetailsWithTotal,
|
|
235
|
+
displayItems: [{ amount: {} } as unknown as PaymentItem],
|
|
236
|
+
})
|
|
237
|
+
).toThrow(new ConstructorError(`required member value is undefined.`));
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
it('should throw when displayItems item.amount.value is not monetary', () => {
|
|
241
|
+
expect.assertions(1);
|
|
242
|
+
|
|
243
|
+
expect(
|
|
244
|
+
() =>
|
|
245
|
+
new PaymentRequest([methodData], {
|
|
246
|
+
...paymentDetailsWithTotal,
|
|
247
|
+
displayItems: [{ amount: { currency: 'USD', value: true } } as unknown as PaymentItem],
|
|
248
|
+
})
|
|
249
|
+
).toThrow(new ConstructorError(`'true' is not a valid amount format for display items`));
|
|
250
|
+
});
|
|
251
|
+
});
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
// eslint-disable-next-line max-lines-per-function,max-statements
|
|
255
|
+
describe('PaymentRequest on Android', () => {
|
|
256
|
+
const androidMethodData: AndroidPaymentMethodDataInterface = {
|
|
257
|
+
supportedMethods: PaymentMethodNameEnum.AndroidPay,
|
|
258
|
+
data: {
|
|
259
|
+
currencyCode: 'USD',
|
|
260
|
+
countryCode: 'US',
|
|
261
|
+
supportedNetworks: [SupportedNetworkEnum.Visa, SupportedNetworkEnum.Mastercard],
|
|
262
|
+
environment: EnvironmentEnum.TEST,
|
|
263
|
+
gatewayConfig: {
|
|
264
|
+
gateway: 'exampleGateway',
|
|
265
|
+
gatewayMerchantId: 'exampleMerchantId',
|
|
266
|
+
},
|
|
267
|
+
},
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
// eslint-disable-next-line jest/no-hooks
|
|
271
|
+
beforeEach(() => {
|
|
272
|
+
Platform.OS = 'android';
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
it('should initialize with the correct id', () => {
|
|
276
|
+
expect.assertions(2);
|
|
277
|
+
|
|
278
|
+
const request = new PaymentRequest([androidMethodData], paymentDetails);
|
|
279
|
+
|
|
280
|
+
expect(request.id).toBeDefined();
|
|
281
|
+
expect(request.state).toBe('created');
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
it('should throw when `canMakePayment` is called in invalid state', async () => {
|
|
285
|
+
expect.assertions(1);
|
|
286
|
+
|
|
287
|
+
const request = new PaymentRequest([androidMethodData], paymentDetails);
|
|
288
|
+
request.state = 'closed';
|
|
289
|
+
|
|
290
|
+
await expect(request.canMakePayment()).rejects.toThrow(new DOMException(PaymentsErrorEnum.InvalidStateError));
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
it('should throw when NativePayments.show rejects', async () => {
|
|
294
|
+
expect.assertions(2);
|
|
295
|
+
|
|
296
|
+
const request = new PaymentRequest([androidMethodData], paymentDetails);
|
|
297
|
+
request.state = 'created';
|
|
298
|
+
jest.mocked(NativePayments.show).mockRejectedValue(new DOMException(PaymentsErrorEnum.NotAllowedError));
|
|
299
|
+
|
|
300
|
+
await expect(request.show()).rejects.toThrow(new DOMException(PaymentsErrorEnum.NotAllowedError));
|
|
301
|
+
expect(NativePayments.show).toHaveBeenCalledWith(expect.any(String), expect.any(Object));
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
it('should return true from `canMakePayment` when valid', async () => {
|
|
305
|
+
expect.assertions(2);
|
|
306
|
+
|
|
307
|
+
const request = new PaymentRequest([androidMethodData], paymentDetails);
|
|
308
|
+
jest.mocked(NativePayments.canMakePayments).mockResolvedValue(true);
|
|
309
|
+
|
|
310
|
+
const result = await request.canMakePayment();
|
|
311
|
+
|
|
312
|
+
expect(NativePayments.canMakePayments).toHaveBeenCalledWith(expect.any(String));
|
|
313
|
+
expect(result).toBe(true);
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
it('should throw when `show` is called in invalid state', async () => {
|
|
317
|
+
expect.assertions(1);
|
|
318
|
+
|
|
319
|
+
const request = new PaymentRequest([androidMethodData], paymentDetails);
|
|
320
|
+
request.state = 'closed';
|
|
321
|
+
|
|
322
|
+
await expect(request.show()).rejects.toThrow(new DOMException(PaymentsErrorEnum.InvalidStateError));
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
it(`should handle 'examplePaymentMethodToken' tokenization type`, async () => {
|
|
326
|
+
expect.assertions(3);
|
|
327
|
+
|
|
328
|
+
jest.mocked(NativePayments.show).mockResolvedValue(
|
|
329
|
+
JSON.stringify({
|
|
330
|
+
apiVersion: 2,
|
|
331
|
+
apiVersionMinor: 0,
|
|
332
|
+
email: 'test@example.com',
|
|
333
|
+
paymentMethodData: {
|
|
334
|
+
info: {},
|
|
335
|
+
tokenizationData: {
|
|
336
|
+
type: 'PAYMENT_GATEWAY',
|
|
337
|
+
token: 'examplePaymentMethodToken',
|
|
338
|
+
},
|
|
339
|
+
},
|
|
340
|
+
})
|
|
341
|
+
);
|
|
342
|
+
|
|
343
|
+
const request = new PaymentRequest([androidMethodData], paymentDetails);
|
|
344
|
+
request.state = 'created';
|
|
345
|
+
const result = await request.show();
|
|
346
|
+
|
|
347
|
+
expect(NativePayments.show).toHaveBeenCalledWith(expect.any(String), expect.any(Object));
|
|
348
|
+
expect(result).toBeDefined();
|
|
349
|
+
expect(request.state).toBe('interactive');
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
it('should throw when `NativePayments.show` returns invalid data', async () => {
|
|
353
|
+
expect.assertions(1);
|
|
354
|
+
|
|
355
|
+
jest.mocked(NativePayments.show).mockResolvedValue(`...`);
|
|
356
|
+
const expectedError = new PaymentsError(`Failed parsing PaymentRequest details`);
|
|
357
|
+
|
|
358
|
+
const request = new PaymentRequest([androidMethodData], paymentDetails);
|
|
359
|
+
request.state = 'created';
|
|
360
|
+
|
|
361
|
+
await expect(request.show()).rejects.toThrow(expectedError);
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
it('should call NativePayments.show and resolve correctly', async () => {
|
|
365
|
+
expect.assertions(3);
|
|
366
|
+
|
|
367
|
+
const request = new PaymentRequest([androidMethodData], paymentDetails);
|
|
368
|
+
request.state = 'created';
|
|
369
|
+
jest.mocked(NativePayments.show).mockResolvedValue(
|
|
370
|
+
JSON.stringify({
|
|
371
|
+
apiVersion: 2,
|
|
372
|
+
apiVersionMinor: 0,
|
|
373
|
+
email: 'test@example.com',
|
|
374
|
+
paymentMethodData: {
|
|
375
|
+
info: {
|
|
376
|
+
billingAddress: {
|
|
377
|
+
countryCode: 'US',
|
|
378
|
+
name: 'John Doe',
|
|
379
|
+
phoneNumber: '+1234567890',
|
|
380
|
+
postalCode: '12345',
|
|
381
|
+
address1: '123 Main St',
|
|
382
|
+
address2: 'Suite 1',
|
|
383
|
+
address3: 'Building B',
|
|
384
|
+
administrativeArea: 'CA',
|
|
385
|
+
locality: 'Mountain View',
|
|
386
|
+
sortingCode: '123',
|
|
387
|
+
},
|
|
388
|
+
cardDetails: '1234',
|
|
389
|
+
cardNetwork: 'VISA',
|
|
390
|
+
assuranceDetails: {
|
|
391
|
+
accountVerified: true,
|
|
392
|
+
cardHolderAuthenticated: true,
|
|
393
|
+
},
|
|
394
|
+
},
|
|
395
|
+
tokenizationData: {
|
|
396
|
+
type: 'PAYMENT_GATEWAY',
|
|
397
|
+
token: JSON.stringify({
|
|
398
|
+
protocolVersion: 'ECv2',
|
|
399
|
+
signature: 'testSignature',
|
|
400
|
+
signedMessage: JSON.stringify({
|
|
401
|
+
encryptedMessage: 'testEncryptedMessage',
|
|
402
|
+
ephemeralPublicKey: 'testEphemeralPublicKey',
|
|
403
|
+
tag: 'testTag',
|
|
404
|
+
}),
|
|
405
|
+
intermediateSigningKey: {
|
|
406
|
+
signatures: ['testSignature'],
|
|
407
|
+
signedKey: JSON.stringify({
|
|
408
|
+
keyExpiration: '2024-01-01T00:00:00.000Z',
|
|
409
|
+
keyValue: 'testKeyValue',
|
|
410
|
+
}),
|
|
411
|
+
},
|
|
412
|
+
}),
|
|
413
|
+
},
|
|
414
|
+
},
|
|
415
|
+
shippingAddress: {
|
|
416
|
+
countryCode: 'US',
|
|
417
|
+
name: 'Jane Doe',
|
|
418
|
+
phoneNumber: '+9876543210',
|
|
419
|
+
postalCode: '54321',
|
|
420
|
+
address1: '456 Elm St',
|
|
421
|
+
address2: 'Apt 2',
|
|
422
|
+
address3: '',
|
|
423
|
+
administrativeArea: 'NY',
|
|
424
|
+
locality: 'New York',
|
|
425
|
+
sortingCode: '',
|
|
426
|
+
},
|
|
427
|
+
} as AndroidPaymentData)
|
|
428
|
+
);
|
|
429
|
+
|
|
430
|
+
const result = await request.show();
|
|
431
|
+
|
|
432
|
+
expect(NativePayments.show).toHaveBeenCalledWith(expect.any(String), expect.any(Object));
|
|
433
|
+
expect(result).toBeDefined();
|
|
434
|
+
expect(request.state).toBe('interactive');
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
it('should throw when `abort` is called in invalid state', async () => {
|
|
438
|
+
expect.assertions(1);
|
|
439
|
+
|
|
440
|
+
const request = new PaymentRequest([androidMethodData], paymentDetails);
|
|
441
|
+
|
|
442
|
+
await expect(request.abort()).rejects.toThrow(new DOMException(PaymentsErrorEnum.InvalidStateError));
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
it('should throw when `NativePayments.abort` rejects', async () => {
|
|
446
|
+
expect.assertions(1);
|
|
447
|
+
|
|
448
|
+
const expectedError = new PaymentsError(`Failed aborting PaymentRequest`);
|
|
449
|
+
jest.mocked(NativePayments.abort).mockRejectedValue(expectedError);
|
|
450
|
+
|
|
451
|
+
const request = new PaymentRequest([androidMethodData], paymentDetails);
|
|
452
|
+
request.state = 'interactive';
|
|
453
|
+
|
|
454
|
+
await expect(request.abort()).rejects.toThrow(expectedError);
|
|
455
|
+
});
|
|
456
|
+
|
|
457
|
+
it('should call NativePayments.abort and reject correctly when aborted', async () => {
|
|
458
|
+
expect.assertions(2);
|
|
459
|
+
|
|
460
|
+
const request = new PaymentRequest([androidMethodData], paymentDetails);
|
|
461
|
+
request.state = 'interactive';
|
|
462
|
+
jest.mocked(NativePayments.abort).mockResolvedValue(undefined);
|
|
463
|
+
|
|
464
|
+
await request.abort();
|
|
465
|
+
|
|
466
|
+
expect(NativePayments.abort).toHaveBeenCalledWith();
|
|
467
|
+
expect(request.state).toBe('closed');
|
|
468
|
+
});
|
|
469
|
+
|
|
470
|
+
it('should throw NotSupportedError if platform payment method is not found', () => {
|
|
471
|
+
expect.assertions(1);
|
|
472
|
+
|
|
473
|
+
const invalidMethodData = [
|
|
474
|
+
{
|
|
475
|
+
supportedMethods: 'unsupported-method',
|
|
476
|
+
data: {},
|
|
477
|
+
},
|
|
478
|
+
] as unknown as PaymentMethodData[];
|
|
479
|
+
|
|
480
|
+
expect(() => new PaymentRequest(invalidMethodData, paymentDetails)).toThrow(
|
|
481
|
+
new DOMException(PaymentsErrorEnum.NotSupportedError)
|
|
482
|
+
);
|
|
483
|
+
});
|
|
484
|
+
});
|
|
485
|
+
|
|
486
|
+
// eslint-disable-next-line max-lines-per-function,max-statements
|
|
487
|
+
describe('PaymentRequest on iOS', () => {
|
|
488
|
+
const iosMethodData: IosPaymentMethodDataInterface = {
|
|
489
|
+
supportedMethods: PaymentMethodNameEnum.ApplePay,
|
|
490
|
+
data: {
|
|
491
|
+
requestBillingAddress: true,
|
|
492
|
+
requestPayerEmail: true,
|
|
493
|
+
requestPayerName: true,
|
|
494
|
+
requestPayerPhone: true,
|
|
495
|
+
requestShipping: true,
|
|
496
|
+
currencyCode: 'USD',
|
|
497
|
+
countryCode: 'US',
|
|
498
|
+
merchantIdentifier: 'merchant.com.example',
|
|
499
|
+
supportedNetworks: [SupportedNetworkEnum.Visa, SupportedNetworkEnum.Mastercard],
|
|
500
|
+
},
|
|
501
|
+
};
|
|
502
|
+
|
|
503
|
+
// eslint-disable-next-line jest/no-hooks
|
|
504
|
+
beforeEach(() => {
|
|
505
|
+
Platform.OS = 'ios';
|
|
506
|
+
});
|
|
507
|
+
|
|
508
|
+
it('should initialize with the correct id', () => {
|
|
509
|
+
expect.assertions(2);
|
|
510
|
+
|
|
511
|
+
const request = new PaymentRequest([iosMethodData], paymentDetails);
|
|
512
|
+
|
|
513
|
+
expect(request.id).toBeDefined();
|
|
514
|
+
expect(request.state).toBe('created');
|
|
515
|
+
});
|
|
516
|
+
|
|
517
|
+
it('should throw when `canMakePayment` is called in invalid state', async () => {
|
|
518
|
+
expect.assertions(1);
|
|
519
|
+
|
|
520
|
+
const request = new PaymentRequest([iosMethodData], paymentDetails);
|
|
521
|
+
request.state = 'closed';
|
|
522
|
+
|
|
523
|
+
await expect(request.canMakePayment()).rejects.toThrow(new DOMException(PaymentsErrorEnum.InvalidStateError));
|
|
524
|
+
});
|
|
525
|
+
|
|
526
|
+
it('should return true from `canMakePayment` when valid', async () => {
|
|
527
|
+
expect.assertions(2);
|
|
528
|
+
|
|
529
|
+
const request = new PaymentRequest([iosMethodData], paymentDetails);
|
|
530
|
+
jest.mocked(NativePayments.canMakePayments).mockResolvedValue(true);
|
|
531
|
+
|
|
532
|
+
const result = await request.canMakePayment();
|
|
533
|
+
|
|
534
|
+
expect(NativePayments.canMakePayments).toHaveBeenCalledWith(expect.any(String));
|
|
535
|
+
expect(result).toBe(true);
|
|
536
|
+
});
|
|
537
|
+
|
|
538
|
+
it('should throw when `show` is called in invalid state', async () => {
|
|
539
|
+
expect.assertions(1);
|
|
540
|
+
|
|
541
|
+
const request = new PaymentRequest([iosMethodData], paymentDetails);
|
|
542
|
+
request.state = 'closed';
|
|
543
|
+
|
|
544
|
+
await expect(request.show()).rejects.toThrow(new DOMException(PaymentsErrorEnum.InvalidStateError));
|
|
545
|
+
});
|
|
546
|
+
|
|
547
|
+
it('should throw when NativePayments.show rejects', async () => {
|
|
548
|
+
expect.assertions(2);
|
|
549
|
+
|
|
550
|
+
const request = new PaymentRequest([iosMethodData], paymentDetails);
|
|
551
|
+
request.state = 'created';
|
|
552
|
+
jest.mocked(NativePayments.show).mockRejectedValue(new DOMException(PaymentsErrorEnum.NotAllowedError));
|
|
553
|
+
|
|
554
|
+
await expect(request.show()).rejects.toThrow(new DOMException(PaymentsErrorEnum.NotAllowedError));
|
|
555
|
+
expect(NativePayments.show).toHaveBeenCalledWith(expect.any(String), expect.any(Object));
|
|
556
|
+
});
|
|
557
|
+
|
|
558
|
+
it('should throw when `NativePayments.show` returns invalid data', async () => {
|
|
559
|
+
expect.assertions(1);
|
|
560
|
+
|
|
561
|
+
jest.mocked(NativePayments.show).mockResolvedValue(`...`);
|
|
562
|
+
const expectedError = new PaymentsError(`Failed parsing PaymentRequest details`);
|
|
563
|
+
|
|
564
|
+
const request = new PaymentRequest([iosMethodData], paymentDetails);
|
|
565
|
+
request.state = 'created';
|
|
566
|
+
|
|
567
|
+
await expect(request.show()).rejects.toThrow(expectedError);
|
|
568
|
+
});
|
|
569
|
+
|
|
570
|
+
it('should call NativePayments.show and resolve correctly', async () => {
|
|
571
|
+
expect.assertions(3);
|
|
572
|
+
|
|
573
|
+
const request = new PaymentRequest([iosMethodData], paymentDetails);
|
|
574
|
+
request.state = 'created';
|
|
575
|
+
jest.mocked(NativePayments.show).mockResolvedValue(
|
|
576
|
+
JSON.stringify({
|
|
577
|
+
billingContact: {
|
|
578
|
+
postalAddress: {
|
|
579
|
+
street: '1 Infinite Loop',
|
|
580
|
+
city: 'Cupertino',
|
|
581
|
+
state: 'CA',
|
|
582
|
+
postalCode: '95014',
|
|
583
|
+
country: 'USA',
|
|
584
|
+
ISOCountryCode: 'US',
|
|
585
|
+
subAdministrativeArea: '',
|
|
586
|
+
subLocality: '',
|
|
587
|
+
},
|
|
588
|
+
},
|
|
589
|
+
shippingContact: {
|
|
590
|
+
name: {
|
|
591
|
+
givenName: 'John',
|
|
592
|
+
familyName: 'Doe',
|
|
593
|
+
middleName: '',
|
|
594
|
+
namePrefix: '',
|
|
595
|
+
nameSuffix: '',
|
|
596
|
+
nickname: '',
|
|
597
|
+
},
|
|
598
|
+
emailAddress: 'johndoe@example.com',
|
|
599
|
+
phoneNumber: { stringValue: '+1-555-555-5555' },
|
|
600
|
+
postalAddress: {
|
|
601
|
+
street: '1 Infinite Loop',
|
|
602
|
+
city: 'Cupertino',
|
|
603
|
+
state: 'CA',
|
|
604
|
+
postalCode: '95014',
|
|
605
|
+
country: 'USA',
|
|
606
|
+
ISOCountryCode: 'US',
|
|
607
|
+
subAdministrativeArea: '',
|
|
608
|
+
subLocality: '',
|
|
609
|
+
},
|
|
610
|
+
},
|
|
611
|
+
shippingMethod: {
|
|
612
|
+
identifier: 'standard',
|
|
613
|
+
detail: 'Standard Shipping (3-5 business days)',
|
|
614
|
+
},
|
|
615
|
+
token: {
|
|
616
|
+
paymentData: JSON.stringify({
|
|
617
|
+
version: 'EC_v1',
|
|
618
|
+
data: 'enHx9XCGOPE...',
|
|
619
|
+
signature: 'abcd1234...',
|
|
620
|
+
header: {
|
|
621
|
+
ephemeralPublicKey: 'AbCdEf...',
|
|
622
|
+
publicKeyHash: 'gHiJkL...',
|
|
623
|
+
transactionId: 'txn01',
|
|
624
|
+
},
|
|
625
|
+
}),
|
|
626
|
+
paymentMethod: {
|
|
627
|
+
displayName: 'Visa',
|
|
628
|
+
network: 'Visa',
|
|
629
|
+
type: IosPKPaymentMethodType.PKPaymentMethodTypeDebit,
|
|
630
|
+
},
|
|
631
|
+
transactionIdentifier: 'txn123456789',
|
|
632
|
+
},
|
|
633
|
+
} as IosPKPayment)
|
|
634
|
+
);
|
|
635
|
+
|
|
636
|
+
const result = await request.show();
|
|
637
|
+
|
|
638
|
+
expect(NativePayments.show).toHaveBeenCalledWith(expect.any(String), expect.any(Object));
|
|
639
|
+
expect(result).toBeDefined();
|
|
640
|
+
expect(request.state).toBe('interactive');
|
|
641
|
+
});
|
|
642
|
+
|
|
643
|
+
it('should throw when `abort` is called in invalid state', async () => {
|
|
644
|
+
expect.assertions(1);
|
|
645
|
+
|
|
646
|
+
const request = new PaymentRequest([iosMethodData], paymentDetails);
|
|
647
|
+
|
|
648
|
+
await expect(request.abort()).rejects.toThrow(new DOMException(PaymentsErrorEnum.InvalidStateError));
|
|
649
|
+
});
|
|
650
|
+
|
|
651
|
+
it('should throw when `NativePayments.abort` rejects', async () => {
|
|
652
|
+
expect.assertions(1);
|
|
653
|
+
|
|
654
|
+
const expectedError = new PaymentsError(`Failed aborting PaymentRequest`);
|
|
655
|
+
jest.mocked(NativePayments.abort).mockRejectedValue(expectedError);
|
|
656
|
+
|
|
657
|
+
const request = new PaymentRequest([iosMethodData], paymentDetails);
|
|
658
|
+
request.state = 'interactive';
|
|
659
|
+
|
|
660
|
+
await expect(request.abort()).rejects.toThrow(expectedError);
|
|
661
|
+
});
|
|
662
|
+
|
|
663
|
+
it('should call NativePayments.abort and reject correctly when aborted', async () => {
|
|
664
|
+
expect.assertions(2);
|
|
665
|
+
|
|
666
|
+
const request = new PaymentRequest([iosMethodData], paymentDetails);
|
|
667
|
+
request.state = 'interactive';
|
|
668
|
+
jest.mocked(NativePayments.abort).mockResolvedValue(undefined);
|
|
669
|
+
|
|
670
|
+
await request.abort();
|
|
671
|
+
|
|
672
|
+
expect(NativePayments.abort).toHaveBeenCalledWith();
|
|
673
|
+
expect(request.state).toBe('closed');
|
|
674
|
+
});
|
|
675
|
+
|
|
676
|
+
it('should throw NotSupportedError if platform payment method is not found', () => {
|
|
677
|
+
expect.assertions(1);
|
|
678
|
+
|
|
679
|
+
const invalidMethodData = [
|
|
680
|
+
{
|
|
681
|
+
supportedMethods: 'unsupported-method',
|
|
682
|
+
data: {},
|
|
683
|
+
},
|
|
684
|
+
] as unknown as PaymentMethodData[];
|
|
685
|
+
|
|
686
|
+
expect(() => new PaymentRequest(invalidMethodData, paymentDetails)).toThrow(
|
|
687
|
+
new DOMException(PaymentsErrorEnum.NotSupportedError)
|
|
688
|
+
);
|
|
689
|
+
});
|
|
690
|
+
});
|
|
691
|
+
});
|