@infosoftas/s4-orders-js-sdk 0.1.18 → 0.1.20
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/README.md +22 -4
- package/package.json +25 -18
- package/src/App.tsx +28 -3
- package/src/api/OrdersApi.ts +2 -2
- package/src/components/EHFForm/EHFForm.tsx +2 -2
- package/src/components/OIOForm/OIOForm.tsx +2 -2
- package/src/components/OrderForm/OrderForm.tsx +145 -114
- package/src/enums/general.ts +1 -0
- package/src/hooks/useMessageEvent.tsx +54 -3
- package/src/hooks/useOrderForm.tsx +9 -2
- package/src/types/general.ts +15 -4
- package/src/types/order.ts +8 -0
- package/src/utils/order.helper.ts +14 -3
package/README.md
CHANGED
|
@@ -33,6 +33,7 @@ enum PaymentMethodEnum {
|
|
|
33
33
|
Vipps = 'Vipps',
|
|
34
34
|
MobilePay = 'MobilePay',
|
|
35
35
|
SwedbankPay = 'SwedbankPay',
|
|
36
|
+
Mollie = 'Mollie',
|
|
36
37
|
Invoice = 'Invoice',
|
|
37
38
|
Email = 'Email',
|
|
38
39
|
EHF = 'EHF',
|
|
@@ -46,11 +47,11 @@ type OrderFormFieldType = {
|
|
|
46
47
|
label?: string;
|
|
47
48
|
};
|
|
48
49
|
|
|
49
|
-
type
|
|
50
|
-
[key in PaymentMethodEnum]
|
|
50
|
+
type PaymentMethodSettingsType = {
|
|
51
|
+
[key in PaymentMethodEnum]?: {
|
|
51
52
|
generateSubscriberContact?: boolean;
|
|
52
53
|
accountId?: string;
|
|
53
|
-
orderFormFields
|
|
54
|
+
orderFormFields?: OrderFormFieldType[];
|
|
54
55
|
paymentInvoiceFields?: OrderFormFieldType[] | never[] | null;
|
|
55
56
|
};
|
|
56
57
|
}
|
|
@@ -105,7 +106,7 @@ type ConfigType = {
|
|
|
105
106
|
* Controls which input fields are shown in the order form for each payment method.
|
|
106
107
|
* Use accountId on SwedbankPay to set your Swedbank Pay account ID.
|
|
107
108
|
*/
|
|
108
|
-
paymentMethodsOptions?:
|
|
109
|
+
paymentMethodsOptions?: PaymentMethodSettingsType;
|
|
109
110
|
requireTermsAcceptance?: boolean;
|
|
110
111
|
language?: string;
|
|
111
112
|
merchantAgreementUrl?: string;
|
|
@@ -211,6 +212,7 @@ export const prepareConfig = ({
|
|
|
211
212
|
{ label: "Vipps", value: "Vipps" },
|
|
212
213
|
{ label: "MobilePay", value: "MobilePay" },
|
|
213
214
|
{ label: "Credit Card/Debit Card", value: "SwedbankPay" },
|
|
215
|
+
{ label: "Mollie", value: "Mollie" },
|
|
214
216
|
{ label: 'Invoice', translateKey: 'Invoice', value: 'Invoice' },
|
|
215
217
|
{ label: 'Email', translateKey: 'Email', value: 'Email' },
|
|
216
218
|
{ label: 'EHF', translateKey: 'EHF', value: 'EHF' },
|
|
@@ -242,6 +244,22 @@ export const prepareConfig = ({
|
|
|
242
244
|
},
|
|
243
245
|
],
|
|
244
246
|
},
|
|
247
|
+
Mollie: {
|
|
248
|
+
accountId: 'your-mollie-account-id',
|
|
249
|
+
orderFormFields: [
|
|
250
|
+
{
|
|
251
|
+
name: OrderModuleFiledNameEnum.name,
|
|
252
|
+
label: 'Name',
|
|
253
|
+
required: false,
|
|
254
|
+
},
|
|
255
|
+
{
|
|
256
|
+
name: OrderModuleFiledNameEnum.email,
|
|
257
|
+
label: 'Email',
|
|
258
|
+
required: true,
|
|
259
|
+
readOnly: !!userEmail,
|
|
260
|
+
},
|
|
261
|
+
],
|
|
262
|
+
},
|
|
245
263
|
Invoice: {
|
|
246
264
|
orderFormFields: [
|
|
247
265
|
{
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@infosoftas/s4-orders-js-sdk",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.20",
|
|
4
4
|
"scripts": {
|
|
5
5
|
"build": "rimraf lib && webpack --mode production",
|
|
6
6
|
"release": "release-it"
|
|
@@ -11,33 +11,40 @@
|
|
|
11
11
|
]
|
|
12
12
|
},
|
|
13
13
|
"dependencies": {
|
|
14
|
-
"
|
|
15
|
-
"react": "^
|
|
16
|
-
"react-
|
|
17
|
-
"react-hook-form": "^7.66.1"
|
|
14
|
+
"react": "^19.2.8",
|
|
15
|
+
"react-dom": "^19.2.8",
|
|
16
|
+
"react-hook-form": "^7.85.0"
|
|
18
17
|
},
|
|
19
18
|
"devDependencies": {
|
|
20
|
-
"@babel/core": "^
|
|
21
|
-
"@babel/preset-env": "^
|
|
22
|
-
"@babel/preset-react": "^
|
|
23
|
-
"@babel/preset-typescript": "^
|
|
24
|
-
"@types/react": "^19.2.
|
|
25
|
-
"@types/react-dom": "^19.2.
|
|
19
|
+
"@babel/core": "^8.0.1",
|
|
20
|
+
"@babel/preset-env": "^8.0.2",
|
|
21
|
+
"@babel/preset-react": "^8.0.1",
|
|
22
|
+
"@babel/preset-typescript": "^8.0.1",
|
|
23
|
+
"@types/react": "^19.2.18",
|
|
24
|
+
"@types/react-dom": "^19.2.4",
|
|
26
25
|
"babel-loader": "^10.0.0",
|
|
27
26
|
"copy-webpack-plugin": "^14.0.0",
|
|
28
27
|
"css-loader": "^7.1.2",
|
|
29
28
|
"mini-css-extract-plugin": "^2.9.4",
|
|
30
|
-
"release-it": "^
|
|
29
|
+
"release-it": "^21.0.2",
|
|
31
30
|
"rimraf": "^6.1.2",
|
|
32
|
-
"sass": "^1.
|
|
33
|
-
"sass-loader": "^
|
|
31
|
+
"sass": "^1.102.0",
|
|
32
|
+
"sass-loader": "^17.0.0",
|
|
34
33
|
"style-loader": "^4.0.0",
|
|
35
|
-
"ts-loader": "^9.
|
|
34
|
+
"ts-loader": "^9.6.2",
|
|
36
35
|
"typescript": "^6.0.2",
|
|
37
|
-
"webpack": "^5.
|
|
38
|
-
"webpack-cli": "^
|
|
36
|
+
"webpack": "^5.109.2",
|
|
37
|
+
"webpack-cli": "^7.2.2"
|
|
39
38
|
},
|
|
40
39
|
"publishConfig": {
|
|
41
40
|
"access": "public"
|
|
42
|
-
}
|
|
41
|
+
},
|
|
42
|
+
"engines": {
|
|
43
|
+
"node": "^22.21.0 || >=24.11.0"
|
|
44
|
+
},
|
|
45
|
+
"browserslist": [
|
|
46
|
+
"> 0.5%",
|
|
47
|
+
"last 2 versions",
|
|
48
|
+
"not dead"
|
|
49
|
+
]
|
|
43
50
|
}
|
package/src/App.tsx
CHANGED
|
@@ -212,11 +212,15 @@ const App: FC<ConfigType> = ({
|
|
|
212
212
|
setShowOrderForm(true);
|
|
213
213
|
};
|
|
214
214
|
|
|
215
|
-
const handleMessageEvent = async (
|
|
215
|
+
const handleMessageEvent = async (
|
|
216
|
+
type: MessageEventTypeEnum,
|
|
217
|
+
canceledOrderId?: string
|
|
218
|
+
) => {
|
|
216
219
|
if (type === MessageEventTypeEnum.CANCEL) {
|
|
217
220
|
try {
|
|
218
|
-
|
|
219
|
-
|
|
221
|
+
const orderIdToCancel = canceledOrderId || orderId;
|
|
222
|
+
if (orderIdToCancel) {
|
|
223
|
+
await handleOrderDelete(orderIdToCancel);
|
|
220
224
|
}
|
|
221
225
|
} catch (error) {
|
|
222
226
|
console.log(error);
|
|
@@ -284,11 +288,32 @@ const App: FC<ConfigType> = ({
|
|
|
284
288
|
return;
|
|
285
289
|
}
|
|
286
290
|
|
|
291
|
+
if (
|
|
292
|
+
data?.paymentMethod === PaymentMethodEnum.Mollie &&
|
|
293
|
+
data?.orderId
|
|
294
|
+
) {
|
|
295
|
+
try {
|
|
296
|
+
sessionStorage.setItem(
|
|
297
|
+
'mollieOrderId',
|
|
298
|
+
JSON.stringify({ id: data.orderId, ts: Date.now() })
|
|
299
|
+
);
|
|
300
|
+
} catch (e) {
|
|
301
|
+
console.warn(
|
|
302
|
+
'Could not persist mollieOrderId; return leg will rely on the S4OrderId param.',
|
|
303
|
+
e
|
|
304
|
+
);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
287
308
|
setTimeout(() => {
|
|
288
309
|
window.location.href = url;
|
|
289
310
|
}, 300);
|
|
290
311
|
|
|
291
312
|
return;
|
|
313
|
+
} else if (data?.paymentMethod) {
|
|
314
|
+
setIsFailed(true);
|
|
315
|
+
setShowOrderForm(true);
|
|
316
|
+
setFormType(FormTypeEnum.ORDER);
|
|
292
317
|
}
|
|
293
318
|
|
|
294
319
|
setIframeSrc(null);
|
package/src/api/OrdersApi.ts
CHANGED
|
@@ -8,7 +8,7 @@ import InputField from '../FormFields/InputField';
|
|
|
8
8
|
import { PaymentMethodEnum, UserActionEnum } from '../../enums/general';
|
|
9
9
|
import {
|
|
10
10
|
OrderFormFieldType,
|
|
11
|
-
|
|
11
|
+
PaymentMethodSettingsType,
|
|
12
12
|
ContactRequestType,
|
|
13
13
|
OrderDenialFallbackOfferType,
|
|
14
14
|
} from '../../types/general';
|
|
@@ -44,7 +44,7 @@ type Props = {
|
|
|
44
44
|
language: string;
|
|
45
45
|
merchantAgreementUrl: string;
|
|
46
46
|
orderValues?: OrderFormInputsType;
|
|
47
|
-
paymentMethodsOptions?:
|
|
47
|
+
paymentMethodsOptions?: PaymentMethodSettingsType;
|
|
48
48
|
invoiceAddressSelection?: {
|
|
49
49
|
enabled?: boolean;
|
|
50
50
|
label?: string;
|
|
@@ -10,7 +10,7 @@ import { DEFAULT_ORDER_FORM_FIELDS } from '../FormFields/FormFields.helper';
|
|
|
10
10
|
import { PaymentMethodEnum, UserActionEnum } from '../../enums/general';
|
|
11
11
|
import {
|
|
12
12
|
OrderFormFieldType,
|
|
13
|
-
|
|
13
|
+
PaymentMethodSettingsType,
|
|
14
14
|
ContactRequestType,
|
|
15
15
|
OrderDenialFallbackOfferType,
|
|
16
16
|
} from '../../types/general';
|
|
@@ -45,7 +45,7 @@ type Props = {
|
|
|
45
45
|
language: string;
|
|
46
46
|
merchantAgreementUrl: string;
|
|
47
47
|
orderValues?: OrderFormInputsType;
|
|
48
|
-
paymentMethodsOptions?:
|
|
48
|
+
paymentMethodsOptions?: PaymentMethodSettingsType;
|
|
49
49
|
invoiceAddressSelection?: {
|
|
50
50
|
enabled?: boolean;
|
|
51
51
|
label?: string;
|
|
@@ -22,7 +22,8 @@ import formFieldsMapper from '../FormFields/FormFieldsMapper';
|
|
|
22
22
|
import { DEFAULT_ORDER_FORM_FIELDS } from '../FormFields/FormFields.helper';
|
|
23
23
|
import { OrderFormInputsType, OrderInfoType } from '../../types/order';
|
|
24
24
|
import {
|
|
25
|
-
|
|
25
|
+
PaymentMethodSettingsType,
|
|
26
|
+
PaymentMethodOptionType,
|
|
26
27
|
OrderFormFieldType,
|
|
27
28
|
ContactRequestType,
|
|
28
29
|
OrderDenialFallbackOfferType,
|
|
@@ -51,9 +52,9 @@ type Props = {
|
|
|
51
52
|
showIframe?: boolean;
|
|
52
53
|
language: string;
|
|
53
54
|
merchantAgreementUrl: string;
|
|
54
|
-
paymentMethodsOptions?:
|
|
55
|
+
paymentMethodsOptions?: PaymentMethodSettingsType;
|
|
55
56
|
defaultValues?: OrderFormInputsType;
|
|
56
|
-
paymentMethods?:
|
|
57
|
+
paymentMethods?: PaymentMethodOptionType[];
|
|
57
58
|
allowedPaymentMethods?: PaymentMethodEnum[];
|
|
58
59
|
submitButtonText?: string;
|
|
59
60
|
orderDenialCloseButtonText?: string;
|
|
@@ -304,123 +305,153 @@ const OrderForm: FC<Props> = ({
|
|
|
304
305
|
data-testid="sdk-order-form-id"
|
|
305
306
|
>
|
|
306
307
|
{paymentMethods?.length > 0 && (
|
|
307
|
-
<div className="
|
|
308
|
-
<div className="field-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
className="radio-button-control"
|
|
312
|
-
key={item.value}
|
|
313
|
-
>
|
|
314
|
-
<label className="radio-badge">
|
|
315
|
-
<input
|
|
316
|
-
{...register('paymentMethod', {
|
|
317
|
-
required: true,
|
|
318
|
-
})}
|
|
319
|
-
onChange={handlePaymentChange}
|
|
320
|
-
type="radio"
|
|
321
|
-
value={item.value}
|
|
322
|
-
/>
|
|
323
|
-
<span className="value-block">
|
|
324
|
-
<span className="prefix-icon"></span>
|
|
325
|
-
<span className="text-block">
|
|
326
|
-
{item.label}
|
|
327
|
-
</span>
|
|
328
|
-
</span>
|
|
329
|
-
</label>
|
|
308
|
+
<div className="sdk-payment-method-section">
|
|
309
|
+
<div className="field-wrapper radio-button-group">
|
|
310
|
+
<div className="field-label">
|
|
311
|
+
{paymentMethodLabel}
|
|
330
312
|
</div>
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
313
|
+
{paymentMethods.map((item) => (
|
|
314
|
+
<div
|
|
315
|
+
className="radio-button-control"
|
|
316
|
+
key={item.value}
|
|
317
|
+
>
|
|
318
|
+
<label className="radio-badge">
|
|
319
|
+
<input
|
|
320
|
+
{...register('paymentMethod', {
|
|
321
|
+
required: true,
|
|
322
|
+
})}
|
|
323
|
+
onChange={handlePaymentChange}
|
|
324
|
+
type="radio"
|
|
325
|
+
value={item.value}
|
|
326
|
+
/>
|
|
327
|
+
<span className="value-block">
|
|
328
|
+
{item.icons &&
|
|
329
|
+
item.icons.length > 0 ? (
|
|
330
|
+
<span className="prefix-icon">
|
|
331
|
+
{item.icons.map((icon) => (
|
|
332
|
+
<img
|
|
333
|
+
key={icon.src}
|
|
334
|
+
src={icon.src}
|
|
335
|
+
alt={icon.alt}
|
|
336
|
+
/>
|
|
337
|
+
))}
|
|
338
|
+
</span>
|
|
339
|
+
) : null}
|
|
340
|
+
<span className="text-block">
|
|
341
|
+
{item.label}
|
|
342
|
+
</span>
|
|
343
|
+
</span>
|
|
344
|
+
</label>
|
|
345
|
+
</div>
|
|
346
|
+
))}
|
|
347
|
+
{errors.paymentMethod && (
|
|
348
|
+
<div className="text-error caption">
|
|
349
|
+
{errors.paymentMethod.message}
|
|
350
|
+
</div>
|
|
351
|
+
)}
|
|
352
|
+
</div>
|
|
337
353
|
</div>
|
|
338
354
|
)}
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
355
|
+
<div className="sdk-order-details-section">
|
|
356
|
+
{orderFields?.length > 0 && allowPaymentMethod && (
|
|
357
|
+
<Suspense fallback={null}>
|
|
358
|
+
{orderFields.map((field) => {
|
|
359
|
+
const Component = formFieldsMapper[field.name];
|
|
343
360
|
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
361
|
+
return Component ? (
|
|
362
|
+
<Component
|
|
363
|
+
key={field.name}
|
|
364
|
+
name={field.name}
|
|
365
|
+
label={field.label}
|
|
366
|
+
required={field.required || false}
|
|
367
|
+
readOnly={field.readOnly || false}
|
|
368
|
+
errors={errors}
|
|
369
|
+
errorReqMsg={errorReqMsg}
|
|
370
|
+
errorInvalidEmailMsg={
|
|
371
|
+
errorInvalidEmailMsg
|
|
372
|
+
}
|
|
373
|
+
errorInvalidPhoneMsg={
|
|
374
|
+
errorInvalidPhoneMsg
|
|
375
|
+
}
|
|
376
|
+
/>
|
|
377
|
+
) : null;
|
|
378
|
+
})}
|
|
379
|
+
</Suspense>
|
|
380
|
+
)}
|
|
381
|
+
{showInvoiceToggle && (
|
|
382
|
+
<ToggleField
|
|
383
|
+
name="invoiceAddressSelection"
|
|
384
|
+
label={
|
|
385
|
+
invoiceAddressSelection?.label ??
|
|
386
|
+
'Invoice Address'
|
|
387
|
+
}
|
|
388
|
+
toggleCallback={(e) =>
|
|
389
|
+
userActionCallback?.(
|
|
390
|
+
UserActionEnum.TOGGLE_INVOICE_ADDRESS,
|
|
391
|
+
{ value: e }
|
|
392
|
+
)
|
|
393
|
+
}
|
|
394
|
+
/>
|
|
395
|
+
)}
|
|
396
|
+
{showInvoiceFields && (
|
|
397
|
+
<Suspense fallback={null}>
|
|
398
|
+
{invoiceOrderFields.map((field) => {
|
|
399
|
+
const Component = formFieldsMapper[field.name];
|
|
378
400
|
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
401
|
+
return Component ? (
|
|
402
|
+
<Component
|
|
403
|
+
key={field.name}
|
|
404
|
+
name={field.name}
|
|
405
|
+
label={field.label}
|
|
406
|
+
required={field.required || false}
|
|
407
|
+
readOnly={field.readOnly || false}
|
|
408
|
+
errors={errors}
|
|
409
|
+
errorReqMsg={errorReqMsg}
|
|
410
|
+
errorInvalidEmailMsg={
|
|
411
|
+
errorInvalidEmailMsg
|
|
412
|
+
}
|
|
413
|
+
errorInvalidPhoneMsg={
|
|
414
|
+
errorInvalidPhoneMsg
|
|
415
|
+
}
|
|
416
|
+
/>
|
|
417
|
+
) : null;
|
|
418
|
+
})}
|
|
419
|
+
</Suspense>
|
|
420
|
+
)}
|
|
421
|
+
{requireTermsAcceptance && (
|
|
422
|
+
<TermsCheckbox
|
|
423
|
+
name="termsAccept"
|
|
424
|
+
label={termsAndConditionsText}
|
|
425
|
+
required
|
|
426
|
+
errors={errors}
|
|
427
|
+
/>
|
|
428
|
+
)}
|
|
429
|
+
{allowPaymentMethod && (
|
|
430
|
+
<Button
|
|
431
|
+
type="submit"
|
|
432
|
+
loading={loading}
|
|
433
|
+
buttonText={submitButtonText}
|
|
434
|
+
disable={isSubmitButtonDisabled}
|
|
435
|
+
/>
|
|
436
|
+
)}
|
|
437
|
+
<Alert
|
|
438
|
+
className="mt-2"
|
|
439
|
+
msg={
|
|
440
|
+
allowPaymentMethod
|
|
441
|
+
? apiErrorMsg
|
|
442
|
+
: paymentMethodNotAllowedMsg
|
|
443
|
+
}
|
|
409
444
|
/>
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
allowPaymentMethod &&
|
|
421
|
-
errorsMsg.map((i, index) => (
|
|
422
|
-
<Alert key={`${i}-${index}`} className="mt-2" msg={i} />
|
|
423
|
-
))}
|
|
445
|
+
{errorsMsg?.length > 0 &&
|
|
446
|
+
allowPaymentMethod &&
|
|
447
|
+
errorsMsg.map((i, index) => (
|
|
448
|
+
<Alert
|
|
449
|
+
key={`${i}-${index}`}
|
|
450
|
+
className="mt-2"
|
|
451
|
+
msg={i}
|
|
452
|
+
/>
|
|
453
|
+
))}
|
|
454
|
+
</div>
|
|
424
455
|
<OrderDenialModal
|
|
425
456
|
isOpen={!!orderDenialType}
|
|
426
457
|
message={orderDenialMessage}
|
package/src/enums/general.ts
CHANGED
|
@@ -4,9 +4,30 @@ import { MessageEventTypeEnum, PaymentMethodEnum } from '../enums/general';
|
|
|
4
4
|
import { CompleteOrderParamsType, OrderInfoType } from '../types/order';
|
|
5
5
|
import useQueryParams from './useQueryParams';
|
|
6
6
|
|
|
7
|
+
const MOLLIE_ORDER_TTL_MS = 20 * 60 * 1000;
|
|
8
|
+
|
|
9
|
+
// Reads and consumes the persisted Mollie order id. The key is always removed:
|
|
10
|
+
// the return leg gets one chance to use it, whether or not it is still valid.
|
|
11
|
+
const takeStoredMollieOrderId = (): string | null => {
|
|
12
|
+
const raw = sessionStorage.getItem('mollieOrderId');
|
|
13
|
+
if (!raw) {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
sessionStorage.removeItem('mollieOrderId');
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
const { id, ts } = JSON.parse(raw) as { id: string; ts: number };
|
|
21
|
+
|
|
22
|
+
return Date.now() - ts <= MOLLIE_ORDER_TTL_MS ? id : null;
|
|
23
|
+
} catch {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
|
|
7
28
|
const useMessageEvent = (
|
|
8
29
|
messageCallback: (data: CompleteOrderParamsType) => Promise<void>,
|
|
9
|
-
handleMessageEvent: (type: MessageEventTypeEnum) => void,
|
|
30
|
+
handleMessageEvent: (type: MessageEventTypeEnum, orderId?: string) => void,
|
|
10
31
|
showIframe: boolean,
|
|
11
32
|
orderInfo: OrderInfoType | null
|
|
12
33
|
) => {
|
|
@@ -126,10 +147,13 @@ const useMessageEvent = (
|
|
|
126
147
|
|
|
127
148
|
if (
|
|
128
149
|
queryParams.get('action') === MessageEventTypeEnum.CANCEL &&
|
|
129
|
-
!showIframe
|
|
150
|
+
(!showIframe || !queryParams.get('TransactionId'))
|
|
130
151
|
) {
|
|
152
|
+
const cancelOrderId =
|
|
153
|
+
queryParams.get('S4OrderId') || takeStoredMollieOrderId();
|
|
131
154
|
handleMessageEvent(
|
|
132
|
-
queryParams.get('action') as MessageEventTypeEnum
|
|
155
|
+
queryParams.get('action') as MessageEventTypeEnum,
|
|
156
|
+
cancelOrderId || undefined
|
|
133
157
|
);
|
|
134
158
|
}
|
|
135
159
|
}, [
|
|
@@ -138,6 +162,33 @@ const useMessageEvent = (
|
|
|
138
162
|
queryParams.get('TransactionId'),
|
|
139
163
|
showIframe,
|
|
140
164
|
]);
|
|
165
|
+
|
|
166
|
+
// Mollie: a completion return with no TransactionId (that one is SwedbankPay's).
|
|
167
|
+
useEffect(() => {
|
|
168
|
+
if (
|
|
169
|
+
queryParams.get('action') !== MessageEventTypeEnum.COMPLETE ||
|
|
170
|
+
queryParams.get('TransactionId')
|
|
171
|
+
) {
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const s4OrderId = queryParams.get('S4OrderId');
|
|
176
|
+
const storedOrderId = takeStoredMollieOrderId();
|
|
177
|
+
const mollieOrderId = s4OrderId || storedOrderId;
|
|
178
|
+
if (!mollieOrderId) {
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
messageCallback({
|
|
183
|
+
orderId: mollieOrderId,
|
|
184
|
+
agreementId: '',
|
|
185
|
+
orderInfo: orderInfo || null,
|
|
186
|
+
});
|
|
187
|
+
}, [
|
|
188
|
+
queryParams.get('action'),
|
|
189
|
+
queryParams.get('S4OrderId'),
|
|
190
|
+
queryParams.get('TransactionId'),
|
|
191
|
+
]);
|
|
141
192
|
};
|
|
142
193
|
|
|
143
194
|
export default useMessageEvent;
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
ErrorsMsg,
|
|
16
16
|
OrderFormFieldType,
|
|
17
17
|
OrderDenialFallbackOfferType,
|
|
18
|
-
|
|
18
|
+
PaymentMethodSettingsType,
|
|
19
19
|
ContactRequestType,
|
|
20
20
|
} from '../types/general';
|
|
21
21
|
import {
|
|
@@ -46,7 +46,7 @@ type Props = {
|
|
|
46
46
|
showIframe?: boolean;
|
|
47
47
|
language: string;
|
|
48
48
|
merchantAgreementUrl: string;
|
|
49
|
-
paymentMethodsOptions?:
|
|
49
|
+
paymentMethodsOptions?: PaymentMethodSettingsType;
|
|
50
50
|
orderFields: OrderFormFieldType[];
|
|
51
51
|
invoiceAddressToggle?: boolean;
|
|
52
52
|
invoiceOrderFields: OrderFormFieldType[];
|
|
@@ -293,6 +293,13 @@ const useOrderForm = ({
|
|
|
293
293
|
orderReference: data.orderReference || undefined,
|
|
294
294
|
});
|
|
295
295
|
|
|
296
|
+
if (!responseOrder.id) {
|
|
297
|
+
callback(null);
|
|
298
|
+
setApiErrorMsg(WRONG_MSG);
|
|
299
|
+
setErrorsMsg([]);
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
|
|
296
303
|
callback(responseOrder.terminalRedirectUrl, {
|
|
297
304
|
...(data || {}),
|
|
298
305
|
orderId: responseOrder.id,
|
package/src/types/general.ts
CHANGED
|
@@ -16,8 +16,8 @@ export type OrderFormFieldType = {
|
|
|
16
16
|
label?: string;
|
|
17
17
|
};
|
|
18
18
|
|
|
19
|
-
export type
|
|
20
|
-
[key in PaymentMethodEnum]
|
|
19
|
+
export type PaymentMethodSettingsType = {
|
|
20
|
+
[key in PaymentMethodEnum]?: {
|
|
21
21
|
generateSubscriberContact?: boolean;
|
|
22
22
|
accountId?: string;
|
|
23
23
|
orderFormFields?: OrderFormFieldType[];
|
|
@@ -45,6 +45,17 @@ export type OrderDenialFallbackOfferType = {
|
|
|
45
45
|
templatePackageId?: string;
|
|
46
46
|
};
|
|
47
47
|
|
|
48
|
+
export type PaymentIconType = {
|
|
49
|
+
src: string;
|
|
50
|
+
alt: string;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export type PaymentMethodOptionType = {
|
|
54
|
+
label: string;
|
|
55
|
+
value: PaymentMethodEnum;
|
|
56
|
+
icons?: PaymentIconType[];
|
|
57
|
+
};
|
|
58
|
+
|
|
48
59
|
export type ConfigType = {
|
|
49
60
|
submitStartCallback?: (subscriberId: string) => void;
|
|
50
61
|
userActionCallback?: (
|
|
@@ -64,9 +75,9 @@ export type ConfigType = {
|
|
|
64
75
|
apiUrl?: string;
|
|
65
76
|
redirectUrl?: string;
|
|
66
77
|
showIframe?: boolean;
|
|
67
|
-
availablePaymentMethods?:
|
|
78
|
+
availablePaymentMethods?: PaymentMethodOptionType[];
|
|
68
79
|
allowedPaymentMethods?: PaymentMethodEnum[];
|
|
69
|
-
paymentMethodsOptions?:
|
|
80
|
+
paymentMethodsOptions?: PaymentMethodSettingsType;
|
|
70
81
|
requireTermsAcceptance?: boolean;
|
|
71
82
|
language?: string;
|
|
72
83
|
merchantAgreementUrl?: string;
|
package/src/types/order.ts
CHANGED
|
@@ -42,10 +42,18 @@ export type SwedbankPay = {
|
|
|
42
42
|
language?: string;
|
|
43
43
|
};
|
|
44
44
|
|
|
45
|
+
export type Mollie = {
|
|
46
|
+
accountId?: string;
|
|
47
|
+
returnUrl: string;
|
|
48
|
+
cancelUrl?: string;
|
|
49
|
+
language?: string;
|
|
50
|
+
};
|
|
51
|
+
|
|
45
52
|
export type AgreementsType = {
|
|
46
53
|
paymentProvider: PaymentMethodEnum;
|
|
47
54
|
vippsMobilePay?: VippsMobilePay;
|
|
48
55
|
swedbankPay?: SwedbankPay;
|
|
56
|
+
mollie?: Mollie;
|
|
49
57
|
};
|
|
50
58
|
|
|
51
59
|
export type OrderInfoType = {
|
|
@@ -14,7 +14,7 @@ type AgreementModelType = {
|
|
|
14
14
|
accountId?: string;
|
|
15
15
|
};
|
|
16
16
|
|
|
17
|
-
const
|
|
17
|
+
const mapToPaymentProviderLanguage = (language: string) => {
|
|
18
18
|
const allowedLanguages = [
|
|
19
19
|
'en-US',
|
|
20
20
|
'sv-SE',
|
|
@@ -32,7 +32,7 @@ const mapToSwedbankLanguage = (language: string) => {
|
|
|
32
32
|
|
|
33
33
|
console.warn(`Language ${language} not supported.`);
|
|
34
34
|
|
|
35
|
-
return 'en-US';
|
|
35
|
+
return 'en-US';
|
|
36
36
|
};
|
|
37
37
|
|
|
38
38
|
export const prepareAgreementModel = ({
|
|
@@ -78,13 +78,24 @@ export const prepareAgreementModel = ({
|
|
|
78
78
|
model.swedbankPay = {
|
|
79
79
|
cancelUrl: `${cancelUrl}`,
|
|
80
80
|
completeUrl: `${confirmUrl}`,
|
|
81
|
-
language:
|
|
81
|
+
language: mapToPaymentProviderLanguage(language),
|
|
82
82
|
accountId,
|
|
83
83
|
};
|
|
84
84
|
|
|
85
85
|
return model;
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
+
if (paymentMethod === PaymentMethodEnum.Mollie) {
|
|
89
|
+
model.mollie = {
|
|
90
|
+
accountId,
|
|
91
|
+
returnUrl: `${confirmUrl}`,
|
|
92
|
+
cancelUrl: `${cancelUrl}`,
|
|
93
|
+
language: mapToPaymentProviderLanguage(language),
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
return model;
|
|
97
|
+
}
|
|
98
|
+
|
|
88
99
|
if (
|
|
89
100
|
paymentMethod === PaymentMethodEnum.Vipps ||
|
|
90
101
|
paymentMethod === PaymentMethodEnum.MobilePay
|