@amos.com/amos-js 0.9.10 → 0.9.12
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 +9 -5
- package/dist/form-skeleton.d.ts +20 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +64 -1
- package/dist/index.mjs +184 -55
- package/dist/mount.d.ts +6 -0
- package/dist/payment-method-form.d.ts +7 -1
- package/dist/types.d.ts +16 -0
- package/package.json +1 -1
- package/src/form-skeleton.ts +244 -0
- package/src/index.ts +1 -0
- package/src/mount.ts +166 -38
- package/src/payment-method-form.ts +16 -1
- package/src/types.ts +18 -0
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@ 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`, `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.
|
|
17
|
+
- **Mount functions** (`mountAmosCreditCardPaymentMethodForm`, `mountAmosBankAccountPaymentMethodForm`, `mountAmosGooglePayButton`, `mountAmosApplePayButton`) that create the iframe, wire up its message protocol, manage its height/opacity, show a field-shaped loading skeleton for card/bank forms, and return a small controller for updating options and tearing it down.
|
|
18
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.
|
|
@@ -63,6 +63,9 @@ const form = mountAmosCreditCardPaymentMethodForm(
|
|
|
63
63
|
console.log("Recoverable:", result.reason);
|
|
64
64
|
}
|
|
65
65
|
},
|
|
66
|
+
onValidityChange: ({ isValid }) => {
|
|
67
|
+
document.querySelector("#pay-now")!.disabled = !isValid;
|
|
68
|
+
},
|
|
66
69
|
},
|
|
67
70
|
);
|
|
68
71
|
|
|
@@ -93,7 +96,7 @@ form.destroy();
|
|
|
93
96
|
The following flow is for credit card and bank account payment method types only.
|
|
94
97
|
|
|
95
98
|
1. **Set up prerequisites**: create a `renderToken` (safe for client), and keep `apiKey` and `accountId` server-side only.
|
|
96
|
-
2. **Render your checkout UI** by calling `mountAmosCreditCardPaymentMethodForm(container, options)` (or `mountAmosBankAccountPaymentMethodForm(...)`) along with the required `onResult` callback. The
|
|
99
|
+
2. **Render your checkout UI** by calling `mountAmosCreditCardPaymentMethodForm(container, options)` (or `mountAmosBankAccountPaymentMethodForm(...)`) along with the required `onResult` callback. The SDK shows a field-shaped skeleton immediately (sized from `appearance`, `additionalFields`, and `billingAddressRequirement`) and auto-manages iframe height.
|
|
97
100
|
3. **User clicks "Pay now" button**: call `validateForm({ iframe: form.iframe })`, which returns `Promise<true>` if the embedded form is valid, and `Promise<false>` otherwise.
|
|
98
101
|
4. **Create payment intent on your server**: use your server-side Amos client to call `POST /payment_intents`. You may also associate this payment intent with a new or existing customer via `POST /customers`. This must be server-side because it uses your private API key.
|
|
99
102
|
5. **Return the payment intent token to the browser**: your backend responds with the embed token (`components["schemas"]["EmbedToken"]`) needed for confirmation.
|
|
@@ -264,13 +267,14 @@ Mount the secure credit-card payment method form into a container element (an `H
|
|
|
264
267
|
- `billingAddressRequirement` (`"country" | "full"`, defaults to `"country"`) — how much billing address the iframe collects. `country` collects country / region and, for CA / PR / GB / US, a postal code (labeled ZIP for the United States). `full` shows a full street address form with Smarty autocomplete.
|
|
265
268
|
|
|
266
269
|
|
|
267
|
-
- `
|
|
270
|
+
- `onValidityChange` (`(event: { isValid: boolean }) => void`) — called when form validity changes. `isValid` is true when all required fields are present and valid. Does not include PCI data. Use this to enable or disable your checkout button.
|
|
271
|
+
- `onHeightChange`, `onAppearanceReady` (advanced — override the default iframe styling logic). The skeleton is removed and the iframe faded in when `onAppearanceReady` fires.
|
|
268
272
|
|
|
269
273
|
**Returns** `AmosPaymentMethodFormMountController`:
|
|
270
274
|
|
|
271
275
|
- `iframe` — the underlying `<iframe>` element.
|
|
272
276
|
- `update(patch)` — patch any of the options listed above.
|
|
273
|
-
- `destroy()` — remove the iframe and detach listeners.
|
|
277
|
+
- `destroy()` — remove the iframe (and any loading skeleton) and detach listeners.
|
|
274
278
|
|
|
275
279
|
### `mountAmosBankAccountPaymentMethodForm(container, options)`
|
|
276
280
|
|
|
@@ -363,7 +367,7 @@ Advanced helpers exposed for integrators that need to construct or inspect the m
|
|
|
363
367
|
|
|
364
368
|
### Exported types
|
|
365
369
|
|
|
366
|
-
`Message`, `Appearance`, `ThemeVariable`, `FormattedGooglePayPaymentData`, plus the per-form `*Options` and `*Controller` types. For OpenAPI schema types, import `components` from `@amos.com/node`.
|
|
370
|
+
`Message`, `Appearance`, `ThemeVariable`, `FormattedGooglePayPaymentData`, `PaymentMethodFormValidityChangeEvent`, plus the per-form `*Options` and `*Controller` types. For OpenAPI schema types, import `components` from `@amos.com/node`.
|
|
367
371
|
|
|
368
372
|
## Notes and potential gotchas
|
|
369
373
|
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { BillingAddressRequirement, CreditCardAdditionalFields } from './payment-method-form';
|
|
2
|
+
import { Appearance } from './types';
|
|
3
|
+
export type PaymentMethodFormSkeletonKind = "card" | "bank";
|
|
4
|
+
export type PaymentMethodFormSkeletonOptions = {
|
|
5
|
+
kind: PaymentMethodFormSkeletonKind;
|
|
6
|
+
appearance?: Appearance;
|
|
7
|
+
additionalFields?: CreditCardAdditionalFields;
|
|
8
|
+
billingAddressRequirement?: BillingAddressRequirement;
|
|
9
|
+
};
|
|
10
|
+
export type PaymentMethodFormSkeleton = {
|
|
11
|
+
element: HTMLElement;
|
|
12
|
+
update: (options: PaymentMethodFormSkeletonOptions) => void;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Host-page placeholder that mirrors card/bank field layout using the
|
|
16
|
+
* same appearance variables the iframe will apply. Shown immediately
|
|
17
|
+
* while the iframe document loads, then removed when appearance is
|
|
18
|
+
* ready.
|
|
19
|
+
*/
|
|
20
|
+
export declare function createPaymentMethodFormSkeleton(options: PaymentMethodFormSkeletonOptions): PaymentMethodFormSkeleton;
|
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, ApplePayButtonElementProps, ApplePayButtonStyle, ApplePayButtonType, ConfirmationIncompleteReason, ConfirmationResult, GooglePayButtonElementProps, Message, ThemeVariable, WalletButtonStyle, } from './types';
|
|
12
|
+
export type { Appearance, AppearanceLabels, ApplePayButtonElementProps, ApplePayButtonStyle, ApplePayButtonType, ConfirmationIncompleteReason, ConfirmationResult, GooglePayButtonElementProps, Message, PaymentMethodFormValidityChangeEvent, ThemeVariable, WalletButtonStyle, } from './types';
|
|
13
13
|
export { createMessage, pickApplePayButtonElementProps, pickGooglePayButtonElementProps, serializeWalletButtonStyle, } from './types';
|
package/dist/index.js
CHANGED
|
@@ -1 +1,64 @@
|
|
|
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 814 1000" fill="currentColor" xmlns="http://www.w3.org/2000/svg"><path d="M788.1 340.9c-5.8 4.5-108.2 62.2-108.2 190.5 0 148.4 130.3 200.9 134.2 202.2-.6 3.2-20.7 71.9-68.7 141.9-42.8 61.6-87.5 123.1-155.5 123.1s-85.5-39.5-164-39.5c-76.5 0-103.7 40.8-165.9 40.8s-105.6-57-155.5-127C46.7 790.7 0 663 0 541.8c0-194.4 126.4-297.5 250.8-297.5 66.1 0 121.2 43.4 162.7 43.4 39.5 0 101.1-46 176.3-46 28.5 0 130.9 2.6 198.3 99.2zm-234-181.5c31.1-36.9 53.1-88.1 53.1-139.3 0-7.1-.6-14.3-1.9-20.1-50.6 1.9-110.8 33.7-147.1 75.8-28.5 32.4-55.1 83.6-55.1 135.5 0 7.8 1.3 15.6 1.9 18.1 3.2.6 8.4 1.3 13.6 1.3 45.4 0 102.5-30.4 135.5-71.3z"/></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){if(!e||typeof e!=`object`)return;let t={};for(let[n,r]of Object.entries(e)){if(typeof r==`string`){if(r.length===0)continue;t[n]=r;continue}typeof r==`number`&&Number.isFinite(r)&&(t[n]=r)}return Object.keys(t).length>0?t:void 0}function o(e){let t={};if(e.buttonstyle!==void 0&&(t.buttonstyle=e.buttonstyle),e.type!==void 0&&(t.type=e.type),e.locale!==void 0&&(t.locale=e.locale),e.style!==void 0){let n=a(e.style);n&&(t.style=n)}return t}function s(e){let t={};if(e.buttonType!==void 0&&(t.buttonType=e.buttonType),e.buttonColor!==void 0&&(t.buttonColor=e.buttonColor),e.buttonRadius!==void 0&&(t.buttonRadius=e.buttonRadius),e.buttonSizeMode!==void 0&&(t.buttonSizeMode=e.buttonSizeMode),e.buttonLocale!==void 0&&(t.buttonLocale=e.buttonLocale),e.buttonBorderType!==void 0&&(t.buttonBorderType=e.buttonBorderType),e.style!==void 0){let n=a(e.style);n&&(t.style=n)}return t}function c(e){return e}function l(e){return new URL(e.src).origin}function u(e){e?.contentWindow&&e.contentWindow.postMessage(c({type:`PARENT_ACKNOWLEDGED_IFRAME_READY`}),l(e))}function d({iframe:e,appearance:t={}}){e?.contentWindow&&e.contentWindow.postMessage(c({type:`UPDATE_APPEARANCE`,appearance:t}),l(e))}function f({iframe:e,props:t}){e?.contentWindow&&e.contentWindow.postMessage(c({type:`UPDATE_APPLE_PAY_BUTTON`,props:o(t)}),l(e))}function p({iframe:e,props:t}){e?.contentWindow&&e.contentWindow.postMessage(c({type:`UPDATE_GOOGLE_PAY_BUTTON`,props:s(t)}),l(e))}function m({iframe:e,amount:t}){e?.contentWindow&&e.contentWindow.postMessage(c({type:`UPDATE_AMOUNT`,amount:t}),l(e))}function h({iframe:e,merchantName:t}){e?.contentWindow&&e.contentWindow.postMessage(c({type:`UPDATE_MERCHANT_NAME`,merchantName:t}),l(e))}function g({iframe:e}){let t=crypto.randomUUID();return new Promise(n=>{e?.contentWindow&&e.contentWindow.postMessage(c({type:`VALIDATE_FORM`,requestId:t}),l(e));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 _({iframe:e}){e?.contentWindow&&e.contentWindow.postMessage(c({type:`RESET_FORM`}),l(e))}function v({iframe:e,token:t}){if(!e?.contentWindow)return;let{payment_intent_id:n}=r(t).payload;e.contentWindow.postMessage(c({type:`CONFIRM_PAYMENT_INTENT`,token:t,id:n??void 0}),l(e))}function y({iframe:e,token:t}){if(!e?.contentWindow)return;let{setup_intent_id:n}=r(t).payload;e.contentWindow.postMessage(c({type:`CONFIRM_SETUP_INTENT`,token:t,id:n??void 0}),l(e))}function b({iframe:e,result:t}){e?.contentWindow&&e.contentWindow.postMessage(c({type:`CONFIRMATION_RESULT`,result:t}),l(e))}function x(e){return`${i(e)}/iframe/apple-pay?token=${e}`}function S(){return`40px`}function C(e){e.contentWindow?.postMessage(c({type:`APPLE_PAY_CANCEL`}),l(e))}function w(e,r){let i={...r};function a(){m({iframe:e,amount:i.amount})}function o(){h({iframe:e,merchantName:i.merchantName})}function s(){f({iframe:e,props:i})}function c(r){if(r.source===e.contentWindow)switch(r.data.type){case`IFRAME_READY`:u(e),d({iframe:e,appearance:i.appearance}),a(),o(),s();break;case`UPDATE_HEIGHT`:i.onHeightChange?.(r.data.height);break;case`UPDATE_APPEARANCE`:d({iframe:e,appearance:r.data.appearance});break;case`UPDATED_APPEARANCE`:i.onAppearanceReady?.();break;case`APPLE_PAY_WINDOW_OPEN`:t({onCancel:()=>{C(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=>{v({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`,c),{update(t){let n=`appearance`in t,r=`amount`in t,c=`merchantName`in t,l=`buttonstyle`in t||`type`in t||`locale`in t||`style`in t;i={...i,...t},n&&d({iframe:e,appearance:i.appearance}),r&&a(),c&&o(),l&&s()},destroy(){window.removeEventListener(`message`,c),n()}}}function T(e){return`${i(e)}/iframe/google-pay?token=${e}`}function E(){return`40px`}function D(e,t){let n={...t};function r(){m({iframe:e,amount:n.amount})}function i(){h({iframe:e,merchantName:n.merchantName})}function a(){p({iframe:e,props:n})}function o(t){if(t.source===e.contentWindow)switch(t.data.type){case`IFRAME_READY`:u(e),d({iframe:e,appearance:n.appearance}),r(),i(),a();break;case`UPDATE_HEIGHT`:n.onHeightChange?.(t.data.height);break;case`UPDATE_APPEARANCE`:d({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=>{v({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`,o),{update(t){let o=`appearance`in t,s=`amount`in t,c=`merchantName`in t,l=`buttonType`in t||`buttonColor`in t||`buttonRadius`in t||`buttonSizeMode`in t||`buttonLocale`in t||`buttonBorderType`in t||`style`in t;n={...n,...t},o&&d({iframe:e,appearance:n.appearance}),s&&r(),c&&i(),l&&a()},destroy(){window.removeEventListener(`message`,o)}}}function O({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_payload:e.paymentMethodData.tokenizationData.token}}}}var k={country:212,full:452},A=80,j={country:400,full:640};function M(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 N(e,t=`country`){let n=new URLSearchParams({token:e,billingAddressRequirement:t});return`${i(e)}/iframe/bank?${n}`}function P(e={cardholderName:!1},t=`country`){return`${(k[t]??k.country)+(e.cardholderName?A:0)}px`}function F(e=`country`){return`${j[e]??j.country}px`}function I(e,t){let n={...t};function r(t){if(t.source===e.contentWindow)switch(t.data.type){case`IFRAME_READY`:u(e),d({iframe:e,appearance:n.appearance});break;case`UPDATE_HEIGHT`:n.onHeightChange?.(t.data.height);break;case`UPDATE_APPEARANCE`:d({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&&d({iframe:e,appearance:n.appearance})},destroy(){window.removeEventListener(`message`,r)}}}function L(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 R={width:`calc(100% + 8px)`,transition:`opacity 150ms ease-in, height 200ms ease-in-out`,margin:`0 -4px`,opacity:`0`,border:`0`};function z({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,R,{height:r}),a}function B(e,t){let n=L(e),{renderToken:r,additionalFields:i={cardholderName:!1},billingAddressRequirement:a=`country`,...o}=t,s=z({src:M(r,i,a),title:`Secure credit card payment method form powered by Amos`,name:`amos-credit-card-payment-method-form`,height:P(i,a)});n.appendChild(s);let c=I(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 V(e,t){let n=L(e),{renderToken:r,billingAddressRequirement:i=`country`,...a}=t,o=z({src:N(r,i),title:`Secure bank account payment method form powered by Amos`,name:`amos-bank-account-payment-method-form`,height:F(i)});n.appendChild(o);let s=I(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 H(e,t){let n=L(e),{renderToken:r,...i}=t,a=z({src:T(r),title:`Secure Google Pay button powered by Amos`,name:`amos-google-pay-button`,height:E(),allow:`payment`});n.appendChild(a);let o=D(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 U(e,t){let n=L(e),{renderToken:r,...i}=t,a=z({src:x(r),title:`Secure Apple Pay button powered by Amos`,name:`amos-apple-pay-button`,height:S(),allow:`payment`});n.appendChild(a);let o=w(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=w,exports.attachGooglePayButtonListeners=D,exports.attachPaymentMethodFormListeners=I,exports.confirmPaymentIntent=v,exports.confirmSetupIntent=y,exports.createMessage=c,exports.decodeJwt=r,exports.formatGooglePayPaymentData=O,exports.getApplePayButtonInitialHeight=S,exports.getApplePayButtonSrc=x,exports.getBankAccountFormInitialHeight=F,exports.getBankAccountFormSrc=N,exports.getCreditCardFormInitialHeight=P,exports.getCreditCardFormSrc=M,exports.getEmbedOrigin=i,exports.getGooglePayButtonInitialHeight=E,exports.getGooglePayButtonSrc=T,exports.mountAmosApplePayButton=U,exports.mountAmosBankAccountPaymentMethodForm=V,exports.mountAmosCreditCardPaymentMethodForm=B,exports.mountAmosGooglePayButton=H,exports.pickApplePayButtonElementProps=o,exports.pickGooglePayButtonElementProps=s,exports.resetForm=_,exports.sendConfirmationResult=b,exports.sendParentReadyMessage=u,exports.serializeWalletButtonStyle=a,exports.updateAmount=m,exports.updateAppearance=d,exports.updateApplePayButton=f,exports.updateGooglePayButton=p,exports.updateMerchantName=h,exports.validateForm=g;
|
|
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 814 1000" fill="currentColor" xmlns="http://www.w3.org/2000/svg"><path d="M788.1 340.9c-5.8 4.5-108.2 62.2-108.2 190.5 0 148.4 130.3 200.9 134.2 202.2-.6 3.2-20.7 71.9-68.7 141.9-42.8 61.6-87.5 123.1-155.5 123.1s-85.5-39.5-164-39.5c-76.5 0-103.7 40.8-165.9 40.8s-105.6-57-155.5-127C46.7 790.7 0 663 0 541.8c0-194.4 126.4-297.5 250.8-297.5 66.1 0 121.2 43.4 162.7 43.4 39.5 0 101.1-46 176.3-46 28.5 0 130.9 2.6 198.3 99.2zm-234-181.5c31.1-36.9 53.1-88.1 53.1-139.3 0-7.1-.6-14.3-1.9-20.1-50.6 1.9-110.8 33.7-147.1 75.8-28.5 32.4-55.1 83.6-55.1 135.5 0 7.8 1.3 15.6 1.9 18.1 3.2.6 8.4 1.3 13.6 1.3 45.4 0 102.5-30.4 135.5-71.3z"/></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){if(!e||typeof e!=`object`)return;let t={};for(let[n,r]of Object.entries(e)){if(typeof r==`string`){if(r.length===0)continue;t[n]=r;continue}typeof r==`number`&&Number.isFinite(r)&&(t[n]=r)}return Object.keys(t).length>0?t:void 0}function o(e){let t={};if(e.buttonstyle!==void 0&&(t.buttonstyle=e.buttonstyle),e.type!==void 0&&(t.type=e.type),e.locale!==void 0&&(t.locale=e.locale),e.style!==void 0){let n=a(e.style);n&&(t.style=n)}return t}function s(e){let t={};if(e.buttonType!==void 0&&(t.buttonType=e.buttonType),e.buttonColor!==void 0&&(t.buttonColor=e.buttonColor),e.buttonRadius!==void 0&&(t.buttonRadius=e.buttonRadius),e.buttonSizeMode!==void 0&&(t.buttonSizeMode=e.buttonSizeMode),e.buttonLocale!==void 0&&(t.buttonLocale=e.buttonLocale),e.buttonBorderType!==void 0&&(t.buttonBorderType=e.buttonBorderType),e.style!==void 0){let n=a(e.style);n&&(t.style=n)}return t}function c(e){return e}function l(e){return new URL(e.src).origin}function u(e){e?.contentWindow&&e.contentWindow.postMessage(c({type:`PARENT_ACKNOWLEDGED_IFRAME_READY`}),l(e))}function d({iframe:e,appearance:t={}}){e?.contentWindow&&e.contentWindow.postMessage(c({type:`UPDATE_APPEARANCE`,appearance:t}),l(e))}function f({iframe:e,props:t}){e?.contentWindow&&e.contentWindow.postMessage(c({type:`UPDATE_APPLE_PAY_BUTTON`,props:o(t)}),l(e))}function p({iframe:e,props:t}){e?.contentWindow&&e.contentWindow.postMessage(c({type:`UPDATE_GOOGLE_PAY_BUTTON`,props:s(t)}),l(e))}function m({iframe:e,amount:t}){e?.contentWindow&&e.contentWindow.postMessage(c({type:`UPDATE_AMOUNT`,amount:t}),l(e))}function h({iframe:e,merchantName:t}){e?.contentWindow&&e.contentWindow.postMessage(c({type:`UPDATE_MERCHANT_NAME`,merchantName:t}),l(e))}function g({iframe:e}){let t=crypto.randomUUID();return new Promise(n=>{e?.contentWindow&&e.contentWindow.postMessage(c({type:`VALIDATE_FORM`,requestId:t}),l(e));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 _({iframe:e}){e?.contentWindow&&e.contentWindow.postMessage(c({type:`RESET_FORM`}),l(e))}function v({iframe:e,token:t}){if(!e?.contentWindow)return;let{payment_intent_id:n}=r(t).payload;e.contentWindow.postMessage(c({type:`CONFIRM_PAYMENT_INTENT`,token:t,id:n??void 0}),l(e))}function y({iframe:e,token:t}){if(!e?.contentWindow)return;let{setup_intent_id:n}=r(t).payload;e.contentWindow.postMessage(c({type:`CONFIRM_SETUP_INTENT`,token:t,id:n??void 0}),l(e))}function b({iframe:e,result:t}){e?.contentWindow&&e.contentWindow.postMessage(c({type:`CONFIRMATION_RESULT`,result:t}),l(e))}function x(e){return`${i(e)}/iframe/apple-pay?token=${e}`}function S(){return`40px`}function C(e){e.contentWindow?.postMessage(c({type:`APPLE_PAY_CANCEL`}),l(e))}function w(e,r){let i={...r};function a(){m({iframe:e,amount:i.amount})}function o(){h({iframe:e,merchantName:i.merchantName})}function s(){f({iframe:e,props:i})}function c(r){if(r.source===e.contentWindow)switch(r.data.type){case`IFRAME_READY`:u(e),d({iframe:e,appearance:i.appearance}),a(),o(),s();break;case`UPDATE_HEIGHT`:i.onHeightChange?.(r.data.height);break;case`UPDATE_APPEARANCE`:d({iframe:e,appearance:r.data.appearance});break;case`UPDATED_APPEARANCE`:i.onAppearanceReady?.();break;case`APPLE_PAY_WINDOW_OPEN`:t({onCancel:()=>{C(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=>{v({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`,c),{update(t){let n=`appearance`in t,r=`amount`in t,c=`merchantName`in t,l=`buttonstyle`in t||`type`in t||`locale`in t||`style`in t;i={...i,...t},n&&d({iframe:e,appearance:i.appearance}),r&&a(),c&&o(),l&&s()},destroy(){window.removeEventListener(`message`,c),n()}}}function T(e){return`${i(e)}/iframe/google-pay?token=${e}`}function E(){return`40px`}function D(e,t){let n={...t};function r(){m({iframe:e,amount:n.amount})}function i(){h({iframe:e,merchantName:n.merchantName})}function a(){p({iframe:e,props:n})}function o(t){if(t.source===e.contentWindow)switch(t.data.type){case`IFRAME_READY`:u(e),d({iframe:e,appearance:n.appearance}),r(),i(),a();break;case`UPDATE_HEIGHT`:n.onHeightChange?.(t.data.height);break;case`UPDATE_APPEARANCE`:d({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=>{v({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`,o),{update(t){let o=`appearance`in t,s=`amount`in t,c=`merchantName`in t,l=`buttonType`in t||`buttonColor`in t||`buttonRadius`in t||`buttonSizeMode`in t||`buttonLocale`in t||`buttonBorderType`in t||`style`in t;n={...n,...t},o&&d({iframe:e,appearance:n.appearance}),s&&r(),c&&i(),l&&a()},destroy(){window.removeEventListener(`message`,o)}}}function O({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_payload:e.paymentMethodData.tokenizationData.token}}}}var k=`amos-js-form-skeleton-styles`,A={"--accent":`oklch(0.97 0 0)`,"--radius":`0.625rem`,"--input-height":`2.25rem`,"--floating-input-height":`3.25rem`,"--field-gap":`1rem`,"--control-gap":`0.5rem`,"--label-font-size":`0.875rem`},j=`
|
|
2
|
+
.amos-js-form-skeleton {
|
|
3
|
+
box-sizing: border-box;
|
|
4
|
+
container-type: inline-size;
|
|
5
|
+
display: flex;
|
|
6
|
+
flex-direction: column;
|
|
7
|
+
gap: var(--field-gap);
|
|
8
|
+
margin: 0 -4px;
|
|
9
|
+
padding-block: 0.25rem;
|
|
10
|
+
pointer-events: none;
|
|
11
|
+
width: calc(100% + 8px);
|
|
12
|
+
}
|
|
13
|
+
.amos-js-form-skeleton-field {
|
|
14
|
+
display: flex;
|
|
15
|
+
flex: 1 1 0;
|
|
16
|
+
flex-direction: column;
|
|
17
|
+
min-width: 0;
|
|
18
|
+
width: 100%;
|
|
19
|
+
}
|
|
20
|
+
.amos-js-form-skeleton-label {
|
|
21
|
+
flex-shrink: 0;
|
|
22
|
+
font-size: var(--label-font-size);
|
|
23
|
+
height: 1.75rem;
|
|
24
|
+
line-height: 1.75rem;
|
|
25
|
+
}
|
|
26
|
+
.amos-js-form-skeleton-input {
|
|
27
|
+
animation: amos-js-skeleton-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
|
|
28
|
+
background: var(--accent);
|
|
29
|
+
border-radius: calc(var(--radius) * 0.8);
|
|
30
|
+
box-sizing: border-box;
|
|
31
|
+
height: var(--input-height);
|
|
32
|
+
width: 100%;
|
|
33
|
+
}
|
|
34
|
+
.amos-js-form-skeleton-input-floating {
|
|
35
|
+
height: var(--floating-input-height);
|
|
36
|
+
}
|
|
37
|
+
.amos-js-form-skeleton-row {
|
|
38
|
+
align-items: flex-start;
|
|
39
|
+
display: flex;
|
|
40
|
+
gap: var(--control-gap);
|
|
41
|
+
width: 100%;
|
|
42
|
+
}
|
|
43
|
+
.amos-js-form-skeleton-row-stack {
|
|
44
|
+
display: flex;
|
|
45
|
+
flex-direction: column;
|
|
46
|
+
gap: var(--field-gap);
|
|
47
|
+
width: 100%;
|
|
48
|
+
}
|
|
49
|
+
@container (min-width: 24rem) {
|
|
50
|
+
.amos-js-form-skeleton-row-stack {
|
|
51
|
+
align-items: flex-start;
|
|
52
|
+
flex-direction: row;
|
|
53
|
+
gap: var(--control-gap);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
@keyframes amos-js-skeleton-pulse {
|
|
57
|
+
50% { opacity: 0.5; }
|
|
58
|
+
}
|
|
59
|
+
@media (prefers-reduced-motion: reduce) {
|
|
60
|
+
.amos-js-form-skeleton-input {
|
|
61
|
+
animation: none;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
`;function M(){if(document.getElementById(k))return;let e=document.createElement(`style`);e.id=k,e.textContent=j,document.head.appendChild(e)}function N(e,t){for(let[t,n]of Object.entries(A))e.style.setProperty(t,n);let n=t?.themeVariables;if(n)for(let[t,r]of Object.entries(n))typeof r==`string`&&r.trim()!==``&&e.style.setProperty(t,r.trim())}function P(e,t){let n=document.createElement(`div`);if(n.className=e,t)for(let e of t)n.appendChild(e);return n}function F(e,t){let n=P(`amos-js-form-skeleton-field`);t!==void 0&&(n.style.flexGrow=String(t)),e===`above`&&n.appendChild(P(`amos-js-form-skeleton-label`));let r=P(`amos-js-form-skeleton-input`);return e===`floating`&&r.classList.add(`amos-js-form-skeleton-input-floating`),n.appendChild(r),n}function I(e,t){return P(t?`amos-js-form-skeleton-row-stack`:`amos-js-form-skeleton-row`,e)}function L({labels:e,requirement:t,wrapCountryZip:n}){return t===`full`?[F(e),F(e),I([F(e,1.4),F(e,.7),F(e,.8)],!1),F(e)]:[I([F(e),F(e)],n)]}function R(e){let t=e.appearance?.labels??`above`,n=e.billingAddressRequirement??`country`;if(e.kind===`card`){let r=[F(t),I([F(t),F(t)],!1)];return e.additionalFields?.cardholderName&&r.push(F(t)),r.push(...L({labels:t,requirement:n,wrapCountryZip:!1})),r}return[F(t),I([F(t),F(t)],!0),F(t),I([F(`above`),F(`above`)],!0),...L({labels:t,requirement:n,wrapCountryZip:!0})]}function z(e){M();let t=P(`amos-js-form-skeleton`);t.setAttribute(`aria-hidden`,`true`);function n(e){N(t,e.appearance),t.replaceChildren(...R(e))}return n(e),{element:t,update:n}}var B={country:212,full:452},V=80,H={country:400,full:640};function U(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 W(e,t=`country`){let n=new URLSearchParams({token:e,billingAddressRequirement:t});return`${i(e)}/iframe/bank?${n}`}function G(e={cardholderName:!1},t=`country`){return`${(B[t]??B.country)+(e.cardholderName?V:0)}px`}function K(e=`country`){return`${H[e]??H.country}px`}function q(e,t){let n={...t};function r(t){if(t.source===e.contentWindow)switch(t.data.type){case`IFRAME_READY`:u(e),d({iframe:e,appearance:n.appearance});break;case`UPDATE_HEIGHT`:n.onHeightChange?.(t.data.height);break;case`UPDATE_APPEARANCE`:d({iframe:e,appearance:t.data.appearance});break;case`UPDATED_APPEARANCE`:n.onAppearanceReady?.();break;case`FORM_VALIDITY_CHANGE`:n.onValidityChange?.({isValid:t.data.isValid});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&&d({iframe:e,appearance:n.appearance})},destroy(){window.removeEventListener(`message`,r)}}}function J(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 Y={width:`calc(100% + 8px)`,transition:`opacity 150ms ease-in, height 200ms ease-in-out`,margin:`0 -4px`,opacity:`0`,border:`0`},X={position:`absolute`,top:`0`,left:`-4px`,width:`calc(100% + 8px)`,height:`100%`,margin:`0`,transition:`none`,pointerEvents:`none`};function Z({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,Y,{height:r}),a}function Q({host:e,iframe:t,listenerOptions:n,skeletonOptions:r}){let i=document.createElement(`div`);i.style.position=`relative`,i.style.width=`100%`,i.setAttribute(`aria-busy`,`true`);let a=z(r);Object.assign(t.style,X),i.append(a.element,t),e.appendChild(i);let o=!1,s=!1,c,l=r.appearance,u,d;function f(){return i.getBoundingClientRect().height}function p(){return c?Number.parseFloat(c):NaN}function m(){if(o)return;o=!0,d!==void 0&&(clearTimeout(d),d=void 0);let e=f(),n=p(),r=Number.isFinite(n)?Math.max(n,e):e;t.style.transition=`none`,t.style.position=``,t.style.top=``,t.style.left=``,t.style.margin=Y.margin??``,t.style.height=`${r}px`,t.style.opacity=`1`,t.style.pointerEvents=``,a.element.remove(),i.removeAttribute(`aria-busy`),u=setTimeout(()=>{t.style.transition=`height 200ms ease-in-out`},400)}function h(){if(o||!s)return;let e=p(),t=f();Number.isFinite(e)&&e>=t-2&&m()}let g=q(t,{...n,onHeightChange:e=>{c=e,o?t.style.height=e:h(),n.onHeightChange?.(e)},onAppearanceReady:()=>{s=!0,h(),!o&&d===void 0&&(d=setTimeout(()=>{m()},1500)),n.onAppearanceReady?.()}});return{iframe:t,update(e){g.update(e),!o&&`appearance`in e&&(l=e.appearance,a.update({...r,appearance:l}))},destroy(){u!==void 0&&clearTimeout(u),d!==void 0&&clearTimeout(d),g.destroy(),i.remove()}}}function $(e,t){let n=J(e),{renderToken:r,additionalFields:i={cardholderName:!1},billingAddressRequirement:a=`country`,...o}=t;return Q({host:n,iframe:Z({src:U(r,i,a),title:`Secure credit card payment method form powered by Amos`,name:`amos-credit-card-payment-method-form`,height:G(i,a)}),listenerOptions:o,skeletonOptions:{kind:`card`,appearance:o.appearance,additionalFields:i,billingAddressRequirement:a}})}function ee(e,t){let n=J(e),{renderToken:r,billingAddressRequirement:i=`country`,...a}=t;return Q({host:n,iframe:Z({src:W(r,i),title:`Secure bank account payment method form powered by Amos`,name:`amos-bank-account-payment-method-form`,height:K(i)}),listenerOptions:a,skeletonOptions:{kind:`bank`,appearance:a.appearance,billingAddressRequirement:i}})}function te(e,t){let n=J(e),{renderToken:r,...i}=t,a=Z({src:T(r),title:`Secure Google Pay button powered by Amos`,name:`amos-google-pay-button`,height:E(),allow:`payment`});n.appendChild(a);let o=D(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 ne(e,t){let n=J(e),{renderToken:r,...i}=t,a=Z({src:x(r),title:`Secure Apple Pay button powered by Amos`,name:`amos-apple-pay-button`,height:S(),allow:`payment`});n.appendChild(a);let o=w(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=w,exports.attachGooglePayButtonListeners=D,exports.attachPaymentMethodFormListeners=q,exports.confirmPaymentIntent=v,exports.confirmSetupIntent=y,exports.createMessage=c,exports.decodeJwt=r,exports.formatGooglePayPaymentData=O,exports.getApplePayButtonInitialHeight=S,exports.getApplePayButtonSrc=x,exports.getBankAccountFormInitialHeight=K,exports.getBankAccountFormSrc=W,exports.getCreditCardFormInitialHeight=G,exports.getCreditCardFormSrc=U,exports.getEmbedOrigin=i,exports.getGooglePayButtonInitialHeight=E,exports.getGooglePayButtonSrc=T,exports.mountAmosApplePayButton=ne,exports.mountAmosBankAccountPaymentMethodForm=ee,exports.mountAmosCreditCardPaymentMethodForm=$,exports.mountAmosGooglePayButton=te,exports.pickApplePayButtonElementProps=o,exports.pickGooglePayButtonElementProps=s,exports.resetForm=_,exports.sendConfirmationResult=b,exports.sendParentReadyMessage=u,exports.serializeWalletButtonStyle=a,exports.updateAmount=m,exports.updateAppearance=d,exports.updateApplePayButton=f,exports.updateGooglePayButton=p,exports.updateMerchantName=h,exports.validateForm=g;
|
package/dist/index.mjs
CHANGED
|
@@ -392,15 +392,96 @@ function O({ paymentData: e }) {
|
|
|
392
392
|
} };
|
|
393
393
|
}
|
|
394
394
|
//#endregion
|
|
395
|
+
//#region src/form-skeleton.ts
|
|
396
|
+
var k = "amos-js-form-skeleton-styles", A = {
|
|
397
|
+
"--accent": "oklch(0.97 0 0)",
|
|
398
|
+
"--radius": "0.625rem",
|
|
399
|
+
"--input-height": "2.25rem",
|
|
400
|
+
"--floating-input-height": "3.25rem",
|
|
401
|
+
"--field-gap": "1rem",
|
|
402
|
+
"--control-gap": "0.5rem",
|
|
403
|
+
"--label-font-size": "0.875rem"
|
|
404
|
+
}, j = "\n.amos-js-form-skeleton {\n box-sizing: border-box;\n container-type: inline-size;\n display: flex;\n flex-direction: column;\n gap: var(--field-gap);\n margin: 0 -4px;\n padding-block: 0.25rem;\n pointer-events: none;\n width: calc(100% + 8px);\n}\n.amos-js-form-skeleton-field {\n display: flex;\n flex: 1 1 0;\n flex-direction: column;\n min-width: 0;\n width: 100%;\n}\n.amos-js-form-skeleton-label {\n flex-shrink: 0;\n font-size: var(--label-font-size);\n height: 1.75rem;\n line-height: 1.75rem;\n}\n.amos-js-form-skeleton-input {\n animation: amos-js-skeleton-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;\n background: var(--accent);\n border-radius: calc(var(--radius) * 0.8);\n box-sizing: border-box;\n height: var(--input-height);\n width: 100%;\n}\n.amos-js-form-skeleton-input-floating {\n height: var(--floating-input-height);\n}\n.amos-js-form-skeleton-row {\n align-items: flex-start;\n display: flex;\n gap: var(--control-gap);\n width: 100%;\n}\n.amos-js-form-skeleton-row-stack {\n display: flex;\n flex-direction: column;\n gap: var(--field-gap);\n width: 100%;\n}\n@container (min-width: 24rem) {\n .amos-js-form-skeleton-row-stack {\n align-items: flex-start;\n flex-direction: row;\n gap: var(--control-gap);\n }\n}\n@keyframes amos-js-skeleton-pulse {\n 50% { opacity: 0.5; }\n}\n@media (prefers-reduced-motion: reduce) {\n .amos-js-form-skeleton-input {\n animation: none;\n }\n}\n";
|
|
405
|
+
function M() {
|
|
406
|
+
if (document.getElementById(k)) return;
|
|
407
|
+
let e = document.createElement("style");
|
|
408
|
+
e.id = k, e.textContent = j, document.head.appendChild(e);
|
|
409
|
+
}
|
|
410
|
+
function N(e, t) {
|
|
411
|
+
for (let [t, n] of Object.entries(A)) e.style.setProperty(t, n);
|
|
412
|
+
let n = t?.themeVariables;
|
|
413
|
+
if (n) for (let [t, r] of Object.entries(n)) typeof r == "string" && r.trim() !== "" && e.style.setProperty(t, r.trim());
|
|
414
|
+
}
|
|
415
|
+
function P(e, t) {
|
|
416
|
+
let n = document.createElement("div");
|
|
417
|
+
if (n.className = e, t) for (let e of t) n.appendChild(e);
|
|
418
|
+
return n;
|
|
419
|
+
}
|
|
420
|
+
function F(e, t) {
|
|
421
|
+
let n = P("amos-js-form-skeleton-field");
|
|
422
|
+
t !== void 0 && (n.style.flexGrow = String(t)), e === "above" && n.appendChild(P("amos-js-form-skeleton-label"));
|
|
423
|
+
let r = P("amos-js-form-skeleton-input");
|
|
424
|
+
return e === "floating" && r.classList.add("amos-js-form-skeleton-input-floating"), n.appendChild(r), n;
|
|
425
|
+
}
|
|
426
|
+
function I(e, t) {
|
|
427
|
+
return P(t ? "amos-js-form-skeleton-row-stack" : "amos-js-form-skeleton-row", e);
|
|
428
|
+
}
|
|
429
|
+
function L({ labels: e, requirement: t, wrapCountryZip: n }) {
|
|
430
|
+
return t === "full" ? [
|
|
431
|
+
F(e),
|
|
432
|
+
F(e),
|
|
433
|
+
I([
|
|
434
|
+
F(e, 1.4),
|
|
435
|
+
F(e, .7),
|
|
436
|
+
F(e, .8)
|
|
437
|
+
], !1),
|
|
438
|
+
F(e)
|
|
439
|
+
] : [I([F(e), F(e)], n)];
|
|
440
|
+
}
|
|
441
|
+
function R(e) {
|
|
442
|
+
let t = e.appearance?.labels ?? "above", n = e.billingAddressRequirement ?? "country";
|
|
443
|
+
if (e.kind === "card") {
|
|
444
|
+
let r = [F(t), I([F(t), F(t)], !1)];
|
|
445
|
+
return e.additionalFields?.cardholderName && r.push(F(t)), r.push(...L({
|
|
446
|
+
labels: t,
|
|
447
|
+
requirement: n,
|
|
448
|
+
wrapCountryZip: !1
|
|
449
|
+
})), r;
|
|
450
|
+
}
|
|
451
|
+
return [
|
|
452
|
+
F(t),
|
|
453
|
+
I([F(t), F(t)], !0),
|
|
454
|
+
F(t),
|
|
455
|
+
I([F("above"), F("above")], !0),
|
|
456
|
+
...L({
|
|
457
|
+
labels: t,
|
|
458
|
+
requirement: n,
|
|
459
|
+
wrapCountryZip: !0
|
|
460
|
+
})
|
|
461
|
+
];
|
|
462
|
+
}
|
|
463
|
+
function z(e) {
|
|
464
|
+
M();
|
|
465
|
+
let t = P("amos-js-form-skeleton");
|
|
466
|
+
t.setAttribute("aria-hidden", "true");
|
|
467
|
+
function n(e) {
|
|
468
|
+
N(t, e.appearance), t.replaceChildren(...R(e));
|
|
469
|
+
}
|
|
470
|
+
return n(e), {
|
|
471
|
+
element: t,
|
|
472
|
+
update: n
|
|
473
|
+
};
|
|
474
|
+
}
|
|
475
|
+
//#endregion
|
|
395
476
|
//#region src/payment-method-form.ts
|
|
396
|
-
var
|
|
477
|
+
var B = {
|
|
397
478
|
country: 212,
|
|
398
479
|
full: 452
|
|
399
|
-
},
|
|
480
|
+
}, V = 80, H = {
|
|
400
481
|
country: 400,
|
|
401
482
|
full: 640
|
|
402
483
|
};
|
|
403
|
-
function
|
|
484
|
+
function U(e, t = { cardholderName: !1 }, n = "country") {
|
|
404
485
|
let r = Object.entries(t).filter(([, e]) => e).map(([e]) => e).join(","), a = new URLSearchParams({
|
|
405
486
|
token: e,
|
|
406
487
|
additionalFields: r,
|
|
@@ -408,20 +489,20 @@ function M(e, t = { cardholderName: !1 }, n = "country") {
|
|
|
408
489
|
});
|
|
409
490
|
return `${i(e)}/iframe/card?${a}`;
|
|
410
491
|
}
|
|
411
|
-
function
|
|
492
|
+
function W(e, t = "country") {
|
|
412
493
|
let n = new URLSearchParams({
|
|
413
494
|
token: e,
|
|
414
495
|
billingAddressRequirement: t
|
|
415
496
|
});
|
|
416
497
|
return `${i(e)}/iframe/bank?${n}`;
|
|
417
498
|
}
|
|
418
|
-
function
|
|
419
|
-
return `${(
|
|
499
|
+
function G(e = { cardholderName: !1 }, t = "country") {
|
|
500
|
+
return `${(B[t] ?? B.country) + (e.cardholderName ? V : 0)}px`;
|
|
420
501
|
}
|
|
421
|
-
function
|
|
422
|
-
return `${
|
|
502
|
+
function K(e = "country") {
|
|
503
|
+
return `${H[e] ?? H.country}px`;
|
|
423
504
|
}
|
|
424
|
-
function
|
|
505
|
+
function q(e, t) {
|
|
425
506
|
let n = { ...t };
|
|
426
507
|
function r(t) {
|
|
427
508
|
if (t.source === e.contentWindow) switch (t.data.type) {
|
|
@@ -443,6 +524,9 @@ function I(e, t) {
|
|
|
443
524
|
case "UPDATED_APPEARANCE":
|
|
444
525
|
n.onAppearanceReady?.();
|
|
445
526
|
break;
|
|
527
|
+
case "FORM_VALIDITY_CHANGE":
|
|
528
|
+
n.onValidityChange?.({ isValid: t.data.isValid });
|
|
529
|
+
break;
|
|
446
530
|
case "CONFIRMATION_RESULT": n.onResult(t.data.result);
|
|
447
531
|
}
|
|
448
532
|
}
|
|
@@ -464,7 +548,7 @@ function I(e, t) {
|
|
|
464
548
|
}
|
|
465
549
|
//#endregion
|
|
466
550
|
//#region src/mount.ts
|
|
467
|
-
function
|
|
551
|
+
function J(e) {
|
|
468
552
|
if (typeof e == "string") {
|
|
469
553
|
let t = document.querySelector(e);
|
|
470
554
|
if (!(t instanceof HTMLElement)) throw Error(`[amos-js] Container "${e}" did not match any HTMLElement.`);
|
|
@@ -472,69 +556,114 @@ function L(e) {
|
|
|
472
556
|
}
|
|
473
557
|
return e;
|
|
474
558
|
}
|
|
475
|
-
var
|
|
559
|
+
var Y = {
|
|
476
560
|
width: "calc(100% + 8px)",
|
|
477
561
|
transition: "opacity 150ms ease-in, height 200ms ease-in-out",
|
|
478
562
|
margin: "0 -4px",
|
|
479
563
|
opacity: "0",
|
|
480
564
|
border: "0"
|
|
565
|
+
}, X = {
|
|
566
|
+
position: "absolute",
|
|
567
|
+
top: "0",
|
|
568
|
+
left: "-4px",
|
|
569
|
+
width: "calc(100% + 8px)",
|
|
570
|
+
height: "100%",
|
|
571
|
+
margin: "0",
|
|
572
|
+
transition: "none",
|
|
573
|
+
pointerEvents: "none"
|
|
481
574
|
};
|
|
482
|
-
function
|
|
575
|
+
function Z({ src: e, title: t, name: n, height: r, allow: i }) {
|
|
483
576
|
let a = document.createElement("iframe");
|
|
484
|
-
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,
|
|
485
|
-
}
|
|
486
|
-
function
|
|
487
|
-
let
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
577
|
+
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, Y, { height: r }), a;
|
|
578
|
+
}
|
|
579
|
+
function Q({ host: e, iframe: t, listenerOptions: n, skeletonOptions: r }) {
|
|
580
|
+
let i = document.createElement("div");
|
|
581
|
+
i.style.position = "relative", i.style.width = "100%", i.setAttribute("aria-busy", "true");
|
|
582
|
+
let a = z(r);
|
|
583
|
+
Object.assign(t.style, X), i.append(a.element, t), e.appendChild(i);
|
|
584
|
+
let o = !1, s = !1, c, l = r.appearance, u, d;
|
|
585
|
+
function f() {
|
|
586
|
+
return i.getBoundingClientRect().height;
|
|
587
|
+
}
|
|
588
|
+
function p() {
|
|
589
|
+
return c ? Number.parseFloat(c) : NaN;
|
|
590
|
+
}
|
|
591
|
+
function m() {
|
|
592
|
+
if (o) return;
|
|
593
|
+
o = !0, d !== void 0 && (clearTimeout(d), d = void 0);
|
|
594
|
+
let e = f(), n = p(), r = Number.isFinite(n) ? Math.max(n, e) : e;
|
|
595
|
+
t.style.transition = "none", t.style.position = "", t.style.top = "", t.style.left = "", t.style.margin = Y.margin ?? "", t.style.height = `${r}px`, t.style.opacity = "1", t.style.pointerEvents = "", a.element.remove(), i.removeAttribute("aria-busy"), u = setTimeout(() => {
|
|
596
|
+
t.style.transition = "height 200ms ease-in-out";
|
|
597
|
+
}, 400);
|
|
598
|
+
}
|
|
599
|
+
function h() {
|
|
600
|
+
if (o || !s) return;
|
|
601
|
+
let e = p(), t = f();
|
|
602
|
+
Number.isFinite(e) && e >= t - 2 && m();
|
|
603
|
+
}
|
|
604
|
+
let g = q(t, {
|
|
605
|
+
...n,
|
|
496
606
|
onHeightChange: (e) => {
|
|
497
|
-
|
|
607
|
+
c = e, o ? t.style.height = e : h(), n.onHeightChange?.(e);
|
|
498
608
|
},
|
|
499
609
|
onAppearanceReady: () => {
|
|
500
|
-
s
|
|
610
|
+
s = !0, h(), !o && d === void 0 && (d = setTimeout(() => {
|
|
611
|
+
m();
|
|
612
|
+
}, 1500)), n.onAppearanceReady?.();
|
|
501
613
|
}
|
|
502
614
|
});
|
|
503
615
|
return {
|
|
504
|
-
iframe:
|
|
505
|
-
update
|
|
616
|
+
iframe: t,
|
|
617
|
+
update(e) {
|
|
618
|
+
g.update(e), !o && "appearance" in e && (l = e.appearance, a.update({
|
|
619
|
+
...r,
|
|
620
|
+
appearance: l
|
|
621
|
+
}));
|
|
622
|
+
},
|
|
506
623
|
destroy() {
|
|
507
|
-
|
|
624
|
+
u !== void 0 && clearTimeout(u), d !== void 0 && clearTimeout(d), g.destroy(), i.remove();
|
|
508
625
|
}
|
|
509
626
|
};
|
|
510
627
|
}
|
|
511
|
-
function
|
|
512
|
-
let n =
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
628
|
+
function $(e, t) {
|
|
629
|
+
let n = J(e), { renderToken: r, additionalFields: i = { cardholderName: !1 }, billingAddressRequirement: a = "country", ...o } = t;
|
|
630
|
+
return Q({
|
|
631
|
+
host: n,
|
|
632
|
+
iframe: Z({
|
|
633
|
+
src: U(r, i, a),
|
|
634
|
+
title: "Secure credit card payment method form powered by Amos",
|
|
635
|
+
name: "amos-credit-card-payment-method-form",
|
|
636
|
+
height: G(i, a)
|
|
637
|
+
}),
|
|
638
|
+
listenerOptions: o,
|
|
639
|
+
skeletonOptions: {
|
|
640
|
+
kind: "card",
|
|
641
|
+
appearance: o.appearance,
|
|
642
|
+
additionalFields: i,
|
|
643
|
+
billingAddressRequirement: a
|
|
526
644
|
}
|
|
527
645
|
});
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
646
|
+
}
|
|
647
|
+
function ee(e, t) {
|
|
648
|
+
let n = J(e), { renderToken: r, billingAddressRequirement: i = "country", ...a } = t;
|
|
649
|
+
return Q({
|
|
650
|
+
host: n,
|
|
651
|
+
iframe: Z({
|
|
652
|
+
src: W(r, i),
|
|
653
|
+
title: "Secure bank account payment method form powered by Amos",
|
|
654
|
+
name: "amos-bank-account-payment-method-form",
|
|
655
|
+
height: K(i)
|
|
656
|
+
}),
|
|
657
|
+
listenerOptions: a,
|
|
658
|
+
skeletonOptions: {
|
|
659
|
+
kind: "bank",
|
|
660
|
+
appearance: a.appearance,
|
|
661
|
+
billingAddressRequirement: i
|
|
533
662
|
}
|
|
534
|
-
};
|
|
663
|
+
});
|
|
535
664
|
}
|
|
536
|
-
function
|
|
537
|
-
let n =
|
|
665
|
+
function te(e, t) {
|
|
666
|
+
let n = J(e), { renderToken: r, ...i } = t, a = Z({
|
|
538
667
|
src: T(r),
|
|
539
668
|
title: "Secure Google Pay button powered by Amos",
|
|
540
669
|
name: "amos-google-pay-button",
|
|
@@ -559,8 +688,8 @@ function H(e, t) {
|
|
|
559
688
|
}
|
|
560
689
|
};
|
|
561
690
|
}
|
|
562
|
-
function
|
|
563
|
-
let n =
|
|
691
|
+
function ne(e, t) {
|
|
692
|
+
let n = J(e), { renderToken: r, ...i } = t, a = Z({
|
|
564
693
|
src: x(r),
|
|
565
694
|
title: "Secure Apple Pay button powered by Amos",
|
|
566
695
|
name: "amos-apple-pay-button",
|
|
@@ -586,4 +715,4 @@ function U(e, t) {
|
|
|
586
715
|
};
|
|
587
716
|
}
|
|
588
717
|
//#endregion
|
|
589
|
-
export { w as attachApplePayButtonListeners, D as attachGooglePayButtonListeners,
|
|
718
|
+
export { w as attachApplePayButtonListeners, D as attachGooglePayButtonListeners, q as attachPaymentMethodFormListeners, v as confirmPaymentIntent, y as confirmSetupIntent, c as createMessage, r as decodeJwt, O as formatGooglePayPaymentData, S as getApplePayButtonInitialHeight, x as getApplePayButtonSrc, K as getBankAccountFormInitialHeight, W as getBankAccountFormSrc, G as getCreditCardFormInitialHeight, U as getCreditCardFormSrc, i as getEmbedOrigin, E as getGooglePayButtonInitialHeight, T as getGooglePayButtonSrc, ne as mountAmosApplePayButton, ee as mountAmosBankAccountPaymentMethodForm, $ as mountAmosCreditCardPaymentMethodForm, te as mountAmosGooglePayButton, o as pickApplePayButtonElementProps, s as pickGooglePayButtonElementProps, _ as resetForm, b as sendConfirmationResult, u as sendParentReadyMessage, a as serializeWalletButtonStyle, m as updateAmount, d as updateAppearance, f as updateApplePayButton, p as updateGooglePayButton, h as updateMerchantName, g as validateForm };
|
package/dist/mount.d.ts
CHANGED
|
@@ -45,6 +45,9 @@ export type AmosPaymentMethodFormMountController = PaymentMethodFormController &
|
|
|
45
45
|
* element. Returns a controller exposing the underlying iframe, an
|
|
46
46
|
* `update()` method, and a `destroy()` method.
|
|
47
47
|
*
|
|
48
|
+
* A field-shaped skeleton is shown immediately and replaced by the
|
|
49
|
+
* iframe once appearance is applied.
|
|
50
|
+
*
|
|
48
51
|
* Use the returned `controller.iframe` when calling
|
|
49
52
|
* {@link validateForm}, {@link confirmPaymentIntent}, or
|
|
50
53
|
* {@link confirmSetupIntent}.
|
|
@@ -73,6 +76,9 @@ export type AmosBankAccountPaymentMethodFormOptions = PaymentMethodFormListenerO
|
|
|
73
76
|
* element. Returns a controller exposing the underlying iframe, an
|
|
74
77
|
* `update()` method, and a `destroy()` method.
|
|
75
78
|
*
|
|
79
|
+
* A field-shaped skeleton is shown immediately and replaced by the
|
|
80
|
+
* iframe once appearance is applied.
|
|
81
|
+
*
|
|
76
82
|
* Use the returned `controller.iframe` when calling
|
|
77
83
|
* {@link validateForm}, {@link confirmPaymentIntent}, or
|
|
78
84
|
* {@link confirmSetupIntent}.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Appearance, ConfirmationResult } from './types';
|
|
1
|
+
import { Appearance, ConfirmationResult, PaymentMethodFormValidityChangeEvent } from './types';
|
|
2
2
|
/**
|
|
3
3
|
* The additional fields beyond the standard card number, expiration
|
|
4
4
|
* date, CVV, and billing address fields that are required to be filled
|
|
@@ -59,6 +59,12 @@ export type PaymentMethodFormListenerOptions = {
|
|
|
59
59
|
* iframe's opacity from `0` to `1` to fade it in.
|
|
60
60
|
*/
|
|
61
61
|
onAppearanceReady?: () => void;
|
|
62
|
+
/**
|
|
63
|
+
* Called when card/bank form validity changes. `isValid` is true
|
|
64
|
+
* when all required fields are present and valid. Does not include
|
|
65
|
+
* PCI data. Use this to enable or disable a host checkout button.
|
|
66
|
+
*/
|
|
67
|
+
onValidityChange?: (event: PaymentMethodFormValidityChangeEvent) => void;
|
|
62
68
|
/**
|
|
63
69
|
* Called when the interactive confirmation flow finishes (success or
|
|
64
70
|
* terminal failure). Not settlement proof — verify via webhooks.
|
package/dist/types.d.ts
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
import { components } from '@amos.com/node';
|
|
2
|
+
/**
|
|
3
|
+
* PCI-safe snapshot of card/bank form validity, posted when HTML
|
|
4
|
+
* constraint validation changes. Use this to enable or disable a host
|
|
5
|
+
* checkout button.
|
|
6
|
+
*/
|
|
7
|
+
export type PaymentMethodFormValidityChangeEvent = {
|
|
8
|
+
isValid: boolean;
|
|
9
|
+
};
|
|
2
10
|
/**
|
|
3
11
|
* Why an interactive confirmation attempt ended with `status: "incomplete"`.
|
|
4
12
|
*
|
|
@@ -243,6 +251,14 @@ export type Message = {
|
|
|
243
251
|
} | {
|
|
244
252
|
/** Parent → embed: clear all form field values and API errors. */
|
|
245
253
|
type: "RESET_FORM";
|
|
254
|
+
} | {
|
|
255
|
+
/**
|
|
256
|
+
* Embed → parent: card/bank form validity changed.
|
|
257
|
+
* `isValid` is true when all required fields are present and
|
|
258
|
+
* valid. Does not include PCI data.
|
|
259
|
+
*/
|
|
260
|
+
type: "FORM_VALIDITY_CHANGE";
|
|
261
|
+
isValid: boolean;
|
|
246
262
|
};
|
|
247
263
|
/**
|
|
248
264
|
* Identity helper that brands an object as a typed `Message`.
|
package/package.json
CHANGED
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
BillingAddressRequirement,
|
|
3
|
+
CreditCardAdditionalFields,
|
|
4
|
+
} from "./payment-method-form";
|
|
5
|
+
import type { Appearance, AppearanceLabels, ThemeVariable } from "./types";
|
|
6
|
+
|
|
7
|
+
const STYLE_ID = "amos-js-form-skeleton-styles";
|
|
8
|
+
|
|
9
|
+
const SKELETON_THEME_DEFAULTS: Record<string, string> = {
|
|
10
|
+
"--accent": "oklch(0.97 0 0)",
|
|
11
|
+
"--radius": "0.625rem",
|
|
12
|
+
"--input-height": "2.25rem",
|
|
13
|
+
"--floating-input-height": "3.25rem",
|
|
14
|
+
"--field-gap": "1rem",
|
|
15
|
+
"--control-gap": "0.5rem",
|
|
16
|
+
"--label-font-size": "0.875rem",
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const SKELETON_STYLES = `
|
|
20
|
+
.amos-js-form-skeleton {
|
|
21
|
+
box-sizing: border-box;
|
|
22
|
+
container-type: inline-size;
|
|
23
|
+
display: flex;
|
|
24
|
+
flex-direction: column;
|
|
25
|
+
gap: var(--field-gap);
|
|
26
|
+
margin: 0 -4px;
|
|
27
|
+
padding-block: 0.25rem;
|
|
28
|
+
pointer-events: none;
|
|
29
|
+
width: calc(100% + 8px);
|
|
30
|
+
}
|
|
31
|
+
.amos-js-form-skeleton-field {
|
|
32
|
+
display: flex;
|
|
33
|
+
flex: 1 1 0;
|
|
34
|
+
flex-direction: column;
|
|
35
|
+
min-width: 0;
|
|
36
|
+
width: 100%;
|
|
37
|
+
}
|
|
38
|
+
.amos-js-form-skeleton-label {
|
|
39
|
+
flex-shrink: 0;
|
|
40
|
+
font-size: var(--label-font-size);
|
|
41
|
+
height: 1.75rem;
|
|
42
|
+
line-height: 1.75rem;
|
|
43
|
+
}
|
|
44
|
+
.amos-js-form-skeleton-input {
|
|
45
|
+
animation: amos-js-skeleton-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
|
|
46
|
+
background: var(--accent);
|
|
47
|
+
border-radius: calc(var(--radius) * 0.8);
|
|
48
|
+
box-sizing: border-box;
|
|
49
|
+
height: var(--input-height);
|
|
50
|
+
width: 100%;
|
|
51
|
+
}
|
|
52
|
+
.amos-js-form-skeleton-input-floating {
|
|
53
|
+
height: var(--floating-input-height);
|
|
54
|
+
}
|
|
55
|
+
.amos-js-form-skeleton-row {
|
|
56
|
+
align-items: flex-start;
|
|
57
|
+
display: flex;
|
|
58
|
+
gap: var(--control-gap);
|
|
59
|
+
width: 100%;
|
|
60
|
+
}
|
|
61
|
+
.amos-js-form-skeleton-row-stack {
|
|
62
|
+
display: flex;
|
|
63
|
+
flex-direction: column;
|
|
64
|
+
gap: var(--field-gap);
|
|
65
|
+
width: 100%;
|
|
66
|
+
}
|
|
67
|
+
@container (min-width: 24rem) {
|
|
68
|
+
.amos-js-form-skeleton-row-stack {
|
|
69
|
+
align-items: flex-start;
|
|
70
|
+
flex-direction: row;
|
|
71
|
+
gap: var(--control-gap);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
@keyframes amos-js-skeleton-pulse {
|
|
75
|
+
50% { opacity: 0.5; }
|
|
76
|
+
}
|
|
77
|
+
@media (prefers-reduced-motion: reduce) {
|
|
78
|
+
.amos-js-form-skeleton-input {
|
|
79
|
+
animation: none;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
`;
|
|
83
|
+
|
|
84
|
+
function ensureSkeletonStyles(): void {
|
|
85
|
+
if (document.getElementById(STYLE_ID)) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
const style = document.createElement("style");
|
|
89
|
+
style.id = STYLE_ID;
|
|
90
|
+
style.textContent = SKELETON_STYLES;
|
|
91
|
+
document.head.appendChild(style);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function applyTheme(
|
|
95
|
+
element: HTMLElement,
|
|
96
|
+
appearance: Appearance | undefined,
|
|
97
|
+
): void {
|
|
98
|
+
for (const [property, value] of Object.entries(SKELETON_THEME_DEFAULTS)) {
|
|
99
|
+
element.style.setProperty(property, value);
|
|
100
|
+
}
|
|
101
|
+
const themeVariables = appearance?.themeVariables;
|
|
102
|
+
if (!themeVariables) {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
for (const [property, value] of Object.entries(themeVariables) as Array<
|
|
106
|
+
[ThemeVariable, string | undefined]
|
|
107
|
+
>) {
|
|
108
|
+
if (typeof value === "string" && value.trim() !== "") {
|
|
109
|
+
element.style.setProperty(property, value.trim());
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function div(className: string, children?: Array<HTMLElement>): HTMLDivElement {
|
|
115
|
+
const node = document.createElement("div");
|
|
116
|
+
node.className = className;
|
|
117
|
+
if (children) {
|
|
118
|
+
for (const child of children) {
|
|
119
|
+
node.appendChild(child);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return node;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function field(labels: AppearanceLabels, grow?: number): HTMLDivElement {
|
|
126
|
+
const wrap = div("amos-js-form-skeleton-field");
|
|
127
|
+
if (grow !== undefined) {
|
|
128
|
+
wrap.style.flexGrow = String(grow);
|
|
129
|
+
}
|
|
130
|
+
if (labels === "above") {
|
|
131
|
+
wrap.appendChild(div("amos-js-form-skeleton-label"));
|
|
132
|
+
}
|
|
133
|
+
const input = div("amos-js-form-skeleton-input");
|
|
134
|
+
if (labels === "floating") {
|
|
135
|
+
input.classList.add("amos-js-form-skeleton-input-floating");
|
|
136
|
+
}
|
|
137
|
+
wrap.appendChild(input);
|
|
138
|
+
return wrap;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function row(children: Array<HTMLElement>, wrapAtSm: boolean): HTMLDivElement {
|
|
142
|
+
return div(
|
|
143
|
+
wrapAtSm ? "amos-js-form-skeleton-row-stack" : "amos-js-form-skeleton-row",
|
|
144
|
+
children,
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function billingFields({
|
|
149
|
+
labels,
|
|
150
|
+
requirement,
|
|
151
|
+
wrapCountryZip,
|
|
152
|
+
}: {
|
|
153
|
+
labels: AppearanceLabels;
|
|
154
|
+
requirement: BillingAddressRequirement;
|
|
155
|
+
wrapCountryZip: boolean;
|
|
156
|
+
}): Array<HTMLElement> {
|
|
157
|
+
if (requirement === "full") {
|
|
158
|
+
return [
|
|
159
|
+
field(labels),
|
|
160
|
+
field(labels),
|
|
161
|
+
row([field(labels, 1.4), field(labels, 0.7), field(labels, 0.8)], false),
|
|
162
|
+
field(labels),
|
|
163
|
+
];
|
|
164
|
+
}
|
|
165
|
+
return [row([field(labels), field(labels)], wrapCountryZip)];
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export type PaymentMethodFormSkeletonKind = "card" | "bank";
|
|
169
|
+
|
|
170
|
+
export type PaymentMethodFormSkeletonOptions = {
|
|
171
|
+
kind: PaymentMethodFormSkeletonKind;
|
|
172
|
+
appearance?: Appearance;
|
|
173
|
+
additionalFields?: CreditCardAdditionalFields;
|
|
174
|
+
billingAddressRequirement?: BillingAddressRequirement;
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
function buildChildren(
|
|
178
|
+
options: PaymentMethodFormSkeletonOptions,
|
|
179
|
+
): Array<HTMLElement> {
|
|
180
|
+
const labels = options.appearance?.labels ?? "above";
|
|
181
|
+
const billingAddressRequirement =
|
|
182
|
+
options.billingAddressRequirement ?? "country";
|
|
183
|
+
|
|
184
|
+
if (options.kind === "card") {
|
|
185
|
+
const children: Array<HTMLElement> = [
|
|
186
|
+
field(labels),
|
|
187
|
+
row([field(labels), field(labels)], false),
|
|
188
|
+
];
|
|
189
|
+
if (options.additionalFields?.cardholderName) {
|
|
190
|
+
children.push(field(labels));
|
|
191
|
+
}
|
|
192
|
+
children.push(
|
|
193
|
+
...billingFields({
|
|
194
|
+
labels,
|
|
195
|
+
requirement: billingAddressRequirement,
|
|
196
|
+
wrapCountryZip: false,
|
|
197
|
+
}),
|
|
198
|
+
);
|
|
199
|
+
return children;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
return [
|
|
203
|
+
field(labels),
|
|
204
|
+
row([field(labels), field(labels)], true),
|
|
205
|
+
field(labels),
|
|
206
|
+
row([field("above"), field("above")], true),
|
|
207
|
+
...billingFields({
|
|
208
|
+
labels,
|
|
209
|
+
requirement: billingAddressRequirement,
|
|
210
|
+
wrapCountryZip: true,
|
|
211
|
+
}),
|
|
212
|
+
];
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
export type PaymentMethodFormSkeleton = {
|
|
216
|
+
element: HTMLElement;
|
|
217
|
+
update: (options: PaymentMethodFormSkeletonOptions) => void;
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Host-page placeholder that mirrors card/bank field layout using the
|
|
222
|
+
* same appearance variables the iframe will apply. Shown immediately
|
|
223
|
+
* while the iframe document loads, then removed when appearance is
|
|
224
|
+
* ready.
|
|
225
|
+
*/
|
|
226
|
+
export function createPaymentMethodFormSkeleton(
|
|
227
|
+
options: PaymentMethodFormSkeletonOptions,
|
|
228
|
+
): PaymentMethodFormSkeleton {
|
|
229
|
+
ensureSkeletonStyles();
|
|
230
|
+
const element = div("amos-js-form-skeleton");
|
|
231
|
+
element.setAttribute("aria-hidden", "true");
|
|
232
|
+
|
|
233
|
+
function render(next: PaymentMethodFormSkeletonOptions): void {
|
|
234
|
+
applyTheme(element, next.appearance);
|
|
235
|
+
element.replaceChildren(...buildChildren(next));
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
render(options);
|
|
239
|
+
|
|
240
|
+
return {
|
|
241
|
+
element,
|
|
242
|
+
update: render,
|
|
243
|
+
};
|
|
244
|
+
}
|
package/src/index.ts
CHANGED
package/src/mount.ts
CHANGED
|
@@ -5,6 +5,10 @@ import {
|
|
|
5
5
|
getApplePayButtonInitialHeight,
|
|
6
6
|
getApplePayButtonSrc,
|
|
7
7
|
} from "./apple-pay";
|
|
8
|
+
import {
|
|
9
|
+
createPaymentMethodFormSkeleton,
|
|
10
|
+
type PaymentMethodFormSkeletonOptions,
|
|
11
|
+
} from "./form-skeleton";
|
|
8
12
|
import {
|
|
9
13
|
attachGooglePayButtonListeners,
|
|
10
14
|
type GooglePayButtonController,
|
|
@@ -47,6 +51,18 @@ const SHARED_IFRAME_STYLE: Partial<CSSStyleDeclaration> = {
|
|
|
47
51
|
border: "0",
|
|
48
52
|
};
|
|
49
53
|
|
|
54
|
+
const SKELETON_IFRAME_STYLE: Partial<CSSStyleDeclaration> = {
|
|
55
|
+
position: "absolute",
|
|
56
|
+
top: "0",
|
|
57
|
+
left: "-4px",
|
|
58
|
+
width: "calc(100% + 8px)",
|
|
59
|
+
height: "100%",
|
|
60
|
+
margin: "0",
|
|
61
|
+
// Do not interpolate opacity while the skeleton is showing.
|
|
62
|
+
transition: "none",
|
|
63
|
+
pointerEvents: "none",
|
|
64
|
+
};
|
|
65
|
+
|
|
50
66
|
function createIframe({
|
|
51
67
|
src,
|
|
52
68
|
title,
|
|
@@ -73,6 +89,133 @@ function createIframe({
|
|
|
73
89
|
return iframe;
|
|
74
90
|
}
|
|
75
91
|
|
|
92
|
+
function mountPaymentMethodFormWithSkeleton({
|
|
93
|
+
host,
|
|
94
|
+
iframe,
|
|
95
|
+
listenerOptions,
|
|
96
|
+
skeletonOptions,
|
|
97
|
+
}: {
|
|
98
|
+
host: HTMLElement;
|
|
99
|
+
iframe: HTMLIFrameElement;
|
|
100
|
+
listenerOptions: PaymentMethodFormListenerOptions;
|
|
101
|
+
skeletonOptions: PaymentMethodFormSkeletonOptions;
|
|
102
|
+
}): AmosPaymentMethodFormMountController {
|
|
103
|
+
const wrapper = document.createElement("div");
|
|
104
|
+
wrapper.style.position = "relative";
|
|
105
|
+
wrapper.style.width = "100%";
|
|
106
|
+
wrapper.setAttribute("aria-busy", "true");
|
|
107
|
+
|
|
108
|
+
const skeleton = createPaymentMethodFormSkeleton(skeletonOptions);
|
|
109
|
+
Object.assign(iframe.style, SKELETON_IFRAME_STYLE);
|
|
110
|
+
|
|
111
|
+
wrapper.append(skeleton.element, iframe);
|
|
112
|
+
host.appendChild(wrapper);
|
|
113
|
+
|
|
114
|
+
let revealed = false;
|
|
115
|
+
let appearanceReady = false;
|
|
116
|
+
let lastHeight: string | undefined;
|
|
117
|
+
let appearance = skeletonOptions.appearance;
|
|
118
|
+
let heightTransitionTimer: ReturnType<typeof setTimeout> | undefined;
|
|
119
|
+
let fallbackRevealTimer: ReturnType<typeof setTimeout> | undefined;
|
|
120
|
+
|
|
121
|
+
function skeletonHeightPx(): number {
|
|
122
|
+
return wrapper.getBoundingClientRect().height;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function reportedHeightPx(): number {
|
|
126
|
+
return lastHeight ? Number.parseFloat(lastHeight) : Number.NaN;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function reveal(): void {
|
|
130
|
+
if (revealed) {
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
revealed = true;
|
|
134
|
+
if (fallbackRevealTimer !== undefined) {
|
|
135
|
+
clearTimeout(fallbackRevealTimer);
|
|
136
|
+
fallbackRevealTimer = undefined;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const skeletonPx = skeletonHeightPx();
|
|
140
|
+
const reportedPx = reportedHeightPx();
|
|
141
|
+
const revealPx = Number.isFinite(reportedPx)
|
|
142
|
+
? Math.max(reportedPx, skeletonPx)
|
|
143
|
+
: skeletonPx;
|
|
144
|
+
|
|
145
|
+
iframe.style.transition = "none";
|
|
146
|
+
iframe.style.position = "";
|
|
147
|
+
iframe.style.top = "";
|
|
148
|
+
iframe.style.left = "";
|
|
149
|
+
iframe.style.margin = SHARED_IFRAME_STYLE.margin ?? "";
|
|
150
|
+
iframe.style.height = `${revealPx}px`;
|
|
151
|
+
iframe.style.opacity = "1";
|
|
152
|
+
iframe.style.pointerEvents = "";
|
|
153
|
+
skeleton.element.remove();
|
|
154
|
+
wrapper.removeAttribute("aria-busy");
|
|
155
|
+
|
|
156
|
+
// Height easing is for later layout changes (ZIP show/hide, errors),
|
|
157
|
+
// not the first paint — that would animate the last row into place.
|
|
158
|
+
heightTransitionTimer = setTimeout(() => {
|
|
159
|
+
iframe.style.transition = "height 200ms ease-in-out";
|
|
160
|
+
}, 400);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function tryReveal(): void {
|
|
164
|
+
if (revealed || !appearanceReady) {
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
const reportedPx = reportedHeightPx();
|
|
168
|
+
const skeletonPx = skeletonHeightPx();
|
|
169
|
+
if (Number.isFinite(reportedPx) && reportedPx >= skeletonPx - 2) {
|
|
170
|
+
reveal();
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const controller = attachPaymentMethodFormListeners(iframe, {
|
|
175
|
+
...listenerOptions,
|
|
176
|
+
onHeightChange: (height) => {
|
|
177
|
+
lastHeight = height;
|
|
178
|
+
if (revealed) {
|
|
179
|
+
iframe.style.height = height;
|
|
180
|
+
} else {
|
|
181
|
+
tryReveal();
|
|
182
|
+
}
|
|
183
|
+
listenerOptions.onHeightChange?.(height);
|
|
184
|
+
},
|
|
185
|
+
onAppearanceReady: () => {
|
|
186
|
+
appearanceReady = true;
|
|
187
|
+
tryReveal();
|
|
188
|
+
if (!revealed && fallbackRevealTimer === undefined) {
|
|
189
|
+
fallbackRevealTimer = setTimeout(() => {
|
|
190
|
+
reveal();
|
|
191
|
+
}, 1500);
|
|
192
|
+
}
|
|
193
|
+
listenerOptions.onAppearanceReady?.();
|
|
194
|
+
},
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
return {
|
|
198
|
+
iframe,
|
|
199
|
+
update(patch) {
|
|
200
|
+
controller.update(patch);
|
|
201
|
+
if (!revealed && "appearance" in patch) {
|
|
202
|
+
appearance = patch.appearance;
|
|
203
|
+
skeleton.update({ ...skeletonOptions, appearance });
|
|
204
|
+
}
|
|
205
|
+
},
|
|
206
|
+
destroy() {
|
|
207
|
+
if (heightTransitionTimer !== undefined) {
|
|
208
|
+
clearTimeout(heightTransitionTimer);
|
|
209
|
+
}
|
|
210
|
+
if (fallbackRevealTimer !== undefined) {
|
|
211
|
+
clearTimeout(fallbackRevealTimer);
|
|
212
|
+
}
|
|
213
|
+
controller.destroy();
|
|
214
|
+
wrapper.remove();
|
|
215
|
+
},
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
|
|
76
219
|
/**
|
|
77
220
|
* Options accepted by {@link mountAmosCreditCardPaymentMethodForm}.
|
|
78
221
|
*/
|
|
@@ -120,6 +263,9 @@ export type AmosPaymentMethodFormMountController =
|
|
|
120
263
|
* element. Returns a controller exposing the underlying iframe, an
|
|
121
264
|
* `update()` method, and a `destroy()` method.
|
|
122
265
|
*
|
|
266
|
+
* A field-shaped skeleton is shown immediately and replaced by the
|
|
267
|
+
* iframe once appearance is applied.
|
|
268
|
+
*
|
|
123
269
|
* Use the returned `controller.iframe` when calling
|
|
124
270
|
* {@link validateForm}, {@link confirmPaymentIntent}, or
|
|
125
271
|
* {@link confirmSetupIntent}.
|
|
@@ -149,28 +295,18 @@ export function mountAmosCreditCardPaymentMethodForm(
|
|
|
149
295
|
billingAddressRequirement,
|
|
150
296
|
),
|
|
151
297
|
});
|
|
152
|
-
host.appendChild(iframe);
|
|
153
|
-
|
|
154
|
-
const controller = attachPaymentMethodFormListeners(iframe, {
|
|
155
|
-
...listenerOptions,
|
|
156
|
-
onHeightChange: (height) => {
|
|
157
|
-
iframe.style.height = height;
|
|
158
|
-
listenerOptions.onHeightChange?.(height);
|
|
159
|
-
},
|
|
160
|
-
onAppearanceReady: () => {
|
|
161
|
-
iframe.style.opacity = "1";
|
|
162
|
-
listenerOptions.onAppearanceReady?.();
|
|
163
|
-
},
|
|
164
|
-
});
|
|
165
298
|
|
|
166
|
-
return {
|
|
299
|
+
return mountPaymentMethodFormWithSkeleton({
|
|
300
|
+
host,
|
|
167
301
|
iframe,
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
302
|
+
listenerOptions,
|
|
303
|
+
skeletonOptions: {
|
|
304
|
+
kind: "card",
|
|
305
|
+
appearance: listenerOptions.appearance,
|
|
306
|
+
additionalFields,
|
|
307
|
+
billingAddressRequirement,
|
|
172
308
|
},
|
|
173
|
-
};
|
|
309
|
+
});
|
|
174
310
|
}
|
|
175
311
|
|
|
176
312
|
/**
|
|
@@ -198,6 +334,9 @@ export type AmosBankAccountPaymentMethodFormOptions =
|
|
|
198
334
|
* element. Returns a controller exposing the underlying iframe, an
|
|
199
335
|
* `update()` method, and a `destroy()` method.
|
|
200
336
|
*
|
|
337
|
+
* A field-shaped skeleton is shown immediately and replaced by the
|
|
338
|
+
* iframe once appearance is applied.
|
|
339
|
+
*
|
|
201
340
|
* Use the returned `controller.iframe` when calling
|
|
202
341
|
* {@link validateForm}, {@link confirmPaymentIntent}, or
|
|
203
342
|
* {@link confirmSetupIntent}.
|
|
@@ -219,28 +358,17 @@ export function mountAmosBankAccountPaymentMethodForm(
|
|
|
219
358
|
name: "amos-bank-account-payment-method-form",
|
|
220
359
|
height: getBankAccountFormInitialHeight(billingAddressRequirement),
|
|
221
360
|
});
|
|
222
|
-
host.appendChild(iframe);
|
|
223
|
-
|
|
224
|
-
const controller = attachPaymentMethodFormListeners(iframe, {
|
|
225
|
-
...listenerOptions,
|
|
226
|
-
onHeightChange: (height) => {
|
|
227
|
-
iframe.style.height = height;
|
|
228
|
-
listenerOptions.onHeightChange?.(height);
|
|
229
|
-
},
|
|
230
|
-
onAppearanceReady: () => {
|
|
231
|
-
iframe.style.opacity = "1";
|
|
232
|
-
listenerOptions.onAppearanceReady?.();
|
|
233
|
-
},
|
|
234
|
-
});
|
|
235
361
|
|
|
236
|
-
return {
|
|
362
|
+
return mountPaymentMethodFormWithSkeleton({
|
|
363
|
+
host,
|
|
237
364
|
iframe,
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
365
|
+
listenerOptions,
|
|
366
|
+
skeletonOptions: {
|
|
367
|
+
kind: "bank",
|
|
368
|
+
appearance: listenerOptions.appearance,
|
|
369
|
+
billingAddressRequirement,
|
|
242
370
|
},
|
|
243
|
-
};
|
|
371
|
+
});
|
|
244
372
|
}
|
|
245
373
|
|
|
246
374
|
/**
|
|
@@ -3,7 +3,12 @@ import {
|
|
|
3
3
|
sendParentReadyMessage,
|
|
4
4
|
updateAppearance as sendUpdateAppearance,
|
|
5
5
|
} from "./messaging";
|
|
6
|
-
import type {
|
|
6
|
+
import type {
|
|
7
|
+
Appearance,
|
|
8
|
+
ConfirmationResult,
|
|
9
|
+
Message,
|
|
10
|
+
PaymentMethodFormValidityChangeEvent,
|
|
11
|
+
} from "./types";
|
|
7
12
|
|
|
8
13
|
/**
|
|
9
14
|
* The additional fields beyond the standard card number, expiration
|
|
@@ -126,6 +131,12 @@ export type PaymentMethodFormListenerOptions = {
|
|
|
126
131
|
* iframe's opacity from `0` to `1` to fade it in.
|
|
127
132
|
*/
|
|
128
133
|
onAppearanceReady?: () => void;
|
|
134
|
+
/**
|
|
135
|
+
* Called when card/bank form validity changes. `isValid` is true
|
|
136
|
+
* when all required fields are present and valid. Does not include
|
|
137
|
+
* PCI data. Use this to enable or disable a host checkout button.
|
|
138
|
+
*/
|
|
139
|
+
onValidityChange?: (event: PaymentMethodFormValidityChangeEvent) => void;
|
|
129
140
|
/**
|
|
130
141
|
* Called when the interactive confirmation flow finishes (success or
|
|
131
142
|
* terminal failure). Not settlement proof — verify via webhooks.
|
|
@@ -191,6 +202,10 @@ export function attachPaymentMethodFormListeners(
|
|
|
191
202
|
current.onAppearanceReady?.();
|
|
192
203
|
break;
|
|
193
204
|
|
|
205
|
+
case "FORM_VALIDITY_CHANGE":
|
|
206
|
+
current.onValidityChange?.({ isValid: event.data.isValid });
|
|
207
|
+
break;
|
|
208
|
+
|
|
194
209
|
case "CONFIRMATION_RESULT":
|
|
195
210
|
current.onResult(event.data.result);
|
|
196
211
|
break;
|
package/src/types.ts
CHANGED
|
@@ -2,6 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
import type { components } from "@amos.com/node";
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* PCI-safe snapshot of card/bank form validity, posted when HTML
|
|
7
|
+
* constraint validation changes. Use this to enable or disable a host
|
|
8
|
+
* checkout button.
|
|
9
|
+
*/
|
|
10
|
+
export type PaymentMethodFormValidityChangeEvent = {
|
|
11
|
+
isValid: boolean;
|
|
12
|
+
};
|
|
13
|
+
|
|
5
14
|
/**
|
|
6
15
|
* Why an interactive confirmation attempt ended with `status: "incomplete"`.
|
|
7
16
|
*
|
|
@@ -593,6 +602,15 @@ export type Message =
|
|
|
593
602
|
| {
|
|
594
603
|
/** Parent → embed: clear all form field values and API errors. */
|
|
595
604
|
type: "RESET_FORM";
|
|
605
|
+
}
|
|
606
|
+
| {
|
|
607
|
+
/**
|
|
608
|
+
* Embed → parent: card/bank form validity changed.
|
|
609
|
+
* `isValid` is true when all required fields are present and
|
|
610
|
+
* valid. Does not include PCI data.
|
|
611
|
+
*/
|
|
612
|
+
type: "FORM_VALIDITY_CHANGE";
|
|
613
|
+
isValid: boolean;
|
|
596
614
|
};
|
|
597
615
|
|
|
598
616
|
/**
|