flintn-checkout 0.0.15 → 0.0.17

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
 
@@ -326,7 +331,11 @@ function CheckoutForm() {
326
331
  </div>
327
332
  <div style={{ flex: 1 }}>
328
333
  <label>CVV</label>
329
- <div ref={cvvRef} style={{ height: 40, marginBottom: 4 }} />
334
+ {/* position/zIndex let the CVV helper panel overlay content below */}
335
+ <div
336
+ ref={cvvRef}
337
+ style={{ height: 40, marginBottom: 4, position: 'relative', zIndex: 1 }}
338
+ />
330
339
  {fieldErrors['cvv'] && (
331
340
  <span style={{ color: 'red' }}>{fieldErrors['cvv']}</span>
332
341
  )}
@@ -374,7 +383,7 @@ const fields = createFlintNFields({
374
383
 
375
384
  const cardNumber = fields.createField('card-number', { placeholder: '4111 1111 1111 1111' });
376
385
  const expiry = fields.createField('expiry', { placeholder: 'MM/YY' });
377
- const cvv = fields.createField('cvv', { placeholder: 'CVC' });
386
+ const cvv = fields.createField('cvv');
378
387
 
379
388
  cardNumber.mount('#card-number');
380
389
  expiry.mount('#expiry');
@@ -424,12 +433,13 @@ Additional React-only options:
424
433
  | `onChange` | `(event: FieldChangeEvent) => void` | Optional additional callback |
425
434
  | `onFocus` | `(fieldType: TFieldType) => void` | Optional additional callback |
426
435
  | `onBlur` | `(fieldType: TFieldType, state: FieldState) => void` | Optional additional callback |
436
+ | `onEvent` | `(event: ExpressAttempt) => void` | Per-attempt wallet analytics from the express buttons (see "Express buttons") |
427
437
 
428
438
  ### FieldOptions
429
439
 
430
440
  | Property | Type | Default | Description |
431
441
  |----------|------|---------|-------------|
432
- | `placeholder` | `string` | Field-specific default | Input placeholder text |
442
+ | `placeholder` | `string` | `'1234 1234 1234 1234'` / `'MM/YY'` / `'***'` | Input placeholder text |
433
443
  | `disabled` | `boolean` | `false` | Disable the input |
434
444
 
435
445
  ### React Hook Return Values
@@ -439,6 +449,10 @@ Additional React-only options:
439
449
  | `cardNumberRef` | `RefObject<HTMLDivElement>` | Ref for card number container |
440
450
  | `expiryRef` | `RefObject<HTMLDivElement>` | Ref for expiry container |
441
451
  | `cvvRef` | `RefObject<HTMLDivElement>` | Ref for CVV container |
452
+ | `applePayRef` | `RefObject<HTMLDivElement>` | Ref for the Apple Pay button container (attaching it enables the wallet) |
453
+ | `googlePayRef` | `RefObject<HTMLDivElement>` | Ref for the Google Pay button container (attaching it enables the wallet) |
454
+ | `payPalRef` | `RefObject<HTMLDivElement>` | Ref for the PayPal button container (attaching it enables the wallet) |
455
+ | `expressAvailable` | `boolean` | True once wallet detection settles and at least one express button is renderable |
442
456
  | `isReady` | `boolean` | All fields loaded and ready |
443
457
  | `fieldErrors` | `Partial<Record<TFieldType, string \| null>>` | Per-field validation errors (populated after first submit) |
444
458
  | `fieldStates` | `Partial<Record<TFieldType, FieldState>>` | Per-field state (always up to date) |
@@ -459,6 +473,7 @@ Validation mirrors react-hook-form `mode: 'onSubmit'`:
459
473
  - After first submit: errors update on every field change (revalidation)
460
474
  - Card number changes trigger CVV revalidation (CVV length depends on card brand)
461
475
  - All three fields (card number, expiry, CVV) are required — submitting with a missing field returns a `MISSING_FIELDS` error
476
+ - The CVV field ships a built-in helper: tapping the card icon expands a “where to find CVV” panel below the input. The field iframe grows to fit it, so keep the CVV container at a fixed `height` with `position: relative; z-index: 1` — the open panel then overlays the content below (like a popover) instead of pushing your layout
462
477
 
463
478
  ### Field Types
464
479
 
@@ -466,7 +481,7 @@ Validation mirrors react-hook-form `mode: 'onSubmit'`:
466
481
  |-------|-------------|
467
482
  | `'card-number'` | Card number input with automatic formatting and brand detection |
468
483
  | `'expiry'` | Expiry date input (MM/YY format) |
469
- | `'cvv'` | CVV/CVC input (3 or 4 digits depending on card brand) |
484
+ | `'cvv'` | CVV/CVC input (3 or 4 digits depending on card brand). Includes a built-in “where to find CVV” helper: tapping the card icon expands an explainer panel below the input |
470
485
 
471
486
  ### Individual Field Methods (Vanilla JS)
472
487
 
@@ -479,6 +494,105 @@ Validation mirrors react-hook-form `mode: 'onSubmit'`:
479
494
  | `field.getState()` | Get current field state |
480
495
  | `field.getFieldType()` | Get the field type |
481
496
 
497
+ ### Express buttons (Apple Pay / Google Pay / PayPal)
498
+
499
+ Hosted Fields can also render **fast checkout buttons** so buyers can pay with a
500
+ wallet instead of typing card details. The buttons render **directly into your
501
+ DOM** — one element per wallet, no iframe. Your page's own layout sizes and
502
+ positions each button; wallet UI (the Apple Pay sheet or desktop QR modal, the
503
+ Google Pay sheet, the PayPal popup) overlays your page natively. Only encrypted
504
+ wallet tokens pass through the page; they are authorized against the
505
+ session-scoped payments API, so no API key ever reaches the browser. Terminal
506
+ results arrive through the **same `onPayment`** callback as the card `submit()`.
507
+
508
+ **React**
509
+
510
+ ```tsx
511
+ import { useFlintNFields } from 'flintn-checkout/react';
512
+
513
+ function CheckoutForm() {
514
+ const {
515
+ cardNumberRef, expiryRef, cvvRef,
516
+ applePayRef, googlePayRef, payPalRef, // one container per wallet
517
+ expressAvailable, // true once ≥1 wallet button is renderable
518
+ submit,
519
+ } = useFlintNFields({
520
+ config: { clientSessionId: 'your_client_session_id' },
521
+ onPayment: (result) => {
522
+ if (result.status === 'PAYMENT_SUCCESS') console.log('Paid:', result.data);
523
+ },
524
+ onEvent: (e) => console.log('express attempt:', e.event, e.method),
525
+ });
526
+
527
+ return (
528
+ <form onSubmit={(e) => { e.preventDefault(); submit(); }}>
529
+ {/* attach a ref to enable that wallet; unavailable wallets render
530
+ nothing — collapse empty containers via CSS if they carry spacing */}
531
+ <div ref={applePayRef} />
532
+ <div ref={googlePayRef} />
533
+ <div ref={payPalRef} />
534
+ {expressAvailable && <div className="divider">or pay by card</div>}
535
+
536
+ <div ref={cardNumberRef} style={{ height: 40 }} />
537
+ <div ref={expiryRef} style={{ height: 40 }} />
538
+ <div ref={cvvRef} style={{ height: 40 }} />
539
+ <button type="submit">Pay</button>
540
+ </form>
541
+ );
542
+ }
543
+ ```
544
+
545
+ **Vanilla JavaScript**
546
+
547
+ ```javascript
548
+ import { createFlintNFields } from 'flintn-checkout';
549
+
550
+ const fields = createFlintNFields({
551
+ config: { clientSessionId: 'your_client_session_id' },
552
+ onPayment: (result) => console.log('Payment result:', result),
553
+ });
554
+
555
+ const express = fields.createExpressButtons({
556
+ onCapability: (cap) => console.log('available wallets:', cap),
557
+ onEvent: (attempt) => console.log('express attempt:', attempt),
558
+ });
559
+
560
+ await express.mountExpressButtons([
561
+ { method: 'APPLE_PAY', elementSelector: '#apple-pay' },
562
+ { method: 'GOOGLE_PAY', elementSelector: '#google-pay' },
563
+ { method: 'PAYPAL', elementSelector: '#paypal' },
564
+ ]);
565
+
566
+ // unmount a single wallet, or all of them
567
+ express.unmountExpressButton('APPLE_PAY');
568
+ express.unmountAllExpressButtons();
569
+
570
+ // card fields as usual
571
+ fields.createField('card-number').mount('#card-number');
572
+ fields.createField('expiry').mount('#expiry');
573
+ fields.createField('cvv').mount('#cvv');
574
+ ```
575
+
576
+ Notes:
577
+
578
+ - **No `submit()` for express.** Wallet buttons self-trigger (the buyer taps Apple
579
+ Pay and the sheet opens). Success/error come through `onPayment`; per-attempt
580
+ analytics through the top-level `onEvent` (React) /
581
+ `createExpressButtons({ onEvent })` (vanilla).
582
+ - **Availability.** Buttons for methods that are unavailable on this
583
+ device/session are silently skipped at mount — their containers just stay
584
+ empty. `expressAvailable` (React) / the `onCapability` callback (vanilla)
585
+ gate your "or pay by card" divider.
586
+ - **Which wallets appear** is driven entirely by the merchant session config
587
+ (`payment_methods`) intersected with device support — there is no client-side
588
+ allow-list.
589
+ - **3DS** for wallet payments is handled automatically via a popup (make sure the
590
+ buyer's click isn't intercepted by a popup blocker).
591
+ - **Apple Pay domain registration.** Your live domain must be registered with
592
+ Apple (handled during merchant onboarding) — merchant validation runs against
593
+ the domain the buttons render on. On non-Safari browsers Apple's JS SDK shows
594
+ its QR-code modal over your page. Google Pay and PayPal have no such limits.
595
+
482
596
  ---
483
597
 
484
598
  ## Shared Types
@@ -503,6 +617,39 @@ interface PaymentError {
503
617
  }
504
618
  ```
505
619
 
620
+ ### Error handling
621
+
622
+ Errors surface through three separate channels — don't conflate them:
623
+
624
+ | Channel | What lands there | Show as |
625
+ |---------|------------------|---------|
626
+ | `error` / `onError` | Infrastructure: SDK failed to initialize, a container is missing, the session config couldn't load | Page-level banner ("checkout unavailable / session expired") |
627
+ | `paymentResult` / `onPayment` with `PAYMENT_ERROR` | A payment attempt failed (card or wallet) | Inline payment error, offer retry |
628
+ | `fieldErrors` | Per-field validation (populated after the first `submit()`/`validate()`) | Messages next to each field |
629
+
630
+ Common `code` values:
631
+
632
+ | Code | Channel | Meaning |
633
+ |------|---------|---------|
634
+ | `INIT_ERROR` | error | SDK could not initialize (missing `clientSessionId`, bad `origin`) |
635
+ | `MOUNT_ERROR` | error | A field/button container was not found or failed to mount |
636
+ | `SESSION_EXPIRED` (and other API codes) | error or payment | Passed through from the payments API as-is |
637
+ | `CONFIG_ERROR` | error | Session config failed to load and the API gave no code |
638
+ | `NETWORK_ERROR` / `HTTP_<status>` / `INVALID_RESPONSE` | error or payment | Network failure / HTTP error without an API code / malformed response |
639
+ | `VALIDATION_ERROR` | payment | `submit()` blocked by field validation (details in `fieldErrors`) |
640
+ | `MISSING_FIELDS` / `NOT_INITIALIZED` / `SUBMIT_IN_PROGRESS` / `SUBMIT_TIMEOUT` | payment | Card `submit()` preconditions / duplicate call / no response in time (30s) |
641
+ | `THREE_DS_TIMEOUT` | payment | A 3DS challenge stayed unresolved for 10 minutes |
642
+ | `PAYMENT_DECLINED` / processor decline codes | payment | Declined; the processor's own code is passed through when present |
643
+ | `PAYMENT_FAILED` | payment | Non-declined failure (including unexpected 3DS outcomes) |
644
+ | `APPLE_PAY_SDK_ERROR` / `GOOGLE_PAY_SDK_ERROR` / `PAYPAL_ERROR` | payment | The wallet SDK failed mid-flow |
645
+ | `PAYPAL_MISSING_PAYMENT_ID` | payment | PayPal approved but no payment id was captured (anomaly) |
646
+ | `THREE_DS_FAILED` / `THREE_DS_CANCELLED` | payment | Challenge declined / dismissed by the buyer |
647
+ | `THREE_DS_POPUP_BLOCKED` | payment | Express buttons only — the wallet 3DS window was blocked by the browser |
648
+
649
+ Worth handling specifically: `SESSION_EXPIRED` (create a fresh session and remount),
650
+ `THREE_DS_POPUP_BLOCKED` (ask the buyer to allow popups), `VALIDATION_ERROR`
651
+ (highlight fields from `fieldErrors`). Everything else is safely generic.
652
+
506
653
  ### CheckoutEvent
507
654
 
508
655
  ```typescript
@@ -683,8 +830,8 @@ on `createField` (or the `fields` prop of `useFlintNFields`) instead.
683
830
  | `loaderColor` | ✅ | — | Loading spinner color |
684
831
  | `backgroundColor` | ✅ | — | Form background color |
685
832
  | `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`) |
833
+ | `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 |
834
+ | `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
835
  | `inputBackgroundColor` | ✅ | ✅ | Input field background color |
689
836
  | `inputBorderRadius` | ✅ | ✅ | Input field border radius |
690
837
  | `inputBorderColor` | ✅ | ✅ | Default input border color |
@@ -754,6 +901,7 @@ Every cancellation still emits a `CANCELLED` `onEvent` regardless of variant, so
754
901
 
755
902
  ### Hosted Fields
756
903
  - Credit/Debit Cards (Visa, Mastercard, Amex, Discover)
904
+ - Apple Pay, Google Pay, PayPal (via the express buttons — see "Express buttons" above)
757
905
 
758
906
  ## Debug Mode
759
907
 
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 };