@amos.com/react-amos-js 0.9.16 → 0.9.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -82,7 +82,7 @@ In short, your app orchestrates the payment flow, while sensitive payment data s
82
82
 
83
83
  ## Appearance
84
84
 
85
- Card and bank components accept an optional `appearance` prop that controls the look of the iframe UI. It contains a `themeVariables` object whose keys are CSS custom-property names and whose values are strings, and an optional `labels` setting for field label placement. You can update this prop after page load to update the iframe appearance. Wallet buttons do not take `appearance`.
85
+ Card and bank components accept an optional `appearance` prop that controls the look of the iframe UI, and of the parent-page **Connect bank account** button when ACH verification is required. It contains a `themeVariables` object whose keys are CSS custom-property names and whose values are strings, and an optional `labels` setting for field label placement. You can update this prop after page load to update the iframe appearance. Wallet buttons do not take `appearance`.
86
86
 
87
87
  ```tsx
88
88
  <AmosCreditCardPaymentMethodForm
@@ -155,7 +155,7 @@ Radio groups (e.g. account type) always use an above-style group label regardles
155
155
  | `--control-gap` | Horizontal gap between side-by-side controls | `0.5rem` |
156
156
  | `--error-font-size` | Font size of field-level error messages | `0.875rem` |
157
157
  | `--radio-size` | Size of radio buttons on the bank account form | `1rem` |
158
- | `--ring` | Focus ring and outline color | `oklch(0.708 0 0)` |
158
+ | `--ring` | Focus ring and outline color (inputs, Connect button) | `oklch(0.708 0 0)` |
159
159
  | `--ring-width` | Focus ring width | `3px` |
160
160
  | `--radius` | Base border-radius (derived into sm/md/lg/xl) | `0.625rem` |
161
161
 
@@ -492,9 +492,27 @@ Renders the secure credit card iframe form. A field-shaped skeleton is shown imm
492
492
 
493
493
  Renders the secure bank account iframe form. A field-shaped skeleton is shown immediately and replaced by the iframe once appearance is applied.
494
494
 
495
+ When the charge meets the merchant’s ACH verification threshold, the SDK hides the routing/account iframe and renders a **Connect bank account** button in the parent page. The button follows the same outline/focus defaults as Amos UI (`--ring`, `--border`, `--radius`, `--input-height`, …): it inherits those CSS variables from the host page when present, and `appearance.themeVariables` overrides them the same way as the iframe. Clicking it asks the iframe to mint a Plaid Link token, then opens [Plaid Link](https://plaid.com/docs/link/web/). Hosts do not proxy Pay API (`GET /merchants`, `POST /plaid_link_tokens`); embed does that with `PAY_API_KEY`. Do not put Plaid secrets in the browser.
496
+
495
497
  **Required props:** same as `AmosCreditCardPaymentMethodForm` — `renderToken`, `onResult`.
496
498
 
497
- **Optional props:** same as `AmosCreditCardPaymentMethodForm` — `appearance`, `billingAddressRequirement`, `onValidityChange`.
499
+ **Optional props:** same as `AmosCreditCardPaymentMethodForm` — `appearance`, `billingAddressRequirement`, `onValidityChange` (`isValid` is also true after Plaid Link returns credentials), plus:
500
+
501
+ - `amount` (`string`, major-currency decimal, e.g. `"50.00"`) — same format as Google Pay / Apple Pay. Compared to the threshold the iframe fetches (cents). Omit to always Connect once a threshold exists (setup-intent save, or when you do not know the charge yet). Pass a new `amount` when the customer changes the charge if small charges should stay on the manual form.
502
+
503
+ Compare locally once the iframe posts `ACH_THRESHOLD`: Plaid when the amount (converted to cents) is `>= achThreshold`, or when `amount` is omitted and a threshold is set. No threshold (or `null`) keeps the manual bank form. If `amount` later drops under the threshold, Plaid credentials are dropped and the iframe form is shown again.
504
+
505
+ ```tsx
506
+ <AmosBankAccountPaymentMethodForm
507
+ renderToken={renderToken}
508
+ amount="50.00" // omit to always Connect
509
+ onResult={onResult}
510
+ />
511
+ ```
512
+
513
+ `validateForm` / `confirmPaymentIntent` / `confirmSetupIntent` stay iframe-based. When Plaid succeeded, confirm sends `payment_method.plaid` (`public_token`, `account_id`) and does not require typed account numbers.
514
+
515
+ **CSP:** the parent page must allow Plaid’s script and frames, for example `script-src https://cdn.plaid.com` and `frame-src https://cdn.plaid.com https://*.plaid.com`. Amos never loads `PLAID_SECRET` / `PLAID_CLIENT_ID` in the SDK or embed iframe.
498
516
 
499
517
  **Also accepts:** standard iframe props.
500
518
 
@@ -572,7 +590,8 @@ Re-exports of the same advanced helpers exposed by `@amos.com/amos-js`. Most int
572
590
  - **`ref` / `iframeRef`**: for card and bank forms, pass `ref={iframeRef}` to the form component. The same `iframeRef` must be used when calling `validateForm`, `confirmPaymentIntent`, `confirmSetupIntent`, or `resetForm`. The component forwards the ref to the inner iframe.
573
591
  - **`onResult` is not settlement proof**: `onResult` tells you when to stop waiting (e.g. dismiss a spinner). Verify payment or setup success on your backend via webhooks. On `status: "incomplete"`, unlock your UI — the customer can fix fields in the iframe and retry. Use `result.reason` (`"field_errors"` or `"validation_failed"`) to distinguish recoverable states.
574
592
  - **Same components for payment vs setup intents**: `AmosCreditCardPaymentMethodForm` and `AmosBankAccountPaymentMethodForm` support both payment intents and setup intents. The flow differs only by which server call you make and which confirmation function you use (`confirmPaymentIntent` vs `confirmSetupIntent`). Handle both payment and setup outcomes via `onResult`.
575
- - **Amount format**: for `AmosGooglePayButton` and `AmosApplePayButton`, `amount` is a major-currency decimal string (e.g. `"50.00"` for $50.00). For `components["schemas"]["CreatePaymentIntentInput"]` on the server (card/bank create, and the object the wallet iframe sends to `onInitiatePaymentIntentRequest`), `amount` is a number in cents (e.g. `5000`).
593
+ - **Amount format**: for `AmosGooglePayButton`, `AmosApplePayButton`, and `AmosBankAccountPaymentMethodForm`, `amount` is a major-currency decimal string (e.g. `"50.00"` for $50.00). For `components["schemas"]["CreatePaymentIntentInput"]` on the server (card/bank create, and the object the wallet iframe sends to `onInitiatePaymentIntentRequest`), `amount` is a number in cents (e.g. `5000`).
594
+ - **Plaid Link (ACH verification)**: load `cdn.plaid.com` from the **parent** document (see CSP on `AmosBankAccountPaymentMethodForm`). Merchants do not proxy Pay API; the bank iframe fetches the ACH threshold and mints link tokens. Confirm still goes through the bank iframe so Amos can attach `plaid` to the payment method.
576
595
  - **Apple Pay waiting overlay**: on browsers where Apple's QR handoff opens in a popup (non-Safari), `AmosApplePayButton` shows a fixed full-viewport overlay on the host page until payment completes, the popup closes, or the user clicks **Cancel payment**. Avoid stacking other fixed UI above it.
577
596
  - **Going framework-free**: if you need to use Amos outside of React (vanilla JS, another framework, etc.), use [`@amos.com/amos-js`](../amos-js) directly.
578
597
 
package/dist/index.d.ts CHANGED
@@ -63,14 +63,23 @@ type AmosBankAccountPaymentMethodFormProps = IframePassthroughProps & {
63
63
  onResult: (result: ConfirmationResult) => void;
64
64
  /**
65
65
  * Called when form validity changes. `isValid` is true when all
66
- * required fields are present and valid. Does not include PCI data.
66
+ * required fields are present and valid, or when Plaid Link has
67
+ * returned credentials. Does not include PCI data.
67
68
  */
68
69
  onValidityChange?: (event: {
69
70
  isValid: boolean;
70
71
  }) => void;
71
72
  billingAddressRequirement?: BillingAddressRequirement;
73
+ /**
74
+ * Charge amount as a major-currency decimal string (e.g. `"50.00"`
75
+ * for $50.00), the same format as Google Pay / Apple Pay. Compared
76
+ * to the merchant ACH threshold fetched by the iframe. Omit for
77
+ * setup intents or when the charge is unknown — if a threshold is
78
+ * set, Plaid is required.
79
+ */
80
+ amount?: string;
72
81
  };
73
- export declare function AmosBankAccountPaymentMethodForm({ ref, renderToken, appearance, onResult, onValidityChange, billingAddressRequirement, style, ...rest }: AmosBankAccountPaymentMethodFormProps): import("react").JSX.Element;
82
+ export declare function AmosBankAccountPaymentMethodForm({ ref, renderToken, appearance, onResult, onValidityChange, billingAddressRequirement, amount, style, ...rest }: AmosBankAccountPaymentMethodFormProps): import("react").JSX.Element;
74
83
  type AmosGooglePayButtonProps = {
75
84
  ref?: ForwardedIframeRef;
76
85
  renderToken: string;
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require("@amos.com/amos-js"),t=require("react"),n=require("react/jsx-runtime");function r(e){return e?e.current??null:null}function i({iframeRef:t}){return(0,e.validateForm)({iframe:r(t)})}function a({iframeRef:t,token:n}){(0,e.confirmPaymentIntent)({iframe:r(t),token:n})}function o({iframeRef:t,token:n}){(0,e.confirmSetupIntent)({iframe:r(t),token:n})}function s({iframeRef:t}){(0,e.resetForm)({iframe:r(t)})}function c(e,t){typeof e==`function`?e(t):e&&(e.current=t)}function l(e,{style:t,className:n,id:r,...i}){n!=null&&(e.className=n),r!=null&&(e.id=r),Object.assign(e.style,t);for(let[t,n]of Object.entries(i))n!=null&&(t in e?Reflect.set(e,t,n):e.setAttribute(t,String(n)))}function u({containerRef:e,iframeRef:n,mount:r,options:i,remountDeps:a,iframePassthrough:o,updateDeps:s}){let u=(0,t.useRef)(null);(0,t.useLayoutEffect)(()=>{let t=e.current;if(!t)return;let a=r(t,i);return u.current=a,c(n,a.iframe),l(a.iframe,o),()=>{a.destroy(),u.current=null,c(n,null)}},[...a]),(0,t.useEffect)(()=>{u.current?.update(i)},[...s]),(0,t.useEffect)(()=>{let e=u.current?.iframe;e&&l(e,o)})}var d=`oklch(0.97 0 0)`;function f({height:r,borderRadius:i,containerRef:a}){return(0,t.useLayoutEffect)(()=>{(0,e.ensureSkeletonStyles)()},[]),(0,n.jsxs)(`div`,{style:{boxSizing:`border-box`,position:`relative`,width:`100%`,height:r,minHeight:r,overflow:`hidden`},children:[(0,n.jsx)(`div`,{className:`amos-js-form-skeleton-input amos-js-wallet-skeleton`,style:{position:`absolute`,inset:0,height:r,borderRadius:i,background:d,pointerEvents:`none`,zIndex:0},"aria-hidden":!0}),(0,n.jsx)(`div`,{ref:a,style:{position:`absolute`,inset:0,zIndex:1,width:`100%`,height:`100%`}})]})}function p({ref:r,renderToken:i,appearance:a,onResult:o,onValidityChange:s,additionalFields:c={cardholderName:!1},billingAddressRequirement:l=`country`,style:d,...f}){let p=(0,t.useRef)(null);return u({containerRef:p,iframeRef:r,mount:e.mountAmosCreditCardPaymentMethodForm,options:{renderToken:i,appearance:a,additionalFields:c,billingAddressRequirement:l,onResult:o,onValidityChange:s},remountDeps:[i,c.cardholderName,l],iframePassthrough:{style:d,...f},updateDeps:[a,c,l,o,s]}),(0,n.jsx)(`div`,{ref:p})}function m({ref:r,renderToken:i,appearance:a,onResult:o,onValidityChange:s,billingAddressRequirement:c=`country`,style:l,...d}){let f=(0,t.useRef)(null);return u({containerRef:f,iframeRef:r,mount:e.mountAmosBankAccountPaymentMethodForm,options:{renderToken:i,appearance:a,billingAddressRequirement:c,onResult:o,onValidityChange:s},remountDeps:[i,c],iframePassthrough:{style:l,...d},updateDeps:[a,c,o,s]}),(0,n.jsx)(`div`,{ref:f})}function h({ref:r,renderToken:i,amount:a,merchantName:o,height:s=`48px`,buttonProps:c,iframeProps:l,onInitiatePaymentIntentRequest:d,onResult:p}){let m=(0,t.useRef)(null),h=(0,e.resolveWalletButtonSkeletonBorderRadius)({iframeStyle:l?.style,buttonProps:c});return u({containerRef:m,iframeRef:r,mount:e.mountAmosGooglePayButton,options:{renderToken:i,amount:a,merchantName:o,height:s,buttonProps:c,onInitiatePaymentIntentRequest:d,onResult:p},remountDeps:[i],iframePassthrough:l??{},updateDeps:[a,o,s,c,d,p]}),(0,n.jsx)(f,{height:s,borderRadius:h,containerRef:m})}function g({ref:r,renderToken:i,amount:a,merchantName:o,height:s=`48px`,buttonProps:c,iframeProps:l,onInitiatePaymentIntentRequest:d,onResult:p}){let m=(0,t.useRef)(null),h=(0,e.resolveWalletButtonSkeletonBorderRadius)({iframeStyle:l?.style,buttonProps:c});return u({containerRef:m,iframeRef:r,mount:e.mountAmosApplePayButton,options:{renderToken:i,amount:a,merchantName:o,height:s,buttonProps:c,onInitiatePaymentIntentRequest:d,onResult:p},remountDeps:[i],iframePassthrough:l??{},updateDeps:[a,o,s,c,d,p]}),(0,n.jsx)(f,{height:s,borderRadius:h,containerRef:m})}exports.AmosApplePayButton=g,exports.AmosBankAccountPaymentMethodForm=m,exports.AmosCreditCardPaymentMethodForm=p,exports.AmosGooglePayButton=h,exports.confirmPaymentIntent=a,exports.confirmSetupIntent=o,exports.resetForm=s,exports.validateForm=i,Object.keys(e).forEach(function(t){t!=="default"&&!Object.prototype.hasOwnProperty.call(exports,t)&&Object.defineProperty(exports,t,{enumerable:!0,get:function(){return e[t]}})});
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require("@amos.com/amos-js"),t=require("react"),n=require("react/jsx-runtime");function r(e){return e?e.current??null:null}function i({iframeRef:t}){return(0,e.validateForm)({iframe:r(t)})}function a({iframeRef:t,token:n}){(0,e.confirmPaymentIntent)({iframe:r(t),token:n})}function o({iframeRef:t,token:n}){(0,e.confirmSetupIntent)({iframe:r(t),token:n})}function s({iframeRef:t}){(0,e.resetForm)({iframe:r(t)})}function c(e,t){typeof e==`function`?e(t):e&&(e.current=t)}function l(e,{style:t,className:n,id:r,...i}){n!=null&&(e.className=n),r!=null&&(e.id=r),Object.assign(e.style,t);for(let[t,n]of Object.entries(i))n!=null&&(t in e?Reflect.set(e,t,n):e.setAttribute(t,String(n)))}function u({containerRef:e,iframeRef:n,mount:r,options:i,remountDeps:a,iframePassthrough:o,updateDeps:s}){let u=(0,t.useRef)(null);(0,t.useLayoutEffect)(()=>{let t=e.current;if(!t)return;let a=r(t,i);return u.current=a,c(n,a.iframe),l(a.iframe,o),()=>{a.destroy(),u.current=null,c(n,null)}},[...a]),(0,t.useEffect)(()=>{u.current?.update(i)},[...s]),(0,t.useEffect)(()=>{let e=u.current?.iframe;e&&l(e,o)})}var d=`oklch(0.97 0 0)`;function f({height:r,borderRadius:i,containerRef:a}){return(0,t.useLayoutEffect)(()=>{(0,e.ensureSkeletonStyles)()},[]),(0,n.jsxs)(`div`,{style:{boxSizing:`border-box`,position:`relative`,width:`100%`,height:r,minHeight:r,overflow:`hidden`},children:[(0,n.jsx)(`div`,{className:`amos-js-form-skeleton-input amos-js-wallet-skeleton`,style:{position:`absolute`,inset:0,height:r,borderRadius:i,background:d,pointerEvents:`none`,zIndex:0},"aria-hidden":!0}),(0,n.jsx)(`div`,{ref:a,style:{position:`absolute`,inset:0,zIndex:1,width:`100%`,height:`100%`}})]})}function p({ref:r,renderToken:i,appearance:a,onResult:o,onValidityChange:s,additionalFields:c={cardholderName:!1},billingAddressRequirement:l=`country`,style:d,...f}){let p=(0,t.useRef)(null);return u({containerRef:p,iframeRef:r,mount:e.mountAmosCreditCardPaymentMethodForm,options:{renderToken:i,appearance:a,additionalFields:c,billingAddressRequirement:l,onResult:o,onValidityChange:s},remountDeps:[i,c.cardholderName,l],iframePassthrough:{style:d,...f},updateDeps:[a,c,l,o,s]}),(0,n.jsx)(`div`,{ref:p})}function m({ref:r,renderToken:i,appearance:a,onResult:o,onValidityChange:s,billingAddressRequirement:c=`country`,amount:l,style:d,...f}){let p=(0,t.useRef)(null);return u({containerRef:p,iframeRef:r,mount:e.mountAmosBankAccountPaymentMethodForm,options:{renderToken:i,appearance:a,billingAddressRequirement:c,amount:l,onResult:o,onValidityChange:s},remountDeps:[i,c],iframePassthrough:{style:d,...f},updateDeps:[a,c,l,o,s]}),(0,n.jsx)(`div`,{ref:p})}function h({ref:r,renderToken:i,amount:a,merchantName:o,height:s=`48px`,buttonProps:c,iframeProps:l,onInitiatePaymentIntentRequest:d,onResult:p}){let m=(0,t.useRef)(null),h=(0,e.resolveWalletButtonSkeletonBorderRadius)({iframeStyle:l?.style,buttonProps:c});return u({containerRef:m,iframeRef:r,mount:e.mountAmosGooglePayButton,options:{renderToken:i,amount:a,merchantName:o,height:s,buttonProps:c,onInitiatePaymentIntentRequest:d,onResult:p},remountDeps:[i],iframePassthrough:l??{},updateDeps:[a,o,s,c,d,p]}),(0,n.jsx)(f,{height:s,borderRadius:h,containerRef:m})}function g({ref:r,renderToken:i,amount:a,merchantName:o,height:s=`48px`,buttonProps:c,iframeProps:l,onInitiatePaymentIntentRequest:d,onResult:p}){let m=(0,t.useRef)(null),h=(0,e.resolveWalletButtonSkeletonBorderRadius)({iframeStyle:l?.style,buttonProps:c});return u({containerRef:m,iframeRef:r,mount:e.mountAmosApplePayButton,options:{renderToken:i,amount:a,merchantName:o,height:s,buttonProps:c,onInitiatePaymentIntentRequest:d,onResult:p},remountDeps:[i],iframePassthrough:l??{},updateDeps:[a,o,s,c,d,p]}),(0,n.jsx)(f,{height:s,borderRadius:h,containerRef:m})}exports.AmosApplePayButton=g,exports.AmosBankAccountPaymentMethodForm=m,exports.AmosCreditCardPaymentMethodForm=p,exports.AmosGooglePayButton=h,exports.confirmPaymentIntent=a,exports.confirmSetupIntent=o,exports.resetForm=s,exports.validateForm=i,Object.keys(e).forEach(function(t){t!=="default"&&!Object.prototype.hasOwnProperty.call(exports,t)&&Object.defineProperty(exports,t,{enumerable:!0,get:function(){return e[t]}})});
package/dist/index.mjs CHANGED
@@ -116,31 +116,33 @@ function T({ ref: e, renderToken: t, appearance: n, onResult: r, onValidityChang
116
116
  ]
117
117
  }), /* @__PURE__ */ p("div", { ref: u });
118
118
  }
119
- function E({ ref: e, renderToken: t, appearance: n, onResult: r, onValidityChange: a, billingAddressRequirement: o = "country", style: s, ...c }) {
120
- let l = f(null);
119
+ function E({ ref: e, renderToken: t, appearance: n, onResult: r, onValidityChange: a, billingAddressRequirement: o = "country", amount: s, style: c, ...l }) {
120
+ let u = f(null);
121
121
  return S({
122
- containerRef: l,
122
+ containerRef: u,
123
123
  iframeRef: e,
124
124
  mount: i,
125
125
  options: {
126
126
  renderToken: t,
127
127
  appearance: n,
128
128
  billingAddressRequirement: o,
129
+ amount: s,
129
130
  onResult: r,
130
131
  onValidityChange: a
131
132
  },
132
133
  remountDeps: [t, o],
133
134
  iframePassthrough: {
134
- style: s,
135
- ...c
135
+ style: c,
136
+ ...l
136
137
  },
137
138
  updateDeps: [
138
139
  n,
139
140
  o,
141
+ s,
140
142
  r,
141
143
  a
142
144
  ]
143
- }), /* @__PURE__ */ p("div", { ref: l });
145
+ }), /* @__PURE__ */ p("div", { ref: u });
144
146
  }
145
147
  function D({ ref: e, renderToken: t, amount: n, merchantName: r, height: i = "48px", buttonProps: a, iframeProps: s, onInitiatePaymentIntentRequest: l, onResult: u }) {
146
148
  let d = f(null), m = c({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@amos.com/react-amos-js",
3
- "version": "0.9.16",
3
+ "version": "0.9.17",
4
4
  "main": "dist/index.js",
5
5
  "repository": {
6
6
  "type": "git",
@@ -48,7 +48,7 @@
48
48
  "vite-plugin-dts": "5.0.3"
49
49
  },
50
50
  "dependencies": {
51
- "@amos.com/amos-js": "0.9.17",
51
+ "@amos.com/amos-js": "0.9.18",
52
52
  "@types/googlepay": "0.7.11"
53
53
  },
54
54
  "peerDependencies": {
package/src/index.tsx CHANGED
@@ -306,10 +306,19 @@ type AmosBankAccountPaymentMethodFormProps = IframePassthroughProps & {
306
306
  onResult: (result: ConfirmationResult) => void;
307
307
  /**
308
308
  * Called when form validity changes. `isValid` is true when all
309
- * required fields are present and valid. Does not include PCI data.
309
+ * required fields are present and valid, or when Plaid Link has
310
+ * returned credentials. Does not include PCI data.
310
311
  */
311
312
  onValidityChange?: (event: { isValid: boolean }) => void;
312
313
  billingAddressRequirement?: BillingAddressRequirement;
314
+ /**
315
+ * Charge amount as a major-currency decimal string (e.g. `"50.00"`
316
+ * for $50.00), the same format as Google Pay / Apple Pay. Compared
317
+ * to the merchant ACH threshold fetched by the iframe. Omit for
318
+ * setup intents or when the charge is unknown — if a threshold is
319
+ * set, Plaid is required.
320
+ */
321
+ amount?: string;
313
322
  };
314
323
 
315
324
  export function AmosBankAccountPaymentMethodForm({
@@ -319,6 +328,7 @@ export function AmosBankAccountPaymentMethodForm({
319
328
  onResult,
320
329
  onValidityChange,
321
330
  billingAddressRequirement = "country",
331
+ amount,
322
332
  style,
323
333
  ...rest
324
334
  }: AmosBankAccountPaymentMethodFormProps) {
@@ -332,6 +342,7 @@ export function AmosBankAccountPaymentMethodForm({
332
342
  renderToken,
333
343
  appearance,
334
344
  billingAddressRequirement,
345
+ amount,
335
346
  onResult,
336
347
  onValidityChange,
337
348
  },
@@ -340,6 +351,7 @@ export function AmosBankAccountPaymentMethodForm({
340
351
  updateDeps: [
341
352
  appearance,
342
353
  billingAddressRequirement,
354
+ amount,
343
355
  onResult,
344
356
  onValidityChange,
345
357
  ],