@wirexapp/card-sdk-core 1.1.0-beta.3 → 1.1.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/CHANGELOG.md +260 -0
- package/dist/collectElement.d.ts +48 -2
- package/dist/collectElement.d.ts.map +1 -1
- package/dist/collectElement.js +20 -0
- package/dist/collectElement.js.map +1 -1
- package/dist/collectGroup.d.ts +24 -4
- package/dist/collectGroup.d.ts.map +1 -1
- package/dist/collectGroup.js +43 -2
- package/dist/collectGroup.js.map +1 -1
- package/dist/collectMask.d.ts +24 -0
- package/dist/collectMask.d.ts.map +1 -0
- package/dist/collectMask.js +42 -0
- package/dist/collectMask.js.map +1 -0
- package/dist/frameChannel.d.ts +17 -1
- package/dist/frameChannel.d.ts.map +1 -1
- package/dist/frameChannel.js +40 -4
- package/dist/frameChannel.js.map +1 -1
- package/dist/frameUrl.d.ts +4 -2
- package/dist/frameUrl.d.ts.map +1 -1
- package/dist/frameUrl.js.map +1 -1
- package/dist/index.d.ts +6 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/protocol.d.ts +403 -19
- package/dist/protocol.d.ts.map +1 -1
- package/dist/walletLaunch.d.ts +44 -0
- package/dist/walletLaunch.d.ts.map +1 -0
- package/dist/walletLaunch.js +45 -0
- package/dist/walletLaunch.js.map +1 -0
- package/dist/walletReturnUrl.d.ts +30 -0
- package/dist/walletReturnUrl.d.ts.map +1 -0
- package/dist/walletReturnUrl.js +63 -0
- package/dist/walletReturnUrl.js.map +1 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,265 @@
|
|
|
1
1
|
# @wirexapp/card-sdk-core
|
|
2
2
|
|
|
3
|
+
## 1.1.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 8097859: CVV entry masking — show / hide what was typed into the top-up CVV field. The
|
|
8
|
+
CVV has always been masked as it is typed (a password input); this adds the
|
|
9
|
+
reveal element's masking model to that input, for both collect variants
|
|
10
|
+
(`createCardTopUpFields`, `createCardTopUpForm`). The card number is not
|
|
11
|
+
affected, and with no option passed nothing changes.
|
|
12
|
+
|
|
13
|
+
- `masked` (boolean, default `true`) — the state at mount; `false` starts the
|
|
14
|
+
field shown.
|
|
15
|
+
- `maskToggle: 'button' | 'text' | 'none'` (default `'none'`) — an in-frame
|
|
16
|
+
control at the field's right edge: the reveal's eye / eye-off icon, or a
|
|
17
|
+
text button from the new collect strings `show` / `hide`. It is the same
|
|
18
|
+
control the reveal rows draw (one module in the frames), themed by the same
|
|
19
|
+
`copyIconSize` / `colorCopyIcon` / `colorCopyIconHover` tokens. No `'row'`
|
|
20
|
+
mode: a tap on an input has to focus it. The toggle never takes the focus
|
|
21
|
+
from the field; the value and the caret stay.
|
|
22
|
+
- `maskAfter` (ms, default never) — shown digits re-mask from the frame,
|
|
23
|
+
counted from the moment they are visible.
|
|
24
|
+
- `setMasked(boolean)` on `PciCollectElement` and `PciCollectGroup` (the group
|
|
25
|
+
forwards to its `cvv` field) — host control in place, safe before `mount()`
|
|
26
|
+
resolves and after `destroy()`. Host→frame event `collect-set-masked`, sent
|
|
27
|
+
with the reply-less `PciFrameChannel.post()`.
|
|
28
|
+
- `onMaskedChange({ masked, reason })` — the state the frame applied, once at
|
|
29
|
+
mount (`'init'`) and after every change (`'user'`, `'host'`, `'timeout'`).
|
|
30
|
+
Frame→host event `collect-masked-changed`: a boolean and a reason, never
|
|
31
|
+
the value.
|
|
32
|
+
- `PciCollectGroup.field(name, options?)` takes the same masking options for
|
|
33
|
+
the `cvv` field (they win over the group config's).
|
|
34
|
+
- React: `CardCvvField` takes `masked` (live — forwarded in place, never a
|
|
35
|
+
remount), `maskToggle`, `maskAfter` and `onMaskedChange`. Mirroring
|
|
36
|
+
`onMaskedChange` into the state `masked` is rendered from is safe — the rule
|
|
37
|
+
is "when in doubt, mask": a hide is always sent, a show is not sent when it
|
|
38
|
+
is the echo of the frame's own "shown" report.
|
|
39
|
+
|
|
40
|
+
No reveal gate (`onRevealRequested`) on the input — the digits are the user's
|
|
41
|
+
own typing — and no `maskedPlaceholder`: the masked rendering is the browser's
|
|
42
|
+
password bullet. Microsoft Edge's built-in reveal button is now hidden in the
|
|
43
|
+
CVV field (it showed the digits behind the frame's state); the SDK's toggle
|
|
44
|
+
and `setMasked()` are the only ways to show them, in every browser alike.
|
|
45
|
+
The OCT package's surface is unchanged (its profile has no CVV). Additive
|
|
46
|
+
within protocol v1: frames that predate this ignore the three options and
|
|
47
|
+
`collect-set-masked`, keep the CVV masked and never send
|
|
48
|
+
`collect-masked-changed`; ship the frames first.
|
|
49
|
+
|
|
50
|
+
- 901cddf: Collect failures. `PciFrameRequestError` gains `code`,
|
|
51
|
+
`retryable` and `reason`: the mobile SDK's failure payload
|
|
52
|
+
(`WirexCollectErrorPayload`), with the same values classified by the same rules.
|
|
53
|
+
|
|
54
|
+
- `code` is one of a frozen set: `unauthorized`, `actionRequired`,
|
|
55
|
+
`updateRequired`, `invalidInput`, `temporary`, `failed`. It is typed as the
|
|
56
|
+
`PciFrameErrorCode` union, so a `switch` over it checks exhaustively.
|
|
57
|
+
- `retryable` says whether sending the request again is safe.
|
|
58
|
+
- `reason` is present where one `code` has two remedies: `invalidInput` with
|
|
59
|
+
`cardAlreadyLinked` (enter another card) or `cardLimitReached` (remove one
|
|
60
|
+
first). The set grows; handle an unknown value.
|
|
61
|
+
|
|
62
|
+
The frame classifies a tokenize failure by the backend's business code on a 400
|
|
63
|
+
(`0243`, `0366`), otherwise by the HTTP status. A request without an answer is
|
|
64
|
+
`temporary`; a `Validation failed — …` reply is `invalidInput`. Messages are
|
|
65
|
+
unchanged. Older frames send none of the fields (`code` and `reason` undefined,
|
|
66
|
+
`retryable` false): ship the frames first.
|
|
67
|
+
|
|
68
|
+
- cc46627: Protocol (additive within v1): every frame init config accepts an optional `diagnostics.userId` — an opaque, host-chosen correlation id the frames attach as `user.id` to the error events they report to Wirex (`PciDiagnosticsInit`, docs/ERROR-TRACKING.md §5, §7). Validated in-frame; never an email or wallet address. The wallet documents honour it from this release; the host-side packages do not send it yet (Phase 3).
|
|
69
|
+
- 390c416: New `fontSmoothing` appearance token — `'auto'` (default, as today) or
|
|
70
|
+
`'antialiased'`: the frame document gets `-webkit-font-smoothing:
|
|
71
|
+
antialiased` and `-moz-osx-font-smoothing: grayscale`. Host pages whose
|
|
72
|
+
global CSS antialiases the body (Mantine, Tailwind preflight, most design
|
|
73
|
+
systems) otherwise sit next to frame text that macOS renders visibly heavier
|
|
74
|
+
at the same family, size and weight. Applies to the collect, reveal and
|
|
75
|
+
wallet frames; enumerated and re-validated in the frame, and an older frame
|
|
76
|
+
drops the token and keeps `auto`.
|
|
77
|
+
- b43affc: Wallet availability tells WHY Google Pay is unavailable: `PciWalletAvailability`
|
|
78
|
+
gains `googlePayState` — `ready` / `handoff` / `needs_card` / `unavailable`.
|
|
79
|
+
`needs_card` is an Android device where Google Pay works but no card in Google
|
|
80
|
+
Wallet on the phone qualifies for the cryptogram-only top-up (the probe's
|
|
81
|
+
cryptogram check fails, a `PAN_ONLY` capability check passes, and the WebView
|
|
82
|
+
is at or above the config's `min_webview_version`). A host may keep the wallet
|
|
83
|
+
row and send the user to Google Wallet instead of checkout.
|
|
84
|
+
|
|
85
|
+
Additive: `googlePay` keeps meaning "can pay here", so hosts that ignore the
|
|
86
|
+
new field behave as before. `checkWalletAvailability` persists the field and
|
|
87
|
+
derives it from the booleans when an older frames deployment omits it.
|
|
88
|
+
|
|
89
|
+
- d4654e9: Reveal `copy` option — `'button'` (default: the icon button, as today),
|
|
90
|
+
`'row'` (the whole row is one copy control: a real `<button>`, no icon, no
|
|
91
|
+
reserved width; the copied state shows on the value and is announced to
|
|
92
|
+
assistive tech) or `'none'` (no copy affordance; the value stays selectable
|
|
93
|
+
text). Copy still runs inside the frame and `onCopied` still carries the
|
|
94
|
+
field name only. Enumerated and re-validated in the frame; an older frame
|
|
95
|
+
ignores the option and renders the icon button.
|
|
96
|
+
|
|
97
|
+
Reveal `showLabels` option (default `true`). `false` renders no label
|
|
98
|
+
element at all — not an empty one — so a row is exactly as tall as its
|
|
99
|
+
value; the label string becomes the value's `aria-label`, and the icon copy
|
|
100
|
+
button's name carries the field ("Card Number: Copy"). With `copy:
|
|
101
|
+
'button'` such a row is floored at the icon's hit target (`copyIconSize` +
|
|
102
|
+
12px) so adjacent buttons never overlap. Replaces the `fontSizeLabel: '0px'`
|
|
103
|
+
/ `lineHeightLabel: '0px'` workaround.
|
|
104
|
+
|
|
105
|
+
Reveal `rowStyle` option — `'plain'` (default, as today) or `'boxed'`: each
|
|
106
|
+
row becomes a pill drawn from the box tokens the collect inputs already use
|
|
107
|
+
(`colorBackground`, `colorBorder`, `borderRadius`, `borderWidth`,
|
|
108
|
+
`controlMinHeight`; 16px horizontal padding, `spacingUnit` vertical,
|
|
109
|
+
`fieldGap` between pills), so a border hugs the value instead of the
|
|
110
|
+
iframe. Deliberately opt-in: those tokens already reach reveal frames from
|
|
111
|
+
hosts that share one appearance object with collect, so honouring them on
|
|
112
|
+
presence would have restyled existing reveal rows.
|
|
113
|
+
|
|
114
|
+
Reveal `layout` option — `'column'` (default) or `'row'`: the frame's
|
|
115
|
+
fields sit side by side with equal widths, separated by the new `fieldGap`
|
|
116
|
+
appearance token (length; default `12px`; also the gap between `'boxed'`
|
|
117
|
+
pills). `fields: ['expiry', 'cvv']` with `layout: 'row'` is one frame and
|
|
118
|
+
one `/details` call where two single-field frames called it twice with the
|
|
119
|
+
same action token. Frame-wide on purpose: a mixed arrangement is two
|
|
120
|
+
frames, because a nested `fields` shape would not be ignorable by older
|
|
121
|
+
frames.
|
|
122
|
+
|
|
123
|
+
New `wordSpacing` appearance token (length grammar): extra space between
|
|
124
|
+
the card number's four-digit groups in the reveal frame — the value is
|
|
125
|
+
still chunked with single spaces, and no other row is affected. Reveal
|
|
126
|
+
`fields` entries are now validated against the known row names (unknown
|
|
127
|
+
entries are dropped instead of rendering an `undefined` label).
|
|
128
|
+
|
|
129
|
+
- 417ab29: Reveal masking — show/hide each sensitive value in place (WPAY-5064). The
|
|
130
|
+
masked state is per row (number, expiry, CVV, PIN; never the cardholder) and
|
|
131
|
+
lives in the frame; while masked the value TEXT is replaced (card number:
|
|
132
|
+
one glyph per digit except the last four; expiry `••/••`; CVV / PIN fully),
|
|
133
|
+
with the same character count so the row keeps its height. The values are
|
|
134
|
+
still fetched once at mount under the step-up and the copy control still
|
|
135
|
+
copies the real value — masking is a display aid, not a second
|
|
136
|
+
authorisation. Two things change the state:
|
|
137
|
+
|
|
138
|
+
- the USER, through an opt-in in-frame control: `maskToggle` (`'button'` =
|
|
139
|
+
eye / eye-off icon, themed by the `copyIcon*` tokens; `'text'` = a text
|
|
140
|
+
button from the new `show` / `hide` strings, sized to the wider word;
|
|
141
|
+
`'row'` = the value itself; default `'none'`), with `maskToggleByField`
|
|
142
|
+
overrides;
|
|
143
|
+
- the HOST, through `masked` at mount (`true`, or a per-row map such as
|
|
144
|
+
`{ cvv: true, pin: true }`) and `PciRevealElement.setMasked(boolean | map)`
|
|
145
|
+
later — no refetch, no new `actionToken` (host→frame event
|
|
146
|
+
`reveal-set-masked`, sent with the new reply-less `PciFrameChannel.post()`).
|
|
147
|
+
|
|
148
|
+
`onMaskedChange` / `reveal-masked-changed` reports the state of every
|
|
149
|
+
rendered maskable row (never the cardholder) after every change with
|
|
150
|
+
`{ masked, field?, reason }` (`'init'`,
|
|
151
|
+
`'user'`, `'host'`, `'timeout'`). `maskAfter` (ms, a number or a per-row map;
|
|
152
|
+
default never) re-masks a shown row from the frame; `onRevealRequested`
|
|
153
|
+
gates revealing: the toggle then only asks (`reveal-requested { field }`)
|
|
154
|
+
and the host reveals with `setMasked({ [field]: false })`. React: the
|
|
155
|
+
`masked` prop of `CardReveal` is live (forwarded in place, a map compared by
|
|
156
|
+
value); `onMaskedChange` and `onRevealRequested` are props. New string
|
|
157
|
+
`maskedPlaceholder` (the glyph; default `•`). Older frames ignore every
|
|
158
|
+
masking option and show the values: ship the frames first.
|
|
159
|
+
|
|
160
|
+
Reveal copy control — `copy: 'text'` renders the `copy` / `copied` strings
|
|
161
|
+
as a text button (the same shape set as `maskToggle`), and `copyByField`
|
|
162
|
+
overrides `copy` per row (`{ pin: 'none' }` keeps the icon button everywhere
|
|
163
|
+
but the PIN). Each entry is validated in the frame; an invalid entry leaves
|
|
164
|
+
that row on `copy`; older frames ignore the map. `copy: 'none'` keeps its
|
|
165
|
+
meaning (no control, the value stays selectable text).
|
|
166
|
+
|
|
167
|
+
Frames: the reveal presentation options are now resolved in one pure module
|
|
168
|
+
(`revealOptions.ts`) with unit tests; `fields` that is not an array is
|
|
169
|
+
ignored instead of thrown on; a row's side controls (toggle, copy) are
|
|
170
|
+
siblings of the cell, so `copy: 'row'` and a toggle button combine.
|
|
171
|
+
|
|
172
|
+
- 1d89a12: Wallet polish: `checkWalletAvailability` gains a persistent
|
|
173
|
+
stale-while-revalidate `cache` option (Storage-shaped adapter,
|
|
174
|
+
localStorage by default, `null` to disable); the checkout popup centers
|
|
175
|
+
over the opener window; the payment-sheet's billing contact, cardholder
|
|
176
|
+
name and Apple transaction id ride the token payload as optional fields
|
|
177
|
+
(collected only when the provider config requests billing).
|
|
178
|
+
- 5f67be4: Wallet checkout polish: loading skeleton + popup page padding; the popup
|
|
179
|
+
carries `appearance` in the URL fragment so the pre-quote skeleton renders
|
|
180
|
+
themed; privacy-only legal line with a partner-configurable
|
|
181
|
+
`branding.privacyUrl` (https-only, validated in-frame, Wirex policy
|
|
182
|
+
fallback); Google Pay button draws the design pill radius natively; fee row
|
|
183
|
+
rounds up to the minor unit (host-app parity).
|
|
184
|
+
- 33b4595: New wallet frame mode `wallet-preview` (`PciWalletPreviewInitConfig`): Google's
|
|
185
|
+
own Google Pay sheet with no price (`NOT_CURRENTLY_KNOWN`), for the probe's
|
|
186
|
+
`needs_card` — a phone where Google Pay works but Google Wallet holds no card
|
|
187
|
+
the top-up accepts. No quote and no backend call; the sheet's result is dropped
|
|
188
|
+
in the frame and the host hears only how it closed (`wallet-preview-closed`,
|
|
189
|
+
`PciWalletPreviewOutcome`) and, with `autoOpen`, whether it opened without a tap
|
|
190
|
+
(`wallet-preview-auto`). `PciFrameDocument` gains `wallet-preview`. Additive.
|
|
191
|
+
- 731da3e: Wallet checkout TOKEN MODE (WPAY-4431): pass the backend's short-lived top-up
|
|
192
|
+
token as `auth.checkoutToken` and the hosted checkout executes the top-up
|
|
193
|
+
itself, reporting `onExecuted` / `onExecuteFailed` (`wallet-executed` /
|
|
194
|
+
`wallet-execute-failed` on the wire) — the wallet payment token never leaves
|
|
195
|
+
the frame. Legacy tokenize-and-return (`onToken`) is unchanged without `auth`.
|
|
196
|
+
|
|
197
|
+
Token mode also brings Google Pay to desktop (`googlePayHandoff: 'qr' | 'none'`,
|
|
198
|
+
probe flag `googlePayHandoff`): the frame draws a QR that carries the checkout
|
|
199
|
+
itself — the token, the quote and the estimate's `provider` name (new init /
|
|
200
|
+
`arm()` / `update()` field) — and an Android phone opens the same checkout
|
|
201
|
+
top-level, pays and executes. Nothing is stored on the frames service and the
|
|
202
|
+
desktop never learns the outcome: hosts reconcile by their own status reads
|
|
203
|
+
after `wallet-state: handoff-pending`. `arm(quote, config, auth?, provider?)` /
|
|
204
|
+
`update(quote, config?, auth?, provider?)`; `wallet-update` carries `auth` and
|
|
205
|
+
`provider`; new `wallet-state` values `executing`, `handoff-pending`.
|
|
206
|
+
|
|
207
|
+
`buildWalletLaunchFragment` / `buildWalletLaunchUrl` (core) build the same
|
|
208
|
+
compact fragment for a Chrome Custom Tab launch (`#t=&e=&s=&p=[&r=]`); the page
|
|
209
|
+
resolves the provider block from the public BaaS config by name.
|
|
210
|
+
|
|
211
|
+
### Patch Changes
|
|
212
|
+
|
|
213
|
+
- 274c69f: Fixed: the frames rendered as solid white rectangles in host pages that declare
|
|
214
|
+
`<meta name="color-scheme" content="dark">` (or `light dark` with dark
|
|
215
|
+
preferred) — typical of hybrid-app shells and dark-themed sites. The SDK set
|
|
216
|
+
`color-scheme: normal` on the iframe element; Chromium resolves `normal` from
|
|
217
|
+
the host's meta, so under a dark host the element became dark while the frame
|
|
218
|
+
document is light, and on that mismatch the browser paints the frame's canvas
|
|
219
|
+
opaque (CSS Color Adjust). The iframe element is now explicitly
|
|
220
|
+
`color-scheme: light`, matching the frame documents, which now declare
|
|
221
|
+
`:root { color-scheme: light }` as well — the frames stay transparent under any
|
|
222
|
+
host scheme. Hosts that set their dark theme through CSS alone were never
|
|
223
|
+
affected. The SDK change alone removes the mismatch for an updated host; the
|
|
224
|
+
frame-document change makes the pair explicit and independent of any meta.
|
|
225
|
+
- a629a98: Wallet checkout return link: `returnUrl` (`buildWalletLaunchUrl` → `r`) may
|
|
226
|
+
now be your app's own scheme (`com.partner.app:/wallet/return`) as well as an
|
|
227
|
+
`https` App Link / Universal Link. Only the schemes a browser would execute in
|
|
228
|
+
the checkout's own origin (`javascript:`, `data:`, `blob:`, …),
|
|
229
|
+
browser-internal ones and cleartext `http:` are refused —
|
|
230
|
+
`buildWalletLaunchFragment` throws a `TypeError` on those, and the frame
|
|
231
|
+
ignores them. New exports `resolveWalletReturnUrl` and
|
|
232
|
+
`PCI_WALLET_RETURN_URL_BLOCKED_SCHEMES`.
|
|
233
|
+
|
|
234
|
+
The frames (service) must be deployed for an app scheme to be offered; an SDK
|
|
235
|
+
built before this release already passes any `returnUrl` through, so the
|
|
236
|
+
frames deployment alone enables the change — this release only adds the
|
|
237
|
+
early validation and the documentation.
|
|
238
|
+
|
|
239
|
+
## 1.1.0-beta.4
|
|
240
|
+
|
|
241
|
+
### Minor Changes
|
|
242
|
+
|
|
243
|
+
- 731da3e: Wallet checkout TOKEN MODE (WPAY-4431): pass the backend's short-lived top-up
|
|
244
|
+
token as `auth.checkoutToken` and the hosted checkout executes the top-up
|
|
245
|
+
itself, reporting `onExecuted` / `onExecuteFailed` (`wallet-executed` /
|
|
246
|
+
`wallet-execute-failed` on the wire) — the wallet payment token never leaves
|
|
247
|
+
the frame. Legacy tokenize-and-return (`onToken`) is unchanged without `auth`.
|
|
248
|
+
|
|
249
|
+
Token mode also brings Google Pay to desktop (`googlePayHandoff: 'qr' | 'none'`,
|
|
250
|
+
probe flag `googlePayHandoff`): the frame draws a QR that carries the checkout
|
|
251
|
+
itself — the token, the quote and the estimate's `provider` name (new init /
|
|
252
|
+
`arm()` / `update()` field) — and an Android phone opens the same checkout
|
|
253
|
+
top-level, pays and executes. Nothing is stored on the frames service and the
|
|
254
|
+
desktop never learns the outcome: hosts reconcile by their own status reads
|
|
255
|
+
after `wallet-state: handoff-pending`. `arm(quote, config, auth?, provider?)` /
|
|
256
|
+
`update(quote, config?, auth?, provider?)`; `wallet-update` carries `auth` and
|
|
257
|
+
`provider`; new `wallet-state` values `executing`, `handoff-pending`.
|
|
258
|
+
|
|
259
|
+
`buildWalletLaunchFragment` / `buildWalletLaunchUrl` (core) build the same
|
|
260
|
+
compact fragment for a Chrome Custom Tab launch (`#t=&e=&s=&p=[&r=]`); the page
|
|
261
|
+
resolves the provider block from the public BaaS config by name.
|
|
262
|
+
|
|
3
263
|
## 1.1.0-beta.3
|
|
4
264
|
|
|
5
265
|
### Minor Changes
|
package/dist/collectElement.d.ts
CHANGED
|
@@ -1,11 +1,48 @@
|
|
|
1
|
-
import type { PciAppearance, PciCollectChangeState, PciCollectProfile, PciFrameStrings, PciTokenizeOctResult, PciTokenizeTopUpResult } from './protocol.js';
|
|
1
|
+
import type { PciAppearance, PciCollectChangeState, PciCollectMaskedReason, PciCollectMaskToggleMode, PciCollectProfile, PciFrameStrings, PciTokenizeOctResult, PciTokenizeTopUpResult } from './protocol.js';
|
|
2
2
|
export interface PciCollectAuth {
|
|
3
3
|
/** Called right before every backend hop so the frame always gets a fresh
|
|
4
4
|
* access token. By design the frame authenticates with the caller's bearer —
|
|
5
5
|
* the host passes its own user's bearer. There is no publishable-key layer. */
|
|
6
6
|
getBearerToken: () => Promise<string>;
|
|
7
7
|
}
|
|
8
|
-
|
|
8
|
+
/** `onMaskedChange` payload: whether the CVV input is masked after a change
|
|
9
|
+
* and who made it — the mount report (`'init'`), the user's in-frame toggle
|
|
10
|
+
* (`'user'`), your `setMasked()` (`'host'`) or the `maskAfter` timer
|
|
11
|
+
* (`'timeout'`). A boolean and a reason — never the value. */
|
|
12
|
+
export interface PciCollectMaskedChange {
|
|
13
|
+
masked: boolean;
|
|
14
|
+
reason: PciCollectMaskedReason;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Masking of the CVV input while the user types — the same options on the
|
|
18
|
+
* single-form element and the per-field group. The card number is never
|
|
19
|
+
* masked; a profile without a CVV (`oct-destination`) ignores all of this.
|
|
20
|
+
*/
|
|
21
|
+
export interface PciCollectMaskConfig {
|
|
22
|
+
/** Start with the CVV masked (default `true` — the digits are bullets as
|
|
23
|
+
* they are typed, as before this option existed). `false` starts it
|
|
24
|
+
* shown. The state lives in the frame; the user's `maskToggle` and your
|
|
25
|
+
* `setMasked()` change it in place — the value, the caret and the focus
|
|
26
|
+
* stay. Read at mount. */
|
|
27
|
+
masked?: boolean;
|
|
28
|
+
/** The in-frame show/hide control at the field's right edge (default
|
|
29
|
+
* `'none'`: no control — today's field). `'button'` — an eye / eye-off
|
|
30
|
+
* icon, the same control and `copyIcon*` tokens as the reveal element's;
|
|
31
|
+
* `'text'` — a text button from the `show` / `hide` strings. Read at
|
|
32
|
+
* mount. */
|
|
33
|
+
maskToggle?: PciCollectMaskToggleMode;
|
|
34
|
+
/** Re-mask a shown CVV after this many milliseconds, counted from the
|
|
35
|
+
* moment typed digits are visible; masking it earlier cancels the timer.
|
|
36
|
+
* Default: never. Reported through `onMaskedChange` with reason
|
|
37
|
+
* `'timeout'`. Read at mount. */
|
|
38
|
+
maskAfter?: number;
|
|
39
|
+
/** The CVV input's masked state as the frame applied it: once at mount,
|
|
40
|
+
* then after every change. A frame deployment that predates CVV masking
|
|
41
|
+
* never calls it (and keeps the field masked) — that is how you can
|
|
42
|
+
* tell. */
|
|
43
|
+
onMaskedChange?: (event: PciCollectMaskedChange) => void;
|
|
44
|
+
}
|
|
45
|
+
export interface PciCollectElementConfig extends PciCollectMaskConfig {
|
|
9
46
|
profile: PciCollectProfile;
|
|
10
47
|
auth: PciCollectAuth;
|
|
11
48
|
appearance?: PciAppearance;
|
|
@@ -46,8 +83,17 @@ export interface PciCollectTokenizer {
|
|
|
46
83
|
export declare class PciCollectElement implements PciCollectTokenizer {
|
|
47
84
|
private readonly config;
|
|
48
85
|
private readonly channel;
|
|
86
|
+
private readonly mask;
|
|
49
87
|
constructor(frameUrl: string, config: PciCollectElementConfig);
|
|
50
88
|
mount(container: HTMLElement): Promise<void>;
|
|
89
|
+
/** Mask (`true`) or show (`false`) the CVV input in place — the value,
|
|
90
|
+
* the caret and the focus stay where they are. Safe to call before
|
|
91
|
+
* `mount()` resolves (the last value is applied once the frame is
|
|
92
|
+
* initialized) and after `destroy()` (ignored). The frame confirms
|
|
93
|
+
* through `onMaskedChange` when the state actually changed; a profile
|
|
94
|
+
* without a CVV, and a frame deployment that predates CVV masking,
|
|
95
|
+
* ignore it. */
|
|
96
|
+
setMasked(masked: boolean): void;
|
|
51
97
|
/** Profile `topup`: link a new external top-up card. */
|
|
52
98
|
tokenizeTopUp(params: TokenizeTopUpParams): Promise<PciTokenizeTopUpResult>;
|
|
53
99
|
/** Profile `oct-destination`: tokenize a payout destination card (no CVV). */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"collectElement.d.ts","sourceRoot":"","sources":["../src/collectElement.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"collectElement.d.ts","sourceRoot":"","sources":["../src/collectElement.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,aAAa,EACb,qBAAqB,EACrB,sBAAsB,EACtB,wBAAwB,EACxB,iBAAiB,EAEjB,eAAe,EACf,oBAAoB,EACpB,sBAAsB,EACvB,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,cAAc;IAC7B;;oFAEgF;IAChF,cAAc,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;CACvC;AAED;;;+DAG+D;AAC/D,MAAM,WAAW,sBAAsB;IACrC,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,sBAAsB,CAAC;CAChC;AAED;;;;GAIG;AACH,MAAM,WAAW,oBAAoB;IACnC;;;;+BAI2B;IAC3B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;;iBAIa;IACb,UAAU,CAAC,EAAE,wBAAwB,CAAC;IACtC;;;sCAGkC;IAClC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;gBAGY;IACZ,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,sBAAsB,KAAK,IAAI,CAAC;CAC1D;AAED,MAAM,WAAW,uBAAwB,SAAQ,oBAAoB;IACnE,OAAO,EAAE,iBAAiB,CAAC;IAC3B,IAAI,EAAE,cAAc,CAAC;IACrB,UAAU,CAAC,EAAE,aAAa,CAAC;IAC3B,OAAO,CAAC,EAAE,eAAe,CAAC;IAC1B,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,qBAAqB,KAAK,IAAI,CAAC;CACnD;AAED,MAAM,WAAW,mBAAmB;IAClC,cAAc,EAAE;QACd,KAAK,EAAE,MAAM,CAAC;QACd,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,OAAO,EAAE,MAAM,CAAC;QAChB,OAAO,EAAE,MAAM,CAAC;QAChB,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,yDAAyD;IACzD,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,4BAA4B;IAC3C,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;IACjB,YAAY,EAAE,OAAO,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;4DAC4D;AAC5D,MAAM,WAAW,mBAAmB;IAClC,aAAa,CAAC,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAAC;IAC5E,sBAAsB,CAAC,MAAM,EAAE,4BAA4B,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAAC;CAC7F;AAED;;;;;GAKG;AACH,qBAAa,iBAAkB,YAAW,mBAAmB;IAC3D,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA0B;IACjD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAkB;IAC1C,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAoB;gBAE7B,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,uBAAuB;IAYvD,KAAK,CAAC,SAAS,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAqBlD;;;;;;qBAMiB;IACjB,SAAS,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI;IAIhC,wDAAwD;IAClD,aAAa,CAAC,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAAC,sBAAsB,CAAC;IAsBjF,8EAA8E;IACxE,sBAAsB,CAAC,MAAM,EAAE,4BAA4B,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAkBjG,OAAO,IAAI,IAAI;CAIhB"}
|
package/dist/collectElement.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { CollectMaskSender } from './collectMask.js';
|
|
1
2
|
import { PciFrameChannel } from './frameChannel.js';
|
|
2
3
|
/**
|
|
3
4
|
* Card-entry element. Renders PAN/expiry/CVV/name (profile `topup`) or PAN
|
|
@@ -9,9 +10,13 @@ export class PciCollectElement {
|
|
|
9
10
|
constructor(frameUrl, config) {
|
|
10
11
|
this.config = config;
|
|
11
12
|
this.channel = new PciFrameChannel(frameUrl);
|
|
13
|
+
this.mask = new CollectMaskSender(this.channel);
|
|
12
14
|
this.channel.onMessage(message => {
|
|
13
15
|
if (message.type === 'change')
|
|
14
16
|
config.onChange?.(message.state);
|
|
17
|
+
if (message.type === 'collect-masked-changed') {
|
|
18
|
+
config.onMaskedChange?.({ masked: message.masked, reason: message.reason });
|
|
19
|
+
}
|
|
15
20
|
});
|
|
16
21
|
}
|
|
17
22
|
async mount(container) {
|
|
@@ -27,8 +32,22 @@ export class PciCollectElement {
|
|
|
27
32
|
auth,
|
|
28
33
|
appearance: this.config.appearance,
|
|
29
34
|
strings: this.config.strings,
|
|
35
|
+
masked: this.config.masked,
|
|
36
|
+
maskToggle: this.config.maskToggle,
|
|
37
|
+
maskAfter: this.config.maskAfter,
|
|
30
38
|
},
|
|
31
39
|
});
|
|
40
|
+
this.mask.open();
|
|
41
|
+
}
|
|
42
|
+
/** Mask (`true`) or show (`false`) the CVV input in place — the value,
|
|
43
|
+
* the caret and the focus stay where they are. Safe to call before
|
|
44
|
+
* `mount()` resolves (the last value is applied once the frame is
|
|
45
|
+
* initialized) and after `destroy()` (ignored). The frame confirms
|
|
46
|
+
* through `onMaskedChange` when the state actually changed; a profile
|
|
47
|
+
* without a CVV, and a frame deployment that predates CVV masking,
|
|
48
|
+
* ignore it. */
|
|
49
|
+
setMasked(masked) {
|
|
50
|
+
this.mask.send(masked);
|
|
32
51
|
}
|
|
33
52
|
/** Profile `topup`: link a new external top-up card. */
|
|
34
53
|
async tokenizeTopUp(params) {
|
|
@@ -71,6 +90,7 @@ export class PciCollectElement {
|
|
|
71
90
|
});
|
|
72
91
|
}
|
|
73
92
|
destroy() {
|
|
93
|
+
this.mask.close();
|
|
74
94
|
this.channel.destroy();
|
|
75
95
|
}
|
|
76
96
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"collectElement.js","sourceRoot":"","sources":["../src/collectElement.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"collectElement.js","sourceRoot":"","sources":["../src/collectElement.ts"],"names":[],"mappings":"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"}
|
package/dist/collectGroup.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { PciCollectAuth, PciCollectTokenizer, TokenizeOctDestinationParams, TokenizeTopUpParams } from './collectElement.js';
|
|
1
|
+
import type { PciCollectAuth, PciCollectMaskConfig, PciCollectTokenizer, TokenizeOctDestinationParams, TokenizeTopUpParams } from './collectElement.js';
|
|
2
2
|
import type { PciAppearance, PciCollectChangeState, PciCollectFieldName, PciCollectProfile, PciFrameStrings, PciTokenizeOctResult, PciTokenizeTopUpResult } from './protocol.js';
|
|
3
|
-
export interface PciCollectGroupConfig {
|
|
3
|
+
export interface PciCollectGroupConfig extends PciCollectMaskConfig {
|
|
4
4
|
profile: PciCollectProfile;
|
|
5
5
|
auth: PciCollectAuth;
|
|
6
6
|
appearance?: PciAppearance;
|
|
@@ -8,6 +8,15 @@ export interface PciCollectGroupConfig {
|
|
|
8
8
|
/** Aggregated validity/brand across all mounted fields (never card values). */
|
|
9
9
|
onChange?: (state: PciCollectChangeState) => void;
|
|
10
10
|
}
|
|
11
|
+
/**
|
|
12
|
+
* Options of ONE field, passed to `field(name, options)`. Today only the
|
|
13
|
+
* `cvv` field has any — its masking options — and each entry given here wins
|
|
14
|
+
* over the group config's for that field; other fields ignore them. The
|
|
15
|
+
* group config is the usual place (it reads the same for the single-form
|
|
16
|
+
* element); this is for a wrapper that owns one field, not the group — the
|
|
17
|
+
* React `CardCvvField` passes its props here.
|
|
18
|
+
*/
|
|
19
|
+
export type PciCollectFieldOptions = PciCollectMaskConfig;
|
|
11
20
|
/** Handle for one field's iframe. The host mounts it into its own container. */
|
|
12
21
|
export interface PciCollectField {
|
|
13
22
|
mount(container: HTMLElement): Promise<void>;
|
|
@@ -26,9 +35,20 @@ export declare class PciCollectGroup implements PciCollectTokenizer {
|
|
|
26
35
|
private readonly channels;
|
|
27
36
|
private readonly states;
|
|
28
37
|
private brand;
|
|
38
|
+
/** The `setMasked()` path of the CVV field's frame, while that field exists. */
|
|
39
|
+
private cvvMask;
|
|
29
40
|
constructor(frameUrl: string, config: PciCollectGroupConfig);
|
|
30
|
-
/** Create a field element. Mount it into your own container for layout.
|
|
31
|
-
|
|
41
|
+
/** Create a field element. Mount it into your own container for layout.
|
|
42
|
+
* `options` — see `PciCollectFieldOptions` (the `cvv` field only). */
|
|
43
|
+
field(name: PciCollectFieldName, options?: PciCollectFieldOptions): PciCollectField;
|
|
44
|
+
/** Mask (`true`) or show (`false`) the CVV input in place — forwarded to
|
|
45
|
+
* the CVV field's frame; the value, the caret and the focus stay. A no-op
|
|
46
|
+
* while no CVV field exists (not created yet, destroyed, or a profile
|
|
47
|
+
* without one); called between `field('cvv')` and the end of its
|
|
48
|
+
* `mount()`, the last value is applied once the frame is initialized. The
|
|
49
|
+
* frame confirms through `onMaskedChange` when the state actually
|
|
50
|
+
* changed. */
|
|
51
|
+
setMasked(masked: boolean): void;
|
|
32
52
|
private emitChange;
|
|
33
53
|
private aggregator;
|
|
34
54
|
private otherFields;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"collectGroup.d.ts","sourceRoot":"","sources":["../src/collectGroup.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,cAAc,EACd,mBAAmB,EACnB,4BAA4B,EAC5B,mBAAmB,EACpB,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"collectGroup.d.ts","sourceRoot":"","sources":["../src/collectGroup.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,cAAc,EACd,oBAAoB,EACpB,mBAAmB,EACnB,4BAA4B,EAC5B,mBAAmB,EACpB,MAAM,kBAAkB,CAAC;AAG1B,OAAO,KAAK,EACV,aAAa,EAEb,qBAAqB,EACrB,mBAAmB,EACnB,iBAAiB,EACjB,eAAe,EACf,oBAAoB,EACpB,sBAAsB,EACvB,MAAM,YAAY,CAAC;AAapB,MAAM,WAAW,qBAAsB,SAAQ,oBAAoB;IACjE,OAAO,EAAE,iBAAiB,CAAC;IAC3B,IAAI,EAAE,cAAc,CAAC;IACrB,UAAU,CAAC,EAAE,aAAa,CAAC;IAC3B,OAAO,CAAC,EAAE,eAAe,CAAC;IAC1B,+EAA+E;IAC/E,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,qBAAqB,KAAK,IAAI,CAAC;CACnD;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,sBAAsB,GAAG,oBAAoB,CAAC;AAE1D,gFAAgF;AAChF,MAAM,WAAW,eAAe;IAC9B,KAAK,CAAC,SAAS,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C,OAAO,IAAI,IAAI,CAAC;CACjB;AAOD;;;;;GAKG;AACH,qBAAa,eAAgB,YAAW,mBAAmB;IACzD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAwB;IAC/C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAc;IACtC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAmD;IAC5E,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAkF;IACzG,OAAO,CAAC,KAAK,CAA2B;IACxC,gFAAgF;IAChF,OAAO,CAAC,OAAO,CAAkC;gBAErC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,qBAAqB;IAK3D;2EACuE;IACvE,KAAK,CAAC,IAAI,EAAE,mBAAmB,EAAE,OAAO,GAAE,sBAA2B,GAAG,eAAe;IA2DvF;;;;;;mBAMe;IACf,SAAS,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI;IAIhC,OAAO,CAAC,UAAU;IAYlB,OAAO,CAAC,UAAU;IAMlB,OAAO,CAAC,WAAW;IAIb,aAAa,CAAC,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAAC,sBAAsB,CAAC;IAuB3E,sBAAsB,CAAC,MAAM,EAAE,4BAA4B,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAmBjG,OAAO,IAAI,IAAI;CAMhB"}
|
package/dist/collectGroup.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { CollectMaskSender } from './collectMask.js';
|
|
1
2
|
import { PciFrameChannel } from './frameChannel.js';
|
|
2
3
|
const FIELDS_BY_PROFILE = {
|
|
3
4
|
topup: ['number', 'expiry', 'cvv', 'name'],
|
|
@@ -5,6 +6,8 @@ const FIELDS_BY_PROFILE = {
|
|
|
5
6
|
};
|
|
6
7
|
/** The aggregator field — the one that gathers siblings and POSTs. */
|
|
7
8
|
const AGGREGATOR = 'number';
|
|
9
|
+
/** The one field with a masked state — see `PciCollectMaskConfig`. */
|
|
10
|
+
const MASKABLE = 'cvv';
|
|
8
11
|
const randomId = () => typeof crypto !== 'undefined' && 'randomUUID' in crypto
|
|
9
12
|
? crypto.randomUUID()
|
|
10
13
|
: `${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
@@ -20,11 +23,14 @@ export class PciCollectGroup {
|
|
|
20
23
|
this.channels = new Map();
|
|
21
24
|
this.states = {};
|
|
22
25
|
this.brand = 'unknown';
|
|
26
|
+
/** The `setMasked()` path of the CVV field's frame, while that field exists. */
|
|
27
|
+
this.cvvMask = null;
|
|
23
28
|
this.frameUrl = frameUrl;
|
|
24
29
|
this.config = config;
|
|
25
30
|
}
|
|
26
|
-
/** Create a field element. Mount it into your own container for layout.
|
|
27
|
-
|
|
31
|
+
/** Create a field element. Mount it into your own container for layout.
|
|
32
|
+
* `options` — see `PciCollectFieldOptions` (the `cvv` field only). */
|
|
33
|
+
field(name, options = {}) {
|
|
28
34
|
if (!FIELDS_BY_PROFILE[this.config.profile].includes(name)) {
|
|
29
35
|
throw new Error(`Field "${name}" is not part of the "${this.config.profile}" profile`);
|
|
30
36
|
}
|
|
@@ -33,7 +39,17 @@ export class PciCollectGroup {
|
|
|
33
39
|
}
|
|
34
40
|
const channel = new PciFrameChannel(this.frameUrl, { group: this.groupId });
|
|
35
41
|
this.channels.set(name, channel);
|
|
42
|
+
const mask = name === MASKABLE ? new CollectMaskSender(channel) : null;
|
|
43
|
+
if (mask)
|
|
44
|
+
this.cvvMask = mask;
|
|
36
45
|
channel.onMessage(message => {
|
|
46
|
+
if (message.type === 'collect-masked-changed') {
|
|
47
|
+
// Only the CVV frame has a masked state to report.
|
|
48
|
+
if (mask) {
|
|
49
|
+
(options.onMaskedChange ?? this.config.onMaskedChange)?.({ masked: message.masked, reason: message.reason });
|
|
50
|
+
}
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
37
53
|
if (message.type !== 'change')
|
|
38
54
|
return;
|
|
39
55
|
Object.assign(this.states, message.state.fields);
|
|
@@ -52,15 +68,38 @@ export class PciCollectGroup {
|
|
|
52
68
|
groupId: this.groupId,
|
|
53
69
|
appearance: this.config.appearance,
|
|
54
70
|
strings: this.config.strings,
|
|
71
|
+
// The masking options go to the CVV frame only; the field's own
|
|
72
|
+
// options win over the group's, entry by entry.
|
|
73
|
+
...(mask
|
|
74
|
+
? {
|
|
75
|
+
masked: options.masked ?? this.config.masked,
|
|
76
|
+
maskToggle: options.maskToggle ?? this.config.maskToggle,
|
|
77
|
+
maskAfter: options.maskAfter ?? this.config.maskAfter,
|
|
78
|
+
}
|
|
79
|
+
: {}),
|
|
55
80
|
},
|
|
56
81
|
});
|
|
82
|
+
mask?.open();
|
|
57
83
|
},
|
|
58
84
|
destroy: () => {
|
|
85
|
+
mask?.close();
|
|
86
|
+
if (mask && this.cvvMask === mask)
|
|
87
|
+
this.cvvMask = null;
|
|
59
88
|
channel.destroy();
|
|
60
89
|
this.channels.delete(name);
|
|
61
90
|
},
|
|
62
91
|
};
|
|
63
92
|
}
|
|
93
|
+
/** Mask (`true`) or show (`false`) the CVV input in place — forwarded to
|
|
94
|
+
* the CVV field's frame; the value, the caret and the focus stay. A no-op
|
|
95
|
+
* while no CVV field exists (not created yet, destroyed, or a profile
|
|
96
|
+
* without one); called between `field('cvv')` and the end of its
|
|
97
|
+
* `mount()`, the last value is applied once the frame is initialized. The
|
|
98
|
+
* frame confirms through `onMaskedChange` when the state actually
|
|
99
|
+
* changed. */
|
|
100
|
+
setMasked(masked) {
|
|
101
|
+
this.cvvMask?.send(masked);
|
|
102
|
+
}
|
|
64
103
|
emitChange() {
|
|
65
104
|
const active = FIELDS_BY_PROFILE[this.config.profile];
|
|
66
105
|
const fields = {};
|
|
@@ -123,6 +162,8 @@ export class PciCollectGroup {
|
|
|
123
162
|
});
|
|
124
163
|
}
|
|
125
164
|
destroy() {
|
|
165
|
+
this.cvvMask?.close();
|
|
166
|
+
this.cvvMask = null;
|
|
126
167
|
for (const channel of this.channels.values())
|
|
127
168
|
channel.destroy();
|
|
128
169
|
this.channels.clear();
|
package/dist/collectGroup.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"collectGroup.js","sourceRoot":"","sources":["../src/collectGroup.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"collectGroup.js","sourceRoot":"","sources":["../src/collectGroup.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { PciFrameChannel } from './frameChannel.js';
|
|
2
|
+
/**
|
|
3
|
+
* The host→frame `collect-set-masked` path of ONE collect frame, shared by
|
|
4
|
+
* the single-form element and the per-field group (internal — not part of
|
|
5
|
+
* the package surface).
|
|
6
|
+
*
|
|
7
|
+
* A frame drops every event that arrives before its `init`, so a command
|
|
8
|
+
* given earlier is held and sent once `open()` says the init was
|
|
9
|
+
* acknowledged. Only the LAST one is kept: the state is a single boolean, an
|
|
10
|
+
* earlier command has nothing left to say. After `close()` everything is
|
|
11
|
+
* ignored, so `setMasked()` on a destroyed element is safe.
|
|
12
|
+
*/
|
|
13
|
+
export declare class CollectMaskSender {
|
|
14
|
+
private readonly channel;
|
|
15
|
+
private pending;
|
|
16
|
+
private opened;
|
|
17
|
+
private closed;
|
|
18
|
+
constructor(channel: PciFrameChannel);
|
|
19
|
+
send(masked: boolean): void;
|
|
20
|
+
/** The frame acknowledged its init: events reach it from now on. */
|
|
21
|
+
open(): void;
|
|
22
|
+
close(): void;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=collectMask.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collectMask.d.ts","sourceRoot":"","sources":["../src/collectMask.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEtD;;;;;;;;;;GAUG;AACH,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAkB;IAC1C,OAAO,CAAC,OAAO,CAAwB;IACvC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,MAAM,CAAS;gBAEX,OAAO,EAAE,eAAe;IAIpC,IAAI,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI;IAS3B,oEAAoE;IACpE,IAAI,IAAI,IAAI;IAOZ,KAAK,IAAI,IAAI;CAId"}
|