@hypermid/checkout 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 +107 -0
- package/dist/checkout.esm.js +140 -0
- package/dist/checkout.esm.js.map +1 -0
- package/dist/checkout.js +2 -0
- package/dist/checkout.js.map +1 -0
- package/dist/index.d.ts +90 -0
- package/dist/index.d.ts.map +1 -0
- package/package.json +55 -0
package/README.md
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# @hypermid/checkout
|
|
2
|
+
|
|
3
|
+
Embeddable crypto checkout for [Hypermid](https://hypermid.io) — **pay with any token, the merchant receives the token they asked for.** Drop it into any site with a single script tag or npm import; it mounts the live Hypermid checkout app in a sandboxed iframe, so it always reflects production with zero maintenance on your side.
|
|
4
|
+
|
|
5
|
+
The destination — amount, token, chain, recipient — is bound to the checkout session **server-side** when you create it. The only thing the payer chooses is which token to pay with; the destination can never be overridden from the client.
|
|
6
|
+
|
|
7
|
+
## How it works
|
|
8
|
+
|
|
9
|
+
1. **Server-side**, create a checkout with your secret key:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
curl -X POST https://api.hypermid.io/v1/checkout \
|
|
13
|
+
-H "Authorization: Bearer sk_live_…" \
|
|
14
|
+
-H "Content-Type: application/json" \
|
|
15
|
+
-d '{ "amount": "100000000", "token": "0x833…913", "chain": 8453,
|
|
16
|
+
"recipient": "0xYourTreasury", "orderId": "order-123" }'
|
|
17
|
+
# → { "data": { "id": "co_…", … } }
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
2. **Client-side**, embed the returned `checkoutId`.
|
|
21
|
+
|
|
22
|
+
## Install
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm install @hypermid/checkout
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
```ts
|
|
29
|
+
import { HypermidCheckout } from "@hypermid/checkout";
|
|
30
|
+
|
|
31
|
+
const checkout = HypermidCheckout.init({
|
|
32
|
+
containerId: "pay",
|
|
33
|
+
checkoutId: "co_…", // from POST /v1/checkout (server-side)
|
|
34
|
+
theme: "dark",
|
|
35
|
+
onSuccess: ({ checkoutId }) => { // backend on-chain-VERIFIED completion
|
|
36
|
+
// fulfill the order
|
|
37
|
+
},
|
|
38
|
+
onError: ({ reason }) => {
|
|
39
|
+
// show a retry
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Script tag (no build step)
|
|
45
|
+
|
|
46
|
+
```html
|
|
47
|
+
<div id="hypermid-checkout"></div>
|
|
48
|
+
<script src="https://hypermid.io/checkout/v1/embed.js"></script>
|
|
49
|
+
<script>
|
|
50
|
+
HypermidCheckout.init({
|
|
51
|
+
containerId: "hypermid-checkout",
|
|
52
|
+
checkoutId: "co_…",
|
|
53
|
+
theme: "dark",
|
|
54
|
+
onSuccess: function (p) { /* fulfill order */ },
|
|
55
|
+
});
|
|
56
|
+
</script>
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Or auto-init from a data attribute:
|
|
60
|
+
|
|
61
|
+
```html
|
|
62
|
+
<div data-hypermid-checkout data-checkout-id="co_…" data-theme="light" id="hypermid-checkout"></div>
|
|
63
|
+
<script src="https://hypermid.io/checkout/v1/embed.js"></script>
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Config
|
|
67
|
+
|
|
68
|
+
| Option | Type | Notes |
|
|
69
|
+
| --- | --- | --- |
|
|
70
|
+
| `containerId` | `string` | **Required.** Element id to mount into. |
|
|
71
|
+
| `checkoutId` | `string` | **Required.** Session id (`co_…`) from `POST /v1/checkout`. |
|
|
72
|
+
| `theme` | `"dark" \| "light"` | Default `"dark"`. |
|
|
73
|
+
| `label` | `string` | Action label, e.g. `"Pay"` / `"Deposit"`. |
|
|
74
|
+
| `accent`, `bgPage`, `bgCard`, `border`, `textPrimary`, `textMuted` | `string` (hex) | Full theme-token overrides. |
|
|
75
|
+
| `fontFamily` | `string` | Font family for the embed. |
|
|
76
|
+
| `width`, `height`, `borderRadius` | `string` (CSS) | `height` is a floor — the frame grows to fit taller content. |
|
|
77
|
+
|
|
78
|
+
## Events
|
|
79
|
+
|
|
80
|
+
The embed relays the checkout lifecycle to your callbacks over `postMessage`. Only messages from a Hypermid origin **and** this embed's own iframe are accepted.
|
|
81
|
+
|
|
82
|
+
| Callback | Fires when | Payload |
|
|
83
|
+
| --- | --- | --- |
|
|
84
|
+
| `onReady()` | the iframe has mounted | — |
|
|
85
|
+
| `onResize(height)` | content height changed (grow-only floor) | `number` |
|
|
86
|
+
| `onSuccess(p)` | the payment is **backend on-chain-verified complete** | `{ checkoutId, status, paidAmount?, txHash? }` |
|
|
87
|
+
| `onError(p)` | the payment failed | `{ checkoutId, reason? }` |
|
|
88
|
+
| `onClose()` | the payer dismissed the checkout | — |
|
|
89
|
+
|
|
90
|
+
> `onSuccess` is the **only** trustworthy "paid" signal — it fires on the backend's on-chain settlement verification (delivered amount ≥ the requested amount), never merely because a transaction mined. Fulfill orders on `onSuccess`.
|
|
91
|
+
|
|
92
|
+
## Methods
|
|
93
|
+
|
|
94
|
+
```ts
|
|
95
|
+
checkout.update({ checkoutId: "co_next…" }); // swap in a new session + reload
|
|
96
|
+
checkout.destroy(); // remove the embed + its listener
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Security
|
|
100
|
+
|
|
101
|
+
- The iframe is `sandbox`ed; the wallet signature is the final gate.
|
|
102
|
+
- The destination is server-bound to the session — query params can only set cosmetics / the pay-token, never money.
|
|
103
|
+
- `onSuccess` is gated on backend on-chain verification, not on a mined tx.
|
|
104
|
+
|
|
105
|
+
## License
|
|
106
|
+
|
|
107
|
+
MIT
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
const APP_BASE = "https://app.hypermid.io";
|
|
2
|
+
const ALLOWED_ORIGINS = [APP_BASE, "https://hypermid.io"];
|
|
3
|
+
const hex = (c) => encodeURIComponent(c.replace("#", ""));
|
|
4
|
+
function buildUrl(config) {
|
|
5
|
+
const p = [];
|
|
6
|
+
p.push("checkoutId=" + encodeURIComponent(config.checkoutId));
|
|
7
|
+
if (config.theme) p.push("theme=" + encodeURIComponent(config.theme));
|
|
8
|
+
if (config.label) p.push("label=" + encodeURIComponent(config.label));
|
|
9
|
+
if (config.accent) p.push("accent=" + hex(config.accent));
|
|
10
|
+
if (config.bgPage) p.push("bgPage=" + hex(config.bgPage));
|
|
11
|
+
if (config.bgCard) p.push("bgCard=" + hex(config.bgCard));
|
|
12
|
+
if (config.border) p.push("border=" + hex(config.border));
|
|
13
|
+
if (config.textPrimary) p.push("textPrimary=" + hex(config.textPrimary));
|
|
14
|
+
if (config.textMuted) p.push("textMuted=" + hex(config.textMuted));
|
|
15
|
+
if (config.fontFamily) p.push("font=" + encodeURIComponent(config.fontFamily));
|
|
16
|
+
p.push("embed=1");
|
|
17
|
+
return APP_BASE + "/checkout?" + p.join("&");
|
|
18
|
+
}
|
|
19
|
+
class HypermidCheckout {
|
|
20
|
+
constructor(config) {
|
|
21
|
+
this.container = null;
|
|
22
|
+
this.iframe = null;
|
|
23
|
+
this.onMessage = null;
|
|
24
|
+
this.config = { theme: "dark", ...config };
|
|
25
|
+
}
|
|
26
|
+
/** Construct + mount in one call (mirrors the script-tag `HypermidCheckout.init`). */
|
|
27
|
+
static init(config) {
|
|
28
|
+
const c = new HypermidCheckout(config);
|
|
29
|
+
c.mount();
|
|
30
|
+
return c;
|
|
31
|
+
}
|
|
32
|
+
mount() {
|
|
33
|
+
if (typeof document === "undefined") return this;
|
|
34
|
+
if (!this.config.checkoutId) {
|
|
35
|
+
console.error("[HypermidCheckout] `checkoutId` is required");
|
|
36
|
+
return this;
|
|
37
|
+
}
|
|
38
|
+
const container = document.getElementById(this.config.containerId);
|
|
39
|
+
if (!container) {
|
|
40
|
+
console.error("[HypermidCheckout] Element #" + this.config.containerId + " not found");
|
|
41
|
+
return this;
|
|
42
|
+
}
|
|
43
|
+
this.container = container;
|
|
44
|
+
const iframe = document.createElement("iframe");
|
|
45
|
+
iframe.src = buildUrl(this.config);
|
|
46
|
+
iframe.title = "Hypermid Checkout";
|
|
47
|
+
iframe.allow = "clipboard-write; payment; web-share";
|
|
48
|
+
iframe.setAttribute(
|
|
49
|
+
"sandbox",
|
|
50
|
+
"allow-scripts allow-same-origin allow-popups allow-popups-to-escape-sandbox allow-forms allow-top-navigation-by-user-activation"
|
|
51
|
+
);
|
|
52
|
+
const width = this.config.width || "400px";
|
|
53
|
+
const height = this.config.height || "640px";
|
|
54
|
+
const radius = this.config.borderRadius || "24px";
|
|
55
|
+
iframe.style.cssText = [
|
|
56
|
+
"width:" + width,
|
|
57
|
+
"height:" + height,
|
|
58
|
+
"max-width:100%",
|
|
59
|
+
"border:none",
|
|
60
|
+
"border-radius:" + radius,
|
|
61
|
+
"box-shadow:0 4px 24px rgba(0,0,0,0.15)",
|
|
62
|
+
"display:block",
|
|
63
|
+
"margin:0 auto",
|
|
64
|
+
"color-scheme:normal"
|
|
65
|
+
].join(";");
|
|
66
|
+
this.iframe = iframe;
|
|
67
|
+
container.innerHTML = "";
|
|
68
|
+
container.appendChild(iframe);
|
|
69
|
+
this.listen();
|
|
70
|
+
return this;
|
|
71
|
+
}
|
|
72
|
+
listen() {
|
|
73
|
+
const floor = parseInt(String(this.config.height), 10) || 640;
|
|
74
|
+
this.onMessage = (event) => {
|
|
75
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
|
|
76
|
+
if (ALLOWED_ORIGINS.indexOf(event.origin) === -1) return;
|
|
77
|
+
if (!this.iframe || event.source !== this.iframe.contentWindow) return;
|
|
78
|
+
const data = event.data;
|
|
79
|
+
if (!data || typeof data.type !== "string" || data.type.indexOf("hypermid:") !== 0) return;
|
|
80
|
+
switch (data.type.slice("hypermid:".length)) {
|
|
81
|
+
case "ready":
|
|
82
|
+
(_b = (_a = this.config).onReady) == null ? void 0 : _b.call(_a);
|
|
83
|
+
break;
|
|
84
|
+
case "success":
|
|
85
|
+
(_d = (_c = this.config).onSuccess) == null ? void 0 : _d.call(_c, data.payload);
|
|
86
|
+
break;
|
|
87
|
+
case "error":
|
|
88
|
+
(_f = (_e = this.config).onError) == null ? void 0 : _f.call(_e, data.payload);
|
|
89
|
+
break;
|
|
90
|
+
case "close":
|
|
91
|
+
(_h = (_g = this.config).onClose) == null ? void 0 : _h.call(_g);
|
|
92
|
+
break;
|
|
93
|
+
case "resize":
|
|
94
|
+
if (this.iframe && data.payload && typeof data.payload.height === "number") {
|
|
95
|
+
const h = Math.max(data.payload.height, floor);
|
|
96
|
+
this.iframe.style.height = h + "px";
|
|
97
|
+
(_j = (_i = this.config).onResize) == null ? void 0 : _j.call(_i, h);
|
|
98
|
+
}
|
|
99
|
+
break;
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
window.addEventListener("message", this.onMessage);
|
|
103
|
+
}
|
|
104
|
+
/** Merge new config and reload the iframe URL (e.g. swap in a new checkoutId). */
|
|
105
|
+
update(partial) {
|
|
106
|
+
this.config = { ...this.config, ...partial };
|
|
107
|
+
if (this.iframe) this.iframe.src = buildUrl(this.config);
|
|
108
|
+
}
|
|
109
|
+
/** Remove the embed + its listener. */
|
|
110
|
+
destroy() {
|
|
111
|
+
if (this.onMessage) window.removeEventListener("message", this.onMessage);
|
|
112
|
+
if (this.container) this.container.innerHTML = "";
|
|
113
|
+
this.onMessage = null;
|
|
114
|
+
this.iframe = null;
|
|
115
|
+
this.container = null;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
if (typeof window !== "undefined") {
|
|
119
|
+
window.HypermidCheckout = HypermidCheckout;
|
|
120
|
+
if (typeof document !== "undefined") {
|
|
121
|
+
document.addEventListener("DOMContentLoaded", () => {
|
|
122
|
+
const el = document.querySelector("[data-hypermid-checkout]");
|
|
123
|
+
if (!el) return;
|
|
124
|
+
const checkoutId = el.getAttribute("data-checkout-id");
|
|
125
|
+
if (!checkoutId) return;
|
|
126
|
+
HypermidCheckout.init({
|
|
127
|
+
containerId: el.id || "hypermid-checkout",
|
|
128
|
+
checkoutId,
|
|
129
|
+
theme: el.getAttribute("data-theme") || "dark",
|
|
130
|
+
label: el.getAttribute("data-label") || void 0,
|
|
131
|
+
accent: el.getAttribute("data-accent") || void 0
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
export {
|
|
137
|
+
HypermidCheckout,
|
|
138
|
+
HypermidCheckout as default
|
|
139
|
+
};
|
|
140
|
+
//# sourceMappingURL=checkout.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"checkout.esm.js","sources":["../src/index.ts"],"sourcesContent":["/**\n * @hypermid/checkout\n *\n * Embeddable crypto checkout for Hypermid — \"pay with any token, the merchant\n * receives the token they asked for.\" Mounts the live Hypermid checkout app\n * (wallet connect, pay-token picker, approval, on-chain-verified settlement) in\n * a sandboxed iframe, so it always reflects production with zero maintenance on\n * the integrator's side. This is the typed, npm-distributed counterpart of the\n * hosted `embed.js` script tag; both share the same iframe contract.\n *\n * The merchant creates a checkout server-side (POST /v1/checkout with their\n * sk_live key) and passes the returned `checkoutId` here. The destination —\n * amount, token, chain, recipient — is bound to that session on the server and\n * can never be overridden from the client (the only thing the payer chooses is\n * which token to pay with).\n *\n * Usage (npm):\n * import { HypermidCheckout } from \"@hypermid/checkout\";\n * const checkout = HypermidCheckout.init({\n * containerId: \"pay\",\n * checkoutId: \"co_…\", // from POST /v1/checkout (server-side)\n * theme: \"dark\",\n * onSuccess: ({ checkoutId }) => fulfillOrder(checkoutId),\n * onError: ({ reason }) => showRetry(reason),\n * });\n *\n * Usage (script tag / UMD):\n * <div id=\"hypermid-checkout\"></div>\n * <script src=\"https://hypermid.io/checkout/v1/embed.js\"></script>\n * <script>\n * HypermidCheckout.init({ containerId: \"hypermid-checkout\", checkoutId: \"co_…\" });\n * </script>\n */\n\nconst APP_BASE = \"https://app.hypermid.io\";\nconst ALLOWED_ORIGINS = [APP_BASE, \"https://hypermid.io\"];\n\nexport interface HypermidCheckoutSuccess {\n checkoutId: string;\n status: \"completed\";\n /** On-chain-verified delivered amount (base units), when surfaced. */\n paidAmount?: string | null;\n /** The payer's source-chain transaction hash. */\n txHash?: string | null;\n}\n\nexport interface HypermidCheckoutError {\n checkoutId: string;\n reason?: string;\n}\n\nexport interface HypermidCheckoutConfig {\n /** DOM element id to mount into. Required. */\n containerId: string;\n /** The checkout session id (co_…) from POST /v1/checkout. Required. */\n checkoutId: string;\n\n theme?: \"dark\" | \"light\";\n /** Action label, e.g. \"Pay\" | \"Deposit\" (merchant-configurable, D-16). */\n label?: string;\n\n /** Theming overrides (hex, with or without `#`) — full token set (D-18). */\n accent?: string;\n bgPage?: string;\n bgCard?: string;\n border?: string;\n textPrimary?: string;\n textMuted?: string;\n fontFamily?: string;\n\n /** Layout. Height is a floor — the frame grows to fit taller content. */\n width?: string;\n height?: string;\n borderRadius?: string;\n\n /** Event callbacks. */\n onReady?: () => void;\n /** Fires on backend on-chain-VERIFIED completion — the only \"paid\" signal. */\n onSuccess?: (payload: HypermidCheckoutSuccess) => void;\n onError?: (payload: HypermidCheckoutError) => void;\n onClose?: () => void;\n onResize?: (height: number) => void;\n}\n\nconst hex = (c: string) => encodeURIComponent(c.replace(\"#\", \"\"));\n\nfunction buildUrl(config: HypermidCheckoutConfig): string {\n const p: string[] = [];\n // checkoutId is the ONLY money-affecting input — destination is server-bound.\n p.push(\"checkoutId=\" + encodeURIComponent(config.checkoutId));\n if (config.theme) p.push(\"theme=\" + encodeURIComponent(config.theme));\n if (config.label) p.push(\"label=\" + encodeURIComponent(config.label));\n\n if (config.accent) p.push(\"accent=\" + hex(config.accent));\n if (config.bgPage) p.push(\"bgPage=\" + hex(config.bgPage));\n if (config.bgCard) p.push(\"bgCard=\" + hex(config.bgCard));\n if (config.border) p.push(\"border=\" + hex(config.border));\n if (config.textPrimary) p.push(\"textPrimary=\" + hex(config.textPrimary));\n if (config.textMuted) p.push(\"textMuted=\" + hex(config.textMuted));\n if (config.fontFamily) p.push(\"font=\" + encodeURIComponent(config.fontFamily));\n\n p.push(\"embed=1\");\n return APP_BASE + \"/checkout?\" + p.join(\"&\");\n}\n\nexport class HypermidCheckout {\n private config: HypermidCheckoutConfig;\n private container: HTMLElement | null = null;\n private iframe: HTMLIFrameElement | null = null;\n private onMessage: ((e: MessageEvent) => void) | null = null;\n\n constructor(config: HypermidCheckoutConfig) {\n this.config = { theme: \"dark\", ...config };\n }\n\n /** Construct + mount in one call (mirrors the script-tag `HypermidCheckout.init`). */\n static init(config: HypermidCheckoutConfig): HypermidCheckout {\n const c = new HypermidCheckout(config);\n c.mount();\n return c;\n }\n\n mount(): this {\n if (typeof document === \"undefined\") return this; // SSR no-op\n if (!this.config.checkoutId) {\n console.error(\"[HypermidCheckout] `checkoutId` is required\");\n return this;\n }\n const container = document.getElementById(this.config.containerId);\n if (!container) {\n console.error(\"[HypermidCheckout] Element #\" + this.config.containerId + \" not found\");\n return this;\n }\n this.container = container;\n\n const iframe = document.createElement(\"iframe\");\n iframe.src = buildUrl(this.config);\n iframe.title = \"Hypermid Checkout\";\n iframe.allow = \"clipboard-write; payment; web-share\";\n iframe.setAttribute(\n \"sandbox\",\n \"allow-scripts allow-same-origin allow-popups allow-popups-to-escape-sandbox allow-forms allow-top-navigation-by-user-activation\",\n );\n const width = this.config.width || \"400px\";\n const height = this.config.height || \"640px\";\n const radius = this.config.borderRadius || \"24px\";\n iframe.style.cssText = [\n \"width:\" + width,\n \"height:\" + height,\n \"max-width:100%\",\n \"border:none\",\n \"border-radius:\" + radius,\n \"box-shadow:0 4px 24px rgba(0,0,0,0.15)\",\n \"display:block\",\n \"margin:0 auto\",\n \"color-scheme:normal\",\n ].join(\";\");\n\n this.iframe = iframe;\n container.innerHTML = \"\";\n container.appendChild(iframe);\n this.listen();\n return this;\n }\n\n private listen(): void {\n const floor = parseInt(String(this.config.height), 10) || 640;\n this.onMessage = (event: MessageEvent) => {\n // Accept only messages from a Hypermid origin AND this checkout's own\n // iframe (supports multiple embeds per page).\n if (ALLOWED_ORIGINS.indexOf(event.origin) === -1) return;\n if (!this.iframe || event.source !== this.iframe.contentWindow) return;\n const data = event.data as { type?: string; payload?: any };\n if (!data || typeof data.type !== \"string\" || data.type.indexOf(\"hypermid:\") !== 0) return;\n\n switch (data.type.slice(\"hypermid:\".length)) {\n case \"ready\":\n this.config.onReady?.();\n break;\n case \"success\":\n this.config.onSuccess?.(data.payload as HypermidCheckoutSuccess);\n break;\n case \"error\":\n this.config.onError?.(data.payload as HypermidCheckoutError);\n break;\n case \"close\":\n this.config.onClose?.();\n break;\n case \"resize\":\n // Grow-only: never shrink below the configured/default height, so a\n // transient small measurement can't collapse the embed.\n if (this.iframe && data.payload && typeof data.payload.height === \"number\") {\n const h = Math.max(data.payload.height, floor);\n this.iframe.style.height = h + \"px\";\n this.config.onResize?.(h);\n }\n break;\n }\n };\n window.addEventListener(\"message\", this.onMessage);\n }\n\n /** Merge new config and reload the iframe URL (e.g. swap in a new checkoutId). */\n update(partial: Partial<HypermidCheckoutConfig>): void {\n this.config = { ...this.config, ...partial };\n if (this.iframe) this.iframe.src = buildUrl(this.config);\n }\n\n /** Remove the embed + its listener. */\n destroy(): void {\n if (this.onMessage) window.removeEventListener(\"message\", this.onMessage);\n if (this.container) this.container.innerHTML = \"\";\n this.onMessage = null;\n this.iframe = null;\n this.container = null;\n }\n}\n\nexport default HypermidCheckout;\n\n// Script-tag (UMD) convenience: expose globally + auto-init from data attributes.\nif (typeof window !== \"undefined\") {\n (window as any).HypermidCheckout = HypermidCheckout;\n if (typeof document !== \"undefined\") {\n document.addEventListener(\"DOMContentLoaded\", () => {\n const el = document.querySelector(\"[data-hypermid-checkout]\") as HTMLElement | null;\n if (!el) return;\n const checkoutId = el.getAttribute(\"data-checkout-id\");\n if (!checkoutId) return;\n HypermidCheckout.init({\n containerId: el.id || \"hypermid-checkout\",\n checkoutId,\n theme: (el.getAttribute(\"data-theme\") as \"dark\" | \"light\") || \"dark\",\n label: el.getAttribute(\"data-label\") || undefined,\n accent: el.getAttribute(\"data-accent\") || undefined,\n });\n });\n }\n}\n"],"names":[],"mappings":"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"}
|
package/dist/checkout.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).HypermidCheckout={})}(this,function(e){"use strict";const t="https://app.hypermid.io",i=[t,"https://hypermid.io"],o=e=>encodeURIComponent(e.replace("#",""));function n(e){const i=[];return i.push("checkoutId="+encodeURIComponent(e.checkoutId)),e.theme&&i.push("theme="+encodeURIComponent(e.theme)),e.label&&i.push("label="+encodeURIComponent(e.label)),e.accent&&i.push("accent="+o(e.accent)),e.bgPage&&i.push("bgPage="+o(e.bgPage)),e.bgCard&&i.push("bgCard="+o(e.bgCard)),e.border&&i.push("border="+o(e.border)),e.textPrimary&&i.push("textPrimary="+o(e.textPrimary)),e.textMuted&&i.push("textMuted="+o(e.textMuted)),e.fontFamily&&i.push("font="+encodeURIComponent(e.fontFamily)),i.push("embed=1"),t+"/checkout?"+i.join("&")}class s{constructor(e){this.container=null,this.iframe=null,this.onMessage=null,this.config={theme:"dark",...e}}static init(e){const t=new s(e);return t.mount(),t}mount(){if("undefined"==typeof document)return this;if(!this.config.checkoutId)return console.error("[HypermidCheckout] `checkoutId` is required"),this;const e=document.getElementById(this.config.containerId);if(!e)return console.error("[HypermidCheckout] Element #"+this.config.containerId+" not found"),this;this.container=e;const t=document.createElement("iframe");t.src=n(this.config),t.title="Hypermid Checkout",t.allow="clipboard-write; payment; web-share",t.setAttribute("sandbox","allow-scripts allow-same-origin allow-popups allow-popups-to-escape-sandbox allow-forms allow-top-navigation-by-user-activation");const i=this.config.width||"400px",o=this.config.height||"640px",s=this.config.borderRadius||"24px";return t.style.cssText=["width:"+i,"height:"+o,"max-width:100%","border:none","border-radius:"+s,"box-shadow:0 4px 24px rgba(0,0,0,0.15)","display:block","margin:0 auto","color-scheme:normal"].join(";"),this.iframe=t,e.innerHTML="",e.appendChild(t),this.listen(),this}listen(){const e=parseInt(String(this.config.height),10)||640;this.onMessage=t=>{var o,n,s,r,a,c,d,h,l,u;if(-1===i.indexOf(t.origin))return;if(!this.iframe||t.source!==this.iframe.contentWindow)return;const p=t.data;if(p&&"string"==typeof p.type&&0===p.type.indexOf("hypermid:"))switch(p.type.slice(9)){case"ready":null==(n=(o=this.config).onReady)||n.call(o);break;case"success":null==(r=(s=this.config).onSuccess)||r.call(s,p.payload);break;case"error":null==(c=(a=this.config).onError)||c.call(a,p.payload);break;case"close":null==(h=(d=this.config).onClose)||h.call(d);break;case"resize":if(this.iframe&&p.payload&&"number"==typeof p.payload.height){const t=Math.max(p.payload.height,e);this.iframe.style.height=t+"px",null==(u=(l=this.config).onResize)||u.call(l,t)}}},window.addEventListener("message",this.onMessage)}update(e){this.config={...this.config,...e},this.iframe&&(this.iframe.src=n(this.config))}destroy(){this.onMessage&&window.removeEventListener("message",this.onMessage),this.container&&(this.container.innerHTML=""),this.onMessage=null,this.iframe=null,this.container=null}}"undefined"!=typeof window&&(window.HypermidCheckout=s,"undefined"!=typeof document&&document.addEventListener("DOMContentLoaded",()=>{const e=document.querySelector("[data-hypermid-checkout]");if(!e)return;const t=e.getAttribute("data-checkout-id");t&&s.init({containerId:e.id||"hypermid-checkout",checkoutId:t,theme:e.getAttribute("data-theme")||"dark",label:e.getAttribute("data-label")||void 0,accent:e.getAttribute("data-accent")||void 0})})),e.HypermidCheckout=s,e.default=s,Object.defineProperties(e,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})});
|
|
2
|
+
//# sourceMappingURL=checkout.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"checkout.js","sources":["../src/index.ts"],"sourcesContent":["/**\n * @hypermid/checkout\n *\n * Embeddable crypto checkout for Hypermid — \"pay with any token, the merchant\n * receives the token they asked for.\" Mounts the live Hypermid checkout app\n * (wallet connect, pay-token picker, approval, on-chain-verified settlement) in\n * a sandboxed iframe, so it always reflects production with zero maintenance on\n * the integrator's side. This is the typed, npm-distributed counterpart of the\n * hosted `embed.js` script tag; both share the same iframe contract.\n *\n * The merchant creates a checkout server-side (POST /v1/checkout with their\n * sk_live key) and passes the returned `checkoutId` here. The destination —\n * amount, token, chain, recipient — is bound to that session on the server and\n * can never be overridden from the client (the only thing the payer chooses is\n * which token to pay with).\n *\n * Usage (npm):\n * import { HypermidCheckout } from \"@hypermid/checkout\";\n * const checkout = HypermidCheckout.init({\n * containerId: \"pay\",\n * checkoutId: \"co_…\", // from POST /v1/checkout (server-side)\n * theme: \"dark\",\n * onSuccess: ({ checkoutId }) => fulfillOrder(checkoutId),\n * onError: ({ reason }) => showRetry(reason),\n * });\n *\n * Usage (script tag / UMD):\n * <div id=\"hypermid-checkout\"></div>\n * <script src=\"https://hypermid.io/checkout/v1/embed.js\"></script>\n * <script>\n * HypermidCheckout.init({ containerId: \"hypermid-checkout\", checkoutId: \"co_…\" });\n * </script>\n */\n\nconst APP_BASE = \"https://app.hypermid.io\";\nconst ALLOWED_ORIGINS = [APP_BASE, \"https://hypermid.io\"];\n\nexport interface HypermidCheckoutSuccess {\n checkoutId: string;\n status: \"completed\";\n /** On-chain-verified delivered amount (base units), when surfaced. */\n paidAmount?: string | null;\n /** The payer's source-chain transaction hash. */\n txHash?: string | null;\n}\n\nexport interface HypermidCheckoutError {\n checkoutId: string;\n reason?: string;\n}\n\nexport interface HypermidCheckoutConfig {\n /** DOM element id to mount into. Required. */\n containerId: string;\n /** The checkout session id (co_…) from POST /v1/checkout. Required. */\n checkoutId: string;\n\n theme?: \"dark\" | \"light\";\n /** Action label, e.g. \"Pay\" | \"Deposit\" (merchant-configurable, D-16). */\n label?: string;\n\n /** Theming overrides (hex, with or without `#`) — full token set (D-18). */\n accent?: string;\n bgPage?: string;\n bgCard?: string;\n border?: string;\n textPrimary?: string;\n textMuted?: string;\n fontFamily?: string;\n\n /** Layout. Height is a floor — the frame grows to fit taller content. */\n width?: string;\n height?: string;\n borderRadius?: string;\n\n /** Event callbacks. */\n onReady?: () => void;\n /** Fires on backend on-chain-VERIFIED completion — the only \"paid\" signal. */\n onSuccess?: (payload: HypermidCheckoutSuccess) => void;\n onError?: (payload: HypermidCheckoutError) => void;\n onClose?: () => void;\n onResize?: (height: number) => void;\n}\n\nconst hex = (c: string) => encodeURIComponent(c.replace(\"#\", \"\"));\n\nfunction buildUrl(config: HypermidCheckoutConfig): string {\n const p: string[] = [];\n // checkoutId is the ONLY money-affecting input — destination is server-bound.\n p.push(\"checkoutId=\" + encodeURIComponent(config.checkoutId));\n if (config.theme) p.push(\"theme=\" + encodeURIComponent(config.theme));\n if (config.label) p.push(\"label=\" + encodeURIComponent(config.label));\n\n if (config.accent) p.push(\"accent=\" + hex(config.accent));\n if (config.bgPage) p.push(\"bgPage=\" + hex(config.bgPage));\n if (config.bgCard) p.push(\"bgCard=\" + hex(config.bgCard));\n if (config.border) p.push(\"border=\" + hex(config.border));\n if (config.textPrimary) p.push(\"textPrimary=\" + hex(config.textPrimary));\n if (config.textMuted) p.push(\"textMuted=\" + hex(config.textMuted));\n if (config.fontFamily) p.push(\"font=\" + encodeURIComponent(config.fontFamily));\n\n p.push(\"embed=1\");\n return APP_BASE + \"/checkout?\" + p.join(\"&\");\n}\n\nexport class HypermidCheckout {\n private config: HypermidCheckoutConfig;\n private container: HTMLElement | null = null;\n private iframe: HTMLIFrameElement | null = null;\n private onMessage: ((e: MessageEvent) => void) | null = null;\n\n constructor(config: HypermidCheckoutConfig) {\n this.config = { theme: \"dark\", ...config };\n }\n\n /** Construct + mount in one call (mirrors the script-tag `HypermidCheckout.init`). */\n static init(config: HypermidCheckoutConfig): HypermidCheckout {\n const c = new HypermidCheckout(config);\n c.mount();\n return c;\n }\n\n mount(): this {\n if (typeof document === \"undefined\") return this; // SSR no-op\n if (!this.config.checkoutId) {\n console.error(\"[HypermidCheckout] `checkoutId` is required\");\n return this;\n }\n const container = document.getElementById(this.config.containerId);\n if (!container) {\n console.error(\"[HypermidCheckout] Element #\" + this.config.containerId + \" not found\");\n return this;\n }\n this.container = container;\n\n const iframe = document.createElement(\"iframe\");\n iframe.src = buildUrl(this.config);\n iframe.title = \"Hypermid Checkout\";\n iframe.allow = \"clipboard-write; payment; web-share\";\n iframe.setAttribute(\n \"sandbox\",\n \"allow-scripts allow-same-origin allow-popups allow-popups-to-escape-sandbox allow-forms allow-top-navigation-by-user-activation\",\n );\n const width = this.config.width || \"400px\";\n const height = this.config.height || \"640px\";\n const radius = this.config.borderRadius || \"24px\";\n iframe.style.cssText = [\n \"width:\" + width,\n \"height:\" + height,\n \"max-width:100%\",\n \"border:none\",\n \"border-radius:\" + radius,\n \"box-shadow:0 4px 24px rgba(0,0,0,0.15)\",\n \"display:block\",\n \"margin:0 auto\",\n \"color-scheme:normal\",\n ].join(\";\");\n\n this.iframe = iframe;\n container.innerHTML = \"\";\n container.appendChild(iframe);\n this.listen();\n return this;\n }\n\n private listen(): void {\n const floor = parseInt(String(this.config.height), 10) || 640;\n this.onMessage = (event: MessageEvent) => {\n // Accept only messages from a Hypermid origin AND this checkout's own\n // iframe (supports multiple embeds per page).\n if (ALLOWED_ORIGINS.indexOf(event.origin) === -1) return;\n if (!this.iframe || event.source !== this.iframe.contentWindow) return;\n const data = event.data as { type?: string; payload?: any };\n if (!data || typeof data.type !== \"string\" || data.type.indexOf(\"hypermid:\") !== 0) return;\n\n switch (data.type.slice(\"hypermid:\".length)) {\n case \"ready\":\n this.config.onReady?.();\n break;\n case \"success\":\n this.config.onSuccess?.(data.payload as HypermidCheckoutSuccess);\n break;\n case \"error\":\n this.config.onError?.(data.payload as HypermidCheckoutError);\n break;\n case \"close\":\n this.config.onClose?.();\n break;\n case \"resize\":\n // Grow-only: never shrink below the configured/default height, so a\n // transient small measurement can't collapse the embed.\n if (this.iframe && data.payload && typeof data.payload.height === \"number\") {\n const h = Math.max(data.payload.height, floor);\n this.iframe.style.height = h + \"px\";\n this.config.onResize?.(h);\n }\n break;\n }\n };\n window.addEventListener(\"message\", this.onMessage);\n }\n\n /** Merge new config and reload the iframe URL (e.g. swap in a new checkoutId). */\n update(partial: Partial<HypermidCheckoutConfig>): void {\n this.config = { ...this.config, ...partial };\n if (this.iframe) this.iframe.src = buildUrl(this.config);\n }\n\n /** Remove the embed + its listener. */\n destroy(): void {\n if (this.onMessage) window.removeEventListener(\"message\", this.onMessage);\n if (this.container) this.container.innerHTML = \"\";\n this.onMessage = null;\n this.iframe = null;\n this.container = null;\n }\n}\n\nexport default HypermidCheckout;\n\n// Script-tag (UMD) convenience: expose globally + auto-init from data attributes.\nif (typeof window !== \"undefined\") {\n (window as any).HypermidCheckout = HypermidCheckout;\n if (typeof document !== \"undefined\") {\n document.addEventListener(\"DOMContentLoaded\", () => {\n const el = document.querySelector(\"[data-hypermid-checkout]\") as HTMLElement | null;\n if (!el) return;\n const checkoutId = el.getAttribute(\"data-checkout-id\");\n if (!checkoutId) return;\n HypermidCheckout.init({\n containerId: el.id || \"hypermid-checkout\",\n checkoutId,\n theme: (el.getAttribute(\"data-theme\") as \"dark\" | \"light\") || \"dark\",\n label: el.getAttribute(\"data-label\") || undefined,\n accent: el.getAttribute(\"data-accent\") || undefined,\n });\n });\n }\n}\n"],"names":["APP_BASE","ALLOWED_ORIGINS","hex","c","encodeURIComponent","replace","buildUrl","config","p","push","checkoutId","theme","label","accent","bgPage","bgCard","border","textPrimary","textMuted","fontFamily","join","HypermidCheckout","constructor","this","container","iframe","onMessage","init","mount","document","console","error","getElementById","containerId","createElement","src","title","allow","setAttribute","width","height","radius","borderRadius","style","cssText","innerHTML","appendChild","listen","floor","parseInt","String","event","indexOf","origin","source","contentWindow","data","type","slice","_b","_a","onReady","call","_d","_c","onSuccess","payload","_f","_e","onError","_h","_g","onClose","h","Math","max","_j","_i","onResize","window","addEventListener","update","partial","destroy","removeEventListener","el","querySelector","getAttribute","id"],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @hypermid/checkout
|
|
3
|
+
*
|
|
4
|
+
* Embeddable crypto checkout for Hypermid — "pay with any token, the merchant
|
|
5
|
+
* receives the token they asked for." Mounts the live Hypermid checkout app
|
|
6
|
+
* (wallet connect, pay-token picker, approval, on-chain-verified settlement) in
|
|
7
|
+
* a sandboxed iframe, so it always reflects production with zero maintenance on
|
|
8
|
+
* the integrator's side. This is the typed, npm-distributed counterpart of the
|
|
9
|
+
* hosted `embed.js` script tag; both share the same iframe contract.
|
|
10
|
+
*
|
|
11
|
+
* The merchant creates a checkout server-side (POST /v1/checkout with their
|
|
12
|
+
* sk_live key) and passes the returned `checkoutId` here. The destination —
|
|
13
|
+
* amount, token, chain, recipient — is bound to that session on the server and
|
|
14
|
+
* can never be overridden from the client (the only thing the payer chooses is
|
|
15
|
+
* which token to pay with).
|
|
16
|
+
*
|
|
17
|
+
* Usage (npm):
|
|
18
|
+
* import { HypermidCheckout } from "@hypermid/checkout";
|
|
19
|
+
* const checkout = HypermidCheckout.init({
|
|
20
|
+
* containerId: "pay",
|
|
21
|
+
* checkoutId: "co_…", // from POST /v1/checkout (server-side)
|
|
22
|
+
* theme: "dark",
|
|
23
|
+
* onSuccess: ({ checkoutId }) => fulfillOrder(checkoutId),
|
|
24
|
+
* onError: ({ reason }) => showRetry(reason),
|
|
25
|
+
* });
|
|
26
|
+
*
|
|
27
|
+
* Usage (script tag / UMD):
|
|
28
|
+
* <div id="hypermid-checkout"></div>
|
|
29
|
+
* <script src="https://hypermid.io/checkout/v1/embed.js"></script>
|
|
30
|
+
* <script>
|
|
31
|
+
* HypermidCheckout.init({ containerId: "hypermid-checkout", checkoutId: "co_…" });
|
|
32
|
+
* </script>
|
|
33
|
+
*/
|
|
34
|
+
export interface HypermidCheckoutSuccess {
|
|
35
|
+
checkoutId: string;
|
|
36
|
+
status: "completed";
|
|
37
|
+
/** On-chain-verified delivered amount (base units), when surfaced. */
|
|
38
|
+
paidAmount?: string | null;
|
|
39
|
+
/** The payer's source-chain transaction hash. */
|
|
40
|
+
txHash?: string | null;
|
|
41
|
+
}
|
|
42
|
+
export interface HypermidCheckoutError {
|
|
43
|
+
checkoutId: string;
|
|
44
|
+
reason?: string;
|
|
45
|
+
}
|
|
46
|
+
export interface HypermidCheckoutConfig {
|
|
47
|
+
/** DOM element id to mount into. Required. */
|
|
48
|
+
containerId: string;
|
|
49
|
+
/** The checkout session id (co_…) from POST /v1/checkout. Required. */
|
|
50
|
+
checkoutId: string;
|
|
51
|
+
theme?: "dark" | "light";
|
|
52
|
+
/** Action label, e.g. "Pay" | "Deposit" (merchant-configurable, D-16). */
|
|
53
|
+
label?: string;
|
|
54
|
+
/** Theming overrides (hex, with or without `#`) — full token set (D-18). */
|
|
55
|
+
accent?: string;
|
|
56
|
+
bgPage?: string;
|
|
57
|
+
bgCard?: string;
|
|
58
|
+
border?: string;
|
|
59
|
+
textPrimary?: string;
|
|
60
|
+
textMuted?: string;
|
|
61
|
+
fontFamily?: string;
|
|
62
|
+
/** Layout. Height is a floor — the frame grows to fit taller content. */
|
|
63
|
+
width?: string;
|
|
64
|
+
height?: string;
|
|
65
|
+
borderRadius?: string;
|
|
66
|
+
/** Event callbacks. */
|
|
67
|
+
onReady?: () => void;
|
|
68
|
+
/** Fires on backend on-chain-VERIFIED completion — the only "paid" signal. */
|
|
69
|
+
onSuccess?: (payload: HypermidCheckoutSuccess) => void;
|
|
70
|
+
onError?: (payload: HypermidCheckoutError) => void;
|
|
71
|
+
onClose?: () => void;
|
|
72
|
+
onResize?: (height: number) => void;
|
|
73
|
+
}
|
|
74
|
+
export declare class HypermidCheckout {
|
|
75
|
+
private config;
|
|
76
|
+
private container;
|
|
77
|
+
private iframe;
|
|
78
|
+
private onMessage;
|
|
79
|
+
constructor(config: HypermidCheckoutConfig);
|
|
80
|
+
/** Construct + mount in one call (mirrors the script-tag `HypermidCheckout.init`). */
|
|
81
|
+
static init(config: HypermidCheckoutConfig): HypermidCheckout;
|
|
82
|
+
mount(): this;
|
|
83
|
+
private listen;
|
|
84
|
+
/** Merge new config and reload the iframe URL (e.g. swap in a new checkoutId). */
|
|
85
|
+
update(partial: Partial<HypermidCheckoutConfig>): void;
|
|
86
|
+
/** Remove the embed + its listener. */
|
|
87
|
+
destroy(): void;
|
|
88
|
+
}
|
|
89
|
+
export default HypermidCheckout;
|
|
90
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAKH,MAAM,WAAW,uBAAuB;IACtC,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,WAAW,CAAC;IACpB,sEAAsE;IACtE,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,iDAAiD;IACjD,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;AAED,MAAM,WAAW,qBAAqB;IACpC,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,sBAAsB;IACrC,8CAA8C;IAC9C,WAAW,EAAE,MAAM,CAAC;IACpB,uEAAuE;IACvE,UAAU,EAAE,MAAM,CAAC;IAEnB,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IACzB,0EAA0E;IAC1E,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf,4EAA4E;IAC5E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB,yEAAyE;IACzE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB,uBAAuB;IACvB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,8EAA8E;IAC9E,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,uBAAuB,KAAK,IAAI,CAAC;IACvD,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,qBAAqB,KAAK,IAAI,CAAC;IACnD,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;CACrC;AAuBD,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,MAAM,CAAyB;IACvC,OAAO,CAAC,SAAS,CAA4B;IAC7C,OAAO,CAAC,MAAM,CAAkC;IAChD,OAAO,CAAC,SAAS,CAA4C;gBAEjD,MAAM,EAAE,sBAAsB;IAI1C,sFAAsF;IACtF,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,sBAAsB,GAAG,gBAAgB;IAM7D,KAAK,IAAI,IAAI;IA2Cb,OAAO,CAAC,MAAM;IAqCd,kFAAkF;IAClF,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,sBAAsB,CAAC,GAAG,IAAI;IAKtD,uCAAuC;IACvC,OAAO,IAAI,IAAI;CAOhB;AAED,eAAe,gBAAgB,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@hypermid/checkout",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Embeddable crypto checkout for Hypermid — pay with any token, the merchant receives the token they asked for. Drop in with a single script tag or npm import.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/checkout.js",
|
|
7
|
+
"module": "dist/checkout.esm.js",
|
|
8
|
+
"types": "dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": "./dist/checkout.esm.js",
|
|
12
|
+
"require": "./dist/checkout.js",
|
|
13
|
+
"types": "./dist/index.d.ts"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"publishConfig": {
|
|
17
|
+
"access": "public"
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"dist"
|
|
21
|
+
],
|
|
22
|
+
"sideEffects": false,
|
|
23
|
+
"scripts": {
|
|
24
|
+
"dev": "vite",
|
|
25
|
+
"build": "vite build && tsc --emitDeclarationOnly --declaration --outDir dist",
|
|
26
|
+
"preview": "vite preview",
|
|
27
|
+
"typecheck": "tsc --noEmit",
|
|
28
|
+
"test": "tsx --test test/*.test.ts",
|
|
29
|
+
"prepublishOnly": "npm run build"
|
|
30
|
+
},
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"viem": "^2.21.0"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"@playwright/test": "^1.61.0",
|
|
36
|
+
"terser": "^5.31.0",
|
|
37
|
+
"tsx": "^4.19.0",
|
|
38
|
+
"typescript": "^5.4.0",
|
|
39
|
+
"vite": "^5.4.0"
|
|
40
|
+
},
|
|
41
|
+
"keywords": [
|
|
42
|
+
"hypermid",
|
|
43
|
+
"checkout",
|
|
44
|
+
"crypto",
|
|
45
|
+
"payments",
|
|
46
|
+
"web3",
|
|
47
|
+
"stablecoin",
|
|
48
|
+
"embeddable"
|
|
49
|
+
],
|
|
50
|
+
"license": "MIT",
|
|
51
|
+
"repository": {
|
|
52
|
+
"type": "git",
|
|
53
|
+
"url": "https://github.com/hypermid/checkout.git"
|
|
54
|
+
}
|
|
55
|
+
}
|