@swype-org/deposit 0.3.26 → 0.3.32

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
@@ -99,6 +99,7 @@ const deposit = new Deposit({
99
99
  signerTimeoutMs: 15_000,
100
100
  flowTimeoutMs: 300_000,
101
101
  enableFullWidget: true,
102
+ presentation: 'overlay', // or 'embedded' — see below
102
103
  debug: false,
103
104
  });
104
105
  ```
@@ -112,6 +113,174 @@ has it enabled — disabling either turns it off. Set it to `false` to force the
112
113
  hosted flow straight to the standard deposit flow regardless of merchant
113
114
  config.
114
115
 
116
+ ## Embedded presentation (payment-method aggregators)
117
+
118
+ By default Blink opens as a full-screen modal: a dim + blur backdrop with the
119
+ payment card centered on top. If you're a payment-method aggregator listing
120
+ Blink as one option **inside your own widget**, set
121
+ `presentation: 'embedded'` and Blink renders inline in an element you provide —
122
+ no backdrop, no page scroll lock, no Escape/backdrop dismissal (all of that
123
+ stays yours), and chrome-less so your panel owns the radius, background and
124
+ shadow.
125
+
126
+ ```typescript
127
+ // On widget mount. `slot` is an empty <div> inside your method panel.
128
+ const deposit = new Deposit({
129
+ signer: '/api/sign-payment',
130
+ merchantId: MERCHANT_ID,
131
+ presentation: 'embedded',
132
+ containerElement: slot, // required
133
+ embedMaxHeightPx: 640, // your panel's height budget
134
+ });
135
+
136
+ // Blink reports its rendered height; the SDK has already resized the iframe.
137
+ // Follow it to size your own panel — height with NO transition (see below).
138
+ deposit.on('resize', ({ heightPx, preferredWidthPx, minWidthPx }) => {
139
+ panel.style.height = `${heightPx}px`;
140
+ // Optional: size the panel's width to the flow too. These are constants
141
+ // (440 / 320), not measurements, so they are safe to transition.
142
+ panel.style.width = `${preferredWidthPx}px`;
143
+ });
144
+
145
+ // When the user picks the Blink row:
146
+ try {
147
+ const { transfer } = await deposit.requestDeposit({ amount, chainId, address, token });
148
+ } catch (err) {
149
+ if (err instanceof DepositError && err.code === 'DEPOSIT_DISMISSED') {
150
+ showMethodList(); // user backed out of Blink
151
+ }
152
+ }
153
+
154
+ deposit.close(); // your own back button
155
+ deposit.destroy(); // widget unmount
156
+ ```
157
+
158
+ `close()` rejects the pending `requestDeposit()` with `DEPOSIT_DISMISSED` — the
159
+ same code the flow's own close control uses — so the `catch` above is the single
160
+ place you restore your method list. (It reports no `error` event and leaves
161
+ `status` on `'idle'`: you asked for this one.)
162
+
163
+ Three things to get right:
164
+
165
+ - **`containerElement` is required, must be stable across re-renders, and must
166
+ never be hidden with `display: none`.** Moving an iframe in the DOM reloads it,
167
+ throwing away the warm-up and the user's session — so render the slot once and
168
+ never recreate the node. When another method is selected, hide it *without
169
+ removing its layout*:
170
+
171
+ ```css
172
+ /* the Blink slot while another method is showing */
173
+ visibility: hidden; height: 0; overflow: hidden; pointer-events: none;
174
+ ```
175
+
176
+ `display: none` looks equivalent and is not: that subtree has no layout, so the
177
+ flow Blink re-warms into it after every close cannot measure itself and may
178
+ report no height at all. The panel then reopens blank at its budget height with
179
+ nothing in your console. It is timing-dependent, which is what makes it nasty —
180
+ closing your whole dialog tends to flip `display` in the same commit and works,
181
+ while a back button inside the panel does not. The SDK warns when it reveals
182
+ into an unrendered container; that warning is this.
183
+ - **Don't add a CSS transition to the height you get from `resize`.** Blink
184
+ animates its own height over ~300ms and reports every frame of it. A
185
+ transition on your side makes the panel chase each of those frames, so it
186
+ keeps moving after the content has settled — which shifts elements under the
187
+ pointer and shows up as the cursor flickering between pointer and default.
188
+ Apply the height directly. `preferredWidthPx` is a constant, so transitioning
189
+ the *width* is fine.
190
+ - **Embedded is a desktop presentation.** On mobile the same config presents as
191
+ Blink's normal full-screen sheet, because a phone-sized method panel cannot
192
+ hold a flow with a keypad, wallet lists, a QR code and a passkey ceremony.
193
+ Nothing else downgrades it: no failure in the hosted flow will drop a modal
194
+ over your app unannounced. Read the resolved value from `deposit.presentation`
195
+ (`'overlay' | 'embedded'`) — on `'overlay'` no `resize` event ever fires, so
196
+ collapse or skip the slot rather than leaving an empty box behind the sheet:
197
+
198
+ ```typescript
199
+ // Re-read on resize; it is live until Blink builds its warm-up frame.
200
+ {deposit.presentation === 'embedded' && <MethodPanel />}
201
+ ```
202
+
203
+ ### Brand colors
204
+
205
+ Five colors, and Blink derives its whole palette from them — surfaces, the
206
+ recessed and hover steps, the text ramp, borders, the button hover and label, the
207
+ tinted semantic surfaces — so the flow reads as part of your product rather than a
208
+ foreign panel inside it. Names follow Stripe's `appearance.variables`.
209
+
210
+ ```typescript
211
+ const deposit = new Deposit({
212
+ signer: '/api/sign-payment',
213
+ presentation: 'embedded',
214
+ containerElement: slot,
215
+ appearance: {
216
+ theme: 'light',
217
+ variables: {
218
+ colorPrimary: '#0f62fe', // primary button, focus ring, selected states
219
+ colorBackground: '#101828', // the card surface
220
+ colorText: '#f7f9fc',
221
+ colorDanger: '#ff5c5c', // optional
222
+ colorBorder: '#22304a', // optional — derived from surface + text if omitted
223
+ },
224
+ },
225
+ });
226
+ ```
227
+
228
+ Worth knowing before you pick values:
229
+
230
+ - **Opaque hex only** (`#rgb` or `#rrggbb`). `rgb()`, CSS color names and 8-digit
231
+ hex with alpha are dropped with a console error naming the key — a solid card
232
+ cannot honour translucency, and an unparseable color cannot be contrast-checked.
233
+ - **`colorBackground` picks the light or dark base** for everything that is not
234
+ themeable (shadows, the scrim, semantic hues), by its own luminance. A dark
235
+ surface stays coherent even with `theme: 'light'`.
236
+ - **Contrast is enforced.** Where a derived pair would fall below WCAG AA the
237
+ *derived* value is substituted — never your input — and the substitution is
238
+ reported in the hosted flow's console. A `colorPrimary` too mid-toned for either
239
+ a black or a white label is reported and left alone; only you can fix that one.
240
+ - **Not themeable:** the type scale, weights, radii, fonts, Blink's own marks, and
241
+ the success/error/warning hues and authorization states.
242
+
243
+ Everything else derives, deliberately: the palette has ~35 mutually-constrained
244
+ tokens and five inputs cannot produce a broken combination, where thirty-five
245
+ reliably would.
246
+
247
+ `embedMaxHeightPx` (default: 90% of the host viewport) caps the iframe and is
248
+ also the basis Blink sizes its own layout against. Past that height the flow
249
+ scrolls internally instead of growing your panel.
250
+
251
+ Blink's screens are designed for **320–440px wide**, and the flow reports that
252
+ envelope on every `resize` as `minWidthPx` (320) and `preferredWidthPx` (440).
253
+ The iframe fills the width you give it, capped at 440 — it cannot widen your
254
+ panel on its own, so if you want the panel to fit the flow, size it from
255
+ `preferredWidthPx`. Below `minWidthPx`, fixed-width elements inside the flow
256
+ overflow.
257
+
258
+ Your CSP needs `frame-src https://pay.blink.cash`.
259
+
260
+ ### Merchant balance
261
+
262
+ If your users hold a balance on your platform, pass it on the request and the
263
+ flow shows it under the deposit header — "Acme balance: $20.70" — so the user
264
+ tops up with their number in view:
265
+
266
+ ```typescript
267
+ await deposit.requestDeposit({
268
+ amount: null,
269
+ chainId: 8453,
270
+ address: userWalletAddress,
271
+ token: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
272
+ balance: 20.7, // USD — display only
273
+ });
274
+ ```
275
+
276
+ Display-only by design: the value is never part of the signed payload and never
277
+ reaches your signer, so no signer changes are needed — and it cannot affect the
278
+ transfer amount or destination. It must be a finite number, `>= 0` and below
279
+ `1e12`; the SDK floors it to whole cents (`20.789` shows as `$20.78`). Anything
280
+ else is dropped with a console error naming the field, and the deposit proceeds
281
+ without the subtitle. When you don't pass it, Blink may fall back to showing
282
+ the destination wallet's on-chain balance instead.
283
+
115
284
  ## Error Handling
116
285
 
117
286
  Every error is a `DepositError` with a machine-readable `code`:
@@ -145,6 +314,8 @@ transfer.on('complete', (result) => { /* DepositResult */ });
145
314
  transfer.on('error', (error) => { /* DepositError */ });
146
315
  transfer.on('close', () => { /* iframe closed */ });
147
316
  transfer.on('status-change', (status) => { /* DepositStatus */ });
317
+ // Embedded presentation only — the inline iframe was resized to this height.
318
+ transfer.on('resize', ({ heightPx }) => { /* number */ });
148
319
  ```
149
320
 
150
321
  ## Lifecycle