flintn-checkout 0.0.15 → 0.0.16

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
@@ -265,7 +265,12 @@ Individual PCI-compliant input fields rendered as separate iframes. You control
265
265
 
266
266
  Each field (card number, expiry, CVV) is a separate iframe. Raw card data never touches your page.
267
267
 
268
- > **Note:** Hosted Fields emits only the terminal `onPayment` event there is no `onEvent` per-attempt stream. Use Iframe Checkout if you need granular attempt analytics.
268
+ When a card payment requires 3DS, the SDK shows the bank's challenge in a
269
+ **modal dialog on your page**. The buyer can dismiss it, which surfaces as a
270
+ `THREE_DS_CANCELLED` payment error. While the challenge is open, the
271
+ `submit()` promise stays pending with a 10-minute timeout.
272
+
273
+ > **Note:** For card fields, Hosted Fields emits only the terminal `onPayment` event. The express buttons additionally emit per-attempt wallet analytics via the top-level `onEvent` callback (same `onPayment` / `onEvent` split as Iframe Checkout) — see "Express buttons" below. For full per-attempt analytics across the card form too, use Iframe Checkout.
269
274
 
270
275
  **React**
271
276
 
@@ -424,6 +429,7 @@ Additional React-only options:
424
429
  | `onChange` | `(event: FieldChangeEvent) => void` | Optional additional callback |
425
430
  | `onFocus` | `(fieldType: TFieldType) => void` | Optional additional callback |
426
431
  | `onBlur` | `(fieldType: TFieldType, state: FieldState) => void` | Optional additional callback |
432
+ | `onEvent` | `(event: ExpressAttempt) => void` | Per-attempt wallet analytics from the express buttons (see "Express buttons") |
427
433
 
428
434
  ### FieldOptions
429
435
 
@@ -439,6 +445,10 @@ Additional React-only options:
439
445
  | `cardNumberRef` | `RefObject<HTMLDivElement>` | Ref for card number container |
440
446
  | `expiryRef` | `RefObject<HTMLDivElement>` | Ref for expiry container |
441
447
  | `cvvRef` | `RefObject<HTMLDivElement>` | Ref for CVV container |
448
+ | `applePayRef` | `RefObject<HTMLDivElement>` | Ref for the Apple Pay button container (attaching it enables the wallet) |
449
+ | `googlePayRef` | `RefObject<HTMLDivElement>` | Ref for the Google Pay button container (attaching it enables the wallet) |
450
+ | `payPalRef` | `RefObject<HTMLDivElement>` | Ref for the PayPal button container (attaching it enables the wallet) |
451
+ | `expressAvailable` | `boolean` | True once wallet detection settles and at least one express button is renderable |
442
452
  | `isReady` | `boolean` | All fields loaded and ready |
443
453
  | `fieldErrors` | `Partial<Record<TFieldType, string \| null>>` | Per-field validation errors (populated after first submit) |
444
454
  | `fieldStates` | `Partial<Record<TFieldType, FieldState>>` | Per-field state (always up to date) |
@@ -479,6 +489,105 @@ Validation mirrors react-hook-form `mode: 'onSubmit'`:
479
489
  | `field.getState()` | Get current field state |
480
490
  | `field.getFieldType()` | Get the field type |
481
491
 
492
+ ### Express buttons (Apple Pay / Google Pay / PayPal)
493
+
494
+ Hosted Fields can also render **fast checkout buttons** so buyers can pay with a
495
+ wallet instead of typing card details. The buttons render **directly into your
496
+ DOM** — one element per wallet, no iframe. Your page's own layout sizes and
497
+ positions each button; wallet UI (the Apple Pay sheet or desktop QR modal, the
498
+ Google Pay sheet, the PayPal popup) overlays your page natively. Only encrypted
499
+ wallet tokens pass through the page; they are authorized against the
500
+ session-scoped payments API, so no API key ever reaches the browser. Terminal
501
+ results arrive through the **same `onPayment`** callback as the card `submit()`.
502
+
503
+ **React**
504
+
505
+ ```tsx
506
+ import { useFlintNFields } from 'flintn-checkout/react';
507
+
508
+ function CheckoutForm() {
509
+ const {
510
+ cardNumberRef, expiryRef, cvvRef,
511
+ applePayRef, googlePayRef, payPalRef, // one container per wallet
512
+ expressAvailable, // true once ≥1 wallet button is renderable
513
+ submit,
514
+ } = useFlintNFields({
515
+ config: { clientSessionId: 'your_client_session_id' },
516
+ onPayment: (result) => {
517
+ if (result.status === 'PAYMENT_SUCCESS') console.log('Paid:', result.data);
518
+ },
519
+ onEvent: (e) => console.log('express attempt:', e.event, e.method),
520
+ });
521
+
522
+ return (
523
+ <form onSubmit={(e) => { e.preventDefault(); submit(); }}>
524
+ {/* attach a ref to enable that wallet; unavailable wallets render
525
+ nothing — collapse empty containers via CSS if they carry spacing */}
526
+ <div ref={applePayRef} />
527
+ <div ref={googlePayRef} />
528
+ <div ref={payPalRef} />
529
+ {expressAvailable && <div className="divider">or pay by card</div>}
530
+
531
+ <div ref={cardNumberRef} style={{ height: 40 }} />
532
+ <div ref={expiryRef} style={{ height: 40 }} />
533
+ <div ref={cvvRef} style={{ height: 40 }} />
534
+ <button type="submit">Pay</button>
535
+ </form>
536
+ );
537
+ }
538
+ ```
539
+
540
+ **Vanilla JavaScript**
541
+
542
+ ```javascript
543
+ import { createFlintNFields } from 'flintn-checkout';
544
+
545
+ const fields = createFlintNFields({
546
+ config: { clientSessionId: 'your_client_session_id' },
547
+ onPayment: (result) => console.log('Payment result:', result),
548
+ });
549
+
550
+ const express = fields.createExpressButtons({
551
+ onCapability: (cap) => console.log('available wallets:', cap),
552
+ onEvent: (attempt) => console.log('express attempt:', attempt),
553
+ });
554
+
555
+ await express.mountExpressButtons([
556
+ { method: 'APPLE_PAY', elementSelector: '#apple-pay' },
557
+ { method: 'GOOGLE_PAY', elementSelector: '#google-pay' },
558
+ { method: 'PAYPAL', elementSelector: '#paypal' },
559
+ ]);
560
+
561
+ // unmount a single wallet, or all of them
562
+ express.unmountExpressButton('APPLE_PAY');
563
+ express.unmountAllExpressButtons();
564
+
565
+ // card fields as usual
566
+ fields.createField('card-number').mount('#card-number');
567
+ fields.createField('expiry').mount('#expiry');
568
+ fields.createField('cvv').mount('#cvv');
569
+ ```
570
+
571
+ Notes:
572
+
573
+ - **No `submit()` for express.** Wallet buttons self-trigger (the buyer taps Apple
574
+ Pay and the sheet opens). Success/error come through `onPayment`; per-attempt
575
+ analytics through the top-level `onEvent` (React) /
576
+ `createExpressButtons({ onEvent })` (vanilla).
577
+ - **Availability.** Buttons for methods that are unavailable on this
578
+ device/session are silently skipped at mount — their containers just stay
579
+ empty. `expressAvailable` (React) / the `onCapability` callback (vanilla)
580
+ gate your "or pay by card" divider.
581
+ - **Which wallets appear** is driven entirely by the merchant session config
582
+ (`payment_methods`) intersected with device support — there is no client-side
583
+ allow-list.
584
+ - **3DS** for wallet payments is handled automatically via a popup (make sure the
585
+ buyer's click isn't intercepted by a popup blocker).
586
+ - **Apple Pay domain registration.** Your live domain must be registered with
587
+ Apple (handled during merchant onboarding) — merchant validation runs against
588
+ the domain the buttons render on. On non-Safari browsers Apple's JS SDK shows
589
+ its QR-code modal over your page. Google Pay and PayPal have no such limits.
590
+
482
591
  ---
483
592
 
484
593
  ## Shared Types
@@ -503,6 +612,38 @@ interface PaymentError {
503
612
  }
504
613
  ```
505
614
 
615
+ ### Error handling
616
+
617
+ Errors surface through three separate channels — don't conflate them:
618
+
619
+ | Channel | What lands there | Show as |
620
+ |---------|------------------|---------|
621
+ | `error` / `onError` | Infrastructure: SDK failed to initialize, a container is missing, the session config couldn't load | Page-level banner ("checkout unavailable / session expired") |
622
+ | `paymentResult` / `onPayment` with `PAYMENT_ERROR` | A payment attempt failed (card or wallet) | Inline payment error, offer retry |
623
+ | `fieldErrors` | Per-field validation (populated after the first `submit()`/`validate()`) | Messages next to each field |
624
+
625
+ Common `code` values:
626
+
627
+ | Code | Channel | Meaning |
628
+ |------|---------|---------|
629
+ | `INIT_ERROR` | error | SDK could not initialize (missing `clientSessionId`, bad `origin`) |
630
+ | `MOUNT_ERROR` | error | A field/button container was not found or failed to mount |
631
+ | `SESSION_EXPIRED` (and other API codes) | error or payment | Passed through from the payments API as-is |
632
+ | `CONFIG_ERROR` | error | Session config failed to load and the API gave no code |
633
+ | `NETWORK_ERROR` / `HTTP_<status>` / `INVALID_RESPONSE` | error or payment | Network failure / HTTP error without an API code / malformed response |
634
+ | `VALIDATION_ERROR` | payment | `submit()` blocked by field validation (details in `fieldErrors`) |
635
+ | `MISSING_FIELDS` / `NOT_INITIALIZED` / `SUBMIT_IN_PROGRESS` / `SUBMIT_TIMEOUT` | payment | Card `submit()` preconditions / duplicate call / no response in time (30s; 10 min during a 3DS challenge) |
636
+ | `PAYMENT_DECLINED` / processor decline codes | payment | Declined; the processor's own code is passed through when present |
637
+ | `PAYMENT_FAILED` | payment | Non-declined failure (including unexpected 3DS outcomes) |
638
+ | `APPLE_PAY_SDK_ERROR` / `GOOGLE_PAY_SDK_ERROR` / `PAYPAL_ERROR` | payment | The wallet SDK failed mid-flow |
639
+ | `PAYPAL_MISSING_PAYMENT_ID` | payment | PayPal approved but no payment id was captured (anomaly) |
640
+ | `THREE_DS_FAILED` / `THREE_DS_CANCELLED` | payment | Challenge declined / dismissed by the buyer |
641
+ | `THREE_DS_POPUP_BLOCKED` | payment | Express buttons only — the wallet 3DS window was blocked by the browser |
642
+
643
+ Worth handling specifically: `SESSION_EXPIRED` (create a fresh session and remount),
644
+ `THREE_DS_POPUP_BLOCKED` (ask the buyer to allow popups), `VALIDATION_ERROR`
645
+ (highlight fields from `fieldErrors`). Everything else is safely generic.
646
+
506
647
  ### CheckoutEvent
507
648
 
508
649
  ```typescript
@@ -683,8 +824,8 @@ on `createField` (or the `fields` prop of `useFlintNFields`) instead.
683
824
  | `loaderColor` | ✅ | — | Loading spinner color |
684
825
  | `backgroundColor` | ✅ | — | Form background color |
685
826
  | `lightLogos` | ✅ | — | Use light logo variants in the safe-checkout row (for dark backgrounds) |
686
- | `expressButtonsSpacing` | ✅ | — | Spacing between express payment buttons |
687
- | `expressButtonsBorderRadius` | ✅ | | Corner radius for express buttons — Apple Pay, Google Pay, PayPal, and the "Credit or debit card" button in the LIST variant (e.g. `8px`) |
827
+ | `expressButtonsSpacing` | ✅ | — | Spacing between express payment buttons. Not applicable to Hosted Fields — the buttons render into your own containers, so spacing is your page's layout |
828
+ | `expressButtonsBorderRadius` | ✅ | | Corner radius for express buttons — Apple Pay, Google Pay, PayPal, and the "Credit or debit card" button in the LIST variant (e.g. `8px`). In Hosted Fields it applies to all wallet buttons at once |
688
829
  | `inputBackgroundColor` | ✅ | ✅ | Input field background color |
689
830
  | `inputBorderRadius` | ✅ | ✅ | Input field border radius |
690
831
  | `inputBorderColor` | ✅ | ✅ | Default input border color |
@@ -754,6 +895,7 @@ Every cancellation still emits a `CANCELLED` `onEvent` regardless of variant, so
754
895
 
755
896
  ### Hosted Fields
756
897
  - Credit/Debit Cards (Visa, Mastercard, Amex, Discover)
898
+ - Apple Pay, Google Pay, PayPal (via the express buttons — see "Express buttons" above)
757
899
 
758
900
  ## Debug Mode
759
901
 
package/dist/index.d.mts CHANGED
@@ -171,6 +171,8 @@ declare const FieldEventType: {
171
171
  readonly FIELD_VALIDATION: "FIELD_VALIDATION";
172
172
  readonly FIELD_SUBMIT_RESULT: "FIELD_SUBMIT_RESULT";
173
173
  readonly FIELD_HEIGHT: "FIELD_HEIGHT";
174
+ readonly FIELD_THREE_DS_REQUIRED: "FIELD_THREE_DS_REQUIRED";
175
+ readonly FIELD_THREE_DS_RESULT: "FIELD_THREE_DS_RESULT";
174
176
  };
175
177
  type TFieldEventType = (typeof FieldEventType)[keyof typeof FieldEventType];
176
178
  interface FieldOptions {
@@ -200,8 +202,31 @@ interface FieldsValidationResult {
200
202
  isValid: boolean;
201
203
  errors: Partial<Record<TFieldType, string | null>>;
202
204
  }
205
+ interface SubmitBillingAddress {
206
+ addressLine1?: string;
207
+ addressLine2?: string;
208
+ city?: string;
209
+ state?: string;
210
+ postalCode?: string;
211
+ country?: string;
212
+ }
203
213
  interface SubmitOptions {
204
214
  cardholderName?: string;
215
+ email?: string;
216
+ billingAddress?: SubmitBillingAddress;
217
+ }
218
+ interface ExpressCapability {
219
+ applePay: boolean;
220
+ googlePay: boolean;
221
+ paypal: boolean;
222
+ }
223
+ interface ExpressAttempt {
224
+ event: TPaymentAttemptResult;
225
+ method: TPaymentMethod;
226
+ paymentId?: string;
227
+ code?: string;
228
+ message?: string;
229
+ timestamp: number;
205
230
  }
206
231
  interface FlintNFieldsConfig {
207
232
  clientSessionId: string;
@@ -247,9 +272,144 @@ interface FlintNField {
247
272
  }
248
273
  declare function createFlintNField(fieldType: TFieldType, origin: string, clientSessionId: string, options: FieldOptions, callbacks: FlintNFieldInternalCallbacks, debug?: boolean, formStyles?: FormStyles): FlintNField;
249
274
 
275
+ declare const EXPRESS_BUTTON_HEIGHT = 40;
276
+ declare const ExpressMethod: {
277
+ readonly APPLE_PAY: "APPLE_PAY";
278
+ readonly GOOGLE_PAY: "GOOGLE_PAY";
279
+ readonly PAYPAL: "PAYPAL";
280
+ };
281
+ type TExpressMethod = (typeof ExpressMethod)[keyof typeof ExpressMethod];
282
+ /** One entry per wallet button: which method goes into which element. */
283
+ interface ExpressButtonMount {
284
+ method: TExpressMethod;
285
+ /** CSS selector or the element itself. The button fills the element. */
286
+ elementSelector: string | HTMLElement;
287
+ }
288
+ interface FlintNExpressButtonsOptions {
289
+ config: {
290
+ clientSessionId: string;
291
+ };
292
+ origin?: string;
293
+ apiUrl?: string;
294
+ buttonBorderRadius?: number;
295
+ onPayment?: (result: PaymentResult) => void;
296
+ onEvent?: (event: ExpressAttempt) => void;
297
+ onCapability?: (capability: ExpressCapability) => void;
298
+ onError?: (error: PaymentError) => void;
299
+ debug?: boolean;
300
+ }
301
+ interface FlintNExpressButtons {
302
+ mountExpressButtons(buttons: ExpressButtonMount[]): Promise<void>;
303
+ unmountExpressButton(method: TExpressMethod): void;
304
+ unmountAllExpressButtons(): void;
305
+ }
306
+ interface ApplePayConfig {
307
+ country_code: string;
308
+ supported_networks: string[];
309
+ merchant_capabilities: string[];
310
+ display_name: string;
311
+ merchant_identifier?: string;
312
+ }
313
+ interface GooglePayConfig {
314
+ environment: string;
315
+ gateway: string;
316
+ gateway_merchant_id: string;
317
+ merchant_id?: string;
318
+ merchant_name?: string;
319
+ country_code?: string;
320
+ }
321
+ interface PayPalConfig {
322
+ client_id: string;
323
+ merchant_id?: string;
324
+ }
325
+ interface SessionConfig {
326
+ session: {
327
+ amount: number;
328
+ currency_code: string;
329
+ };
330
+ payment_methods: string[];
331
+ apple_pay_config?: ApplePayConfig;
332
+ google_pay_config?: GooglePayConfig;
333
+ paypal_config?: PayPalConfig;
334
+ }
335
+ declare const AuthorizedStatus: {
336
+ readonly PENDING: "PENDING";
337
+ readonly AUTHORIZED: "AUTHORIZED";
338
+ readonly FAILED: "FAILED";
339
+ readonly DECLINED: "DECLINED";
340
+ readonly THREE_DS_INITIATED: "THREE_DS_INITIATED";
341
+ };
342
+ type TAuthorizedStatus = (typeof AuthorizedStatus)[keyof typeof AuthorizedStatus];
343
+ interface AuthorizeResponse {
344
+ id: string;
345
+ status: TAuthorizedStatus;
346
+ required_action?: {
347
+ type: string;
348
+ redirect_url: string;
349
+ };
350
+ status_reason?: {
351
+ decline_reason?: string;
352
+ message?: string;
353
+ };
354
+ }
355
+ interface ApplePayToken {
356
+ payment_data: {
357
+ data: string;
358
+ header: {
359
+ ephemeral_public_key: string;
360
+ public_key_hash: string;
361
+ transaction_id: string;
362
+ };
363
+ signature: string;
364
+ version: string;
365
+ };
366
+ payment_method: {
367
+ display_name: string;
368
+ network: string;
369
+ type: string;
370
+ };
371
+ transaction_identifier: string;
372
+ }
373
+ interface GooglePayToken {
374
+ token: string;
375
+ }
376
+ /** Terminal-ish callbacks the wallet flows report into (mirrors the iframe's ExpressAuthorizeHandlers). */
377
+ interface WalletAuthorizeHandlers {
378
+ onAuthorized: (paymentId: string) => void;
379
+ onThreeDs: (redirectUrl: string, paymentId: string) => void;
380
+ onFailed: (code: string, message: string, paymentId?: string) => void;
381
+ }
382
+
383
+ /**
384
+ * Express fast-checkout buttons (Apple Pay / Google Pay / PayPal) rendered
385
+ * DIRECTLY into the merchant's DOM — one element per wallet, Yuno-style.
386
+ * There is no iframe: wallet SDKs load on the merchant page, wallet UI
387
+ * (Apple Pay QR modal, payment sheets) overlays the page natively, and the
388
+ * page's own layout sizes the buttons. Only encrypted wallet tokens pass
389
+ * through the page; they are authorized against the session-scoped payments
390
+ * API (no API key in the browser).
391
+ */
392
+ declare function createFlintNExpressButtons(options: FlintNExpressButtonsOptions): FlintNExpressButtons;
393
+
394
+ declare const deriveApiUrl: (payOrigin: string) => string;
395
+
396
+ /**
397
+ * Per-instance express options; session/origin/onPayment come from the fields
398
+ * instance, and button styling comes from `config.styles`
399
+ * (expressButtonsBorderRadius).
400
+ */
401
+ type ExpressButtonsOptions = Pick<FlintNExpressButtonsOptions, 'apiUrl' | 'onEvent' | 'onCapability'>;
250
402
  interface FlintNFields {
251
403
  createField(fieldType: TFieldType, options?: FieldOptions): FlintNField;
252
404
  getField(fieldType: TFieldType): FlintNField | undefined;
405
+ /**
406
+ * Create the express fast-checkout buttons (Apple Pay / Google Pay /
407
+ * PayPal), rendered directly into merchant-owned elements (one per wallet —
408
+ * see `mountExpressButtons`). Terminal results flow through the same
409
+ * `onPayment` callback as the card `submit()`; per-attempt analytics and
410
+ * capability flags are delivered via the options passed here.
411
+ */
412
+ createExpressButtons(options?: ExpressButtonsOptions): FlintNExpressButtons;
253
413
  validate(): Promise<FieldsValidationResult>;
254
414
  submit(options?: SubmitOptions, skipValidation?: boolean): Promise<PaymentResult>;
255
415
  unmount(): void;
@@ -265,4 +425,4 @@ declare const validateConfig: (config: {
265
425
  }) => void;
266
426
  declare const sanitizeToken: (token: string) => string;
267
427
 
268
- export { type CheckoutEvent, CheckoutFormVariant, CheckoutView, EventType, type FieldChangeEvent, FieldEventType, type FieldOptions, type FieldState, FieldType, type FieldValidationResult, type FieldsValidationResult, type FlintNConfig, type FlintNField, type FlintNFieldInternalCallbacks, type FlintNFields, type FlintNFieldsConfig, type FlintNFieldsOptions, type FlintNPayment, type FlintNPaymentOptions, type FormFields, type FormPlaceholders, type FormStyles, type PaymentAttempt, PaymentAttemptResult, type PaymentError, PaymentMethod, type PaymentResult, type PaymentUI, PaymentUIEvent, type SubmitOptions, type TCheckoutFormVariant, type TCheckoutView, type TEventType, type TFieldEventType, type TFieldType, type TPaymentAttemptResult, type TPaymentMethod, type TPaymentUIEvent, buildIframeSrc, createFlintNField, createFlintNFields, createFlintNPayment, createIframeElement, isAttemptEvent, isUIEvent, parseOrigin, sanitizeToken, validateConfig };
428
+ export { type ApplePayConfig, type ApplePayToken, type AuthorizeResponse, AuthorizedStatus, type CheckoutEvent, CheckoutFormVariant, CheckoutView, EXPRESS_BUTTON_HEIGHT, EventType, type ExpressAttempt, type ExpressButtonMount, type ExpressButtonsOptions, type ExpressCapability, ExpressMethod, type FieldChangeEvent, FieldEventType, type FieldOptions, type FieldState, FieldType, type FieldValidationResult, type FieldsValidationResult, type FlintNConfig, type FlintNExpressButtons, type FlintNExpressButtonsOptions, type FlintNField, type FlintNFieldInternalCallbacks, type FlintNFields, type FlintNFieldsConfig, type FlintNFieldsOptions, type FlintNPayment, type FlintNPaymentOptions, type FormFields, type FormPlaceholders, type FormStyles, type GooglePayConfig, type GooglePayToken, type PayPalConfig, type PaymentAttempt, PaymentAttemptResult, type PaymentError, PaymentMethod, type PaymentResult, type PaymentUI, PaymentUIEvent, type SessionConfig, type SubmitBillingAddress, type SubmitOptions, type TAuthorizedStatus, type TCheckoutFormVariant, type TCheckoutView, type TEventType, type TExpressMethod, type TFieldEventType, type TFieldType, type TPaymentAttemptResult, type TPaymentMethod, type TPaymentUIEvent, type WalletAuthorizeHandlers, buildIframeSrc, createFlintNExpressButtons, createFlintNField, createFlintNFields, createFlintNPayment, createIframeElement, deriveApiUrl, isAttemptEvent, isUIEvent, parseOrigin, sanitizeToken, validateConfig };
package/dist/index.d.ts CHANGED
@@ -171,6 +171,8 @@ declare const FieldEventType: {
171
171
  readonly FIELD_VALIDATION: "FIELD_VALIDATION";
172
172
  readonly FIELD_SUBMIT_RESULT: "FIELD_SUBMIT_RESULT";
173
173
  readonly FIELD_HEIGHT: "FIELD_HEIGHT";
174
+ readonly FIELD_THREE_DS_REQUIRED: "FIELD_THREE_DS_REQUIRED";
175
+ readonly FIELD_THREE_DS_RESULT: "FIELD_THREE_DS_RESULT";
174
176
  };
175
177
  type TFieldEventType = (typeof FieldEventType)[keyof typeof FieldEventType];
176
178
  interface FieldOptions {
@@ -200,8 +202,31 @@ interface FieldsValidationResult {
200
202
  isValid: boolean;
201
203
  errors: Partial<Record<TFieldType, string | null>>;
202
204
  }
205
+ interface SubmitBillingAddress {
206
+ addressLine1?: string;
207
+ addressLine2?: string;
208
+ city?: string;
209
+ state?: string;
210
+ postalCode?: string;
211
+ country?: string;
212
+ }
203
213
  interface SubmitOptions {
204
214
  cardholderName?: string;
215
+ email?: string;
216
+ billingAddress?: SubmitBillingAddress;
217
+ }
218
+ interface ExpressCapability {
219
+ applePay: boolean;
220
+ googlePay: boolean;
221
+ paypal: boolean;
222
+ }
223
+ interface ExpressAttempt {
224
+ event: TPaymentAttemptResult;
225
+ method: TPaymentMethod;
226
+ paymentId?: string;
227
+ code?: string;
228
+ message?: string;
229
+ timestamp: number;
205
230
  }
206
231
  interface FlintNFieldsConfig {
207
232
  clientSessionId: string;
@@ -247,9 +272,144 @@ interface FlintNField {
247
272
  }
248
273
  declare function createFlintNField(fieldType: TFieldType, origin: string, clientSessionId: string, options: FieldOptions, callbacks: FlintNFieldInternalCallbacks, debug?: boolean, formStyles?: FormStyles): FlintNField;
249
274
 
275
+ declare const EXPRESS_BUTTON_HEIGHT = 40;
276
+ declare const ExpressMethod: {
277
+ readonly APPLE_PAY: "APPLE_PAY";
278
+ readonly GOOGLE_PAY: "GOOGLE_PAY";
279
+ readonly PAYPAL: "PAYPAL";
280
+ };
281
+ type TExpressMethod = (typeof ExpressMethod)[keyof typeof ExpressMethod];
282
+ /** One entry per wallet button: which method goes into which element. */
283
+ interface ExpressButtonMount {
284
+ method: TExpressMethod;
285
+ /** CSS selector or the element itself. The button fills the element. */
286
+ elementSelector: string | HTMLElement;
287
+ }
288
+ interface FlintNExpressButtonsOptions {
289
+ config: {
290
+ clientSessionId: string;
291
+ };
292
+ origin?: string;
293
+ apiUrl?: string;
294
+ buttonBorderRadius?: number;
295
+ onPayment?: (result: PaymentResult) => void;
296
+ onEvent?: (event: ExpressAttempt) => void;
297
+ onCapability?: (capability: ExpressCapability) => void;
298
+ onError?: (error: PaymentError) => void;
299
+ debug?: boolean;
300
+ }
301
+ interface FlintNExpressButtons {
302
+ mountExpressButtons(buttons: ExpressButtonMount[]): Promise<void>;
303
+ unmountExpressButton(method: TExpressMethod): void;
304
+ unmountAllExpressButtons(): void;
305
+ }
306
+ interface ApplePayConfig {
307
+ country_code: string;
308
+ supported_networks: string[];
309
+ merchant_capabilities: string[];
310
+ display_name: string;
311
+ merchant_identifier?: string;
312
+ }
313
+ interface GooglePayConfig {
314
+ environment: string;
315
+ gateway: string;
316
+ gateway_merchant_id: string;
317
+ merchant_id?: string;
318
+ merchant_name?: string;
319
+ country_code?: string;
320
+ }
321
+ interface PayPalConfig {
322
+ client_id: string;
323
+ merchant_id?: string;
324
+ }
325
+ interface SessionConfig {
326
+ session: {
327
+ amount: number;
328
+ currency_code: string;
329
+ };
330
+ payment_methods: string[];
331
+ apple_pay_config?: ApplePayConfig;
332
+ google_pay_config?: GooglePayConfig;
333
+ paypal_config?: PayPalConfig;
334
+ }
335
+ declare const AuthorizedStatus: {
336
+ readonly PENDING: "PENDING";
337
+ readonly AUTHORIZED: "AUTHORIZED";
338
+ readonly FAILED: "FAILED";
339
+ readonly DECLINED: "DECLINED";
340
+ readonly THREE_DS_INITIATED: "THREE_DS_INITIATED";
341
+ };
342
+ type TAuthorizedStatus = (typeof AuthorizedStatus)[keyof typeof AuthorizedStatus];
343
+ interface AuthorizeResponse {
344
+ id: string;
345
+ status: TAuthorizedStatus;
346
+ required_action?: {
347
+ type: string;
348
+ redirect_url: string;
349
+ };
350
+ status_reason?: {
351
+ decline_reason?: string;
352
+ message?: string;
353
+ };
354
+ }
355
+ interface ApplePayToken {
356
+ payment_data: {
357
+ data: string;
358
+ header: {
359
+ ephemeral_public_key: string;
360
+ public_key_hash: string;
361
+ transaction_id: string;
362
+ };
363
+ signature: string;
364
+ version: string;
365
+ };
366
+ payment_method: {
367
+ display_name: string;
368
+ network: string;
369
+ type: string;
370
+ };
371
+ transaction_identifier: string;
372
+ }
373
+ interface GooglePayToken {
374
+ token: string;
375
+ }
376
+ /** Terminal-ish callbacks the wallet flows report into (mirrors the iframe's ExpressAuthorizeHandlers). */
377
+ interface WalletAuthorizeHandlers {
378
+ onAuthorized: (paymentId: string) => void;
379
+ onThreeDs: (redirectUrl: string, paymentId: string) => void;
380
+ onFailed: (code: string, message: string, paymentId?: string) => void;
381
+ }
382
+
383
+ /**
384
+ * Express fast-checkout buttons (Apple Pay / Google Pay / PayPal) rendered
385
+ * DIRECTLY into the merchant's DOM — one element per wallet, Yuno-style.
386
+ * There is no iframe: wallet SDKs load on the merchant page, wallet UI
387
+ * (Apple Pay QR modal, payment sheets) overlays the page natively, and the
388
+ * page's own layout sizes the buttons. Only encrypted wallet tokens pass
389
+ * through the page; they are authorized against the session-scoped payments
390
+ * API (no API key in the browser).
391
+ */
392
+ declare function createFlintNExpressButtons(options: FlintNExpressButtonsOptions): FlintNExpressButtons;
393
+
394
+ declare const deriveApiUrl: (payOrigin: string) => string;
395
+
396
+ /**
397
+ * Per-instance express options; session/origin/onPayment come from the fields
398
+ * instance, and button styling comes from `config.styles`
399
+ * (expressButtonsBorderRadius).
400
+ */
401
+ type ExpressButtonsOptions = Pick<FlintNExpressButtonsOptions, 'apiUrl' | 'onEvent' | 'onCapability'>;
250
402
  interface FlintNFields {
251
403
  createField(fieldType: TFieldType, options?: FieldOptions): FlintNField;
252
404
  getField(fieldType: TFieldType): FlintNField | undefined;
405
+ /**
406
+ * Create the express fast-checkout buttons (Apple Pay / Google Pay /
407
+ * PayPal), rendered directly into merchant-owned elements (one per wallet —
408
+ * see `mountExpressButtons`). Terminal results flow through the same
409
+ * `onPayment` callback as the card `submit()`; per-attempt analytics and
410
+ * capability flags are delivered via the options passed here.
411
+ */
412
+ createExpressButtons(options?: ExpressButtonsOptions): FlintNExpressButtons;
253
413
  validate(): Promise<FieldsValidationResult>;
254
414
  submit(options?: SubmitOptions, skipValidation?: boolean): Promise<PaymentResult>;
255
415
  unmount(): void;
@@ -265,4 +425,4 @@ declare const validateConfig: (config: {
265
425
  }) => void;
266
426
  declare const sanitizeToken: (token: string) => string;
267
427
 
268
- export { type CheckoutEvent, CheckoutFormVariant, CheckoutView, EventType, type FieldChangeEvent, FieldEventType, type FieldOptions, type FieldState, FieldType, type FieldValidationResult, type FieldsValidationResult, type FlintNConfig, type FlintNField, type FlintNFieldInternalCallbacks, type FlintNFields, type FlintNFieldsConfig, type FlintNFieldsOptions, type FlintNPayment, type FlintNPaymentOptions, type FormFields, type FormPlaceholders, type FormStyles, type PaymentAttempt, PaymentAttemptResult, type PaymentError, PaymentMethod, type PaymentResult, type PaymentUI, PaymentUIEvent, type SubmitOptions, type TCheckoutFormVariant, type TCheckoutView, type TEventType, type TFieldEventType, type TFieldType, type TPaymentAttemptResult, type TPaymentMethod, type TPaymentUIEvent, buildIframeSrc, createFlintNField, createFlintNFields, createFlintNPayment, createIframeElement, isAttemptEvent, isUIEvent, parseOrigin, sanitizeToken, validateConfig };
428
+ export { type ApplePayConfig, type ApplePayToken, type AuthorizeResponse, AuthorizedStatus, type CheckoutEvent, CheckoutFormVariant, CheckoutView, EXPRESS_BUTTON_HEIGHT, EventType, type ExpressAttempt, type ExpressButtonMount, type ExpressButtonsOptions, type ExpressCapability, ExpressMethod, type FieldChangeEvent, FieldEventType, type FieldOptions, type FieldState, FieldType, type FieldValidationResult, type FieldsValidationResult, type FlintNConfig, type FlintNExpressButtons, type FlintNExpressButtonsOptions, type FlintNField, type FlintNFieldInternalCallbacks, type FlintNFields, type FlintNFieldsConfig, type FlintNFieldsOptions, type FlintNPayment, type FlintNPaymentOptions, type FormFields, type FormPlaceholders, type FormStyles, type GooglePayConfig, type GooglePayToken, type PayPalConfig, type PaymentAttempt, PaymentAttemptResult, type PaymentError, PaymentMethod, type PaymentResult, type PaymentUI, PaymentUIEvent, type SessionConfig, type SubmitBillingAddress, type SubmitOptions, type TAuthorizedStatus, type TCheckoutFormVariant, type TCheckoutView, type TEventType, type TExpressMethod, type TFieldEventType, type TFieldType, type TPaymentAttemptResult, type TPaymentMethod, type TPaymentUIEvent, type WalletAuthorizeHandlers, buildIframeSrc, createFlintNExpressButtons, createFlintNField, createFlintNFields, createFlintNPayment, createIframeElement, deriveApiUrl, isAttemptEvent, isUIEvent, parseOrigin, sanitizeToken, validateConfig };