@amos.com/amos-js 0.10.1 → 0.10.3
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 +17 -4
- package/dist/index.js +3 -3
- package/dist/index.mjs +334 -267
- package/dist/jwt.d.ts +4 -0
- package/dist/log.d.ts +12 -0
- package/dist/mount.d.ts +15 -1
- package/dist/payment-method-form.d.ts +4 -1
- package/dist/types.d.ts +15 -4
- package/package.json +5 -5
- package/src/jwt.ts +25 -0
- package/src/log.ts +74 -0
- package/src/messaging.ts +19 -0
- package/src/mount.ts +17 -2
- package/src/payment-method-form.ts +7 -0
- package/src/plaid-bank-ui.ts +11 -0
- package/src/types.ts +16 -4
package/dist/jwt.d.ts
CHANGED
|
@@ -12,5 +12,9 @@ export declare function decodeJwt(token: string | undefined): {
|
|
|
12
12
|
/**
|
|
13
13
|
* Resolve the Amos embed origin (production vs. sandbox) from a render
|
|
14
14
|
* token's decoded payload.
|
|
15
|
+
*
|
|
16
|
+
* When the parent page is on localhost / *.localhost, always target the
|
|
17
|
+
* local embed app (https://embed.localhost) so dashboard + embed can be
|
|
18
|
+
* developed together without pointing at sandbox/production iframes.
|
|
15
19
|
*/
|
|
16
20
|
export declare function getEmbedOrigin(renderToken: string): string;
|
package/dist/log.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Forward an info log to the embed iframe so Rollbar records it with
|
|
3
|
+
* Amos credentials. Payload shape matches dashboard/embed API traces:
|
|
4
|
+
* `endpoint`, `headers`, `body`.
|
|
5
|
+
*/
|
|
6
|
+
export declare function reportParentInfoLog({ iframe, message, endpoint, headers, body, }: {
|
|
7
|
+
iframe: HTMLIFrameElement;
|
|
8
|
+
message: string;
|
|
9
|
+
endpoint: string;
|
|
10
|
+
headers?: Record<string, string>;
|
|
11
|
+
body?: unknown;
|
|
12
|
+
}): void;
|
package/dist/mount.d.ts
CHANGED
|
@@ -85,9 +85,22 @@ export type AmosBankAccountPaymentMethodFormOptions = PaymentMethodFormListenerO
|
|
|
85
85
|
* `"0"`, which is typically under the threshold so Connect / Plaid
|
|
86
86
|
* stays hidden until the host passes a real charge.
|
|
87
87
|
*
|
|
88
|
+
* Ignored when {@link AmosBankAccountPaymentMethodFormOptions.intent}
|
|
89
|
+
* is `"setup"` — setup intents always show Connect (unless the render
|
|
90
|
+
* token disables Plaid verification).
|
|
91
|
+
*
|
|
88
92
|
* @default "0"
|
|
89
93
|
*/
|
|
90
94
|
amount?: string;
|
|
95
|
+
/**
|
|
96
|
+
* `"setup"` saves a bank account for later charges and always shows
|
|
97
|
+
* Connect / Plaid (no merchant threshold lookup). `"payment"`
|
|
98
|
+
* compares {@link AmosBankAccountPaymentMethodFormOptions.amount} to
|
|
99
|
+
* the merchant ACH threshold.
|
|
100
|
+
*
|
|
101
|
+
* @default "payment"
|
|
102
|
+
*/
|
|
103
|
+
intent?: "payment" | "setup";
|
|
91
104
|
};
|
|
92
105
|
/**
|
|
93
106
|
* Mount the secure bank-account payment method form into a container
|
|
@@ -98,7 +111,8 @@ export type AmosBankAccountPaymentMethodFormOptions = PaymentMethodFormListenerO
|
|
|
98
111
|
* Connect bank button is rendered in the parent document and Plaid Link
|
|
99
112
|
* is opened on click. `amount` defaults to `"0"`, which is typically
|
|
100
113
|
* under the threshold so Connect stays hidden on open-amount forms
|
|
101
|
-
* until the host passes a real charge.
|
|
114
|
+
* until the host passes a real charge. Pass `intent: "setup"` to always
|
|
115
|
+
* show Connect without a merchant lookup. The button uses the same
|
|
102
116
|
* `appearance.themeVariables` as the iframe (and inherits host-page
|
|
103
117
|
* tokens when those variables are unset). Otherwise a field-shaped
|
|
104
118
|
* skeleton is shown and replaced by the iframe once appearance is applied.
|
|
@@ -22,8 +22,11 @@ export type BillingAddressRequirement = "country" | "full";
|
|
|
22
22
|
export declare function getCreditCardFormSrc(renderToken: string, additionalFields?: CreditCardAdditionalFields, billingAddressRequirement?: BillingAddressRequirement): string;
|
|
23
23
|
/**
|
|
24
24
|
* Build the iframe `src` URL for the embedded bank-account form.
|
|
25
|
+
*
|
|
26
|
+
* Pass `intent: "setup"` so the iframe always requires Plaid (when the
|
|
27
|
+
* render token allows it) and does not `GET /merchants` for a threshold.
|
|
25
28
|
*/
|
|
26
|
-
export declare function getBankAccountFormSrc(renderToken: string, billingAddressRequirement?: BillingAddressRequirement): string;
|
|
29
|
+
export declare function getBankAccountFormSrc(renderToken: string, billingAddressRequirement?: BillingAddressRequirement, intent?: "payment" | "setup"): string;
|
|
27
30
|
/**
|
|
28
31
|
* Default iframe pixel height for the credit-card form, taking the
|
|
29
32
|
* configured `additionalFields` and `billingAddressRequirement` into
|
package/dist/types.d.ts
CHANGED
|
@@ -248,10 +248,11 @@ export type Message = {
|
|
|
248
248
|
brand: CardBrand | null;
|
|
249
249
|
} | {
|
|
250
250
|
/**
|
|
251
|
-
* Embed → parent:
|
|
252
|
-
*
|
|
253
|
-
*
|
|
254
|
-
*
|
|
251
|
+
* Embed → parent: ACH verification policy for this bank iframe.
|
|
252
|
+
* `achThreshold` is cents, or `null` when the merchant has no
|
|
253
|
+
* threshold (manual ACH). `requireVerification` is true for setup
|
|
254
|
+
* intents (always Plaid, no merchant lookup), and when a payment
|
|
255
|
+
* threshold fetch failed in production (fail closed).
|
|
255
256
|
*/
|
|
256
257
|
type: "ACH_THRESHOLD";
|
|
257
258
|
achThreshold?: number | null;
|
|
@@ -266,6 +267,16 @@ export type Message = {
|
|
|
266
267
|
requestId: string;
|
|
267
268
|
link_token?: string;
|
|
268
269
|
error?: string;
|
|
270
|
+
} | {
|
|
271
|
+
/**
|
|
272
|
+
* Parent → embed: info telemetry for Rollbar. The iframe reports
|
|
273
|
+
* `message` with `endpoint`, `headers`, and `body`.
|
|
274
|
+
*/
|
|
275
|
+
type: "PARENT_INFO_LOG";
|
|
276
|
+
message: string;
|
|
277
|
+
endpoint?: string;
|
|
278
|
+
headers?: Record<string, string>;
|
|
279
|
+
body?: unknown;
|
|
269
280
|
};
|
|
270
281
|
/**
|
|
271
282
|
* Identity helper that brands an object as a typed `Message`.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@amos.com/amos-js",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.3",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -35,11 +35,8 @@
|
|
|
35
35
|
"author": "Amos",
|
|
36
36
|
"license": "MIT",
|
|
37
37
|
"description": "Amos JavaScript SDK for embedding payment methods via iframes.",
|
|
38
|
-
"peerDependencies": {
|
|
39
|
-
"@amos.com/node": ">=0.1.39"
|
|
40
|
-
},
|
|
41
38
|
"devDependencies": {
|
|
42
|
-
"@amos.com/node": "0.1.
|
|
39
|
+
"@amos.com/node": "0.1.50",
|
|
43
40
|
"@biomejs/biome": "2.5.7",
|
|
44
41
|
"@changesets/cli": "2.31.1",
|
|
45
42
|
"@types/node": "26.1.2",
|
|
@@ -50,5 +47,8 @@
|
|
|
50
47
|
},
|
|
51
48
|
"dependencies": {
|
|
52
49
|
"@types/googlepay": "0.7.11"
|
|
50
|
+
},
|
|
51
|
+
"peerDependencies": {
|
|
52
|
+
"@amos.com/node": ">=0.1.50"
|
|
53
53
|
}
|
|
54
54
|
}
|
package/src/jwt.ts
CHANGED
|
@@ -25,11 +25,35 @@ export function decodeJwt(token: string | undefined): {
|
|
|
25
25
|
};
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
// --- BEGIN amos-ui sdk:link localhost embed origin ---
|
|
29
|
+
/**
|
|
30
|
+
* True when the parent page is running on localhost / *.localhost
|
|
31
|
+
* (e.g. https://dashboard.localhost via portless).
|
|
32
|
+
*
|
|
33
|
+
* Injected by amos-ui `pnpm sdk:link`; removed by `pnpm sdk:unlink`.
|
|
34
|
+
* Do not commit this block to amos-js.
|
|
35
|
+
*/
|
|
36
|
+
function isLocalhostParent(): boolean {
|
|
37
|
+
if (typeof window === "undefined") {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
const { hostname } = window.location;
|
|
41
|
+
return hostname === "localhost" || hostname.endsWith(".localhost");
|
|
42
|
+
}
|
|
43
|
+
|
|
28
44
|
/**
|
|
29
45
|
* Resolve the Amos embed origin (production vs. sandbox) from a render
|
|
30
46
|
* token's decoded payload.
|
|
47
|
+
*
|
|
48
|
+
* When the parent page is on localhost / *.localhost, always target the
|
|
49
|
+
* local embed app (https://embed.localhost) so dashboard + embed can be
|
|
50
|
+
* developed together without pointing at sandbox/production iframes.
|
|
31
51
|
*/
|
|
32
52
|
export function getEmbedOrigin(renderToken: string): string {
|
|
53
|
+
if (isLocalhostParent()) {
|
|
54
|
+
return "https://embed.localhost";
|
|
55
|
+
}
|
|
56
|
+
|
|
33
57
|
const { env = "sandbox" }: components["schemas"]["RenderTokenJwt"] =
|
|
34
58
|
decodeJwt(renderToken).payload;
|
|
35
59
|
|
|
@@ -42,3 +66,4 @@ export function getEmbedOrigin(renderToken: string): string {
|
|
|
42
66
|
return "https://embed-sandbox.amos.com";
|
|
43
67
|
}
|
|
44
68
|
}
|
|
69
|
+
// --- END amos-ui sdk:link localhost embed origin ---
|
package/src/log.ts
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { createMessage } from "./types";
|
|
2
|
+
|
|
3
|
+
const SENSITIVE_HEADER_NAMES = new Set([
|
|
4
|
+
"authorization",
|
|
5
|
+
"cookie",
|
|
6
|
+
"set-cookie",
|
|
7
|
+
"x-api-key",
|
|
8
|
+
]);
|
|
9
|
+
|
|
10
|
+
const SENSITIVE_BODY_KEYS = new Set([
|
|
11
|
+
"authorization",
|
|
12
|
+
"encrypted_account_number",
|
|
13
|
+
"link_token",
|
|
14
|
+
"public_token",
|
|
15
|
+
"token",
|
|
16
|
+
]);
|
|
17
|
+
|
|
18
|
+
function redactHeaders(
|
|
19
|
+
headers: Record<string, string>,
|
|
20
|
+
): Record<string, string> {
|
|
21
|
+
const redacted: Record<string, string> = {};
|
|
22
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
23
|
+
redacted[key] = SENSITIVE_HEADER_NAMES.has(key.toLowerCase())
|
|
24
|
+
? "[REDACTED]"
|
|
25
|
+
: value;
|
|
26
|
+
}
|
|
27
|
+
return redacted;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function redactBody(body: unknown): unknown {
|
|
31
|
+
if (Array.isArray(body)) {
|
|
32
|
+
return body.map(redactBody);
|
|
33
|
+
}
|
|
34
|
+
if (body !== null && typeof body === "object") {
|
|
35
|
+
const redacted: Record<string, unknown> = {};
|
|
36
|
+
for (const [key, value] of Object.entries(body)) {
|
|
37
|
+
redacted[key] = SENSITIVE_BODY_KEYS.has(key)
|
|
38
|
+
? "[REDACTED]"
|
|
39
|
+
: redactBody(value);
|
|
40
|
+
}
|
|
41
|
+
return redacted;
|
|
42
|
+
}
|
|
43
|
+
return body;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Forward an info log to the embed iframe so Rollbar records it with
|
|
48
|
+
* Amos credentials. Payload shape matches dashboard/embed API traces:
|
|
49
|
+
* `endpoint`, `headers`, `body`.
|
|
50
|
+
*/
|
|
51
|
+
export function reportParentInfoLog({
|
|
52
|
+
iframe,
|
|
53
|
+
message,
|
|
54
|
+
endpoint,
|
|
55
|
+
headers = {},
|
|
56
|
+
body,
|
|
57
|
+
}: {
|
|
58
|
+
iframe: HTMLIFrameElement;
|
|
59
|
+
message: string;
|
|
60
|
+
endpoint: string;
|
|
61
|
+
headers?: Record<string, string>;
|
|
62
|
+
body?: unknown;
|
|
63
|
+
}): void {
|
|
64
|
+
iframe.contentWindow?.postMessage(
|
|
65
|
+
createMessage({
|
|
66
|
+
type: "PARENT_INFO_LOG",
|
|
67
|
+
message,
|
|
68
|
+
endpoint,
|
|
69
|
+
headers: redactHeaders(headers),
|
|
70
|
+
body: redactBody(body),
|
|
71
|
+
}),
|
|
72
|
+
new URL(iframe.src).origin,
|
|
73
|
+
);
|
|
74
|
+
}
|
package/src/messaging.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { components } from "@amos.com/node";
|
|
2
2
|
import { decodeJwt } from "./jwt";
|
|
3
|
+
import { reportParentInfoLog } from "./log";
|
|
3
4
|
import { getBankPlaidSession } from "./plaid-session";
|
|
4
5
|
import {
|
|
5
6
|
type Appearance,
|
|
@@ -292,6 +293,24 @@ function postConfirmIntent({
|
|
|
292
293
|
resetIframeFields(iframe);
|
|
293
294
|
}
|
|
294
295
|
|
|
296
|
+
const origin = getIframeTargetOrigin(iframe);
|
|
297
|
+
reportParentInfoLog({
|
|
298
|
+
iframe,
|
|
299
|
+
message:
|
|
300
|
+
type === "CONFIRM_PAYMENT_INTENT"
|
|
301
|
+
? "confirmPaymentIntent"
|
|
302
|
+
: "confirmSetupIntent",
|
|
303
|
+
endpoint:
|
|
304
|
+
type === "CONFIRM_PAYMENT_INTENT"
|
|
305
|
+
? "POST /embed/payment_intents/{id}/confirm_with_payment_method"
|
|
306
|
+
: "POST /embed/setup_intents/{id}/confirm_with_payment_method",
|
|
307
|
+
headers: {
|
|
308
|
+
origin: window.location.origin,
|
|
309
|
+
"iframe-origin": origin,
|
|
310
|
+
},
|
|
311
|
+
body: { id, token, ...(plaid ? { plaid } : {}) },
|
|
312
|
+
});
|
|
313
|
+
|
|
295
314
|
iframe.contentWindow?.postMessage(
|
|
296
315
|
createMessage({
|
|
297
316
|
type,
|
package/src/mount.ts
CHANGED
|
@@ -514,9 +514,22 @@ export type AmosBankAccountPaymentMethodFormOptions =
|
|
|
514
514
|
* `"0"`, which is typically under the threshold so Connect / Plaid
|
|
515
515
|
* stays hidden until the host passes a real charge.
|
|
516
516
|
*
|
|
517
|
+
* Ignored when {@link AmosBankAccountPaymentMethodFormOptions.intent}
|
|
518
|
+
* is `"setup"` — setup intents always show Connect (unless the render
|
|
519
|
+
* token disables Plaid verification).
|
|
520
|
+
*
|
|
517
521
|
* @default "0"
|
|
518
522
|
*/
|
|
519
523
|
amount?: string;
|
|
524
|
+
/**
|
|
525
|
+
* `"setup"` saves a bank account for later charges and always shows
|
|
526
|
+
* Connect / Plaid (no merchant threshold lookup). `"payment"`
|
|
527
|
+
* compares {@link AmosBankAccountPaymentMethodFormOptions.amount} to
|
|
528
|
+
* the merchant ACH threshold.
|
|
529
|
+
*
|
|
530
|
+
* @default "payment"
|
|
531
|
+
*/
|
|
532
|
+
intent?: "payment" | "setup";
|
|
520
533
|
};
|
|
521
534
|
|
|
522
535
|
/**
|
|
@@ -528,7 +541,8 @@ export type AmosBankAccountPaymentMethodFormOptions =
|
|
|
528
541
|
* Connect bank button is rendered in the parent document and Plaid Link
|
|
529
542
|
* is opened on click. `amount` defaults to `"0"`, which is typically
|
|
530
543
|
* under the threshold so Connect stays hidden on open-amount forms
|
|
531
|
-
* until the host passes a real charge.
|
|
544
|
+
* until the host passes a real charge. Pass `intent: "setup"` to always
|
|
545
|
+
* show Connect without a merchant lookup. The button uses the same
|
|
532
546
|
* `appearance.themeVariables` as the iframe (and inherits host-page
|
|
533
547
|
* tokens when those variables are unset). Otherwise a field-shaped
|
|
534
548
|
* skeleton is shown and replaced by the iframe once appearance is applied.
|
|
@@ -546,11 +560,12 @@ export function mountAmosBankAccountPaymentMethodForm(
|
|
|
546
560
|
renderToken,
|
|
547
561
|
billingAddressRequirement = "country",
|
|
548
562
|
amount = "0",
|
|
563
|
+
intent = "payment",
|
|
549
564
|
...listenerOptions
|
|
550
565
|
} = options;
|
|
551
566
|
|
|
552
567
|
const iframe = createIframe({
|
|
553
|
-
src: getBankAccountFormSrc(renderToken, billingAddressRequirement),
|
|
568
|
+
src: getBankAccountFormSrc(renderToken, billingAddressRequirement, intent),
|
|
554
569
|
title: "Secure bank account payment method form powered by Amos",
|
|
555
570
|
name: "amos-bank-account-payment-method-form",
|
|
556
571
|
height: getBankAccountFormInitialHeight(billingAddressRequirement),
|
|
@@ -68,15 +68,22 @@ export function getCreditCardFormSrc(
|
|
|
68
68
|
|
|
69
69
|
/**
|
|
70
70
|
* Build the iframe `src` URL for the embedded bank-account form.
|
|
71
|
+
*
|
|
72
|
+
* Pass `intent: "setup"` so the iframe always requires Plaid (when the
|
|
73
|
+
* render token allows it) and does not `GET /merchants` for a threshold.
|
|
71
74
|
*/
|
|
72
75
|
export function getBankAccountFormSrc(
|
|
73
76
|
renderToken: string,
|
|
74
77
|
billingAddressRequirement: BillingAddressRequirement = "country",
|
|
78
|
+
intent: "payment" | "setup" = "payment",
|
|
75
79
|
): string {
|
|
76
80
|
const params = new URLSearchParams({
|
|
77
81
|
token: renderToken,
|
|
78
82
|
billingAddressRequirement,
|
|
79
83
|
});
|
|
84
|
+
if (intent === "setup") {
|
|
85
|
+
params.set("intent", "setup");
|
|
86
|
+
}
|
|
80
87
|
|
|
81
88
|
return `${getEmbedOrigin(renderToken)}/iframe/bank?${params}`;
|
|
82
89
|
}
|
package/src/plaid-bank-ui.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { reportParentInfoLog } from "./log";
|
|
1
2
|
import { requestPlaidLinkToken } from "./messaging";
|
|
2
3
|
import type { PaymentMethodFormListenerOptions } from "./payment-method-form";
|
|
3
4
|
import {
|
|
@@ -309,6 +310,16 @@ export function attachPlaidBankUi({
|
|
|
309
310
|
thresholdKnown = true;
|
|
310
311
|
achThreshold = event.data.achThreshold ?? undefined;
|
|
311
312
|
requireVerification = event.data.requireVerification === true;
|
|
313
|
+
reportParentInfoLog({
|
|
314
|
+
iframe,
|
|
315
|
+
message: "merchant ach_threshold",
|
|
316
|
+
endpoint: "ACH_THRESHOLD",
|
|
317
|
+
headers: { origin: event.origin },
|
|
318
|
+
body: {
|
|
319
|
+
achThreshold,
|
|
320
|
+
requireVerification,
|
|
321
|
+
},
|
|
322
|
+
});
|
|
312
323
|
if (linked && !requiresConnect()) {
|
|
313
324
|
unlink();
|
|
314
325
|
return;
|
package/src/types.ts
CHANGED
|
@@ -536,10 +536,11 @@ export type Message =
|
|
|
536
536
|
}
|
|
537
537
|
| {
|
|
538
538
|
/**
|
|
539
|
-
* Embed → parent:
|
|
540
|
-
*
|
|
541
|
-
*
|
|
542
|
-
*
|
|
539
|
+
* Embed → parent: ACH verification policy for this bank iframe.
|
|
540
|
+
* `achThreshold` is cents, or `null` when the merchant has no
|
|
541
|
+
* threshold (manual ACH). `requireVerification` is true for setup
|
|
542
|
+
* intents (always Plaid, no merchant lookup), and when a payment
|
|
543
|
+
* threshold fetch failed in production (fail closed).
|
|
543
544
|
*/
|
|
544
545
|
type: "ACH_THRESHOLD";
|
|
545
546
|
achThreshold?: number | null;
|
|
@@ -556,6 +557,17 @@ export type Message =
|
|
|
556
557
|
requestId: string;
|
|
557
558
|
link_token?: string;
|
|
558
559
|
error?: string;
|
|
560
|
+
}
|
|
561
|
+
| {
|
|
562
|
+
/**
|
|
563
|
+
* Parent → embed: info telemetry for Rollbar. The iframe reports
|
|
564
|
+
* `message` with `endpoint`, `headers`, and `body`.
|
|
565
|
+
*/
|
|
566
|
+
type: "PARENT_INFO_LOG";
|
|
567
|
+
message: string;
|
|
568
|
+
endpoint?: string;
|
|
569
|
+
headers?: Record<string, string>;
|
|
570
|
+
body?: unknown;
|
|
559
571
|
};
|
|
560
572
|
|
|
561
573
|
/**
|