@infosoftas/s4-orders-js-sdk 0.1.19 → 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 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 PaymentMethodOptionsType = {
50
- [key in PaymentMethodEnum]: {
50
+ type PaymentMethodSettingsType = {
51
+ [key in PaymentMethodEnum]?: {
51
52
  generateSubscriberContact?: boolean;
52
53
  accountId?: string;
53
- orderFormFields: OrderFormFieldType[];
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?: PaymentMethodOptionsType;
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.19",
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
- "prop-types": "^15.8.1",
15
- "react": "^18.3.1",
16
- "react-dom": "^18.3.1",
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": "^7.29.6",
21
- "@babel/preset-env": "^7.28.5",
22
- "@babel/preset-react": "^7.28.5",
23
- "@babel/preset-typescript": "^7.28.5",
24
- "@types/react": "^19.2.6",
25
- "@types/react-dom": "^19.2.3",
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": "^20.2.1",
29
+ "release-it": "^21.0.2",
31
30
  "rimraf": "^6.1.2",
32
- "sass": "^1.94.2",
33
- "sass-loader": "^16.0.6",
31
+ "sass": "^1.102.0",
32
+ "sass-loader": "^17.0.0",
34
33
  "style-loader": "^4.0.0",
35
- "ts-loader": "^9.5.4",
34
+ "ts-loader": "^9.6.2",
36
35
  "typescript": "^6.0.2",
37
- "webpack": "^5.105.2",
38
- "webpack-cli": "^6.0.1"
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 (type: MessageEventTypeEnum) => {
215
+ const handleMessageEvent = async (
216
+ type: MessageEventTypeEnum,
217
+ canceledOrderId?: string
218
+ ) => {
216
219
  if (type === MessageEventTypeEnum.CANCEL) {
217
220
  try {
218
- if (orderId) {
219
- await handleOrderDelete(orderId);
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);
@@ -19,8 +19,8 @@ type OrderCompleteRequestType = {
19
19
  };
20
20
 
21
21
  type OrderResponseType = {
22
- terminalRedirectUrl: string;
23
- id: string;
22
+ terminalRedirectUrl: string | null;
23
+ id: string | null;
24
24
  };
25
25
 
26
26
  export const orderStart = (
@@ -8,7 +8,7 @@ import InputField from '../FormFields/InputField';
8
8
  import { PaymentMethodEnum, UserActionEnum } from '../../enums/general';
9
9
  import {
10
10
  OrderFormFieldType,
11
- PaymentMethodOptionsType,
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?: PaymentMethodOptionsType;
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
- PaymentMethodOptionsType,
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?: PaymentMethodOptionsType;
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
- PaymentMethodOptionsType,
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?: PaymentMethodOptionsType;
55
+ paymentMethodsOptions?: PaymentMethodSettingsType;
55
56
  defaultValues?: OrderFormInputsType;
56
- paymentMethods?: { label: string; value: PaymentMethodEnum }[];
57
+ paymentMethods?: PaymentMethodOptionType[];
57
58
  allowedPaymentMethods?: PaymentMethodEnum[];
58
59
  submitButtonText?: string;
59
60
  orderDenialCloseButtonText?: string;
@@ -324,7 +325,18 @@ const OrderForm: FC<Props> = ({
324
325
  value={item.value}
325
326
  />
326
327
  <span className="value-block">
327
- <span className="prefix-icon"></span>
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}
328
340
  <span className="text-block">
329
341
  {item.label}
330
342
  </span>
@@ -2,6 +2,7 @@ export enum PaymentMethodEnum {
2
2
  Vipps = 'Vipps',
3
3
  MobilePay = 'MobilePay',
4
4
  SwedbankPay = 'SwedbankPay',
5
+ Mollie = 'Mollie',
5
6
  Invoice = 'Invoice',
6
7
  Email = 'Email',
7
8
  EHF = 'EHF',
@@ -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
- PaymentMethodOptionsType,
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?: PaymentMethodOptionsType;
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,
@@ -16,8 +16,8 @@ export type OrderFormFieldType = {
16
16
  label?: string;
17
17
  };
18
18
 
19
- export type PaymentMethodOptionsType = {
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?: { label: string; value: PaymentMethodEnum }[];
78
+ availablePaymentMethods?: PaymentMethodOptionType[];
68
79
  allowedPaymentMethods?: PaymentMethodEnum[];
69
- paymentMethodsOptions?: PaymentMethodOptionsType;
80
+ paymentMethodsOptions?: PaymentMethodSettingsType;
70
81
  requireTermsAcceptance?: boolean;
71
82
  language?: string;
72
83
  merchantAgreementUrl?: string;
@@ -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 mapToSwedbankLanguage = (language: string) => {
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'; // Default value for 'Swedbank'
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: mapToSwedbankLanguage(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