@movmo_app/payments 0.8.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs.js CHANGED
@@ -27,5 +27,5 @@ Check the top-level render call using <`+d+">.")}return i}}function U(t,i){{if(!
27
27
  <%s {...props} />
28
28
  React keys must be passed directly to JSX without using spread:
29
29
  let props = %s;
30
- <%s key={someKey} {...props} />`,mt,Be,tr,Be),jt[Be+mt]=!0}}return t===o?ct(H):Me(H),H}}function Zt(t,i,d){return _t(t,i,d,!0)}function Xt(t,i,d){return _t(t,i,d,!1)}var Qt=Xt,er=Zt;tt.Fragment=o,tt.jsx=Qt,tt.jsxs=er}()),tt}process.env.NODE_ENV==="production"?vt.exports=sr():vt.exports=or();var a=vt.exports;function Dt(e){var r,n,o="";if(typeof e=="string"||typeof e=="number")o+=e;else if(typeof e=="object")if(Array.isArray(e)){var l=e.length;for(r=0;r<l;r++)e[r]&&(n=Dt(e[r]))&&(o&&(o+=" "),o+=n)}else for(n in e)e[n]&&(o&&(o+=" "),o+=n);return o}function X(){for(var e,r,n=0,o="",l=arguments.length;n<l;n++)(e=arguments[n])&&(r=Dt(e))&&(o&&(o+=" "),o+=r);return o}const ir="https://core.spreedly.com/iframe/iframe-v1.min.js",lr="https://cdn.e2e.movmo.io";let Ge={};const cr=e=>{Ge={...Ge,...e}},ft=e=>{var n;const r=globalThis.process;return(n=r==null?void 0:r.env)==null?void 0:n[e]},Ye=()=>({baseUrl:Ge.baseUrl??ft("REACT_APP_MOVMO_API_URL")??"",spreedlyScriptUrl:Ge.spreedlyScriptUrl??ft("REACT_APP_SPREEDLY_SCRIPT_URL")??ir,iconCdnBaseUrl:Ge.iconCdnBaseUrl??ft("REACT_APP_MOVMO_ICON_CDN_URL")??lr,fetch:Ge.fetch??(typeof globalThis<"u"&&typeof globalThis.fetch=="function"?globalThis.fetch.bind(globalThis):(()=>{throw new Error("@movmo_app/payments: no fetch available")})())}),dr="recovery_cooldown",ur=(e,r)=>{if(!e)return"";const n=new Date(e);if(Number.isNaN(n.getTime()))return"";try{return new Intl.DateTimeFormat("en-US",{month:"short",day:"numeric",year:"numeric",hour:"numeric",minute:"2-digit",timeZone:r}).format(n)}catch{return""}},mr=e=>{const r=ur(e??"");return r?`Account recovery is in progress. You can change payment methods after ${r}.`:"Account recovery is in progress. You can change payment methods once it ends."},gt=e=>({id:e.id,last4:e.last4,brand:e.brand,expMonth:e.expMonth,expYear:e.expYear,isDefault:e.isDefault,vault:e.vault,cardholderName:e.cardholderName,zip:e.zip}),Ke={Accept:"application/json","Content-Type":"application/json"},Je=e=>{const{baseUrl:r}=Ye();if(!r)throw new Error("@movmo_app/payments: baseUrl is not configured. Call setPaymentsConfig({ baseUrl }) at app boot, or set REACT_APP_MOVMO_API_URL.");return`${r}${e}`},Ze=(e,r)=>{const{fetch:n}=Ye();return n(e,r)};class wt extends Error{constructor(n,o){super(n);qe(this,"status");qe(this,"code");qe(this,"until");this.name="PaymentsApiError",this.status=o.status,this.code=o.code,this.until=o.until}}const fr=async e=>{try{const r=await e.json();if(typeof r!="object"||r===null)return{};const{message:n,code:o,until:l}=r;return{message:typeof n=="string"?n:void 0,code:typeof o=="string"?o:void 0,until:typeof l=="string"?l:void 0}}catch{return{}}},Xe=async(e,r)=>{const{message:n,code:o,until:l}=await fr(e),c=o===dr?mr(l):n||r;return new wt(c,{status:e.status,code:o,until:l})};let rt=null;const xt=()=>{if(rt)return rt;const e=(async()=>{const r=await Ze(Je("/v1/payments/tokenization-session"),{method:"GET",headers:Ke,credentials:"include"});if(!r.ok)throw await Xe(r,`Failed to fetch tokenization session (${r.status})`);return await r.json()})();return rt=e,e.finally(()=>{rt===e&&(rt=null)}).catch(()=>{}),e},pr=e=>typeof e.brand=="string"&&typeof e.last4=="string"&&typeof e.expMonth=="number"&&typeof e.expYear=="number",hr=async(e,r,n)=>{const o=await Ze(Je(`/v1/users/${encodeURIComponent(e)}/payment-methods/from-token`),{method:"POST",headers:Ke,credentials:"include",body:JSON.stringify({tokenizedPaymentMethodId:r,isDefault:n})});if(!o.ok)throw await Xe(o,`Failed to save payment method (${o.status})`);const l=await o.json();if(pr(l))return gt(l);const c=await Ze(Je(`/v1/users/${encodeURIComponent(e)}/payment-methods/${encodeURIComponent(l.id)}`),{method:"GET",headers:Ke,credentials:"include"});if(!c.ok)throw await Xe(c,`Failed to fetch saved payment method (${c.status})`);const m=await c.json();return gt(m)},yr=async(e,r)=>{if(!e)return[];const n=await Ze(Je(`/v1/users/${encodeURIComponent(e)}/payment-methods`),{method:"GET",headers:Ke,credentials:"include",signal:r});if(!n.ok)throw await Xe(n,`Failed to fetch payment methods (${n.status})`);return(await n.json()??[]).map(gt)},vr=async(e,r)=>{const n=await Ze(Je(`/v1/users/${encodeURIComponent(e)}/payment-methods/${encodeURIComponent(r)}`),{method:"DELETE",headers:Ke,credentials:"include"});if(!n.ok)throw await Xe(n,`Failed to delete payment method (${n.status})`)},$t=async(e,r,n)=>{const o=await Ze(Je(`/v1/users/${encodeURIComponent(e)}/payment-methods/${encodeURIComponent(r)}`),{method:"PUT",headers:Ke,credentials:"include",body:JSON.stringify(n)});if(!o.ok)throw await Xe(o,`Failed to update payment method (${o.status})`)},nt=(e,r,n)=>r instanceof wt?{source:e,message:r.message,cause:r,status:r.status,...r.code!==void 0?{code:r.code}:{},...r.until!==void 0?{until:r.until}:{}}:{source:e,message:r instanceof Error?r.message:n,cause:r},gr={visa:"visa",master:"mastercard",mastercard:"mastercard",american_express:"amex",amex:"amex",discover:"discover"},xr=28,Ie=({brand:e,className:r,width:n=46})=>{const o=e?gr[e.toLowerCase()]??"credit-card":"credit-card",l=Math.max(n,xr),c=Math.round(l*24/34),{iconCdnBaseUrl:m}=Ye();return a.jsx("img",{src:`${m}/website/assets/icons/payments/${o}.svg`,alt:o==="credit-card"?"card":`${o} card`,"data-testid":`card-brand-${e.toLowerCase()}`,className:r,width:l,height:c,loading:"lazy"})};let at=null;const br=()=>{if(typeof window>"u")return Promise.reject(new Error("Spreedly can only be loaded in a browser environment."));if(window.Spreedly)return Promise.resolve(window.Spreedly);if(at)return at;const{spreedlyScriptUrl:e}=Ye();return at=new Promise((r,n)=>{const o=document.querySelector(`script[src="${e}"]`),l=()=>{window.Spreedly?r(window.Spreedly):n(new Error("Spreedly script loaded but window.Spreedly is undefined."))};if(o){if(window.Spreedly){l();return}o.addEventListener("load",l,{once:!0}),o.addEventListener("error",()=>{o.remove(),n(new Error("Failed to load Spreedly iframe script."))},{once:!0});return}const c=document.createElement("script");c.src=e,c.async=!0,c.onload=l,c.onerror=()=>{c.remove(),n(new Error("Failed to load Spreedly iframe script."))},document.head.appendChild(c)}).catch(r=>{throw at=null,r}),at};class dt extends Error{constructor(n,o){super(o instanceof Error?o.message:"Failed to initialize card form.");qe(this,"source");qe(this,"cause");this.name="InitError",this.source=n,this.cause=o}}const wr="movmo-card-number",Er="movmo-card-cvv",jr=e=>({first_name:e.firstName,last_name:e.lastName,month:e.month,year:e.year,...e.email?{email:e.email}:{},...e.zip?{zip:e.zip}:{}}),Ut=(e={})=>{const{numberEl:r=wr,cvvEl:n=Er,onCardTokenized:o,onFieldErrors:l,onValidityChange:c,onBrandChange:m}=e,[v,h]=s.useState("idle"),[j,_]=s.useState(null),[w,C]=s.useState(null),[x,P]=s.useState(null),[M,I]=s.useState({number:!1,cvv:!1}),[k,E]=s.useState(null),[O,pe]=s.useState({number:!1,cvv:!1}),de=s.useRef(null),we=s.useRef(o),ue=s.useRef(l),se=s.useRef(c),Q=s.useRef(m);s.useEffect(()=>{we.current=o},[o]),s.useEffect(()=>{ue.current=l},[l]),s.useEffect(()=>{se.current=c},[c]),s.useEffect(()=>{Q.current=m},[m]);const b=s.useRef({number:!1,cvv:!1});s.useEffect(()=>{let z=!1;return h("loading"),_(null),C(null),P(null),I({number:!1,cvv:!1}),E(null),b.current={number:!1,cvv:!1},(async()=>{try{const[p,y]=await Promise.all([xt().catch(S=>{throw new dt("tokenization-session",S)}),br().catch(S=>{throw new dt("spreedly-script",S)})]);if(z)return;de.current=y,y.on("ready",()=>{var S,A,re,ae,q,G;if(!z){h("ready");try{(S=y.setFieldType)==null||S.call(y,"number","tel"),(A=y.setFieldType)==null||A.call(y,"cvv","tel")}catch(V){console.warn("@movmo_app/payments: setFieldType failed",V)}try{typeof y.setNumberFormat=="function"?y.setNumberFormat("prettyFormat"):console.warn("@movmo_app/payments: Spreedly.setNumberFormat is missing — card-number digits will not be space-grouped.")}catch(V){console.warn("@movmo_app/payments: setNumberFormat failed",V)}try{(re=y.setPlaceholder)==null||re.call(y,"number","Card number"),(ae=y.setPlaceholder)==null||ae.call(y,"cvv","CVV");const V='box-sizing: border-box;width: 100%;height: 100%;margin: 0;padding: 0;border: 0;background: transparent;outline: none;font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;font-size: 14px;font-weight: 400;line-height: 20px;color: rgb(28 25 23);letter-spacing: 0;vertical-align: middle;';(q=y.setStyle)==null||q.call(y,"number",V),(G=y.setStyle)==null||G.call(y,"cvv",V)}catch(V){console.warn("@movmo_app/payments: setPlaceholder/setStyle failed",V)}}}),y.on("paymentMethod",S=>{var A;(A=we.current)==null||A.call(we,S)}),y.on("errors",S=>{var A;(A=ue.current)==null||A.call(ue,S)}),y.on("fieldEvent",(S,A,re,ae)=>{var V;if(z)return;if(A==="focus"){pe(T=>T[S]?T:{...T,[S]:!0});return}if(A==="blur"){pe(T=>T[S]?{...T,[S]:!1}:T);return}if(A!=="input")return;const q=ae??{};if(S==="number"){if(q.numberLength===0)E(T=>{var K;return T!==null&&((K=Q.current)==null||K.call(Q,null)),null});else if(q.cardType!==void 0){const T=q.cardType||null;E(K=>{var Ce;return K!==T&&((Ce=Q.current)==null||Ce.call(Q,T)),T})}}const G=S==="number"?typeof q.validNumber=="boolean"?q.validNumber:void 0:typeof q.validCvv=="boolean"?q.validCvv:void 0;if(typeof G=="boolean"){const T=b.current,K=S==="number"?{...T,number:G}:{...T,cvv:G};(K.number!==T.number||K.cvv!==T.cvv)&&(b.current=K,I(K),(V=se.current)==null||V.call(se,K))}}),y.on("consoleError",S=>{var re;const A=S instanceof Error?S.message:"An unexpected error occurred while securing your card.";(re=ue.current)==null||re.call(ue,[{message:A}])}),y.init(p.environmentKey,{numberEl:r,cvvEl:n,nonce:p.nonce,timestamp:String(p.timestamp),certificateToken:p.certificateToken,signature:p.signature})}catch(p){if(z)return;const y=p instanceof Error?p.message:"Failed to initialize card form.";_(y),C(p instanceof dt?p.source:"spreedly-script"),P(p instanceof dt?p.cause:p),h("error")}})(),()=>{var p,y;z=!0;try{(y=(p=de.current)==null?void 0:p.removeHandlers)==null||y.call(p)}catch(S){console.warn("@movmo_app/payments: removeHandlers failed during cleanup",S)}de.current=null}},[r,n]);const ye=s.useCallback(z=>{var p;(p=de.current)==null||p.tokenizeCreditCard(jr(z))},[]),_e=s.useCallback(z=>{var p,y;(y=(p=de.current)==null?void 0:p.transferFocus)==null||y.call(p,z)},[]);return{status:v,error:j,errorSource:w,errorCause:x,tokenize:ye,validity:M,brand:k,focused:O,focusField:_e}},bt={number:"Enter your card number",expiry:"Enter a valid expiry date",cvv:"Enter the security code",zip:"Enter your postal code",name:"Enter first and last name as printed on the card"},_r="Your card saves automatically once all fields are complete.",It=e=>/^[A-Za-z0-9][A-Za-z0-9 -]{1,8}[A-Za-z0-9]$/.test(e.trim()),zt=e=>{const r=e.replace(/[^A-Za-z0-9 -]/g,"").slice(0,10);if(!/^[\d-]*$/.test(r))return r;const n=r.replace(/-+/g,"-").replace(/^-/,"");return n.includes("-")?n:n.length===9?`${n.slice(0,5)}-${n.slice(5)}`:n},Cr=({hookStatus:e,validity:r,nameValid:n,expiryValid:o,zipValid:l})=>{const c=e==="ready",m=[],v=h=>m.push({field:h,message:bt[h]});return c&&!r.number&&v("number"),o||v("expiry"),c&&!r.cvv&&v("cvv"),l||v("zip"),n||v("name"),m},pt="h-11 w-full rounded-md border bg-white px-3 py-2 text-sm text-grayWarm-900 placeholder:text-grayWarm-400 outline-none focus:border-grayWarm-950 focus:shadow-[0_0_0_1px_#1c1917]",kt="flex h-11 w-full items-center gap-2 rounded-md border bg-white px-3 py-2 transition-shadow",Nt="border-grayWarm-950 shadow-[0_0_0_1px_#1c1917]",St="border-grayWarm-300",Pt="border-error-500",Rr={number:!1,expiry:!1,cvv:!1,zip:!1,name:!1},kr=e=>{const r=e.trim();if(!r)return{firstName:"",lastName:""};const n=r.lastIndexOf(" ");return n===-1?{firstName:r,lastName:""}:{firstName:r.slice(0,n).trim(),lastName:r.slice(n+1).trim()}},Lt=e=>{const r=e.replace(/\D/g,"");if(r.length<4)return{month:"",year:""};const n=r.slice(0,2),o=r.slice(2),l=o.length===2?`20${o}`:o.slice(0,4);return{month:n,year:l}},Nr=e=>{const r=e.replace(/\D/g,"").slice(0,4);return r.length<=2?r:`${r.slice(0,2)} / ${r.slice(2)}`},ht=e=>{const{month:r,year:n}=Lt(e);if(!r||!n)return!1;const o=parseInt(r,10),l=parseInt(n,10);if(isNaN(o)||o<1||o>12)return!1;const c=new Date;return!(l<c.getFullYear()||l>c.getFullYear()+20||l===c.getFullYear()&&o<c.getMonth()+1)},Et=({userId:e,onSuccess:r,onError:n,onErrorDetail:o,isDefault:l=!1,className:c,defaultCardholderName:m,defaultCardholderFirstName:v="",defaultCardholderLastName:h="",autoSave:j=!1,formId:_="movmo-card-form",hideInternalSaveButton:w=!1,onCanSubmitChange:C,onSavingChange:x,autoFocus:P,onValidationChange:M})=>{const I=m??[v,h].filter(Boolean).join(" "),[k,E]=s.useState(I),[O,pe]=s.useState(""),[de,we]=s.useState(""),[ue,se]=s.useState("idle"),Q=s.useRef(!1),b=s.useRef(o);s.useEffect(()=>{b.current=o},[o]);const ye=s.useCallback(async f=>{var D;se("saving");try{const me=await hr(e,f,l);se("ready"),r(me)}catch(me){const ne=me instanceof Error?me.message:"Failed to save payment method.";se("ready"),Q.current=!1,n(ne),(D=b.current)==null||D.call(b,{...nt("save-from-token",me,ne),message:ne})}},[e,l,r,n]),_e=s.useCallback(f=>{var me;const D=f.map(ne=>ne.message).filter(Boolean).join("; ")||"Card validation failed.";se(ne=>ne==="tokenizing"?"ready":ne),Q.current=!1,n(D),(me=b.current)==null||me.call(b,{source:"card-tokenize",message:D})},[n]),z=s.useMemo(()=>({onCardTokenized:ye,onFieldErrors:_e}),[ye,_e]),{status:p,error:y,errorSource:S,errorCause:A,tokenize:re,validity:ae,brand:q,focused:G,focusField:V}=Ut(z),T=s.useRef(!1);s.useEffect(()=>{!P||p!=="ready"||T.current||(T.current=!0,V(P))},[P,p,V]);const K=k.trim().split(/\s+/).length>=2,Ce=ht(O),ve=It(de),[J,Ee]=s.useState(Rr),Z=s.useCallback(f=>Ee(D=>D[f]?D:{...D,[f]:!0}),[]),Pe=s.useRef(G);s.useEffect(()=>{const f=Pe.current;Pe.current=G,f.number&&!G.number&&Z("number"),f.cvv&&!G.cvv&&Z("cvv")},[G,Z]);const Re=s.useMemo(()=>Cr({hookStatus:p,validity:ae,nameValid:K,expiryValid:Ce,zipValid:ve}),[p,ae,K,Ce,ve]),oe=f=>Re.some(D=>D.field===f),ee=s.useMemo(()=>Re.map(f=>f.field),[Re]),Te=s.useMemo(()=>ee.filter(f=>J[f]),[ee,J]),$=s.useRef(M);s.useEffect(()=>{$.current=M},[M]),s.useEffect(()=>{var f;(f=$.current)==null||f.call($,{valid:ee.length===0,missing:ee,touchedInvalid:Te})},[ee,Te]);const ie=J.number&&oe("number"),ke=J.name&&oe("name"),te=Re.find(f=>(f.field==="expiry"||f.field==="cvv"||f.field==="zip")&&J[f.field]),je=s.useRef(!1),Fe=s.useRef(!1),he=s.useRef(null);s.useEffect(()=>{var f;ae.cvv?Fe.current||(Fe.current=!0,(f=he.current)==null||f.focus()):Fe.current=!1},[ae.cvv]);const ze=!K||!Ce||!ve||!ae.number||!ae.cvv,ge=ue==="tokenizing"||ue==="saving",le=p==="ready"&&!ge&&!ze,Oe=s.useRef(C);s.useEffect(()=>{Oe.current=C},[C]),s.useEffect(()=>{var f;(f=Oe.current)==null||f.call(Oe,le)},[le]);const We=s.useRef(x);s.useEffect(()=>{We.current=x},[x]),s.useEffect(()=>{var f;(f=We.current)==null||f.call(We,ge)},[ge]);const Ae=s.useCallback(()=>{const{firstName:f,lastName:D}=kr(k),{month:me,year:ne}=Lt(O);se("tokenizing"),re({firstName:f,lastName:D,month:me,year:ne,zip:de.trim()})},[k,O,de,re]);s.useEffect(()=>{if(!j||!le||Q.current)return;const f=setTimeout(()=>{Q.current=!0,Ae()},1200);return()=>clearTimeout(f)},[j,le,Ae]);const Le=f=>{f.preventDefault(),le&&Ae()},De=p!=="ready"||ge,Ne=s.useRef(n);return s.useEffect(()=>{Ne.current=n},[n]),s.useEffect(()=>{var f,D;y&&((f=Ne.current)==null||f.call(Ne,y),(D=b.current)==null||D.call(b,{...nt(S??"spreedly-script",A,y),message:y}))},[y,S,A]),a.jsxs("form",{id:_,onSubmit:Le,className:X("flex w-full flex-col gap-3",c),"data-testid":"movmo-card-form",children:[a.jsxs("div",{children:[a.jsx("span",{id:"movmo-card-number-label",className:"sr-only",children:"Card number"}),a.jsxs("div",{className:X(kt,G.number?Nt:ie?Pt:St),"data-testid":"movmo-card-number-shell",children:[a.jsx(Ie,{brand:q??"",width:32,className:"flex-shrink-0"}),a.jsxs("div",{className:"relative h-full flex-1",children:[a.jsx("div",{id:"movmo-card-number","aria-labelledby":"movmo-card-number-label","aria-busy":p!=="ready","aria-describedby":ie?"movmo-card-number-error":void 0,className:"absolute inset-0","data-testid":"movmo-card-number-mount"}),p!=="ready"?a.jsx("div",{"aria-hidden":"true",className:"pointer-events-none absolute inset-y-2 left-0 right-2 animate-pulse rounded bg-grayWarm-200"}):null]})]}),ie&&a.jsx("p",{id:"movmo-card-number-error",className:"mt-1 text-xs text-error-700","data-testid":"movmo-card-number-error",children:bt.number})]}),a.jsxs("div",{children:[a.jsxs("div",{className:"grid grid-cols-3 gap-3",children:[a.jsxs("div",{children:[a.jsx("label",{htmlFor:"movmo-card-expiry",className:"sr-only",children:"Expiry MM / YY"}),a.jsx("input",{id:"movmo-card-expiry",type:"text",inputMode:"numeric",autoComplete:"cc-exp",placeholder:"MM / YY",value:O,onChange:f=>{const D=Nr(f.target.value);pe(D),ht(D)&&!je.current?(je.current=!0,V("cvv")):ht(D)||(je.current=!1)},onBlur:()=>Z("expiry"),"aria-invalid":J.expiry&&oe("expiry"),"aria-describedby":(te==null?void 0:te.field)==="expiry"?"movmo-card-row-error":void 0,className:X(pt,J.expiry&&oe("expiry")?"border-error-500":"border-grayWarm-300"),disabled:De,required:!0,"data-testid":"movmo-card-expiry-input"})]}),a.jsxs("div",{className:"relative",children:[a.jsx("span",{id:"movmo-card-cvv-label",className:"sr-only",children:"CVV"}),a.jsxs("div",{className:X(kt,G.cvv?Nt:J.cvv&&oe("cvv")?Pt:St),"data-testid":"movmo-card-cvv-shell",children:[a.jsxs("div",{className:"relative h-full flex-1",children:[a.jsx("div",{id:"movmo-card-cvv","aria-labelledby":"movmo-card-cvv-label","aria-busy":p!=="ready","aria-describedby":(te==null?void 0:te.field)==="cvv"?"movmo-card-row-error":void 0,className:"absolute inset-0","data-testid":"movmo-card-cvv-mount"}),p!=="ready"?a.jsx("div",{"aria-hidden":"true",className:"pointer-events-none absolute inset-y-2 left-0 right-2 animate-pulse rounded bg-grayWarm-200"}):null]}),a.jsx("button",{type:"button","aria-label":"CVV help",tabIndex:-1,className:"flex h-5 w-5 flex-shrink-0 items-center justify-center rounded-full border border-grayWarm-300 text-xs text-grayWarm-500",title:q==="american_express"?"The 4 numbers on the front of your card.":"The 3 numbers on the back of your card.","data-testid":"movmo-cvv-help",children:"?"})]})]}),a.jsxs("div",{children:[a.jsx("label",{htmlFor:"movmo-card-zip",className:"sr-only",children:"Postal code"}),a.jsx("input",{id:"movmo-card-zip",ref:he,type:"text",autoComplete:"postal-code",placeholder:"Postal code",value:de,onChange:f=>{we(zt(f.target.value))},onBlur:()=>Z("zip"),"aria-invalid":J.zip&&oe("zip"),"aria-describedby":(te==null?void 0:te.field)==="zip"?"movmo-card-row-error":void 0,className:X(pt,J.zip&&oe("zip")?"border-error-500":"border-grayWarm-300"),disabled:De,required:!0,"data-testid":"movmo-card-zip-input"})]})]}),te&&a.jsx("p",{id:"movmo-card-row-error",className:"mt-1 text-xs text-error-700","data-testid":"movmo-card-row-error",children:te.message})]}),a.jsxs("div",{children:[a.jsx("label",{htmlFor:"movmo-card-cardholder-name",className:"sr-only",children:"Cardholder name"}),a.jsx("input",{id:"movmo-card-cardholder-name",type:"text",autoComplete:"cc-name",placeholder:"Cardholder name",value:k,onChange:f=>E(f.target.value),onBlur:()=>Z("name"),"aria-invalid":ke,"aria-describedby":ke?"movmo-card-name-error":void 0,className:X(pt,ke?"border-error-500":"border-grayWarm-300"),disabled:De,required:!0,"data-testid":"movmo-card-cardholder-name-input"}),ke&&a.jsx("p",{id:"movmo-card-name-error",className:"mt-1 text-xs text-error-700","data-testid":"movmo-card-name-error",children:bt.name})]}),!j&&!w?a.jsx("button",{type:"submit",disabled:!le,className:X("inline-flex h-11 items-center justify-center rounded-md bg-brand-500 px-4 text-sm font-semibold text-white transition-colors hover:bg-brand-600","disabled:cursor-not-allowed disabled:bg-grayWarm-300 disabled:text-grayWarm-500"),"data-testid":"movmo-card-save",children:ge?"Saving…":p==="loading"?"Loading…":"Save card"}):j?a.jsx("div",{className:"text-center","aria-live":"polite","data-testid":"movmo-card-autosave-status",children:ge?a.jsx("p",{className:"text-sm text-grayWarm-500","data-testid":"movmo-card-autosaving",children:"Saving card…"}):Re.length>0?a.jsx("p",{className:"text-xs text-grayWarm-600","data-testid":"movmo-card-autosave-hint",children:_r}):null}):null]})};Et.displayName="MovmoCardForm";const Bt=e=>{const[r,n]=s.useState("idle"),[o,l]=s.useState(null);return{deletePaymentMethod:s.useCallback(async m=>{n("pending"),l(null);try{await vr(e,m),n("idle")}catch(v){const h=v instanceof Error?v.message:"Failed to delete payment method.";throw l(h),n("error"),v}},[e]),status:r,error:o}},Yt=e=>{const[r,n]=s.useState("idle"),[o,l]=s.useState(null);return{setDefault:s.useCallback(async m=>{n("pending"),l(null);try{await $t(e,m,{isDefault:!0}),n("idle")}catch(v){const h=v instanceof Error?v.message:"Failed to set default payment method.";throw l(h),n("error"),v}},[e]),status:r,error:o}},Vt=e=>{const[r,n]=s.useState("idle"),[o,l]=s.useState(null);return{updatePaymentMethod:s.useCallback(async(m,v)=>{n("pending"),l(null);try{await $t(e,m,v),n("idle")}catch(h){const j=h instanceof Error?h.message:"Failed to update payment method.";throw l(j),n("error"),h}},[e]),status:r,error:o}},Ht=e=>{const[r,n]=s.useState([]),[o,l]=s.useState("idle"),[c,m]=s.useState(null),[v,h]=s.useState(0),j=s.useRef(null);s.useEffect(()=>{var C;if((C=j.current)==null||C.abort(),!e){j.current=null,n([]),m(null),l("ready");return}const w=new AbortController;return j.current=w,l("loading"),m(null),yr(e,w.signal).then(x=>{w.signal.aborted||(n(x),l("ready"))}).catch(x=>{if(w.signal.aborted||x instanceof DOMException&&x.name==="AbortError")return;const P=x instanceof Error?x.message:"Failed to fetch payment methods.";m(P),l("error")}),()=>{w.abort()}},[e,v]);const _=s.useCallback(()=>{h(w=>w+1)},[]);return{items:r,status:o,error:c,refetch:_}},qt=({open:e,onClose:r,title:n,titleId:o,formId:l,saveDisabled:c,saveLabel:m="Save",isSaving:v=!1,testIdPrefix:h="payment-method",children:j})=>(s.useEffect(()=>{if(!e)return;const _=w=>{w.key==="Escape"&&r()};return document.addEventListener("keydown",_),()=>document.removeEventListener("keydown",_)},[e,r]),s.useEffect(()=>{if(!e)return;const _=document.body.style.overflow;return document.body.style.overflow="hidden",()=>{document.body.style.overflow=_}},[e]),e?a.jsx("div",{role:"dialog","aria-modal":"true","aria-labelledby":o,className:"fixed inset-0 z-50 flex items-center justify-center bg-black/40 p-4",onClick:_=>{_.target===_.currentTarget&&r()},"data-testid":`${h}-modal`,children:a.jsxs("div",{className:"flex max-h-full w-full max-w-md flex-col rounded-xl bg-white shadow-2xl",children:[a.jsxs("div",{className:"flex items-center gap-3 border-b border-grayWarm-200 px-4 py-3",children:[a.jsx("button",{type:"button",onClick:r,"aria-label":`Close ${n.toLowerCase()} dialog`,className:"flex h-9 w-9 flex-shrink-0 items-center justify-center rounded-lg bg-grayWarm-100 p-2 text-grayWarm-950 transition-colors hover:bg-grayWarm-200","data-testid":`${h}-close`,children:a.jsx("svg",{viewBox:"0 0 20 20",width:"20",height:"20","aria-hidden":"true",children:a.jsx("path",{d:"M5 5L15 15M15 5L5 15",stroke:"currentColor",strokeWidth:"1.67",strokeLinecap:"round",strokeLinejoin:"round"})})}),a.jsx("h2",{id:o,className:"flex-1 text-center text-base font-semibold text-grayWarm-900",children:n}),a.jsx("span",{"aria-hidden":"true",className:"h-9 w-9 flex-shrink-0"})]}),a.jsx("div",{className:"flex-1 overflow-y-auto p-4",children:j}),a.jsxs("div",{className:"flex gap-3 border-t border-grayWarm-200 p-4",children:[a.jsx("button",{type:"button",onClick:r,className:"inline-flex h-11 flex-1 items-center justify-center rounded-lg border border-grayWarm-300 px-4 text-sm font-semibold text-grayWarm-900 transition-colors hover:border-grayWarm-900 hover:shadow-[0_0_0_1px_#1c1917]","data-testid":`${h}-cancel`,children:"Cancel"}),a.jsx("button",{type:"submit",form:l,disabled:v?!1:c,"aria-disabled":v||c,"aria-busy":v,className:X("inline-flex h-11 flex-1 items-center justify-center rounded-lg bg-brand-500 px-4 text-sm font-semibold text-white transition-colors",v?"cursor-not-allowed opacity-90 pointer-events-none":"hover:bg-brand-600 disabled:cursor-not-allowed disabled:bg-grayWarm-300 disabled:text-grayWarm-500"),"data-testid":`${h}-save`,children:v?a.jsxs(a.Fragment,{children:[a.jsxs("svg",{className:"h-[18px] w-[18px] animate-spin",viewBox:"0 0 24 24",fill:"none","aria-hidden":"true",children:[a.jsx("circle",{cx:"12",cy:"12",r:"10",stroke:"currentColor",strokeWidth:"3",strokeOpacity:"0.25"}),a.jsx("path",{d:"M22 12a10 10 0 0 1-10 10",stroke:"currentColor",strokeWidth:"3",strokeLinecap:"round"})]}),a.jsx("span",{className:"sr-only",children:"Saving"})]}):m})]})]})}):null),Sr=({open:e,onClose:r,formId:n,saveDisabled:o,saveLabel:l="Save",isSaving:c=!1,children:m})=>a.jsx(qt,{open:e,onClose:r,title:"Add payment method",titleId:"movmo-add-payment-title",formId:n,saveDisabled:o,saveLabel:l,isSaving:c,testIdPrefix:"add-payment-method",children:m}),Pr=(e,r)=>`${String(e).padStart(2,"0")}/${r}`,Tr=e=>{if(!e)return"Card";const r=e.toLowerCase();return r==="amex"?"Amex":r.charAt(0).toUpperCase()+r.slice(1)},Fr=({open:e,method:r,onClose:n,onSave:o,isSaving:l=!1,error:c=null})=>{const m=s.useId(),[v,h]=s.useState(""),[j,_]=s.useState("");if(s.useEffect(()=>{e&&r&&(h(r.cardholderName??""),_(r.zip??""))},[e,r]),!r)return null;const w=v.trim(),C=j.trim(),x=w!==(r.cardholderName??"").trim(),P=C!==(r.zip??"").trim(),M=x||P,I=(!x||w.length>0)&&(!P||It(C)),k=!M||!I,E=O=>{if(O.preventDefault(),k||l)return;const pe={};x&&(pe.cardholderName=w),P&&(pe.zip=C),o(pe).catch(()=>{})};return a.jsxs(qt,{open:e,onClose:n,title:"Edit payment method",titleId:"movmo-edit-payment-title",formId:m,saveDisabled:k,isSaving:l,testIdPrefix:"edit-payment-method",children:[a.jsxs("div",{className:"mb-4 flex items-start gap-3 rounded-lg bg-grayWarm-50 p-3","data-testid":"edit-payment-method-context",children:[a.jsx(Ie,{brand:r.brand,className:"flex-shrink-0"}),a.jsxs("div",{className:"min-w-0 flex-1",children:[a.jsxs("div",{className:"text-lg font-semibold text-grayWarm-950",children:[Tr(r.brand)," •••• ",r.last4]}),a.jsxs("div",{className:"text-sm text-grayWarm-600",children:["Exp. ",Pr(r.expMonth,r.expYear)]})]})]}),a.jsxs("form",{id:m,onSubmit:E,className:"flex flex-col gap-4",noValidate:!0,children:[a.jsxs("label",{className:"flex flex-col gap-1.5 text-sm font-medium text-grayWarm-700",children:["Cardholder name",a.jsx("input",{type:"text",autoComplete:"cc-name",value:v,onChange:O=>h(O.target.value),disabled:l,maxLength:255,"data-testid":"edit-payment-method-cardholder",className:"h-11 rounded-lg border border-grayWarm-300 px-3 text-base font-normal text-grayWarm-950 focus:border-grayWarm-950 focus:outline-none focus:ring-1 focus:ring-grayWarm-950"})]}),a.jsxs("label",{className:"flex flex-col gap-1.5 text-sm font-medium text-grayWarm-700",children:["Postal code",a.jsx("input",{type:"text",autoComplete:"postal-code",value:j,onChange:O=>_(zt(O.target.value)),disabled:l,"data-testid":"edit-payment-method-zip",className:"h-11 rounded-lg border border-grayWarm-300 px-3 text-base font-normal text-grayWarm-950 focus:border-grayWarm-950 focus:outline-none focus:ring-1 focus:ring-grayWarm-950"})]}),c?a.jsx("p",{role:"alert",className:"text-sm text-error-700","data-testid":"edit-payment-method-error",children:c}):null]})]})},Or=e=>{if(!e)return"Card";const r=e.toLowerCase();return r==="amex"?"Amex":r.charAt(0).toUpperCase()+r.slice(1)},Gt=({method:e,trailing:r,onClick:n,cardLabelFormat:o="branded",className:l})=>a.jsxs("div",{className:X("flex items-center gap-3",n&&"cursor-pointer",l),onClick:n,role:n?"button":void 0,tabIndex:n?0:void 0,onKeyDown:n?c=>{(c.key==="Enter"||c.key===" ")&&(c.preventDefault(),n())}:void 0,"data-testid":`payment-method-preview-${e.id}`,children:[a.jsx(Ie,{brand:e.brand,width:34,className:"flex-shrink-0"}),a.jsx("span",{className:"flex-1 text-lg font-semibold text-grayWarm-950",children:o==="masked"?`•••• ${e.last4}`:`${Or(e.brand)} ${e.last4}`}),r]}),Wr=(e,r,n=new Date)=>r<n.getFullYear()||r===n.getFullYear()&&e<n.getMonth()+1,Tt=e=>{if(!e)return"Card";const r=e.toLowerCase();return r==="amex"?"Amex":r.charAt(0).toUpperCase()+r.slice(1)},Mr=(e,r)=>`${String(e).padStart(2,"0")}/${r}`,Ar=({method:e,selectable:r,selected:n,onSelect:o,onSetDefault:l,onRemove:c,onEdit:m,disabled:v,showDefaultBadge:h,hideSetDefaultMenuItem:j,cardLabelFormat:_="branded"})=>{const[w,C]=s.useState(!1),x=s.useRef(null),P=Wr(e.expMonth,e.expYear);s.useEffect(()=>{if(!w)return;const I=k=>{var E;(E=x.current)!=null&&E.contains(k.target)||C(!1)};return document.addEventListener("mousedown",I),()=>document.removeEventListener("mousedown",I)},[w]);const M=()=>{r&&o&&!v&&o(e)};return a.jsxs("div",{className:X("flex items-start gap-3 transition-colors",r?"rounded-lg border px-3 py-3":"py-4",r&&!v&&"cursor-pointer",r&&(n?"border-grayWarm-950 shadow-[0_0_0_1px_#1c1917]":"border-grayWarm-200 hover:border-grayWarm-300")),onClick:M,"data-testid":`payment-method-row-${e.id}`,children:[r?a.jsx("input",{type:"radio",name:"movmo-payment-method",checked:n,onChange:()=>o==null?void 0:o(e),onClick:I=>I.stopPropagation(),"aria-label":`Select ${Tt(e.brand)} ending ${e.last4}`,className:"mr-1 h-4 w-4 mt-2 cursor-pointer appearance-none rounded-full border-2 border-grayWarm-300 checked:border-[5px] checked:border-grayWarm-950 bg-white checked:bg-white focus:ring-0 focus:ring-offset-0",disabled:v}):null,a.jsx(Ie,{brand:e.brand,className:"flex-shrink-0"}),a.jsxs("div",{className:"min-w-0 flex-1",children:[a.jsxs("div",{className:"flex flex-wrap items-center gap-2",children:[a.jsx("span",{className:"text-lg font-semibold text-grayWarm-950",children:_==="masked"?`•••• ${e.last4}`:`${Tt(e.brand)} ${e.last4}`}),h&&e.isDefault?a.jsx("span",{className:"inline-flex items-center rounded-full bg-success-50 px-2 py-0.5 text-xs font-semibold text-success-700","data-testid":`badge-default-${e.id}`,children:"Default"}):null,P?a.jsx("span",{className:"inline-flex items-center rounded-full bg-error-50 px-2 py-0.5 text-xs font-semibold text-error-700","data-testid":`badge-expired-${e.id}`,children:"Expired"}):null]}),a.jsxs("div",{className:X("text-base",P?"text-grayWarm-400":"text-grayWarm-600"),children:["Exp. ",Mr(e.expMonth,e.expYear)]})]}),a.jsxs("div",{className:"relative flex-shrink-0",ref:x,children:[a.jsx("button",{type:"button","aria-label":`Open actions for card ending ${e.last4}`,"aria-haspopup":"menu","aria-expanded":w,className:"rounded-lg p-2 text-grayWarm-600 transition-colors hover:bg-grayWarm-100 disabled:cursor-not-allowed disabled:opacity-50",disabled:v,onClick:I=>{I.stopPropagation(),C(k=>!k)},"data-testid":`menu-trigger-${e.id}`,children:a.jsx("svg",{viewBox:"0 0 24 24",width:"20",height:"20",fill:"currentColor","aria-hidden":"true",children:a.jsx("path",{d:"M12 8c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm0 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z"})})}),w?a.jsxs("div",{role:"menu",className:X("absolute z-10 whitespace-nowrap rounded-lg border border-grayWarm-200 bg-white py-1 shadow-lg",r?"bottom-full left-1/2 mb-2 -translate-x-1/2":"right-0 top-full mt-1"),onClick:I=>I.stopPropagation(),"data-testid":`menu-${e.id}`,children:[r?a.jsx("span",{"aria-hidden":"true",className:"absolute -bottom-1.5 left-1/2 h-3 w-3 -translate-x-1/2 rotate-45 border-b border-r border-grayWarm-200 bg-white"}):null,!e.isDefault&&!j?a.jsx("button",{type:"button",role:"menuitem",className:"block w-full px-4 py-1 text-left text-md font-semibold text-grayWarm-950 hover:text-grayWarm-700",onClick:()=>{C(!1),l(e)},"data-testid":`menu-set-default-${e.id}`,children:"Set as default"}):null,m&&e.vault==="spreedly"?a.jsx("button",{type:"button",role:"menuitem",className:"block w-full px-4 py-1 text-left text-md font-semibold text-grayWarm-950 hover:text-grayWarm-700",onClick:()=>{C(!1),m(e)},"data-testid":`menu-edit-${e.id}`,children:"Edit"}):null,a.jsx("button",{type:"button",role:"menuitem",className:"block w-full px-4 py-1 text-left text-md font-semibold text-error-700 hover:text-error-500",onClick:()=>{C(!1),c(e)},"data-testid":`menu-remove-${e.id}`,children:"Delete"})]}):null]})]})},Dr=e=>`${Ye().iconCdnBaseUrl}/website/assets/icons/payments/${e}.svg`,yt=({slug:e,alt:r})=>a.jsx("img",{src:Dr(e),alt:r,height:20,loading:"lazy",style:{height:20,width:"auto"}}),ut=({type:e,label:r,iconNode:n,disabled:o=!1})=>a.jsxs("label",{className:"flex cursor-pointer items-center gap-3 rounded-lg border border-grayWarm-200 px-4 py-3 transition-colors hover:border-grayWarm-300","data-testid":`payment-type-${e}`,children:[a.jsx("input",{type:"radio",name:"movmo-payment-type",value:e,disabled:o,onChange:()=>{},className:X("h-4 w-4 accent-grayWarm-900",o?"cursor-not-allowed":"cursor-pointer"),"aria-label":r}),a.jsxs("span",{className:"flex flex-1 items-center gap-2 text-sm font-semibold text-grayWarm-900",children:[r,n]})]}),Ft=({selected:e,onSelect:r,creditCardBody:n,disabled:o=!1,className:l})=>{const c=e==="credit_card";return a.jsxs("div",{className:X("flex w-full flex-col gap-3",l),children:[a.jsxs("label",{className:X("flex cursor-pointer flex-col gap-3 rounded-lg border px-4 py-3 transition-colors",c?"border-grayWarm-900 shadow-[0_0_0_1px_#1c1917]":"border-grayWarm-200"),"data-testid":"payment-type-credit_card",children:[a.jsxs("div",{className:"flex items-center gap-3",children:[a.jsx("input",{type:"radio",name:"movmo-payment-type",value:"credit_card",checked:c,disabled:o,onChange:()=>{o||r("credit_card")},className:X("h-4 w-4 accent-grayWarm-900",o?"cursor-not-allowed":"cursor-pointer"),"aria-label":"Credit card"}),a.jsx("span",{className:"flex-1 text-sm font-semibold text-grayWarm-900",children:"Credit card"}),a.jsxs("div",{className:"flex items-center gap-1.5",children:[a.jsx(Ie,{brand:"mastercard",width:28}),a.jsx(Ie,{brand:"visa",width:28}),a.jsx(Ie,{brand:"amex",width:28}),a.jsx(Ie,{brand:"discover",width:28})]})]}),c?a.jsx("div",{"data-testid":"payment-type-credit_card-body",children:n}):null]}),c?a.jsx("p",{className:"mt-2 text-sm font-semibold text-grayWarm-900","data-testid":"payment-type-other-options-header",children:"Other options"}):null,a.jsx(ut,{type:"paypal",label:"PayPal",iconNode:a.jsx(yt,{slug:"paypal",alt:"PayPal"}),disabled:o}),a.jsx(ut,{type:"googlepay",label:"GooglePay",iconNode:a.jsx(yt,{slug:"google-pay",alt:"GooglePay"}),disabled:o}),a.jsx(ut,{type:"klarna",label:"Klarna",iconNode:a.jsx(yt,{slug:"klarna",alt:"Klarna"}),disabled:o}),a.jsx(ut,{type:"ach",label:"Bank account (ACH)",iconNode:a.jsx("span",{className:"text-xs font-medium text-grayWarm-500",children:"Direct debit"}),disabled:o})]})},Ot="movmo-add-payment-form",$r=e=>{console.error("@movmo_app/payments: failed to update payment method",e);const r=e instanceof Error?e.message.trim():"";return/spreedly-backed cards/i.test(r)?"This card's details can't be edited.":r||"Failed to update payment method."},Wt=e=>{const r=e.filter(o=>o.isDefault),n=e.filter(o=>!o.isDefault);return[...r,...n]},Mt=({message:e,testId:r})=>a.jsx("p",{role:"alert","aria-live":"polite",className:"mt-3 rounded-md bg-error-50 px-3 py-2 text-sm text-error-700","data-testid":r,children:e}),At=({withDefaultBadge:e=!1})=>a.jsxs("div",{className:"flex items-start justify-between py-4","data-testid":"payment-method-skeleton",children:[a.jsxs("div",{className:"flex items-start gap-3",children:[a.jsx("div",{className:"h-8 w-[46px] flex-shrink-0 animate-pulse rounded bg-grayWarm-200"}),a.jsxs("div",{className:"space-y-2",children:[a.jsxs("div",{className:"flex items-center gap-2",children:[a.jsx("div",{className:"h-5 w-[120px] animate-pulse rounded bg-grayWarm-200"}),e?a.jsx("div",{className:"h-5 w-[56px] animate-pulse rounded-full bg-grayWarm-200"}):null]}),a.jsx("div",{className:"h-4 w-[100px] animate-pulse rounded bg-grayWarm-200"})]})]}),a.jsx("div",{className:"h-5 w-5 flex-shrink-0 animate-pulse rounded-full bg-grayWarm-200"})]}),Kt=({userId:e,defaultCardholderName:r,defaultCardholderFirstName:n,defaultCardholderLastName:o,selectedId:l,onSelect:c,onChange:m,onError:v,onEvent:h,paymentTypeSelector:j=!0,autoSaveFirstCard:_=!0,selectionSetsDefault:w=!1,showDefaultBadge:C=!0,collapsible:x=!1,cardLabelFormat:P="branded",className:M})=>{const{items:I,status:k,error:E,refetch:O}=Ht(e),{deletePaymentMethod:pe,status:de}=Bt(e),{setDefault:we,status:ue}=Yt(e),{updatePaymentMethod:se,status:Q}=Vt(e),[b,ye]=s.useState([]),_e=s.useRef(b);s.useEffect(()=>{_e.current=b},[b]);const[z,p]=s.useState(null),[y,S]=s.useState(!1),[A,re]=s.useState(null),[ae,q]=s.useState(!1),[G,V]=s.useState(!1),[T,K]=s.useState(null),[Ce,ve]=s.useState(null),[J,Ee]=s.useState(null),[Z,Pe]=s.useState(!1),Re=s.useRef(!1),oe=s.useRef(v);s.useEffect(()=>{oe.current=v},[v]);const ee=s.useCallback(u=>{var B;(B=oe.current)==null||B.call(oe,u)},[]),Te=s.useRef(h);s.useEffect(()=>{Te.current=h},[h]);const $=s.useCallback((u,B)=>{var L;(L=Te.current)==null||L.call(Te,u,B)},[]),ie=s.useRef(!1),ke=s.useCallback(u=>{if(u.touchedInvalid.length===0){ie.current=!1;return}ie.current||(ie.current=!0,$("validation_error",{field_count:u.touchedInvalid.length,fields:u.touchedInvalid}))},[$]),te=s.useCallback(()=>{S(!1),re(null),Ee(null),ie.current=!1},[]),je=r??([n,o].filter(Boolean).join(" ")||void 0);s.useEffect(()=>{k==="ready"&&ye(Wt(I))},[I,k]);const Fe=s.useRef(null);s.useEffect(()=>{if(k==="error"&&E){if(Fe.current===E)return;Fe.current=E,ee({source:"list",message:E})}else k!=="error"&&(Fe.current=null)},[k,E,ee]),s.useEffect(()=>{if(!c||Re.current||k!=="ready"||b.length===0||l)return;Re.current=!0,$("autoselected",{had_saved_methods:!0});const u=b.find(B=>B.isDefault)??b[0];c(u)},[c,k,b,l,$]);const he=s.useCallback(u=>{ye(B=>{const L=typeof u=="function"?u(B):u,U=Wt(L);return queueMicrotask(()=>m==null?void 0:m(U)),U})},[m]),ze=de==="pending"||ue==="pending"||Q==="pending",ge=s.useCallback(async u=>{const B=b;p(null),he(L=>L.map(U=>({...U,isDefault:U.id===u.id})));try{await we(u.id),O(),$("set_default")}catch(L){ye(B),m==null||m(B);const U=L instanceof Error?L.message:"Failed to set default payment method.";p(U),ee({...nt("set-default",L,"Failed to set default payment method."),message:U})}},[b,he,we,O,m,ee,$]),le=s.useCallback(u=>{c==null||c(u),w&&(u.isDefault||ge(u))},[c,w,ge]),Oe=s.useCallback(u=>{$("selected",{method_count:b.length}),le(u)},[$,b,le]),We=s.useCallback(async u=>{const B=b;p(null);const L=b.filter(Me=>Me.id!==u.id);he(()=>L);const U=u.id===l&&c?L.find(Me=>Me.isDefault)??L[0]??null:null;U&&le(U);const xe=!!U&&w&&U!==null&&!U.isDefault;try{await pe(u.id),xe||O(),$("deleted",{remaining_count:L.length})}catch(Me){ye(B),m==null||m(B);const ct=Me instanceof Error?Me.message:"Failed to delete payment method.";p(ct),ee({...nt("delete",Me,"Failed to delete payment method."),message:ct})}},[b,he,pe,O,m,l,c,le,w,ee,$]),Ae=s.useCallback(u=>{ve(null),K(u)},[]),Le=s.useCallback(()=>{K(null),ve(null)},[]),De=s.useCallback(async u=>{if(!T)return;const B=T,L=b;ve(null),he(U=>U.map(xe=>xe.id===B.id?{...xe,...u.cardholderName!==void 0?{cardholderName:u.cardholderName}:{},...u.zip!==void 0?{zip:u.zip}:{}}:xe));try{await se(B.id,u),Le(),O(),$("edited",{fields:Object.keys(u)})}catch(U){ye(L),m==null||m(L);const xe=$r(U);ve(xe),ee({...nt("update",U,xe),message:xe})}},[T,b,he,se,O,m,Le,ee,$]),Ne=s.useCallback(u=>{te(),he(L=>[u,...L.filter(U=>U.id!==u.id)]),le(u),!!c&&w&&!u.isDefault||O(),$("added",{method_count:_e.current.length+1})},[te,he,O,le,c,w,$]),f=s.useCallback(u=>{re(u)},[]),D=s.useCallback(u=>{V(u),u&&re(null)},[]),me=k==="ready"&&b.length===0,ne=typeof c=="function",st=!ne&&!x,ot=b.length===0,it=_&&b.length===0,$e=s.useCallback(u=>a.jsx(Et,{userId:e,isDefault:ot,defaultCardholderName:je,autoSave:!u&&it,hideInternalSaveButton:u,formId:u?Ot:void 0,onCanSubmitChange:u?q:void 0,onSavingChange:D,autoFocus:u?"number":void 0,onSuccess:Ne,onError:f,onErrorDetail:ee,onValidationChange:ke}),[e,ot,je,it,Ne,f,D,ee,ke]),Ve=s.useMemo(()=>$e(!1),[$e]),Se=s.useMemo(()=>$e(!0),[$e]),Ue=!e,Qe=s.useMemo(()=>j?a.jsx(Ft,{selected:J,onSelect:u=>Ee(u),creditCardBody:Ve,disabled:Ue}):Ve,[j,J,Ve,Ue]),lt=s.useMemo(()=>j?a.jsx(Ft,{selected:J,onSelect:u=>Ee(u),creditCardBody:Se,disabled:Ue}):Se,[j,J,Se,Ue]);return a.jsxs("div",{className:X("flex w-full flex-col gap-4",M),"data-testid":"payment-methods-manager",children:[k==="loading"&&b.length===0?a.jsxs("div",{className:X(st?"flex flex-col divide-y divide-grayWarm-200 rounded-xl border border-grayWarm-200 px-4 md:px-6":"flex flex-col gap-2"),"data-testid":"payment-methods-loading",children:[a.jsx(At,{withDefaultBadge:!0}),a.jsx(At,{})]}):null,k==="error"?a.jsxs("div",{role:"alert","aria-live":"polite",className:"flex items-center justify-between gap-3 rounded-md bg-error-50 p-3 text-sm text-error-700","data-testid":"payment-methods-list-error",children:[a.jsx("span",{children:E??"Failed to load payment methods."}),a.jsx("button",{type:"button",onClick:O,className:"rounded-md border border-error-700 px-3 py-1 text-xs font-semibold text-error-700 hover:bg-error-50",children:"Retry"})]}):null,z?a.jsx("div",{role:"alert","aria-live":"polite",className:"rounded-md bg-error-50 p-3 text-sm text-error-700","data-testid":"payment-methods-mutation-error",children:z}):null,x&&b.length>0?a.jsx("div",{className:"border-t border-grayWarm-200 pt-4",children:a.jsxs("div",{className:"flex items-center justify-between","data-testid":Z?"payment-methods-drawer-header":"payment-methods-collapsed-preview",children:[Z?a.jsx("h3",{className:"text-md font-semibold text-grayWarm-950",children:"Payment methods"}):a.jsx(Gt,{method:b.find(u=>u.id===l)??b.find(u=>u.isDefault)??b[0],cardLabelFormat:P,className:"flex-1"}),a.jsx("button",{type:"button","aria-label":Z?"Collapse payment methods":"Expand payment methods","aria-expanded":Z,className:"bg-grayWarm-100 p-2 rounded-lg hover:bg-grayWarm-200 transition-colors",onClick:()=>Pe(u=>!u),"data-testid":Z?"payment-methods-drawer-close":"payment-methods-drawer-open",children:a.jsx("img",{src:`${Ye().iconCdnBaseUrl}/website/assets/icons/chevron-${Z?"up":"down"}-double.svg`,alt:"",className:"w-5 h-5"})})]})}):null,b.length>0&&(!x||Z)?a.jsx("div",{className:X("flex flex-col",st?"rounded-xl border border-grayWarm-200 px-4 md:px-6 divide-y divide-grayWarm-200":"space-y-3"),children:b.map(u=>a.jsx(Ar,{method:u,selectable:ne,selected:ne&&u.id===l,onSelect:Oe,onSetDefault:ge,onRemove:We,onEdit:Ae,disabled:ze,showDefaultBadge:C,hideSetDefaultMenuItem:w,cardLabelFormat:P},u.id))}):null,me?a.jsxs("div",{"data-testid":"payment-methods-empty",children:[Qe,A&&!y?a.jsx(Mt,{message:A,testId:"payment-methods-inline-error"}):null]}):b.length>0&&(!x||Z)?a.jsx("button",{type:"button",onClick:()=>{re(null),Ee(null),q(!1),S(!0),ie.current=!1},onMouseEnter:()=>{xt().catch(()=>{})},onFocus:()=>{xt().catch(()=>{})},className:"self-start rounded-lg bg-brand-50 px-6 py-3 text-md font-semibold text-brand-600 transition-colors hover:bg-brand-100","data-testid":"payment-methods-add-trigger",children:"Add payment method"}):null,a.jsxs(Sr,{open:y,onClose:te,formId:Ot,saveDisabled:!ae,isSaving:G,children:[lt,A?a.jsx(Mt,{message:A,testId:"payment-methods-modal-error"}):null]}),a.jsx(Fr,{open:T!==null,method:T,onClose:Le,onSave:De,isSaving:Q==="pending",error:Ce})]})};Kt.displayName="PaymentMethodsManager";const Ur="/v1/auth/",Ir="movmo_csrf_token",Jt=new Set(["reauth_required","movmo_session_required","first_party_only","recovery_cooldown"]),zr=e=>{if(typeof document>"u")return null;const r=document.cookie?document.cookie.split(";"):[];for(const n of r){const o=n.trim();if(o.startsWith(e+"="))return decodeURIComponent(o.substring(e.length+1))}return null},Lr=e=>typeof e=="string"?e:e instanceof URL?e.href:e.url,Br=()=>{if(typeof globalThis<"u"&&typeof globalThis.fetch=="function")return globalThis.fetch.bind(globalThis);throw new Error("@movmo_app/payments: no global fetch available in this environment")},Yr=e=>{const{refreshSession:r,onRefreshFailed:n,csrfCookieName:o=Ir,extraHeaders:l,authPathFragment:c=Ur,baseFetch:m=Br(),readCookie:v=zr}=e,h=new Set(e.noRefreshCodes??Jt);let j=null;const _=()=>{if(j)return j;const C=(async()=>{await r()})();return j=C,C.finally(()=>{j===C&&(j=null)}).catch(()=>{}),C},w=(C,x)=>{const P=new Headers(x==null?void 0:x.headers),M=v(o);if(M&&!P.has("X-CSRF-Token")&&P.set("X-CSRF-Token",M),l){const I=l(C,x);for(const[k,E]of Object.entries(I))P.has(k)||P.set(k,E)}return{...x,headers:P,credentials:(x==null?void 0:x.credentials)??"include"}};return async function(x,P){const M=await m(x,w(x,P));if(Lr(x).includes(c)||M.status!==401&&M.status!==403)return M;if(M.status===403)try{const E=await M.clone().json(),O=E==null?void 0:E.code;if(typeof O=="string"&&h.has(O))return M}catch{}try{await _()}catch(E){try{n(E)}catch(O){console.error("@movmo_app/payments: onRefreshFailed callback threw — auth retry path is misconfigured",O)}return M}try{return await m(x,w(x,P))}catch(E){return console.error("@movmo_app/payments: retry after token refresh failed — returning original response",E),M}}};exports.DEFAULT_NO_REFRESH_CODES=Jt;exports.MovmoCardForm=Et;exports.PaymentMethodPreview=Gt;exports.PaymentMethodsManager=Kt;exports.PaymentsApiError=wt;exports.createMovmoAuthFetch=Yr;exports.getPaymentsConfig=Ye;exports.setPaymentsConfig=cr;exports.useDeletePaymentMethod=Bt;exports.useMovmoCardFields=Ut;exports.useSetDefaultPaymentMethod=Yt;exports.useUpdatePaymentMethod=Vt;exports.useUserPaymentMethods=Ht;
30
+ <%s key={someKey} {...props} />`,mt,Be,tr,Be),jt[Be+mt]=!0}}return t===o?ct(H):Me(H),H}}function Zt(t,i,d){return _t(t,i,d,!0)}function Xt(t,i,d){return _t(t,i,d,!1)}var Qt=Xt,er=Zt;tt.Fragment=o,tt.jsx=Qt,tt.jsxs=er}()),tt}process.env.NODE_ENV==="production"?vt.exports=sr():vt.exports=or();var a=vt.exports;function Dt(e){var r,n,o="";if(typeof e=="string"||typeof e=="number")o+=e;else if(typeof e=="object")if(Array.isArray(e)){var l=e.length;for(r=0;r<l;r++)e[r]&&(n=Dt(e[r]))&&(o&&(o+=" "),o+=n)}else for(n in e)e[n]&&(o&&(o+=" "),o+=n);return o}function X(){for(var e,r,n=0,o="",l=arguments.length;n<l;n++)(e=arguments[n])&&(r=Dt(e))&&(o&&(o+=" "),o+=r);return o}const ir="https://core.spreedly.com/iframe/iframe-v1.min.js",lr="https://cdn.e2e.movmo.io";let Ge={};const cr=e=>{Ge={...Ge,...e}},ft=e=>{var n;const r=globalThis.process;return(n=r==null?void 0:r.env)==null?void 0:n[e]},Ye=()=>({baseUrl:Ge.baseUrl??ft("REACT_APP_MOVMO_API_URL")??"",spreedlyScriptUrl:Ge.spreedlyScriptUrl??ft("REACT_APP_SPREEDLY_SCRIPT_URL")??ir,iconCdnBaseUrl:Ge.iconCdnBaseUrl??ft("REACT_APP_MOVMO_ICON_CDN_URL")??lr,fetch:Ge.fetch??(typeof globalThis<"u"&&typeof globalThis.fetch=="function"?globalThis.fetch.bind(globalThis):(()=>{throw new Error("@movmo_app/payments: no fetch available")})())}),dr="recovery_cooldown",ur=(e,r)=>{if(!e)return"";const n=new Date(e);if(Number.isNaN(n.getTime()))return"";try{return new Intl.DateTimeFormat("en-US",{month:"short",day:"numeric",year:"numeric",hour:"numeric",minute:"2-digit",timeZone:r}).format(n)}catch{return""}},mr=e=>{const r=ur(e??"");return r?`Account recovery is in progress. You can change payment methods after ${r}.`:"Account recovery is in progress. You can change payment methods once it ends."},gt=e=>({id:e.id,last4:e.last4,brand:e.brand,expMonth:e.expMonth,expYear:e.expYear,isDefault:e.isDefault,vault:e.vault,cardholderName:e.cardholderName,zip:e.zip}),Ke={Accept:"application/json","Content-Type":"application/json"},Je=e=>{const{baseUrl:r}=Ye();if(!r)throw new Error("@movmo_app/payments: baseUrl is not configured. Call setPaymentsConfig({ baseUrl }) at app boot, or set REACT_APP_MOVMO_API_URL.");return`${r}${e}`},Ze=(e,r)=>{const{fetch:n}=Ye();return n(e,r)};class wt extends Error{constructor(n,o){super(n);qe(this,"status");qe(this,"code");qe(this,"until");this.name="PaymentsApiError",this.status=o.status,this.code=o.code,this.until=o.until}}const fr=async e=>{try{const r=await e.json();if(typeof r!="object"||r===null)return{};const{message:n,code:o,until:l}=r;return{message:typeof n=="string"?n:void 0,code:typeof o=="string"?o:void 0,until:typeof l=="string"?l:void 0}}catch{return{}}},Xe=async(e,r)=>{const{message:n,code:o,until:l}=await fr(e),c=o===dr?mr(l):n||r;return new wt(c,{status:e.status,code:o,until:l})};let rt=null;const xt=()=>{if(rt)return rt;const e=(async()=>{const r=await Ze(Je("/v1/payments/tokenization-session"),{method:"GET",headers:Ke,credentials:"include"});if(!r.ok)throw await Xe(r,`Failed to fetch tokenization session (${r.status})`);return await r.json()})();return rt=e,e.finally(()=>{rt===e&&(rt=null)}).catch(()=>{}),e},pr=e=>typeof e.brand=="string"&&typeof e.last4=="string"&&typeof e.expMonth=="number"&&typeof e.expYear=="number",hr=async(e,r,n)=>{const o=await Ze(Je(`/v1/users/${encodeURIComponent(e)}/payment-methods/from-token`),{method:"POST",headers:Ke,credentials:"include",body:JSON.stringify({tokenizedPaymentMethodId:r,isDefault:n})});if(!o.ok)throw await Xe(o,`Failed to save payment method (${o.status})`);const l=await o.json();if(pr(l))return gt(l);const c=await Ze(Je(`/v1/users/${encodeURIComponent(e)}/payment-methods/${encodeURIComponent(l.id)}`),{method:"GET",headers:Ke,credentials:"include"});if(!c.ok)throw await Xe(c,`Failed to fetch saved payment method (${c.status})`);const m=await c.json();return gt(m)},yr=async(e,r)=>{if(!e)return[];const n=await Ze(Je(`/v1/users/${encodeURIComponent(e)}/payment-methods`),{method:"GET",headers:Ke,credentials:"include",signal:r});if(!n.ok)throw await Xe(n,`Failed to fetch payment methods (${n.status})`);return(await n.json()??[]).map(gt)},vr=async(e,r)=>{const n=await Ze(Je(`/v1/users/${encodeURIComponent(e)}/payment-methods/${encodeURIComponent(r)}`),{method:"DELETE",headers:Ke,credentials:"include"});if(!n.ok)throw await Xe(n,`Failed to delete payment method (${n.status})`)},$t=async(e,r,n)=>{const o=await Ze(Je(`/v1/users/${encodeURIComponent(e)}/payment-methods/${encodeURIComponent(r)}`),{method:"PUT",headers:Ke,credentials:"include",body:JSON.stringify(n)});if(!o.ok)throw await Xe(o,`Failed to update payment method (${o.status})`)},nt=(e,r,n)=>r instanceof wt?{source:e,message:r.message,cause:r,status:r.status,...r.code!==void 0?{code:r.code}:{},...r.until!==void 0?{until:r.until}:{}}:{source:e,message:r instanceof Error?r.message:n,cause:r},gr={visa:"visa",master:"mastercard",mastercard:"mastercard",american_express:"amex",amex:"amex",discover:"discover"},xr=28,Ie=({brand:e,className:r,width:n=46})=>{const o=e?gr[e.toLowerCase()]??"credit-card":"credit-card",l=Math.max(n,xr),c=Math.round(l*24/34),{iconCdnBaseUrl:m}=Ye();return a.jsx("img",{src:`${m}/website/assets/icons/payments/${o}.svg`,alt:o==="credit-card"?"card":`${o} card`,"data-testid":`card-brand-${e.toLowerCase()}`,className:r,width:l,height:c,loading:"lazy"})};let at=null;const br=()=>{if(typeof window>"u")return Promise.reject(new Error("Spreedly can only be loaded in a browser environment."));if(window.Spreedly)return Promise.resolve(window.Spreedly);if(at)return at;const{spreedlyScriptUrl:e}=Ye();return at=new Promise((r,n)=>{const o=document.querySelector(`script[src="${e}"]`),l=()=>{window.Spreedly?r(window.Spreedly):n(new Error("Spreedly script loaded but window.Spreedly is undefined."))};if(o){if(window.Spreedly){l();return}o.addEventListener("load",l,{once:!0}),o.addEventListener("error",()=>{o.remove(),n(new Error("Failed to load Spreedly iframe script."))},{once:!0});return}const c=document.createElement("script");c.src=e,c.async=!0,c.onload=l,c.onerror=()=>{c.remove(),n(new Error("Failed to load Spreedly iframe script."))},document.head.appendChild(c)}).catch(r=>{throw at=null,r}),at};class dt extends Error{constructor(n,o){super(o instanceof Error?o.message:"Failed to initialize card form.");qe(this,"source");qe(this,"cause");this.name="InitError",this.source=n,this.cause=o}}const wr="movmo-card-number",Er="movmo-card-cvv",jr=e=>({first_name:e.firstName,last_name:e.lastName,month:e.month,year:e.year,...e.email?{email:e.email}:{},...e.zip?{zip:e.zip}:{}}),Ut=(e={})=>{const{numberEl:r=wr,cvvEl:n=Er,onCardTokenized:o,onFieldErrors:l,onValidityChange:c,onBrandChange:m}=e,[v,h]=s.useState("idle"),[j,_]=s.useState(null),[w,C]=s.useState(null),[x,P]=s.useState(null),[M,I]=s.useState({number:!1,cvv:!1}),[k,E]=s.useState(null),[O,pe]=s.useState({number:!1,cvv:!1}),de=s.useRef(null),we=s.useRef(o),ue=s.useRef(l),se=s.useRef(c),Q=s.useRef(m);s.useEffect(()=>{we.current=o},[o]),s.useEffect(()=>{ue.current=l},[l]),s.useEffect(()=>{se.current=c},[c]),s.useEffect(()=>{Q.current=m},[m]);const b=s.useRef({number:!1,cvv:!1});s.useEffect(()=>{let z=!1;return h("loading"),_(null),C(null),P(null),I({number:!1,cvv:!1}),E(null),b.current={number:!1,cvv:!1},(async()=>{try{const[p,y]=await Promise.all([xt().catch(S=>{throw new dt("tokenization-session",S)}),br().catch(S=>{throw new dt("spreedly-script",S)})]);if(z)return;de.current=y,y.on("ready",()=>{var S,A,re,ae,q,G;if(!z){h("ready");try{(S=y.setFieldType)==null||S.call(y,"number","tel"),(A=y.setFieldType)==null||A.call(y,"cvv","tel")}catch(V){console.warn("@movmo_app/payments: setFieldType failed",V)}try{typeof y.setNumberFormat=="function"?y.setNumberFormat("prettyFormat"):console.warn("@movmo_app/payments: Spreedly.setNumberFormat is missing — card-number digits will not be space-grouped.")}catch(V){console.warn("@movmo_app/payments: setNumberFormat failed",V)}try{(re=y.setPlaceholder)==null||re.call(y,"number","Card number"),(ae=y.setPlaceholder)==null||ae.call(y,"cvv","CVV");const V='box-sizing: border-box;width: 100%;height: 100%;margin: 0;padding: 0;border: 0;background: transparent;outline: none;font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;font-size: 14px;font-weight: 400;line-height: 20px;color: rgb(28 25 23);letter-spacing: 0;vertical-align: middle;';(q=y.setStyle)==null||q.call(y,"number",V),(G=y.setStyle)==null||G.call(y,"cvv",V)}catch(V){console.warn("@movmo_app/payments: setPlaceholder/setStyle failed",V)}}}),y.on("paymentMethod",S=>{var A;(A=we.current)==null||A.call(we,S)}),y.on("errors",S=>{var A;(A=ue.current)==null||A.call(ue,S)}),y.on("fieldEvent",(S,A,re,ae)=>{var V;if(z)return;if(A==="focus"){pe(T=>T[S]?T:{...T,[S]:!0});return}if(A==="blur"){pe(T=>T[S]?{...T,[S]:!1}:T);return}if(A!=="input")return;const q=ae??{};if(S==="number"){if(q.numberLength===0)E(T=>{var K;return T!==null&&((K=Q.current)==null||K.call(Q,null)),null});else if(q.cardType!==void 0){const T=q.cardType||null;E(K=>{var Ce;return K!==T&&((Ce=Q.current)==null||Ce.call(Q,T)),T})}}const G=S==="number"?typeof q.validNumber=="boolean"?q.validNumber:void 0:typeof q.validCvv=="boolean"?q.validCvv:void 0;if(typeof G=="boolean"){const T=b.current,K=S==="number"?{...T,number:G}:{...T,cvv:G};(K.number!==T.number||K.cvv!==T.cvv)&&(b.current=K,I(K),(V=se.current)==null||V.call(se,K))}}),y.on("consoleError",S=>{var re;const A=S instanceof Error?S.message:"An unexpected error occurred while securing your card.";(re=ue.current)==null||re.call(ue,[{message:A}])}),y.init(p.environmentKey,{numberEl:r,cvvEl:n,nonce:p.nonce,timestamp:String(p.timestamp),certificateToken:p.certificateToken,signature:p.signature})}catch(p){if(z)return;const y=p instanceof Error?p.message:"Failed to initialize card form.";_(y),C(p instanceof dt?p.source:"spreedly-script"),P(p instanceof dt?p.cause:p),h("error")}})(),()=>{var p,y;z=!0;try{(y=(p=de.current)==null?void 0:p.removeHandlers)==null||y.call(p)}catch(S){console.warn("@movmo_app/payments: removeHandlers failed during cleanup",S)}de.current=null}},[r,n]);const ye=s.useCallback(z=>{var p;(p=de.current)==null||p.tokenizeCreditCard(jr(z))},[]),_e=s.useCallback(z=>{var p,y;(y=(p=de.current)==null?void 0:p.transferFocus)==null||y.call(p,z)},[]);return{status:v,error:j,errorSource:w,errorCause:x,tokenize:ye,validity:M,brand:k,focused:O,focusField:_e}},bt={number:"Enter your card number",expiry:"Enter a valid expiry date",cvv:"Enter the security code",zip:"Enter your postal code",name:"Enter first and last name as printed on the card"},_r="Your card saves automatically once all fields are complete.",It=e=>/^[A-Za-z0-9][A-Za-z0-9 -]{1,8}[A-Za-z0-9]$/.test(e.trim()),zt=e=>{const r=e.replace(/[^A-Za-z0-9 -]/g,"").slice(0,10);if(!/^[\d-]*$/.test(r))return r;const n=r.replace(/-+/g,"-").replace(/^-/,"");return n.includes("-")?n:n.length===9?`${n.slice(0,5)}-${n.slice(5)}`:n},Cr=({hookStatus:e,validity:r,nameValid:n,expiryValid:o,zipValid:l})=>{const c=e==="ready",m=[],v=h=>m.push({field:h,message:bt[h]});return c&&!r.number&&v("number"),o||v("expiry"),c&&!r.cvv&&v("cvv"),l||v("zip"),n||v("name"),m},pt="h-11 w-full rounded-md border bg-white px-3 py-2 text-sm text-grayWarm-900 placeholder:text-grayWarm-400 outline-none focus:border-grayWarm-950 focus:shadow-[0_0_0_1px_#1c1917]",kt="flex h-11 w-full items-center gap-2 rounded-md border bg-white px-3 py-2 transition-shadow",Nt="border-grayWarm-950 shadow-[0_0_0_1px_#1c1917]",St="border-grayWarm-300",Pt="border-error-500",Rr={number:!1,expiry:!1,cvv:!1,zip:!1,name:!1},kr=e=>{const r=e.trim();if(!r)return{firstName:"",lastName:""};const n=r.lastIndexOf(" ");return n===-1?{firstName:r,lastName:""}:{firstName:r.slice(0,n).trim(),lastName:r.slice(n+1).trim()}},Lt=e=>{const r=e.replace(/\D/g,"");if(r.length<4)return{month:"",year:""};const n=r.slice(0,2),o=r.slice(2),l=o.length===2?`20${o}`:o.slice(0,4);return{month:n,year:l}},Nr=e=>{const r=e.replace(/\D/g,"").slice(0,4);return r.length<=2?r:`${r.slice(0,2)} / ${r.slice(2)}`},ht=e=>{const{month:r,year:n}=Lt(e);if(!r||!n)return!1;const o=parseInt(r,10),l=parseInt(n,10);if(isNaN(o)||o<1||o>12)return!1;const c=new Date;return!(l<c.getFullYear()||l>c.getFullYear()+20||l===c.getFullYear()&&o<c.getMonth()+1)},Et=({userId:e,onSuccess:r,onError:n,onErrorDetail:o,isDefault:l=!1,className:c,defaultCardholderName:m,defaultCardholderFirstName:v="",defaultCardholderLastName:h="",autoSave:j=!1,formId:_="movmo-card-form",hideInternalSaveButton:w=!1,onCanSubmitChange:C,onSavingChange:x,autoFocus:P,onValidationChange:M})=>{const I=m??[v,h].filter(Boolean).join(" "),[k,E]=s.useState(I),[O,pe]=s.useState(""),[de,we]=s.useState(""),[ue,se]=s.useState("idle"),Q=s.useRef(!1),b=s.useRef(o);s.useEffect(()=>{b.current=o},[o]);const ye=s.useCallback(async f=>{var D;se("saving");try{const me=await hr(e,f,l);se("ready"),r(me)}catch(me){const ne=me instanceof Error?me.message:"Failed to save payment method.";se("ready"),Q.current=!1,n(ne),(D=b.current)==null||D.call(b,{...nt("save-from-token",me,ne),message:ne})}},[e,l,r,n]),_e=s.useCallback(f=>{var me;const D=f.map(ne=>ne.message).filter(Boolean).join("; ")||"Card validation failed.";se(ne=>ne==="tokenizing"?"ready":ne),Q.current=!1,n(D),(me=b.current)==null||me.call(b,{source:"card-tokenize",message:D})},[n]),z=s.useMemo(()=>({onCardTokenized:ye,onFieldErrors:_e}),[ye,_e]),{status:p,error:y,errorSource:S,errorCause:A,tokenize:re,validity:ae,brand:q,focused:G,focusField:V}=Ut(z),T=s.useRef(!1);s.useEffect(()=>{!P||p!=="ready"||T.current||(T.current=!0,V(P))},[P,p,V]);const K=k.trim().split(/\s+/).length>=2,Ce=ht(O),ve=It(de),[J,Ee]=s.useState(Rr),Z=s.useCallback(f=>Ee(D=>D[f]?D:{...D,[f]:!0}),[]),Pe=s.useRef(G);s.useEffect(()=>{const f=Pe.current;Pe.current=G,f.number&&!G.number&&Z("number"),f.cvv&&!G.cvv&&Z("cvv")},[G,Z]);const Re=s.useMemo(()=>Cr({hookStatus:p,validity:ae,nameValid:K,expiryValid:Ce,zipValid:ve}),[p,ae,K,Ce,ve]),oe=f=>Re.some(D=>D.field===f),ee=s.useMemo(()=>Re.map(f=>f.field),[Re]),Te=s.useMemo(()=>ee.filter(f=>J[f]),[ee,J]),$=s.useRef(M);s.useEffect(()=>{$.current=M},[M]),s.useEffect(()=>{var f;(f=$.current)==null||f.call($,{valid:ee.length===0,missing:ee,touchedInvalid:Te})},[ee,Te]);const ie=J.number&&oe("number"),ke=J.name&&oe("name"),te=Re.find(f=>(f.field==="expiry"||f.field==="cvv"||f.field==="zip")&&J[f.field]),je=s.useRef(!1),Fe=s.useRef(!1),he=s.useRef(null);s.useEffect(()=>{var f;ae.cvv?Fe.current||(Fe.current=!0,(f=he.current)==null||f.focus()):Fe.current=!1},[ae.cvv]);const ze=!K||!Ce||!ve||!ae.number||!ae.cvv,ge=ue==="tokenizing"||ue==="saving",le=p==="ready"&&!ge&&!ze,Oe=s.useRef(C);s.useEffect(()=>{Oe.current=C},[C]),s.useEffect(()=>{var f;(f=Oe.current)==null||f.call(Oe,le)},[le]);const We=s.useRef(x);s.useEffect(()=>{We.current=x},[x]),s.useEffect(()=>{var f;(f=We.current)==null||f.call(We,ge)},[ge]);const Ae=s.useCallback(()=>{const{firstName:f,lastName:D}=kr(k),{month:me,year:ne}=Lt(O);se("tokenizing"),re({firstName:f,lastName:D,month:me,year:ne,zip:de.trim()})},[k,O,de,re]);s.useEffect(()=>{if(!j||!le||Q.current)return;const f=setTimeout(()=>{Q.current=!0,Ae()},1200);return()=>clearTimeout(f)},[j,le,Ae]);const Le=f=>{f.preventDefault(),le&&Ae()},De=p!=="ready"||ge,Ne=s.useRef(n);return s.useEffect(()=>{Ne.current=n},[n]),s.useEffect(()=>{var f,D;y&&((f=Ne.current)==null||f.call(Ne,y),(D=b.current)==null||D.call(b,{...nt(S??"spreedly-script",A,y),message:y}))},[y,S,A]),a.jsxs("form",{id:_,onSubmit:Le,className:X("flex w-full flex-col gap-3",c),"data-testid":"movmo-card-form",children:[a.jsxs("div",{children:[a.jsx("span",{id:"movmo-card-number-label",className:"sr-only",children:"Card number"}),a.jsxs("div",{className:X(kt,G.number?Nt:ie?Pt:St),"data-testid":"movmo-card-number-shell",children:[a.jsx(Ie,{brand:q??"",width:32,className:"flex-shrink-0"}),a.jsxs("div",{className:"relative h-full flex-1",children:[a.jsx("div",{id:"movmo-card-number","aria-labelledby":"movmo-card-number-label","aria-busy":p!=="ready","aria-describedby":ie?"movmo-card-number-error":void 0,className:"absolute inset-0","data-testid":"movmo-card-number-mount"}),p!=="ready"?a.jsx("div",{"aria-hidden":"true",className:"pointer-events-none absolute inset-y-2 left-0 right-2 animate-pulse rounded bg-grayWarm-200"}):null]})]}),ie&&a.jsx("p",{id:"movmo-card-number-error",className:"mt-1 text-xs text-error-700","data-testid":"movmo-card-number-error",children:bt.number})]}),a.jsxs("div",{children:[a.jsxs("div",{className:"grid grid-cols-3 gap-3",children:[a.jsxs("div",{children:[a.jsx("label",{htmlFor:"movmo-card-expiry",className:"sr-only",children:"Expiry MM / YY"}),a.jsx("input",{id:"movmo-card-expiry",type:"text",inputMode:"numeric",autoComplete:"cc-exp",placeholder:"MM / YY",value:O,onChange:f=>{const D=Nr(f.target.value);pe(D),ht(D)&&!je.current?(je.current=!0,V("cvv")):ht(D)||(je.current=!1)},onBlur:()=>Z("expiry"),"aria-invalid":J.expiry&&oe("expiry"),"aria-describedby":(te==null?void 0:te.field)==="expiry"?"movmo-card-row-error":void 0,className:X(pt,J.expiry&&oe("expiry")?"border-error-500":"border-grayWarm-300"),disabled:De,required:!0,"data-testid":"movmo-card-expiry-input"})]}),a.jsxs("div",{className:"relative",children:[a.jsx("span",{id:"movmo-card-cvv-label",className:"sr-only",children:"CVV"}),a.jsxs("div",{className:X(kt,G.cvv?Nt:J.cvv&&oe("cvv")?Pt:St),"data-testid":"movmo-card-cvv-shell",children:[a.jsxs("div",{className:"relative h-full flex-1",children:[a.jsx("div",{id:"movmo-card-cvv","aria-labelledby":"movmo-card-cvv-label","aria-busy":p!=="ready","aria-describedby":(te==null?void 0:te.field)==="cvv"?"movmo-card-row-error":void 0,className:"absolute inset-0","data-testid":"movmo-card-cvv-mount"}),p!=="ready"?a.jsx("div",{"aria-hidden":"true",className:"pointer-events-none absolute inset-y-2 left-0 right-2 animate-pulse rounded bg-grayWarm-200"}):null]}),a.jsx("button",{type:"button","aria-label":"CVV help",tabIndex:-1,className:"flex h-5 w-5 flex-shrink-0 items-center justify-center rounded-full border border-grayWarm-300 text-xs text-grayWarm-500",title:q==="american_express"?"The 4 numbers on the front of your card.":"The 3 numbers on the back of your card.","data-testid":"movmo-cvv-help",children:"?"})]})]}),a.jsxs("div",{children:[a.jsx("label",{htmlFor:"movmo-card-zip",className:"sr-only",children:"Postal code"}),a.jsx("input",{id:"movmo-card-zip",ref:he,type:"text",autoComplete:"postal-code",placeholder:"Postal code",value:de,onChange:f=>{we(zt(f.target.value))},onBlur:()=>Z("zip"),"aria-invalid":J.zip&&oe("zip"),"aria-describedby":(te==null?void 0:te.field)==="zip"?"movmo-card-row-error":void 0,className:X(pt,J.zip&&oe("zip")?"border-error-500":"border-grayWarm-300"),disabled:De,required:!0,"data-testid":"movmo-card-zip-input"})]})]}),te&&a.jsx("p",{id:"movmo-card-row-error",className:"mt-1 text-xs text-error-700","data-testid":"movmo-card-row-error",children:te.message})]}),a.jsxs("div",{children:[a.jsx("label",{htmlFor:"movmo-card-cardholder-name",className:"sr-only",children:"Cardholder name"}),a.jsx("input",{id:"movmo-card-cardholder-name",type:"text",autoComplete:"cc-name",placeholder:"Cardholder name",value:k,onChange:f=>E(f.target.value),onBlur:()=>Z("name"),"aria-invalid":ke,"aria-describedby":ke?"movmo-card-name-error":void 0,className:X(pt,ke?"border-error-500":"border-grayWarm-300"),disabled:De,required:!0,"data-testid":"movmo-card-cardholder-name-input"}),ke&&a.jsx("p",{id:"movmo-card-name-error",className:"mt-1 text-xs text-error-700","data-testid":"movmo-card-name-error",children:bt.name})]}),!j&&!w?a.jsx("button",{type:"submit",disabled:!le,className:X("inline-flex h-11 items-center justify-center rounded-md bg-brand-500 px-4 text-sm font-semibold text-white transition-colors hover:bg-brand-600","disabled:cursor-not-allowed disabled:bg-grayWarm-300 disabled:text-grayWarm-500"),"data-testid":"movmo-card-save",children:ge?"Saving…":p==="loading"?"Loading…":"Save card"}):j?a.jsx("div",{className:"text-center","aria-live":"polite","data-testid":"movmo-card-autosave-status",children:ge?a.jsx("p",{className:"text-sm text-grayWarm-500","data-testid":"movmo-card-autosaving",children:"Saving card…"}):Re.length>0?a.jsx("p",{className:"text-xs text-grayWarm-600","data-testid":"movmo-card-autosave-hint",children:_r}):null}):null]})};Et.displayName="MovmoCardForm";const Bt=e=>{const[r,n]=s.useState("idle"),[o,l]=s.useState(null);return{deletePaymentMethod:s.useCallback(async m=>{n("pending"),l(null);try{await vr(e,m),n("idle")}catch(v){const h=v instanceof Error?v.message:"Failed to delete payment method.";throw l(h),n("error"),v}},[e]),status:r,error:o}},Yt=e=>{const[r,n]=s.useState("idle"),[o,l]=s.useState(null);return{setDefault:s.useCallback(async m=>{n("pending"),l(null);try{await $t(e,m,{isDefault:!0}),n("idle")}catch(v){const h=v instanceof Error?v.message:"Failed to set default payment method.";throw l(h),n("error"),v}},[e]),status:r,error:o}},Vt=e=>{const[r,n]=s.useState("idle"),[o,l]=s.useState(null);return{updatePaymentMethod:s.useCallback(async(m,v)=>{n("pending"),l(null);try{await $t(e,m,v),n("idle")}catch(h){const j=h instanceof Error?h.message:"Failed to update payment method.";throw l(j),n("error"),h}},[e]),status:r,error:o}},Ht=e=>{const[r,n]=s.useState([]),[o,l]=s.useState("idle"),[c,m]=s.useState(null),[v,h]=s.useState(0),j=s.useRef(null);s.useEffect(()=>{var C;if((C=j.current)==null||C.abort(),!e){j.current=null,n([]),m(null),l("ready");return}const w=new AbortController;return j.current=w,l("loading"),m(null),yr(e,w.signal).then(x=>{w.signal.aborted||(n(x),l("ready"))}).catch(x=>{if(w.signal.aborted||x instanceof DOMException&&x.name==="AbortError")return;const P=x instanceof Error?x.message:"Failed to fetch payment methods.";m(P),l("error")}),()=>{w.abort()}},[e,v]);const _=s.useCallback(()=>{h(w=>w+1)},[]);return{items:r,status:o,error:c,refetch:_}},qt=({open:e,onClose:r,title:n,titleId:o,formId:l,saveDisabled:c,saveLabel:m="Save",isSaving:v=!1,testIdPrefix:h="payment-method",children:j})=>(s.useEffect(()=>{if(!e)return;const _=w=>{w.key==="Escape"&&r()};return document.addEventListener("keydown",_),()=>document.removeEventListener("keydown",_)},[e,r]),s.useEffect(()=>{if(!e)return;const _=document.body.style.overflow;return document.body.style.overflow="hidden",()=>{document.body.style.overflow=_}},[e]),e?a.jsx("div",{role:"dialog","aria-modal":"true","aria-labelledby":o,className:"fixed inset-0 z-50 flex items-center justify-center bg-black/40 p-4",onClick:_=>{_.target===_.currentTarget&&r()},"data-testid":`${h}-modal`,children:a.jsxs("div",{className:"flex max-h-full w-full max-w-md flex-col rounded-xl bg-white shadow-2xl",children:[a.jsxs("div",{className:"flex items-center gap-3 border-b border-grayWarm-200 px-4 py-3",children:[a.jsx("button",{type:"button",onClick:r,"aria-label":`Close ${n.toLowerCase()} dialog`,className:"flex h-9 w-9 flex-shrink-0 items-center justify-center rounded-lg bg-grayWarm-100 p-2 text-grayWarm-950 transition-colors hover:bg-grayWarm-200","data-testid":`${h}-close`,children:a.jsx("svg",{viewBox:"0 0 20 20",width:"20",height:"20","aria-hidden":"true",children:a.jsx("path",{d:"M5 5L15 15M15 5L5 15",stroke:"currentColor",strokeWidth:"1.67",strokeLinecap:"round",strokeLinejoin:"round"})})}),a.jsx("h2",{id:o,className:"flex-1 text-center text-base font-semibold text-grayWarm-900",children:n}),a.jsx("span",{"aria-hidden":"true",className:"h-9 w-9 flex-shrink-0"})]}),a.jsx("div",{className:"flex-1 overflow-y-auto p-4",children:j}),a.jsxs("div",{className:"flex gap-3 border-t border-grayWarm-200 p-4",children:[a.jsx("button",{type:"button",onClick:r,className:"inline-flex h-11 flex-1 items-center justify-center rounded-lg border border-grayWarm-300 px-4 text-sm font-semibold text-grayWarm-900 transition-colors hover:border-grayWarm-900 hover:shadow-[0_0_0_1px_#1c1917]","data-testid":`${h}-cancel`,children:"Cancel"}),a.jsx("button",{type:"submit",form:l,disabled:v?!1:c,"aria-disabled":v||c,"aria-busy":v,className:X("inline-flex h-11 flex-1 items-center justify-center rounded-lg bg-brand-500 px-4 text-sm font-semibold text-white transition-colors",v?"cursor-not-allowed opacity-90 pointer-events-none":"hover:bg-brand-600 disabled:cursor-not-allowed disabled:bg-grayWarm-300 disabled:text-grayWarm-500"),"data-testid":`${h}-save`,children:v?a.jsxs(a.Fragment,{children:[a.jsxs("svg",{className:"h-[18px] w-[18px] animate-spin",viewBox:"0 0 24 24",fill:"none","aria-hidden":"true",children:[a.jsx("circle",{cx:"12",cy:"12",r:"10",stroke:"currentColor",strokeWidth:"3",strokeOpacity:"0.25"}),a.jsx("path",{d:"M22 12a10 10 0 0 1-10 10",stroke:"currentColor",strokeWidth:"3",strokeLinecap:"round"})]}),a.jsx("span",{className:"sr-only",children:"Saving"})]}):m})]})]})}):null),Sr=({open:e,onClose:r,formId:n,saveDisabled:o,saveLabel:l="Save",isSaving:c=!1,children:m})=>a.jsx(qt,{open:e,onClose:r,title:"Add payment method",titleId:"movmo-add-payment-title",formId:n,saveDisabled:o,saveLabel:l,isSaving:c,testIdPrefix:"add-payment-method",children:m}),Pr=(e,r)=>`${String(e).padStart(2,"0")}/${r}`,Tr=e=>{if(!e)return"Card";const r=e.toLowerCase();return r==="amex"?"Amex":r.charAt(0).toUpperCase()+r.slice(1)},Fr=({open:e,method:r,onClose:n,onSave:o,isSaving:l=!1,error:c=null})=>{const m=s.useId(),[v,h]=s.useState(""),[j,_]=s.useState("");if(s.useEffect(()=>{e&&r&&(h(r.cardholderName??""),_(r.zip??""))},[e,r]),!r)return null;const w=v.trim(),C=j.trim(),x=w!==(r.cardholderName??"").trim(),P=C!==(r.zip??"").trim(),M=x||P,I=(!x||w.length>0)&&(!P||It(C)),k=!M||!I,E=O=>{if(O.preventDefault(),k||l)return;const pe={};x&&(pe.cardholderName=w),P&&(pe.zip=C),o(pe).catch(()=>{})};return a.jsxs(qt,{open:e,onClose:n,title:"Edit payment method",titleId:"movmo-edit-payment-title",formId:m,saveDisabled:k,isSaving:l,testIdPrefix:"edit-payment-method",children:[a.jsxs("div",{className:"mb-4 flex items-start gap-3 rounded-lg bg-grayWarm-50 p-3","data-testid":"edit-payment-method-context",children:[a.jsx(Ie,{brand:r.brand,className:"flex-shrink-0"}),a.jsxs("div",{className:"min-w-0 flex-1",children:[a.jsxs("div",{className:"text-lg font-semibold text-grayWarm-950",children:[Tr(r.brand)," •••• ",r.last4]}),a.jsxs("div",{className:"text-sm text-grayWarm-600",children:["Exp. ",Pr(r.expMonth,r.expYear)]})]})]}),a.jsxs("form",{id:m,onSubmit:E,className:"flex flex-col gap-4",noValidate:!0,children:[a.jsxs("label",{className:"flex flex-col gap-1.5 text-sm font-medium text-grayWarm-700",children:["Cardholder name",a.jsx("input",{type:"text",autoComplete:"cc-name",value:v,onChange:O=>h(O.target.value),disabled:l,maxLength:255,"data-testid":"edit-payment-method-cardholder",className:"h-11 rounded-lg border border-grayWarm-300 px-3 text-base font-normal text-grayWarm-950 focus:border-grayWarm-950 focus:outline-none focus:ring-1 focus:ring-grayWarm-950"})]}),a.jsxs("label",{className:"flex flex-col gap-1.5 text-sm font-medium text-grayWarm-700",children:["Postal code",a.jsx("input",{type:"text",autoComplete:"postal-code",value:j,onChange:O=>_(zt(O.target.value)),disabled:l,"data-testid":"edit-payment-method-zip",className:"h-11 rounded-lg border border-grayWarm-300 px-3 text-base font-normal text-grayWarm-950 focus:border-grayWarm-950 focus:outline-none focus:ring-1 focus:ring-grayWarm-950"})]}),c?a.jsx("p",{role:"alert",className:"text-sm text-error-700","data-testid":"edit-payment-method-error",children:c}):null]})]})},Or=e=>{if(!e)return"Card";const r=e.toLowerCase();return r==="amex"?"Amex":r.charAt(0).toUpperCase()+r.slice(1)},Gt=({method:e,trailing:r,onClick:n,cardLabelFormat:o="branded",className:l})=>a.jsxs("div",{className:X("flex items-center gap-3",n&&"cursor-pointer",l),onClick:n,role:n?"button":void 0,tabIndex:n?0:void 0,onKeyDown:n?c=>{(c.key==="Enter"||c.key===" ")&&(c.preventDefault(),n())}:void 0,"data-testid":`payment-method-preview-${e.id}`,children:[a.jsx(Ie,{brand:e.brand,width:34,className:"flex-shrink-0"}),a.jsx("span",{className:"flex-1 text-lg font-semibold text-grayWarm-950",children:o==="masked"?`•••• ${e.last4}`:`${Or(e.brand)} ${e.last4}`}),r]}),Wr=(e,r,n=new Date)=>r<n.getFullYear()||r===n.getFullYear()&&e<n.getMonth()+1,Tt=e=>{if(!e)return"Card";const r=e.toLowerCase();return r==="amex"?"Amex":r.charAt(0).toUpperCase()+r.slice(1)},Mr=(e,r)=>`${String(e).padStart(2,"0")}/${r}`,Ar=({method:e,selectable:r,selected:n,onSelect:o,onSetDefault:l,onRemove:c,onEdit:m,disabled:v,showDefaultBadge:h,hideSetDefaultMenuItem:j,cardLabelFormat:_="branded"})=>{const[w,C]=s.useState(!1),x=s.useRef(null),P=Wr(e.expMonth,e.expYear);s.useEffect(()=>{if(!w)return;const I=k=>{var E;(E=x.current)!=null&&E.contains(k.target)||C(!1)};return document.addEventListener("mousedown",I),()=>document.removeEventListener("mousedown",I)},[w]);const M=()=>{r&&o&&!v&&o(e)};return a.jsxs("div",{className:X("flex items-start gap-3 transition-colors",r?"rounded-lg border px-3 py-3":"py-4",r&&!v&&"cursor-pointer",r&&(n?"border-grayWarm-950 shadow-[0_0_0_1px_#1c1917]":"border-grayWarm-200 hover:border-grayWarm-300")),onClick:M,"data-testid":`payment-method-row-${e.id}`,children:[r?a.jsx("input",{type:"radio",name:"movmo-payment-method",checked:n,onChange:()=>o==null?void 0:o(e),onClick:I=>I.stopPropagation(),"aria-label":`Select ${Tt(e.brand)} ending ${e.last4}`,className:"mr-1 h-4 w-4 mt-2 cursor-pointer appearance-none rounded-full border-2 border-grayWarm-300 checked:border-[5px] checked:border-grayWarm-950 bg-white checked:bg-white focus:ring-0 focus:ring-offset-0",disabled:v}):null,a.jsx(Ie,{brand:e.brand,className:"flex-shrink-0"}),a.jsxs("div",{className:"min-w-0 flex-1",children:[a.jsxs("div",{className:"flex flex-wrap items-center gap-2",children:[a.jsx("span",{className:"text-lg font-semibold text-grayWarm-950",children:_==="masked"?`•••• ${e.last4}`:`${Tt(e.brand)} ${e.last4}`}),h&&e.isDefault?a.jsx("span",{className:"inline-flex items-center rounded-full bg-success-50 px-2 py-0.5 text-xs font-semibold text-success-700","data-testid":`badge-default-${e.id}`,children:"Default"}):null,P?a.jsx("span",{className:"inline-flex items-center rounded-full bg-error-50 px-2 py-0.5 text-xs font-semibold text-error-700","data-testid":`badge-expired-${e.id}`,children:"Expired"}):null]}),a.jsxs("div",{className:X("text-base",P?"text-grayWarm-400":"text-grayWarm-600"),children:["Exp. ",Mr(e.expMonth,e.expYear)]})]}),a.jsxs("div",{className:"relative flex-shrink-0",ref:x,children:[a.jsx("button",{type:"button","aria-label":`Open actions for card ending ${e.last4}`,"aria-haspopup":"menu","aria-expanded":w,className:"rounded-lg p-2 text-grayWarm-600 transition-colors hover:bg-grayWarm-100 disabled:cursor-not-allowed disabled:opacity-50",disabled:v,onClick:I=>{I.stopPropagation(),C(k=>!k)},"data-testid":`menu-trigger-${e.id}`,children:a.jsx("svg",{viewBox:"0 0 24 24",width:"20",height:"20",fill:"currentColor","aria-hidden":"true",children:a.jsx("path",{d:"M12 8c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm0 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z"})})}),w?a.jsxs("div",{role:"menu",className:X("absolute z-10 whitespace-nowrap rounded-lg border border-grayWarm-200 bg-white py-1 shadow-lg",r?"bottom-full left-1/2 mb-2 -translate-x-1/2":"right-0 top-full mt-1"),onClick:I=>I.stopPropagation(),"data-testid":`menu-${e.id}`,children:[r?a.jsx("span",{"aria-hidden":"true",className:"absolute -bottom-1.5 left-1/2 h-3 w-3 -translate-x-1/2 rotate-45 border-b border-r border-grayWarm-200 bg-white"}):null,!e.isDefault&&!j?a.jsx("button",{type:"button",role:"menuitem",className:"block w-full px-4 py-1 text-left text-md font-semibold text-grayWarm-950 hover:text-grayWarm-700",onClick:()=>{C(!1),l(e)},"data-testid":`menu-set-default-${e.id}`,children:"Set as default"}):null,m&&e.vault==="spreedly"?a.jsx("button",{type:"button",role:"menuitem",className:"block w-full px-4 py-1 text-left text-md font-semibold text-grayWarm-950 hover:text-grayWarm-700",onClick:()=>{C(!1),m(e)},"data-testid":`menu-edit-${e.id}`,children:"Edit"}):null,a.jsx("button",{type:"button",role:"menuitem",className:"block w-full px-4 py-1 text-left text-md font-semibold text-error-700 hover:text-error-500",onClick:()=>{C(!1),c(e)},"data-testid":`menu-remove-${e.id}`,children:"Delete"})]}):null]})]})},Dr=e=>`${Ye().iconCdnBaseUrl}/website/assets/icons/payments/${e}.svg`,yt=({slug:e,alt:r})=>a.jsx("img",{src:Dr(e),alt:r,height:20,loading:"lazy",style:{height:20,width:"auto"}}),ut=({type:e,label:r,iconNode:n,disabled:o=!1})=>a.jsxs("label",{className:"flex cursor-pointer items-center gap-3 rounded-lg border border-grayWarm-200 px-4 py-3 transition-colors hover:border-grayWarm-300","data-testid":`payment-type-${e}`,children:[a.jsx("input",{type:"radio",name:"movmo-payment-type",value:e,disabled:o,onChange:()=>{},className:X("h-4 w-4 accent-grayWarm-900",o?"cursor-not-allowed":"cursor-pointer"),"aria-label":r}),a.jsxs("span",{className:"flex flex-1 items-center gap-2 text-sm font-semibold text-grayWarm-900",children:[r,n]})]}),Ft=({selected:e,onSelect:r,creditCardBody:n,disabled:o=!1,className:l})=>{const c=e==="credit_card";return a.jsxs("div",{className:X("flex w-full flex-col gap-3",l),children:[a.jsxs("label",{className:X("flex cursor-pointer flex-col gap-3 rounded-lg border px-4 py-3 transition-colors",c?"border-grayWarm-900 shadow-[0_0_0_1px_#1c1917]":"border-grayWarm-200"),"data-testid":"payment-type-credit_card",children:[a.jsxs("div",{className:"flex items-center gap-3",children:[a.jsx("input",{type:"radio",name:"movmo-payment-type",value:"credit_card",checked:c,disabled:o,onChange:()=>{o||r("credit_card")},className:X("h-4 w-4 accent-grayWarm-900",o?"cursor-not-allowed":"cursor-pointer"),"aria-label":"Credit card"}),a.jsx("span",{className:"flex-1 text-sm font-semibold text-grayWarm-900",children:"Credit card"}),a.jsxs("div",{className:"flex items-center gap-1.5",children:[a.jsx(Ie,{brand:"mastercard",width:28}),a.jsx(Ie,{brand:"visa",width:28}),a.jsx(Ie,{brand:"amex",width:28}),a.jsx(Ie,{brand:"discover",width:28})]})]}),c?a.jsx("div",{"data-testid":"payment-type-credit_card-body",children:n}):null]}),c?a.jsx("p",{className:"mt-2 text-sm font-semibold text-grayWarm-900","data-testid":"payment-type-other-options-header",children:"Other options"}):null,a.jsx(ut,{type:"paypal",label:"PayPal",iconNode:a.jsx(yt,{slug:"paypal",alt:"PayPal"}),disabled:o}),a.jsx(ut,{type:"googlepay",label:"GooglePay",iconNode:a.jsx(yt,{slug:"google-pay",alt:"GooglePay"}),disabled:o}),a.jsx(ut,{type:"klarna",label:"Klarna",iconNode:a.jsx(yt,{slug:"klarna",alt:"Klarna"}),disabled:o}),a.jsx(ut,{type:"ach",label:"Bank account (ACH)",iconNode:a.jsx("span",{className:"text-xs font-medium text-grayWarm-500",children:"Direct debit"}),disabled:o})]})},Ot="movmo-add-payment-form",$r=e=>{console.error("@movmo_app/payments: failed to update payment method",e);const r=e instanceof Error?e.message.trim():"";return/spreedly-backed cards/i.test(r)?"This card's details can't be edited.":r||"Failed to update payment method."},Wt=e=>{const r=e.filter(o=>o.isDefault),n=e.filter(o=>!o.isDefault);return[...r,...n]},Mt=({message:e,testId:r})=>a.jsx("p",{role:"alert","aria-live":"polite",className:"mt-3 rounded-md bg-error-50 px-3 py-2 text-sm text-error-700","data-testid":r,children:e}),At=({withDefaultBadge:e=!1})=>a.jsxs("div",{className:"flex items-start justify-between py-4","data-testid":"payment-method-skeleton",children:[a.jsxs("div",{className:"flex items-start gap-3",children:[a.jsx("div",{className:"h-8 w-[46px] flex-shrink-0 animate-pulse rounded bg-grayWarm-200"}),a.jsxs("div",{className:"space-y-2",children:[a.jsxs("div",{className:"flex items-center gap-2",children:[a.jsx("div",{className:"h-5 w-[120px] animate-pulse rounded bg-grayWarm-200"}),e?a.jsx("div",{className:"h-5 w-[56px] animate-pulse rounded-full bg-grayWarm-200"}):null]}),a.jsx("div",{className:"h-4 w-[100px] animate-pulse rounded bg-grayWarm-200"})]})]}),a.jsx("div",{className:"h-5 w-5 flex-shrink-0 animate-pulse rounded-full bg-grayWarm-200"})]}),Kt=({userId:e,defaultCardholderName:r,defaultCardholderFirstName:n,defaultCardholderLastName:o,selectedId:l,onSelect:c,onChange:m,onError:v,onEvent:h,paymentTypeSelector:j=!0,autoSaveFirstCard:_=!0,selectionSetsDefault:w=!1,showDefaultBadge:C=!0,collapsible:x=!1,cardLabelFormat:P="branded",className:M})=>{const{items:I,status:k,error:E,refetch:O}=Ht(e),{deletePaymentMethod:pe,status:de}=Bt(e),{setDefault:we,status:ue}=Yt(e),{updatePaymentMethod:se,status:Q}=Vt(e),[b,ye]=s.useState([]),_e=s.useRef(b);s.useEffect(()=>{_e.current=b},[b]);const[z,p]=s.useState(null),[y,S]=s.useState(!1),[A,re]=s.useState(null),[ae,q]=s.useState(!1),[G,V]=s.useState(!1),[T,K]=s.useState(null),[Ce,ve]=s.useState(null),[J,Ee]=s.useState(null),[Z,Pe]=s.useState(!1),Re=s.useRef(!1),oe=s.useRef(v);s.useEffect(()=>{oe.current=v},[v]);const ee=s.useCallback(u=>{var B;(B=oe.current)==null||B.call(oe,u)},[]),Te=s.useRef(h);s.useEffect(()=>{Te.current=h},[h]);const $=s.useCallback((u,B)=>{var L;(L=Te.current)==null||L.call(Te,u,B)},[]),ie=s.useRef(!1),ke=s.useCallback(u=>{if(u.touchedInvalid.length===0){ie.current=!1;return}ie.current||(ie.current=!0,$("validation_error",{field_count:u.touchedInvalid.length,fields:u.touchedInvalid}))},[$]),te=s.useCallback(()=>{S(!1),re(null),Ee(null),ie.current=!1},[]),je=r??([n,o].filter(Boolean).join(" ")||void 0);s.useEffect(()=>{k==="ready"&&ye(Wt(I))},[I,k]);const Fe=s.useRef(null);s.useEffect(()=>{if(k==="error"&&E){if(Fe.current===E)return;Fe.current=E,ee({source:"list",message:E})}else k!=="error"&&(Fe.current=null)},[k,E,ee]),s.useEffect(()=>{if(!c||Re.current||k!=="ready"||b.length===0||l)return;Re.current=!0,$("autoselected",{had_saved_methods:!0});const u=b.find(B=>B.isDefault)??b[0];c(u)},[c,k,b,l,$]);const he=s.useCallback(u=>{ye(B=>{const L=typeof u=="function"?u(B):u,U=Wt(L);return queueMicrotask(()=>m==null?void 0:m(U)),U})},[m]),ze=de==="pending"||ue==="pending"||Q==="pending",ge=s.useCallback(async u=>{const B=b;p(null),he(L=>L.map(U=>({...U,isDefault:U.id===u.id})));try{await we(u.id),O(),$("set_default")}catch(L){ye(B),m==null||m(B);const U=L instanceof Error?L.message:"Failed to set default payment method.";p(U),ee({...nt("set-default",L,"Failed to set default payment method."),message:U})}},[b,he,we,O,m,ee,$]),le=s.useCallback(u=>{c==null||c(u),w&&(u.isDefault||ge(u))},[c,w,ge]),Oe=s.useCallback(u=>{$("selected",{method_count:b.length}),le(u)},[$,b,le]),We=s.useCallback(async u=>{const B=b;p(null);const L=b.filter(Me=>Me.id!==u.id);he(()=>L);const U=u.id===l&&c?L.find(Me=>Me.isDefault)??L[0]??null:null;U&&le(U);const xe=!!U&&w&&U!==null&&!U.isDefault;try{await pe(u.id),xe||O(),$("deleted",{remaining_count:L.length})}catch(Me){ye(B),m==null||m(B);const ct=Me instanceof Error?Me.message:"Failed to delete payment method.";p(ct),ee({...nt("delete",Me,"Failed to delete payment method."),message:ct})}},[b,he,pe,O,m,l,c,le,w,ee,$]),Ae=s.useCallback(u=>{ve(null),K(u)},[]),Le=s.useCallback(()=>{K(null),ve(null)},[]),De=s.useCallback(async u=>{if(!T)return;const B=T,L=b;ve(null),he(U=>U.map(xe=>xe.id===B.id?{...xe,...u.cardholderName!==void 0?{cardholderName:u.cardholderName}:{},...u.zip!==void 0?{zip:u.zip}:{}}:xe));try{await se(B.id,u),Le(),O(),$("edited",{fields:Object.keys(u)})}catch(U){ye(L),m==null||m(L);const xe=$r(U);ve(xe),ee({...nt("update",U,xe),message:xe})}},[T,b,he,se,O,m,Le,ee,$]),Ne=s.useCallback(u=>{te(),he(L=>[u,...L.filter(U=>U.id!==u.id)]),le(u),!!c&&w&&!u.isDefault||O(),$("added",{method_count:_e.current.length+1})},[te,he,O,le,c,w,$]),f=s.useCallback(u=>{re(u)},[]),D=s.useCallback(u=>{V(u),u&&re(null)},[]),me=k==="ready"&&b.length===0,ne=typeof c=="function",st=!ne&&!x,ot=b.length===0,it=_&&b.length===0,$e=s.useCallback(u=>a.jsx(Et,{userId:e,isDefault:ot,defaultCardholderName:je,autoSave:!u&&it,hideInternalSaveButton:u,formId:u?Ot:void 0,onCanSubmitChange:u?q:void 0,onSavingChange:D,autoFocus:u?"number":void 0,onSuccess:Ne,onError:f,onErrorDetail:ee,onValidationChange:ke}),[e,ot,je,it,Ne,f,D,ee,ke]),Ve=s.useMemo(()=>$e(!1),[$e]),Se=s.useMemo(()=>$e(!0),[$e]),Ue=!e,Qe=s.useMemo(()=>j?a.jsx(Ft,{selected:J,onSelect:u=>Ee(u),creditCardBody:Ve,disabled:Ue}):Ve,[j,J,Ve,Ue]),lt=s.useMemo(()=>j?a.jsx(Ft,{selected:J,onSelect:u=>Ee(u),creditCardBody:Se,disabled:Ue}):Se,[j,J,Se,Ue]);return a.jsxs("div",{className:X("flex w-full flex-col gap-4",M),"data-testid":"payment-methods-manager",children:[k==="loading"&&b.length===0?a.jsxs("div",{className:X(st?"flex flex-col divide-y divide-grayWarm-200 rounded-xl border border-grayWarm-200 px-4 md:px-6":"flex flex-col gap-2"),"data-testid":"payment-methods-loading",children:[a.jsx(At,{withDefaultBadge:!0}),a.jsx(At,{})]}):null,k==="error"?a.jsxs("div",{role:"alert","aria-live":"polite",className:"flex items-center justify-between gap-3 rounded-md bg-error-50 p-3 text-sm text-error-700","data-testid":"payment-methods-list-error",children:[a.jsx("span",{children:E??"Failed to load payment methods."}),a.jsx("button",{type:"button",onClick:O,className:"rounded-md border border-error-700 px-3 py-1 text-xs font-semibold text-error-700 hover:bg-error-50",children:"Retry"})]}):null,z?a.jsx("div",{role:"alert","aria-live":"polite",className:"rounded-md bg-error-50 p-3 text-sm text-error-700","data-testid":"payment-methods-mutation-error",children:z}):null,x&&b.length>0?a.jsx("div",{className:"border-t border-grayWarm-200 pt-4",children:a.jsxs("div",{className:"flex items-center justify-between","data-testid":Z?"payment-methods-drawer-header":"payment-methods-collapsed-preview",children:[Z?a.jsx("h3",{className:"text-md font-semibold text-grayWarm-950",children:"Payment methods"}):a.jsx(Gt,{method:b.find(u=>u.id===l)??b.find(u=>u.isDefault)??b[0],cardLabelFormat:P,className:"flex-1"}),a.jsx("button",{type:"button","aria-label":Z?"Collapse payment methods":"Expand payment methods","aria-expanded":Z,className:"bg-grayWarm-100 p-2 rounded-lg hover:bg-grayWarm-200 transition-colors",onClick:()=>Pe(u=>!u),"data-testid":Z?"payment-methods-drawer-close":"payment-methods-drawer-open",children:a.jsx("img",{src:`${Ye().iconCdnBaseUrl}/website/assets/icons/chevron-${Z?"up":"down"}-double.svg`,alt:"",className:"w-5 h-5"})})]})}):null,b.length>0&&(!x||Z)?a.jsx("div",{className:X("flex flex-col",st?"rounded-xl border border-grayWarm-200 px-4 md:px-6 divide-y divide-grayWarm-200":"space-y-3"),children:b.map(u=>a.jsx(Ar,{method:u,selectable:ne,selected:ne&&u.id===l,onSelect:Oe,onSetDefault:ge,onRemove:We,onEdit:Ae,disabled:ze,showDefaultBadge:C,hideSetDefaultMenuItem:w,cardLabelFormat:P},u.id))}):null,me?a.jsxs("div",{"data-testid":"payment-methods-empty",children:[Qe,A&&!y?a.jsx(Mt,{message:A,testId:"payment-methods-inline-error"}):null]}):b.length>0&&(!x||Z)?a.jsx("button",{type:"button",onClick:()=>{re(null),Ee(null),q(!1),S(!0),ie.current=!1},onMouseEnter:()=>{xt().catch(()=>{})},onFocus:()=>{xt().catch(()=>{})},className:"self-start rounded-lg bg-brand-50 px-6 py-3 text-md font-semibold text-brand-600 transition-colors hover:bg-brand-100","data-testid":"payment-methods-add-trigger",children:"Add payment method"}):null,a.jsxs(Sr,{open:y,onClose:te,formId:Ot,saveDisabled:!ae,isSaving:G,children:[lt,A?a.jsx(Mt,{message:A,testId:"payment-methods-modal-error"}):null]}),a.jsx(Fr,{open:T!==null,method:T,onClose:Le,onSave:De,isSaving:Q==="pending",error:Ce})]})};Kt.displayName="PaymentMethodsManager";const Ur="/v1/auth/",Ir="movmo_csrf_token",Jt=new Set(["reauth_required","movmo_session_required","first_party_only","recovery_cooldown","account_deletion_pending"]),zr=e=>{if(typeof document>"u")return null;const r=document.cookie?document.cookie.split(";"):[];for(const n of r){const o=n.trim();if(o.startsWith(e+"="))return decodeURIComponent(o.substring(e.length+1))}return null},Lr=e=>typeof e=="string"?e:e instanceof URL?e.href:e.url,Br=()=>{if(typeof globalThis<"u"&&typeof globalThis.fetch=="function")return globalThis.fetch.bind(globalThis);throw new Error("@movmo_app/payments: no global fetch available in this environment")},Yr=e=>{const{refreshSession:r,onRefreshFailed:n,csrfCookieName:o=Ir,extraHeaders:l,authPathFragment:c=Ur,baseFetch:m=Br(),readCookie:v=zr}=e,h=new Set(e.noRefreshCodes??Jt);let j=null;const _=()=>{if(j)return j;const C=(async()=>{await r()})();return j=C,C.finally(()=>{j===C&&(j=null)}).catch(()=>{}),C},w=(C,x)=>{const P=new Headers(x==null?void 0:x.headers),M=v(o);if(M&&!P.has("X-CSRF-Token")&&P.set("X-CSRF-Token",M),l){const I=l(C,x);for(const[k,E]of Object.entries(I))P.has(k)||P.set(k,E)}return{...x,headers:P,credentials:(x==null?void 0:x.credentials)??"include"}};return async function(x,P){const M=await m(x,w(x,P));if(Lr(x).includes(c)||M.status!==401&&M.status!==403)return M;if(M.status===403)try{const E=await M.clone().json(),O=E==null?void 0:E.code;if(typeof O=="string"&&h.has(O))return M}catch{}try{await _()}catch(E){try{n(E)}catch(O){console.error("@movmo_app/payments: onRefreshFailed callback threw — auth retry path is misconfigured",O)}return M}try{return await m(x,w(x,P))}catch(E){return console.error("@movmo_app/payments: retry after token refresh failed — returning original response",E),M}}};exports.DEFAULT_NO_REFRESH_CODES=Jt;exports.MovmoCardForm=Et;exports.PaymentMethodPreview=Gt;exports.PaymentMethodsManager=Kt;exports.PaymentsApiError=wt;exports.createMovmoAuthFetch=Yr;exports.getPaymentsConfig=Ye;exports.setPaymentsConfig=cr;exports.useDeletePaymentMethod=Bt;exports.useMovmoCardFields=Ut;exports.useSetDefaultPaymentMethod=Yt;exports.useUpdatePaymentMethod=Vt;exports.useUserPaymentMethods=Ht;
31
31
  //# sourceMappingURL=index.cjs.js.map