@amos.com/amos-js 0.1.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.
@@ -0,0 +1,95 @@
1
+ import { GooglePayButtonController, GooglePayButtonListenerOptions } from './google-pay';
2
+ import { CreditCardAdditionalFields, PaymentMethodFormController, PaymentMethodFormListenerOptions } from './payment-method-form';
3
+ type Container = HTMLElement | string;
4
+ /**
5
+ * Options accepted by {@link mountAmosCreditCardPaymentMethodForm}.
6
+ */
7
+ export type AmosCreditCardPaymentMethodFormOptions = PaymentMethodFormListenerOptions & {
8
+ /**
9
+ * The Amos render token for the credit-card payment method form.
10
+ *
11
+ * It is safe to pass this to the client. Create this on
12
+ * https://dashboard.amos.com.
13
+ */
14
+ renderToken: string;
15
+ /**
16
+ * The additional fields that are required to be filled out in the
17
+ * form in addition to the card number, expiration date, CVV,
18
+ * country, and postal code.
19
+ *
20
+ * @default { cardholderName: false }
21
+ */
22
+ additionalFields?: CreditCardAdditionalFields;
23
+ };
24
+ /**
25
+ * Controller returned by {@link mountAmosCreditCardPaymentMethodForm}
26
+ * and {@link mountAmosBankAccountPaymentMethodForm}.
27
+ */
28
+ export type AmosPaymentMethodFormMountController = PaymentMethodFormController & {
29
+ /**
30
+ * The underlying `<iframe>` element. Pass this as the `iframe`
31
+ * argument to {@link validateForm}, {@link confirmPaymentIntent}, or
32
+ * {@link confirmSetupIntent}.
33
+ */
34
+ iframe: HTMLIFrameElement;
35
+ };
36
+ /**
37
+ * Mount the secure credit-card payment method form into a container
38
+ * element. Returns a controller exposing the underlying iframe, an
39
+ * `update()` method, and a `destroy()` method.
40
+ *
41
+ * Use the returned `controller.iframe` when calling
42
+ * {@link validateForm}, {@link confirmPaymentIntent}, or
43
+ * {@link confirmSetupIntent}.
44
+ */
45
+ export declare function mountAmosCreditCardPaymentMethodForm(container: Container, options: AmosCreditCardPaymentMethodFormOptions): AmosPaymentMethodFormMountController;
46
+ /**
47
+ * Options accepted by {@link mountAmosBankAccountPaymentMethodForm}.
48
+ */
49
+ export type AmosBankAccountPaymentMethodFormOptions = PaymentMethodFormListenerOptions & {
50
+ /**
51
+ * The Amos render token for the bank-account payment method form.
52
+ *
53
+ * It is safe to pass this to the client. Create this on
54
+ * https://dashboard.amos.com.
55
+ */
56
+ renderToken: string;
57
+ };
58
+ /**
59
+ * Mount the secure bank-account payment method form into a container
60
+ * element. Returns a controller exposing the underlying iframe, an
61
+ * `update()` method, and a `destroy()` method.
62
+ *
63
+ * Use the returned `controller.iframe` when calling
64
+ * {@link validateForm}, {@link confirmPaymentIntent}, or
65
+ * {@link confirmSetupIntent}.
66
+ */
67
+ export declare function mountAmosBankAccountPaymentMethodForm(container: Container, options: AmosBankAccountPaymentMethodFormOptions): AmosPaymentMethodFormMountController;
68
+ /**
69
+ * Options accepted by {@link mountAmosGooglePayButton}.
70
+ */
71
+ export type AmosGooglePayButtonOptions = GooglePayButtonListenerOptions & {
72
+ /**
73
+ * The Amos render token for the Google Pay button.
74
+ *
75
+ * It is safe to pass this to the client. Create this on
76
+ * https://dashboard.amos.com.
77
+ */
78
+ renderToken: string;
79
+ };
80
+ /**
81
+ * Controller returned by {@link mountAmosGooglePayButton}.
82
+ */
83
+ export type AmosGooglePayButtonMountController = GooglePayButtonController & {
84
+ /**
85
+ * The underlying `<iframe>` element.
86
+ */
87
+ iframe: HTMLIFrameElement;
88
+ };
89
+ /**
90
+ * Mount the secure Google Pay button (express checkout) into a
91
+ * container element. Returns a controller exposing the underlying
92
+ * iframe, an `update()` method, and a `destroy()` method.
93
+ */
94
+ export declare function mountAmosGooglePayButton(container: Container, options: AmosGooglePayButtonOptions): AmosGooglePayButtonMountController;
95
+ export {};
@@ -0,0 +1,90 @@
1
+ import { components } from '@amos.com/node';
2
+ import { Appearance } from './types';
3
+ /**
4
+ * The additional fields beyond the standard card number, expiration
5
+ * date, CVV, country, and postal code that are required to be filled
6
+ * out in the embedded credit-card form.
7
+ */
8
+ export type CreditCardAdditionalFields = {
9
+ cardholderName: boolean;
10
+ };
11
+ /**
12
+ * Build the iframe `src` URL for the embedded credit-card form.
13
+ */
14
+ export declare function getCreditCardFormSrc(renderToken: string, additionalFields?: CreditCardAdditionalFields): string;
15
+ /**
16
+ * Build the iframe `src` URL for the embedded bank-account form.
17
+ */
18
+ export declare function getBankAccountFormSrc(renderToken: string): string;
19
+ /**
20
+ * Default iframe pixel height for the credit-card form, taking the
21
+ * configured `additionalFields` into account.
22
+ */
23
+ export declare function getCreditCardFormInitialHeight(additionalFields?: CreditCardAdditionalFields): string;
24
+ /**
25
+ * Default iframe pixel height for the bank-account form.
26
+ */
27
+ export declare function getBankAccountFormInitialHeight(): string;
28
+ /**
29
+ * Options accepted by {@link attachPaymentMethodFormListeners}.
30
+ *
31
+ * Used by both the credit-card and bank-account forms, which share the
32
+ * same message protocol.
33
+ */
34
+ export type PaymentMethodFormListenerOptions = {
35
+ /**
36
+ * Custom appearance to apply when the iframe first becomes ready and
37
+ * whenever the appearance changes. Can be updated later via the
38
+ * returned controller's `update({ appearance })` method.
39
+ */
40
+ appearance?: Appearance;
41
+ /**
42
+ * Called whenever the iframe asks the host page to resize it. Update
43
+ * the iframe's `height` style here.
44
+ */
45
+ onHeightChange?: (height: string) => void;
46
+ /**
47
+ * Called once the iframe has applied the requested appearance and is
48
+ * ready to be revealed. A common implementation is to set the
49
+ * iframe's opacity from `0` to `1` to fade it in.
50
+ */
51
+ onAppearanceReady?: () => void;
52
+ /**
53
+ * Called when payment intent confirmation succeeds.
54
+ */
55
+ onPaymentIntentConfirmationSucceeded?: (paymentIntent: components["schemas"]["PaymentIntent"]) => void;
56
+ /**
57
+ * Called when setup intent confirmation succeeds.
58
+ */
59
+ onSetupIntentConfirmationSucceeded?: (setupIntent: components["schemas"]["SetupIntent"]) => void;
60
+ /**
61
+ * Called when payment or setup intent confirmation fails.
62
+ */
63
+ onConfirmationFailed: (errorMessage: string) => void;
64
+ };
65
+ /**
66
+ * Controller returned by {@link attachPaymentMethodFormListeners} and
67
+ * the credit-card / bank-account `mount*` helpers.
68
+ */
69
+ export type PaymentMethodFormController = {
70
+ /**
71
+ * Update one or more listener options without re-attaching the
72
+ * message listener. Pass `{ appearance }` to push new appearance
73
+ * overrides into the iframe.
74
+ */
75
+ update: (patch: Partial<PaymentMethodFormListenerOptions>) => void;
76
+ /**
77
+ * Detach the iframe message listener.
78
+ */
79
+ destroy: () => void;
80
+ };
81
+ /**
82
+ * Wire up the host-page side of the credit-card or bank-account iframe
83
+ * message protocol on an existing `<iframe>` element. Returns a
84
+ * controller for updating options and tearing down the listener.
85
+ *
86
+ * The iframe is expected to have already been added to the DOM with the
87
+ * correct `src` (see {@link getCreditCardFormSrc} /
88
+ * {@link getBankAccountFormSrc}).
89
+ */
90
+ export declare function attachPaymentMethodFormListeners(iframe: HTMLIFrameElement, options: PaymentMethodFormListenerOptions): PaymentMethodFormController;
@@ -0,0 +1,64 @@
1
+ import { components } from '@amos.com/node';
2
+ /**
3
+ * CSS custom properties that control the appearance of the embedded
4
+ * Amos iframe UI. Only the variables you provide are sent; omitted
5
+ * variables keep their defaults.
6
+ */
7
+ export type ThemeVariable = "--background" | "--foreground" | "--primary" | "--primary-foreground" | "--secondary" | "--secondary-foreground" | "--muted-foreground" | "--accent" | "--accent-foreground" | "--destructive" | "--border" | "--input" | "--ring" | "--radius";
8
+ /**
9
+ * Appearance overrides for the embedded Amos iframe UI.
10
+ */
11
+ export type Appearance = {
12
+ themeVariables?: Partial<Record<ThemeVariable, string>>;
13
+ };
14
+ /**
15
+ * Typed `postMessage` payloads exchanged between the host page and the
16
+ * embedded Amos iframe.
17
+ */
18
+ export type Message = {
19
+ type: "IFRAME_READY";
20
+ } | {
21
+ type: "PARENT_ACKNOWLEDGED_IFRAME_READY";
22
+ } | {
23
+ type: "UPDATE_HEIGHT";
24
+ height: string;
25
+ } | {
26
+ type: "UPDATE_AMOUNT";
27
+ amount: string;
28
+ } | {
29
+ type: "UPDATE_MERCHANT_NAME";
30
+ merchantName: string;
31
+ } | {
32
+ type: "UPDATE_APPEARANCE";
33
+ appearance: Appearance;
34
+ } | {
35
+ type: "VALIDATE_FORM";
36
+ requestId: string;
37
+ isValid?: boolean;
38
+ } | {
39
+ type: "CREATE_PAYMENT_INTENT";
40
+ paymentIntentCreateAttributes: components["schemas"]["CreatePaymentIntentInput"];
41
+ customerCreateAttributes: components["schemas"]["CreateCustomerInput"];
42
+ } | ({
43
+ type: "CONFIRM_PAYMENT_INTENT";
44
+ } & Pick<components["schemas"]["PaymentIntent"], "id"> & Pick<components["schemas"]["EmbedToken"], "token">) | ({
45
+ type: "CONFIRM_SETUP_INTENT";
46
+ } & Pick<components["schemas"]["SetupIntent"], "id"> & Pick<components["schemas"]["EmbedToken"], "token">) | {
47
+ type: "PAYMENT_INTENT_CONFIRMATION_SUCCEEDED";
48
+ paymentIntent: components["schemas"]["PaymentIntent"];
49
+ } | {
50
+ type: "SETUP_INTENT_CONFIRMATION_SUCCEEDED";
51
+ setupIntent: components["schemas"]["SetupIntent"];
52
+ } | {
53
+ type: "CONFIRMATION_FAILED";
54
+ errorMessage: string;
55
+ } | {
56
+ type: "UPDATED_APPEARANCE";
57
+ };
58
+ /**
59
+ * Identity helper that brands an object as a typed `Message`.
60
+ *
61
+ * Useful when constructing `postMessage` payloads to ensure they conform
62
+ * to the schema understood by the embedded Amos iframe.
63
+ */
64
+ export declare function createMessage(message: Message): Message;
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "@amos.com/amos-js",
3
+ "version": "0.1.0",
4
+ "main": "dist/index.js",
5
+ "module": "dist/index.mjs",
6
+ "types": "dist/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/index.d.ts",
10
+ "import": "./dist/index.mjs",
11
+ "require": "./dist/index.js"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist",
16
+ "src"
17
+ ],
18
+ "scripts": {
19
+ "biome": "biome check --write",
20
+ "dev": "vite",
21
+ "prebuild": "npm run typecheck",
22
+ "build": "vite build",
23
+ "preview": "vite preview",
24
+ "typecheck": "tsc --noEmit",
25
+ "prepack": "npm run build",
26
+ "changeset": "changeset",
27
+ "version": "changeset version",
28
+ "release": "npm run build && changeset publish"
29
+ },
30
+ "author": "Amos",
31
+ "description": "Amos JavaScript SDK for embedding payment methods via iframes.",
32
+ "devDependencies": {
33
+ "@changesets/cli": "2.31.0",
34
+ "@biomejs/biome": "2.4.15",
35
+ "@types/node": "25.9.1",
36
+ "typescript": "6.0.3",
37
+ "vite": "7.3.3",
38
+ "vite-plugin-dts": "4.5.4"
39
+ },
40
+ "dependencies": {
41
+ "@amos.com/node": "0.1.6",
42
+ "@types/googlepay": "0.7.10"
43
+ },
44
+ "peerDependencies": {
45
+ "openapi-fetch": "^0 || ^1"
46
+ }
47
+ }
@@ -0,0 +1,234 @@
1
+ /// <reference types="googlepay" />
2
+
3
+ import type { components } from "@amos.com/node";
4
+ import { getEmbedOrigin } from "./jwt";
5
+ import {
6
+ confirmPaymentIntent,
7
+ sendConfirmationFailed,
8
+ sendParentReadyMessage,
9
+ updateAmount as sendUpdateAmount,
10
+ updateAppearance as sendUpdateAppearance,
11
+ updateMerchantName as sendUpdateMerchantName,
12
+ } from "./messaging";
13
+ import type { Appearance, Message } from "./types";
14
+
15
+ /**
16
+ * Build the iframe `src` URL for the embedded Google Pay button.
17
+ */
18
+ export function getGooglePayButtonSrc(renderToken: string): string {
19
+ return `${getEmbedOrigin(renderToken)}/iframe/google-pay?token=${renderToken}`;
20
+ }
21
+
22
+ /**
23
+ * Default iframe pixel height for the Google Pay button.
24
+ */
25
+ export function getGooglePayButtonInitialHeight(): string {
26
+ return "40px";
27
+ }
28
+
29
+ /**
30
+ * Options accepted by {@link attachGooglePayButtonListeners}.
31
+ */
32
+ export type GooglePayButtonListenerOptions = {
33
+ /** The amount of the payment, in the same format passed in props. */
34
+ amount: string;
35
+ /** A user-visible merchant name. */
36
+ merchantName: string;
37
+ /**
38
+ * Custom appearance to apply when the iframe first becomes ready and
39
+ * whenever the appearance changes.
40
+ */
41
+ appearance?: Appearance;
42
+ /**
43
+ * Called whenever the iframe asks the host page to resize it. Update
44
+ * the iframe's `height` style here.
45
+ */
46
+ onHeightChange?: (height: string) => void;
47
+ /**
48
+ * Called once the iframe has applied the requested appearance and is
49
+ * ready to be revealed.
50
+ */
51
+ onAppearanceReady?: () => void;
52
+ /**
53
+ * Called when the user initiates a payment intent request via the
54
+ * Google Pay button. Your implementation should create a payment
55
+ * intent on your server and resolve with the resulting embed token.
56
+ */
57
+ onInitiatePaymentIntentRequest: ({
58
+ paymentIntentCreateAttributes,
59
+ customerCreateAttributes,
60
+ }: {
61
+ paymentIntentCreateAttributes: components["schemas"]["CreatePaymentIntentInput"];
62
+ customerCreateAttributes: components["schemas"]["CreateCustomerInput"];
63
+ }) => Promise<components["schemas"]["EmbedToken"]["token"]>;
64
+ /**
65
+ * Called when payment intent confirmation succeeds.
66
+ */
67
+ onPaymentIntentConfirmationSucceeded: (
68
+ paymentIntent: components["schemas"]["PaymentIntent"],
69
+ ) => void;
70
+ /**
71
+ * Called when payment intent confirmation fails.
72
+ */
73
+ onConfirmationFailed: (errorMessage: string) => void;
74
+ };
75
+
76
+ /**
77
+ * Controller returned by {@link attachGooglePayButtonListeners} and
78
+ * {@link mountAmosGooglePayButton}.
79
+ */
80
+ export type GooglePayButtonController = {
81
+ /**
82
+ * Update one or more listener options without re-attaching the
83
+ * message listener. Pass `amount` or `merchantName` to push the new
84
+ * value into the iframe; pass `appearance` to update theme variables.
85
+ */
86
+ update: (patch: Partial<GooglePayButtonListenerOptions>) => void;
87
+ /**
88
+ * Detach the iframe message listener.
89
+ */
90
+ destroy: () => void;
91
+ };
92
+
93
+ /**
94
+ * Wire up the host-page side of the Google Pay iframe message protocol
95
+ * on an existing `<iframe>` element. Returns a controller for updating
96
+ * options and tearing down the listener.
97
+ *
98
+ * The iframe is expected to have already been added to the DOM with the
99
+ * correct `src` (see {@link getGooglePayButtonSrc}).
100
+ */
101
+ export function attachGooglePayButtonListeners(
102
+ iframe: HTMLIFrameElement,
103
+ options: GooglePayButtonListenerOptions,
104
+ ): GooglePayButtonController {
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
+ }
188
+
189
+ /**
190
+ * Transform raw Google Pay payment data into an Amos-compatible
191
+ * `paymentMethod` payload. Use this when integrating with the raw
192
+ * Google Pay API directly instead of through
193
+ * {@link mountAmosGooglePayButton} (or the React equivalent).
194
+ */
195
+ export function formatGooglePayPaymentData({
196
+ paymentData,
197
+ }: {
198
+ paymentData: google.payments.api.PaymentData;
199
+ }) {
200
+ return {
201
+ paymentMethod: {
202
+ billing_address_attributes: {
203
+ name: paymentData.shippingAddress?.name,
204
+ address_line1: paymentData.shippingAddress?.address1,
205
+ address_line2: paymentData.shippingAddress?.address2,
206
+ city: paymentData.shippingAddress?.locality,
207
+ state: paymentData.shippingAddress?.administrativeArea,
208
+ postal_code: paymentData.shippingAddress?.postalCode,
209
+ country: paymentData.shippingAddress?.countryCode,
210
+ email: paymentData.email,
211
+ phone: paymentData.shippingAddress?.phoneNumber,
212
+ },
213
+ card_profile_attributes: {
214
+ wallet_provider: "googlepay",
215
+ wallet_payload: paymentData.paymentMethodData.tokenizationData.token,
216
+ wallet_last4: paymentData.paymentMethodData.info?.cardDetails,
217
+ wallet_brand: (() => {
218
+ switch (paymentData.paymentMethodData.info?.cardNetwork) {
219
+ case "AMEX":
220
+ return "american_express";
221
+ case "VISA":
222
+ return "visa";
223
+ case "MASTERCARD":
224
+ return "master";
225
+ case "DISCOVER":
226
+ return "discover";
227
+ default:
228
+ return undefined;
229
+ }
230
+ })(),
231
+ },
232
+ },
233
+ };
234
+ }
package/src/index.ts ADDED
@@ -0,0 +1,101 @@
1
+ /// <reference types="googlepay" />
2
+
3
+ import type { components } from "@amos.com/node";
4
+
5
+ export type {
6
+ GooglePayButtonController,
7
+ GooglePayButtonListenerOptions,
8
+ } from "./google-pay";
9
+ export {
10
+ attachGooglePayButtonListeners,
11
+ formatGooglePayPaymentData,
12
+ getGooglePayButtonInitialHeight,
13
+ getGooglePayButtonSrc,
14
+ } from "./google-pay";
15
+
16
+ export { decodeJwt, getEmbedOrigin } from "./jwt";
17
+
18
+ export {
19
+ confirmPaymentIntent,
20
+ confirmSetupIntent,
21
+ sendConfirmationFailed,
22
+ sendParentReadyMessage,
23
+ updateAmount,
24
+ updateAppearance,
25
+ updateMerchantName,
26
+ validateForm,
27
+ } from "./messaging";
28
+ export type {
29
+ AmosBankAccountPaymentMethodFormOptions,
30
+ AmosCreditCardPaymentMethodFormOptions,
31
+ AmosGooglePayButtonMountController,
32
+ AmosGooglePayButtonOptions,
33
+ AmosPaymentMethodFormMountController,
34
+ } from "./mount";
35
+ export {
36
+ mountAmosBankAccountPaymentMethodForm,
37
+ mountAmosCreditCardPaymentMethodForm,
38
+ mountAmosGooglePayButton,
39
+ } from "./mount";
40
+ export type {
41
+ CreditCardAdditionalFields,
42
+ PaymentMethodFormController,
43
+ PaymentMethodFormListenerOptions,
44
+ } from "./payment-method-form";
45
+ export {
46
+ attachPaymentMethodFormListeners,
47
+ getBankAccountFormInitialHeight,
48
+ getBankAccountFormSrc,
49
+ getCreditCardFormInitialHeight,
50
+ getCreditCardFormSrc,
51
+ } from "./payment-method-form";
52
+ export type {
53
+ Appearance,
54
+ Message,
55
+ ThemeVariable,
56
+ } from "./types";
57
+ export { createMessage } from "./types";
58
+
59
+ /**
60
+ * Convenience alias for `components["schemas"]["CreateCustomerInput"]`.
61
+ */
62
+ export type CreateCustomerInput = components["schemas"]["CreateCustomerInput"];
63
+ /**
64
+ * Convenience alias for
65
+ * `components["schemas"]["CreatePaymentIntentInput"]`.
66
+ */
67
+ export type CreatePaymentIntentInput =
68
+ components["schemas"]["CreatePaymentIntentInput"];
69
+ /**
70
+ * Convenience alias for
71
+ * `components["schemas"]["CreateSetupIntentInput"]`.
72
+ */
73
+ export type CreateSetupIntentInput =
74
+ components["schemas"]["CreateSetupIntentInput"];
75
+ /**
76
+ * Convenience alias for `components["schemas"]["PaymentIntent"]`.
77
+ */
78
+ export type PaymentIntent = components["schemas"]["PaymentIntent"];
79
+ /**
80
+ * Convenience alias for `components["schemas"]["SetupIntent"]`.
81
+ */
82
+ export type SetupIntent = components["schemas"]["SetupIntent"];
83
+ /**
84
+ * API envelope `{ token?, ttl? }` for a minted embed JWT.
85
+ *
86
+ * `POST /payment_intents` and `POST /setup_intents` resolve to this
87
+ * shape. {@link confirmPaymentIntent}, {@link confirmSetupIntent}, and
88
+ * the Google Pay `onInitiatePaymentIntentRequest` return type use
89
+ * `Pick<EmbedToken, "token">` (the JWT string returned by your server).
90
+ */
91
+ export type EmbedToken = components["schemas"]["EmbedToken"];
92
+ /**
93
+ * Decoded JWT payload for an embed token (`account_id`,
94
+ * `payment_intent_id`, `setup_intent_id`, etc.).
95
+ */
96
+ export type EmbedTokenJwt = components["schemas"]["EmbedTokenJwt"];
97
+ /**
98
+ * Decoded JWT payload for the dashboard render token (`env`, `origins`,
99
+ * `allowed_payment_method_types`, `render_template_id`, etc.).
100
+ */
101
+ export type RenderTokenJwt = components["schemas"]["RenderTokenJwt"];
package/src/jwt.ts ADDED
@@ -0,0 +1,44 @@
1
+ import type { components } from "@amos.com/node";
2
+
3
+ /**
4
+ * Decode a JWT into its `{ header, payload, signature }` parts without
5
+ * verifying the signature.
6
+ *
7
+ * Works in both browser (`atob`) and Node (`Buffer`) environments.
8
+ */
9
+ export function decodeJwt(token: string | undefined): {
10
+ header: Record<string, unknown>;
11
+ payload: Record<string, unknown>;
12
+ signature: string;
13
+ } {
14
+ const [header = "", payload = "", signature = ""] = token?.split(".") ?? [];
15
+
16
+ const decoder =
17
+ typeof atob === "function"
18
+ ? atob
19
+ : (encoded: string) => Buffer.from(encoded, "base64").toString("utf8");
20
+
21
+ return {
22
+ header: JSON.parse(decoder(header)),
23
+ payload: JSON.parse(decoder(payload)),
24
+ signature,
25
+ };
26
+ }
27
+
28
+ /**
29
+ * Resolve the Amos embed origin (production vs. sandbox) from a render
30
+ * token's decoded payload.
31
+ */
32
+ export function getEmbedOrigin(renderToken: string): string {
33
+ const { env = "sandbox" }: components["schemas"]["RenderTokenJwt"] =
34
+ decodeJwt(renderToken).payload;
35
+
36
+ switch (env) {
37
+ case "production":
38
+ return "https://embed.amos.com";
39
+ case "sandbox":
40
+ return "https://embed-sandbox.amos.com";
41
+ default:
42
+ return "https://embed-sandbox.amos.com";
43
+ }
44
+ }