@amos.com/amos-js 0.4.1 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -96,7 +96,7 @@ The following flow is for credit card and bank account payment method types only
96
96
 
97
97
  ### Google Pay
98
98
 
99
- Google Pay (and soon, Apple Pay) is a form of express checkout. The Google Pay button is an alternative to the "Pay now" button in your payment forms. Users can make a payment with either flow.
99
+ 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.
100
100
 
101
101
  The key differences between the express and non-express payment flows are:
102
102
 
@@ -266,6 +266,10 @@ Mount the secure Google Pay button (express checkout) into a container element.
266
266
 
267
267
  - `iframe`, `update(patch)`, `destroy()`. Use `update({ amount, merchantName })` to push new values into the iframe.
268
268
 
269
+ ### `mountAmosApplePayButton(container, options)`
270
+
271
+ Mount the secure Apple Pay button (express checkout). Same options and return shape as `mountAmosGooglePayButton`.
272
+
269
273
  ### `validateForm({ iframe })`
270
274
 
271
275
  Validates the embedded card/bank iframe form. Returns `Promise<boolean>` (resolves to `false` after 5 seconds if the iframe does not respond).
@@ -0,0 +1,78 @@
1
+ import { components } from '@amos.com/node';
2
+ import { Appearance } from './types';
3
+ /**
4
+ * Build the iframe `src` URL for the embedded Apple Pay button.
5
+ */
6
+ export declare function getApplePayButtonSrc(renderToken: string): string;
7
+ /**
8
+ * Default iframe pixel height for the Apple Pay button.
9
+ */
10
+ export declare function getApplePayButtonInitialHeight(): string;
11
+ /**
12
+ * Options accepted by {@link attachApplePayButtonListeners}.
13
+ */
14
+ export type ApplePayButtonListenerOptions = {
15
+ /** The amount of the payment, in the same format passed in props. */
16
+ amount: string;
17
+ /** A user-visible merchant name. */
18
+ merchantName: string;
19
+ /**
20
+ * Custom appearance to apply when the iframe first becomes ready and
21
+ * whenever the appearance changes.
22
+ */
23
+ appearance?: Appearance;
24
+ /**
25
+ * Called whenever the iframe asks the host page to resize it. Update
26
+ * the iframe's `height` style here.
27
+ */
28
+ onHeightChange?: (height: string) => void;
29
+ /**
30
+ * Called once the iframe has applied the requested appearance and is
31
+ * ready to be revealed.
32
+ */
33
+ onAppearanceReady?: () => void;
34
+ /**
35
+ * Called when the user initiates a payment intent request via the
36
+ * Apple Pay button. Your implementation should create a payment
37
+ * intent on your server and resolve with the resulting embed token.
38
+ */
39
+ onInitiatePaymentIntentRequest: ({ paymentIntentCreateAttributes, customerCreateAttributes, }: {
40
+ paymentIntentCreateAttributes: components["schemas"]["CreatePaymentIntentInput"];
41
+ customerCreateAttributes: components["schemas"]["CreateCustomerInput"];
42
+ }) => Promise<components["schemas"]["EmbedToken"]["token"]>;
43
+ /**
44
+ * Called when payment intent confirmation succeeds.
45
+ */
46
+ onPaymentIntentConfirmationSucceeded: (paymentIntent: components["schemas"]["PaymentIntent"]) => void;
47
+ /**
48
+ * Called when payment intent confirmation fails.
49
+ */
50
+ onConfirmationFailed: (errorMessage: string) => void;
51
+ };
52
+ /**
53
+ * Controller returned by {@link attachApplePayButtonListeners} and
54
+ * {@link mountAmosApplePayButton}.
55
+ */
56
+ export type ApplePayButtonController = {
57
+ /**
58
+ * Update one or more listener options without re-attaching the
59
+ * message listener. Pass `amount` or `merchantName` to push the new
60
+ * value into the iframe; pass `appearance` to update theme variables.
61
+ */
62
+ update: (patch: Partial<ApplePayButtonListenerOptions>) => void;
63
+ /**
64
+ * Detach the iframe message listener.
65
+ */
66
+ destroy: () => void;
67
+ };
68
+ /**
69
+ * Wire up the host-page side of the Apple Pay iframe message protocol
70
+ * on an existing `<iframe>` element. Returns a controller for updating
71
+ * options and tearing down the listener.
72
+ *
73
+ * The iframe is expected to have already been added to the DOM with the
74
+ * correct `src` (see {@link getApplePayButtonSrc}).
75
+ *
76
+ * Uses the same postMessage protocol as Google Pay express checkout.
77
+ */
78
+ export declare function attachApplePayButtonListeners(iframe: HTMLIFrameElement, options: ApplePayButtonListenerOptions): ApplePayButtonController;
package/dist/index.d.ts CHANGED
@@ -1,10 +1,12 @@
1
1
  /// <reference types="googlepay" />
2
+ export type { ApplePayButtonController, ApplePayButtonListenerOptions, } from './apple-pay';
3
+ export { attachApplePayButtonListeners, getApplePayButtonInitialHeight, getApplePayButtonSrc, } from './apple-pay';
2
4
  export type { FormattedGooglePayPaymentData, GooglePayButtonController, GooglePayButtonListenerOptions, } from './google-pay';
3
5
  export { attachGooglePayButtonListeners, formatGooglePayPaymentData, getGooglePayButtonInitialHeight, getGooglePayButtonSrc, } from './google-pay';
4
6
  export { decodeJwt, getEmbedOrigin } from './jwt';
5
7
  export { confirmPaymentIntent, confirmSetupIntent, sendConfirmationFailed, sendParentReadyMessage, updateAmount, updateAppearance, updateMerchantName, validateForm, } from './messaging';
6
- export type { AmosBankAccountPaymentMethodFormOptions, AmosCreditCardPaymentMethodFormOptions, AmosGooglePayButtonMountController, AmosGooglePayButtonOptions, AmosPaymentMethodFormMountController, } from './mount';
7
- export { mountAmosBankAccountPaymentMethodForm, mountAmosCreditCardPaymentMethodForm, mountAmosGooglePayButton, } from './mount';
8
+ export type { AmosApplePayButtonMountController, AmosApplePayButtonOptions, AmosBankAccountPaymentMethodFormOptions, AmosCreditCardPaymentMethodFormOptions, AmosGooglePayButtonMountController, AmosGooglePayButtonOptions, AmosPaymentMethodFormMountController, } from './mount';
9
+ export { mountAmosApplePayButton, mountAmosBankAccountPaymentMethodForm, mountAmosCreditCardPaymentMethodForm, mountAmosGooglePayButton, } from './mount';
8
10
  export type { BillingAddressRequirement, CreditCardAdditionalFields, PaymentMethodFormController, PaymentMethodFormListenerOptions, } from './payment-method-form';
9
11
  export { attachPaymentMethodFormListeners, getBankAccountFormInitialHeight, getBankAccountFormSrc, getCreditCardFormInitialHeight, getCreditCardFormSrc, } from './payment-method-form';
10
12
  export type { Appearance, AppearanceLabels, Message, ThemeVariable, } from './types';
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});function e(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 t(t){let{env:n=`sandbox`}=e(t).payload;switch(n){case`production`:return`https://embed.amos.com`;case`sandbox`:return`https://embed-sandbox.amos.com`;default:return`https://embed-sandbox.amos.com`}}function n(e){return e}function r(e){e?.contentWindow?.postMessage(n({type:`PARENT_ACKNOWLEDGED_IFRAME_READY`}),`*`)}function i({iframe:e,appearance:t={}}){e?.contentWindow?.postMessage(n({type:`UPDATE_APPEARANCE`,appearance:t}),`*`)}function a({iframe:e,amount:t}){e?.contentWindow?.postMessage(n({type:`UPDATE_AMOUNT`,amount:t}),`*`)}function o({iframe:e,merchantName:t}){e?.contentWindow?.postMessage(n({type:`UPDATE_MERCHANT_NAME`,merchantName:t}),`*`)}function s({iframe:e}){let t=crypto.randomUUID();return new Promise(r=>{e?.contentWindow?.postMessage(n({type:`VALIDATE_FORM`,requestId:t}),`*`);let i=setTimeout(()=>{window.removeEventListener(`message`,a),r(!1)},5e3);function a(e){e.data.type===`VALIDATE_FORM`&&e.data.requestId===t&&(window.removeEventListener(`message`,a),clearTimeout(i),r(e.data.isValid??!1))}window.addEventListener(`message`,a)})}function c({iframe:t,token:r}){let{payment_intent_id:i}=e(r).payload;t?.contentWindow?.postMessage(n({type:`CONFIRM_PAYMENT_INTENT`,token:r,id:i??void 0}),`*`)}function l({iframe:t,token:r}){let{setup_intent_id:i}=e(r).payload;t?.contentWindow?.postMessage(n({type:`CONFIRM_SETUP_INTENT`,token:r,id:i??void 0}),`*`)}function u({iframe:e,errorMessage:t}){e?.contentWindow?.postMessage(n({type:`CONFIRMATION_FAILED`,errorMessage:t}),`*`)}function d(e){return`${t(e)}/iframe/google-pay?token=${e}`}function f(){return`40px`}function p(e,t){let n={...t};function s(){a({iframe:e,amount:n.amount})}function l(){o({iframe:e,merchantName:n.merchantName})}function d(t){switch(t.data.type){case`IFRAME_READY`:r(e),i({iframe:e,appearance:n.appearance}),s(),l();break;case`UPDATE_HEIGHT`:n.onHeightChange?.(t.data.height);break;case`UPDATE_APPEARANCE`:i({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=>{c({iframe:e,token:t})}).catch(t=>{u({iframe:e,errorMessage:t instanceof Error?t.message:`Unknown error`})});break;case`PAYMENT_INTENT_CONFIRMATION_SUCCEEDED`:n.onPaymentIntentConfirmationSucceeded(t.data.paymentIntent);break;case`CONFIRMATION_FAILED`:n.onConfirmationFailed(t.data.errorMessage);break}}return window.addEventListener(`message`,d),{update(t){let r=`appearance`in t,a=`amount`in t,o=`merchantName`in t;n={...n,...t},r&&i({iframe:e,appearance:n.appearance}),a&&s(),o&&l()},destroy(){window.removeEventListener(`message`,d)}}}function m({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 h={country:212,full:452},g=80,_={country:400,full:640};function v(e,n={cardholderName:!1},r=`country`){let i=Object.entries(n).filter(([,e])=>e).map(([e])=>e).join(`,`),a=new URLSearchParams({token:e,additionalFields:i,billingAddressRequirement:r});return`${t(e)}/iframe/card?${a}`}function y(e,n=`country`){let r=new URLSearchParams({token:e,billingAddressRequirement:n});return`${t(e)}/iframe/bank?${r}`}function b(e={cardholderName:!1},t=`country`){return`${(h[t]??h.country)+(e.cardholderName?g:0)}px`}function x(e=`country`){return`${_[e]??_.country}px`}function S(e,t){let n={...t};function a(t){switch(t.data.type){case`IFRAME_READY`:r(e),i({iframe:e,appearance:n.appearance});break;case`UPDATE_HEIGHT`:n.onHeightChange?.(t.data.height);break;case`UPDATE_APPEARANCE`:i({iframe:e,appearance:t.data.appearance});break;case`UPDATED_APPEARANCE`:n.onAppearanceReady?.();break;case`PAYMENT_INTENT_CONFIRMATION_SUCCEEDED`:n.onPaymentIntentConfirmationSucceeded?.(t.data.paymentIntent);break;case`SETUP_INTENT_CONFIRMATION_SUCCEEDED`:n.onSetupIntentConfirmationSucceeded?.(t.data.setupIntent);break;case`CONFIRMATION_FAILED`:n.onConfirmationFailed(t.data.errorMessage);break}}return window.addEventListener(`message`,a),{update(t){let r=`appearance`in t;n={...n,...t},r&&i({iframe:e,appearance:n.appearance})},destroy(){window.removeEventListener(`message`,a)}}}function C(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 w={width:`calc(100% + 8px)`,transition:`opacity 150ms ease-in, height 200ms ease-in-out`,margin:`0 -4px`,opacity:`0`,border:`0`};function T({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,w,{height:r}),a}function E(e,t){let n=C(e),{renderToken:r,additionalFields:i={cardholderName:!1},billingAddressRequirement:a=`country`,...o}=t,s=T({src:v(r,i,a),title:`Secure credit card payment method form powered by Amos`,name:`amos-credit-card-payment-method-form`,height:b(i,a)});n.appendChild(s);let c=S(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 D(e,t){let n=C(e),{renderToken:r,billingAddressRequirement:i=`country`,...a}=t,o=T({src:y(r,i),title:`Secure bank account payment method form powered by Amos`,name:`amos-bank-account-payment-method-form`,height:x(i)});n.appendChild(o);let s=S(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 O(e,t){let n=C(e),{renderToken:r,...i}=t,a=T({src:d(r),title:`Secure Google Pay button powered by Amos`,name:`amos-google-pay-button`,height:f(),allow:`payment`});n.appendChild(a);let o=p(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.attachGooglePayButtonListeners=p,exports.attachPaymentMethodFormListeners=S,exports.confirmPaymentIntent=c,exports.confirmSetupIntent=l,exports.createMessage=n,exports.decodeJwt=e,exports.formatGooglePayPaymentData=m,exports.getBankAccountFormInitialHeight=x,exports.getBankAccountFormSrc=y,exports.getCreditCardFormInitialHeight=b,exports.getCreditCardFormSrc=v,exports.getEmbedOrigin=t,exports.getGooglePayButtonInitialHeight=f,exports.getGooglePayButtonSrc=d,exports.mountAmosBankAccountPaymentMethodForm=D,exports.mountAmosCreditCardPaymentMethodForm=E,exports.mountAmosGooglePayButton=O,exports.sendConfirmationFailed=u,exports.sendParentReadyMessage=r,exports.updateAmount=a,exports.updateAppearance=i,exports.updateMerchantName=o,exports.validateForm=s;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});function e(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 t(t){let{env:n=`sandbox`}=e(t).payload;switch(n){case`production`:return`https://embed.amos.com`;case`sandbox`:return`https://embed-sandbox.amos.com`;default:return`https://embed-sandbox.amos.com`}}function n(e){return e}function r(e){e?.contentWindow?.postMessage(n({type:`PARENT_ACKNOWLEDGED_IFRAME_READY`}),`*`)}function i({iframe:e,appearance:t={}}){e?.contentWindow?.postMessage(n({type:`UPDATE_APPEARANCE`,appearance:t}),`*`)}function a({iframe:e,amount:t}){e?.contentWindow?.postMessage(n({type:`UPDATE_AMOUNT`,amount:t}),`*`)}function o({iframe:e,merchantName:t}){e?.contentWindow?.postMessage(n({type:`UPDATE_MERCHANT_NAME`,merchantName:t}),`*`)}function s({iframe:e}){let t=crypto.randomUUID();return new Promise(r=>{e?.contentWindow?.postMessage(n({type:`VALIDATE_FORM`,requestId:t}),`*`);let i=setTimeout(()=>{window.removeEventListener(`message`,a),r(!1)},5e3);function a(e){e.data.type===`VALIDATE_FORM`&&e.data.requestId===t&&(window.removeEventListener(`message`,a),clearTimeout(i),r(e.data.isValid??!1))}window.addEventListener(`message`,a)})}function c({iframe:t,token:r}){let{payment_intent_id:i}=e(r).payload;t?.contentWindow?.postMessage(n({type:`CONFIRM_PAYMENT_INTENT`,token:r,id:i??void 0}),`*`)}function l({iframe:t,token:r}){let{setup_intent_id:i}=e(r).payload;t?.contentWindow?.postMessage(n({type:`CONFIRM_SETUP_INTENT`,token:r,id:i??void 0}),`*`)}function u({iframe:e,errorMessage:t}){e?.contentWindow?.postMessage(n({type:`CONFIRMATION_FAILED`,errorMessage:t}),`*`)}function d(e){return`${t(e)}/iframe/apple-pay?token=${e}`}function f(){return`40px`}function p(e,t){let n={...t};function s(){a({iframe:e,amount:n.amount})}function l(){o({iframe:e,merchantName:n.merchantName})}function d(t){switch(t.data.type){case`IFRAME_READY`:r(e),i({iframe:e,appearance:n.appearance}),s(),l();break;case`UPDATE_HEIGHT`:n.onHeightChange?.(t.data.height);break;case`UPDATE_APPEARANCE`:i({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=>{c({iframe:e,token:t})}).catch(t=>{u({iframe:e,errorMessage:t instanceof Error?t.message:`Unknown error`})});break;case`PAYMENT_INTENT_CONFIRMATION_SUCCEEDED`:n.onPaymentIntentConfirmationSucceeded(t.data.paymentIntent);break;case`CONFIRMATION_FAILED`:n.onConfirmationFailed(t.data.errorMessage);break}}return window.addEventListener(`message`,d),{update(t){let r=`appearance`in t,a=`amount`in t,o=`merchantName`in t;n={...n,...t},r&&i({iframe:e,appearance:n.appearance}),a&&s(),o&&l()},destroy(){window.removeEventListener(`message`,d)}}}function m(e){return`${t(e)}/iframe/google-pay?token=${e}`}function h(){return`40px`}function g(e,t){let n={...t};function s(){a({iframe:e,amount:n.amount})}function l(){o({iframe:e,merchantName:n.merchantName})}function d(t){switch(t.data.type){case`IFRAME_READY`:r(e),i({iframe:e,appearance:n.appearance}),s(),l();break;case`UPDATE_HEIGHT`:n.onHeightChange?.(t.data.height);break;case`UPDATE_APPEARANCE`:i({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=>{c({iframe:e,token:t})}).catch(t=>{u({iframe:e,errorMessage:t instanceof Error?t.message:`Unknown error`})});break;case`PAYMENT_INTENT_CONFIRMATION_SUCCEEDED`:n.onPaymentIntentConfirmationSucceeded(t.data.paymentIntent);break;case`CONFIRMATION_FAILED`:n.onConfirmationFailed(t.data.errorMessage);break}}return window.addEventListener(`message`,d),{update(t){let r=`appearance`in t,a=`amount`in t,o=`merchantName`in t;n={...n,...t},r&&i({iframe:e,appearance:n.appearance}),a&&s(),o&&l()},destroy(){window.removeEventListener(`message`,d)}}}function _({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 v={country:212,full:452},y=80,b={country:400,full:640};function x(e,n={cardholderName:!1},r=`country`){let i=Object.entries(n).filter(([,e])=>e).map(([e])=>e).join(`,`),a=new URLSearchParams({token:e,additionalFields:i,billingAddressRequirement:r});return`${t(e)}/iframe/card?${a}`}function S(e,n=`country`){let r=new URLSearchParams({token:e,billingAddressRequirement:n});return`${t(e)}/iframe/bank?${r}`}function C(e={cardholderName:!1},t=`country`){return`${(v[t]??v.country)+(e.cardholderName?y:0)}px`}function w(e=`country`){return`${b[e]??b.country}px`}function T(e,t){let n={...t};function a(t){switch(t.data.type){case`IFRAME_READY`:r(e),i({iframe:e,appearance:n.appearance});break;case`UPDATE_HEIGHT`:n.onHeightChange?.(t.data.height);break;case`UPDATE_APPEARANCE`:i({iframe:e,appearance:t.data.appearance});break;case`UPDATED_APPEARANCE`:n.onAppearanceReady?.();break;case`PAYMENT_INTENT_CONFIRMATION_SUCCEEDED`:n.onPaymentIntentConfirmationSucceeded?.(t.data.paymentIntent);break;case`SETUP_INTENT_CONFIRMATION_SUCCEEDED`:n.onSetupIntentConfirmationSucceeded?.(t.data.setupIntent);break;case`CONFIRMATION_FAILED`:n.onConfirmationFailed(t.data.errorMessage);break}}return window.addEventListener(`message`,a),{update(t){let r=`appearance`in t;n={...n,...t},r&&i({iframe:e,appearance:n.appearance})},destroy(){window.removeEventListener(`message`,a)}}}function E(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 D={width:`calc(100% + 8px)`,transition:`opacity 150ms ease-in, height 200ms ease-in-out`,margin:`0 -4px`,opacity:`0`,border:`0`};function O({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,D,{height:r}),a}function k(e,t){let n=E(e),{renderToken:r,additionalFields:i={cardholderName:!1},billingAddressRequirement:a=`country`,...o}=t,s=O({src:x(r,i,a),title:`Secure credit card payment method form powered by Amos`,name:`amos-credit-card-payment-method-form`,height:C(i,a)});n.appendChild(s);let c=T(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 A(e,t){let n=E(e),{renderToken:r,billingAddressRequirement:i=`country`,...a}=t,o=O({src:S(r,i),title:`Secure bank account payment method form powered by Amos`,name:`amos-bank-account-payment-method-form`,height:w(i)});n.appendChild(o);let s=T(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 j(e,t){let n=E(e),{renderToken:r,...i}=t,a=O({src:m(r),title:`Secure Google Pay button powered by Amos`,name:`amos-google-pay-button`,height:h(),allow:`payment`});n.appendChild(a);let o=g(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 M(e,t){let n=E(e),{renderToken:r,...i}=t,a=O({src:d(r),title:`Secure Apple Pay button powered by Amos`,name:`amos-apple-pay-button`,height:f(),allow:`payment`});n.appendChild(a);let o=p(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=p,exports.attachGooglePayButtonListeners=g,exports.attachPaymentMethodFormListeners=T,exports.confirmPaymentIntent=c,exports.confirmSetupIntent=l,exports.createMessage=n,exports.decodeJwt=e,exports.formatGooglePayPaymentData=_,exports.getApplePayButtonInitialHeight=f,exports.getApplePayButtonSrc=d,exports.getBankAccountFormInitialHeight=w,exports.getBankAccountFormSrc=S,exports.getCreditCardFormInitialHeight=C,exports.getCreditCardFormSrc=x,exports.getEmbedOrigin=t,exports.getGooglePayButtonInitialHeight=h,exports.getGooglePayButtonSrc=m,exports.mountAmosApplePayButton=M,exports.mountAmosBankAccountPaymentMethodForm=A,exports.mountAmosCreditCardPaymentMethodForm=k,exports.mountAmosGooglePayButton=j,exports.sendConfirmationFailed=u,exports.sendParentReadyMessage=r,exports.updateAmount=a,exports.updateAppearance=i,exports.updateMerchantName=o,exports.validateForm=s;
package/dist/index.mjs CHANGED
@@ -82,9 +82,9 @@ function u({ iframe: e, errorMessage: t }) {
82
82
  }), "*");
83
83
  }
84
84
  //#endregion
85
- //#region src/google-pay.ts
85
+ //#region src/apple-pay.ts
86
86
  function d(e) {
87
- return `${t(e)}/iframe/google-pay?token=${e}`;
87
+ return `${t(e)}/iframe/apple-pay?token=${e}`;
88
88
  }
89
89
  function f() {
90
90
  return "40px";
@@ -163,7 +163,89 @@ function p(e, t) {
163
163
  }
164
164
  };
165
165
  }
166
- function m({ paymentData: e }) {
166
+ //#endregion
167
+ //#region src/google-pay.ts
168
+ function m(e) {
169
+ return `${t(e)}/iframe/google-pay?token=${e}`;
170
+ }
171
+ function h() {
172
+ return "40px";
173
+ }
174
+ function g(e, t) {
175
+ let n = { ...t };
176
+ function s() {
177
+ a({
178
+ iframe: e,
179
+ amount: n.amount
180
+ });
181
+ }
182
+ function l() {
183
+ o({
184
+ iframe: e,
185
+ merchantName: n.merchantName
186
+ });
187
+ }
188
+ function d(t) {
189
+ switch (t.data.type) {
190
+ case "IFRAME_READY":
191
+ r(e), i({
192
+ iframe: e,
193
+ appearance: n.appearance
194
+ }), s(), l();
195
+ break;
196
+ case "UPDATE_HEIGHT":
197
+ n.onHeightChange?.(t.data.height);
198
+ break;
199
+ case "UPDATE_APPEARANCE":
200
+ i({
201
+ iframe: e,
202
+ appearance: t.data.appearance
203
+ });
204
+ break;
205
+ case "UPDATED_APPEARANCE":
206
+ n.onAppearanceReady?.();
207
+ break;
208
+ case "CREATE_PAYMENT_INTENT":
209
+ n.onInitiatePaymentIntentRequest({
210
+ paymentIntentCreateAttributes: t.data.paymentIntentCreateAttributes,
211
+ customerCreateAttributes: t.data.customerCreateAttributes
212
+ }).then((t) => {
213
+ c({
214
+ iframe: e,
215
+ token: t
216
+ });
217
+ }).catch((t) => {
218
+ u({
219
+ iframe: e,
220
+ errorMessage: t instanceof Error ? t.message : "Unknown error"
221
+ });
222
+ });
223
+ break;
224
+ case "PAYMENT_INTENT_CONFIRMATION_SUCCEEDED":
225
+ n.onPaymentIntentConfirmationSucceeded(t.data.paymentIntent);
226
+ break;
227
+ case "CONFIRMATION_FAILED":
228
+ n.onConfirmationFailed(t.data.errorMessage);
229
+ break;
230
+ }
231
+ }
232
+ return window.addEventListener("message", d), {
233
+ update(t) {
234
+ let r = "appearance" in t, a = "amount" in t, o = "merchantName" in t;
235
+ n = {
236
+ ...n,
237
+ ...t
238
+ }, r && i({
239
+ iframe: e,
240
+ appearance: n.appearance
241
+ }), a && s(), o && l();
242
+ },
243
+ destroy() {
244
+ window.removeEventListener("message", d);
245
+ }
246
+ };
247
+ }
248
+ function _({ paymentData: e }) {
167
249
  return { paymentMethod: {
168
250
  type: "googlepay",
169
251
  billing_address_attributes: {
@@ -195,14 +277,14 @@ function m({ paymentData: e }) {
195
277
  }
196
278
  //#endregion
197
279
  //#region src/payment-method-form.ts
198
- var h = {
280
+ var v = {
199
281
  country: 212,
200
282
  full: 452
201
- }, g = 80, _ = {
283
+ }, y = 80, b = {
202
284
  country: 400,
203
285
  full: 640
204
286
  };
205
- function v(e, n = { cardholderName: !1 }, r = "country") {
287
+ function x(e, n = { cardholderName: !1 }, r = "country") {
206
288
  let i = Object.entries(n).filter(([, e]) => e).map(([e]) => e).join(","), a = new URLSearchParams({
207
289
  token: e,
208
290
  additionalFields: i,
@@ -210,20 +292,20 @@ function v(e, n = { cardholderName: !1 }, r = "country") {
210
292
  });
211
293
  return `${t(e)}/iframe/card?${a}`;
212
294
  }
213
- function y(e, n = "country") {
295
+ function S(e, n = "country") {
214
296
  let r = new URLSearchParams({
215
297
  token: e,
216
298
  billingAddressRequirement: n
217
299
  });
218
300
  return `${t(e)}/iframe/bank?${r}`;
219
301
  }
220
- function b(e = { cardholderName: !1 }, t = "country") {
221
- return `${(h[t] ?? h.country) + (e.cardholderName ? g : 0)}px`;
302
+ function C(e = { cardholderName: !1 }, t = "country") {
303
+ return `${(v[t] ?? v.country) + (e.cardholderName ? y : 0)}px`;
222
304
  }
223
- function x(e = "country") {
224
- return `${_[e] ?? _.country}px`;
305
+ function w(e = "country") {
306
+ return `${b[e] ?? b.country}px`;
225
307
  }
226
- function S(e, t) {
308
+ function T(e, t) {
227
309
  let n = { ...t };
228
310
  function a(t) {
229
311
  switch (t.data.type) {
@@ -274,7 +356,7 @@ function S(e, t) {
274
356
  }
275
357
  //#endregion
276
358
  //#region src/mount.ts
277
- function C(e) {
359
+ function E(e) {
278
360
  if (typeof e == "string") {
279
361
  let t = document.querySelector(e);
280
362
  if (!(t instanceof HTMLElement)) throw Error(`[amos-js] Container "${e}" did not match any HTMLElement.`);
@@ -282,26 +364,26 @@ function C(e) {
282
364
  }
283
365
  return e;
284
366
  }
285
- var w = {
367
+ var D = {
286
368
  width: "calc(100% + 8px)",
287
369
  transition: "opacity 150ms ease-in, height 200ms ease-in-out",
288
370
  margin: "0 -4px",
289
371
  opacity: "0",
290
372
  border: "0"
291
373
  };
292
- function T({ src: e, title: t, name: n, height: r, allow: i }) {
374
+ function O({ src: e, title: t, name: n, height: r, allow: i }) {
293
375
  let a = document.createElement("iframe");
294
- 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, w, { height: r }), a;
376
+ 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, D, { height: r }), a;
295
377
  }
296
- function E(e, t) {
297
- let n = C(e), { renderToken: r, additionalFields: i = { cardholderName: !1 }, billingAddressRequirement: a = "country", ...o } = t, s = T({
298
- src: v(r, i, a),
378
+ function k(e, t) {
379
+ let n = E(e), { renderToken: r, additionalFields: i = { cardholderName: !1 }, billingAddressRequirement: a = "country", ...o } = t, s = O({
380
+ src: x(r, i, a),
299
381
  title: "Secure credit card payment method form powered by Amos",
300
382
  name: "amos-credit-card-payment-method-form",
301
- height: b(i, a)
383
+ height: C(i, a)
302
384
  });
303
385
  n.appendChild(s);
304
- let c = S(s, {
386
+ let c = T(s, {
305
387
  ...o,
306
388
  onHeightChange: (e) => {
307
389
  s.style.height = e, o.onHeightChange?.(e);
@@ -318,15 +400,15 @@ function E(e, t) {
318
400
  }
319
401
  };
320
402
  }
321
- function D(e, t) {
322
- let n = C(e), { renderToken: r, billingAddressRequirement: i = "country", ...a } = t, o = T({
323
- src: y(r, i),
403
+ function A(e, t) {
404
+ let n = E(e), { renderToken: r, billingAddressRequirement: i = "country", ...a } = t, o = O({
405
+ src: S(r, i),
324
406
  title: "Secure bank account payment method form powered by Amos",
325
407
  name: "amos-bank-account-payment-method-form",
326
- height: x(i)
408
+ height: w(i)
327
409
  });
328
410
  n.appendChild(o);
329
- let s = S(o, {
411
+ let s = T(o, {
330
412
  ...a,
331
413
  onHeightChange: (e) => {
332
414
  o.style.height = e, a.onHeightChange?.(e);
@@ -343,11 +425,37 @@ function D(e, t) {
343
425
  }
344
426
  };
345
427
  }
346
- function O(e, t) {
347
- let n = C(e), { renderToken: r, ...i } = t, a = T({
348
- src: d(r),
428
+ function j(e, t) {
429
+ let n = E(e), { renderToken: r, ...i } = t, a = O({
430
+ src: m(r),
349
431
  title: "Secure Google Pay button powered by Amos",
350
432
  name: "amos-google-pay-button",
433
+ height: h(),
434
+ allow: "payment"
435
+ });
436
+ n.appendChild(a);
437
+ let o = g(a, {
438
+ ...i,
439
+ onHeightChange: (e) => {
440
+ a.style.height = e, i.onHeightChange?.(e);
441
+ },
442
+ onAppearanceReady: () => {
443
+ a.style.opacity = "1", i.onAppearanceReady?.();
444
+ }
445
+ });
446
+ return {
447
+ iframe: a,
448
+ update: o.update,
449
+ destroy() {
450
+ o.destroy(), a.remove();
451
+ }
452
+ };
453
+ }
454
+ function M(e, t) {
455
+ let n = E(e), { renderToken: r, ...i } = t, a = O({
456
+ src: d(r),
457
+ title: "Secure Apple Pay button powered by Amos",
458
+ name: "amos-apple-pay-button",
351
459
  height: f(),
352
460
  allow: "payment"
353
461
  });
@@ -370,4 +478,4 @@ function O(e, t) {
370
478
  };
371
479
  }
372
480
  //#endregion
373
- export { p as attachGooglePayButtonListeners, S as attachPaymentMethodFormListeners, c as confirmPaymentIntent, l as confirmSetupIntent, n as createMessage, e as decodeJwt, m as formatGooglePayPaymentData, x as getBankAccountFormInitialHeight, y as getBankAccountFormSrc, b as getCreditCardFormInitialHeight, v as getCreditCardFormSrc, t as getEmbedOrigin, f as getGooglePayButtonInitialHeight, d as getGooglePayButtonSrc, D as mountAmosBankAccountPaymentMethodForm, E as mountAmosCreditCardPaymentMethodForm, O as mountAmosGooglePayButton, u as sendConfirmationFailed, r as sendParentReadyMessage, a as updateAmount, i as updateAppearance, o as updateMerchantName, s as validateForm };
481
+ export { p as attachApplePayButtonListeners, g as attachGooglePayButtonListeners, T as attachPaymentMethodFormListeners, c as confirmPaymentIntent, l as confirmSetupIntent, n as createMessage, e as decodeJwt, _ as formatGooglePayPaymentData, f as getApplePayButtonInitialHeight, d as getApplePayButtonSrc, w as getBankAccountFormInitialHeight, S as getBankAccountFormSrc, C as getCreditCardFormInitialHeight, x as getCreditCardFormSrc, t as getEmbedOrigin, h as getGooglePayButtonInitialHeight, m as getGooglePayButtonSrc, M as mountAmosApplePayButton, A as mountAmosBankAccountPaymentMethodForm, k as mountAmosCreditCardPaymentMethodForm, j as mountAmosGooglePayButton, u as sendConfirmationFailed, r as sendParentReadyMessage, a as updateAmount, i as updateAppearance, o as updateMerchantName, s as validateForm };
package/dist/mount.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { ApplePayButtonController, ApplePayButtonListenerOptions } from './apple-pay';
1
2
  import { GooglePayButtonController, GooglePayButtonListenerOptions } from './google-pay';
2
3
  import { BillingAddressRequirement, CreditCardAdditionalFields, PaymentMethodFormController, PaymentMethodFormListenerOptions } from './payment-method-form';
3
4
  type Container = HTMLElement | string;
@@ -104,4 +105,31 @@ export type AmosGooglePayButtonMountController = GooglePayButtonController & {
104
105
  * iframe, an `update()` method, and a `destroy()` method.
105
106
  */
106
107
  export declare function mountAmosGooglePayButton(container: Container, options: AmosGooglePayButtonOptions): AmosGooglePayButtonMountController;
108
+ /**
109
+ * Options accepted by {@link mountAmosApplePayButton}.
110
+ */
111
+ export type AmosApplePayButtonOptions = ApplePayButtonListenerOptions & {
112
+ /**
113
+ * The Amos render token for the Apple Pay button.
114
+ *
115
+ * It is safe to pass this to the client. Create this on
116
+ * https://dashboard.amos.com.
117
+ */
118
+ renderToken: string;
119
+ };
120
+ /**
121
+ * Controller returned by {@link mountAmosApplePayButton}.
122
+ */
123
+ export type AmosApplePayButtonMountController = ApplePayButtonController & {
124
+ /**
125
+ * The underlying `<iframe>` element.
126
+ */
127
+ iframe: HTMLIFrameElement;
128
+ };
129
+ /**
130
+ * Mount the secure Apple Pay button (express checkout) into a
131
+ * container element. Returns a controller exposing the underlying
132
+ * iframe, an `update()` method, and a `destroy()` method.
133
+ */
134
+ export declare function mountAmosApplePayButton(container: Container, options: AmosApplePayButtonOptions): AmosApplePayButtonMountController;
107
135
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@amos.com/amos-js",
3
- "version": "0.4.1",
3
+ "version": "0.5.0",
4
4
  "main": "dist/index.js",
5
5
  "repository": {
6
6
  "type": "git",
@@ -0,0 +1,187 @@
1
+ import type { components } from "@amos.com/node";
2
+ import { getEmbedOrigin } from "./jwt";
3
+ import {
4
+ confirmPaymentIntent,
5
+ sendConfirmationFailed,
6
+ sendParentReadyMessage,
7
+ updateAmount as sendUpdateAmount,
8
+ updateAppearance as sendUpdateAppearance,
9
+ updateMerchantName as sendUpdateMerchantName,
10
+ } from "./messaging";
11
+ import type { Appearance, Message } from "./types";
12
+
13
+ /**
14
+ * Build the iframe `src` URL for the embedded Apple Pay button.
15
+ */
16
+ export function getApplePayButtonSrc(renderToken: string): string {
17
+ return `${getEmbedOrigin(renderToken)}/iframe/apple-pay?token=${renderToken}`;
18
+ }
19
+
20
+ /**
21
+ * Default iframe pixel height for the Apple Pay button.
22
+ */
23
+ export function getApplePayButtonInitialHeight(): string {
24
+ return "40px";
25
+ }
26
+
27
+ /**
28
+ * Options accepted by {@link attachApplePayButtonListeners}.
29
+ */
30
+ export type ApplePayButtonListenerOptions = {
31
+ /** The amount of the payment, in the same format passed in props. */
32
+ amount: string;
33
+ /** A user-visible merchant name. */
34
+ merchantName: string;
35
+ /**
36
+ * Custom appearance to apply when the iframe first becomes ready and
37
+ * whenever the appearance changes.
38
+ */
39
+ appearance?: Appearance;
40
+ /**
41
+ * Called whenever the iframe asks the host page to resize it. Update
42
+ * the iframe's `height` style here.
43
+ */
44
+ onHeightChange?: (height: string) => void;
45
+ /**
46
+ * Called once the iframe has applied the requested appearance and is
47
+ * ready to be revealed.
48
+ */
49
+ onAppearanceReady?: () => void;
50
+ /**
51
+ * Called when the user initiates a payment intent request via the
52
+ * Apple Pay button. Your implementation should create a payment
53
+ * intent on your server and resolve with the resulting embed token.
54
+ */
55
+ onInitiatePaymentIntentRequest: ({
56
+ paymentIntentCreateAttributes,
57
+ customerCreateAttributes,
58
+ }: {
59
+ paymentIntentCreateAttributes: components["schemas"]["CreatePaymentIntentInput"];
60
+ customerCreateAttributes: components["schemas"]["CreateCustomerInput"];
61
+ }) => Promise<components["schemas"]["EmbedToken"]["token"]>;
62
+ /**
63
+ * Called when payment intent confirmation succeeds.
64
+ */
65
+ onPaymentIntentConfirmationSucceeded: (
66
+ paymentIntent: components["schemas"]["PaymentIntent"],
67
+ ) => void;
68
+ /**
69
+ * Called when payment intent confirmation fails.
70
+ */
71
+ onConfirmationFailed: (errorMessage: string) => void;
72
+ };
73
+
74
+ /**
75
+ * Controller returned by {@link attachApplePayButtonListeners} and
76
+ * {@link mountAmosApplePayButton}.
77
+ */
78
+ export type ApplePayButtonController = {
79
+ /**
80
+ * Update one or more listener options without re-attaching the
81
+ * message listener. Pass `amount` or `merchantName` to push the new
82
+ * value into the iframe; pass `appearance` to update theme variables.
83
+ */
84
+ update: (patch: Partial<ApplePayButtonListenerOptions>) => void;
85
+ /**
86
+ * Detach the iframe message listener.
87
+ */
88
+ destroy: () => void;
89
+ };
90
+
91
+ /**
92
+ * Wire up the host-page side of the Apple Pay iframe message protocol
93
+ * on an existing `<iframe>` element. Returns a controller for updating
94
+ * options and tearing down the listener.
95
+ *
96
+ * The iframe is expected to have already been added to the DOM with the
97
+ * correct `src` (see {@link getApplePayButtonSrc}).
98
+ *
99
+ * Uses the same postMessage protocol as Google Pay express checkout.
100
+ */
101
+ export function attachApplePayButtonListeners(
102
+ iframe: HTMLIFrameElement,
103
+ options: ApplePayButtonListenerOptions,
104
+ ): ApplePayButtonController {
105
+ let current = { ...options };
106
+
107
+ function pushAmount() {
108
+ sendUpdateAmount({ iframe, amount: current.amount });
109
+ }
110
+
111
+ function pushMerchantName() {
112
+ sendUpdateMerchantName({ iframe, merchantName: current.merchantName });
113
+ }
114
+
115
+ function handleMessage(event: MessageEvent<Message>) {
116
+ switch (event.data.type) {
117
+ case "IFRAME_READY":
118
+ sendParentReadyMessage(iframe);
119
+ sendUpdateAppearance({ iframe, appearance: current.appearance });
120
+ pushAmount();
121
+ pushMerchantName();
122
+ break;
123
+
124
+ case "UPDATE_HEIGHT":
125
+ current.onHeightChange?.(event.data.height);
126
+ break;
127
+
128
+ case "UPDATE_APPEARANCE":
129
+ sendUpdateAppearance({ iframe, appearance: event.data.appearance });
130
+ break;
131
+
132
+ case "UPDATED_APPEARANCE":
133
+ current.onAppearanceReady?.();
134
+ break;
135
+
136
+ case "CREATE_PAYMENT_INTENT":
137
+ current
138
+ .onInitiatePaymentIntentRequest({
139
+ paymentIntentCreateAttributes:
140
+ event.data.paymentIntentCreateAttributes,
141
+ customerCreateAttributes: event.data.customerCreateAttributes,
142
+ })
143
+ .then((token) => {
144
+ confirmPaymentIntent({ iframe, token });
145
+ })
146
+ .catch((error: unknown) => {
147
+ sendConfirmationFailed({
148
+ iframe,
149
+ errorMessage:
150
+ error instanceof Error ? error.message : "Unknown error",
151
+ });
152
+ });
153
+ break;
154
+
155
+ case "PAYMENT_INTENT_CONFIRMATION_SUCCEEDED":
156
+ current.onPaymentIntentConfirmationSucceeded(event.data.paymentIntent);
157
+ break;
158
+
159
+ case "CONFIRMATION_FAILED":
160
+ current.onConfirmationFailed(event.data.errorMessage);
161
+ break;
162
+ }
163
+ }
164
+
165
+ window.addEventListener("message", handleMessage);
166
+
167
+ return {
168
+ update(patch) {
169
+ const hadAppearance = "appearance" in patch;
170
+ const hadAmount = "amount" in patch;
171
+ const hadMerchantName = "merchantName" in patch;
172
+ current = { ...current, ...patch };
173
+ if (hadAppearance) {
174
+ sendUpdateAppearance({ iframe, appearance: current.appearance });
175
+ }
176
+ if (hadAmount) {
177
+ pushAmount();
178
+ }
179
+ if (hadMerchantName) {
180
+ pushMerchantName();
181
+ }
182
+ },
183
+ destroy() {
184
+ window.removeEventListener("message", handleMessage);
185
+ },
186
+ };
187
+ }
package/src/index.ts CHANGED
@@ -1,5 +1,15 @@
1
1
  /// <reference types="googlepay" />
2
2
 
3
+ export type {
4
+ ApplePayButtonController,
5
+ ApplePayButtonListenerOptions,
6
+ } from "./apple-pay";
7
+ export {
8
+ attachApplePayButtonListeners,
9
+ getApplePayButtonInitialHeight,
10
+ getApplePayButtonSrc,
11
+ } from "./apple-pay";
12
+
3
13
  export type {
4
14
  FormattedGooglePayPaymentData,
5
15
  GooglePayButtonController,
@@ -25,6 +35,8 @@ export {
25
35
  validateForm,
26
36
  } from "./messaging";
27
37
  export type {
38
+ AmosApplePayButtonMountController,
39
+ AmosApplePayButtonOptions,
28
40
  AmosBankAccountPaymentMethodFormOptions,
29
41
  AmosCreditCardPaymentMethodFormOptions,
30
42
  AmosGooglePayButtonMountController,
@@ -32,6 +44,7 @@ export type {
32
44
  AmosPaymentMethodFormMountController,
33
45
  } from "./mount";
34
46
  export {
47
+ mountAmosApplePayButton,
35
48
  mountAmosBankAccountPaymentMethodForm,
36
49
  mountAmosCreditCardPaymentMethodForm,
37
50
  mountAmosGooglePayButton,
package/src/mount.ts CHANGED
@@ -1,3 +1,10 @@
1
+ import {
2
+ attachApplePayButtonListeners,
3
+ type ApplePayButtonController,
4
+ type ApplePayButtonListenerOptions,
5
+ getApplePayButtonInitialHeight,
6
+ getApplePayButtonSrc,
7
+ } from "./apple-pay";
1
8
  import {
2
9
  attachGooglePayButtonListeners,
3
10
  type GooglePayButtonController,
@@ -301,3 +308,69 @@ export function mountAmosGooglePayButton(
301
308
  },
302
309
  };
303
310
  }
311
+
312
+ /**
313
+ * Options accepted by {@link mountAmosApplePayButton}.
314
+ */
315
+ export type AmosApplePayButtonOptions = ApplePayButtonListenerOptions & {
316
+ /**
317
+ * The Amos render token for the Apple Pay button.
318
+ *
319
+ * It is safe to pass this to the client. Create this on
320
+ * https://dashboard.amos.com.
321
+ */
322
+ renderToken: string;
323
+ };
324
+
325
+ /**
326
+ * Controller returned by {@link mountAmosApplePayButton}.
327
+ */
328
+ export type AmosApplePayButtonMountController = ApplePayButtonController & {
329
+ /**
330
+ * The underlying `<iframe>` element.
331
+ */
332
+ iframe: HTMLIFrameElement;
333
+ };
334
+
335
+ /**
336
+ * Mount the secure Apple Pay button (express checkout) into a
337
+ * container element. Returns a controller exposing the underlying
338
+ * iframe, an `update()` method, and a `destroy()` method.
339
+ */
340
+ export function mountAmosApplePayButton(
341
+ container: Container,
342
+ options: AmosApplePayButtonOptions,
343
+ ): AmosApplePayButtonMountController {
344
+ const host = resolveContainer(container);
345
+ const { renderToken, ...listenerOptions } = options;
346
+
347
+ const iframe = createIframe({
348
+ src: getApplePayButtonSrc(renderToken),
349
+ title: "Secure Apple Pay button powered by Amos",
350
+ name: "amos-apple-pay-button",
351
+ height: getApplePayButtonInitialHeight(),
352
+ allow: "payment",
353
+ });
354
+ host.appendChild(iframe);
355
+
356
+ const controller = attachApplePayButtonListeners(iframe, {
357
+ ...listenerOptions,
358
+ onHeightChange: (height) => {
359
+ iframe.style.height = height;
360
+ listenerOptions.onHeightChange?.(height);
361
+ },
362
+ onAppearanceReady: () => {
363
+ iframe.style.opacity = "1";
364
+ listenerOptions.onAppearanceReady?.();
365
+ },
366
+ });
367
+
368
+ return {
369
+ iframe,
370
+ update: controller.update,
371
+ destroy() {
372
+ controller.destroy();
373
+ iframe.remove();
374
+ },
375
+ };
376
+ }