@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.
- package/README.md +295 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/index.mjs +422 -0
- package/dist/src/google-pay.d.ts +105 -0
- package/dist/src/index.d.ts +52 -0
- package/dist/src/jwt.d.ts +16 -0
- package/dist/src/messaging.d.ts +73 -0
- package/dist/src/mount.d.ts +95 -0
- package/dist/src/payment-method-form.d.ts +90 -0
- package/dist/src/types.d.ts +64 -0
- package/package.json +47 -0
- package/src/google-pay.ts +234 -0
- package/src/index.ts +101 -0
- package/src/jwt.ts +44 -0
- package/src/messaging.ts +175 -0
- package/src/mount.ts +278 -0
- package/src/payment-method-form.ts +180 -0
- package/src/types.ts +171 -0
|
@@ -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
|
+
}
|