@amos.com/react-amos-js 0.9.14 → 0.9.16
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 +95 -44
- package/dist/index.d.ts +20 -0
- package/dist/index.js +1 -1
- package/dist/index.mjs +115 -64
- package/package.json +2 -2
- package/src/index.tsx +99 -3
package/README.md
CHANGED
|
@@ -39,7 +39,7 @@ The render token configures the iframe's allowed origin(s), allowed payment meth
|
|
|
39
39
|
The following flow is for credit card and bank account payment method types only.
|
|
40
40
|
|
|
41
41
|
1. **Set up prerequisites**: create a `renderToken` (safe for client), and keep `apiKey` and `accountId` server-side only.
|
|
42
|
-
2. **Render your checkout UI** with one of the payment method components (e.g. `AmosCreditCardPaymentMethodForm`) along with the required `onResult` prop. Card and bank forms show a field-shaped skeleton immediately (sized from `appearance`, `additionalFields`, and `billingAddressRequirement`);
|
|
42
|
+
2. **Render your checkout UI** with one of the payment method components (e.g. `AmosCreditCardPaymentMethodForm`) along with the required `onResult` prop. Card and bank forms show a field-shaped skeleton immediately (sized from `appearance`, `additionalFields`, and `billingAddressRequirement`); Google Pay and Apple Pay paint a button-shaped skeleton in the parent document on first render so the 48px slot is reserved before the iframe loads.
|
|
43
43
|
3. **User clicks "Pay now" button**: call `validateForm({ iframeRef })`, which returns `Promise<true>` if the embedded form is valid and `Promise<false>` otherwise.
|
|
44
44
|
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.
|
|
45
45
|
5. **Return the payment intent token to the browser**: your backend responds with the embed token (`components["schemas"]["EmbedToken"]`) needed for confirmation.
|
|
@@ -247,58 +247,109 @@ function CheckoutForm() {
|
|
|
247
247
|
}
|
|
248
248
|
```
|
|
249
249
|
|
|
250
|
-
### Rendering Google Pay within your checkout flow
|
|
250
|
+
### Rendering Google Pay and Apple Pay within your checkout flow
|
|
251
251
|
|
|
252
252
|
```tsx
|
|
253
253
|
import { useState } from "react";
|
|
254
|
-
import {
|
|
254
|
+
import {
|
|
255
|
+
AmosApplePayButton,
|
|
256
|
+
AmosGooglePayButton,
|
|
257
|
+
type ConfirmationResult,
|
|
258
|
+
} from "@amos.com/react-amos-js";
|
|
259
|
+
import type { components } from "@amos.com/node";
|
|
260
|
+
|
|
261
|
+
async function createPaymentIntentToken({
|
|
262
|
+
paymentIntentCreateAttributes,
|
|
263
|
+
customerCreateAttributes,
|
|
264
|
+
}: {
|
|
265
|
+
paymentIntentCreateAttributes: components["schemas"]["CreatePaymentIntentInput"];
|
|
266
|
+
customerCreateAttributes: components["schemas"]["CreateCustomerInput"];
|
|
267
|
+
}): Promise<string> {
|
|
268
|
+
const response = await fetch("/api/payment-intents", {
|
|
269
|
+
method: "POST",
|
|
270
|
+
headers: { "Content-Type": "application/json" },
|
|
271
|
+
body: JSON.stringify({
|
|
272
|
+
customer: customerCreateAttributes,
|
|
273
|
+
paymentIntent: paymentIntentCreateAttributes,
|
|
274
|
+
}),
|
|
275
|
+
});
|
|
276
|
+
if (!response.ok) {
|
|
277
|
+
throw new Error("Failed to create payment intent.");
|
|
278
|
+
}
|
|
279
|
+
const { token } = (await response.json()) as { token: string };
|
|
280
|
+
return token;
|
|
281
|
+
}
|
|
255
282
|
|
|
256
|
-
function
|
|
283
|
+
function CheckoutWallets({ renderToken }: { renderToken: string }) {
|
|
257
284
|
const [error, setError] = useState<string | null>(null);
|
|
258
285
|
|
|
286
|
+
function handleResult(result: ConfirmationResult) {
|
|
287
|
+
if (result.status === "succeeded") {
|
|
288
|
+
console.log("Confirm returned:", result);
|
|
289
|
+
} else if (result.status === "failed") {
|
|
290
|
+
setError(result.errorMessage);
|
|
291
|
+
} else if (result.status === "incomplete") {
|
|
292
|
+
console.log("Recoverable:", result.reason);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
259
296
|
return (
|
|
260
297
|
<>
|
|
261
|
-
<
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
const { token } = await response.json();
|
|
283
|
-
return token;
|
|
284
|
-
}}
|
|
285
|
-
onResult={(result) => {
|
|
286
|
-
if (result.status === "succeeded") {
|
|
287
|
-
console.log("Confirm returned:", result);
|
|
288
|
-
} else if (result.status === "failed") {
|
|
289
|
-
console.error("Confirm failed:", result.errorMessage);
|
|
290
|
-
} else if (result.status === "incomplete") {
|
|
291
|
-
console.log("Recoverable:", result.reason);
|
|
292
|
-
}
|
|
293
|
-
}}
|
|
294
|
-
/>
|
|
295
|
-
{error ? <p>{error}</p> : null}
|
|
298
|
+
<div style={{ display: "flex", gap: "12px" }}>
|
|
299
|
+
<div style={{ flex: "1 1 0", minWidth: 0 }}>
|
|
300
|
+
<AmosGooglePayButton
|
|
301
|
+
renderToken={renderToken}
|
|
302
|
+
amount="50.00"
|
|
303
|
+
merchantName="Example Store"
|
|
304
|
+
onInitiatePaymentIntentRequest={createPaymentIntentToken}
|
|
305
|
+
onResult={handleResult}
|
|
306
|
+
/>
|
|
307
|
+
</div>
|
|
308
|
+
<div style={{ flex: "1 1 0", minWidth: 0 }}>
|
|
309
|
+
<AmosApplePayButton
|
|
310
|
+
renderToken={renderToken}
|
|
311
|
+
amount="50.00"
|
|
312
|
+
merchantName="Example Store"
|
|
313
|
+
onInitiatePaymentIntentRequest={createPaymentIntentToken}
|
|
314
|
+
onResult={handleResult}
|
|
315
|
+
/>
|
|
316
|
+
</div>
|
|
317
|
+
</div>
|
|
318
|
+
{error ? <p role="alert">{error}</p> : null}
|
|
296
319
|
</>
|
|
297
320
|
);
|
|
298
321
|
}
|
|
299
322
|
```
|
|
300
323
|
|
|
301
|
-
|
|
324
|
+
Do not call `validateForm` or `confirmPaymentIntent` — return the embed token from `onInitiatePaymentIntentRequest` and the SDK confirms. Size the mount slot; omitted `buttonProps` keep paint defaults and fill the iframe.
|
|
325
|
+
|
|
326
|
+
On Safari, Apple Pay uses the native payment sheet. On other browsers, Apple's QR handoff opens in a popup (`pay.apple.com`); while that popup is open, the SDK shows a waiting overlay with **Cancel payment**.
|
|
327
|
+
|
|
328
|
+
Optional visuals:
|
|
329
|
+
|
|
330
|
+
```tsx
|
|
331
|
+
<AmosGooglePayButton
|
|
332
|
+
renderToken={renderToken}
|
|
333
|
+
amount="50.00"
|
|
334
|
+
merchantName="Example Store"
|
|
335
|
+
height="48px"
|
|
336
|
+
buttonProps={{ buttonType: "donate", buttonBorderType: "no_border" }}
|
|
337
|
+
iframeProps={{ style: { borderRadius: "8px" } }}
|
|
338
|
+
onInitiatePaymentIntentRequest={createPaymentIntentToken}
|
|
339
|
+
onResult={handleResult}
|
|
340
|
+
/>
|
|
341
|
+
|
|
342
|
+
<AmosApplePayButton
|
|
343
|
+
renderToken={renderToken}
|
|
344
|
+
amount="50.00"
|
|
345
|
+
merchantName="Example Store"
|
|
346
|
+
height="48px"
|
|
347
|
+
buttonProps={{ type: "donate" }}
|
|
348
|
+
iframeProps={{ style: { borderRadius: "8px" } }}
|
|
349
|
+
onInitiatePaymentIntentRequest={createPaymentIntentToken}
|
|
350
|
+
onResult={handleResult}
|
|
351
|
+
/>
|
|
352
|
+
```
|
|
302
353
|
|
|
303
354
|
### Saving a payment method with setup intent (credit card)
|
|
304
355
|
|
|
@@ -449,12 +500,12 @@ Renders the secure bank account iframe form. A field-shaped skeleton is shown im
|
|
|
449
500
|
|
|
450
501
|
### `AmosGooglePayButton`
|
|
451
502
|
|
|
452
|
-
Renders the secure Google Pay iframe button (express checkout flow).
|
|
503
|
+
Renders the secure Google Pay iframe button (express checkout flow). A button-shaped skeleton is shown immediately and replaced by the iframe once appearance is applied.
|
|
453
504
|
|
|
454
505
|
**Required props:**
|
|
455
506
|
|
|
456
507
|
- `renderToken` (`string`)
|
|
457
|
-
- `amount` (`string`)
|
|
508
|
+
- `amount` (`string`) — major-currency decimal string shown in the wallet sheet (e.g. `"50.00"` for $50.00). The iframe converts this to cents in `paymentIntentCreateAttributes.amount`.
|
|
458
509
|
- `merchantName` (`string`)
|
|
459
510
|
- `onInitiatePaymentIntentRequest` (callback receiving `{ paymentIntentCreateAttributes: components["schemas"]["CreatePaymentIntentInput"]; customerCreateAttributes: components["schemas"]["CreateCustomerInput"] }`, returns `Promise<components["schemas"]["EmbedToken"]["token"]>` — the embed JWT string for confirmation)
|
|
460
511
|
|
|
@@ -476,7 +527,7 @@ Renders the secure Google Pay iframe button (express checkout flow).
|
|
|
476
527
|
|
|
477
528
|
### `AmosApplePayButton`
|
|
478
529
|
|
|
479
|
-
Renders the secure Apple Pay iframe button (express checkout flow). Same required props and callbacks as `AmosGooglePayButton`.
|
|
530
|
+
Renders the secure Apple Pay iframe button (express checkout flow). Same required props and callbacks as `AmosGooglePayButton`. A button-shaped skeleton is shown immediately and replaced by the iframe once appearance is applied.
|
|
480
531
|
|
|
481
532
|
**Optional visual props:**
|
|
482
533
|
|
|
@@ -521,7 +572,7 @@ Re-exports of the same advanced helpers exposed by `@amos.com/amos-js`. Most int
|
|
|
521
572
|
- **`ref` / `iframeRef`**: for card and bank forms, pass `ref={iframeRef}` to the form component. The same `iframeRef` must be used when calling `validateForm`, `confirmPaymentIntent`, `confirmSetupIntent`, or `resetForm`. The component forwards the ref to the inner iframe.
|
|
522
573
|
- **`onResult` is not settlement proof**: `onResult` tells you when to stop waiting (e.g. dismiss a spinner). Verify payment or setup success on your backend via webhooks. On `status: "incomplete"`, unlock your UI — the customer can fix fields in the iframe and retry. Use `result.reason` (`"field_errors"` or `"validation_failed"`) to distinguish recoverable states.
|
|
523
574
|
- **Same components for payment vs setup intents**: `AmosCreditCardPaymentMethodForm` and `AmosBankAccountPaymentMethodForm` support both payment intents and setup intents. The flow differs only by which server call you make and which confirmation function you use (`confirmPaymentIntent` vs `confirmSetupIntent`). Handle both payment and setup outcomes via `onResult`.
|
|
524
|
-
- **Amount format**: for `AmosGooglePayButton` and `AmosApplePayButton`, `amount` is a string (e.g. `"
|
|
575
|
+
- **Amount format**: for `AmosGooglePayButton` and `AmosApplePayButton`, `amount` is a major-currency decimal string (e.g. `"50.00"` for $50.00). For `components["schemas"]["CreatePaymentIntentInput"]` on the server (card/bank create, and the object the wallet iframe sends to `onInitiatePaymentIntentRequest`), `amount` is a number in cents (e.g. `5000`).
|
|
525
576
|
- **Apple Pay waiting overlay**: on browsers where Apple's QR handoff opens in a popup (non-Safari), `AmosApplePayButton` shows a fixed full-viewport overlay on the host page until payment completes, the popup closes, or the user clicks **Cancel payment**. Avoid stacking other fixed UI above it.
|
|
526
577
|
- **Going framework-free**: if you need to use Amos outside of React (vanilla JS, another framework, etc.), use [`@amos.com/amos-js`](../amos-js) directly.
|
|
527
578
|
|
package/dist/index.d.ts
CHANGED
|
@@ -74,6 +74,11 @@ export declare function AmosBankAccountPaymentMethodForm({ ref, renderToken, app
|
|
|
74
74
|
type AmosGooglePayButtonProps = {
|
|
75
75
|
ref?: ForwardedIframeRef;
|
|
76
76
|
renderToken: string;
|
|
77
|
+
/**
|
|
78
|
+
* Major-currency decimal string shown in the Google Pay sheet
|
|
79
|
+
* (e.g. `"50.00"` for $50.00). Converted to cents in
|
|
80
|
+
* `paymentIntentCreateAttributes.amount`.
|
|
81
|
+
*/
|
|
77
82
|
amount: string;
|
|
78
83
|
merchantName: string;
|
|
79
84
|
/**
|
|
@@ -96,10 +101,20 @@ type AmosGooglePayButtonProps = {
|
|
|
96
101
|
}) => Promise<components["schemas"]["EmbedToken"]["token"]>;
|
|
97
102
|
onResult: (result: ConfirmationResult) => void;
|
|
98
103
|
};
|
|
104
|
+
/**
|
|
105
|
+
* Renders the secure Google Pay iframe button. A button-shaped
|
|
106
|
+
* skeleton is painted in the parent document on first render (including
|
|
107
|
+
* SSR) so the slot height is reserved before the iframe loads.
|
|
108
|
+
*/
|
|
99
109
|
export declare function AmosGooglePayButton({ ref, renderToken, amount, merchantName, height, buttonProps, iframeProps, onInitiatePaymentIntentRequest, onResult, }: AmosGooglePayButtonProps): import("react").JSX.Element;
|
|
100
110
|
type AmosApplePayButtonProps = {
|
|
101
111
|
ref?: ForwardedIframeRef;
|
|
102
112
|
renderToken: string;
|
|
113
|
+
/**
|
|
114
|
+
* Major-currency decimal string shown in the Apple Pay sheet
|
|
115
|
+
* (e.g. `"50.00"` for $50.00). Converted to cents in
|
|
116
|
+
* `paymentIntentCreateAttributes.amount`.
|
|
117
|
+
*/
|
|
103
118
|
amount: string;
|
|
104
119
|
merchantName: string;
|
|
105
120
|
/**
|
|
@@ -122,4 +137,9 @@ type AmosApplePayButtonProps = {
|
|
|
122
137
|
}) => Promise<components["schemas"]["EmbedToken"]["token"]>;
|
|
123
138
|
onResult: (result: ConfirmationResult) => void;
|
|
124
139
|
};
|
|
140
|
+
/**
|
|
141
|
+
* Renders the secure Apple Pay iframe button. A button-shaped
|
|
142
|
+
* skeleton is painted in the parent document on first render (including
|
|
143
|
+
* SSR) so the slot height is reserved before the iframe loads.
|
|
144
|
+
*/
|
|
125
145
|
export declare function AmosApplePayButton({ ref, renderToken, amount, merchantName, height, buttonProps, iframeProps, onInitiatePaymentIntentRequest, onResult, }: AmosApplePayButtonProps): import("react").JSX.Element;
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require("@amos.com/amos-js"),t=require("react"),n=require("react/jsx-runtime");function r(e){return e?e.current??null:null}function i({iframeRef:t}){return(0,e.validateForm)({iframe:r(t)})}function a({iframeRef:t,token:n}){(0,e.confirmPaymentIntent)({iframe:r(t),token:n})}function o({iframeRef:t,token:n}){(0,e.confirmSetupIntent)({iframe:r(t),token:n})}function s({iframeRef:t}){(0,e.resetForm)({iframe:r(t)})}function c(e,t){typeof e==`function`?e(t):e&&(e.current=t)}function l(e,{style:t,className:n,id:r,...i}){n!=null&&(e.className=n),r!=null&&(e.id=r),Object.assign(e.style,t);for(let[t,n]of Object.entries(i))n!=null&&(t in e?Reflect.set(e,t,n):e.setAttribute(t,String(n)))}function u({containerRef:e,iframeRef:n,mount:r,options:i,remountDeps:a,iframePassthrough:o,updateDeps:s}){let u=(0,t.useRef)(null);(0,t.
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require("@amos.com/amos-js"),t=require("react"),n=require("react/jsx-runtime");function r(e){return e?e.current??null:null}function i({iframeRef:t}){return(0,e.validateForm)({iframe:r(t)})}function a({iframeRef:t,token:n}){(0,e.confirmPaymentIntent)({iframe:r(t),token:n})}function o({iframeRef:t,token:n}){(0,e.confirmSetupIntent)({iframe:r(t),token:n})}function s({iframeRef:t}){(0,e.resetForm)({iframe:r(t)})}function c(e,t){typeof e==`function`?e(t):e&&(e.current=t)}function l(e,{style:t,className:n,id:r,...i}){n!=null&&(e.className=n),r!=null&&(e.id=r),Object.assign(e.style,t);for(let[t,n]of Object.entries(i))n!=null&&(t in e?Reflect.set(e,t,n):e.setAttribute(t,String(n)))}function u({containerRef:e,iframeRef:n,mount:r,options:i,remountDeps:a,iframePassthrough:o,updateDeps:s}){let u=(0,t.useRef)(null);(0,t.useLayoutEffect)(()=>{let t=e.current;if(!t)return;let a=r(t,i);return u.current=a,c(n,a.iframe),l(a.iframe,o),()=>{a.destroy(),u.current=null,c(n,null)}},[...a]),(0,t.useEffect)(()=>{u.current?.update(i)},[...s]),(0,t.useEffect)(()=>{let e=u.current?.iframe;e&&l(e,o)})}var d=`oklch(0.97 0 0)`;function f({height:r,borderRadius:i,containerRef:a}){return(0,t.useLayoutEffect)(()=>{(0,e.ensureSkeletonStyles)()},[]),(0,n.jsxs)(`div`,{style:{boxSizing:`border-box`,position:`relative`,width:`100%`,height:r,minHeight:r,overflow:`hidden`},children:[(0,n.jsx)(`div`,{className:`amos-js-form-skeleton-input amos-js-wallet-skeleton`,style:{position:`absolute`,inset:0,height:r,borderRadius:i,background:d,pointerEvents:`none`,zIndex:0},"aria-hidden":!0}),(0,n.jsx)(`div`,{ref:a,style:{position:`absolute`,inset:0,zIndex:1,width:`100%`,height:`100%`}})]})}function p({ref:r,renderToken:i,appearance:a,onResult:o,onValidityChange:s,additionalFields:c={cardholderName:!1},billingAddressRequirement:l=`country`,style:d,...f}){let p=(0,t.useRef)(null);return u({containerRef:p,iframeRef:r,mount:e.mountAmosCreditCardPaymentMethodForm,options:{renderToken:i,appearance:a,additionalFields:c,billingAddressRequirement:l,onResult:o,onValidityChange:s},remountDeps:[i,c.cardholderName,l],iframePassthrough:{style:d,...f},updateDeps:[a,c,l,o,s]}),(0,n.jsx)(`div`,{ref:p})}function m({ref:r,renderToken:i,appearance:a,onResult:o,onValidityChange:s,billingAddressRequirement:c=`country`,style:l,...d}){let f=(0,t.useRef)(null);return u({containerRef:f,iframeRef:r,mount:e.mountAmosBankAccountPaymentMethodForm,options:{renderToken:i,appearance:a,billingAddressRequirement:c,onResult:o,onValidityChange:s},remountDeps:[i,c],iframePassthrough:{style:l,...d},updateDeps:[a,c,o,s]}),(0,n.jsx)(`div`,{ref:f})}function h({ref:r,renderToken:i,amount:a,merchantName:o,height:s=`48px`,buttonProps:c,iframeProps:l,onInitiatePaymentIntentRequest:d,onResult:p}){let m=(0,t.useRef)(null),h=(0,e.resolveWalletButtonSkeletonBorderRadius)({iframeStyle:l?.style,buttonProps:c});return u({containerRef:m,iframeRef:r,mount:e.mountAmosGooglePayButton,options:{renderToken:i,amount:a,merchantName:o,height:s,buttonProps:c,onInitiatePaymentIntentRequest:d,onResult:p},remountDeps:[i],iframePassthrough:l??{},updateDeps:[a,o,s,c,d,p]}),(0,n.jsx)(f,{height:s,borderRadius:h,containerRef:m})}function g({ref:r,renderToken:i,amount:a,merchantName:o,height:s=`48px`,buttonProps:c,iframeProps:l,onInitiatePaymentIntentRequest:d,onResult:p}){let m=(0,t.useRef)(null),h=(0,e.resolveWalletButtonSkeletonBorderRadius)({iframeStyle:l?.style,buttonProps:c});return u({containerRef:m,iframeRef:r,mount:e.mountAmosApplePayButton,options:{renderToken:i,amount:a,merchantName:o,height:s,buttonProps:c,onInitiatePaymentIntentRequest:d,onResult:p},remountDeps:[i],iframePassthrough:l??{},updateDeps:[a,o,s,c,d,p]}),(0,n.jsx)(f,{height:s,borderRadius:h,containerRef:m})}exports.AmosApplePayButton=g,exports.AmosBankAccountPaymentMethodForm=m,exports.AmosCreditCardPaymentMethodForm=p,exports.AmosGooglePayButton=h,exports.confirmPaymentIntent=a,exports.confirmSetupIntent=o,exports.resetForm=s,exports.validateForm=i,Object.keys(e).forEach(function(t){t!=="default"&&!Object.prototype.hasOwnProperty.call(exports,t)&&Object.defineProperty(exports,t,{enumerable:!0,get:function(){return e[t]}})});
|
package/dist/index.mjs
CHANGED
|
@@ -1,65 +1,102 @@
|
|
|
1
|
-
import { confirmPaymentIntent as e, confirmSetupIntent as t,
|
|
2
|
-
import { useEffect as
|
|
3
|
-
import { jsx as
|
|
1
|
+
import { confirmPaymentIntent as e, confirmSetupIntent as t, ensureSkeletonStyles as n, mountAmosApplePayButton as r, mountAmosBankAccountPaymentMethodForm as i, mountAmosCreditCardPaymentMethodForm as a, mountAmosGooglePayButton as o, resetForm as s, resolveWalletButtonSkeletonBorderRadius as c, validateForm as l } from "@amos.com/amos-js";
|
|
2
|
+
import { useEffect as u, useLayoutEffect as d, useRef as f } from "react";
|
|
3
|
+
import { jsx as p, jsxs as m } from "react/jsx-runtime";
|
|
4
4
|
export * from "@amos.com/amos-js";
|
|
5
5
|
//#region src/index.tsx
|
|
6
|
-
function
|
|
6
|
+
function h(e) {
|
|
7
7
|
return e ? e.current ?? null : null;
|
|
8
8
|
}
|
|
9
|
-
function
|
|
10
|
-
return
|
|
9
|
+
function g({ iframeRef: e }) {
|
|
10
|
+
return l({ iframe: h(e) });
|
|
11
11
|
}
|
|
12
|
-
function
|
|
12
|
+
function _({ iframeRef: t, token: n }) {
|
|
13
13
|
e({
|
|
14
|
-
iframe:
|
|
14
|
+
iframe: h(t),
|
|
15
15
|
token: n
|
|
16
16
|
});
|
|
17
17
|
}
|
|
18
|
-
function
|
|
18
|
+
function v({ iframeRef: e, token: n }) {
|
|
19
19
|
t({
|
|
20
|
-
iframe:
|
|
20
|
+
iframe: h(e),
|
|
21
21
|
token: n
|
|
22
22
|
});
|
|
23
23
|
}
|
|
24
|
-
function
|
|
25
|
-
|
|
24
|
+
function y({ iframeRef: e }) {
|
|
25
|
+
s({ iframe: h(e) });
|
|
26
26
|
}
|
|
27
|
-
function
|
|
27
|
+
function b(e, t) {
|
|
28
28
|
typeof e == "function" ? e(t) : e && (e.current = t);
|
|
29
29
|
}
|
|
30
|
-
function
|
|
30
|
+
function x(e, { style: t, className: n, id: r, ...i }) {
|
|
31
31
|
n != null && (e.className = n), r != null && (e.id = r), Object.assign(e.style, t);
|
|
32
32
|
for (let [t, n] of Object.entries(i)) n != null && (t in e ? Reflect.set(e, t, n) : e.setAttribute(t, String(n)));
|
|
33
33
|
}
|
|
34
|
-
function
|
|
35
|
-
let s =
|
|
36
|
-
|
|
34
|
+
function S({ containerRef: e, iframeRef: t, mount: n, options: r, remountDeps: i, iframePassthrough: a, updateDeps: o }) {
|
|
35
|
+
let s = f(null);
|
|
36
|
+
d(() => {
|
|
37
37
|
let i = e.current;
|
|
38
38
|
if (!i) return;
|
|
39
39
|
let o = n(i, r);
|
|
40
|
-
return s.current = o,
|
|
41
|
-
o.destroy(), s.current = null,
|
|
40
|
+
return s.current = o, b(t, o.iframe), x(o.iframe, a), () => {
|
|
41
|
+
o.destroy(), s.current = null, b(t, null);
|
|
42
42
|
};
|
|
43
|
-
}, [...i]),
|
|
43
|
+
}, [...i]), u(() => {
|
|
44
44
|
s.current?.update(r);
|
|
45
|
-
}, [...o]),
|
|
45
|
+
}, [...o]), u(() => {
|
|
46
46
|
let e = s.current?.iframe;
|
|
47
|
-
e &&
|
|
47
|
+
e && x(e, a);
|
|
48
48
|
});
|
|
49
49
|
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
return
|
|
53
|
-
|
|
50
|
+
var C = "oklch(0.97 0 0)";
|
|
51
|
+
function w({ height: e, borderRadius: t, containerRef: r }) {
|
|
52
|
+
return d(() => {
|
|
53
|
+
n();
|
|
54
|
+
}, []), /* @__PURE__ */ m("div", {
|
|
55
|
+
style: {
|
|
56
|
+
boxSizing: "border-box",
|
|
57
|
+
position: "relative",
|
|
58
|
+
width: "100%",
|
|
59
|
+
height: e,
|
|
60
|
+
minHeight: e,
|
|
61
|
+
overflow: "hidden"
|
|
62
|
+
},
|
|
63
|
+
children: [/* @__PURE__ */ p("div", {
|
|
64
|
+
className: "amos-js-form-skeleton-input amos-js-wallet-skeleton",
|
|
65
|
+
style: {
|
|
66
|
+
position: "absolute",
|
|
67
|
+
inset: 0,
|
|
68
|
+
height: e,
|
|
69
|
+
borderRadius: t,
|
|
70
|
+
background: C,
|
|
71
|
+
pointerEvents: "none",
|
|
72
|
+
zIndex: 0
|
|
73
|
+
},
|
|
74
|
+
"aria-hidden": !0
|
|
75
|
+
}), /* @__PURE__ */ p("div", {
|
|
76
|
+
ref: r,
|
|
77
|
+
style: {
|
|
78
|
+
position: "absolute",
|
|
79
|
+
inset: 0,
|
|
80
|
+
zIndex: 1,
|
|
81
|
+
width: "100%",
|
|
82
|
+
height: "100%"
|
|
83
|
+
}
|
|
84
|
+
})]
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
function T({ ref: e, renderToken: t, appearance: n, onResult: r, onValidityChange: i, additionalFields: o = { cardholderName: !1 }, billingAddressRequirement: s = "country", style: c, ...l }) {
|
|
88
|
+
let u = f(null);
|
|
89
|
+
return S({
|
|
90
|
+
containerRef: u,
|
|
54
91
|
iframeRef: e,
|
|
55
|
-
mount:
|
|
92
|
+
mount: a,
|
|
56
93
|
options: {
|
|
57
94
|
renderToken: t,
|
|
58
95
|
appearance: n,
|
|
59
96
|
additionalFields: o,
|
|
60
97
|
billingAddressRequirement: s,
|
|
61
98
|
onResult: r,
|
|
62
|
-
onValidityChange:
|
|
99
|
+
onValidityChange: i
|
|
63
100
|
},
|
|
64
101
|
remountDeps: [
|
|
65
102
|
t,
|
|
@@ -68,28 +105,28 @@ function y({ ref: e, renderToken: t, appearance: n, onResult: r, onValidityChang
|
|
|
68
105
|
],
|
|
69
106
|
iframePassthrough: {
|
|
70
107
|
style: c,
|
|
71
|
-
...
|
|
108
|
+
...l
|
|
72
109
|
},
|
|
73
110
|
updateDeps: [
|
|
74
111
|
n,
|
|
75
112
|
o,
|
|
76
113
|
s,
|
|
77
114
|
r,
|
|
78
|
-
|
|
115
|
+
i
|
|
79
116
|
]
|
|
80
|
-
}), /* @__PURE__ */
|
|
117
|
+
}), /* @__PURE__ */ p("div", { ref: u });
|
|
81
118
|
}
|
|
82
|
-
function
|
|
83
|
-
let
|
|
84
|
-
return
|
|
85
|
-
containerRef:
|
|
119
|
+
function E({ ref: e, renderToken: t, appearance: n, onResult: r, onValidityChange: a, billingAddressRequirement: o = "country", style: s, ...c }) {
|
|
120
|
+
let l = f(null);
|
|
121
|
+
return S({
|
|
122
|
+
containerRef: l,
|
|
86
123
|
iframeRef: e,
|
|
87
|
-
mount:
|
|
124
|
+
mount: i,
|
|
88
125
|
options: {
|
|
89
126
|
renderToken: t,
|
|
90
127
|
appearance: n,
|
|
91
128
|
billingAddressRequirement: o,
|
|
92
|
-
onResult:
|
|
129
|
+
onResult: r,
|
|
93
130
|
onValidityChange: a
|
|
94
131
|
},
|
|
95
132
|
remountDeps: [t, o],
|
|
@@ -100,25 +137,28 @@ function b({ ref: e, renderToken: t, appearance: n, onResult: i, onValidityChang
|
|
|
100
137
|
updateDeps: [
|
|
101
138
|
n,
|
|
102
139
|
o,
|
|
103
|
-
|
|
140
|
+
r,
|
|
104
141
|
a
|
|
105
142
|
]
|
|
106
|
-
}), /* @__PURE__ */
|
|
143
|
+
}), /* @__PURE__ */ p("div", { ref: l });
|
|
107
144
|
}
|
|
108
|
-
function
|
|
109
|
-
let
|
|
110
|
-
|
|
111
|
-
|
|
145
|
+
function D({ ref: e, renderToken: t, amount: n, merchantName: r, height: i = "48px", buttonProps: a, iframeProps: s, onInitiatePaymentIntentRequest: l, onResult: u }) {
|
|
146
|
+
let d = f(null), m = c({
|
|
147
|
+
iframeStyle: s?.style,
|
|
148
|
+
buttonProps: a
|
|
149
|
+
});
|
|
150
|
+
return S({
|
|
151
|
+
containerRef: d,
|
|
112
152
|
iframeRef: e,
|
|
113
|
-
mount:
|
|
153
|
+
mount: o,
|
|
114
154
|
options: {
|
|
115
155
|
renderToken: t,
|
|
116
156
|
amount: n,
|
|
117
157
|
merchantName: r,
|
|
118
158
|
height: i,
|
|
119
|
-
buttonProps:
|
|
120
|
-
onInitiatePaymentIntentRequest:
|
|
121
|
-
onResult:
|
|
159
|
+
buttonProps: a,
|
|
160
|
+
onInitiatePaymentIntentRequest: l,
|
|
161
|
+
onResult: u
|
|
122
162
|
},
|
|
123
163
|
remountDeps: [t],
|
|
124
164
|
iframePassthrough: s ?? {},
|
|
@@ -126,38 +166,49 @@ function x({ ref: e, renderToken: t, amount: n, merchantName: r, height: i = "48
|
|
|
126
166
|
n,
|
|
127
167
|
r,
|
|
128
168
|
i,
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
169
|
+
a,
|
|
170
|
+
l,
|
|
171
|
+
u
|
|
132
172
|
]
|
|
133
|
-
}), /* @__PURE__ */
|
|
173
|
+
}), /* @__PURE__ */ p(w, {
|
|
174
|
+
height: i,
|
|
175
|
+
borderRadius: m,
|
|
176
|
+
containerRef: d
|
|
177
|
+
});
|
|
134
178
|
}
|
|
135
|
-
function
|
|
136
|
-
let
|
|
137
|
-
|
|
138
|
-
|
|
179
|
+
function O({ ref: e, renderToken: t, amount: n, merchantName: i, height: a = "48px", buttonProps: o, iframeProps: s, onInitiatePaymentIntentRequest: l, onResult: u }) {
|
|
180
|
+
let d = f(null), m = c({
|
|
181
|
+
iframeStyle: s?.style,
|
|
182
|
+
buttonProps: o
|
|
183
|
+
});
|
|
184
|
+
return S({
|
|
185
|
+
containerRef: d,
|
|
139
186
|
iframeRef: e,
|
|
140
|
-
mount:
|
|
187
|
+
mount: r,
|
|
141
188
|
options: {
|
|
142
189
|
renderToken: t,
|
|
143
|
-
amount:
|
|
190
|
+
amount: n,
|
|
144
191
|
merchantName: i,
|
|
145
192
|
height: a,
|
|
146
193
|
buttonProps: o,
|
|
147
|
-
onInitiatePaymentIntentRequest:
|
|
148
|
-
onResult:
|
|
194
|
+
onInitiatePaymentIntentRequest: l,
|
|
195
|
+
onResult: u
|
|
149
196
|
},
|
|
150
197
|
remountDeps: [t],
|
|
151
198
|
iframePassthrough: s ?? {},
|
|
152
199
|
updateDeps: [
|
|
153
|
-
|
|
200
|
+
n,
|
|
154
201
|
i,
|
|
155
202
|
a,
|
|
156
203
|
o,
|
|
157
|
-
|
|
158
|
-
|
|
204
|
+
l,
|
|
205
|
+
u
|
|
159
206
|
]
|
|
160
|
-
}), /* @__PURE__ */
|
|
207
|
+
}), /* @__PURE__ */ p(w, {
|
|
208
|
+
height: a,
|
|
209
|
+
borderRadius: m,
|
|
210
|
+
containerRef: d
|
|
211
|
+
});
|
|
161
212
|
}
|
|
162
213
|
//#endregion
|
|
163
|
-
export {
|
|
214
|
+
export { O as AmosApplePayButton, E as AmosBankAccountPaymentMethodForm, T as AmosCreditCardPaymentMethodForm, D as AmosGooglePayButton, _ as confirmPaymentIntent, v as confirmSetupIntent, y as resetForm, g as validateForm };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@amos.com/react-amos-js",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.16",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
"vite-plugin-dts": "5.0.3"
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
|
-
"@amos.com/amos-js": "0.9.
|
|
51
|
+
"@amos.com/amos-js": "0.9.17",
|
|
52
52
|
"@types/googlepay": "0.7.11"
|
|
53
53
|
},
|
|
54
54
|
"peerDependencies": {
|
package/src/index.tsx
CHANGED
|
@@ -10,11 +10,13 @@ import {
|
|
|
10
10
|
type BillingAddressRequirement,
|
|
11
11
|
type ConfirmationResult,
|
|
12
12
|
type CreditCardAdditionalFields,
|
|
13
|
+
ensureSkeletonStyles,
|
|
13
14
|
type GooglePayButtonElementProps,
|
|
14
15
|
mountAmosApplePayButton,
|
|
15
16
|
mountAmosBankAccountPaymentMethodForm,
|
|
16
17
|
mountAmosCreditCardPaymentMethodForm,
|
|
17
18
|
mountAmosGooglePayButton,
|
|
19
|
+
resolveWalletButtonSkeletonBorderRadius,
|
|
18
20
|
} from "@amos.com/amos-js";
|
|
19
21
|
import type { components } from "@amos.com/node";
|
|
20
22
|
import {
|
|
@@ -22,6 +24,7 @@ import {
|
|
|
22
24
|
type Ref,
|
|
23
25
|
type RefObject,
|
|
24
26
|
useEffect,
|
|
27
|
+
useLayoutEffect,
|
|
25
28
|
useRef,
|
|
26
29
|
} from "react";
|
|
27
30
|
|
|
@@ -157,7 +160,7 @@ function useAmosEmbed<TOptions extends Record<string, unknown>>({
|
|
|
157
160
|
const controllerRef = useRef<AmosEmbedController | null>(null);
|
|
158
161
|
|
|
159
162
|
// biome-ignore lint/correctness/useExhaustiveDependencies: remount only when remountDeps change
|
|
160
|
-
|
|
163
|
+
useLayoutEffect(() => {
|
|
161
164
|
const container = containerRef.current;
|
|
162
165
|
if (!container) {
|
|
163
166
|
return;
|
|
@@ -188,6 +191,59 @@ function useAmosEmbed<TOptions extends Record<string, unknown>>({
|
|
|
188
191
|
});
|
|
189
192
|
}
|
|
190
193
|
|
|
194
|
+
const SKELETON_ACCENT = "oklch(0.97 0 0)";
|
|
195
|
+
|
|
196
|
+
function WalletButtonSlot({
|
|
197
|
+
height,
|
|
198
|
+
borderRadius,
|
|
199
|
+
containerRef,
|
|
200
|
+
}: {
|
|
201
|
+
height: string;
|
|
202
|
+
borderRadius: string;
|
|
203
|
+
containerRef: RefObject<HTMLDivElement | null>;
|
|
204
|
+
}) {
|
|
205
|
+
useLayoutEffect(() => {
|
|
206
|
+
ensureSkeletonStyles();
|
|
207
|
+
}, []);
|
|
208
|
+
|
|
209
|
+
return (
|
|
210
|
+
<div
|
|
211
|
+
style={{
|
|
212
|
+
boxSizing: "border-box",
|
|
213
|
+
position: "relative",
|
|
214
|
+
width: "100%",
|
|
215
|
+
height,
|
|
216
|
+
minHeight: height,
|
|
217
|
+
overflow: "hidden",
|
|
218
|
+
}}
|
|
219
|
+
>
|
|
220
|
+
<div
|
|
221
|
+
className="amos-js-form-skeleton-input amos-js-wallet-skeleton"
|
|
222
|
+
style={{
|
|
223
|
+
position: "absolute",
|
|
224
|
+
inset: 0,
|
|
225
|
+
height,
|
|
226
|
+
borderRadius,
|
|
227
|
+
background: SKELETON_ACCENT,
|
|
228
|
+
pointerEvents: "none",
|
|
229
|
+
zIndex: 0,
|
|
230
|
+
}}
|
|
231
|
+
aria-hidden
|
|
232
|
+
/>
|
|
233
|
+
<div
|
|
234
|
+
ref={containerRef}
|
|
235
|
+
style={{
|
|
236
|
+
position: "absolute",
|
|
237
|
+
inset: 0,
|
|
238
|
+
zIndex: 1,
|
|
239
|
+
width: "100%",
|
|
240
|
+
height: "100%",
|
|
241
|
+
}}
|
|
242
|
+
/>
|
|
243
|
+
</div>
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
|
|
191
247
|
type AmosCreditCardPaymentMethodFormProps = IframePassthroughProps & {
|
|
192
248
|
renderToken: string;
|
|
193
249
|
appearance?: Appearance;
|
|
@@ -295,6 +351,11 @@ export function AmosBankAccountPaymentMethodForm({
|
|
|
295
351
|
type AmosGooglePayButtonProps = {
|
|
296
352
|
ref?: ForwardedIframeRef;
|
|
297
353
|
renderToken: string;
|
|
354
|
+
/**
|
|
355
|
+
* Major-currency decimal string shown in the Google Pay sheet
|
|
356
|
+
* (e.g. `"50.00"` for $50.00). Converted to cents in
|
|
357
|
+
* `paymentIntentCreateAttributes.amount`.
|
|
358
|
+
*/
|
|
298
359
|
amount: string;
|
|
299
360
|
merchantName: string;
|
|
300
361
|
/**
|
|
@@ -321,6 +382,11 @@ type AmosGooglePayButtonProps = {
|
|
|
321
382
|
onResult: (result: ConfirmationResult) => void;
|
|
322
383
|
};
|
|
323
384
|
|
|
385
|
+
/**
|
|
386
|
+
* Renders the secure Google Pay iframe button. A button-shaped
|
|
387
|
+
* skeleton is painted in the parent document on first render (including
|
|
388
|
+
* SSR) so the slot height is reserved before the iframe loads.
|
|
389
|
+
*/
|
|
324
390
|
export function AmosGooglePayButton({
|
|
325
391
|
ref,
|
|
326
392
|
renderToken,
|
|
@@ -333,6 +399,10 @@ export function AmosGooglePayButton({
|
|
|
333
399
|
onResult,
|
|
334
400
|
}: AmosGooglePayButtonProps) {
|
|
335
401
|
const containerRef = useRef<HTMLDivElement | null>(null);
|
|
402
|
+
const borderRadius = resolveWalletButtonSkeletonBorderRadius({
|
|
403
|
+
iframeStyle: iframeProps?.style as { borderRadius?: string | number },
|
|
404
|
+
buttonProps,
|
|
405
|
+
});
|
|
336
406
|
|
|
337
407
|
useAmosEmbed({
|
|
338
408
|
containerRef,
|
|
@@ -359,12 +429,23 @@ export function AmosGooglePayButton({
|
|
|
359
429
|
],
|
|
360
430
|
});
|
|
361
431
|
|
|
362
|
-
return
|
|
432
|
+
return (
|
|
433
|
+
<WalletButtonSlot
|
|
434
|
+
height={height}
|
|
435
|
+
borderRadius={borderRadius}
|
|
436
|
+
containerRef={containerRef}
|
|
437
|
+
/>
|
|
438
|
+
);
|
|
363
439
|
}
|
|
364
440
|
|
|
365
441
|
type AmosApplePayButtonProps = {
|
|
366
442
|
ref?: ForwardedIframeRef;
|
|
367
443
|
renderToken: string;
|
|
444
|
+
/**
|
|
445
|
+
* Major-currency decimal string shown in the Apple Pay sheet
|
|
446
|
+
* (e.g. `"50.00"` for $50.00). Converted to cents in
|
|
447
|
+
* `paymentIntentCreateAttributes.amount`.
|
|
448
|
+
*/
|
|
368
449
|
amount: string;
|
|
369
450
|
merchantName: string;
|
|
370
451
|
/**
|
|
@@ -391,6 +472,11 @@ type AmosApplePayButtonProps = {
|
|
|
391
472
|
onResult: (result: ConfirmationResult) => void;
|
|
392
473
|
};
|
|
393
474
|
|
|
475
|
+
/**
|
|
476
|
+
* Renders the secure Apple Pay iframe button. A button-shaped
|
|
477
|
+
* skeleton is painted in the parent document on first render (including
|
|
478
|
+
* SSR) so the slot height is reserved before the iframe loads.
|
|
479
|
+
*/
|
|
394
480
|
export function AmosApplePayButton({
|
|
395
481
|
ref,
|
|
396
482
|
renderToken,
|
|
@@ -403,6 +489,10 @@ export function AmosApplePayButton({
|
|
|
403
489
|
onResult,
|
|
404
490
|
}: AmosApplePayButtonProps) {
|
|
405
491
|
const containerRef = useRef<HTMLDivElement | null>(null);
|
|
492
|
+
const borderRadius = resolveWalletButtonSkeletonBorderRadius({
|
|
493
|
+
iframeStyle: iframeProps?.style as { borderRadius?: string | number },
|
|
494
|
+
buttonProps,
|
|
495
|
+
});
|
|
406
496
|
|
|
407
497
|
useAmosEmbed({
|
|
408
498
|
containerRef,
|
|
@@ -429,5 +519,11 @@ export function AmosApplePayButton({
|
|
|
429
519
|
],
|
|
430
520
|
});
|
|
431
521
|
|
|
432
|
-
return
|
|
522
|
+
return (
|
|
523
|
+
<WalletButtonSlot
|
|
524
|
+
height={height}
|
|
525
|
+
borderRadius={borderRadius}
|
|
526
|
+
containerRef={containerRef}
|
|
527
|
+
/>
|
|
528
|
+
);
|
|
433
529
|
}
|