@paydock/client-sdk 1.145.0-beta → 1.148.0-beta

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.
Files changed (43) hide show
  1. package/README.md +129 -1
  2. package/bundles/index.cjs +935 -103
  3. package/bundles/index.cjs.d.ts +629 -336
  4. package/bundles/index.mjs +935 -104
  5. package/bundles/index.mjs.d.ts +629 -336
  6. package/bundles/types/api/api-base.d.ts +6 -0
  7. package/bundles/types/api/api-base.d.ts.map +1 -1
  8. package/bundles/types/api/api-charge-internal.d.ts +4 -3
  9. package/bundles/types/api/api-charge-internal.d.ts.map +1 -1
  10. package/bundles/types/canvas-3ds/canvas-3ds-event-map.d.ts +65 -0
  11. package/bundles/types/canvas-3ds/canvas-3ds-event-map.d.ts.map +1 -1
  12. package/bundles/types/canvas-3ds/canvas-3ds.d.ts +34 -5
  13. package/bundles/types/canvas-3ds/canvas-3ds.d.ts.map +1 -1
  14. package/bundles/types/canvas-3ds/index.d.ts +1 -1
  15. package/bundles/types/canvas-3ds/index.d.ts.map +1 -1
  16. package/bundles/types/canvas-3ds/services/gpayments-service.d.ts +151 -2
  17. package/bundles/types/canvas-3ds/services/gpayments-service.d.ts.map +1 -1
  18. package/bundles/types/canvas-3ds/services/standalone3ds-service.d.ts +16 -0
  19. package/bundles/types/canvas-3ds/services/standalone3ds-service.d.ts.map +1 -1
  20. package/bundles/types/components/iframe-event.d.ts +4 -3
  21. package/bundles/types/components/iframe-event.d.ts.map +1 -1
  22. package/bundles/types/components/iframe.d.ts +5 -0
  23. package/bundles/types/components/iframe.d.ts.map +1 -1
  24. package/bundles/types/components/param.d.ts +19 -0
  25. package/bundles/types/components/param.d.ts.map +1 -1
  26. package/bundles/types/index-cba.d.ts +1 -1
  27. package/bundles/types/index-cba.d.ts.map +1 -1
  28. package/bundles/types/index.d.ts +2 -2
  29. package/bundles/types/index.d.ts.map +1 -1
  30. package/bundles/types/widget/multi-widget.d.ts +19 -0
  31. package/bundles/types/widget/multi-widget.d.ts.map +1 -1
  32. package/bundles/widget.umd.js +935 -103
  33. package/bundles/widget.umd.js.d.ts +629 -336
  34. package/bundles/widget.umd.js.min.d.ts +629 -336
  35. package/bundles/widget.umd.min.js +1 -1
  36. package/docs/api-canvas3ds.md +41 -8
  37. package/docs/api-checkout-button.md +15 -0
  38. package/docs/api-ps-widget.md +15 -0
  39. package/docs/api-widget.md +15 -0
  40. package/docs/canvas3ds-examples.md +43 -1
  41. package/docs/widget-examples.md +41 -0
  42. package/package.json +20 -11
  43. package/slate.md +84 -1
@@ -29,6 +29,7 @@ Class Canvas3ds include method for working on html
29
29
  * [.load()](#Canvas3ds+load)
30
30
  * [.setEnv(env, [alias])](#Canvas3ds+setEnv)
31
31
  * [.on(eventName, cb)](#Canvas3ds+on) ⇒
32
+ * [.authenticate()](#Canvas3ds+authenticate)
32
33
  * [.hide([saveSize])](#Canvas3ds+hide)
33
34
  * [.show()](#Canvas3ds+show)
34
35
  * [.reload()](#Canvas3ds+reload)
@@ -98,6 +99,35 @@ widget.on('chargeAuthReject').then((data) => {
98
99
  console.log(data);
99
100
  });
100
101
  ```
102
+ <a name="Canvas3ds+authenticate" id="Canvas3ds+authenticate" href="#Canvas3ds+authenticate">&nbsp;</a>
103
+
104
+ ### canvas3ds.authenticate()
105
+ Two-step mode only (`_3ds.authentication_flow: 'two_step'`): runs the authentication the API deferred after the
106
+ fingerprint. Call it when the shopper confirms (e.g. presses "Pay"); the outcome arrives through the
107
+ usual events (`chargeAuthChallenge`, `chargeAuthSuccess`, `chargeAuthReject`, `error`).
108
+
109
+ Never throws. After `chargeAuthReady` the authentication starts at once; before it, the call is
110
+ remembered and runs as soon as the fingerprint completes. One-step charges, repeated calls and calls after
111
+ a terminal outcome are no-ops. For inbuilt 3DS (`html` / `url`) tokens it is a silent no-op: no event is
112
+ emitted at all, so do not wait for `error` there. Calling it before `load()` is an integration error: it emits `error` and
113
+ is not remembered. If `/process` could not be reached (`error` with `error_code` `service_unreachable` /
114
+ `aborted`), `authenticate()` may be called once more; any other `error`, and any failure of that retry,
115
+ is final.
116
+ No client-side deadline: a stale 3DS Server session surfaces as `error` / `chargeAuthReject`.
117
+
118
+ **Kind**: instance method of [<code>Canvas3ds</code>](#Canvas3ds)
119
+ **Example**
120
+ ```js
121
+ const widget = new Canvas3ds('#widget', token); // charge created with _3ds.authentication_flow: 'two_step'
122
+ widget.on('chargeAuthReady', () => payButton.disabled = false);
123
+ widget.on('chargeAuthChallenge', () => showChallengeModal());
124
+ widget.on('chargeAuthSuccess', (data) => completePayment(data.charge_3ds_id));
125
+ widget.on('chargeAuthReject', () => showDeclined());
126
+ widget.on('error', () => showError());
127
+ widget.load(); // fingerprint runs while the shopper reviews the order
128
+
129
+ payButton.addEventListener('click', () => widget.authenticate());
130
+ ```
101
131
  <a name="Canvas3ds+hide" id="Canvas3ds+hide" href="#Canvas3ds+hide">&nbsp;</a>
102
132
 
103
133
  ### canvas3ds.hide([saveSize])
@@ -154,12 +184,15 @@ List of available event's name for Standalone 3ds flow
154
184
 
155
185
  **Kind**: global constant
156
186
 
157
- | Param | Type | Default |
158
- | --- | --- | --- |
159
- | CHARGE_AUTH_SUCCESS | <code>string</code> | <code>&quot;chargeAuthSuccess&quot;</code> |
160
- | CHARGE_AUTH_REJECT | <code>string</code> | <code>&quot;chargeAuthReject&quot;</code> |
161
- | CHARGE_AUTH_DECOUPLED | <code>string</code> | <code>&quot;chargeAuthDecoupled&quot;</code> |
162
- | CHARGE_AUTH_CHALLENGE | <code>string</code> | <code>&quot;chargeAuthChallenge&quot;</code> |
163
- | CHARGE_AUTH_INFO | <code>string</code> | <code>&quot;chargeAuthInfo&quot;</code> |
164
- | ERROR | <code>string</code> | <code>&quot;error&quot;</code> |
187
+ | Param | Type | Default | Description |
188
+ | --- | --- | --- | --- |
189
+ | AUTH_SUCCESS | <code>string</code> | <code>&quot;chargeAuthSuccess&quot;</code> | |
190
+ | AUTH_ERROR | <code>string</code> | <code>&quot;chargeAuthReject&quot;</code> | |
191
+ | DECOUPLED | <code>string</code> | <code>&quot;chargeAuthDecoupled&quot;</code> | |
192
+ | CHALLENGE | <code>string</code> | <code>&quot;chargeAuthChallenge&quot;</code> | |
193
+ | READY | <code>string</code> | <code>&quot;chargeAuthReady&quot;</code> | two-step mode only: fingerprint done, waiting for `authenticate()` |
194
+ | CHALLENGE_LOADED | <code>string</code> | <code>&quot;chargeAuthChallengeLoaded&quot;</code> | challenge iframe painted (first `load`, or 2.5 s fallback); reveal your challenge UI here |
195
+ | CHALLENGE_COMPLETED | <code>string</code> | <code>&quot;chargeAuthChallengeCompleted&quot;</code> | the shopper finished the challenge (issuer result delivered to the 3DS Server); cover the challenge iframe with your own loader until the terminal event |
196
+ | INFO | <code>string</code> | <code>&quot;chargeAuthInfo&quot;</code> | |
197
+ | ERROR | <code>string</code> | <code>&quot;error&quot;</code> | |
165
198
 
@@ -23,6 +23,12 @@
23
23
  <dl>
24
24
  <dt><a href="#user-content-cb_CHECKOUT_BUTTON_EVENT">CHECKOUT_BUTTON_EVENT</a> : <code>object</code></dt>
25
25
  <dd></dd>
26
+ <dt><a href="#user-content-cb_CARD_SCHEME">CARD_SCHEME</a></dt>
27
+ <dd><p>Card schemes the widget is able to resolve from a card number, and therefore the only values
28
+ <code>setAllowedCardSchemes</code> accepts. This is a separate vocabulary from SUPPORTED_CARD_TYPES, which drives the
29
+ card icons: it omits the icon-only variants <code>visa_white</code> / <code>eftpos_white</code>, and the <code>laser</code> / <code>maestro</code>
30
+ identifiers the widget can never resolve — allowing those would be configuration that can never match.</p>
31
+ </dd>
26
32
  </dl>
27
33
 
28
34
  ## Typedefs
@@ -556,6 +562,15 @@ button.turnOffBackdrop();
556
562
  | FINISH | <code>string</code> | <code>&quot;finish&quot;</code> |
557
563
  | CLOSE | <code>string</code> | <code>&quot;close&quot;</code> |
558
564
 
565
+ <a name="cb_CARD_SCHEME" id="cb_CARD_SCHEME" href="#user-content-cb_CARD_SCHEME">&nbsp;</a>
566
+
567
+ ## CARD\_SCHEME
568
+ Card schemes the widget is able to resolve from a card number, and therefore the only values
569
+ `setAllowedCardSchemes` accepts. This is a separate vocabulary from SUPPORTED_CARD_TYPES, which drives the
570
+ card icons: it omits the icon-only variants `visa_white` / `eftpos_white`, and the `laser` / `maestro`
571
+ identifiers the widget can never resolve — allowing those would be configuration that can never match.
572
+
573
+ **Kind**: global constant
559
574
  <a name="cb_listener" id="cb_listener" href="#user-content-cb_listener">&nbsp;</a>
560
575
 
561
576
  ## listener : <code>function</code>
@@ -21,6 +21,12 @@
21
21
  <dt><a href="#user-content-psw_PAYMENT_SOURCE_TYPE">PAYMENT_SOURCE_TYPE</a> : <code>object</code></dt>
22
22
  <dd><p>List of available payment source types</p>
23
23
  </dd>
24
+ <dt><a href="#user-content-psw_CARD_SCHEME">CARD_SCHEME</a></dt>
25
+ <dd><p>Card schemes the widget is able to resolve from a card number, and therefore the only values
26
+ <code>setAllowedCardSchemes</code> accepts. This is a separate vocabulary from SUPPORTED_CARD_TYPES, which drives the
27
+ card icons: it omits the icon-only variants <code>visa_white</code> / <code>eftpos_white</code>, and the <code>laser</code> / <code>maestro</code>
28
+ identifiers the widget can never resolve — allowing those would be configuration that can never match.</p>
29
+ </dd>
24
30
  </dl>
25
31
 
26
32
  ## Typedefs
@@ -572,6 +578,15 @@ List of available payment source types
572
578
  | BANK_ACCOUNT | <code>string</code> | <code>&quot;bank_account&quot;</code> |
573
579
  | CHECKOUT | <code>string</code> | <code>&quot;checkout&quot;</code> |
574
580
 
581
+ <a name="psw_CARD_SCHEME" id="psw_CARD_SCHEME" href="#user-content-psw_CARD_SCHEME">&nbsp;</a>
582
+
583
+ ## CARD\_SCHEME
584
+ Card schemes the widget is able to resolve from a card number, and therefore the only values
585
+ `setAllowedCardSchemes` accepts. This is a separate vocabulary from SUPPORTED_CARD_TYPES, which drives the
586
+ card icons: it omits the icon-only variants `visa_white` / `eftpos_white`, and the `laser` / `maestro`
587
+ identifiers the widget can never resolve — allowing those would be configuration that can never match.
588
+
589
+ **Kind**: global constant
575
590
  <a name="psw_listener--PaymentSourceWidget" id="psw_listener--PaymentSourceWidget" href="#user-content-psw_listener--PaymentSourceWidget">&nbsp;</a>
576
591
 
577
592
  ## listener--PaymentSourceWidget : <code>function</code>
@@ -28,6 +28,12 @@
28
28
  <dt><a href="#user-content-w_GENERIC_VALIDATORS">GENERIC_VALIDATORS</a> : <code>Record.&lt;string, string&gt;</code></dt>
29
29
  <dd><p>List of available generic form field validators and their definition</p>
30
30
  </dd>
31
+ <dt><a href="#user-content-w_CARD_SCHEME">CARD_SCHEME</a></dt>
32
+ <dd><p>Card schemes the widget is able to resolve from a card number, and therefore the only values
33
+ <code>setAllowedCardSchemes</code> accepts. This is a separate vocabulary from SUPPORTED_CARD_TYPES, which drives the
34
+ card icons: it omits the icon-only variants <code>visa_white</code> / <code>eftpos_white</code>, and the <code>laser</code> / <code>maestro</code>
35
+ identifiers the widget can never resolve — allowing those would be configuration that can never match.</p>
36
+ </dd>
31
37
  <dt><a href="#user-content-w_TRIGGER">TRIGGER</a> : <code>object</code></dt>
32
38
  <dd><p>List of available triggers</p>
33
39
  </dd>
@@ -414,6 +420,15 @@ List of available generic form field validators and their definition
414
420
  | --- | --- | --- | --- |
415
421
  | REQUIRED | <code>string</code> | <code>&quot;required&quot;</code> | Asserts the input or form field has a value defined truthy value |
416
422
 
423
+ <a name="w_CARD_SCHEME" id="w_CARD_SCHEME" href="#user-content-w_CARD_SCHEME">&nbsp;</a>
424
+
425
+ ## CARD\_SCHEME
426
+ Card schemes the widget is able to resolve from a card number, and therefore the only values
427
+ `setAllowedCardSchemes` accepts. This is a separate vocabulary from SUPPORTED_CARD_TYPES, which drives the
428
+ card icons: it omits the icon-only variants `visa_white` / `eftpos_white`, and the `laser` / `maestro`
429
+ identifiers the widget can never resolve — allowing those would be configuration that can never match.
430
+
431
+ **Kind**: global constant
417
432
  <a name="w_TRIGGER" id="w_TRIGGER" href="#user-content-w_TRIGGER">&nbsp;</a>
418
433
 
419
434
  ## TRIGGER : <code>object</code>
@@ -181,7 +181,13 @@ After you initialized the standalone 3ds charge via `v1/charges/standalone-3ds`
181
181
  console.log(data);
182
182
  });
183
183
  canvas3ds.on("chargeAuthChallenge", function (data) {
184
- console.log(data);
184
+ console.log(data); // keep your loader up — the challenge page is still loading
185
+ });
186
+ canvas3ds.on("chargeAuthChallengeLoaded", function (data) {
187
+ console.log(data.reason); // 'load' | 'timeout' — hide your loader, the challenge is visible
188
+ });
189
+ canvas3ds.on("chargeAuthChallengeCompleted", function (data) {
190
+ console.log(data.source); // 'poll' | 'callback' — shopper finished, cover the challenge with "Completing verification"
185
191
  });
186
192
  canvas3ds.on("chargeAuthDecoupled", function (data) {
187
193
  console.log(data.result.description);
@@ -204,10 +210,26 @@ After you initialized the standalone 3ds charge via `v1/charges/standalone-3ds`
204
210
  - A `data.status` of `rejected` will be received when the authorization was rejected.
205
211
  - A `data.status` of `invalid_event` will be received for unhandled situations.
206
212
  - The `chargeAuthChallenge` event is sent before starting a challenge flow (i.e. opening an IFrame for the customer to complete a challenge with ther bank). Once the end customer performs the challenge, the Canvas3ds will be able to identify the challenge result and will either produce a `chargeAuthSuccess` or `chargeAuthReject` event.
213
+ - The `chargeAuthChallengeLoaded` event (SDK **v1.147.0+**) is sent once per challenge, after `chargeAuthChallenge`, when the challenge IFrame has finished loading the bank's page (its first `load` event) — this is the moment the customer can actually see the challenge. `data.reason` is `load` for a real load, or `timeout` when no load was observed within 2.5 seconds: the event is emitted anyway so you never hold a loading indicator forever (a `load` arriving later is simply ignored). Hide your loader on this event either way; `data.reason` is only informational (e.g. analytics). Not sent for decoupled challenges (no challenge IFrame).
214
+ - The `chargeAuthChallengeCompleted` event (SDK **v1.147.0+**) is sent once the customer has finished the challenge (or approved a decoupled authentication): the bank has delivered the final result to the 3DS server and the Canvas3ds is now obtaining the outcome. Whatever the bank or the 3DS server render inside the IFrame from here on is plumbing, not UX — cover the IFrame with your own "Completing verification" state and hold it until the terminal event. `data.source` tells you how the SDK learned about it: `poll` (the 3DS server's result monitor — the usual case, it flips before the IFrame has even left the bank's page) or `callback` (the callback message carrying the outcome arrived first; the terminal event follows immediately) — handle both the same way. Sent at most once per authentication, always before `chargeAuthSuccess` / `chargeAuthReject` / `error` and never after them. A wrong OTP or a further interactive step (e.g. a second OTP screen) does **not** send it — no result has been delivered yet — so no grace timer or reveal fallback is needed on your side. Also sent for decoupled challenges.
207
215
  - The `chargeAuthDecoupled` event is sent when the flow is a decoupled challenge, alongside a `data.result.description` field that you must show to the customer, indicating the method the user must use to authenticate. For example this may happen by having the cardholder authenticating directly with their banking app through biometrics. Once the end customer does this, the Canvas3ds will be able to recognize the challenge result is ready and will either produce a `chargeAuthSuccess` or `chargeAuthReject` event.
208
216
  - The `error` event is sent if an unexpected issue with the client library occurs. In such scenarios, you should consider the autentication process as interrupted:
209
217
  - When getting this event, you will get on `data.error` the full error object.
210
218
 
219
+ ### Recommended loading indicator pattern
220
+
221
+ > Requires SDK **v1.147.0 or later** (`chargeAuthChallengeLoaded` / `chargeAuthChallengeCompleted`). On older versions the same effect needs a `MutationObserver` on the container plus `load` listeners on the challenge IFrame — see the Standalone 3DS UX guide in the playground for that legacy approach.
222
+
223
+ The Canvas3ds renders nothing visible until a challenge is presented, and `chargeAuthChallenge` fires when the API responds — about 1–2 seconds before the bank's page actually paints inside the IFrame. Drive a single loading indicator from the events:
224
+
225
+ 1. Show the loader **before** you `POST /v1/charges/standalone-3ds` and keep it up through `canvas3ds.load()` (fingerprinting and processing are invisible).
226
+ 2. Keep it up on `chargeAuthChallenge` — the challenge IFrame is being injected but is still blank.
227
+ 3. Hide it on `chargeAuthChallengeLoaded` — the challenge is visible. `data.reason` tells you whether it was a real paint (`load`) or the 2.5 s safety fallback (`timeout`); either way, no further handling is needed.
228
+ 4. Cover the IFrame with a "Completing verification" indicator on `chargeAuthChallengeCompleted` — the customer finished the challenge and the SDK is obtaining the outcome — and hold it until the terminal event. No grace timer or reveal fallback is needed: a wrong OTP or a second interactive step does not fire this event.
229
+ 5. Finish on `chargeAuthSuccess` / `chargeAuthReject` / `error`.
230
+
231
+ Decoupled challenges (`chargeAuthDecoupled`) never emit `chargeAuthChallengeLoaded` (there is no challenge IFrame) but do emit `chargeAuthChallengeCompleted` once the customer approved out-of-band: show a dedicated waiting state with `data.result.description` until then, and "Completing verification" until the terminal event. Frictionless flows go straight from `load()` to the terminal event.
232
+
211
233
  ### Events and Values
212
234
 
213
235
  | Event Value | Type | Description |
@@ -215,6 +237,8 @@ After you initialized the standalone 3ds charge via `v1/charges/standalone-3ds`
215
237
  | <code>chargeAuthSuccess</code> | <code>object</code> | Instance of [ChargeEventResponse](#cb_chargeEventResponse) |
216
238
  | <code>chargeAuthReject</code> | <code>object</code> | Instance of [ChargeEventResponse](#cb_chargeEventResponse) |
217
239
  | <code>chargeAuthChallenge</code> | <code>object</code> | Instance of [ChargeEventResponse](#cb_chargeEventResponse) |
240
+ | <code>chargeAuthChallengeLoaded</code> | <code>object</code> | Instance of [ChallengeLoadedResponse](#cb_challengeLoadedResponse) |
241
+ | <code>chargeAuthChallengeCompleted</code> | <code>object</code> | Instance of [ChallengeCompletedResponse](#cb_challengeCompletedResponse) |
218
242
  | <code>chargeAuthDecoupled</code> | <code>object</code> | Instance of [ChargeEventResponse](#cb_chargeEventResponse) |
219
243
  | <code>chargeAuthInfo</code> | <code>object</code> | Instance of [ChargeEventResponse](#cb_chargeEventResponse) |
220
244
  | <code>error</code> | <code>object</code> | Instance of [chargeError](#cb_chargeError) |
@@ -232,6 +256,24 @@ After you initialized the standalone 3ds charge via `v1/charges/standalone-3ds`
232
256
  | <code>info</code> | <code>string</code> | info field for `chargeAuthInfo` event |
233
257
  | <code>result.description</code> | <code>string</code> [Optional] | field that you must show to the customer, indicating the method the user must use to authenticate during the decoupled challenge flow. |
234
258
 
259
+ <a name="cb_challengeLoadedResponse" id="cb_challengeLoadedResponse"></a>
260
+
261
+ ### ChallengeLoadedResponse
262
+
263
+ | Param | Type | Description |
264
+ | ------------------------------- | ------------------------------ | -------------------------------------------------------------------------------------------------- |
265
+ | <code>charge_3ds_id</code> | <code>string</code> | Universal unique transaction identifier to identify the transaction |
266
+ | <code>reason</code> | <code>string</code> | `load` — the challenge IFrame fired its first load event; `timeout` — no load within 2.5 s, emitted so a loader is never held forever (a later load is ignored) |
267
+
268
+ <a name="cb_challengeCompletedResponse" id="cb_challengeCompletedResponse"></a>
269
+
270
+ ### ChallengeCompletedResponse
271
+
272
+ | Param | Type | Description |
273
+ | ------------------------------- | ------------------------------ | -------------------------------------------------------------------------------------------------- |
274
+ | <code>charge_3ds_id</code> | <code>string</code> | Universal unique transaction identifier to identify the transaction |
275
+ | <code>source</code> | <code>string</code> | `poll` — the 3DS server's result monitor reported that the bank delivered the result (the usual case); `callback` — the callback message carrying the outcome arrived first (the terminal event follows immediately). Informational only — handle both the same way |
276
+
235
277
  ### ChargeError
236
278
 
237
279
  <a name="cb_chargeError" id="cb_chargeError"></a>
@@ -94,8 +94,48 @@ widget.setRefId('id'); // your unique identifier to identify the data
94
94
  widget.setFormFields(['phone', 'email']); // add additional fields for form of widget
95
95
 
96
96
  widget.setSupportedCardIcons(['mastercard', 'visa']); // add icons of supported card types
97
+
98
+ widget.setAllowedCardSchemes(['mastercard', 'visa']); // restrict which card schemes the card number field accepts
99
+ ```
100
+
101
+ ### Restricting accepted card schemes
102
+
103
+ `setAllowedCardSchemes(schemes)` restricts which card schemes the card number field accepts, **without** changing
104
+ which card icons the widget displays. Call it before `load()`.
105
+
106
+ ```javascript
107
+ widget.setAllowedCardSchemes(['visa', 'mastercard']);
97
108
  ```
98
109
 
110
+ Accepted identifiers — the schemes the widget can resolve from a card number:
111
+
112
+ `amex`, `ausbc`, `diners`, `discover`, `eftpos`, `japcb`, `mastercard`, `solo`, `unionpay`, `visa`
113
+
114
+ Behaviour:
115
+
116
+ - **Not called, or an empty array** — every scheme is accepted. Existing integrations are unaffected.
117
+ - **Case-insensitive**, and surrounding whitespace is ignored: `' Visa '` and `'MASTERCARD'` both work. Note that
118
+ `setSupportedCardIcons` matches case-**sensitively**; the two methods differ here.
119
+ - **Unsupported identifiers are ignored with a `console.warn`** naming the value, and never throw. A list in which
120
+ every identifier is unsupported therefore applies **no restriction at all** — check the console if a restriction
121
+ seems to have no effect. The icon-only values `visa_white` and `eftpos_white` are not schemes and are rejected,
122
+ as are `laser` and `maestro`, which the widget can never resolve from a card number.
123
+ - **Takes precedence over `setSupportedCardIcons`.** When a non-empty list is supplied here, it is the only list
124
+ used for validation, and the second argument of `setSupportedCardIcons` no longer affects validation. Icons keep
125
+ coming from `setSupportedCardIcons`, so a scheme can legitimately be shown as an icon and still be rejected —
126
+ make sure that is what you intend:
127
+
128
+ ```javascript
129
+ widget.setSupportedCardIcons(['visa', 'mastercard', 'amex'], true);
130
+ widget.setAllowedCardSchemes(['visa', 'mastercard']);
131
+ // three icons are displayed; an Amex card is rejected
132
+ ```
133
+
134
+ - **Calling it again replaces the previous list** rather than merging with it.
135
+ - A card of a rejected scheme shows `Card type not accepted.` on the card number field, the submit button is
136
+ disabled, and the submit handler refuses the card even if the button is re-enabled outside the widget. The same
137
+ message is shown for a card whose scheme cannot be resolved at all.
138
+
99
139
  This example shows how you can use a lot of other methods to settings your form
100
140
 
101
141
  ### Error handling
@@ -255,6 +295,7 @@ When handling errors, consider:
255
295
  var widget = new paydock.HtmlWidget('#widget', 'publicKey', 'gatewayId');
256
296
 
257
297
  widget.setSupportedCardIcons(['mastercard', 'visa']);
298
+ widget.setAllowedCardSchemes(['mastercard', 'visa']);
258
299
  widget.setFormFields(['phone', 'email']);
259
300
  widget.setRefId('custom-ref-id');
260
301
  widget.onFinishInsert('input[name="payment_source_token"]', 'payment_source');
package/package.json CHANGED
@@ -104,7 +104,7 @@
104
104
  }
105
105
  },
106
106
  "name": "@paydock/client-sdk",
107
- "version": "1.145.0-beta",
107
+ "version": "1.148.0-beta",
108
108
  "scripts": {
109
109
  "build:doc": "node docs/html/marked.js",
110
110
  "build:js": "rollup --config rollup.config.ts --configPlugin @rollup/plugin-typescript",
@@ -180,7 +180,6 @@
180
180
  "buffer": "5.7.1",
181
181
  "cross-fetch": "4.1.0",
182
182
  "dotenv": "16.4.5",
183
- "fs": "0.0.2",
184
183
  "grunt": "1.6.1",
185
184
  "grunt-cli": "1.4.3",
186
185
  "grunt-jsdoc-to-markdown": "6.0.0",
@@ -214,38 +213,48 @@
214
213
  "tsify": "3.0.4",
215
214
  "tslib": "2.6.2",
216
215
  "typescript": "5.4.5",
217
- "vitest": "3.2.6",
216
+ "vitest": "4.1.11",
218
217
  "zod": "3.23.8"
219
218
  },
220
219
  "overrides": {
221
220
  "sha.js@>=2.0.0 <=2.4.11": "2.4.12",
222
221
  "pbkdf2": ">=3.1.3",
223
222
  "form-data": ">=4.0.6",
224
- "qs": ">=6.15.2",
225
- "js-yaml": ">=4.2.0",
223
+ "qs": "6.16.0",
224
+ "js-yaml": "4.3.2",
225
+ "fflate@0.4": "0.4.9",
226
+ "fflate@0.7": "0.7.5",
226
227
  "@babel/plugin-transform-modules-systemjs": ">=7.29.4",
227
- "fast-uri": ">=3.1.2",
228
+ "fast-uri": "3.1.7",
228
229
  "tmp": ">=0.2.6",
229
230
  "ws": ">=8.20.1",
230
231
  "minimatch@^3": "3.1.5",
231
232
  "minimatch@^5": "5.1.9",
232
233
  "minimatch@^9": "9.0.9",
233
234
  "@isaacs/brace-expansion": ">=5.0.1",
235
+ "brace-expansion@1": "1.1.21",
236
+ "brace-expansion@2": "2.1.7",
237
+ "postcss": "8.5.23",
234
238
  "bn.js": ">=5.2.3",
235
239
  "ajv": "8.18.0",
236
240
  "serialize-javascript": ">=7.0.3",
237
241
  "underscore": ">=1.13.8",
238
- "dompurify": "3.4.11",
239
- "immutable": ">=5.1.5",
242
+ "dompurify": "3.4.13",
243
+ "immutable": "5.1.8",
240
244
  "flatted": ">=3.4.2",
241
245
  "node-forge": ">=1.4.0",
242
246
  "picomatch": ">=2.3.2",
243
- "socket.io-parser": ">=4.2.6",
247
+ "socket.io-parser": "4.2.7",
248
+ "engine.io": "6.6.11",
244
249
  "lodash": ">=4.18.0",
245
250
  "vite": ">=6.4.2 <7",
246
251
  "follow-redirects": ">=1.16.0",
247
- "shell-quote": ">=1.8.4",
248
- "esbuild": ">=0.28.1"
252
+ "shell-quote": "1.9.0",
253
+ "esbuild": ">=0.28.1",
254
+ "linkify-it": "5.0.2",
255
+ "body-parser": "1.20.6",
256
+ "nanoid": "3.3.18",
257
+ "markdown-it": "14.2.0"
249
258
  },
250
259
  "engines": {
251
260
  "node": ">=16.0.0"
package/slate.md CHANGED
@@ -194,8 +194,48 @@ widget.setRefId('id'); // your unique identifier to identify the data
194
194
  widget.setFormFields(['phone', 'email']); // add additional fields for form of widget
195
195
 
196
196
  widget.setSupportedCardIcons(['mastercard', 'visa']); // add icons of supported card types
197
+
198
+ widget.setAllowedCardSchemes(['mastercard', 'visa']); // restrict which card schemes the card number field accepts
199
+ ```
200
+
201
+ ### Restricting accepted card schemes
202
+
203
+ `setAllowedCardSchemes(schemes)` restricts which card schemes the card number field accepts, **without** changing
204
+ which card icons the widget displays. Call it before `load()`.
205
+
206
+ ```javascript
207
+ widget.setAllowedCardSchemes(['visa', 'mastercard']);
197
208
  ```
198
209
 
210
+ Accepted identifiers — the schemes the widget can resolve from a card number:
211
+
212
+ `amex`, `ausbc`, `diners`, `discover`, `eftpos`, `japcb`, `mastercard`, `solo`, `unionpay`, `visa`
213
+
214
+ Behaviour:
215
+
216
+ - **Not called, or an empty array** — every scheme is accepted. Existing integrations are unaffected.
217
+ - **Case-insensitive**, and surrounding whitespace is ignored: `' Visa '` and `'MASTERCARD'` both work. Note that
218
+ `setSupportedCardIcons` matches case-**sensitively**; the two methods differ here.
219
+ - **Unsupported identifiers are ignored with a `console.warn`** naming the value, and never throw. A list in which
220
+ every identifier is unsupported therefore applies **no restriction at all** — check the console if a restriction
221
+ seems to have no effect. The icon-only values `visa_white` and `eftpos_white` are not schemes and are rejected,
222
+ as are `laser` and `maestro`, which the widget can never resolve from a card number.
223
+ - **Takes precedence over `setSupportedCardIcons`.** When a non-empty list is supplied here, it is the only list
224
+ used for validation, and the second argument of `setSupportedCardIcons` no longer affects validation. Icons keep
225
+ coming from `setSupportedCardIcons`, so a scheme can legitimately be shown as an icon and still be rejected —
226
+ make sure that is what you intend:
227
+
228
+ ```javascript
229
+ widget.setSupportedCardIcons(['visa', 'mastercard', 'amex'], true);
230
+ widget.setAllowedCardSchemes(['visa', 'mastercard']);
231
+ // three icons are displayed; an Amex card is rejected
232
+ ```
233
+
234
+ - **Calling it again replaces the previous list** rather than merging with it.
235
+ - A card of a rejected scheme shows `Card type not accepted.` on the card number field, the submit button is
236
+ disabled, and the submit handler refuses the card even if the button is re-enabled outside the widget. The same
237
+ message is shown for a card whose scheme cannot be resolved at all.
238
+
199
239
  This example shows how you can use a lot of other methods to settings your form
200
240
 
201
241
  ### Error handling
@@ -355,6 +395,7 @@ When handling errors, consider:
355
395
  var widget = new paydock.HtmlWidget('#widget', 'publicKey', 'gatewayId');
356
396
 
357
397
  widget.setSupportedCardIcons(['mastercard', 'visa']);
398
+ widget.setAllowedCardSchemes(['mastercard', 'visa']);
358
399
  widget.setFormFields(['phone', 'email']);
359
400
  widget.setRefId('custom-ref-id');
360
401
  widget.onFinishInsert('input[name="payment_source_token"]', 'payment_source');
@@ -743,7 +784,13 @@ After you initialized the standalone 3ds charge via `v1/charges/standalone-3ds`
743
784
  console.log(data);
744
785
  });
745
786
  canvas3ds.on("chargeAuthChallenge", function (data) {
746
- console.log(data);
787
+ console.log(data); // keep your loader up — the challenge page is still loading
788
+ });
789
+ canvas3ds.on("chargeAuthChallengeLoaded", function (data) {
790
+ console.log(data.reason); // 'load' | 'timeout' — hide your loader, the challenge is visible
791
+ });
792
+ canvas3ds.on("chargeAuthChallengeCompleted", function (data) {
793
+ console.log(data.source); // 'poll' | 'callback' — shopper finished, cover the challenge with "Completing verification"
747
794
  });
748
795
  canvas3ds.on("chargeAuthDecoupled", function (data) {
749
796
  console.log(data.result.description);
@@ -766,10 +813,26 @@ After you initialized the standalone 3ds charge via `v1/charges/standalone-3ds`
766
813
  - A `data.status` of `rejected` will be received when the authorization was rejected.
767
814
  - A `data.status` of `invalid_event` will be received for unhandled situations.
768
815
  - The `chargeAuthChallenge` event is sent before starting a challenge flow (i.e. opening an IFrame for the customer to complete a challenge with ther bank). Once the end customer performs the challenge, the Canvas3ds will be able to identify the challenge result and will either produce a `chargeAuthSuccess` or `chargeAuthReject` event.
816
+ - The `chargeAuthChallengeLoaded` event (SDK **v1.147.0+**) is sent once per challenge, after `chargeAuthChallenge`, when the challenge IFrame has finished loading the bank's page (its first `load` event) — this is the moment the customer can actually see the challenge. `data.reason` is `load` for a real load, or `timeout` when no load was observed within 2.5 seconds: the event is emitted anyway so you never hold a loading indicator forever (a `load` arriving later is simply ignored). Hide your loader on this event either way; `data.reason` is only informational (e.g. analytics). Not sent for decoupled challenges (no challenge IFrame).
817
+ - The `chargeAuthChallengeCompleted` event (SDK **v1.147.0+**) is sent once the customer has finished the challenge (or approved a decoupled authentication): the bank has delivered the final result to the 3DS server and the Canvas3ds is now obtaining the outcome. Whatever the bank or the 3DS server render inside the IFrame from here on is plumbing, not UX — cover the IFrame with your own "Completing verification" state and hold it until the terminal event. `data.source` tells you how the SDK learned about it: `poll` (the 3DS server's result monitor — the usual case, it flips before the IFrame has even left the bank's page) or `callback` (the callback message carrying the outcome arrived first; the terminal event follows immediately) — handle both the same way. Sent at most once per authentication, always before `chargeAuthSuccess` / `chargeAuthReject` / `error` and never after them. A wrong OTP or a further interactive step (e.g. a second OTP screen) does **not** send it — no result has been delivered yet — so no grace timer or reveal fallback is needed on your side. Also sent for decoupled challenges.
769
818
  - The `chargeAuthDecoupled` event is sent when the flow is a decoupled challenge, alongside a `data.result.description` field that you must show to the customer, indicating the method the user must use to authenticate. For example this may happen by having the cardholder authenticating directly with their banking app through biometrics. Once the end customer does this, the Canvas3ds will be able to recognize the challenge result is ready and will either produce a `chargeAuthSuccess` or `chargeAuthReject` event.
770
819
  - The `error` event is sent if an unexpected issue with the client library occurs. In such scenarios, you should consider the autentication process as interrupted:
771
820
  - When getting this event, you will get on `data.error` the full error object.
772
821
 
822
+ ### Recommended loading indicator pattern
823
+
824
+ > Requires SDK **v1.147.0 or later** (`chargeAuthChallengeLoaded` / `chargeAuthChallengeCompleted`). On older versions the same effect needs a `MutationObserver` on the container plus `load` listeners on the challenge IFrame — see the Standalone 3DS UX guide in the playground for that legacy approach.
825
+
826
+ The Canvas3ds renders nothing visible until a challenge is presented, and `chargeAuthChallenge` fires when the API responds — about 1–2 seconds before the bank's page actually paints inside the IFrame. Drive a single loading indicator from the events:
827
+
828
+ 1. Show the loader **before** you `POST /v1/charges/standalone-3ds` and keep it up through `canvas3ds.load()` (fingerprinting and processing are invisible).
829
+ 2. Keep it up on `chargeAuthChallenge` — the challenge IFrame is being injected but is still blank.
830
+ 3. Hide it on `chargeAuthChallengeLoaded` — the challenge is visible. `data.reason` tells you whether it was a real paint (`load`) or the 2.5 s safety fallback (`timeout`); either way, no further handling is needed.
831
+ 4. Cover the IFrame with a "Completing verification" indicator on `chargeAuthChallengeCompleted` — the customer finished the challenge and the SDK is obtaining the outcome — and hold it until the terminal event. No grace timer or reveal fallback is needed: a wrong OTP or a second interactive step does not fire this event.
832
+ 5. Finish on `chargeAuthSuccess` / `chargeAuthReject` / `error`.
833
+
834
+ Decoupled challenges (`chargeAuthDecoupled`) never emit `chargeAuthChallengeLoaded` (there is no challenge IFrame) but do emit `chargeAuthChallengeCompleted` once the customer approved out-of-band: show a dedicated waiting state with `data.result.description` until then, and "Completing verification" until the terminal event. Frictionless flows go straight from `load()` to the terminal event.
835
+
773
836
  ### Events and Values
774
837
 
775
838
  | Event Value | Type | Description |
@@ -777,6 +840,8 @@ After you initialized the standalone 3ds charge via `v1/charges/standalone-3ds`
777
840
  | <code>chargeAuthSuccess</code> | <code>object</code> | Instance of [ChargeEventResponse](#cb_chargeEventResponse) |
778
841
  | <code>chargeAuthReject</code> | <code>object</code> | Instance of [ChargeEventResponse](#cb_chargeEventResponse) |
779
842
  | <code>chargeAuthChallenge</code> | <code>object</code> | Instance of [ChargeEventResponse](#cb_chargeEventResponse) |
843
+ | <code>chargeAuthChallengeLoaded</code> | <code>object</code> | Instance of [ChallengeLoadedResponse](#cb_challengeLoadedResponse) |
844
+ | <code>chargeAuthChallengeCompleted</code> | <code>object</code> | Instance of [ChallengeCompletedResponse](#cb_challengeCompletedResponse) |
780
845
  | <code>chargeAuthDecoupled</code> | <code>object</code> | Instance of [ChargeEventResponse](#cb_chargeEventResponse) |
781
846
  | <code>chargeAuthInfo</code> | <code>object</code> | Instance of [ChargeEventResponse](#cb_chargeEventResponse) |
782
847
  | <code>error</code> | <code>object</code> | Instance of [chargeError](#cb_chargeError) |
@@ -794,6 +859,24 @@ After you initialized the standalone 3ds charge via `v1/charges/standalone-3ds`
794
859
  | <code>info</code> | <code>string</code> | info field for `chargeAuthInfo` event |
795
860
  | <code>result.description</code> | <code>string</code> [Optional] | field that you must show to the customer, indicating the method the user must use to authenticate during the decoupled challenge flow. |
796
861
 
862
+ <a name="cb_challengeLoadedResponse" id="cb_challengeLoadedResponse"></a>
863
+
864
+ ### ChallengeLoadedResponse
865
+
866
+ | Param | Type | Description |
867
+ | ------------------------------- | ------------------------------ | -------------------------------------------------------------------------------------------------- |
868
+ | <code>charge_3ds_id</code> | <code>string</code> | Universal unique transaction identifier to identify the transaction |
869
+ | <code>reason</code> | <code>string</code> | `load` — the challenge IFrame fired its first load event; `timeout` — no load within 2.5 s, emitted so a loader is never held forever (a later load is ignored) |
870
+
871
+ <a name="cb_challengeCompletedResponse" id="cb_challengeCompletedResponse"></a>
872
+
873
+ ### ChallengeCompletedResponse
874
+
875
+ | Param | Type | Description |
876
+ | ------------------------------- | ------------------------------ | -------------------------------------------------------------------------------------------------- |
877
+ | <code>charge_3ds_id</code> | <code>string</code> | Universal unique transaction identifier to identify the transaction |
878
+ | <code>source</code> | <code>string</code> | `poll` — the 3DS server's result monitor reported that the bank delivered the result (the usual case); `callback` — the callback message carrying the outcome arrived first (the terminal event follows immediately). Informational only — handle both the same way |
879
+
797
880
  ### ChargeError
798
881
 
799
882
  <a name="cb_chargeError" id="cb_chargeError"></a>