@movmo_app/payments 0.1.0 → 0.2.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/README.md CHANGED
@@ -4,37 +4,91 @@ PCI-safe card capture for Movmo UIs (`accounts-ui`, `flights-ui`) and airline pa
4
4
 
5
5
  ## What's in the box
6
6
 
7
- - **`<MovmoCardForm />`** — drop-in form: two PCI-safe card-field iframes (card number + CVV) plus standard inputs for cardholder name and expiry, plus a Save button. Tokenizes the card, then calls the Movmo API to save the vaulted card to the user's profile.
8
- - **`useMovmoCardFields()`**lower-level hook for partners who want to compose their own form layout around the two card-field iframes.
7
+ - **`<MovmoCardForm />`** — drop-in card-capture form (PCI-safe number + CVV iframes, single cardholder-name input, combined `MM / YY` expiry). The Save button is gated on Spreedly field validity; opt into auto-save via the `autoSave` prop. Tokenizes the card, then calls the Movmo API to save the vaulted card to the user's profile. Renders the detected card brand inline (Visa / MC / Amex / Discover) as the user types.
8
+ - **`<PaymentMethodsManager />`** full saved-cards UI: lists existing cards, lets the user set default / remove, and embeds `<MovmoCardForm />` for adding a new one. Renders a payment-type selector (Credit card + disabled "Coming soon" rows for PayPal / GooglePay / Klarna / ACH) by default; pass `paymentTypeSelector={false}` to hide it. Auto-saves the first card by default (`autoSaveFirstCard={true}` — matches flights-ui UX). Optional selection mode (`selectedId` + `onSelect`) for checkout flows.
9
+ - **`useMovmoCardFields()`** — lower-level hook for partners composing their own form layout. Exposes per-field `validity` + detected `brand` so the consumer can gate its own submit button and render its own brand icon.
10
+ - **`useUserPaymentMethods(userId)`** — fetches the saved-cards list with `{ items, status, error, refetch }`. Useful standalone (e.g., showing the selected card's last4 in a checkout summary).
11
+ - **`useDeletePaymentMethod(userId)`** — exposes `{ deletePaymentMethod, status, error }`. Resolves on success, rejects on failure so callers can roll back optimistic updates.
12
+ - **`useSetDefaultPaymentMethod(userId)`** — exposes `{ setDefault, status, error }`. Same shape as delete.
9
13
  - **`PaymentMethodSummary`** — vault-neutral type returned to consumers (no vault tokens, no customer/vault IDs leak through).
10
14
 
11
15
  ## Backend contract
12
16
 
13
- Consumes two existing `monolith-api` endpoints (no backend changes required):
17
+ Consumes existing `monolith-api` endpoints (no backend changes required):
14
18
 
15
- | Method | Path | Purpose |
16
- |---|---|---|
17
- | `GET` | `/v1/payments/tokenization-session` | Fetch RSA-signed Spreedly session params (`environmentKey`, `certificateToken`, `nonce`, `timestamp`, `signature`). |
18
- | `POST` | `/v1/users/:userid/payment-methods/from-token` | Save the vaulted card; server derives `last4`/`brand`/`expMonth`/`expYear` from the Spreedly vault, never trusting the client. |
19
+ | Method | Path | Purpose |
20
+ | -------- | ---------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
21
+ | `GET` | `/v1/payments/tokenization-session` | Fetch RSA-signed Spreedly session params (`environmentKey`, `certificateToken`, `nonce`, `timestamp`, `signature`). |
22
+ | `GET` | `/v1/users/:userid/payment-methods` | List the user's saved cards. |
23
+ | `POST` | `/v1/users/:userid/payment-methods/from-token` | Save the vaulted card; server derives `last4`/`brand`/`expMonth`/`expYear` from the Spreedly vault, never trusting the client. |
24
+ | `PUT` | `/v1/users/:userid/payment-methods/:methodid` | Update the saved card. The manager only sends `{ isDefault: true }` to flip the default. |
25
+ | `DELETE` | `/v1/users/:userid/payment-methods/:methodid` | Delete a saved card. |
19
26
 
20
- Both endpoints sit behind the standard RBAC/session-cookie auth.
27
+ All endpoints sit behind the standard RBAC/session-cookie auth.
21
28
 
22
29
  ## Usage
23
30
 
31
+ ### Just adding a card
32
+
24
33
  ```tsx
25
34
  import { MovmoCardForm } from '@movmo_app/payments';
26
35
 
27
36
  <MovmoCardForm
28
37
  userId={user.id}
29
38
  isDefault
30
- defaultCardholderFirstName={user.firstName}
31
- defaultCardholderLastName={user.lastName}
39
+ defaultCardholderName={`${user.firstName} ${user.lastName}`}
32
40
  onSuccess={(pm) => console.log('saved', pm)}
33
41
  onError={(msg) => console.error(msg)}
34
42
  />;
35
43
  ```
36
44
 
37
- Cardholder defaults are editable so users can save a card that isn't in their own name (spouse card, corporate card).
45
+ The cardholder name is a single editable field users can save a card that isn't in their own name (spouse card, corporate card). Pass `autoSave` to submit automatically once the form is valid (no Save button rendered).
46
+
47
+ ### Full saved-cards management (account-page style)
48
+
49
+ ```tsx
50
+ import { PaymentMethodsManager } from '@movmo_app/payments';
51
+
52
+ <Card>
53
+ <PageHeader title="Payment methods" />
54
+ <PaymentMethodsManager
55
+ userId={user.id}
56
+ paymentTypeSelector={false}
57
+ autoSaveFirstCard={false}
58
+ onChange={(items) => refreshGlobalState(items)}
59
+ />
60
+ </Card>;
61
+ ```
62
+
63
+ The manager renders only its list + add-card affordance — consumers own the outer chrome (page, drawer, modal, etc.). accounts-ui typically hides the payment-type selector and opts out of auto-save so the user clicks Save explicitly.
64
+
65
+ ### Checkout flow with payment-type selector + auto-save (flights-ui style)
66
+
67
+ ```tsx
68
+ import { PaymentMethodsManager } from '@movmo_app/payments';
69
+
70
+ <Drawer open={open} onClose={onClose}>
71
+ <PaymentMethodsManager
72
+ userId={user.id}
73
+ selectedId={selected?.id}
74
+ onSelect={(method) => setSelected(method)}
75
+ // paymentTypeSelector and autoSaveFirstCard default to true.
76
+ />
77
+ </Drawer>;
78
+ ```
79
+
80
+ When `onSelect` is provided, each row renders a radio and the whole row is clickable to select. The default behavior matches the existing flights-ui add-payment UX: Credit card / PayPal / GooglePay / Klarna / ACH radios at the top (only Credit card is functional today; the others render as "Coming soon"); auto-save fires when the form is valid AND the user has zero saved cards.
81
+
82
+ ### Standalone hook usage
83
+
84
+ ```tsx
85
+ import { useUserPaymentMethods } from '@movmo_app/payments';
86
+
87
+ const { items, status } = useUserPaymentMethods(user.id);
88
+ const defaultCard = items.find((m) => m.isDefault);
89
+ ```
90
+
91
+ ### Configuration
38
92
 
39
93
  Set the API base URL once at app boot (mirrors `@movmo_app/api`):
40
94
 
@@ -53,4 +107,4 @@ pnpm --filter @movmo_app/payments test
53
107
  pnpm --filter @movmo_app/payments storybook # → http://localhost:6007
54
108
  ```
55
109
 
56
- Storybook is the primary way to exercise the component on a laptop: it mocks both the underlying vault SDK and the `monolith-api` calls, so no vault credentials, no live backend, and no real card data are required. See `src/MovmoCardForm/__docs__/MovmoCardForm.stories.tsx` for the full state matrix.
110
+ Storybook is the primary way to exercise the components on a laptop: it mocks both the underlying vault SDK and the `monolith-api` calls, so no vault credentials, no live backend, and no real card data are required. See `src/MovmoCardForm/__docs__/MovmoCardForm.stories.tsx` and `src/PaymentMethodsManager/__docs__/PaymentMethodsManager.stories.tsx` for the full state matrices.
package/dist/index.cjs.js CHANGED
@@ -1,4 +1,4 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const R=require("react");var ye={exports:{}},q={};/**
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const i=require("react");var Ue={exports:{}},ge={};/**
2
2
  * @license React
3
3
  * react-jsx-runtime.production.min.js
4
4
  *
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * This source code is licensed under the MIT license found in the
8
8
  * LICENSE file in the root directory of this source tree.
9
- */var Ie;function wr(){if(Ie)return q;Ie=1;var n=R,o=Symbol.for("react.element"),c=Symbol.for("react.fragment"),i=Object.prototype.hasOwnProperty,v=n.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner,x={key:!0,ref:!0,__self:!0,__source:!0};function D(k,b,C){var E,y={},N=null,T=null;C!==void 0&&(N=""+C),b.key!==void 0&&(N=""+b.key),b.ref!==void 0&&(T=b.ref);for(E in b)i.call(b,E)&&!x.hasOwnProperty(E)&&(y[E]=b[E]);if(k&&k.defaultProps)for(E in b=k.defaultProps,b)y[E]===void 0&&(y[E]=b[E]);return{$$typeof:o,type:k,key:N,ref:T,props:y,_owner:v.current}}return q.Fragment=c,q.jsx=D,q.jsxs=D,q}var B={};/**
9
+ */var rr;function Ir(){if(rr)return ge;rr=1;var r=i,t=Symbol.for("react.element"),s=Symbol.for("react.fragment"),o=Object.prototype.hasOwnProperty,c=r.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner,m={key:!0,ref:!0,__self:!0,__source:!0};function u(h,v,T){var E,w={},F=null,R=null;T!==void 0&&(F=""+T),v.key!==void 0&&(F=""+v.key),v.ref!==void 0&&(R=v.ref);for(E in v)o.call(v,E)&&!m.hasOwnProperty(E)&&(w[E]=v[E]);if(h&&h.defaultProps)for(E in v=h.defaultProps,v)w[E]===void 0&&(w[E]=v[E]);return{$$typeof:t,type:h,key:F,ref:R,props:w,_owner:c.current}}return ge.Fragment=s,ge.jsx=u,ge.jsxs=u,ge}var Ee={};/**
10
10
  * @license React
11
11
  * react-jsx-runtime.development.js
12
12
  *
@@ -14,18 +14,18 @@
14
14
  *
15
15
  * This source code is licensed under the MIT license found in the
16
16
  * LICENSE file in the root directory of this source tree.
17
- */var $e;function xr(){return $e||($e=1,process.env.NODE_ENV!=="production"&&function(){var n=R,o=Symbol.for("react.element"),c=Symbol.for("react.portal"),i=Symbol.for("react.fragment"),v=Symbol.for("react.strict_mode"),x=Symbol.for("react.profiler"),D=Symbol.for("react.provider"),k=Symbol.for("react.context"),b=Symbol.for("react.forward_ref"),C=Symbol.for("react.suspense"),E=Symbol.for("react.suspense_list"),y=Symbol.for("react.memo"),N=Symbol.for("react.lazy"),T=Symbol.for("react.offscreen"),h=Symbol.iterator,S="@@iterator";function P(e){if(e===null||typeof e!="object")return null;var r=h&&e[h]||e[S];return typeof r=="function"?r:null}var w=n.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;function p(e){{for(var r=arguments.length,t=new Array(r>1?r-1:0),a=1;a<r;a++)t[a-1]=arguments[a];K("error",e,t)}}function K(e,r,t){{var a=w.ReactDebugCurrentFrame,u=a.getStackAddendum();u!==""&&(r+="%s",t=t.concat([u]));var f=t.map(function(l){return String(l)});f.unshift("Warning: "+r),Function.prototype.apply.call(console[e],console,f)}}var G=!1,oe=!1,U=!1,ie=!1,se=!1,H;H=Symbol.for("react.module.reference");function Y(e){return!!(typeof e=="string"||typeof e=="function"||e===i||e===x||se||e===v||e===C||e===E||ie||e===T||G||oe||U||typeof e=="object"&&e!==null&&(e.$$typeof===N||e.$$typeof===y||e.$$typeof===D||e.$$typeof===k||e.$$typeof===b||e.$$typeof===H||e.getModuleId!==void 0))}function X(e,r,t){var a=e.displayName;if(a)return a;var u=r.displayName||r.name||"";return u!==""?t+"("+u+")":t}function Z(e){return e.displayName||"Context"}function M(e){if(e==null)return null;if(typeof e.tag=="number"&&p("Received an unexpected object in getComponentNameFromType(). This is likely a bug in React. Please file an issue."),typeof e=="function")return e.displayName||e.name||null;if(typeof e=="string")return e;switch(e){case i:return"Fragment";case c:return"Portal";case x:return"Profiler";case v:return"StrictMode";case C:return"Suspense";case E:return"SuspenseList"}if(typeof e=="object")switch(e.$$typeof){case k:var r=e;return Z(r)+".Consumer";case D:var t=e;return Z(t._context)+".Provider";case b:return X(e,e.render,"ForwardRef");case y:var a=e.displayName||null;return a!==null?a:M(e.type)||"Memo";case N:{var u=e,f=u._payload,l=u._init;try{return M(l(f))}catch{return null}}}return null}var d=Object.assign,F=0,L,ge,be,Ee,_e,Re,Se;function we(){}we.__reactDisabledLog=!0;function Xe(){{if(F===0){L=console.log,ge=console.info,be=console.warn,Ee=console.error,_e=console.group,Re=console.groupCollapsed,Se=console.groupEnd;var e={configurable:!0,enumerable:!0,value:we,writable:!0};Object.defineProperties(console,{info:e,log:e,warn:e,error:e,group:e,groupCollapsed:e,groupEnd:e})}F++}}function Ze(){{if(F--,F===0){var e={configurable:!0,enumerable:!0,writable:!0};Object.defineProperties(console,{log:d({},e,{value:L}),info:d({},e,{value:ge}),warn:d({},e,{value:be}),error:d({},e,{value:Ee}),group:d({},e,{value:_e}),groupCollapsed:d({},e,{value:Re}),groupEnd:d({},e,{value:Se})})}F<0&&p("disabledDepth fell below zero. This is a bug in React. Please file an issue.")}}var le=w.ReactCurrentDispatcher,ce;function Q(e,r,t){{if(ce===void 0)try{throw Error()}catch(u){var a=u.stack.trim().match(/\n( *(at )?)/);ce=a&&a[1]||""}return`
18
- `+ce+e}}var ue=!1,ee;{var Qe=typeof WeakMap=="function"?WeakMap:Map;ee=new Qe}function xe(e,r){if(!e||ue)return"";{var t=ee.get(e);if(t!==void 0)return t}var a;ue=!0;var u=Error.prepareStackTrace;Error.prepareStackTrace=void 0;var f;f=le.current,le.current=null,Xe();try{if(r){var l=function(){throw Error()};if(Object.defineProperty(l.prototype,"props",{set:function(){throw Error()}}),typeof Reflect=="object"&&Reflect.construct){try{Reflect.construct(l,[])}catch(O){a=O}Reflect.construct(e,[],l)}else{try{l.call()}catch(O){a=O}e.call(l.prototype)}}else{try{throw Error()}catch(O){a=O}e()}}catch(O){if(O&&a&&typeof O.stack=="string"){for(var s=O.stack.split(`
19
- `),j=a.stack.split(`
20
- `),g=s.length-1,_=j.length-1;g>=1&&_>=0&&s[g]!==j[_];)_--;for(;g>=1&&_>=0;g--,_--)if(s[g]!==j[_]){if(g!==1||_!==1)do if(g--,_--,_<0||s[g]!==j[_]){var A=`
21
- `+s[g].replace(" at new "," at ");return e.displayName&&A.includes("<anonymous>")&&(A=A.replace("<anonymous>",e.displayName)),typeof e=="function"&&ee.set(e,A),A}while(g>=1&&_>=0);break}}}finally{ue=!1,le.current=f,Ze(),Error.prepareStackTrace=u}var W=e?e.displayName||e.name:"",I=W?Q(W):"";return typeof e=="function"&&ee.set(e,I),I}function er(e,r,t){return xe(e,!1)}function rr(e){var r=e.prototype;return!!(r&&r.isReactComponent)}function re(e,r,t){if(e==null)return"";if(typeof e=="function")return xe(e,rr(e));if(typeof e=="string")return Q(e);switch(e){case C:return Q("Suspense");case E:return Q("SuspenseList")}if(typeof e=="object")switch(e.$$typeof){case b:return er(e.render);case y:return re(e.type,r,t);case N:{var a=e,u=a._payload,f=a._init;try{return re(f(u),r,t)}catch{}}}return""}var z=Object.prototype.hasOwnProperty,Ce={},Te=w.ReactDebugCurrentFrame;function te(e){if(e){var r=e._owner,t=re(e.type,e._source,r?r.type:null);Te.setExtraStackFrame(t)}else Te.setExtraStackFrame(null)}function tr(e,r,t,a,u){{var f=Function.call.bind(z);for(var l in e)if(f(e,l)){var s=void 0;try{if(typeof e[l]!="function"){var j=Error((a||"React class")+": "+t+" type `"+l+"` is invalid; it must be a function, usually from the `prop-types` package, but received `"+typeof e[l]+"`.This often happens because of typos such as `PropTypes.function` instead of `PropTypes.func`.");throw j.name="Invariant Violation",j}s=e[l](r,l,a,t,null,"SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED")}catch(g){s=g}s&&!(s instanceof Error)&&(te(u),p("%s: type specification of %s `%s` is invalid; the type checker function must return `null` or an `Error` but returned a %s. You may have forgotten to pass an argument to the type checker creator (arrayOf, instanceOf, objectOf, oneOf, oneOfType, and shape all require an argument).",a||"React class",t,l,typeof s),te(null)),s instanceof Error&&!(s.message in Ce)&&(Ce[s.message]=!0,te(u),p("Failed %s type: %s",t,s.message),te(null))}}}var nr=Array.isArray;function de(e){return nr(e)}function ar(e){{var r=typeof Symbol=="function"&&Symbol.toStringTag,t=r&&e[Symbol.toStringTag]||e.constructor.name||"Object";return t}}function or(e){try{return je(e),!1}catch{return!0}}function je(e){return""+e}function Pe(e){if(or(e))return p("The provided key is an unsupported type %s. This value must be coerced to a string before before using it here.",ar(e)),je(e)}var Oe=w.ReactCurrentOwner,ir={key:!0,ref:!0,__self:!0,__source:!0},ke,Fe;function sr(e){if(z.call(e,"ref")){var r=Object.getOwnPropertyDescriptor(e,"ref").get;if(r&&r.isReactWarning)return!1}return e.ref!==void 0}function lr(e){if(z.call(e,"key")){var r=Object.getOwnPropertyDescriptor(e,"key").get;if(r&&r.isReactWarning)return!1}return e.key!==void 0}function cr(e,r){typeof e.ref=="string"&&Oe.current}function ur(e,r){{var t=function(){ke||(ke=!0,p("%s: `key` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://reactjs.org/link/special-props)",r))};t.isReactWarning=!0,Object.defineProperty(e,"key",{get:t,configurable:!0})}}function dr(e,r){{var t=function(){Fe||(Fe=!0,p("%s: `ref` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://reactjs.org/link/special-props)",r))};t.isReactWarning=!0,Object.defineProperty(e,"ref",{get:t,configurable:!0})}}var fr=function(e,r,t,a,u,f,l){var s={$$typeof:o,type:e,key:r,ref:t,props:l,_owner:f};return s._store={},Object.defineProperty(s._store,"validated",{configurable:!1,enumerable:!1,writable:!0,value:!1}),Object.defineProperty(s,"_self",{configurable:!1,enumerable:!1,writable:!1,value:a}),Object.defineProperty(s,"_source",{configurable:!1,enumerable:!1,writable:!1,value:u}),Object.freeze&&(Object.freeze(s.props),Object.freeze(s)),s};function mr(e,r,t,a,u){{var f,l={},s=null,j=null;t!==void 0&&(Pe(t),s=""+t),lr(r)&&(Pe(r.key),s=""+r.key),sr(r)&&(j=r.ref,cr(r,u));for(f in r)z.call(r,f)&&!ir.hasOwnProperty(f)&&(l[f]=r[f]);if(e&&e.defaultProps){var g=e.defaultProps;for(f in g)l[f]===void 0&&(l[f]=g[f])}if(s||j){var _=typeof e=="function"?e.displayName||e.name||"Unknown":e;s&&ur(l,_),j&&dr(l,_)}return fr(e,s,j,u,a,Oe.current,l)}}var fe=w.ReactCurrentOwner,Ae=w.ReactDebugCurrentFrame;function $(e){if(e){var r=e._owner,t=re(e.type,e._source,r?r.type:null);Ae.setExtraStackFrame(t)}else Ae.setExtraStackFrame(null)}var me;me=!1;function ve(e){return typeof e=="object"&&e!==null&&e.$$typeof===o}function De(){{if(fe.current){var e=M(fe.current.type);if(e)return`
17
+ */var tr;function Ur(){return tr||(tr=1,process.env.NODE_ENV!=="production"&&function(){var r=i,t=Symbol.for("react.element"),s=Symbol.for("react.portal"),o=Symbol.for("react.fragment"),c=Symbol.for("react.strict_mode"),m=Symbol.for("react.profiler"),u=Symbol.for("react.provider"),h=Symbol.for("react.context"),v=Symbol.for("react.forward_ref"),T=Symbol.for("react.suspense"),E=Symbol.for("react.suspense_list"),w=Symbol.for("react.memo"),F=Symbol.for("react.lazy"),R=Symbol.for("react.offscreen"),D=Symbol.iterator,C="@@iterator";function q(e){if(e===null||typeof e!="object")return null;var n=D&&e[D]||e[C];return typeof n=="function"?n:null}var O=r.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;function S(e){{for(var n=arguments.length,l=new Array(n>1?n-1:0),f=1;f<n;f++)l[f-1]=arguments[f];K("error",e,l)}}function K(e,n,l){{var f=O.ReactDebugCurrentFrame,x=f.getStackAddendum();x!==""&&(n+="%s",l=l.concat([x]));var _=l.map(function(y){return String(y)});_.unshift("Warning: "+n),Function.prototype.apply.call(console[e],console,_)}}var M=!1,te=!1,ae=!1,W=!1,b=!1,g;g=Symbol.for("react.module.reference");function I(e){return!!(typeof e=="string"||typeof e=="function"||e===o||e===m||b||e===c||e===T||e===E||W||e===R||M||te||ae||typeof e=="object"&&e!==null&&(e.$$typeof===F||e.$$typeof===w||e.$$typeof===u||e.$$typeof===h||e.$$typeof===v||e.$$typeof===g||e.getModuleId!==void 0))}function U(e,n,l){var f=e.displayName;if(f)return f;var x=n.displayName||n.name||"";return x!==""?l+"("+x+")":l}function ne(e){return e.displayName||"Context"}function z(e){if(e==null)return null;if(typeof e.tag=="number"&&S("Received an unexpected object in getComponentNameFromType(). This is likely a bug in React. Please file an issue."),typeof e=="function")return e.displayName||e.name||null;if(typeof e=="string")return e;switch(e){case o:return"Fragment";case s:return"Portal";case m:return"Profiler";case c:return"StrictMode";case T:return"Suspense";case E:return"SuspenseList"}if(typeof e=="object")switch(e.$$typeof){case h:var n=e;return ne(n)+".Consumer";case u:var l=e;return ne(l._context)+".Provider";case v:return U(e,e.render,"ForwardRef");case w:var f=e.displayName||null;return f!==null?f:z(e.type)||"Memo";case F:{var x=e,_=x._payload,y=x._init;try{return z(y(_))}catch{return null}}}return null}var P=Object.assign,Y=0,le,V,k,se,oe,ce,de;function ue(){}ue.__reactDisabledLog=!0;function pe(){{if(Y===0){le=console.log,V=console.info,k=console.warn,se=console.error,oe=console.group,ce=console.groupCollapsed,de=console.groupEnd;var e={configurable:!0,enumerable:!0,value:ue,writable:!0};Object.defineProperties(console,{info:e,log:e,warn:e,error:e,group:e,groupCollapsed:e,groupEnd:e})}Y++}}function be(){{if(Y--,Y===0){var e={configurable:!0,enumerable:!0,writable:!0};Object.defineProperties(console,{log:P({},e,{value:le}),info:P({},e,{value:V}),warn:P({},e,{value:k}),error:P({},e,{value:se}),group:P({},e,{value:oe}),groupCollapsed:P({},e,{value:ce}),groupEnd:P({},e,{value:de})})}Y<0&&S("disabledDepth fell below zero. This is a bug in React. Please file an issue.")}}var ee=O.ReactCurrentDispatcher,H;function Z(e,n,l){{if(H===void 0)try{throw Error()}catch(x){var f=x.stack.trim().match(/\n( *(at )?)/);H=f&&f[1]||""}return`
18
+ `+H+e}}var X=!1,ie;{var fe=typeof WeakMap=="function"?WeakMap:Map;ie=new fe}function me(e,n){if(!e||X)return"";{var l=ie.get(e);if(l!==void 0)return l}var f;X=!0;var x=Error.prepareStackTrace;Error.prepareStackTrace=void 0;var _;_=ee.current,ee.current=null,pe();try{if(n){var y=function(){throw Error()};if(Object.defineProperty(y.prototype,"props",{set:function(){throw Error()}}),typeof Reflect=="object"&&Reflect.construct){try{Reflect.construct(y,[])}catch(J){f=J}Reflect.construct(e,[],y)}else{try{y.call()}catch(J){f=J}e.call(y.prototype)}}else{try{throw Error()}catch(J){f=J}e()}}catch(J){if(J&&f&&typeof J.stack=="string"){for(var p=J.stack.split(`
19
+ `),B=f.stack.split(`
20
+ `),$=p.length-1,L=B.length-1;$>=1&&L>=0&&p[$]!==B[L];)L--;for(;$>=1&&L>=0;$--,L--)if(p[$]!==B[L]){if($!==1||L!==1)do if($--,L--,L<0||p[$]!==B[L]){var Q=`
21
+ `+p[$].replace(" at new "," at ");return e.displayName&&Q.includes("<anonymous>")&&(Q=Q.replace("<anonymous>",e.displayName)),typeof e=="function"&&ie.set(e,Q),Q}while($>=1&&L>=0);break}}}finally{X=!1,ee.current=_,be(),Error.prepareStackTrace=x}var xe=e?e.displayName||e.name:"",ye=xe?Z(xe):"";return typeof e=="function"&&ie.set(e,ye),ye}function N(e,n,l){return me(e,!1)}function d(e){var n=e.prototype;return!!(n&&n.isReactComponent)}function j(e,n,l){if(e==null)return"";if(typeof e=="function")return me(e,d(e));if(typeof e=="string")return Z(e);switch(e){case T:return Z("Suspense");case E:return Z("SuspenseList")}if(typeof e=="object")switch(e.$$typeof){case v:return N(e.render);case w:return j(e.type,n,l);case F:{var f=e,x=f._payload,_=f._init;try{return j(_(x),n,l)}catch{}}}return""}var A=Object.prototype.hasOwnProperty,G={},Ye=O.ReactDebugCurrentFrame;function Pe(e){if(e){var n=e._owner,l=j(e.type,e._source,n?n.type:null);Ye.setExtraStackFrame(l)}else Ye.setExtraStackFrame(null)}function hr(e,n,l,f,x){{var _=Function.call.bind(A);for(var y in e)if(_(e,y)){var p=void 0;try{if(typeof e[y]!="function"){var B=Error((f||"React class")+": "+l+" type `"+y+"` is invalid; it must be a function, usually from the `prop-types` package, but received `"+typeof e[y]+"`.This often happens because of typos such as `PropTypes.function` instead of `PropTypes.func`.");throw B.name="Invariant Violation",B}p=e[y](n,y,f,l,null,"SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED")}catch($){p=$}p&&!(p instanceof Error)&&(Pe(x),S("%s: type specification of %s `%s` is invalid; the type checker function must return `null` or an `Error` but returned a %s. You may have forgotten to pass an argument to the type checker creator (arrayOf, instanceOf, objectOf, oneOf, oneOfType, and shape all require an argument).",f||"React class",l,y,typeof p),Pe(null)),p instanceof Error&&!(p.message in G)&&(G[p.message]=!0,Pe(x),S("Failed %s type: %s",l,p.message),Pe(null))}}}var xr=Array.isArray;function ke(e){return xr(e)}function br(e){{var n=typeof Symbol=="function"&&Symbol.toStringTag,l=n&&e[Symbol.toStringTag]||e.constructor.name||"Object";return l}}function gr(e){try{return Ve(e),!1}catch{return!0}}function Ve(e){return""+e}function ze(e){if(gr(e))return S("The provided key is an unsupported type %s. This value must be coerced to a string before before using it here.",br(e)),Ve(e)}var Be=O.ReactCurrentOwner,Er={key:!0,ref:!0,__self:!0,__source:!0},qe,Ke;function wr(e){if(A.call(e,"ref")){var n=Object.getOwnPropertyDescriptor(e,"ref").get;if(n&&n.isReactWarning)return!1}return e.ref!==void 0}function jr(e){if(A.call(e,"key")){var n=Object.getOwnPropertyDescriptor(e,"key").get;if(n&&n.isReactWarning)return!1}return e.key!==void 0}function _r(e,n){typeof e.ref=="string"&&Be.current}function Rr(e,n){{var l=function(){qe||(qe=!0,S("%s: `key` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://reactjs.org/link/special-props)",n))};l.isReactWarning=!0,Object.defineProperty(e,"key",{get:l,configurable:!0})}}function Cr(e,n){{var l=function(){Ke||(Ke=!0,S("%s: `ref` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://reactjs.org/link/special-props)",n))};l.isReactWarning=!0,Object.defineProperty(e,"ref",{get:l,configurable:!0})}}var Sr=function(e,n,l,f,x,_,y){var p={$$typeof:t,type:e,key:n,ref:l,props:y,_owner:_};return p._store={},Object.defineProperty(p._store,"validated",{configurable:!1,enumerable:!1,writable:!0,value:!1}),Object.defineProperty(p,"_self",{configurable:!1,enumerable:!1,writable:!1,value:f}),Object.defineProperty(p,"_source",{configurable:!1,enumerable:!1,writable:!1,value:x}),Object.freeze&&(Object.freeze(p.props),Object.freeze(p)),p};function Pr(e,n,l,f,x){{var _,y={},p=null,B=null;l!==void 0&&(ze(l),p=""+l),jr(n)&&(ze(n.key),p=""+n.key),wr(n)&&(B=n.ref,_r(n,x));for(_ in n)A.call(n,_)&&!Er.hasOwnProperty(_)&&(y[_]=n[_]);if(e&&e.defaultProps){var $=e.defaultProps;for(_ in $)y[_]===void 0&&(y[_]=$[_])}if(p||B){var L=typeof e=="function"?e.displayName||e.name||"Unknown":e;p&&Rr(y,L),B&&Cr(y,L)}return Sr(e,p,B,x,f,Be.current,y)}}var Te=O.ReactCurrentOwner,Ge=O.ReactDebugCurrentFrame;function he(e){if(e){var n=e._owner,l=j(e.type,e._source,n?n.type:null);Ge.setExtraStackFrame(l)}else Ge.setExtraStackFrame(null)}var Oe;Oe=!1;function Fe(e){return typeof e=="object"&&e!==null&&e.$$typeof===t}function Je(){{if(Te.current){var e=z(Te.current.type);if(e)return`
22
22
 
23
- Check the render method of \``+e+"`."}return""}}function vr(e){return""}var Ne={};function pr(e){{var r=De();if(!r){var t=typeof e=="string"?e:e.displayName||e.name;t&&(r=`
23
+ Check the render method of \``+e+"`."}return""}}function Nr(e){return""}var He={};function kr(e){{var n=Je();if(!n){var l=typeof e=="string"?e:e.displayName||e.name;l&&(n=`
24
24
 
25
- Check the top-level render call using <`+t+">.")}return r}}function Me(e,r){{if(!e._store||e._store.validated||e.key!=null)return;e._store.validated=!0;var t=pr(r);if(Ne[t])return;Ne[t]=!0;var a="";e&&e._owner&&e._owner!==fe.current&&(a=" It was passed a child from "+M(e._owner.type)+"."),$(e),p('Each child in a list should have a unique "key" prop.%s%s See https://reactjs.org/link/warning-keys for more information.',t,a),$(null)}}function Le(e,r){{if(typeof e!="object")return;if(de(e))for(var t=0;t<e.length;t++){var a=e[t];ve(a)&&Me(a,r)}else if(ve(e))e._store&&(e._store.validated=!0);else if(e){var u=P(e);if(typeof u=="function"&&u!==e.entries)for(var f=u.call(e),l;!(l=f.next()).done;)ve(l.value)&&Me(l.value,r)}}}function yr(e){{var r=e.type;if(r==null||typeof r=="string")return;var t;if(typeof r=="function")t=r.propTypes;else if(typeof r=="object"&&(r.$$typeof===b||r.$$typeof===y))t=r.propTypes;else return;if(t){var a=M(r);tr(t,e.props,"prop",a,e)}else if(r.PropTypes!==void 0&&!me){me=!0;var u=M(r);p("Component %s declared `PropTypes` instead of `propTypes`. Did you misspell the property assignment?",u||"Unknown")}typeof r.getDefaultProps=="function"&&!r.getDefaultProps.isReactClassApproved&&p("getDefaultProps is only used on classic React.createClass definitions. Use a static property named `defaultProps` instead.")}}function hr(e){{for(var r=Object.keys(e.props),t=0;t<r.length;t++){var a=r[t];if(a!=="children"&&a!=="key"){$(e),p("Invalid prop `%s` supplied to `React.Fragment`. React.Fragment can only have `key` and `children` props.",a),$(null);break}}e.ref!==null&&($(e),p("Invalid attribute `ref` supplied to `React.Fragment`."),$(null))}}var Ue={};function Ye(e,r,t,a,u,f){{var l=Y(e);if(!l){var s="";(e===void 0||typeof e=="object"&&e!==null&&Object.keys(e).length===0)&&(s+=" You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.");var j=vr();j?s+=j:s+=De();var g;e===null?g="null":de(e)?g="array":e!==void 0&&e.$$typeof===o?(g="<"+(M(e.type)||"Unknown")+" />",s=" Did you accidentally export a JSX literal instead of a component?"):g=typeof e,p("React.jsx: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: %s.%s",g,s)}var _=mr(e,r,t,u,f);if(_==null)return _;if(l){var A=r.children;if(A!==void 0)if(a)if(de(A)){for(var W=0;W<A.length;W++)Le(A[W],e);Object.freeze&&Object.freeze(A)}else p("React.jsx: Static children should always be an array. You are likely explicitly calling React.jsxs or React.jsxDEV. Use the Babel transform instead.");else Le(A,e)}if(z.call(r,"key")){var I=M(e),O=Object.keys(r).filter(function(Sr){return Sr!=="key"}),pe=O.length>0?"{key: someKey, "+O.join(": ..., ")+": ...}":"{key: someKey}";if(!Ue[I+pe]){var Rr=O.length>0?"{"+O.join(": ..., ")+": ...}":"{}";p(`A props object containing a "key" prop is being spread into JSX:
25
+ Check the top-level render call using <`+l+">.")}return n}}function Ze(e,n){{if(!e._store||e._store.validated||e.key!=null)return;e._store.validated=!0;var l=kr(n);if(He[l])return;He[l]=!0;var f="";e&&e._owner&&e._owner!==Te.current&&(f=" It was passed a child from "+z(e._owner.type)+"."),he(e),S('Each child in a list should have a unique "key" prop.%s%s See https://reactjs.org/link/warning-keys for more information.',l,f),he(null)}}function Xe(e,n){{if(typeof e!="object")return;if(ke(e))for(var l=0;l<e.length;l++){var f=e[l];Fe(f)&&Ze(f,n)}else if(Fe(e))e._store&&(e._store.validated=!0);else if(e){var x=q(e);if(typeof x=="function"&&x!==e.entries)for(var _=x.call(e),y;!(y=_.next()).done;)Fe(y.value)&&Ze(y.value,n)}}}function Tr(e){{var n=e.type;if(n==null||typeof n=="string")return;var l;if(typeof n=="function")l=n.propTypes;else if(typeof n=="object"&&(n.$$typeof===v||n.$$typeof===w))l=n.propTypes;else return;if(l){var f=z(n);hr(l,e.props,"prop",f,e)}else if(n.PropTypes!==void 0&&!Oe){Oe=!0;var x=z(n);S("Component %s declared `PropTypes` instead of `propTypes`. Did you misspell the property assignment?",x||"Unknown")}typeof n.getDefaultProps=="function"&&!n.getDefaultProps.isReactClassApproved&&S("getDefaultProps is only used on classic React.createClass definitions. Use a static property named `defaultProps` instead.")}}function Or(e){{for(var n=Object.keys(e.props),l=0;l<n.length;l++){var f=n[l];if(f!=="children"&&f!=="key"){he(e),S("Invalid prop `%s` supplied to `React.Fragment`. React.Fragment can only have `key` and `children` props.",f),he(null);break}}e.ref!==null&&(he(e),S("Invalid attribute `ref` supplied to `React.Fragment`."),he(null))}}var Qe={};function er(e,n,l,f,x,_){{var y=I(e);if(!y){var p="";(e===void 0||typeof e=="object"&&e!==null&&Object.keys(e).length===0)&&(p+=" You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.");var B=Nr();B?p+=B:p+=Je();var $;e===null?$="null":ke(e)?$="array":e!==void 0&&e.$$typeof===t?($="<"+(z(e.type)||"Unknown")+" />",p=" Did you accidentally export a JSX literal instead of a component?"):$=typeof e,S("React.jsx: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: %s.%s",$,p)}var L=Pr(e,n,l,x,_);if(L==null)return L;if(y){var Q=n.children;if(Q!==void 0)if(f)if(ke(Q)){for(var xe=0;xe<Q.length;xe++)Xe(Q[xe],e);Object.freeze&&Object.freeze(Q)}else S("React.jsx: Static children should always be an array. You are likely explicitly calling React.jsxs or React.jsxDEV. Use the Babel transform instead.");else Xe(Q,e)}if(A.call(n,"key")){var ye=z(e),J=Object.keys(n).filter(function($r){return $r!=="key"}),De=J.length>0?"{key: someKey, "+J.join(": ..., ")+": ...}":"{key: someKey}";if(!Qe[ye+De]){var Ar=J.length>0?"{"+J.join(": ..., ")+": ...}":"{}";S(`A props object containing a "key" prop is being spread into JSX:
26
26
  let props = %s;
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} />`,pe,I,Rr,I),Ue[I+pe]=!0}}return e===i?hr(_):yr(_),_}}function gr(e,r,t){return Ye(e,r,t,!0)}function br(e,r,t){return Ye(e,r,t,!1)}var Er=br,_r=gr;B.Fragment=i,B.jsx=Er,B.jsxs=_r}()),B}process.env.NODE_ENV==="production"?ye.exports=wr():ye.exports=xr();var m=ye.exports;function qe(n){var o,c,i="";if(typeof n=="string"||typeof n=="number")i+=n;else if(typeof n=="object")if(Array.isArray(n)){var v=n.length;for(o=0;o<v;o++)n[o]&&(c=qe(n[o]))&&(i&&(i+=" "),i+=c)}else for(c in n)n[c]&&(i&&(i+=" "),i+=c);return i}function We(){for(var n,o,c=0,i="",v=arguments.length;c<v;c++)(n=arguments[c])&&(o=qe(n))&&(i&&(i+=" "),i+=o);return i}const Cr="https://core.spreedly.com/iframe/iframe-v1.min.js";let ae={};const Tr=n=>{ae={...ae,...n}},Ve=n=>{var c;const o=globalThis.process;return(c=o==null?void 0:o.env)==null?void 0:c[n]},he=()=>({baseUrl:ae.baseUrl??Ve("REACT_APP_MOVMO_API_URL")??"",spreedlyScriptUrl:ae.spreedlyScriptUrl??Ve("REACT_APP_SPREEDLY_SCRIPT_URL")??Cr}),Be={Accept:"application/json","Content-Type":"application/json"},Je=n=>{const{baseUrl:o}=he();if(!o)throw new Error("@movmo_app/payments: baseUrl is not configured. Call setPaymentsConfig({ baseUrl }) at app boot, or set REACT_APP_MOVMO_API_URL.");return`${o}${n}`},Ke=async(n,o)=>{try{return(await n.json()).message||o}catch{return o}},jr=async()=>{const n=await fetch(Je("/v1/payments/tokenization-session"),{method:"GET",headers:Be,credentials:"include"});if(!n.ok)throw new Error(await Ke(n,`Failed to fetch tokenization session (${n.status})`));return await n.json()},Pr=async(n,o,c)=>{const i=await fetch(Je(`/v1/users/${encodeURIComponent(n)}/payment-methods/from-token`),{method:"POST",headers:Be,credentials:"include",body:JSON.stringify({tokenizedPaymentMethodId:o,isDefault:c})});if(!i.ok)throw new Error(await Ke(i,`Failed to save payment method (${i.status})`));const v=await i.json();return{id:v.id,last4:v.last4,brand:v.brand,expMonth:v.expMonth,expYear:v.expYear,isDefault:v.isDefault}};let J=null;const Or=()=>{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(J)return J;const{spreedlyScriptUrl:n}=he();return J=new Promise((o,c)=>{const i=document.querySelector(`script[src="${n}"]`),v=()=>{window.Spreedly?o(window.Spreedly):c(new Error("Spreedly script loaded but window.Spreedly is undefined."))};if(i){if(window.Spreedly){v();return}i.addEventListener("load",v,{once:!0}),i.addEventListener("error",()=>{i.remove(),c(new Error("Failed to load Spreedly iframe script."))},{once:!0});return}const x=document.createElement("script");x.src=n,x.async=!0,x.onload=v,x.onerror=()=>{x.remove(),c(new Error("Failed to load Spreedly iframe script."))},document.head.appendChild(x)}).catch(o=>{throw J=null,o}),J},kr="movmo-card-number",Fr="movmo-card-cvv",Ar=n=>({first_name:n.firstName,last_name:n.lastName,month:n.month,year:n.year,...n.email?{email:n.email}:{},...n.zip?{zip:n.zip}:{}}),Ge=(n={})=>{const{numberEl:o=kr,cvvEl:c=Fr,onCardTokenized:i,onFieldErrors:v}=n,[x,D]=R.useState("idle"),[k,b]=R.useState(null),C=R.useRef(null),E=R.useRef(i),y=R.useRef(v);R.useEffect(()=>{E.current=i},[i]),R.useEffect(()=>{y.current=v},[v]),R.useEffect(()=>{let T=!1;return D("loading"),b(null),(async()=>{try{const[h,S]=await Promise.all([jr(),Or()]);if(T)return;C.current=S,S.on("ready",()=>{T||D("ready")}),S.on("paymentMethod",P=>{var w;(w=E.current)==null||w.call(E,P)}),S.on("errors",P=>{var w;(w=y.current)==null||w.call(y,P)}),S.on("consoleError",P=>{var p;const w=P instanceof Error?P.message:"An unexpected error occurred while securing your card.";(p=y.current)==null||p.call(y,[{message:w}])}),S.init(h.environmentKey,{numberEl:o,cvvEl:c,nonce:h.nonce,timestamp:String(h.timestamp),certificateToken:h.certificateToken,signature:h.signature})}catch(h){if(T)return;const S=h instanceof Error?h.message:"Failed to initialize card form.";b(S),D("error")}})(),()=>{var h,S;T=!0;try{(S=(h=C.current)==null?void 0:h.removeHandlers)==null||S.call(h)}catch{}C.current=null}},[o,c]);const N=R.useCallback(T=>{var h;(h=C.current)==null||h.tokenizeCreditCard(Ar(T))},[]);return{status:x,error:k,tokenize:N}},ne="h-11 w-full rounded-md border border-grayWarm-300 bg-white px-3 py-2 text-sm text-grayWarm-900 placeholder:text-grayWarm-400 focus:border-brand-500 focus:outline-none focus:ring-1 focus:ring-brand-500",ze="h-11 w-full rounded-md border border-grayWarm-300 bg-white px-3 py-2 focus-within:border-brand-500 focus-within:ring-1 focus-within:ring-brand-500",V="mb-1 block text-xs font-medium text-grayWarm-700",Dr=Array.from({length:12},(n,o)=>String(o+1).padStart(2,"0")),Nr=(()=>{const n=new Date().getFullYear();return Array.from({length:15},(o,c)=>String(n+c))})(),He=({userId:n,onSuccess:o,onError:c,isDefault:i=!1,className:v,defaultCardholderFirstName:x="",defaultCardholderLastName:D=""})=>{const[k,b]=R.useState(x),[C,E]=R.useState(D),[y,N]=R.useState(""),[T,h]=R.useState(""),[S,P]=R.useState("idle"),[w,p]=R.useState(null),K=R.useCallback(async d=>{P("saving");try{const F=await Pr(n,d,i);P("ready"),o(F)}catch(F){const L=F instanceof Error?F.message:"Failed to save payment method.";p(L),P("ready"),c(L)}},[n,i,o,c]),G=R.useCallback(d=>{const F=d.map(L=>L.message).filter(Boolean).join("; ")||"Card validation failed.";p(F),P(L=>L==="tokenizing"?"ready":L),c(F)},[c]),oe=R.useMemo(()=>({onCardTokenized:K,onFieldErrors:G}),[K,G]),{status:U,error:ie,tokenize:se}=Ge(oe),H=!k.trim()||!C.trim()||!y||!T,Y=S==="tokenizing"||S==="saving",X=U==="ready"&&!Y&&!H,Z=d=>{d.preventDefault(),X&&(p(null),P("tokenizing"),se({firstName:k.trim(),lastName:C.trim(),month:y,year:T}))},M=w??ie;return m.jsxs("form",{onSubmit:Z,className:We("flex w-full flex-col gap-4",v),"data-testid":"movmo-card-form",children:[m.jsxs("div",{className:"grid grid-cols-1 gap-3 sm:grid-cols-2",children:[m.jsxs("div",{children:[m.jsx("label",{htmlFor:"movmo-card-first-name",className:V,children:"First name"}),m.jsx("input",{id:"movmo-card-first-name",type:"text",autoComplete:"cc-given-name",value:k,onChange:d=>b(d.target.value),className:ne,disabled:U!=="ready"||Y,required:!0})]}),m.jsxs("div",{children:[m.jsx("label",{htmlFor:"movmo-card-last-name",className:V,children:"Last name"}),m.jsx("input",{id:"movmo-card-last-name",type:"text",autoComplete:"cc-family-name",value:C,onChange:d=>E(d.target.value),className:ne,disabled:U!=="ready"||Y,required:!0})]})]}),m.jsxs("div",{children:[m.jsx("span",{id:"movmo-card-number-label",className:V,children:"Card number"}),m.jsx("div",{id:"movmo-card-number","aria-labelledby":"movmo-card-number-label",className:ze,"data-testid":"movmo-card-number-mount"})]}),m.jsxs("div",{className:"grid grid-cols-3 gap-3",children:[m.jsxs("div",{children:[m.jsx("label",{htmlFor:"movmo-card-exp-month",className:V,children:"Month"}),m.jsxs("select",{id:"movmo-card-exp-month",value:y,onChange:d=>N(d.target.value),className:ne,disabled:U!=="ready"||Y,required:!0,children:[m.jsx("option",{value:"",children:"MM"}),Dr.map(d=>m.jsx("option",{value:d,children:d},d))]})]}),m.jsxs("div",{children:[m.jsx("label",{htmlFor:"movmo-card-exp-year",className:V,children:"Year"}),m.jsxs("select",{id:"movmo-card-exp-year",value:T,onChange:d=>h(d.target.value),className:ne,disabled:U!=="ready"||Y,required:!0,children:[m.jsx("option",{value:"",children:"YYYY"}),Nr.map(d=>m.jsx("option",{value:d,children:d},d))]})]}),m.jsxs("div",{children:[m.jsx("span",{id:"movmo-card-cvv-label",className:V,children:"CVV"}),m.jsx("div",{id:"movmo-card-cvv","aria-labelledby":"movmo-card-cvv-label",className:ze,"data-testid":"movmo-card-cvv-mount"})]})]}),M?m.jsx("p",{role:"alert",className:"rounded-md bg-error-50 px-3 py-2 text-sm text-error-700","data-testid":"movmo-card-error",children:M}):null,m.jsx("button",{type:"submit",disabled:!X,className:We("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:Y?"Saving…":U==="loading"?"Loading…":"Save card"})]})};He.displayName="MovmoCardForm";exports.MovmoCardForm=He;exports.getPaymentsConfig=he;exports.setPaymentsConfig=Tr;exports.useMovmoCardFields=Ge;
30
+ <%s key={someKey} {...props} />`,De,ye,Ar,ye),Qe[ye+De]=!0}}return e===o?Or(L):Tr(L),L}}function Fr(e,n,l){return er(e,n,l,!0)}function Dr(e,n,l){return er(e,n,l,!1)}var Mr=Dr,Wr=Fr;Ee.Fragment=o,Ee.jsx=Mr,Ee.jsxs=Wr}()),Ee}process.env.NODE_ENV==="production"?Ue.exports=Ir():Ue.exports=Ur();var a=Ue.exports;function cr(r){var t,s,o="";if(typeof r=="string"||typeof r=="number")o+=r;else if(typeof r=="object")if(Array.isArray(r)){var c=r.length;for(t=0;t<c;t++)r[t]&&(s=cr(r[t]))&&(o&&(o+=" "),o+=s)}else for(s in r)r[s]&&(o&&(o+=" "),o+=s);return o}function re(){for(var r,t,s=0,o="",c=arguments.length;s<c;s++)(r=arguments[s])&&(t=cr(r))&&(o&&(o+=" "),o+=t);return o}const Lr="https://core.spreedly.com/iframe/iframe-v1.min.js",Yr="https://cdn.e2e.movmo.io";let je={};const Vr=r=>{je={...je,...r}},Me=r=>{var s;const t=globalThis.process;return(s=t==null?void 0:t.env)==null?void 0:s[r]},_e=()=>({baseUrl:je.baseUrl??Me("REACT_APP_MOVMO_API_URL")??"",spreedlyScriptUrl:je.spreedlyScriptUrl??Me("REACT_APP_SPREEDLY_SCRIPT_URL")??Lr,iconCdnBaseUrl:je.iconCdnBaseUrl??Me("REACT_APP_MOVMO_ICON_CDN_URL")??Yr}),dr=r=>({id:r.id,last4:r.last4,brand:r.brand,expMonth:r.expMonth,expYear:r.expYear,isDefault:r.isDefault}),Re={Accept:"application/json","Content-Type":"application/json"},Ce=r=>{const{baseUrl:t}=_e();if(!t)throw new Error("@movmo_app/payments: baseUrl is not configured. Call setPaymentsConfig({ baseUrl }) at app boot, or set REACT_APP_MOVMO_API_URL.");return`${t}${r}`},Se=async(r,t)=>{try{return(await r.json()).message||t}catch{return t}},zr=async()=>{const r=await fetch(Ce("/v1/payments/tokenization-session"),{method:"GET",headers:Re,credentials:"include"});if(!r.ok)throw new Error(await Se(r,`Failed to fetch tokenization session (${r.status})`));return await r.json()},Br=async(r,t,s)=>{const o=await fetch(Ce(`/v1/users/${encodeURIComponent(r)}/payment-methods/from-token`),{method:"POST",headers:Re,credentials:"include",body:JSON.stringify({tokenizedPaymentMethodId:t,isDefault:s})});if(!o.ok)throw new Error(await Se(o,`Failed to save payment method (${o.status})`));const c=await o.json();return dr(c)},qr=async r=>{const t=await fetch(Ce(`/v1/users/${encodeURIComponent(r)}/payment-methods`),{method:"GET",headers:Re,credentials:"include"});if(!t.ok)throw new Error(await Se(t,`Failed to fetch payment methods (${t.status})`));return(await t.json()??[]).map(dr)},Kr=async(r,t)=>{const s=await fetch(Ce(`/v1/users/${encodeURIComponent(r)}/payment-methods/${encodeURIComponent(t)}`),{method:"DELETE",headers:Re,credentials:"include"});if(!s.ok)throw new Error(await Se(s,`Failed to delete payment method (${s.status})`))},Gr=async(r,t,s)=>{const o=await fetch(Ce(`/v1/users/${encodeURIComponent(r)}/payment-methods/${encodeURIComponent(t)}`),{method:"PUT",headers:Re,credentials:"include",body:JSON.stringify(s)});if(!o.ok)throw new Error(await Se(o,`Failed to update payment method (${o.status})`))},Jr={visa:"visa",master:"mastercard",mastercard:"mastercard",american_express:"amex",amex:"amex",discover:"discover"},Hr=28,ve=({brand:r,className:t,width:s=46})=>{const o=Jr[r.toLowerCase()]??"credit-card",c=Math.max(s,Hr),m=Math.round(c*24/34),{iconCdnBaseUrl:u}=_e();return a.jsx("img",{src:`${u}/website/assets/icons/payments/${o}.svg`,alt:o==="credit-card"?"card":`${o} card`,"data-testid":`card-brand-${r.toLowerCase()}`,className:t,width:c,height:m,loading:"lazy"})};let we=null;const Zr=()=>{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(we)return we;const{spreedlyScriptUrl:r}=_e();return we=new Promise((t,s)=>{const o=document.querySelector(`script[src="${r}"]`),c=()=>{window.Spreedly?t(window.Spreedly):s(new Error("Spreedly script loaded but window.Spreedly is undefined."))};if(o){if(window.Spreedly){c();return}o.addEventListener("load",c,{once:!0}),o.addEventListener("error",()=>{o.remove(),s(new Error("Failed to load Spreedly iframe script."))},{once:!0});return}const m=document.createElement("script");m.src=r,m.async=!0,m.onload=c,m.onerror=()=>{m.remove(),s(new Error("Failed to load Spreedly iframe script."))},document.head.appendChild(m)}).catch(t=>{throw we=null,t}),we},Xr="movmo-card-number",Qr="movmo-card-cvv",et=r=>({first_name:r.firstName,last_name:r.lastName,month:r.month,year:r.year,...r.email?{email:r.email}:{},...r.zip?{zip:r.zip}:{}}),ur=(r={})=>{const{numberEl:t=Xr,cvvEl:s=Qr,onCardTokenized:o,onFieldErrors:c,onValidityChange:m,onBrandChange:u}=r,[h,v]=i.useState("idle"),[T,E]=i.useState(null),[w,F]=i.useState({number:!1,cvv:!1}),[R,D]=i.useState(null),C=i.useRef(null),q=i.useRef(o),O=i.useRef(c),S=i.useRef(m),K=i.useRef(u);i.useEffect(()=>{q.current=o},[o]),i.useEffect(()=>{O.current=c},[c]),i.useEffect(()=>{S.current=m},[m]),i.useEffect(()=>{K.current=u},[u]);const M=i.useRef({number:!1,cvv:!1});i.useEffect(()=>{let W=!1;return v("loading"),E(null),F({number:!1,cvv:!1}),D(null),M.current={number:!1,cvv:!1},(async()=>{try{const[b,g]=await Promise.all([zr(),Zr()]);if(W)return;C.current=g,g.on("ready",()=>{var I;if(!W){v("ready");try{(I=g.setNumberFormat)==null||I.call(g,"prettyFormat")}catch{}}}),g.on("paymentMethod",I=>{var U;(U=q.current)==null||U.call(q,I)}),g.on("errors",I=>{var U;(U=O.current)==null||U.call(O,I)}),g.on("fieldEvent",(I,U,ne,z)=>{var le;if(W||U!=="input")return;const P=z??{};if(I==="number"){if(P.numberLength===0)D(V=>{var k;return V!==null&&((k=K.current)==null||k.call(K,null)),null});else if(P.cardType!==void 0){const V=P.cardType||null;D(k=>{var se;return k!==V&&((se=K.current)==null||se.call(K,V)),V})}}const Y=I==="number"?typeof P.validNumber=="boolean"?P.validNumber:void 0:typeof P.validCvv=="boolean"?P.validCvv:void 0;if(typeof Y=="boolean"){const V=M.current,k=I==="number"?{...V,number:Y}:{...V,cvv:Y};(k.number!==V.number||k.cvv!==V.cvv)&&(M.current=k,F(k),(le=S.current)==null||le.call(S,k))}}),g.on("consoleError",I=>{var ne;const U=I instanceof Error?I.message:"An unexpected error occurred while securing your card.";(ne=O.current)==null||ne.call(O,[{message:U}])}),g.init(b.environmentKey,{numberEl:t,cvvEl:s,nonce:b.nonce,timestamp:String(b.timestamp),certificateToken:b.certificateToken,signature:b.signature})}catch(b){if(W)return;const g=b instanceof Error?b.message:"Failed to initialize card form.";E(g),v("error")}})(),()=>{var b,g;W=!0;try{(g=(b=C.current)==null?void 0:b.removeHandlers)==null||g.call(b)}catch{}C.current=null}},[t,s]);const te=i.useCallback(W=>{var b;(b=C.current)==null||b.tokenizeCreditCard(et(W))},[]),ae=i.useCallback(W=>{var b,g;(g=(b=C.current)==null?void 0:b.transferFocus)==null||g.call(b,W)},[]);return{status:h,error:T,tokenize:te,validity:w,brand:R,focusField:ae}},We="h-11 w-full rounded-md border border-grayWarm-300 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]",ar="flex h-11 w-full items-center gap-2 rounded-md border border-grayWarm-300 bg-white px-3 py-2 transition-shadow focus-within:border-grayWarm-950 focus-within:shadow-[0_0_0_1px_#1c1917]",rt=r=>{const t=r.trim();if(!t)return{firstName:"",lastName:""};const s=t.lastIndexOf(" ");return s===-1?{firstName:t,lastName:""}:{firstName:t.slice(0,s).trim(),lastName:t.slice(s+1).trim()}},fr=r=>{const t=r.replace(/\D/g,"");if(t.length<4)return{month:"",year:""};const s=t.slice(0,2),o=t.slice(2),c=o.length===2?`20${o}`:o.slice(0,4);return{month:s,year:c}},tt=r=>{const t=r.replace(/\D/g,"").slice(0,4);return t.length<=2?t:`${t.slice(0,2)} / ${t.slice(2)}`},Ae=r=>{const{month:t,year:s}=fr(r);if(!t||!s)return!1;const o=parseInt(t,10),c=parseInt(s,10);if(isNaN(o)||o<1||o>12)return!1;const m=new Date;return!(c<m.getFullYear()||c>m.getFullYear()+20||c===m.getFullYear()&&o<m.getMonth()+1)},$e=r=>/^\d{5}(-\d{4})?$/.test(r.trim()),at=r=>{const t=r.replace(/[^\d]/g,"").slice(0,9);return t.length<=5?t:`${t.slice(0,5)}-${t.slice(5)}`},Le=({userId:r,onSuccess:t,onError:s,isDefault:o=!1,className:c,defaultCardholderName:m,defaultCardholderFirstName:u="",defaultCardholderLastName:h="",autoSave:v=!1,formId:T="movmo-card-form",hideInternalSaveButton:E=!1,onCanSubmitChange:w})=>{const F=m??[u,h].filter(Boolean).join(" "),[R,D]=i.useState(F),[C,q]=i.useState(""),[O,S]=i.useState(""),[K,M]=i.useState("idle"),[te,ae]=i.useState(null),W=i.useRef(!1),b=i.useCallback(async N=>{M("saving");try{const d=await Br(r,N,o);M("ready"),t(d)}catch(d){const j=d instanceof Error?d.message:"Failed to save payment method.";ae(j),M("ready"),W.current=!1,s(j)}},[r,o,t,s]),g=i.useCallback(N=>{const d=N.map(j=>j.message).filter(Boolean).join("; ")||"Card validation failed.";ae(d),M(j=>j==="tokenizing"?"ready":j),W.current=!1,s(d)},[s]),I=i.useMemo(()=>({onCardTokenized:b,onFieldErrors:g}),[b,g]),{status:U,error:ne,tokenize:z,validity:P,brand:Y,focusField:le}=ur(I),V=R.trim().split(/\s+/).length>=2,k=Ae(C),se=$e(O),oe=i.useRef(!1),ce=i.useRef(!1),de=i.useRef(!1),ue=i.useRef(null),pe=i.useRef(null);i.useEffect(()=>{var N;P.cvv?ce.current||(ce.current=!0,(N=ue.current)==null||N.focus()):ce.current=!1},[P.cvv]);const be=!V||!k||!se||!P.number||!P.cvv,ee=K==="tokenizing"||K==="saving",H=U==="ready"&&!ee&&!be,Z=i.useRef(w);i.useEffect(()=>{Z.current=w},[w]),i.useEffect(()=>{var N;(N=Z.current)==null||N.call(Z,H)},[H]);const X=i.useCallback(()=>{const{firstName:N,lastName:d}=rt(R),{month:j,year:A}=fr(C);ae(null),M("tokenizing"),z({firstName:N,lastName:d,month:j,year:A,zip:O.trim()})},[R,C,O,z]);i.useEffect(()=>{if(!v||!H||W.current)return;const N=setTimeout(()=>{W.current=!0,X()},1200);return()=>clearTimeout(N)},[v,H,X]);const ie=N=>{N.preventDefault(),H&&X()},fe=te??ne,me=U!=="ready"||ee;return a.jsxs("form",{id:T,onSubmit:ie,className:re("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:ar,children:[a.jsx(ve,{brand:Y??"",width:32,className:"flex-shrink-0"}),a.jsx("div",{id:"movmo-card-number","aria-labelledby":"movmo-card-number-label",className:"flex-1","data-testid":"movmo-card-number-mount"})]})]}),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:C,onChange:N=>{const d=tt(N.target.value);q(d),Ae(d)&&!oe.current?(oe.current=!0,le("cvv")):Ae(d)||(oe.current=!1)},className:re(We,C&&!k&&"border-error-500"),disabled:me,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:ar,children:[a.jsx("div",{id:"movmo-card-cvv","aria-labelledby":"movmo-card-cvv-label",className:"flex-1","data-testid":"movmo-card-cvv-mount"}),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:Y==="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:"ZIP code"}),a.jsx("input",{id:"movmo-card-zip",ref:ue,type:"text",inputMode:"numeric",autoComplete:"postal-code",placeholder:"ZIP code",value:O,onChange:N=>{var j;const d=at(N.target.value);S(d),$e(d)&&!de.current?(de.current=!0,(j=pe.current)==null||j.focus()):$e(d)||(de.current=!1)},className:re(We,O&&!se&&"border-error-500"),disabled:me,required:!0,"data-testid":"movmo-card-zip-input"})]})]}),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",ref:pe,type:"text",autoComplete:"cc-name",placeholder:"Cardholder name",value:R,onChange:N=>D(N.target.value),className:We,disabled:me,required:!0,"data-testid":"movmo-card-cardholder-name-input"})]}),fe?a.jsx("p",{role:"alert",className:"rounded-md bg-error-50 px-3 py-2 text-sm text-error-700","data-testid":"movmo-card-error",children:fe}):null,!v&&!E?a.jsx("button",{type:"submit",disabled:!H,className:re("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:ee?"Saving…":U==="loading"?"Loading…":"Save card"}):ee?a.jsx("p",{className:"text-center text-sm text-grayWarm-500","data-testid":"movmo-card-autosaving",children:"Saving card…"}):null]})};Le.displayName="MovmoCardForm";const mr=r=>{const[t,s]=i.useState("idle"),[o,c]=i.useState(null);return{deletePaymentMethod:i.useCallback(async u=>{s("pending"),c(null);try{await Kr(r,u),s("idle")}catch(h){const v=h instanceof Error?h.message:"Failed to delete payment method.";throw c(v),s("error"),h}},[r]),status:t,error:o}},pr=r=>{const[t,s]=i.useState("idle"),[o,c]=i.useState(null);return{setDefault:i.useCallback(async u=>{s("pending"),c(null);try{await Gr(r,u,{isDefault:!0}),s("idle")}catch(h){const v=h instanceof Error?h.message:"Failed to set default payment method.";throw c(v),s("error"),h}},[r]),status:t,error:o}},yr=r=>{const[t,s]=i.useState([]),[o,c]=i.useState("idle"),[m,u]=i.useState(null),[h,v]=i.useState(0),T=i.useRef(null);i.useEffect(()=>{var F;(F=T.current)==null||F.abort();const w=new AbortController;return T.current=w,c("loading"),u(null),qr(r).then(R=>{w.signal.aborted||(s(R),c("ready"))}).catch(R=>{if(w.signal.aborted)return;const D=R instanceof Error?R.message:"Failed to fetch payment methods.";u(D),c("error")}),()=>{w.abort()}},[r,h]);const E=i.useCallback(()=>{v(w=>w+1)},[]);return{items:t,status:o,error:m,refetch:E}},nt=({open:r,onClose:t,formId:s,saveDisabled:o,saveLabel:c="Save",children:m})=>(i.useEffect(()=>{if(!r)return;const u=h=>{h.key==="Escape"&&t()};return document.addEventListener("keydown",u),()=>document.removeEventListener("keydown",u)},[r,t]),i.useEffect(()=>{if(!r)return;const u=document.body.style.overflow;return document.body.style.overflow="hidden",()=>{document.body.style.overflow=u}},[r]),r?a.jsx("div",{role:"dialog","aria-modal":"true","aria-labelledby":"movmo-add-payment-title",className:"fixed inset-0 z-50 flex items-center justify-center bg-black/40 p-4",onClick:u=>{u.target===u.currentTarget&&t()},"data-testid":"add-payment-method-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:t,"aria-label":"Close add-payment dialog",className:"flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-md border border-grayWarm-200 text-grayWarm-700 hover:bg-grayWarm-100","data-testid":"add-payment-method-close",children:a.jsx("svg",{viewBox:"0 0 16 16",width:"14",height:"14","aria-hidden":"true",children:a.jsx("path",{d:"M3 3L13 13M13 3L3 13",stroke:"currentColor",strokeWidth:"1.75",strokeLinecap:"round"})})}),a.jsx("h2",{id:"movmo-add-payment-title",className:"flex-1 text-center text-base font-semibold text-grayWarm-900",children:"Add payment method"}),a.jsx("span",{"aria-hidden":"true",className:"h-8 w-8 flex-shrink-0"})]}),a.jsx("div",{className:"flex-1 overflow-y-auto p-4",children:m}),a.jsxs("div",{className:"flex gap-3 border-t border-grayWarm-200 p-4",children:[a.jsx("button",{type:"button",onClick:t,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":"add-payment-method-cancel",children:"Cancel"}),a.jsx("button",{type:"submit",form:s,disabled:o,className:re("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 hover:bg-brand-600","disabled:cursor-not-allowed disabled:bg-grayWarm-300 disabled:text-grayWarm-500"),"data-testid":"add-payment-method-save",children:c})]})]})}):null),st=(r,t,s=new Date)=>t<s.getFullYear()||t===s.getFullYear()&&r<s.getMonth()+1,nr=r=>{const t=r.toLowerCase();return t==="amex"?"Amex":t?t.charAt(0).toUpperCase()+t.slice(1):"Card"},ot=(r,t)=>`${String(r).padStart(2,"0")}/${t}`,it=({method:r,selectable:t,selected:s,onSelect:o,onSetDefault:c,onRemove:m,disabled:u,showDefaultBadge:h,hideSetDefaultMenuItem:v})=>{const[T,E]=i.useState(!1),w=i.useRef(null),F=st(r.expMonth,r.expYear);i.useEffect(()=>{if(!T)return;const D=C=>{var q;(q=w.current)!=null&&q.contains(C.target)||E(!1)};return document.addEventListener("mousedown",D),()=>document.removeEventListener("mousedown",D)},[T]);const R=()=>{t&&o&&!u&&o(r)};return a.jsxs("div",{className:re("flex items-center gap-3 rounded-lg border px-3 py-3 transition-colors",t&&!u&&"cursor-pointer",s?"border-grayWarm-950 shadow-[0_0_0_1px_#1c1917]":"border-grayWarm-200 hover:border-grayWarm-300"),onClick:R,"data-testid":`payment-method-row-${r.id}`,children:[t?a.jsx("input",{type:"radio",name:"movmo-payment-method",checked:s,onChange:()=>o==null?void 0:o(r),onClick:D=>D.stopPropagation(),"aria-label":`Select ${nr(r.brand)} ending ${r.last4}`,className:"h-4 w-4 cursor-pointer accent-grayWarm-950",disabled:u}):null,a.jsx(ve,{brand:r.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.jsxs("span",{className:"text-base font-semibold text-grayWarm-900",children:[nr(r.brand)," ••••",r.last4]}),h&&r.isDefault?a.jsx("span",{className:"inline-flex items-center rounded-full bg-success-50 px-2 py-0.5 text-xs font-medium text-success-700","data-testid":`badge-default-${r.id}`,children:"Default"}):null,F?a.jsx("span",{className:"inline-flex items-center rounded-full bg-error-50 px-2 py-0.5 text-xs font-medium text-error-700","data-testid":`badge-expired-${r.id}`,children:"Expired"}):null]}),a.jsxs("div",{className:re("text-sm",F?"text-grayWarm-400":"text-grayWarm-600"),children:["Exp. ",ot(r.expMonth,r.expYear)]})]}),a.jsxs("div",{className:"relative flex-shrink-0",ref:w,children:[a.jsx("button",{type:"button","aria-label":`Open actions for card ending ${r.last4}`,"aria-haspopup":"menu","aria-expanded":T,className:"rounded-lg p-2 text-grayWarm-600 transition-colors hover:bg-grayWarm-100 disabled:cursor-not-allowed disabled:opacity-50",disabled:u,onClick:D=>{D.stopPropagation(),E(C=>!C)},"data-testid":`menu-trigger-${r.id}`,children:a.jsxs("svg",{viewBox:"0 0 16 16",width:"16",height:"16","aria-hidden":"true",children:[a.jsx("circle",{cx:"3",cy:"8",r:"1.5",fill:"currentColor"}),a.jsx("circle",{cx:"8",cy:"8",r:"1.5",fill:"currentColor"}),a.jsx("circle",{cx:"13",cy:"8",r:"1.5",fill:"currentColor"})]})}),T?a.jsxs("div",{role:"menu",className:"absolute right-0 top-full z-10 mt-1 min-w-[160px] rounded-lg border border-grayWarm-200 bg-white py-1 shadow-lg",onClick:D=>D.stopPropagation(),"data-testid":`menu-${r.id}`,children:[!r.isDefault&&!v?a.jsx("button",{type:"button",role:"menuitem",className:"block w-full px-4 py-2 text-left text-sm text-grayWarm-700 hover:bg-grayWarm-100",onClick:()=>{E(!1),c(r)},"data-testid":`menu-set-default-${r.id}`,children:"Set as default"}):null,a.jsx("button",{type:"button",role:"menuitem",className:"block w-full px-4 py-2 text-left text-sm text-error-700 hover:bg-error-50",onClick:()=>{E(!1),m(r)},"data-testid":`menu-remove-${r.id}`,children:"Remove"})]}):null]})]})},lt=r=>`${_e().iconCdnBaseUrl}/website/assets/icons/payments/${r}.svg`,Ie=({slug:r,alt:t})=>a.jsx("img",{src:lt(r),alt:t,height:20,loading:"lazy",style:{height:20,width:"auto"}}),Ne=({type:r,label:t,iconNode:s})=>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-${r}`,children:[a.jsx("input",{type:"radio",name:"movmo-payment-type",value:r,onChange:()=>{},className:"h-4 w-4 cursor-pointer accent-grayWarm-900","aria-label":t}),a.jsxs("span",{className:"flex flex-1 items-center gap-2 text-sm font-semibold text-grayWarm-900",children:[t,s]})]}),sr=({selected:r,onSelect:t,creditCardBody:s,className:o})=>{const c=r==="credit_card";return a.jsxs("div",{className:re("flex w-full flex-col gap-3",o),children:[a.jsxs("label",{className:re("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,onChange:()=>t("credit_card"),className:"h-4 w-4 cursor-pointer accent-grayWarm-900","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(ve,{brand:"mastercard",width:28}),a.jsx(ve,{brand:"visa",width:28}),a.jsx(ve,{brand:"amex",width:28}),a.jsx(ve,{brand:"discover",width:28})]})]}),c?a.jsx("div",{"data-testid":"payment-type-credit_card-body",children:s}):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(Ne,{type:"paypal",label:"PayPal",iconNode:a.jsx(Ie,{slug:"paypal",alt:"PayPal"})}),a.jsx(Ne,{type:"googlepay",label:"GooglePay",iconNode:a.jsx(Ie,{slug:"google-pay",alt:"GooglePay"})}),a.jsx(Ne,{type:"klarna",label:"Klarna",iconNode:a.jsx(Ie,{slug:"klarna",alt:"Klarna"})}),a.jsx(Ne,{type:"ach",label:"Bank account (ACH)",iconNode:a.jsx("span",{className:"text-xs font-medium text-grayWarm-500",children:"Direct debit"})})]})},or="movmo-add-payment-form",ir=r=>{const t=r.filter(o=>o.isDefault),s=r.filter(o=>!o.isDefault);return[...t,...s]},lr=()=>a.jsxs("div",{className:"flex items-center gap-3 rounded-lg border border-grayWarm-200 px-3 py-3","data-testid":"payment-method-skeleton",children:[a.jsx("div",{className:"h-8 w-[46px] flex-shrink-0 animate-pulse rounded bg-grayWarm-200"}),a.jsxs("div",{className:"flex-1 space-y-2",children:[a.jsx("div",{className:"h-4 w-32 animate-pulse rounded bg-grayWarm-200"}),a.jsx("div",{className:"h-3 w-20 animate-pulse rounded bg-grayWarm-200"})]}),a.jsx("div",{className:"h-8 w-8 animate-pulse rounded bg-grayWarm-200"})]}),vr=({userId:r,defaultCardholderName:t,defaultCardholderFirstName:s,defaultCardholderLastName:o,selectedId:c,onSelect:m,onChange:u,paymentTypeSelector:h=!0,autoSaveFirstCard:v=!0,selectionSetsDefault:T=!1,showDefaultBadge:E=!0,className:w})=>{const{items:F,status:R,error:D,refetch:C}=yr(r),{deletePaymentMethod:q,status:O}=mr(r),{setDefault:S,status:K}=pr(r),[M,te]=i.useState([]),[ae,W]=i.useState(null),[b,g]=i.useState(!1),[I,U]=i.useState(null),[ne,z]=i.useState(!1),[P,Y]=i.useState(null),le=i.useCallback(()=>{g(!1),U(null),Y(null)},[]),V=t??([s,o].filter(Boolean).join(" ")||void 0);i.useEffect(()=>{R==="ready"&&te(ir(F))},[F,R]);const k=i.useCallback(d=>{te(j=>{const A=typeof d=="function"?d(j):d,G=ir(A);return u==null||u(G),G})},[u]),se=O==="pending"||K==="pending",oe=i.useCallback(async d=>{const j=M;W(null),k(A=>A.map(G=>({...G,isDefault:G.id===d.id})));try{await S(d.id),C()}catch(A){te(j),u==null||u(j);const G=A instanceof Error?A.message:"Failed to set default payment method.";W(G)}},[M,k,S,C,u]),ce=i.useCallback(d=>{m==null||m(d),T&&(d.isDefault||oe(d))},[m,T,oe]),de=i.useCallback(async d=>{const j=M;W(null),k(A=>A.filter(G=>G.id!==d.id));try{await q(d.id),C()}catch(A){te(j),u==null||u(j);const G=A instanceof Error?A.message:"Failed to delete payment method.";W(G)}},[M,k,q,C,u]),ue=i.useCallback(d=>{U(null),g(!1),Y(null),k(j=>[d,...j.filter(A=>A.id!==d.id)]),C()},[k,C]),pe=i.useCallback(d=>{U(d)},[]),be=R==="ready"&&M.length===0,ee=typeof m=="function",H=M.length===0,Z=v&&M.length===0,X=i.useCallback(d=>a.jsx(Le,{userId:r,isDefault:H,defaultCardholderName:V,autoSave:!d&&Z,hideInternalSaveButton:d,formId:d?or:void 0,onCanSubmitChange:d?z:void 0,onSuccess:ue,onError:pe}),[r,H,V,Z,ue,pe]),ie=i.useMemo(()=>X(!1),[X]),fe=i.useMemo(()=>X(!0),[X]),me=i.useMemo(()=>h?a.jsx(sr,{selected:P,onSelect:d=>Y(d),creditCardBody:ie}):ie,[h,P,ie]),N=i.useMemo(()=>h?a.jsx(sr,{selected:P,onSelect:d=>Y(d),creditCardBody:fe}):fe,[h,P,fe]);return a.jsxs("div",{className:re("flex w-full flex-col gap-4",w),"data-testid":"payment-methods-manager",children:[R==="loading"?a.jsxs("div",{className:"flex flex-col gap-2","data-testid":"payment-methods-loading",children:[a.jsx(lr,{}),a.jsx(lr,{})]}):null,R==="error"?a.jsxs("div",{role:"alert",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:D??"Failed to load payment methods."}),a.jsx("button",{type:"button",onClick:C,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,ae?a.jsx("div",{role:"alert",className:"rounded-md bg-error-50 p-3 text-sm text-error-700","data-testid":"payment-methods-mutation-error",children:ae}):null,R==="ready"&&M.length>0?a.jsx("div",{className:"flex flex-col gap-2",children:M.map(d=>a.jsx(it,{method:d,selectable:ee,selected:ee&&d.id===c,onSelect:ce,onSetDefault:oe,onRemove:de,disabled:se,showDefaultBadge:E,hideSetDefaultMenuItem:T},d.id))}):null,be?a.jsx("div",{"data-testid":"payment-methods-empty",children:me}):R==="ready"?a.jsx("button",{type:"button",onClick:()=>{U(null),Y(null),z(!1),g(!0)},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(nt,{open:b,onClose:le,formId:or,saveDisabled:!ne,children:[N,I?a.jsx("p",{role:"alert",className:"mt-3 rounded-md bg-error-50 px-3 py-2 text-sm text-error-700","data-testid":"payment-methods-modal-error",children:I}):null]})]})};vr.displayName="PaymentMethodsManager";const ct=r=>{const t=r.toLowerCase();return t==="amex"?"Amex":t?t.charAt(0).toUpperCase()+t.slice(1):"Card"},dt=({method:r,trailing:t,onClick:s,className:o})=>a.jsxs("div",{className:re("flex items-center gap-3 py-2",s&&"cursor-pointer",o),onClick:s,role:s?"button":void 0,tabIndex:s?0:void 0,onKeyDown:s?c=>{(c.key==="Enter"||c.key===" ")&&(c.preventDefault(),s())}:void 0,"data-testid":`payment-method-preview-${r.id}`,children:[a.jsx(ve,{brand:r.brand,className:"flex-shrink-0"}),a.jsxs("span",{className:"flex-1 text-base font-semibold text-grayWarm-900",children:[ct(r.brand)," ",r.last4]}),t]});exports.MovmoCardForm=Le;exports.PaymentMethodPreview=dt;exports.PaymentMethodsManager=vr;exports.getPaymentsConfig=_e;exports.setPaymentsConfig=Vr;exports.useDeletePaymentMethod=mr;exports.useMovmoCardFields=ur;exports.useSetDefaultPaymentMethod=pr;exports.useUserPaymentMethods=yr;
31
31
  //# sourceMappingURL=index.cjs.js.map