@amos.com/amos-js 0.9.0 → 0.9.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -13
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.mjs +5 -11
- package/dist/payment-method-form.d.ts +3 -1
- package/dist/types.d.ts +33 -5
- package/package.json +5 -5
- package/src/google-pay.ts +4 -0
- package/src/index.ts +1 -0
- package/src/payment-method-form.ts +7 -1
- package/src/types.ts +34 -5
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Amos JavaScript SDK
|
|
2
2
|
|
|
3
|
-
`@amos.com/amos-js` is a framework-agnostic JavaScript SDK for embedding Amos payment methods (credit card, bank account, Google Pay) into your web app via secure iframes, and for communicating with those iframes from the host page.
|
|
3
|
+
`@amos.com/amos-js` is a framework-agnostic JavaScript SDK for embedding Amos payment methods (credit card, bank account, Google Pay, Apple Pay) into your web app via secure iframes, and for communicating with those iframes from the host page.
|
|
4
4
|
|
|
5
5
|
It is the foundation for `@amos.com/react-amos-js`, but is fully usable on its own from vanilla JavaScript / TypeScript / any other framework.
|
|
6
6
|
|
|
@@ -14,8 +14,8 @@ npm install @amos.com/amos-js
|
|
|
14
14
|
|
|
15
15
|
- **Types** for the `postMessage` protocol used between your page and the Amos iframe (`Message`, `Appearance`, `ThemeVariable`). OpenAPI schema types (for example `components["schemas"]["PaymentIntent"]`) come from `@amos.com/node`.
|
|
16
16
|
- **Iframe-targeted helpers** to validate the form, confirm a payment intent, confirm a setup intent, update appearance, etc.
|
|
17
|
-
- **Mount functions** (`mountAmosCreditCardPaymentMethodForm`, `mountAmosBankAccountPaymentMethodForm`, `mountAmosGooglePayButton`) that create the iframe, wire up its message protocol, manage its height/opacity, and return a small controller for updating options and tearing it down.
|
|
18
|
-
- **Lower-level building blocks** (`getCreditCardFormSrc`, `attachPaymentMethodFormListeners`, `attachGooglePayButtonListeners`, ...) for integrators (such as `@amos.com/react-amos-js`) that want to render the iframe element themselves.
|
|
17
|
+
- **Mount functions** (`mountAmosCreditCardPaymentMethodForm`, `mountAmosBankAccountPaymentMethodForm`, `mountAmosGooglePayButton`, `mountAmosApplePayButton`) that create the iframe, wire up its message protocol, manage its height/opacity, and return a small controller for updating options and tearing it down.
|
|
18
|
+
- **Lower-level building blocks** (`getCreditCardFormSrc`, `attachPaymentMethodFormListeners`, `attachGooglePayButtonListeners`, `attachApplePayButtonListeners`, ...) for integrators (such as `@amos.com/react-amos-js`) that want to render the iframe element themselves.
|
|
19
19
|
|
|
20
20
|
> **Note:** A server-side SDK (for example `@amos.com/node`) must be used alongside `@amos.com/amos-js` for end-to-end payment processing. `@amos.com/amos-js` is the client-side half.
|
|
21
21
|
|
|
@@ -27,7 +27,7 @@ npm install @amos.com/amos-js
|
|
|
27
27
|
3. Amos account ID (provided once your application has been approved)
|
|
28
28
|
```
|
|
29
29
|
|
|
30
|
-
The render token configures the iframe's allowed origin(s), allowed payment methods, and the range of valid payment amounts. If the render token does not allow an origin, the iframe will not render. Similarly, components corresponding to different payment method types will not render if not allowed by the render token.
|
|
30
|
+
The render token configures the iframe's allowed origin(s), allowed payment methods, accepted billing countries or US states, and the range of valid payment amounts. These settings come from the render template and are embedded in the token JWT (`RenderTokenJwt`); billing geography is controlled by `billing_address_options` (`allowed_countries` for international templates, `allowed_states` for US-only). If the render token does not allow an origin, the iframe will not render. Similarly, components corresponding to different payment method types will not render if not allowed by the render token, and billing addresses outside the configured countries or states will be rejected.
|
|
31
31
|
|
|
32
32
|
> **Note**: The render token also determines the environment (`production` or `sandbox`). Render tokens created on `dashboard.amos.com` have a `production` environment. Render tokens created on `dashboard-sandbox.amos.com` have a `sandbox` environment. Similarly, API keys can only access the environment that they were created in.
|
|
33
33
|
|
|
@@ -58,7 +58,10 @@ const form = mountAmosCreditCardPaymentMethodForm(
|
|
|
58
58
|
} else if (result.status === "failed") {
|
|
59
59
|
console.error("Confirm failed:", result.errorMessage);
|
|
60
60
|
}
|
|
61
|
-
// status === "incomplete": field errors
|
|
61
|
+
// status === "incomplete": field errors or validation_failed — unlock UI
|
|
62
|
+
if (result.status === "incomplete") {
|
|
63
|
+
console.log("Recoverable:", result.reason);
|
|
64
|
+
}
|
|
62
65
|
},
|
|
63
66
|
},
|
|
64
67
|
);
|
|
@@ -97,7 +100,7 @@ The following flow is for credit card and bank account payment method types only
|
|
|
97
100
|
6. **Confirm the payment intent from the client**: call `confirmPaymentIntent({ iframe: form.iframe, token })` in the browser to continue the payment flow.
|
|
98
101
|
7. **Handle UX**: show the user a "processing" state when the "Pay now" button is clicked, and handle `onResult`. Do not treat `onResult` as settlement proof — verify payment success on your backend via webhooks. Recoverable field errors are shown in the iframe (`status: "incomplete"`).
|
|
99
102
|
|
|
100
|
-
### Google Pay
|
|
103
|
+
### Google Pay & Apple Pay
|
|
101
104
|
|
|
102
105
|
Google Pay and Apple Pay are forms of express checkout. Their buttons are alternatives to the "Pay now" button in your payment forms. Users can make a payment with either flow.
|
|
103
106
|
|
|
@@ -145,6 +148,8 @@ const button = mountAmosGooglePayButton(
|
|
|
145
148
|
button.update({ amount: "7500" });
|
|
146
149
|
```
|
|
147
150
|
|
|
151
|
+
Apple Pay uses the same express-checkout flow and options — swap `mountAmosGooglePayButton` for `mountAmosApplePayButton`. When Apple Pay Code opens in a separate window, the SDK shows a host-page waiting overlay and tears it down when the session ends.
|
|
152
|
+
|
|
148
153
|
## Understanding the flow for creating and confirming setup intents
|
|
149
154
|
|
|
150
155
|
Setup intents are used to save payment methods for future use (e.g. recurring payments, subscriptions) without charging the customer immediately. The flow is identical to a payment intent, except:
|
|
@@ -165,7 +170,7 @@ Why this matters:
|
|
|
165
170
|
- Raw payment details are submitted from the iframe directly to Amos-controlled infrastructure.
|
|
166
171
|
- Your backend only creates payment intents (or setup intents) and returns a short-lived token used to continue the iframe flow.
|
|
167
172
|
- `confirmPaymentIntent` / `confirmSetupIntent` sends the token back to the iframe to complete confirmation; it does not pass full payment method payloads through your app server.
|
|
168
|
-
- In express flows (e.g. Google Pay), the iframe component handles payment data exchange and only asks your server to create a payment intent token.
|
|
173
|
+
- In express flows (e.g. Google Pay, Apple Pay), the iframe component handles payment data exchange and only asks your server to create a payment intent token.
|
|
169
174
|
|
|
170
175
|
In short, your app orchestrates the payment flow, while sensitive payment data stays within Amos-controlled components and APIs.
|
|
171
176
|
|
|
@@ -230,7 +235,7 @@ Mount the secure credit-card payment method form into a container element (an `H
|
|
|
230
235
|
**Required `options`:**
|
|
231
236
|
|
|
232
237
|
- `renderToken` (`string`)
|
|
233
|
-
- `onResult` (`(result: ConfirmationResult) => void`) — required. Called when the interactive confirmation attempt finishes (`succeeded`, `failed`, or `incomplete`). Not settlement proof; verify via webhooks.
|
|
238
|
+
- `onResult` (`(result: ConfirmationResult) => void`) — required. Called when the interactive confirmation attempt finishes (`succeeded`, `failed`, or `incomplete` with `reason`). Not settlement proof; verify via webhooks.
|
|
234
239
|
|
|
235
240
|
**Optional `options`:**
|
|
236
241
|
|
|
@@ -262,7 +267,7 @@ Mount the secure Google Pay button (express checkout) into a container element.
|
|
|
262
267
|
- `merchantName` (`string`)
|
|
263
268
|
- `onInitiatePaymentIntentRequest` (`({ paymentIntentCreateAttributes, customerCreateAttributes }) => Promise<components["schemas"]["EmbedToken"]["token"]>`)
|
|
264
269
|
|
|
265
|
-
- `onResult` (`(result: ConfirmationResult) => void`) — required. Called when the interactive confirmation attempt finishes (`succeeded`, `failed`, or `incomplete`). Not settlement proof; verify via webhooks.
|
|
270
|
+
- `onResult` (`(result: ConfirmationResult) => void`) — required. Called when the interactive confirmation attempt finishes (`succeeded`, `failed`, or `incomplete` with `reason`). Not settlement proof; verify via webhooks.
|
|
266
271
|
|
|
267
272
|
**Optional `options`:** `appearance`, `onHeightChange`, `onAppearanceReady`.
|
|
268
273
|
|
|
@@ -288,11 +293,11 @@ Lower-level helper that wires up the host-page side of the credit-card or bank-a
|
|
|
288
293
|
|
|
289
294
|
This is what `@amos.com/react-amos-js` uses to integrate with React's rendering model.
|
|
290
295
|
|
|
291
|
-
### `attachGooglePayButtonListeners(iframe, options)`
|
|
296
|
+
### `attachGooglePayButtonListeners(iframe, options)` / `attachApplePayButtonListeners(iframe, options)`
|
|
292
297
|
|
|
293
|
-
The Google Pay
|
|
298
|
+
The Google Pay / Apple Pay equivalents of `attachPaymentMethodFormListeners`.
|
|
294
299
|
|
|
295
|
-
### `getCreditCardFormSrc(renderToken, additionalFields?, billingAddressRequirement?)` / `getBankAccountFormSrc(renderToken, billingAddressRequirement?)` / `getGooglePayButtonSrc(renderToken)`
|
|
300
|
+
### `getCreditCardFormSrc(renderToken, additionalFields?, billingAddressRequirement?)` / `getBankAccountFormSrc(renderToken, billingAddressRequirement?)` / `getGooglePayButtonSrc(renderToken)` / `getApplePayButtonSrc(renderToken)`
|
|
296
301
|
|
|
297
302
|
Build the iframe `src` URL for each form type.
|
|
298
303
|
|
|
@@ -312,7 +317,7 @@ Advanced helpers exposed for integrators that need to construct or inspect the m
|
|
|
312
317
|
|
|
313
318
|
- **`iframe` argument**: every messaging helper (`validateForm`, `confirmPaymentIntent`, `confirmSetupIntent`) accepts the `iframe` element directly. With the mount helpers, use `controller.iframe`.
|
|
314
319
|
- **Same components for payment vs setup intents**: `mountAmosCreditCardPaymentMethodForm` and `mountAmosBankAccountPaymentMethodForm` support both payment intents and setup intents. The flow differs only by which server call you make and which confirmation function you use. Handle both outcomes via `onResult`.
|
|
315
|
-
- **Amount format**: for `mountAmosGooglePayButton`, `amount` is a string (e.g. `"5000"` for $50.00). For `components["schemas"]["CreatePaymentIntentInput"]` on the server, `amount` is a number in cents (e.g. `5000`).
|
|
320
|
+
- **Amount format**: for `mountAmosGooglePayButton` and `mountAmosApplePayButton`, `amount` is a string (e.g. `"5000"` for $50.00). For `components["schemas"]["CreatePaymentIntentInput"]` on the server, `amount` is a number in cents (e.g. `5000`).
|
|
316
321
|
- **Browser-only**: the mount and messaging helpers require `window` and the DOM. They are not safe to call during server-side rendering — call them from client-side code only (for example, inside a `useEffect`-like hook in your framework of choice).
|
|
317
322
|
|
|
318
323
|
---
|
package/dist/index.d.ts
CHANGED
|
@@ -9,5 +9,5 @@ export type { AmosApplePayButtonMountController, AmosApplePayButtonOptions, Amos
|
|
|
9
9
|
export { mountAmosApplePayButton, mountAmosBankAccountPaymentMethodForm, mountAmosCreditCardPaymentMethodForm, mountAmosGooglePayButton, } from './mount';
|
|
10
10
|
export type { BillingAddressRequirement, CreditCardAdditionalFields, PaymentMethodFormController, PaymentMethodFormListenerOptions, } from './payment-method-form';
|
|
11
11
|
export { attachPaymentMethodFormListeners, getBankAccountFormInitialHeight, getBankAccountFormSrc, getCreditCardFormInitialHeight, getCreditCardFormSrc, } from './payment-method-form';
|
|
12
|
-
export type { Appearance, AppearanceLabels, ConfirmationResult, Message, ThemeVariable, } from './types';
|
|
12
|
+
export type { Appearance, AppearanceLabels, ConfirmationIncompleteReason, ConfirmationResult, Message, ThemeVariable, } from './types';
|
|
13
13
|
export { createMessage } from './types';
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=`data-amos-apple-pay-waiting`;function t({onCancel:t}){let n=document.querySelector(`[${e}]`);if(n)return n;let r=document.createElement(`div`);r.setAttribute(e,`true`),r.setAttribute(`role`,`dialog`),r.setAttribute(`aria-modal`,`true`),r.setAttribute(`aria-labelledby`,`amos-apple-pay-waiting-title`),Object.assign(r.style,{position:`fixed`,inset:`0`,zIndex:`2147483646`,display:`flex`,alignItems:`center`,justifyContent:`center`,padding:`24px`,boxSizing:`border-box`,background:`rgba(0, 0, 0, 0.55)`,fontFamily:`-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif`});let i=document.createElement(`div`);Object.assign(i.style,{width:`100%`,maxWidth:`360px`,borderRadius:`12px`,background:`#fff`,padding:`28px 24px 20px`,boxSizing:`border-box`,textAlign:`center`,boxShadow:`0 12px 40px rgba(0, 0, 0, 0.25)`});let a=document.createElement(`div`);a.setAttribute(`aria-hidden`,`true`),Object.assign(a.style,{display:`inline-flex`,alignItems:`center`,justifyContent:`center`,gap:`6px`,marginBottom:`16px`,fontSize:`28px`,fontWeight:`600`,letterSpacing:`-0.02em`,color:`#000`,lineHeight:`1`}),a.innerHTML=`<svg width="22" height="26" viewBox="0 0 14 17" fill="currentColor" xmlns="http://www.w3.org/2000/svg"><path d="M13.072 5.846c-.1.08-1.873 1.09-1.873 3.34 0 2.61 2.29 3.53 2.36 3.55-.04.1-.368 1.27-.766 2.52-.35 1.09-.72 2.18-1.28 2.18-.55 0-.73-.36-1.43-.36-.71 0-.93.37-1.48.37-.55 0-.93-.99-1.35-2.01-.5-1.2-.88-2.43-.88-3.85 0-2.26 1.47-3.46 2.91-3.46.57 0 1.11.38 1.49.38.37 0 .98-.45 1.7-.45.28 0 1.27.03 1.97 1.01ZM9.52 3.37c.3-.36.52-.86.52-1.36 0-.07 0-.14-.01-.2-.5.02-1.1.33-1.46.75-.28.32-.55.84-.55 1.35 0 .07.01.15.02.17.04.01.1.02.16.02.45 0 1.02-.3 1.32-.73Z"/></svg><span>Pay</span>`;let o=document.createElement(`p`);o.id=`amos-apple-pay-waiting-title`,Object.assign(o.style,{margin:`0 0 20px`,fontSize:`15px`,lineHeight:`1.45`,color:`#1a1a1a`}),o.textContent=`Complete your payment in the open Apple Pay window, or close Apple Pay to continue paying another way.`;let s=document.createElement(`button`);return s.type=`button`,s.textContent=`Cancel payment`,Object.assign(s.style,{display:`block`,width:`100%`,border:`none`,borderRadius:`8px`,padding:`12px 16px`,background:`#2c2c2e`,color:`#fff`,fontSize:`15px`,fontWeight:`500`,cursor:`pointer`}),s.addEventListener(`click`,t),i.append(a,o,s),r.append(i),document.body.append(r),r}function n(){document.querySelector(`[${e}]`)?.remove()}function r(e){let[t=``,n=``,r=``]=e?.split(`.`)??[],i=typeof atob==`function`?atob:e=>Buffer.from(e,`base64`).toString(`utf8`);return{header:JSON.parse(i(t)),payload:JSON.parse(i(n)),signature:r}}function i(e){let{env:t=`sandbox`}=r(e).payload;switch(t){case`production`:return`https://embed.amos.com`;case`sandbox`:return`https://embed-sandbox.amos.com`;default:return`https://embed-sandbox.amos.com`}}function a(e){return e}function o(e){e?.contentWindow?.postMessage(a({type:`PARENT_ACKNOWLEDGED_IFRAME_READY`}),`*`)}function s({iframe:e,appearance:t={}}){e?.contentWindow?.postMessage(a({type:`UPDATE_APPEARANCE`,appearance:t}),`*`)}function c({iframe:e,amount:t}){e?.contentWindow?.postMessage(a({type:`UPDATE_AMOUNT`,amount:t}),`*`)}function l({iframe:e,merchantName:t}){e?.contentWindow?.postMessage(a({type:`UPDATE_MERCHANT_NAME`,merchantName:t}),`*`)}function u({iframe:e}){let t=crypto.randomUUID();return new Promise(n=>{e?.contentWindow?.postMessage(a({type:`VALIDATE_FORM`,requestId:t}),`*`);let r=setTimeout(()=>{window.removeEventListener(`message`,i),n(!1)},5e3);function i(e){e.data.type===`VALIDATE_FORM`&&e.data.requestId===t&&(window.removeEventListener(`message`,i),clearTimeout(r),n(e.data.isValid??!1))}window.addEventListener(`message`,i)})}function d({iframe:e,token:t}){let{payment_intent_id:n}=r(t).payload;e?.contentWindow?.postMessage(a({type:`CONFIRM_PAYMENT_INTENT`,token:t,id:n??void 0}),`*`)}function f({iframe:e,token:t}){let{setup_intent_id:n}=r(t).payload;e?.contentWindow?.postMessage(a({type:`CONFIRM_SETUP_INTENT`,token:t,id:n??void 0}),`*`)}function p({iframe:e,result:t}){e?.contentWindow?.postMessage(a({type:`CONFIRMATION_RESULT`,result:t}),`*`)}function m(e){return`${i(e)}/iframe/apple-pay?token=${e}`}function h(){return`40px`}function g(e){e.contentWindow?.postMessage(a({type:`APPLE_PAY_CANCEL`}),`*`)}function _(e,r){let i={...r};function a(){c({iframe:e,amount:i.amount})}function u(){l({iframe:e,merchantName:i.merchantName})}function f(r){if(r.source===e.contentWindow)switch(r.data.type){case`IFRAME_READY`:o(e),s({iframe:e,appearance:i.appearance}),a(),u();break;case`UPDATE_HEIGHT`:i.onHeightChange?.(r.data.height);break;case`UPDATE_APPEARANCE`:s({iframe:e,appearance:r.data.appearance});break;case`UPDATED_APPEARANCE`:i.onAppearanceReady?.();break;case`APPLE_PAY_WINDOW_OPEN`:t({onCancel:()=>{g(e),n()}});break;case`APPLE_PAY_WINDOW_CLOSE`:n();break;case`CREATE_PAYMENT_INTENT`:i.onInitiatePaymentIntentRequest({paymentIntentCreateAttributes:r.data.paymentIntentCreateAttributes,customerCreateAttributes:r.data.customerCreateAttributes}).then(t=>{d({iframe:e,token:t})}).catch(e=>{n(),i.onResult({status:`failed`,errorMessage:e instanceof Error?e.message:`Unknown error`})});break;case`CONFIRMATION_RESULT`:n(),i.onResult(r.data.result);break}}return window.addEventListener(`message`,f),{update(t){let n=`appearance`in t,r=`amount`in t,o=`merchantName`in t;i={...i,...t},n&&s({iframe:e,appearance:i.appearance}),r&&a(),o&&u()},destroy(){window.removeEventListener(`message`,f),n()}}}function v(e){return`${i(e)}/iframe/google-pay?token=${e}`}function y(){return`40px`}function b(e,t){let n={...t};function r(){c({iframe:e,amount:n.amount})}function i(){l({iframe:e,merchantName:n.merchantName})}function a(t){switch(t.data.type){case`IFRAME_READY`:o(e),s({iframe:e,appearance:n.appearance}),r(),i();break;case`UPDATE_HEIGHT`:n.onHeightChange?.(t.data.height);break;case`UPDATE_APPEARANCE`:s({iframe:e,appearance:t.data.appearance});break;case`UPDATED_APPEARANCE`:n.onAppearanceReady?.();break;case`CREATE_PAYMENT_INTENT`:n.onInitiatePaymentIntentRequest({paymentIntentCreateAttributes:t.data.paymentIntentCreateAttributes,customerCreateAttributes:t.data.customerCreateAttributes}).then(t=>{d({iframe:e,token:t})}).catch(e=>{n.onResult({status:`failed`,errorMessage:e instanceof Error?e.message:`Unknown error`})});break;case`CONFIRMATION_RESULT`:n.onResult(t.data.result);break}}return window.addEventListener(`message`,a),{update(t){let a=`appearance`in t,o=`amount`in t,c=`merchantName`in t;n={...n,...t},a&&s({iframe:e,appearance:n.appearance}),o&&r(),c&&i()},destroy(){window.removeEventListener(`message`,a)}}}function x({paymentData:e}){return{paymentMethod:{type:`googlepay`,billing_address_attributes:{name:e.shippingAddress?.name,address_line1:e.shippingAddress?.address1,address_line2:e.shippingAddress?.address2,city:e.shippingAddress?.locality,state:e.shippingAddress?.administrativeArea,postal_code:e.shippingAddress?.postalCode,country:e.shippingAddress?.countryCode,email:e.email,phone:e.shippingAddress?.phoneNumber},card_profile_attributes:{wallet_provider:`googlepay`,wallet_payload:e.paymentMethodData.tokenizationData.token,wallet_last4:e.paymentMethodData.info?.cardDetails,wallet_brand:(()=>{switch(e.paymentMethodData.info?.cardNetwork){case`AMEX`:return`american_express`;case`VISA`:return`visa`;case`MASTERCARD`:return`master`;case`DISCOVER`:return`discover`;default:return}})()}}}}var S={country:212,full:452},C=80,w={country:400,full:640};function T(e,t={cardholderName:!1},n=`country`){let r=Object.entries(t).filter(([,e])=>e).map(([e])=>e).join(`,`),a=new URLSearchParams({token:e,additionalFields:r,billingAddressRequirement:n});return`${i(e)}/iframe/card?${a}`}function E(e,t=`country`){let n=new URLSearchParams({token:e,billingAddressRequirement:t});return`${i(e)}/iframe/bank?${n}`}function D(e={cardholderName:!1},t=`country`){return`${(S[t]??S.country)+(e.cardholderName?C:0)}px`}function O(e=`country`){return`${w[e]??w.country}px`}function k(e,t){let n={...t};function r(t){switch(t.data.type){case`IFRAME_READY`:o(e),s({iframe:e,appearance:n.appearance});break;case`UPDATE_HEIGHT`:n.onHeightChange?.(t.data.height);break;case`UPDATE_APPEARANCE`:s({iframe:e,appearance:t.data.appearance});break;case`UPDATED_APPEARANCE`:n.onAppearanceReady?.();break;case`CONFIRMATION_RESULT`:n.onResult(t.data.result);break}}return window.addEventListener(`message`,r),{update(t){let r=`appearance`in t;n={...n,...t},r&&s({iframe:e,appearance:n.appearance})},destroy(){window.removeEventListener(`message`,r)}}}function A(e){if(typeof e==`string`){let t=document.querySelector(e);if(!(t instanceof HTMLElement))throw Error(`[amos-js] Container "${e}" did not match any HTMLElement.`);return t}return e}var j={width:`calc(100% + 8px)`,transition:`opacity 150ms ease-in, height 200ms ease-in-out`,margin:`0 -4px`,opacity:`0`,border:`0`};function M({src:e,title:t,name:n,height:r,allow:i}){let a=document.createElement(`iframe`);return a.src=e,a.title=t,a.name=n,a.setAttribute(`role`,`presentation`),a.scrolling=`no`,i&&(a.allow=i),Object.assign(a.style,j,{height:r}),a}function N(e,t){let n=A(e),{renderToken:r,additionalFields:i={cardholderName:!1},billingAddressRequirement:a=`country`,...o}=t,s=M({src:T(r,i,a),title:`Secure credit card payment method form powered by Amos`,name:`amos-credit-card-payment-method-form`,height:D(i,a)});n.appendChild(s);let c=k(s,{...o,onHeightChange:e=>{s.style.height=e,o.onHeightChange?.(e)},onAppearanceReady:()=>{s.style.opacity=`1`,o.onAppearanceReady?.()}});return{iframe:s,update:c.update,destroy(){c.destroy(),s.remove()}}}function P(e,t){let n=A(e),{renderToken:r,billingAddressRequirement:i=`country`,...a}=t,o=M({src:E(r,i),title:`Secure bank account payment method form powered by Amos`,name:`amos-bank-account-payment-method-form`,height:O(i)});n.appendChild(o);let s=k(o,{...a,onHeightChange:e=>{o.style.height=e,a.onHeightChange?.(e)},onAppearanceReady:()=>{o.style.opacity=`1`,a.onAppearanceReady?.()}});return{iframe:o,update:s.update,destroy(){s.destroy(),o.remove()}}}function F(e,t){let n=A(e),{renderToken:r,...i}=t,a=M({src:v(r),title:`Secure Google Pay button powered by Amos`,name:`amos-google-pay-button`,height:y(),allow:`payment`});n.appendChild(a);let o=b(a,{...i,onHeightChange:e=>{a.style.height=e,i.onHeightChange?.(e)},onAppearanceReady:()=>{a.style.opacity=`1`,i.onAppearanceReady?.()}});return{iframe:a,update:o.update,destroy(){o.destroy(),a.remove()}}}function I(e,t){let n=A(e),{renderToken:r,...i}=t,a=M({src:m(r),title:`Secure Apple Pay button powered by Amos`,name:`amos-apple-pay-button`,height:h(),allow:`payment`});n.appendChild(a);let o=_(a,{...i,onHeightChange:e=>{a.style.height=e,i.onHeightChange?.(e)},onAppearanceReady:()=>{a.style.opacity=`1`,i.onAppearanceReady?.()}});return{iframe:a,update:o.update,destroy(){o.destroy(),a.remove()}}}exports.attachApplePayButtonListeners=_,exports.attachGooglePayButtonListeners=b,exports.attachPaymentMethodFormListeners=k,exports.confirmPaymentIntent=d,exports.confirmSetupIntent=f,exports.createMessage=a,exports.decodeJwt=r,exports.formatGooglePayPaymentData=x,exports.getApplePayButtonInitialHeight=h,exports.getApplePayButtonSrc=m,exports.getBankAccountFormInitialHeight=O,exports.getBankAccountFormSrc=E,exports.getCreditCardFormInitialHeight=D,exports.getCreditCardFormSrc=T,exports.getEmbedOrigin=i,exports.getGooglePayButtonInitialHeight=y,exports.getGooglePayButtonSrc=v,exports.mountAmosApplePayButton=I,exports.mountAmosBankAccountPaymentMethodForm=P,exports.mountAmosCreditCardPaymentMethodForm=N,exports.mountAmosGooglePayButton=F,exports.sendConfirmationResult=p,exports.sendParentReadyMessage=o,exports.updateAmount=c,exports.updateAppearance=s,exports.updateMerchantName=l,exports.validateForm=u;
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=`data-amos-apple-pay-waiting`;function t({onCancel:t}){let n=document.querySelector(`[${e}]`);if(n)return n;let r=document.createElement(`div`);r.setAttribute(e,`true`),r.setAttribute(`role`,`dialog`),r.setAttribute(`aria-modal`,`true`),r.setAttribute(`aria-labelledby`,`amos-apple-pay-waiting-title`),Object.assign(r.style,{position:`fixed`,inset:`0`,zIndex:`2147483646`,display:`flex`,alignItems:`center`,justifyContent:`center`,padding:`24px`,boxSizing:`border-box`,background:`rgba(0, 0, 0, 0.55)`,fontFamily:`-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif`});let i=document.createElement(`div`);Object.assign(i.style,{width:`100%`,maxWidth:`360px`,borderRadius:`12px`,background:`#fff`,padding:`28px 24px 20px`,boxSizing:`border-box`,textAlign:`center`,boxShadow:`0 12px 40px rgba(0, 0, 0, 0.25)`});let a=document.createElement(`div`);a.setAttribute(`aria-hidden`,`true`),Object.assign(a.style,{display:`inline-flex`,alignItems:`center`,justifyContent:`center`,gap:`6px`,marginBottom:`16px`,fontSize:`28px`,fontWeight:`600`,letterSpacing:`-0.02em`,color:`#000`,lineHeight:`1`}),a.innerHTML=`<svg width="22" height="26" viewBox="0 0 14 17" fill="currentColor" xmlns="http://www.w3.org/2000/svg"><path d="M13.072 5.846c-.1.08-1.873 1.09-1.873 3.34 0 2.61 2.29 3.53 2.36 3.55-.04.1-.368 1.27-.766 2.52-.35 1.09-.72 2.18-1.28 2.18-.55 0-.73-.36-1.43-.36-.71 0-.93.37-1.48.37-.55 0-.93-.99-1.35-2.01-.5-1.2-.88-2.43-.88-3.85 0-2.26 1.47-3.46 2.91-3.46.57 0 1.11.38 1.49.38.37 0 .98-.45 1.7-.45.28 0 1.27.03 1.97 1.01ZM9.52 3.37c.3-.36.52-.86.52-1.36 0-.07 0-.14-.01-.2-.5.02-1.1.33-1.46.75-.28.32-.55.84-.55 1.35 0 .07.01.15.02.17.04.01.1.02.16.02.45 0 1.02-.3 1.32-.73Z"/></svg><span>Pay</span>`;let o=document.createElement(`p`);o.id=`amos-apple-pay-waiting-title`,Object.assign(o.style,{margin:`0 0 20px`,fontSize:`15px`,lineHeight:`1.45`,color:`#1a1a1a`}),o.textContent=`Complete your payment in the open Apple Pay window, or close Apple Pay to continue paying another way.`;let s=document.createElement(`button`);return s.type=`button`,s.textContent=`Cancel payment`,Object.assign(s.style,{display:`block`,width:`100%`,border:`none`,borderRadius:`8px`,padding:`12px 16px`,background:`#2c2c2e`,color:`#fff`,fontSize:`15px`,fontWeight:`500`,cursor:`pointer`}),s.addEventListener(`click`,t),i.append(a,o,s),r.append(i),document.body.append(r),r}function n(){document.querySelector(`[${e}]`)?.remove()}function r(e){let[t=``,n=``,r=``]=e?.split(`.`)??[],i=typeof atob==`function`?atob:e=>Buffer.from(e,`base64`).toString(`utf8`);return{header:JSON.parse(i(t)),payload:JSON.parse(i(n)),signature:r}}function i(e){let{env:t=`sandbox`}=r(e).payload;switch(t){case`production`:return`https://embed.amos.com`;case`sandbox`:return`https://embed-sandbox.amos.com`;default:return`https://embed-sandbox.amos.com`}}function a(e){return e}function o(e){e?.contentWindow?.postMessage(a({type:`PARENT_ACKNOWLEDGED_IFRAME_READY`}),`*`)}function s({iframe:e,appearance:t={}}){e?.contentWindow?.postMessage(a({type:`UPDATE_APPEARANCE`,appearance:t}),`*`)}function c({iframe:e,amount:t}){e?.contentWindow?.postMessage(a({type:`UPDATE_AMOUNT`,amount:t}),`*`)}function l({iframe:e,merchantName:t}){e?.contentWindow?.postMessage(a({type:`UPDATE_MERCHANT_NAME`,merchantName:t}),`*`)}function u({iframe:e}){let t=crypto.randomUUID();return new Promise(n=>{e?.contentWindow?.postMessage(a({type:`VALIDATE_FORM`,requestId:t}),`*`);let r=setTimeout(()=>{window.removeEventListener(`message`,i),n(!1)},5e3);function i(e){e.data.type===`VALIDATE_FORM`&&e.data.requestId===t&&(window.removeEventListener(`message`,i),clearTimeout(r),n(e.data.isValid??!1))}window.addEventListener(`message`,i)})}function d({iframe:e,token:t}){let{payment_intent_id:n}=r(t).payload;e?.contentWindow?.postMessage(a({type:`CONFIRM_PAYMENT_INTENT`,token:t,id:n??void 0}),`*`)}function f({iframe:e,token:t}){let{setup_intent_id:n}=r(t).payload;e?.contentWindow?.postMessage(a({type:`CONFIRM_SETUP_INTENT`,token:t,id:n??void 0}),`*`)}function p({iframe:e,result:t}){e?.contentWindow?.postMessage(a({type:`CONFIRMATION_RESULT`,result:t}),`*`)}function m(e){return`${i(e)}/iframe/apple-pay?token=${e}`}function h(){return`40px`}function g(e){e.contentWindow?.postMessage(a({type:`APPLE_PAY_CANCEL`}),`*`)}function _(e,r){let i={...r};function a(){c({iframe:e,amount:i.amount})}function u(){l({iframe:e,merchantName:i.merchantName})}function f(r){if(r.source===e.contentWindow)switch(r.data.type){case`IFRAME_READY`:o(e),s({iframe:e,appearance:i.appearance}),a(),u();break;case`UPDATE_HEIGHT`:i.onHeightChange?.(r.data.height);break;case`UPDATE_APPEARANCE`:s({iframe:e,appearance:r.data.appearance});break;case`UPDATED_APPEARANCE`:i.onAppearanceReady?.();break;case`APPLE_PAY_WINDOW_OPEN`:t({onCancel:()=>{g(e),n()}});break;case`APPLE_PAY_WINDOW_CLOSE`:n();break;case`CREATE_PAYMENT_INTENT`:i.onInitiatePaymentIntentRequest({paymentIntentCreateAttributes:r.data.paymentIntentCreateAttributes,customerCreateAttributes:r.data.customerCreateAttributes}).then(t=>{d({iframe:e,token:t})}).catch(e=>{n(),i.onResult({status:`failed`,errorMessage:e instanceof Error?e.message:`Unknown error`})});break;case`CONFIRMATION_RESULT`:n(),i.onResult(r.data.result)}}return window.addEventListener(`message`,f),{update(t){let n=`appearance`in t,r=`amount`in t,o=`merchantName`in t;i={...i,...t},n&&s({iframe:e,appearance:i.appearance}),r&&a(),o&&u()},destroy(){window.removeEventListener(`message`,f),n()}}}function v(e){return`${i(e)}/iframe/google-pay?token=${e}`}function y(){return`40px`}function b(e,t){let n={...t};function r(){c({iframe:e,amount:n.amount})}function i(){l({iframe:e,merchantName:n.merchantName})}function a(t){if(t.source===e.contentWindow)switch(t.data.type){case`IFRAME_READY`:o(e),s({iframe:e,appearance:n.appearance}),r(),i();break;case`UPDATE_HEIGHT`:n.onHeightChange?.(t.data.height);break;case`UPDATE_APPEARANCE`:s({iframe:e,appearance:t.data.appearance});break;case`UPDATED_APPEARANCE`:n.onAppearanceReady?.();break;case`CREATE_PAYMENT_INTENT`:n.onInitiatePaymentIntentRequest({paymentIntentCreateAttributes:t.data.paymentIntentCreateAttributes,customerCreateAttributes:t.data.customerCreateAttributes}).then(t=>{d({iframe:e,token:t})}).catch(e=>{n.onResult({status:`failed`,errorMessage:e instanceof Error?e.message:`Unknown error`})});break;case`CONFIRMATION_RESULT`:n.onResult(t.data.result)}}return window.addEventListener(`message`,a),{update(t){let a=`appearance`in t,o=`amount`in t,c=`merchantName`in t;n={...n,...t},a&&s({iframe:e,appearance:n.appearance}),o&&r(),c&&i()},destroy(){window.removeEventListener(`message`,a)}}}function x({paymentData:e}){return{paymentMethod:{type:`googlepay`,billing_address_attributes:{name:e.shippingAddress?.name,address_line1:e.shippingAddress?.address1,address_line2:e.shippingAddress?.address2,city:e.shippingAddress?.locality,state:e.shippingAddress?.administrativeArea,postal_code:e.shippingAddress?.postalCode,country:e.shippingAddress?.countryCode,email:e.email,phone:e.shippingAddress?.phoneNumber},card_profile_attributes:{wallet_provider:`googlepay`,wallet_payload:e.paymentMethodData.tokenizationData.token,wallet_last4:e.paymentMethodData.info?.cardDetails,wallet_brand:(()=>{switch(e.paymentMethodData.info?.cardNetwork){case`AMEX`:return`american_express`;case`VISA`:return`visa`;case`MASTERCARD`:return`master`;case`DISCOVER`:return`discover`;default:return}})()}}}}var S={country:212,full:452},C=80,w={country:400,full:640};function T(e,t={cardholderName:!1},n=`country`){let r=Object.entries(t).filter(([,e])=>e).map(([e])=>e).join(`,`),a=new URLSearchParams({token:e,additionalFields:r,billingAddressRequirement:n});return`${i(e)}/iframe/card?${a}`}function E(e,t=`country`){let n=new URLSearchParams({token:e,billingAddressRequirement:t});return`${i(e)}/iframe/bank?${n}`}function D(e={cardholderName:!1},t=`country`){return`${(S[t]??S.country)+(e.cardholderName?C:0)}px`}function O(e=`country`){return`${w[e]??w.country}px`}function k(e,t){let n={...t};function r(t){if(t.source===e.contentWindow)switch(t.data.type){case`IFRAME_READY`:o(e),s({iframe:e,appearance:n.appearance});break;case`UPDATE_HEIGHT`:n.onHeightChange?.(t.data.height);break;case`UPDATE_APPEARANCE`:s({iframe:e,appearance:t.data.appearance});break;case`UPDATED_APPEARANCE`:n.onAppearanceReady?.();break;case`CONFIRMATION_RESULT`:n.onResult(t.data.result)}}return window.addEventListener(`message`,r),{update(t){let r=`appearance`in t;n={...n,...t},r&&s({iframe:e,appearance:n.appearance})},destroy(){window.removeEventListener(`message`,r)}}}function A(e){if(typeof e==`string`){let t=document.querySelector(e);if(!(t instanceof HTMLElement))throw Error(`[amos-js] Container "${e}" did not match any HTMLElement.`);return t}return e}var j={width:`calc(100% + 8px)`,transition:`opacity 150ms ease-in, height 200ms ease-in-out`,margin:`0 -4px`,opacity:`0`,border:`0`};function M({src:e,title:t,name:n,height:r,allow:i}){let a=document.createElement(`iframe`);return a.src=e,a.title=t,a.name=n,a.setAttribute(`role`,`presentation`),a.scrolling=`no`,i&&(a.allow=i),Object.assign(a.style,j,{height:r}),a}function N(e,t){let n=A(e),{renderToken:r,additionalFields:i={cardholderName:!1},billingAddressRequirement:a=`country`,...o}=t,s=M({src:T(r,i,a),title:`Secure credit card payment method form powered by Amos`,name:`amos-credit-card-payment-method-form`,height:D(i,a)});n.appendChild(s);let c=k(s,{...o,onHeightChange:e=>{s.style.height=e,o.onHeightChange?.(e)},onAppearanceReady:()=>{s.style.opacity=`1`,o.onAppearanceReady?.()}});return{iframe:s,update:c.update,destroy(){c.destroy(),s.remove()}}}function P(e,t){let n=A(e),{renderToken:r,billingAddressRequirement:i=`country`,...a}=t,o=M({src:E(r,i),title:`Secure bank account payment method form powered by Amos`,name:`amos-bank-account-payment-method-form`,height:O(i)});n.appendChild(o);let s=k(o,{...a,onHeightChange:e=>{o.style.height=e,a.onHeightChange?.(e)},onAppearanceReady:()=>{o.style.opacity=`1`,a.onAppearanceReady?.()}});return{iframe:o,update:s.update,destroy(){s.destroy(),o.remove()}}}function F(e,t){let n=A(e),{renderToken:r,...i}=t,a=M({src:v(r),title:`Secure Google Pay button powered by Amos`,name:`amos-google-pay-button`,height:y(),allow:`payment`});n.appendChild(a);let o=b(a,{...i,onHeightChange:e=>{a.style.height=e,i.onHeightChange?.(e)},onAppearanceReady:()=>{a.style.opacity=`1`,i.onAppearanceReady?.()}});return{iframe:a,update:o.update,destroy(){o.destroy(),a.remove()}}}function I(e,t){let n=A(e),{renderToken:r,...i}=t,a=M({src:m(r),title:`Secure Apple Pay button powered by Amos`,name:`amos-apple-pay-button`,height:h(),allow:`payment`});n.appendChild(a);let o=_(a,{...i,onHeightChange:e=>{a.style.height=e,i.onHeightChange?.(e)},onAppearanceReady:()=>{a.style.opacity=`1`,i.onAppearanceReady?.()}});return{iframe:a,update:o.update,destroy(){o.destroy(),a.remove()}}}exports.attachApplePayButtonListeners=_,exports.attachGooglePayButtonListeners=b,exports.attachPaymentMethodFormListeners=k,exports.confirmPaymentIntent=d,exports.confirmSetupIntent=f,exports.createMessage=a,exports.decodeJwt=r,exports.formatGooglePayPaymentData=x,exports.getApplePayButtonInitialHeight=h,exports.getApplePayButtonSrc=m,exports.getBankAccountFormInitialHeight=O,exports.getBankAccountFormSrc=E,exports.getCreditCardFormInitialHeight=D,exports.getCreditCardFormSrc=T,exports.getEmbedOrigin=i,exports.getGooglePayButtonInitialHeight=y,exports.getGooglePayButtonSrc=v,exports.mountAmosApplePayButton=I,exports.mountAmosBankAccountPaymentMethodForm=P,exports.mountAmosCreditCardPaymentMethodForm=N,exports.mountAmosGooglePayButton=F,exports.sendConfirmationResult=p,exports.sendParentReadyMessage=o,exports.updateAmount=c,exports.updateAppearance=s,exports.updateMerchantName=l,exports.validateForm=u;
|
package/dist/index.mjs
CHANGED
|
@@ -217,9 +217,7 @@ function _(e, r) {
|
|
|
217
217
|
});
|
|
218
218
|
});
|
|
219
219
|
break;
|
|
220
|
-
case "CONFIRMATION_RESULT":
|
|
221
|
-
n(), i.onResult(r.data.result);
|
|
222
|
-
break;
|
|
220
|
+
case "CONFIRMATION_RESULT": n(), i.onResult(r.data.result);
|
|
223
221
|
}
|
|
224
222
|
}
|
|
225
223
|
return window.addEventListener("message", f), {
|
|
@@ -261,7 +259,7 @@ function b(e, t) {
|
|
|
261
259
|
});
|
|
262
260
|
}
|
|
263
261
|
function a(t) {
|
|
264
|
-
switch (t.data.type) {
|
|
262
|
+
if (t.source === e.contentWindow) switch (t.data.type) {
|
|
265
263
|
case "IFRAME_READY":
|
|
266
264
|
o(e), s({
|
|
267
265
|
iframe: e,
|
|
@@ -296,9 +294,7 @@ function b(e, t) {
|
|
|
296
294
|
});
|
|
297
295
|
});
|
|
298
296
|
break;
|
|
299
|
-
case "CONFIRMATION_RESULT":
|
|
300
|
-
n.onResult(t.data.result);
|
|
301
|
-
break;
|
|
297
|
+
case "CONFIRMATION_RESULT": n.onResult(t.data.result);
|
|
302
298
|
}
|
|
303
299
|
}
|
|
304
300
|
return window.addEventListener("message", a), {
|
|
@@ -380,7 +376,7 @@ function O(e = "country") {
|
|
|
380
376
|
function k(e, t) {
|
|
381
377
|
let n = { ...t };
|
|
382
378
|
function r(t) {
|
|
383
|
-
switch (t.data.type) {
|
|
379
|
+
if (t.source === e.contentWindow) switch (t.data.type) {
|
|
384
380
|
case "IFRAME_READY":
|
|
385
381
|
o(e), s({
|
|
386
382
|
iframe: e,
|
|
@@ -399,9 +395,7 @@ function k(e, t) {
|
|
|
399
395
|
case "UPDATED_APPEARANCE":
|
|
400
396
|
n.onAppearanceReady?.();
|
|
401
397
|
break;
|
|
402
|
-
case "CONFIRMATION_RESULT":
|
|
403
|
-
n.onResult(t.data.result);
|
|
404
|
-
break;
|
|
398
|
+
case "CONFIRMATION_RESULT": n.onResult(t.data.result);
|
|
405
399
|
}
|
|
406
400
|
}
|
|
407
401
|
return window.addEventListener("message", r), {
|
|
@@ -62,7 +62,9 @@ export type PaymentMethodFormListenerOptions = {
|
|
|
62
62
|
/**
|
|
63
63
|
* Called when the interactive confirmation flow finishes (success or
|
|
64
64
|
* terminal failure). Not settlement proof — verify via webhooks.
|
|
65
|
-
* Recoverable
|
|
65
|
+
* Recoverable validation posts `status: "incomplete"` with a `reason`
|
|
66
|
+
* (`field_errors` or `validation_failed`) instead of invoking this for
|
|
67
|
+
* terminal outcomes only — hosts should unlock UI on `incomplete`.
|
|
66
68
|
*/
|
|
67
69
|
onResult: (result: ConfirmationResult) => void;
|
|
68
70
|
};
|
package/dist/types.d.ts
CHANGED
|
@@ -1,4 +1,15 @@
|
|
|
1
1
|
import { components } from '@amos.com/node';
|
|
2
|
+
/**
|
|
3
|
+
* Why an interactive confirmation attempt ended with `status: "incomplete"`.
|
|
4
|
+
*
|
|
5
|
+
* Helps hosts distinguish recoverable iframe states for analytics and
|
|
6
|
+
* support without exposing PCI data.
|
|
7
|
+
*/
|
|
8
|
+
export type ConfirmationIncompleteReason =
|
|
9
|
+
/** Pay API field errors were mapped onto iframe inputs for retry. */
|
|
10
|
+
"field_errors"
|
|
11
|
+
/** Client-side / HTML5 validation failed when confirm was requested. */
|
|
12
|
+
| "validation_failed";
|
|
2
13
|
/**
|
|
3
14
|
* Outcome of an interactive confirmation flow inside the Amos iframe.
|
|
4
15
|
*
|
|
@@ -8,9 +19,9 @@ import { components } from '@amos.com/node';
|
|
|
8
19
|
* retrieving the PaymentIntent / SetupIntent with your secret key),
|
|
9
20
|
* the same way Stripe recommends.
|
|
10
21
|
*
|
|
11
|
-
* Recoverable field
|
|
12
|
-
*
|
|
13
|
-
* can fix the form and retry.
|
|
22
|
+
* Recoverable validation (field errors shown in the iframe, or client-side
|
|
23
|
+
* validation on confirm) posts `status: "incomplete"` with a `reason` so
|
|
24
|
+
* the host can unlock its UI; the customer can fix the form and retry.
|
|
14
25
|
*/
|
|
15
26
|
export type ConfirmationResult = {
|
|
16
27
|
status: "succeeded";
|
|
@@ -22,10 +33,12 @@ export type ConfirmationResult = {
|
|
|
22
33
|
setupIntent: components["schemas"]["SetupIntent"];
|
|
23
34
|
} | {
|
|
24
35
|
/**
|
|
25
|
-
* Recoverable validation was shown in the iframe
|
|
26
|
-
*
|
|
36
|
+
* Recoverable validation was shown in the iframe or confirm was
|
|
37
|
+
* blocked by client-side validation. Unlock the host UI; the customer
|
|
38
|
+
* can fix fields and retry. Do not treat as settlement.
|
|
27
39
|
*/
|
|
28
40
|
status: "incomplete";
|
|
41
|
+
reason: ConfirmationIncompleteReason;
|
|
29
42
|
} | {
|
|
30
43
|
status: "failed";
|
|
31
44
|
errorMessage: string;
|
|
@@ -61,42 +74,57 @@ export type Appearance = {
|
|
|
61
74
|
* embedded Amos iframe.
|
|
62
75
|
*/
|
|
63
76
|
export type Message = {
|
|
77
|
+
/** Embed → parent: iframe finished loading and is ready for messages. */
|
|
64
78
|
type: "IFRAME_READY";
|
|
65
79
|
} | {
|
|
80
|
+
/** Parent → embed: host acknowledged `IFRAME_READY`. */
|
|
66
81
|
type: "PARENT_ACKNOWLEDGED_IFRAME_READY";
|
|
67
82
|
} | {
|
|
83
|
+
/** Embed → parent: iframe content height changed (resize container). */
|
|
68
84
|
type: "UPDATE_HEIGHT";
|
|
69
85
|
height: string;
|
|
70
86
|
} | {
|
|
87
|
+
/** Parent → embed: express-checkout amount changed. */
|
|
71
88
|
type: "UPDATE_AMOUNT";
|
|
72
89
|
amount: string;
|
|
73
90
|
} | {
|
|
91
|
+
/** Parent → embed: express-checkout merchant display name changed. */
|
|
74
92
|
type: "UPDATE_MERCHANT_NAME";
|
|
75
93
|
merchantName: string;
|
|
76
94
|
} | {
|
|
95
|
+
/** Parent → embed: push appearance overrides into the iframe. */
|
|
77
96
|
type: "UPDATE_APPEARANCE";
|
|
78
97
|
appearance: Appearance;
|
|
79
98
|
} | {
|
|
99
|
+
/**
|
|
100
|
+
* Parent → embed: validate form inputs (`requestId` only).
|
|
101
|
+
* Embed → parent: validation response (`isValid` set).
|
|
102
|
+
*/
|
|
80
103
|
type: "VALIDATE_FORM";
|
|
81
104
|
requestId: string;
|
|
82
105
|
isValid?: boolean;
|
|
83
106
|
} | {
|
|
107
|
+
/** Embed → parent: express checkout requests a payment intent from the host. */
|
|
84
108
|
type: "CREATE_PAYMENT_INTENT";
|
|
85
109
|
paymentIntentCreateAttributes: components["schemas"]["CreatePaymentIntentInput"];
|
|
86
110
|
customerCreateAttributes: components["schemas"]["CreateCustomerInput"];
|
|
87
111
|
} | ({
|
|
112
|
+
/** Parent → embed: confirm a payment intent with an embed token. */
|
|
88
113
|
type: "CONFIRM_PAYMENT_INTENT";
|
|
89
114
|
} & Pick<components["schemas"]["PaymentIntent"], "id"> & Pick<components["schemas"]["EmbedToken"], "token">) | ({
|
|
115
|
+
/** Parent → embed: confirm a setup intent with an embed token. */
|
|
90
116
|
type: "CONFIRM_SETUP_INTENT";
|
|
91
117
|
} & Pick<components["schemas"]["SetupIntent"], "id"> & Pick<components["schemas"]["EmbedToken"], "token">) | {
|
|
92
118
|
/**
|
|
93
119
|
* Embed → parent: the interactive confirmation flow finished.
|
|
120
|
+
* Parent → embed: express-checkout initiation failed on the host.
|
|
94
121
|
* Not settlement proof — verify payment/setup success on your
|
|
95
122
|
* backend via webhooks (or by retrieving the intent).
|
|
96
123
|
*/
|
|
97
124
|
type: "CONFIRMATION_RESULT";
|
|
98
125
|
result: ConfirmationResult;
|
|
99
126
|
} | {
|
|
127
|
+
/** Embed → parent: appearance overrides were applied in the iframe. */
|
|
100
128
|
type: "UPDATED_APPEARANCE";
|
|
101
129
|
} | {
|
|
102
130
|
/** Embed → parent: Apple Pay Code window / waiting UI should show. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@amos.com/amos-js",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.2",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -35,16 +35,16 @@
|
|
|
35
35
|
"license": "MIT",
|
|
36
36
|
"description": "Amos JavaScript SDK for embedding payment methods via iframes.",
|
|
37
37
|
"devDependencies": {
|
|
38
|
-
"@biomejs/biome": "2.5.",
|
|
38
|
+
"@biomejs/biome": "2.5.7",
|
|
39
39
|
"@changesets/cli": "2.31.1",
|
|
40
|
-
"@types/node": "26.1.
|
|
40
|
+
"@types/node": "26.1.2",
|
|
41
41
|
"@typescript/typescript6": "6.0.2",
|
|
42
42
|
"typescript": "7.0.2",
|
|
43
|
-
"vite": "8.
|
|
43
|
+
"vite": "8.2.0",
|
|
44
44
|
"vite-plugin-dts": "5.0.3"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@amos.com/node": "0.1.
|
|
47
|
+
"@amos.com/node": "0.1.35",
|
|
48
48
|
"@types/googlepay": "0.7.11"
|
|
49
49
|
}
|
|
50
50
|
}
|
package/src/google-pay.ts
CHANGED
|
@@ -107,6 +107,10 @@ export function attachGooglePayButtonListeners(
|
|
|
107
107
|
}
|
|
108
108
|
|
|
109
109
|
function handleMessage(event: MessageEvent<Message>) {
|
|
110
|
+
if (event.source !== iframe.contentWindow) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
|
|
110
114
|
switch (event.data.type) {
|
|
111
115
|
case "IFRAME_READY":
|
|
112
116
|
sendParentReadyMessage(iframe);
|
package/src/index.ts
CHANGED
|
@@ -129,7 +129,9 @@ export type PaymentMethodFormListenerOptions = {
|
|
|
129
129
|
/**
|
|
130
130
|
* Called when the interactive confirmation flow finishes (success or
|
|
131
131
|
* terminal failure). Not settlement proof — verify via webhooks.
|
|
132
|
-
* Recoverable
|
|
132
|
+
* Recoverable validation posts `status: "incomplete"` with a `reason`
|
|
133
|
+
* (`field_errors` or `validation_failed`) instead of invoking this for
|
|
134
|
+
* terminal outcomes only — hosts should unlock UI on `incomplete`.
|
|
133
135
|
*/
|
|
134
136
|
onResult: (result: ConfirmationResult) => void;
|
|
135
137
|
};
|
|
@@ -167,6 +169,10 @@ export function attachPaymentMethodFormListeners(
|
|
|
167
169
|
let current = { ...options };
|
|
168
170
|
|
|
169
171
|
function handleMessage(event: MessageEvent<Message>) {
|
|
172
|
+
if (event.source !== iframe.contentWindow) {
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
|
|
170
176
|
switch (event.data.type) {
|
|
171
177
|
case "IFRAME_READY":
|
|
172
178
|
sendParentReadyMessage(iframe);
|
package/src/types.ts
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
import type { components } from "@amos.com/node";
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Why an interactive confirmation attempt ended with `status: "incomplete"`.
|
|
5
|
+
*
|
|
6
|
+
* Helps hosts distinguish recoverable iframe states for analytics and
|
|
7
|
+
* support without exposing PCI data.
|
|
8
|
+
*/
|
|
9
|
+
export type ConfirmationIncompleteReason =
|
|
10
|
+
/** Pay API field errors were mapped onto iframe inputs for retry. */
|
|
11
|
+
| "field_errors"
|
|
12
|
+
/** Client-side / HTML5 validation failed when confirm was requested. */
|
|
13
|
+
| "validation_failed";
|
|
14
|
+
|
|
3
15
|
/**
|
|
4
16
|
* Outcome of an interactive confirmation flow inside the Amos iframe.
|
|
5
17
|
*
|
|
@@ -9,9 +21,9 @@ import type { components } from "@amos.com/node";
|
|
|
9
21
|
* retrieving the PaymentIntent / SetupIntent with your secret key),
|
|
10
22
|
* the same way Stripe recommends.
|
|
11
23
|
*
|
|
12
|
-
* Recoverable field
|
|
13
|
-
*
|
|
14
|
-
* can fix the form and retry.
|
|
24
|
+
* Recoverable validation (field errors shown in the iframe, or client-side
|
|
25
|
+
* validation on confirm) posts `status: "incomplete"` with a `reason` so
|
|
26
|
+
* the host can unlock its UI; the customer can fix the form and retry.
|
|
15
27
|
*/
|
|
16
28
|
export type ConfirmationResult =
|
|
17
29
|
| {
|
|
@@ -26,10 +38,12 @@ export type ConfirmationResult =
|
|
|
26
38
|
}
|
|
27
39
|
| {
|
|
28
40
|
/**
|
|
29
|
-
* Recoverable validation was shown in the iframe
|
|
30
|
-
*
|
|
41
|
+
* Recoverable validation was shown in the iframe or confirm was
|
|
42
|
+
* blocked by client-side validation. Unlock the host UI; the customer
|
|
43
|
+
* can fix fields and retry. Do not treat as settlement.
|
|
31
44
|
*/
|
|
32
45
|
status: "incomplete";
|
|
46
|
+
reason: ConfirmationIncompleteReason;
|
|
33
47
|
}
|
|
34
48
|
| {
|
|
35
49
|
status: "failed";
|
|
@@ -174,48 +188,62 @@ export type Appearance = {
|
|
|
174
188
|
*/
|
|
175
189
|
export type Message =
|
|
176
190
|
| {
|
|
191
|
+
/** Embed → parent: iframe finished loading and is ready for messages. */
|
|
177
192
|
type: "IFRAME_READY";
|
|
178
193
|
}
|
|
179
194
|
| {
|
|
195
|
+
/** Parent → embed: host acknowledged `IFRAME_READY`. */
|
|
180
196
|
type: "PARENT_ACKNOWLEDGED_IFRAME_READY";
|
|
181
197
|
}
|
|
182
198
|
| {
|
|
199
|
+
/** Embed → parent: iframe content height changed (resize container). */
|
|
183
200
|
type: "UPDATE_HEIGHT";
|
|
184
201
|
height: string;
|
|
185
202
|
}
|
|
186
203
|
| {
|
|
204
|
+
/** Parent → embed: express-checkout amount changed. */
|
|
187
205
|
type: "UPDATE_AMOUNT";
|
|
188
206
|
amount: string;
|
|
189
207
|
}
|
|
190
208
|
| {
|
|
209
|
+
/** Parent → embed: express-checkout merchant display name changed. */
|
|
191
210
|
type: "UPDATE_MERCHANT_NAME";
|
|
192
211
|
merchantName: string;
|
|
193
212
|
}
|
|
194
213
|
| {
|
|
214
|
+
/** Parent → embed: push appearance overrides into the iframe. */
|
|
195
215
|
type: "UPDATE_APPEARANCE";
|
|
196
216
|
appearance: Appearance;
|
|
197
217
|
}
|
|
198
218
|
| {
|
|
219
|
+
/**
|
|
220
|
+
* Parent → embed: validate form inputs (`requestId` only).
|
|
221
|
+
* Embed → parent: validation response (`isValid` set).
|
|
222
|
+
*/
|
|
199
223
|
type: "VALIDATE_FORM";
|
|
200
224
|
requestId: string;
|
|
201
225
|
isValid?: boolean;
|
|
202
226
|
}
|
|
203
227
|
| {
|
|
228
|
+
/** Embed → parent: express checkout requests a payment intent from the host. */
|
|
204
229
|
type: "CREATE_PAYMENT_INTENT";
|
|
205
230
|
paymentIntentCreateAttributes: components["schemas"]["CreatePaymentIntentInput"];
|
|
206
231
|
customerCreateAttributes: components["schemas"]["CreateCustomerInput"];
|
|
207
232
|
}
|
|
208
233
|
| ({
|
|
234
|
+
/** Parent → embed: confirm a payment intent with an embed token. */
|
|
209
235
|
type: "CONFIRM_PAYMENT_INTENT";
|
|
210
236
|
} & Pick<components["schemas"]["PaymentIntent"], "id"> &
|
|
211
237
|
Pick<components["schemas"]["EmbedToken"], "token">)
|
|
212
238
|
| ({
|
|
239
|
+
/** Parent → embed: confirm a setup intent with an embed token. */
|
|
213
240
|
type: "CONFIRM_SETUP_INTENT";
|
|
214
241
|
} & Pick<components["schemas"]["SetupIntent"], "id"> &
|
|
215
242
|
Pick<components["schemas"]["EmbedToken"], "token">)
|
|
216
243
|
| {
|
|
217
244
|
/**
|
|
218
245
|
* Embed → parent: the interactive confirmation flow finished.
|
|
246
|
+
* Parent → embed: express-checkout initiation failed on the host.
|
|
219
247
|
* Not settlement proof — verify payment/setup success on your
|
|
220
248
|
* backend via webhooks (or by retrieving the intent).
|
|
221
249
|
*/
|
|
@@ -223,6 +251,7 @@ export type Message =
|
|
|
223
251
|
result: ConfirmationResult;
|
|
224
252
|
}
|
|
225
253
|
| {
|
|
254
|
+
/** Embed → parent: appearance overrides were applied in the iframe. */
|
|
226
255
|
type: "UPDATED_APPEARANCE";
|
|
227
256
|
}
|
|
228
257
|
| {
|