@flopay/react 1.7.0 → 1.8.2
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 +13 -78
- package/dist/card-setup-entry.cjs +2 -2
- package/dist/card-setup-entry.mjs +1 -1
- package/dist/chunk-W24FSJC5.mjs +6 -0
- package/dist/index.cjs +9 -9
- package/dist/index.d.cts +7 -134
- package/dist/index.d.ts +7 -134
- package/dist/index.mjs +8 -8
- package/package.json +3 -3
- package/dist/chunk-VWT2TDA6.mjs +0 -6
- package/dist/payment-logos/alipay.svg +0 -1
- package/dist/payment-logos/bancontact.svg +0 -1
- package/dist/payment-logos/blik.svg +0 -1
- package/dist/payment-logos/eps.svg +0 -1
- package/dist/payment-logos/giropay.svg +0 -1
- package/dist/payment-logos/ideal.svg +0 -1
- package/dist/payment-logos/klarna.svg +0 -1
- package/dist/payment-logos/p24.svg +0 -1
- package/dist/payment-logos/sepa_debit.svg +0 -1
- package/dist/payment-logos/wechat_pay.svg +0 -1
package/README.md
CHANGED
|
@@ -285,12 +285,15 @@ the entire integration change — no SDK redeploy or consumer code update is
|
|
|
285
285
|
required. Wallets only render on supported devices regardless of dashboard
|
|
286
286
|
state (Apple Pay on Safari/macOS/iOS, Google Pay on Chrome).
|
|
287
287
|
|
|
288
|
-
> **
|
|
289
|
-
>
|
|
290
|
-
>
|
|
291
|
-
>
|
|
292
|
-
>
|
|
293
|
-
> a
|
|
288
|
+
> **Payment-logo CDN and CSP:** APM brand logos load from the versioned,
|
|
289
|
+
> FloPay-owned URL `https://cdn.flopay.com/sdk-logos/v1/`. Plain absolute HTTPS
|
|
290
|
+
> URLs work in Vite with no SDK middleware or `optimizeDeps` override, and retain
|
|
291
|
+
> the same behavior in webpack 5, Rollup, Next, and esbuild. Logos stay out of
|
|
292
|
+
> the package and JavaScript bundle; card-only and vault checkouts never request
|
|
293
|
+
> one. Merchants with a strict Content Security Policy must allow the host in
|
|
294
|
+
> the image directive, for example `img-src 'self' https://cdn.flopay.com;`.
|
|
295
|
+
> A failed or CSP-blocked logo request falls back to the method's existing
|
|
296
|
+
> inline monogram, so the tile remains branded without another network request.
|
|
294
297
|
|
|
295
298
|
In `layout="buttons"`, the **Credit / Debit Card** button always opens the
|
|
296
299
|
inline card form. External wallets and PayPal keep their own lifecycle:
|
|
@@ -791,57 +794,13 @@ The SDK exposes the relevant pieces in three ways:
|
|
|
791
794
|
|
|
792
795
|
- **`DirectPayPalButton`** (standalone): can be rendered outside `SplitCardForm` when you only need the PayPal button. Pass `clientId`, `currency`, `environment`, `isSubscription`, and the backend-advertised `providerObjectType` when present. `isSubscription` remains the compatibility fallback when the field is absent.
|
|
793
796
|
|
|
794
|
-
- **`PayPalButton`** (standalone): Must be rendered inside its own `FloPayProvider`:
|
|
795
|
-
|
|
796
|
-
```tsx
|
|
797
|
-
{/* Main checkout provider */}
|
|
798
|
-
<FloPayProvider flopay={flopay} options={{ amount, currency }}>
|
|
799
|
-
<SplitCardForm ... />
|
|
800
|
-
</FloPayProvider>
|
|
801
|
-
|
|
802
|
-
{/* PayPal provider (separate instance) */}
|
|
803
|
-
<FloPayProvider flopay={flopay} options={{ amount, currency, paymentMethodCreation: 'auto' }}>
|
|
804
|
-
<PayPalButton
|
|
805
|
-
sessionId="session_uuid"
|
|
806
|
-
billingApiUrl="https://billing.example.com"
|
|
807
|
-
email="user@example.com"
|
|
808
|
-
onComplete={() => router.push('/success')}
|
|
809
|
-
/>
|
|
810
|
-
</FloPayProvider>
|
|
811
|
-
```
|
|
812
|
-
|
|
813
|
-
### Using Individual Non-card Elements
|
|
814
|
-
|
|
815
|
-
```tsx
|
|
816
|
-
import { AddressElement, PaymentElement } from '@flopay/react';
|
|
817
|
-
|
|
818
|
-
function CustomForm() {
|
|
819
|
-
return (
|
|
820
|
-
<div>
|
|
821
|
-
<PaymentElement
|
|
822
|
-
options={{
|
|
823
|
-
layout: 'tabs',
|
|
824
|
-
paymentMethodTypes: ['cashapp', 'ideal'],
|
|
825
|
-
}}
|
|
826
|
-
/>
|
|
827
|
-
<AddressElement />
|
|
828
|
-
</div>
|
|
829
|
-
);
|
|
830
|
-
}
|
|
831
|
-
```
|
|
832
|
-
|
|
833
|
-
These elements support wallet/APM and address collection. `PaymentElement`
|
|
834
|
-
requires a non-empty `paymentMethodTypes` allowlist and rejects `card`; use the
|
|
835
|
-
hosted vault checkout surface for card payments.
|
|
836
|
-
|
|
837
797
|
### Using Hooks
|
|
838
798
|
|
|
839
799
|
```tsx
|
|
840
|
-
import { useFloPay,
|
|
800
|
+
import { useFloPay, useCheckout } from '@flopay/react';
|
|
841
801
|
|
|
842
802
|
function PaymentStatus() {
|
|
843
803
|
const flopay = useFloPay(); // FloPay | null
|
|
844
|
-
const elements = useElements(); // FloPayElements | null
|
|
845
804
|
const checkout = useCheckout(); // { session, loading, error, claimPending }
|
|
846
805
|
|
|
847
806
|
if (!flopay) return <div>Loading SDK...</div>;
|
|
@@ -855,22 +814,18 @@ function PaymentStatus() {
|
|
|
855
814
|
|
|
856
815
|
| Component | Description |
|
|
857
816
|
|-----------|-------------|
|
|
858
|
-
| `FloPayProvider` | Context provider. Accepts `flopay` (instance or promise), `options?`, and `children`.
|
|
817
|
+
| `FloPayProvider` | Context provider. Accepts `flopay` (instance or promise), optional `paypalFlopay`, `options?`, and `children`. |
|
|
859
818
|
| `FloPayCheckout` | Recommended self-contained session checkout. Resolves gateways, mounts hosted-vault cards, and preserves wallets/APMs/PayPal/saved-payment flows. |
|
|
860
819
|
| `FloPayCardSetup` | Browser-only no-charge card verification for a merchant-server-created setup session. Requires `sessionId` + `nonce`; reports ready, verified completion, decline, validation, retryable technical error, and cancellation outcomes. |
|
|
861
820
|
| `SplitCardForm` | Advanced checkout surface combining hosted-vault cards with wallets, APMs, and PayPal. Supports `ref` for imperative next-action handling. |
|
|
862
821
|
| `VaultCardFields` | Hosted vault PCI card fields. Used internally by `SplitCardForm` on the vault path; consumes a `CardCaptureAdapter` from `useFloPay().cardCapture()`. |
|
|
863
|
-
| `PayPalButton` | Standalone PayPal button. Requires its own `FloPayProvider` with `paymentMethodCreation` set to something other than `'manual'`. |
|
|
864
822
|
| `DirectPayPalButton` | Standalone direct PayPal Order, provider-managed Subscription, or setup-token button using the session-scoped intent contract. |
|
|
865
|
-
| `PaymentElement` | Provider element for an explicitly declared non-card `paymentMethodTypes` allowlist. |
|
|
866
|
-
| `AddressElement` | Address input element |
|
|
867
823
|
|
|
868
824
|
### Hooks
|
|
869
825
|
|
|
870
826
|
| Hook | Returns | Description |
|
|
871
827
|
|------|---------|-------------|
|
|
872
828
|
| `useFloPay()` | `FloPay \| null` | Current FloPay instance from context. `null` while loading. |
|
|
873
|
-
| `useElements()` | `FloPayElements \| null` | Current elements group from context. `null` while loading. |
|
|
874
829
|
| `useCheckout()` | `CheckoutState` | `{ session, loading, error, claimPending }` from CheckoutContext. `claimPending` remains true while a detached shell is not safe to charge. |
|
|
875
830
|
|
|
876
831
|
### FloPayProviderProps
|
|
@@ -878,12 +833,8 @@ function PaymentStatus() {
|
|
|
878
833
|
| Prop | Type | Description |
|
|
879
834
|
|------|------|-------------|
|
|
880
835
|
| `flopay` | `Promise<FloPay> \| FloPay` | SDK instance or promise from `loadFloPay()` |
|
|
881
|
-
| `
|
|
882
|
-
| `options.
|
|
883
|
-
| `options.clientSecret` | `string?` | Existing non-card PaymentIntent or SetupIntent secret; the SDK verifies the provider intent against `PaymentElement`'s explicit wallet/APM allowlist before mounting and rejects card or undeclared methods. Card checkout uses the hosted vault. |
|
|
884
|
-
| `options.amount` | `number?` | Amount in cents for deferred non-card Elements without a `clientSecret` |
|
|
885
|
-
| `options.currency` | `string?` | ISO 4217 currency code for deferred non-card Elements without a `clientSecret` |
|
|
886
|
-
| `options.paymentMethodCreation` | `'manual' \| 'auto'` | How payment methods are created |
|
|
836
|
+
| `paypalFlopay` | `Promise<FloPay> \| FloPay \| null` | Optional Stripe instance used for the Stripe-rendered PayPal redirect leg. |
|
|
837
|
+
| `options.billingApiUrl` | `string?` | Billing API base URL exposed to child components. |
|
|
887
838
|
| `onInstrument` | `(event: FloInstrumentEvent) => void` | Versioned, privacy-safe checkout funnel and phased error feed for merchant analytics. |
|
|
888
839
|
|
|
889
840
|
### FloPayCardSetupProps
|
|
@@ -998,20 +949,6 @@ Backend rollout verification should confirm that create/read/replay no longer
|
|
|
998
949
|
invoke PCIVault and that capture issuance occurs only through the deferred
|
|
999
950
|
endpoint when a buyer enters the card path.
|
|
1000
951
|
|
|
1001
|
-
### ElementComponentProps (shared by all element components)
|
|
1002
|
-
|
|
1003
|
-
| Prop | Type | Description |
|
|
1004
|
-
|------|------|-------------|
|
|
1005
|
-
| `className` | `string?` | CSS class for wrapper div |
|
|
1006
|
-
| `id` | `string?` | HTML id for wrapper div |
|
|
1007
|
-
| `style` | `CSSProperties?` | Inline styles for wrapper div |
|
|
1008
|
-
| `options` | `Partial<ElementOptions>?` | Options for the underlying element |
|
|
1009
|
-
| `onChange` | `(event: ElementChangeEvent) => void` | Value change handler |
|
|
1010
|
-
| `onReady` | `() => void` | Element ready handler |
|
|
1011
|
-
| `onFocus` | `() => void` | Focus handler |
|
|
1012
|
-
| `onBlur` | `() => void` | Blur handler |
|
|
1013
|
-
| `onEscape` | `() => void` | Escape key handler |
|
|
1014
|
-
|
|
1015
952
|
### Types
|
|
1016
953
|
|
|
1017
954
|
| Type | Description |
|
|
@@ -1023,7 +960,5 @@ endpoint when a buyer enters the card path.
|
|
|
1023
960
|
| `FloPayCardSetupCancelEvent` | Unmount-before-terminal cancellation |
|
|
1024
961
|
| `FloPayCardSetupError` | Structured setup failure with stable `code` and `retryable` classification |
|
|
1025
962
|
| `SplitCardFormProps` | Props for `SplitCardForm` |
|
|
1026
|
-
| `PayPalButtonProps` | Props for `PayPalButton` |
|
|
1027
|
-
| `ElementComponentProps` | Shared props for all element components |
|
|
1028
963
|
| `CheckoutState` | `{ session, loading, error, claimPending }` |
|
|
1029
964
|
| `FloInstrumentEvent` | Versioned merchant instrument union with seven lifecycle names, four `checkout_error` phases, and an optional gateway. |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var U=Object.defineProperty;var oe=Object.getOwnPropertyDescriptor;var ne=Object.getOwnPropertyNames;var ae=Object.prototype.hasOwnProperty;var se=(e,t)=>{for(var o in t)U(e,o,{get:t[o],enumerable:!0})},ie=(e,t,o,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of ne(t))!ae.call(e,r)&&r!==o&&U(e,r,{get:()=>t[r],enumerable:!(n=oe(t,r))||n.enumerable});return e};var le=e=>ie(U({},"__esModule",{value:!0}),e);var pe={};se(pe,{FloPayCardSetup:()=>re,FloPayCardSetupError:()=>T});module.exports=le(pe);var O=require("@flopay/js/card-setup"),c=require("@flopay/shared"),a=require("react");function R(e){if(!e)return;let t=o=>{console.error("[FloPay] Merchant callback failed; checkout continued.",o)};try{Promise.resolve(e()).catch(t)}catch(o){t(o)}}var i=require("react/jsx-runtime");function j({status:e,errorMessage:t,authorized:o=!1,processingLabel:n="PROCESSING...",successLabel:r,errorLabel:m="PAYMENT FAILED",successNote:u="You will be automatically redirected, do not close or navigate away from this window."}){return(0,i.jsx)("div",{"data-testid":"flopay-processing-overlay","data-status":e,role:"dialog","aria-modal":"true",style:{position:"fixed",inset:0,background:"rgba(0,0,0,0.35)",display:"flex",alignItems:"center",justifyContent:"center",zIndex:1e3,backdropFilter:"blur(2px)"},children:(0,i.jsxs)("div",{style:{background:"white",borderRadius:12,padding:"2rem 2.5rem",textAlign:"center",boxShadow:"0 8px 32px rgba(0,0,0,0.18)",minWidth:240,display:"flex",flexDirection:"column",alignItems:"center",gap:16},children:[(0,i.jsxs)("div",{style:{width:48,height:48,position:"relative"},children:[e==="processing"&&(0,i.jsxs)("svg",{width:"48",height:"48",viewBox:"0 0 24 24",fill:"none",xmlns:"http://www.w3.org/2000/svg",style:{animation:"flopay-spin 0.8s linear infinite"},children:[(0,i.jsx)("circle",{cx:"12",cy:"12",r:"10",stroke:"#e5e7eb",strokeWidth:"3"}),(0,i.jsx)("path",{d:"M4 12a8 8 0 018-8v3a5 5 0 00-5 5H4z",fill:"#4A49FF"})]}),e==="success"&&(0,i.jsx)("div",{style:{animation:"flopay-pop 0.4s cubic-bezier(0.34,1.56,0.64,1)"},children:(0,i.jsxs)("svg",{width:"48",height:"48",viewBox:"0 0 24 24",fill:"none",xmlns:"http://www.w3.org/2000/svg",children:[(0,i.jsx)("circle",{cx:"12",cy:"12",r:"11",fill:"#22c55e"}),(0,i.jsx)("path",{d:"M7 12.5l3 3 7-7",stroke:"white",strokeWidth:"2.5",strokeLinecap:"round",strokeLinejoin:"round",style:{strokeDasharray:20,strokeDashoffset:20,animation:"flopay-draw 0.4s 0.15s ease forwards"}})]})}),e==="error"&&(0,i.jsx)("div",{style:{animation:"flopay-shake 0.4s ease"},children:(0,i.jsxs)("svg",{width:"48",height:"48",viewBox:"0 0 24 24",fill:"none",xmlns:"http://www.w3.org/2000/svg",children:[(0,i.jsx)("circle",{cx:"12",cy:"12",r:"11",fill:"#ef4444"}),(0,i.jsx)("path",{d:"M8 8l8 8M16 8l-8 8",stroke:"white",strokeWidth:"2.5",strokeLinecap:"round",style:{strokeDasharray:12,strokeDashoffset:12,animation:"flopay-draw 0.3s 0.1s ease forwards"}})]})})]}),(0,i.jsxs)("span",{style:{fontSize:14,fontWeight:600,letterSpacing:"0.05em",color:e==="success"?"#16a34a":e==="error"?"#dc2626":"#374151"},children:[e==="processing"&&n,e==="success"&&(r??(o?"PAYMENT AUTHORISED":"PAYMENT SUCCESSFUL")),e==="error"&&m]}),e==="success"&&u!==null&&(0,i.jsx)("p",{style:{fontSize:13,color:"#6b7280",fontWeight:400,maxWidth:260,lineHeight:1.4,margin:0},children:u}),e==="error"&&t&&(0,i.jsx)("p",{style:{fontSize:13,color:"#6b7280",fontWeight:400,maxWidth:260,lineHeight:1.4,margin:0},children:t}),(0,i.jsx)("style",{children:`
|
|
2
2
|
@keyframes flopay-spin { to { transform: rotate(360deg); } }
|
|
3
3
|
@keyframes flopay-pop { 0% { transform: scale(0); } 100% { transform: scale(1); } }
|
|
4
4
|
@keyframes flopay-draw { to { stroke-dashoffset: 0; } }
|
|
5
5
|
@keyframes flopay-shake { 0%,100% { transform: translateX(0); } 20%,60% { transform: translateX(-4px); } 40%,80% { transform: translateX(4px); } }
|
|
6
|
-
`})]})})}var S=require("react"),
|
|
6
|
+
`})]})})}var S=require("react"),K=require("react/jsx-runtime");function J({capture:e,html:t,messageToken:o,expectedOrigin:n,theme:r,containerStyle:m,onReady:u,onError:y,onValidation:g}){let C=(0,S.useRef)(null),x=(0,S.useRef)(u),w=(0,S.useRef)(y),k=(0,S.useRef)(g);x.current=u,w.current=y,k.current=g;let d=(0,S.useRef)(r);return d.current=r,(0,S.useEffect)(()=>{let F=C.current;if(!F)return;let v=!0,A=!1,V=()=>{!v||A||(A=!0,x.current?.())},D=e.on("ready",()=>{V()}),L=e.on("error",p=>{w.current?.(p.message??"There was a problem loading the secure card form.")}),h=e.on("validation",p=>{k.current?.(p.message??null)}),I={html:t,...o?{messageToken:o}:{},...n?{expectedOrigin:n}:{},...d.current?{theme:d.current}:{}};return e.mount(F,I).then(()=>{V()}).catch(p=>{v&&w.current?.(p instanceof Error?p.message:"Failed to load the secure card form.")}),()=>{v=!1,D(),L(),h(),e.unmount()}},[e,t,o,n]),(0,S.useEffect)(()=>{r&&e.applyTheme?.(r)},[e,r]),(0,K.jsx)("div",{ref:C,"data-testid":"flopay-vault-card-fields",style:m})}function ce(e,t=.12){let o=/^#?([0-9a-fA-F]{6})$/.exec(e.trim());if(!o)return e;let n=parseInt(o[1],16),r=Math.max(0,Math.min(1,1-t)),m=Math.round((n>>16&255)*r),u=Math.round((n>>8&255)*r),y=Math.round((n&255)*r);return`#${(1<<24|m<<16|u<<8|y).toString(16).slice(1)}`}function ue(e){if(typeof e=="number"||typeof e=="string")return e}function Q({appearance:e,buttonsStyles:t,layout:o="default"}){let n=e?.variables,r=C=>typeof C=="string"&&C?C:void 0,m=t.submitButton,u=r(n?.colorPrimary)??r(m?.backgroundColor)??"#4A49FF",y=o==="buttons",g=t.nameInput;return{primaryColor:u,primaryHoverColor:r(n?.colorPrimaryHover)??ce(u,.12),inputBackgroundColor:r(t.cardInputBackground)??r(n?.colorBackground)??"#ffffff",textColor:r(t.cardInputColor)??r(g?.color)??r(n?.colorText)??"#262833",borderColor:r(t.cardInputBorder)??(y?"#e5e7eb":"#A4A4FF"),placeholderColor:r(t.cardInputPlaceholderColor)??"#9ca3af",errorColor:r(n?.colorDanger)??"#dc2626",successColor:"#16a34a",fontFamily:r(g?.fontFamily)??r(n?.fontFamily)??"Poppins, sans-serif",fontSize:r(t.cardInputFontSize)??r(n?.fontSizeBase)??"16px",fontWeight:String(ue(g?.fontWeight)??400),borderRadius:r(n?.borderRadius)??"8px"}}var f=require("react/jsx-runtime"),T=class extends c.FloPayError{constructor(t,o,n){super(t,o,n),this.name="FloPayCardSetupError",this.retryable=n.retryable}};function ee(e,t={}){let o=e instanceof c.FloPayError?e:void 0,n=o?.statusCode,r=t.retryable??(o?.type==="network_error"||o?.type==="rate_limit_error"||typeof n=="number"&&n>=500||o?.code==="vault_capture_timeout"||!o);return new T(o?.message??(e instanceof Error?e.message:"Failed to load the secure card form."),o?.type??"api_error",{code:t.code??o?.code??"card_setup_load_failed",retryable:r,...o?.param?{param:o.param}:{},...n!==void 0?{statusCode:n}:{}})}function re({sessionId:e,nonce:t,billingApiUrl:o,telemetry:n,theme:r="modern-light",containerStyle:m,loading:u=null,onReady:y,onComplete:g,onDecline:C,onCancel:x,onValidation:w,onError:k}){let[d,F]=(0,a.useState)(null),[v,A]=(0,a.useState)(null),[V,D]=(0,a.useState)(null),[L,h]=(0,a.useState)(null),[I,p]=(0,a.useState)(null),P=(0,a.useRef)(!1),B=(0,a.useRef)(!1),te=(0,a.useMemo)(()=>{if(typeof r=="object")return r;let s=(0,c.resolveTheme)(r);if(s)return Q({appearance:s.appearance,buttonsStyles:s.buttonsLayout,layout:"default"})},[r]),H=(0,a.useRef)(y),N=(0,a.useRef)(k),G=(0,a.useRef)(g),q=(0,a.useRef)(C),X=(0,a.useRef)(x),$=(0,a.useRef)(w),_=(0,a.useRef)(!1),W=(0,a.useRef)(!1),Y=(0,a.useRef)(0);return H.current=y,N.current=k,G.current=g,q.current=C,X.current=x,$.current=w,(0,a.useEffect)(()=>{let s=Y.current+1;return Y.current=s,()=>{queueMicrotask(()=>{Y.current===s&&(_.current||W.current||(W.current=!0,R(()=>{X.current?.({status:"cancelled",sessionId:e})})))})}},[e]),(0,a.useEffect)(()=>{let s=!0,b=new O.PaymentAPI((0,c.resolveBillingApiUrl)(o),{telemetry:n});return F(null),A(null),D(null),_.current=!1,W.current=!1,(async()=>{try{if(!e.trim())throw new c.FloPayError("FloPayCardSetup requires a sessionId.","validation_error",{code:"MissingCheckoutSessionId",param:"sessionId"});if(!t.trim())throw new c.FloPayError("FloPayCardSetup requires a session nonce.","validation_error",{code:"MissingCheckoutSessionToken",param:"nonce"});let M=await b.getUnifiedCheckoutSession(e,t);if(!s)return;let E=M.data.session;if(!E||!(0,c.isCardSetupCheckoutSession)(E))throw new c.FloPayError("FloPayCardSetup only accepts a zero-amount, productless setup session.","validation_error",{code:"InvalidCardSetupSession",param:"sessionId"});let l=E.vault;if(l?.html?.trim()||(l=await b.getVaultCapture(e,t)),!l.html?.trim())throw new c.FloPayError("FloPay: this setup session has no vault card capability. Upgrade the billing backend integration.","api_error",{code:"UnsupportedBackendVaultCapability"});if(!s)return;A(l),F(new O.PciVaultCardCapture({sessionId:e,operation:"card_setup",telemetry:n}))}catch(M){if(!s)return;let E=ee(M);_.current=!0,D(E),R(()=>N.current?.(E))}})(),()=>{s=!1,b.destroy()}},[o,t,e,n]),(0,a.useEffect)(()=>{if(!d)return;let s=!1;B.current=!1,P.current=!1;let b=d.on("submitting",()=>{P.current=!0,p(null),h("processing")}),M=d.on("complete",l=>{_.current=!0,P.current=!1,h("success"),(async()=>(await new Promise(z=>setTimeout(z,1200)),!B.current&&(B.current=!0,R(()=>{G.current?.({status:"succeeded",sessionId:e,...l.paymentMethod?{paymentMethod:l.paymentMethod}:{}})}),s||h(null))))()}),E=d.on("decline",l=>{_.current=!0,P.current=!1,p(l.message??null),h("error"),R(()=>{q.current?.({status:"declined",sessionId:e,...l.declineReason?{reason:l.declineReason}:{},...l.message?{message:l.message}:{}})}),(async()=>(await new Promise(z=>setTimeout(z,1500)),s||h(null)))()});return()=>{s=!0,b(),M(),E()}},[d,e]),V?(0,f.jsx)("div",{role:"alert",children:V.message}):!d||!v?.html?(0,f.jsx)(f.Fragment,{children:u}):(0,f.jsxs)(f.Fragment,{children:[L&&(0,f.jsx)(j,{status:L,errorMessage:I,processingLabel:"SAVING CARD...",successLabel:"CARD SAVED",errorLabel:"CARD NOT SAVED",successNote:null}),(0,f.jsx)(J,{capture:d,html:v.html,messageToken:v.messageToken,expectedOrigin:v.expectedOrigin,theme:te,containerStyle:m,onReady:()=>{R(()=>H.current?.())},onValidation:s=>{R(()=>$.current?.(s))},onError:s=>{if(!s)return;P.current&&(P.current=!1,p(s),h("error"),setTimeout(()=>h(null),1500));let b=ee(new Error(s),{code:"card_setup_widget_failed",retryable:!0});R(()=>N.current?.(b))}})]})}0&&(module.exports={FloPayCardSetup,FloPayCardSetupError});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{g as e,h as t}from"./chunk-
|
|
1
|
+
import{g as e,h as t}from"./chunk-W24FSJC5.mjs";export{t as FloPayCardSetup,e as FloPayCardSetupError};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import{useEffect as J,useRef as M}from"react";import{jsx as ne}from"react/jsx-runtime";function K({capture:e,html:n,messageToken:t,expectedOrigin:o,theme:r,containerStyle:f,onReady:i,onError:p,onValidation:m}){let y=M(null),k=M(i),R=M(p),x=M(m);k.current=i,R.current=p,x.current=m;let c=M(r);return c.current=r,J(()=>{let P=y.current;if(!P)return;let h=!0,F=!1,A=()=>{!h||F||(F=!0,k.current?.())},L=e.on("ready",()=>{A()}),D=e.on("error",d=>{R.current?.(d.message??"There was a problem loading the secure card form.")}),g=e.on("validation",d=>{x.current?.(d.message??null)}),I={html:n,...t?{messageToken:t}:{},...o?{expectedOrigin:o}:{},...c.current?{theme:c.current}:{}};return e.mount(P,I).then(()=>{A()}).catch(d=>{h&&R.current?.(d instanceof Error?d.message:"Failed to load the secure card form.")}),()=>{h=!1,L(),D(),g(),e.unmount()}},[e,n,t,o]),J(()=>{r&&e.applyTheme?.(r)},[e,r]),ne("div",{ref:y,"data-testid":"flopay-vault-card-fields",style:f})}import{PaymentAPI as le,PciVaultCardCapture as ce}from"@flopay/js/card-setup";import{FloPayError as E,isCardSetupCheckoutSession as ue,resolveBillingApiUrl as de,resolveTheme as fe}from"@flopay/shared";import{useEffect as U,useMemo as pe,useRef as u,useState as T}from"react";function S(e){if(!e)return;let n=t=>{console.error("[FloPay] Merchant callback failed; checkout continued.",t)};try{Promise.resolve(e()).catch(n)}catch(t){n(t)}}import{jsx as l,jsxs as w}from"react/jsx-runtime";function Q({status:e,errorMessage:n,authorized:t=!1,processingLabel:o="PROCESSING...",successLabel:r,errorLabel:f="PAYMENT FAILED",successNote:i="You will be automatically redirected, do not close or navigate away from this window."}){return l("div",{"data-testid":"flopay-processing-overlay","data-status":e,role:"dialog","aria-modal":"true",style:{position:"fixed",inset:0,background:"rgba(0,0,0,0.35)",display:"flex",alignItems:"center",justifyContent:"center",zIndex:1e3,backdropFilter:"blur(2px)"},children:w("div",{style:{background:"white",borderRadius:12,padding:"2rem 2.5rem",textAlign:"center",boxShadow:"0 8px 32px rgba(0,0,0,0.18)",minWidth:240,display:"flex",flexDirection:"column",alignItems:"center",gap:16},children:[w("div",{style:{width:48,height:48,position:"relative"},children:[e==="processing"&&w("svg",{width:"48",height:"48",viewBox:"0 0 24 24",fill:"none",xmlns:"http://www.w3.org/2000/svg",style:{animation:"flopay-spin 0.8s linear infinite"},children:[l("circle",{cx:"12",cy:"12",r:"10",stroke:"#e5e7eb",strokeWidth:"3"}),l("path",{d:"M4 12a8 8 0 018-8v3a5 5 0 00-5 5H4z",fill:"#4A49FF"})]}),e==="success"&&l("div",{style:{animation:"flopay-pop 0.4s cubic-bezier(0.34,1.56,0.64,1)"},children:w("svg",{width:"48",height:"48",viewBox:"0 0 24 24",fill:"none",xmlns:"http://www.w3.org/2000/svg",children:[l("circle",{cx:"12",cy:"12",r:"11",fill:"#22c55e"}),l("path",{d:"M7 12.5l3 3 7-7",stroke:"white",strokeWidth:"2.5",strokeLinecap:"round",strokeLinejoin:"round",style:{strokeDasharray:20,strokeDashoffset:20,animation:"flopay-draw 0.4s 0.15s ease forwards"}})]})}),e==="error"&&l("div",{style:{animation:"flopay-shake 0.4s ease"},children:w("svg",{width:"48",height:"48",viewBox:"0 0 24 24",fill:"none",xmlns:"http://www.w3.org/2000/svg",children:[l("circle",{cx:"12",cy:"12",r:"11",fill:"#ef4444"}),l("path",{d:"M8 8l8 8M16 8l-8 8",stroke:"white",strokeWidth:"2.5",strokeLinecap:"round",style:{strokeDasharray:12,strokeDashoffset:12,animation:"flopay-draw 0.3s 0.1s ease forwards"}})]})})]}),w("span",{style:{fontSize:14,fontWeight:600,letterSpacing:"0.05em",color:e==="success"?"#16a34a":e==="error"?"#dc2626":"#374151"},children:[e==="processing"&&o,e==="success"&&(r??(t?"PAYMENT AUTHORISED":"PAYMENT SUCCESSFUL")),e==="error"&&f]}),e==="success"&&i!==null&&l("p",{style:{fontSize:13,color:"#6b7280",fontWeight:400,maxWidth:260,lineHeight:1.4,margin:0},children:i}),e==="error"&&n&&l("p",{style:{fontSize:13,color:"#6b7280",fontWeight:400,maxWidth:260,lineHeight:1.4,margin:0},children:n}),l("style",{children:`
|
|
2
|
+
@keyframes flopay-spin { to { transform: rotate(360deg); } }
|
|
3
|
+
@keyframes flopay-pop { 0% { transform: scale(0); } 100% { transform: scale(1); } }
|
|
4
|
+
@keyframes flopay-draw { to { stroke-dashoffset: 0; } }
|
|
5
|
+
@keyframes flopay-shake { 0%,100% { transform: translateX(0); } 20%,60% { transform: translateX(-4px); } 40%,80% { transform: translateX(4px); } }
|
|
6
|
+
`})]})})}function ae(e,n=.12){let t=/^#?([0-9a-fA-F]{6})$/.exec(e.trim());if(!t)return e;let o=parseInt(t[1],16),r=Math.max(0,Math.min(1,1-n)),f=Math.round((o>>16&255)*r),i=Math.round((o>>8&255)*r),p=Math.round((o&255)*r);return`#${(1<<24|f<<16|i<<8|p).toString(16).slice(1)}`}function se(e){if(typeof e=="number"||typeof e=="string")return e}function Z({appearance:e,buttonsStyles:n,layout:t="default"}){let o=e?.variables,r=y=>typeof y=="string"&&y?y:void 0,f=n.submitButton,i=r(o?.colorPrimary)??r(f?.backgroundColor)??"#4A49FF",p=t==="buttons",m=n.nameInput;return{primaryColor:i,primaryHoverColor:r(o?.colorPrimaryHover)??ae(i,.12),inputBackgroundColor:r(n.cardInputBackground)??r(o?.colorBackground)??"#ffffff",textColor:r(n.cardInputColor)??r(m?.color)??r(o?.colorText)??"#262833",borderColor:r(n.cardInputBorder)??(p?"#e5e7eb":"#A4A4FF"),placeholderColor:r(n.cardInputPlaceholderColor)??"#9ca3af",errorColor:r(o?.colorDanger)??"#dc2626",successColor:"#16a34a",fontFamily:r(m?.fontFamily)??r(o?.fontFamily)??"Poppins, sans-serif",fontSize:r(n.cardInputFontSize)??r(o?.fontSizeBase)??"16px",fontWeight:String(se(m?.fontWeight)??400),borderRadius:r(o?.borderRadius)??"8px"}}import{Fragment as te,jsx as O,jsxs as me}from"react/jsx-runtime";var H=class extends E{constructor(n,t,o){super(n,t,o),this.name="FloPayCardSetupError",this.retryable=o.retryable}};function re(e,n={}){let t=e instanceof E?e:void 0,o=t?.statusCode,r=n.retryable??(t?.type==="network_error"||t?.type==="rate_limit_error"||typeof o=="number"&&o>=500||t?.code==="vault_capture_timeout"||!t);return new H(t?.message??(e instanceof Error?e.message:"Failed to load the secure card form."),t?.type??"api_error",{code:n.code??t?.code??"card_setup_load_failed",retryable:r,...t?.param?{param:t.param}:{},...o!==void 0?{statusCode:o}:{}})}function Ae({sessionId:e,nonce:n,billingApiUrl:t,telemetry:o,theme:r="modern-light",containerStyle:f,loading:i=null,onReady:p,onComplete:m,onDecline:y,onCancel:k,onValidation:R,onError:x}){let[c,P]=T(null),[h,F]=T(null),[A,L]=T(null),[D,g]=T(null),[I,d]=T(null),b=u(!1),B=u(!1),oe=pe(()=>{if(typeof r=="object")return r;let a=fe(r);if(a)return Z({appearance:a.appearance,buttonsStyles:a.buttonsLayout,layout:"default"})},[r]),G=u(p),N=u(x),q=u(m),X=u(y),$=u(k),j=u(R),V=u(!1),W=u(!1),Y=u(0);return G.current=p,N.current=x,q.current=m,X.current=y,$.current=k,j.current=R,U(()=>{let a=Y.current+1;return Y.current=a,()=>{queueMicrotask(()=>{Y.current===a&&(V.current||W.current||(W.current=!0,S(()=>{$.current?.({status:"cancelled",sessionId:e})})))})}},[e]),U(()=>{let a=!0,v=new le(de(t),{telemetry:o});return P(null),F(null),L(null),V.current=!1,W.current=!1,(async()=>{try{if(!e.trim())throw new E("FloPayCardSetup requires a sessionId.","validation_error",{code:"MissingCheckoutSessionId",param:"sessionId"});if(!n.trim())throw new E("FloPayCardSetup requires a session nonce.","validation_error",{code:"MissingCheckoutSessionToken",param:"nonce"});let _=await v.getUnifiedCheckoutSession(e,n);if(!a)return;let C=_.data.session;if(!C||!ue(C))throw new E("FloPayCardSetup only accepts a zero-amount, productless setup session.","validation_error",{code:"InvalidCardSetupSession",param:"sessionId"});let s=C.vault;if(s?.html?.trim()||(s=await v.getVaultCapture(e,n)),!s.html?.trim())throw new E("FloPay: this setup session has no vault card capability. Upgrade the billing backend integration.","api_error",{code:"UnsupportedBackendVaultCapability"});if(!a)return;F(s),P(new ce({sessionId:e,operation:"card_setup",telemetry:o}))}catch(_){if(!a)return;let C=re(_);V.current=!0,L(C),S(()=>N.current?.(C))}})(),()=>{a=!1,v.destroy()}},[t,n,e,o]),U(()=>{if(!c)return;let a=!1;B.current=!1,b.current=!1;let v=c.on("submitting",()=>{b.current=!0,d(null),g("processing")}),_=c.on("complete",s=>{V.current=!0,b.current=!1,g("success"),(async()=>(await new Promise(z=>setTimeout(z,1200)),!B.current&&(B.current=!0,S(()=>{q.current?.({status:"succeeded",sessionId:e,...s.paymentMethod?{paymentMethod:s.paymentMethod}:{}})}),a||g(null))))()}),C=c.on("decline",s=>{V.current=!0,b.current=!1,d(s.message??null),g("error"),S(()=>{X.current?.({status:"declined",sessionId:e,...s.declineReason?{reason:s.declineReason}:{},...s.message?{message:s.message}:{}})}),(async()=>(await new Promise(z=>setTimeout(z,1500)),a||g(null)))()});return()=>{a=!0,v(),_(),C()}},[c,e]),A?O("div",{role:"alert",children:A.message}):!c||!h?.html?O(te,{children:i}):me(te,{children:[D&&O(Q,{status:D,errorMessage:I,processingLabel:"SAVING CARD...",successLabel:"CARD SAVED",errorLabel:"CARD NOT SAVED",successNote:null}),O(K,{capture:c,html:h.html,messageToken:h.messageToken,expectedOrigin:h.expectedOrigin,theme:oe,containerStyle:f,onReady:()=>{S(()=>G.current?.())},onValidation:a=>{S(()=>j.current?.(a))},onError:a=>{if(!a)return;b.current&&(b.current=!1,d(a),g("error"),setTimeout(()=>g(null),1500));let v=re(new Error(a),{code:"card_setup_widget_failed",retryable:!0});S(()=>N.current?.(v))}})]})}export{S as a,Q as b,K as c,ae as d,se as e,Z as f,H as g,Ae as h};
|