@yumi-finance/widget 1.0.2 → 1.0.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 CHANGED
@@ -1,68 +1,55 @@
1
1
  # Yumi Widget
2
2
 
3
- ## How to run the project
3
+ Embeddable Yumi BNPL widget: one script or React components; checkout opens in an iframe. After login in the iframe the widget requests a signature; the merchant returns it from their backend (or locally for testing).
4
4
 
5
- ```bash
6
- git clone https://github.com/Yumi-Finance/yumi-widget
7
- npm install
8
- cp .env.example .env
9
- npm run dev
10
- ```
11
-
12
- - **Checkout (iframe):** [http://localhost:3001](http://localhost:3001)
13
- - **Demo:** [http://localhost:8080/demo/](http://localhost:8080/demo/)
5
+ ---
14
6
 
15
- Edit `.env` if you need different `VITE_PRIVY_APP_ID`, `VITE_CHECKOUT_URL`, or `VITE_YUMI_API_BASE_URL`.
7
+ ## Install
16
8
 
17
- ## Example
9
+ ```bash
10
+ npm install @yumi-finance/widget
11
+ ```
18
12
 
19
- **Script (HTML):**
13
+ CDN (no build):
20
14
 
21
15
  ```html
22
- <!DOCTYPE html>
23
- <html>
24
- <head>
25
- <title>Checkout</title>
26
- </head>
27
- <body>
28
- <button id="pay">Pay with Yumi</button>
29
- <script src="https://cdn.jsdelivr.net/npm/@yumi-finance/widget@1.0.0/dist/yumi-widget.min.js"></script>
30
- <script>
31
- const yumi = Yumi.init({
32
- publicKey: 'pk_live_xxx',
33
- getAppSignature: async (payload) => {
34
- const res = await fetch('/api/sign', {
35
- method: 'POST',
36
- headers: { 'Content-Type': 'application/json' },
37
- body: JSON.stringify(payload),
38
- })
39
- const data = await res.json()
40
- if (!res.ok || data.error) throw new Error(data.error || 'Sign failed')
41
- return data.appSignature
42
- },
43
- })
44
- document.getElementById('pay').onclick = () => {
45
- yumi.open({ orderId: 'order_' + Date.now(), amount: 19900 })
46
- }
47
- </script>
48
- </body>
49
- </html>
16
+ <script src="https://cdn.jsdelivr.net/npm/@yumi-finance/widget@1.0.0/dist/yumi-widget.min.js"></script>
50
17
  ```
51
18
 
52
- **React:**
19
+ ---
20
+
21
+ ## React (example)
22
+
23
+ Minimum: `publicKey`, `getAppSignature`, and on click — `open({ orderId, amount })`.
53
24
 
54
25
  ```tsx
55
26
  import { YumiProvider, useYumi } from '@yumi-finance/widget/react'
56
27
 
28
+ // In production getAppSignature calls your backend and returns appSignature
29
+ const getAppSignature = async (payload: {
30
+ publicKey: string
31
+ timestamp: number
32
+ nonce: string
33
+ privyUserId: string
34
+ }): Promise<string> => {
35
+ const res = await fetch('/api/sign', {
36
+ method: 'POST',
37
+ headers: { 'Content-Type': 'application/json' },
38
+ body: JSON.stringify(payload),
39
+ })
40
+ const data = await res.json()
41
+ if (!res.ok || data.error) throw new Error(data.error ?? 'Sign failed')
42
+ return data.appSignature
43
+ }
44
+
57
45
  function CheckoutButton() {
58
46
  const { open, isReady, error } = useYumi()
59
-
60
47
  return (
61
48
  <>
62
49
  {error && <p>Error: {error.message}</p>}
63
50
  <button
64
51
  disabled={!isReady}
65
- onClick={() => open({ orderId: 'order_' + Date.now(), amount: 19900 })}
52
+ onClick={() => open({ orderId: 'order_' + Date.now(), amount: 0.04 })}
66
53
  >
67
54
  Pay with Yumi
68
55
  </button>
@@ -72,313 +59,84 @@ function CheckoutButton() {
72
59
 
73
60
  export default function App() {
74
61
  return (
75
- <YumiProvider
76
- config={{
77
- publicKey: 'pk_live_xxx',
78
- getAppSignature: async (payload) => {
79
- const res = await fetch('/api/sign', {
80
- method: 'POST',
81
- headers: { 'Content-Type': 'application/json' },
82
- body: JSON.stringify(payload),
83
- })
84
- const data = await res.json()
85
- if (!res.ok || data.error) throw new Error(data.error || 'Sign failed')
86
- return data.appSignature
87
- },
88
- }}
89
- >
62
+ <YumiProvider config={{ publicKey: 'YOUR_PUBLIC_KEY', getAppSignature }}>
90
63
  <CheckoutButton />
91
64
  </YumiProvider>
92
65
  )
93
66
  }
94
67
  ```
95
68
 
96
- ---
97
-
98
- # Yumi Widget Integration
99
-
100
- Yumi BNPL integration: client (merchant site), merchant server, and Yumi Server SDK for signing.
101
-
102
- ---
103
-
104
- ## Overview
105
-
106
- A signature is required to authorize the user in Yumi after login via Privy in the iframe. The widget gives the merchant a payload with `publicKey`, `timestamp`, `nonce`, `privyUserId`; the merchant returns a signature; the SDK sends it to Yumi on login.
69
+ Optional: `onClose`, `onEvent`, `onError`.
107
70
 
108
71
  ---
109
72
 
110
- ## 1. Client (Frontend)
111
-
112
- ### 1.1. Script Integration
113
-
114
- ```html
115
- <script src="https://cdn.jsdelivr.net/npm/@yumi-finance/widget@1.0.0/dist/yumi-widget.min.js"></script>
116
- ```
73
+ ## Script only (no React)
117
74
 
118
- Or from your own CDN:
119
-
120
- ```html
121
- <script src="https://cdn.yumi.com/yumi-widget.js"></script>
122
- ```
123
-
124
- ### 1.2. React
125
-
126
- Install the package and wrap your app (or the part that needs checkout) with `YumiProvider`. Use the `useYumi()` hook to open checkout.
127
-
128
- ```bash
129
- npm install @yumi-finance/widget
130
- ```
131
-
132
- ```tsx
133
- import { YumiProvider, useYumi } from '@yumi-finance/widget/react'
134
-
135
- function PayButton() {
136
- const { open, isReady } = useYumi()
137
-
138
- return (
139
- <button
140
- disabled={!isReady}
141
- onClick={() => open({ orderId: 'order_123', amount: 19900 })}
142
- >
143
- Pay with Yumi
144
- </button>
145
- )
146
- }
147
-
148
- function App() {
149
- return (
150
- <YumiProvider
151
- config={{
152
- publicKey: 'pk_live_xxx',
153
- getAppSignature: async (payload) => {
154
- const res = await fetch('/your-api/sign', {
155
- method: 'POST',
156
- headers: { 'Content-Type': 'application/json' },
157
- body: JSON.stringify(payload),
158
- })
159
- const data = await res.json()
160
- if (!res.ok || data.error) throw new Error(data.error || 'Failed to get signature')
161
- return data.appSignature
162
- },
163
- }}
164
- >
165
- <PayButton />
166
- </YumiProvider>
167
- )
168
- }
169
- ```
170
-
171
- Optional: pass `scriptUrl` to load the loader from a custom URL (default: jsDelivr). `useYumi()` returns `{ open, close, isReady, error }`.
172
-
173
- ### 1.3. Initialization and Checkout Opening (script tag only)
75
+ Same contract: `Yumi.init({ publicKey, getAppSignature })`, on click `yumi.open({ orderId, amount })`.
174
76
 
175
77
  ```js
176
78
  const yumi = Yumi.init({
177
- publicKey: 'pk_live_xxx',
79
+ publicKey: 'YOUR_PUBLIC_KEY',
178
80
  getAppSignature: async payload => {
179
- const res = await fetch('/your-api/sign', {
81
+ const res = await fetch('/api/sign', {
180
82
  method: 'POST',
181
83
  headers: { 'Content-Type': 'application/json' },
182
84
  body: JSON.stringify(payload),
183
85
  })
184
- const data = await res.json()
185
- if (!res.ok || data.error) {
186
- throw new Error(data.error || 'Failed to get signature')
187
- }
188
- return data.appSignature
86
+ const { appSignature } = await res.json()
87
+ if (!res.ok) throw new Error('Sign failed')
88
+ return appSignature
189
89
  },
190
90
  })
191
-
192
- document.getElementById('payBtn').addEventListener('click', () => {
193
- yumi.open({
194
- orderId: 'order_123',
195
- amount: 19900,
196
- currency: 'USD',
197
- })
198
- })
91
+ document.getElementById('payBtn').onclick = () => yumi.open({ orderId: 'order_123', amount: 0.04 })
199
92
  ```
200
93
 
201
- ### 1.4. `Yumi.init()` Parameters
202
-
203
-
204
- | Parameter | Required | Description |
205
- | ----------------- | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
206
- | `publicKey` | Yes | Merchant public key (from Yumi). Used in the payload for signing and to identify the merchant on the backend. |
207
- | `getAppSignature` | Yes (production) | Callback `(payload) => Promise<string>`. Widget passes `{ publicKey, timestamp, nonce, privyUserId }`; merchant returns `appSignature` (e.g. from their backend). |
208
- | `onClose` | No | Callback when checkout is closed. |
209
- | `onEvent` | No | Callback for checkout events. |
210
- | `onError` | No | Callback for errors. |
211
- | `meshConfig` | No | Optional Mesh Link config (clientId, clientSecret, useSandbox). |
212
-
213
-
214
- ### 1.5. Payload for Signing
215
-
216
- In `getAppSignature(payload)` you receive:
217
-
218
- ```ts
219
- {
220
- publicKey: string // same publicKey passed to init
221
- timestamp: number // Unix timestamp (seconds)
222
- nonce: string // unique string
223
- privyUserId: string // Privy user ID after login in iframe
224
- }
225
- ```
226
-
227
- The merchant sends this payload to their backend; the backend signs it and returns `appSignature`. On the backend you can use `payload.privyUserId`.
228
-
229
- ### 1.6. `yumi.open()` Options
230
-
231
-
232
- | Option | Required | Description |
233
- | ---------- | -------- | ------------------------------------------------ |
234
- | `orderId` | Yes | Order/intent ID (e.g. `order_123` or intent id). |
235
- | `amount` | Yes | Amount in smallest units (e.g. cents). |
236
- | `currency` | No | Currency code (e.g. `USD`). |
237
-
238
-
239
94
  ---
240
95
 
241
- ## 2. Merchant Server and Yumi Server SDK
242
-
243
- ### 2.1. Yumi Server SDK Purpose
244
-
245
- **Yumi Server SDK** is a library for the merchant backend. It does not start an HTTP server and does not call the Yumi API. The SDK provides **only a signing API**: input is the login payload, output is the `appSignature` string (Ed25519, canonicalize, base64).
246
-
247
- The merchant installs the SDK on the server, stores the secret key (from Yumi), and in their endpoint receives the payload from the frontend, calls the signing method, and returns the signature in the response.
248
-
249
- ### 2.2. Installation
250
-
251
- ```bash
252
- npm install @yumi/backend-sdk
253
- ```
254
-
255
- ### 2.3. Signing API
96
+ ## Parameters
256
97
 
257
- **Method:** sign payload for login.
258
98
 
259
- **Input (payload):**
99
+ | Where | Field | Required | Description |
100
+ | ------ | ----------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
101
+ | `init` | `publicKey` | yes | Merchant public key (from Yumi). Contact the Yumi team to obtain your public and secret keys. |
102
+ | `init` | `getAppSignature` | yes | `(payload) => Promise<string>`. Widget passes `{ publicKey, timestamp, nonce, privyUserId }`; you return the signature (usually from your backend). |
103
+ | `open` | `orderId` | yes | Order or intent id. |
104
+ | `open` | `amount` | yes | Amount (number, e.g. 0.04 in dollars or 4 in cents). |
260
105
 
261
- ```ts
262
- {
263
- publicKey: string
264
- timestamp: number
265
- nonce: string
266
- privyUserId: string
267
- }
268
- ```
269
106
 
270
- **Output:** `appSignature` string (base64 Ed25519 signature of canonicalize(payload) with the merchant secret key).
107
+ ---
271
108
 
272
- **Example (Node.js):**
109
+ ## Backend signing
273
110
 
274
- ```js
275
- const { signLoginPayload } = require('@yumi/backend-sdk')
111
+ Contact the Yumi team to get your **public key** and **secret key**. Keep the secret key only on your backend (e.g. in env); never expose it to the frontend.
276
112
 
277
- app.post('/your-api/sign', async (req, res) => {
278
- try {
279
- const payload = req.body
280
- const appSignature = signLoginPayload(payload, {
281
- secretKeyBase64: process.env.YUMI_SECRET_KEY,
282
- })
283
- res.json({ appSignature })
284
- } catch (err) {
285
- res.status(400).json({ error: err.message })
286
- }
287
- })
288
- ```
113
+ Your `/api/sign` endpoint receives the payload `{ publicKey, timestamp, nonce, privyUserId }` and must return `{ appSignature }`: the **base64-encoded Ed25519 signature** of the **canonical JSON** of that payload, signed with your secret key.
289
114
 
290
- **Example with pre-configuration:**
115
+ Example (Node; deps: `tweetnacl`, `tweetnacl-util`, `json-canonicalize`):
291
116
 
292
117
  ```js
293
- const { YumiBackend } = require('@yumi/backend-sdk')
294
-
295
- const yumi = new YumiBackend({
296
- secretKeyBase64: process.env.YUMI_SECRET_KEY,
297
- publicKeyBase64: process.env.YUMI_PUBLIC_KEY,
298
- })
118
+ const nacl = require('tweetnacl')
119
+ const util = require('tweetnacl-util')
120
+ const { canonicalize } = require('json-canonicalize')
121
+
122
+ function signLoginPayload(payload, secretKeyBase64) {
123
+ const secretKey = util.decodeBase64(secretKeyBase64)
124
+ const message = canonicalize(payload)
125
+ const signature = nacl.sign.detached(util.decodeUTF8(message), secretKey)
126
+ return util.encodeBase64(signature)
127
+ }
299
128
 
300
- app.post('/your-api/sign', async (req, res) => {
129
+ app.post('/api/sign', (req, res) => {
301
130
  try {
302
- const payload = req.body
303
- const appSignature = yumi.signLoginPayload(payload)
304
- res.json({ appSignature })
131
+ res.json({ appSignature: signLoginPayload(req.body, process.env.YUMI_SECRET_KEY) })
305
132
  } catch (err) {
306
133
  res.status(400).json({ error: err.message })
307
134
  }
308
135
  })
309
136
  ```
310
137
 
311
- Package and method names may differ; the SDK provides only signing of the payload and returns `appSignature`.
312
-
313
- ### 2.4. Security
314
-
315
- - Store the secret key (`YUMI_SECRET_KEY`) only on the backend (env or secret store).
316
- - Yumi Server SDK does not make network requests; it only computes the signature.
317
- - The signing endpoint should be callable only from your frontend (CORS; optionally check origin or token).
318
-
319
- ### 2.5. Without Server SDK
320
-
321
- The merchant can implement signing themselves: same algorithm (Ed25519, canonicalize payload, base64). Payload contract and `appSignature` format are as above; details are in Yumi cryptography docs. Using the Server SDK is recommended to avoid reimplementing the logic.
322
-
323
- ---
324
-
325
- ## 3. Login Sequence
326
-
327
- 1. User logs in inside the Yumi iframe with Privy.
328
- 2. SDK in the iframe builds the payload: `publicKey` (from init), `timestamp`, `nonce`, `privyUserId`.
329
- 3. SDK calls `getAppSignature(payload)` — the request is handled by the merchant frontend.
330
- 4. Merchant frontend sends the payload to their backend (e.g. `POST /your-api/sign`).
331
- 5. Backend signs the payload (Yumi Server SDK or custom) and returns `{ appSignature }`.
332
- 6. Frontend returns `appSignature` to the widget callback.
333
- 7. Widget sends the login request to Yumi with `accessToken`, `identityToken`, `payload`, and `appSignature`.
334
-
335
- ---
336
-
337
- ## 4. Quick Checklist
338
-
339
- **Client:**
340
-
341
- - Include `yumi-widget.js` (CDN or self-hosted).
342
- - `Yumi.init({ publicKey, getAppSignature, ... })` — in `getAppSignature` call your backend and return `appSignature`.
343
- - On payment button click: `yumi.open({ orderId, amount, currency? })`.
344
-
345
- **Server:**
346
-
347
- - Install Yumi Server SDK (or implement signing yourself).
348
- - Endpoint: accept payload, call signing, return `{ appSignature }`.
349
- - Configure CORS for the frontend domain and, if needed, access checks.
350
-
351
- **Yumi Server SDK:** provides only the signing API (one method for the login payload); it does not provide HTTP and does not call the Yumi API.
352
-
353
138
  ---
354
139
 
355
- ## 5. Development
356
-
357
- ```bash
358
- npm install
359
- npm run dev
360
- ```
361
-
362
- - Checkout (iframe): [http://localhost:3001](http://localhost:3001)
363
- - Demo: [http://localhost:8080/demo/](http://localhost:8080/demo/)
364
-
365
- ```bash
366
- npm run build
367
- ```
368
-
369
- Builds loader (`dist/yumi-widget.js`, `dist/yumi-widget.min.js`) and checkout (`dist/checkout/`).
370
-
371
-
372
- | Command | Description |
373
- | ---------------------- | ------------------------------------- |
374
- | `npm run dev` | Loader watch + checkout + demo server |
375
- | `npm run build` | Production build |
376
- | `npm run dev:loader` | Loader only (watch) |
377
- | `npm run dev:checkout` | Checkout only (Vite, port 3001) |
378
- | `npm run type-check` | TypeScript check |
379
- | `npm run lint` | ESLint |
380
-
381
-
382
140
  ## License
383
141
 
384
142
  MIT
@@ -9,8 +9,7 @@ export interface YumiContextValue {
9
9
  export declare const YumiContext: import("react").Context<YumiContextValue | null>;
10
10
  interface YumiProviderProps {
11
11
  config: YumiConfig;
12
- scriptUrl?: string;
13
12
  children: ReactNode;
14
13
  }
15
- export declare function YumiProvider({ config, scriptUrl, children }: YumiProviderProps): import("react/jsx-runtime").JSX.Element;
14
+ export declare function YumiProvider({ config, children }: YumiProviderProps): import("react/jsx-runtime").JSX.Element;
16
15
  export {};
package/dist/react.js CHANGED
@@ -1,75 +1,75 @@
1
- import { jsx as h } from "react/jsx-runtime";
2
- import { createContext as v, useRef as f, useState as m, useEffect as x, useCallback as w, useContext as R } from "react";
3
- const g = "https://cdn.jsdelivr.net/npm/@yumi-finance/widget@1.0.0/dist/yumi-widget.min.js", p = v(null);
4
- function C(n) {
5
- return new Promise((o, u) => {
1
+ import { jsx as y } from "react/jsx-runtime";
2
+ import { createContext as Y, useRef as a, useState as f, useEffect as h, useCallback as m, useContext as v } from "react";
3
+ const x = "https://cdn.jsdelivr.net/npm/@yumi-finance/widget@1.0.3/dist/yumi-widget.min.js", w = Y(null);
4
+ function R(n) {
5
+ return new Promise((u, t) => {
6
6
  if (typeof document > "u") {
7
- u(new Error("Document is not available"));
7
+ t(new Error("Document is not available"));
8
8
  return;
9
9
  }
10
- const t = document.querySelector(`script[src="${n}"]`);
11
- if (t) {
12
- const c = window;
13
- if (c.Yumi) {
14
- o();
10
+ const s = document.querySelector(`script[src="${n}"]`);
11
+ if (s) {
12
+ const l = window;
13
+ if (l.Yumi) {
14
+ u();
15
15
  return;
16
16
  }
17
- let d = !1;
18
- const s = (l) => {
19
- d || (d = !0, l ? u(l) : o());
17
+ let c = !1;
18
+ const o = (d) => {
19
+ c || (c = !0, d ? t(d) : u());
20
20
  };
21
- t.addEventListener("load", () => s()), t.addEventListener("error", () => s(new Error("Failed to load Yumi script"))), setTimeout(() => {
22
- c.Yumi && s();
21
+ s.addEventListener("load", () => o()), s.addEventListener("error", () => o(new Error("Failed to load Yumi script"))), setTimeout(() => {
22
+ l.Yumi && o();
23
23
  }, 0);
24
24
  return;
25
25
  }
26
- const i = document.createElement("script");
27
- i.src = n, i.async = !0, i.onload = () => o(), i.onerror = () => u(new Error("Failed to load Yumi script")), document.head.appendChild(i);
26
+ const r = document.createElement("script");
27
+ r.src = n, r.async = !0, r.onload = () => u(), r.onerror = () => t(new Error("Failed to load Yumi script")), document.head.appendChild(r);
28
28
  });
29
29
  }
30
- function b({ config: n, scriptUrl: o, children: u }) {
31
- const t = f(null), [i, c] = m(!1), [d, s] = m(null), l = f(n);
32
- l.current = n, x(() => {
33
- let r = !1;
34
- return C(o ?? g).then(() => {
35
- if (r) return;
30
+ function P({ config: n, children: u }) {
31
+ const t = a(null), [s, r] = f(!1), [l, c] = f(null), o = a(n);
32
+ o.current = n, h(() => {
33
+ let i = !1;
34
+ return R(x).then(() => {
35
+ if (i) return;
36
36
  const e = window.Yumi;
37
37
  if (!(e != null && e.init)) {
38
- s(new Error("Yumi widget not available"));
38
+ c(new Error("Yumi widget not available"));
39
39
  return;
40
40
  }
41
- t.current = e.init(l.current), c(!0);
41
+ t.current = e.init(o.current), r(!0);
42
42
  }).catch((e) => {
43
- r || s(e instanceof Error ? e : new Error(String(e)));
43
+ i || c(e instanceof Error ? e : new Error(String(e)));
44
44
  }), () => {
45
45
  var e;
46
- r = !0, (e = t.current) == null || e.destroy(), t.current = null, c(!1);
46
+ i = !0, (e = t.current) == null || e.destroy(), t.current = null, r(!1);
47
47
  };
48
- }, [o]);
49
- const E = w(async (r) => {
50
- const a = t.current;
51
- if (!a)
48
+ }, []);
49
+ const d = m(async (i) => {
50
+ const e = t.current;
51
+ if (!e)
52
52
  throw new Error("Yumi widget is not ready yet");
53
- await a.open(r);
54
- }, []), y = w(() => {
55
- var r;
56
- (r = t.current) == null || r.close();
57
- }, []), Y = {
58
- open: E,
59
- close: y,
60
- isReady: i,
61
- error: d
53
+ await e.open(i);
54
+ }, []), p = m(() => {
55
+ var i;
56
+ (i = t.current) == null || i.close();
57
+ }, []), E = {
58
+ open: d,
59
+ close: p,
60
+ isReady: s,
61
+ error: l
62
62
  };
63
- return /* @__PURE__ */ h(p.Provider, { value: Y, children: u });
63
+ return /* @__PURE__ */ y(w.Provider, { value: E, children: u });
64
64
  }
65
- function L() {
66
- const n = R(p);
65
+ function S() {
66
+ const n = v(w);
67
67
  if (n === null)
68
68
  throw new Error("useYumi must be used within YumiProvider");
69
69
  return n;
70
70
  }
71
71
  export {
72
- p as YumiContext,
73
- b as YumiProvider,
74
- L as useYumi
72
+ w as YumiContext,
73
+ P as YumiProvider,
74
+ S as useYumi
75
75
  };
@@ -283,7 +283,7 @@
283
283
  }
284
284
  window.Yumi = {
285
285
  init,
286
- version: "1.0.2" ,
286
+ version: "1.0.3" ,
287
287
  };
288
288
  })();
289
289
 
@@ -1 +1 @@
1
- !function(){"use strict";class e{constructor(){Object.defineProperty(this,"iframe",{enumerable:!0,configurable:!0,writable:!0,value:null}),Object.defineProperty(this,"overlay",{enumerable:!0,configurable:!0,writable:!0,value:null})}create(e){return this.overlay=document.createElement("div"),this.overlay.id="yumi-checkout-overlay",this.overlay.style.cssText="\n\t\t\tposition: fixed;\n\t\t\tinset: 0;\n\t\t\tz-index: 999999;\n\t\t\tbackground: rgba(0, 0, 0, 0.5);\n\t\t\tdisplay: flex;\n\t\t\talign-items: center;\n\t\t\tjustify-content: center;\n\t\t",this.iframe=document.createElement("iframe"),this.iframe.id="yumi-checkout-iframe",this.iframe.src=e.url,this.iframe.style.cssText="\n\t\t\twidth: 100%;\n\t\t\tmax-width: 500px;\n\t\t\theight: 90vh;\n\t\t\tmax-height: 800px;\n\t\t\tborder: none;\n\t\t\tborder-radius: 12px;\n\t\t\tbackground: white;\n\t\t",this.iframe.setAttribute("sandbox","allow-scripts allow-same-origin allow-forms allow-popups allow-popups-to-escape-sandbox"),this.iframe.setAttribute("allow","payment; clipboard-read; clipboard-write"),this.overlay.appendChild(this.iframe),document.body.appendChild(this.overlay),this.iframe}resize(e){this.iframe&&(this.iframe.style.height=`${Math.min(e,.9*window.innerHeight)}px`)}destroy(){this.overlay&&(this.overlay.remove(),this.overlay=null),this.iframe=null}}class t{constructor(e){Object.defineProperty(this,"targetWindow",{enumerable:!0,configurable:!0,writable:!0,value:null}),Object.defineProperty(this,"allowedOrigin",{enumerable:!0,configurable:!0,writable:!0,value:"*"}),Object.defineProperty(this,"onEvent",{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,"messageHandler",{enumerable:!0,configurable:!0,writable:!0,value:void 0}),this.onEvent=e.onEvent,this.messageHandler=this.handleMessage.bind(this)}connect(e,t){this.targetWindow=e,this.allowedOrigin=t.allowedOrigin,window.addEventListener("message",this.messageHandler)}disconnect(){window.removeEventListener("message",this.messageHandler),this.targetWindow=null}send(e){this.targetWindow?this.targetWindow.postMessage(e,this.allowedOrigin):console.warn("Bridge not connected, cannot send message")}handleMessage(e){if(e.source!==this.targetWindow)return;const t=e.data;t&&t.type&&this.onEvent(t)}}const i="https://yumi-sdk-checkout.vercel.app/";class r{constructor(i){Object.defineProperty(this,"config",{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,"iframeManager",{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,"bridge",{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,"currentSessionId",{enumerable:!0,configurable:!0,writable:!0,value:null}),Object.defineProperty(this,"currentOpenOptions",{enumerable:!0,configurable:!0,writable:!0,value:null}),this.config=i,this.iframeManager=new e,this.bridge=new t({onEvent:this.handleIframeEvent.bind(this)})}async open(e){if(!e?.orderId)throw new Error("Yumi.open: orderId is required");if(null==e.amount||!Number.isFinite(Number(e.amount)))throw new Error("Yumi.open: amount is required and must be a finite number");this.currentSessionId=this.generateSessionId(),this.currentOpenOptions=e;const t=this.iframeManager.create({url:this.getCheckoutUrl(e),sessionId:this.currentSessionId});this.bridge.connect(t.contentWindow,{allowedOrigin:this.getCheckoutOrigin()})}close(){this.iframeManager.destroy(),this.bridge.disconnect(),this.currentSessionId=null,this.currentOpenOptions=null}destroy(){this.close()}handleIframeEvent(e){switch(e.type){case"LOADED":if(this.currentSessionId&&this.currentOpenOptions){const e={sessionId:this.currentSessionId,publicKey:this.config.publicKey,privyAppId:"cmhx0hub300mgjl0dfm9pg1e6",orderId:this.currentOpenOptions.orderId,amount:this.currentOpenOptions.amount,apiKey:this.config.apiKey};this.bridge.send({type:"INIT",payload:e})}break;case"READY":break;case"RESIZE":this.iframeManager.resize(e.payload.height);break;case"CLOSE":this.close(),this.config.onClose?.();break;case"SUCCESS":case"EVENT":this.config.onEvent?.(e);break;case"ERROR":this.config.onError?.(e.payload);break;case"GET_APP_SIGNATURE":{const{payload:t,requestId:i}=e,r=this.config.getAppSignature;if(!r)return void this.bridge.send({type:"APP_SIGNATURE_RESPONSE",requestId:i,error:"getAppSignature is not configured"});r(t).then(e=>{this.bridge.send({type:"APP_SIGNATURE_RESPONSE",requestId:i,appSignature:e})}).catch(e=>{this.bridge.send({type:"APP_SIGNATURE_RESPONSE",requestId:i,error:e instanceof Error?e.message:String(e)})});break}}}getCheckoutUrl(e){const t=i,r=new URLSearchParams({orderId:e.orderId,session:this.currentSessionId});return e.amount&&r.set("amount",e.amount.toString()),`${t}?${r.toString()}`}getCheckoutOrigin(){const e=i;try{return new URL(e).origin}catch{return"*"}}generateSessionId(){return`sess_${Date.now()}_${Math.random().toString(36).substr(2,9)}`}}window.Yumi?console.warn("Yumi widget already loaded"):window.Yumi={init:function(e){if(!e?.publicKey)throw new Error("Yumi.init: publicKey is required");if("function"!=typeof e.getAppSignature)throw new Error("Yumi.init: getAppSignature is required");return new r(e)},version:"1.0.2"}}();
1
+ !function(){"use strict";class e{constructor(){Object.defineProperty(this,"iframe",{enumerable:!0,configurable:!0,writable:!0,value:null}),Object.defineProperty(this,"overlay",{enumerable:!0,configurable:!0,writable:!0,value:null})}create(e){return this.overlay=document.createElement("div"),this.overlay.id="yumi-checkout-overlay",this.overlay.style.cssText="\n\t\t\tposition: fixed;\n\t\t\tinset: 0;\n\t\t\tz-index: 999999;\n\t\t\tbackground: rgba(0, 0, 0, 0.5);\n\t\t\tdisplay: flex;\n\t\t\talign-items: center;\n\t\t\tjustify-content: center;\n\t\t",this.iframe=document.createElement("iframe"),this.iframe.id="yumi-checkout-iframe",this.iframe.src=e.url,this.iframe.style.cssText="\n\t\t\twidth: 100%;\n\t\t\tmax-width: 500px;\n\t\t\theight: 90vh;\n\t\t\tmax-height: 800px;\n\t\t\tborder: none;\n\t\t\tborder-radius: 12px;\n\t\t\tbackground: white;\n\t\t",this.iframe.setAttribute("sandbox","allow-scripts allow-same-origin allow-forms allow-popups allow-popups-to-escape-sandbox"),this.iframe.setAttribute("allow","payment; clipboard-read; clipboard-write"),this.overlay.appendChild(this.iframe),document.body.appendChild(this.overlay),this.iframe}resize(e){this.iframe&&(this.iframe.style.height=`${Math.min(e,.9*window.innerHeight)}px`)}destroy(){this.overlay&&(this.overlay.remove(),this.overlay=null),this.iframe=null}}class t{constructor(e){Object.defineProperty(this,"targetWindow",{enumerable:!0,configurable:!0,writable:!0,value:null}),Object.defineProperty(this,"allowedOrigin",{enumerable:!0,configurable:!0,writable:!0,value:"*"}),Object.defineProperty(this,"onEvent",{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,"messageHandler",{enumerable:!0,configurable:!0,writable:!0,value:void 0}),this.onEvent=e.onEvent,this.messageHandler=this.handleMessage.bind(this)}connect(e,t){this.targetWindow=e,this.allowedOrigin=t.allowedOrigin,window.addEventListener("message",this.messageHandler)}disconnect(){window.removeEventListener("message",this.messageHandler),this.targetWindow=null}send(e){this.targetWindow?this.targetWindow.postMessage(e,this.allowedOrigin):console.warn("Bridge not connected, cannot send message")}handleMessage(e){if(e.source!==this.targetWindow)return;const t=e.data;t&&t.type&&this.onEvent(t)}}const i="https://yumi-sdk-checkout.vercel.app/";class r{constructor(i){Object.defineProperty(this,"config",{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,"iframeManager",{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,"bridge",{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,"currentSessionId",{enumerable:!0,configurable:!0,writable:!0,value:null}),Object.defineProperty(this,"currentOpenOptions",{enumerable:!0,configurable:!0,writable:!0,value:null}),this.config=i,this.iframeManager=new e,this.bridge=new t({onEvent:this.handleIframeEvent.bind(this)})}async open(e){if(!e?.orderId)throw new Error("Yumi.open: orderId is required");if(null==e.amount||!Number.isFinite(Number(e.amount)))throw new Error("Yumi.open: amount is required and must be a finite number");this.currentSessionId=this.generateSessionId(),this.currentOpenOptions=e;const t=this.iframeManager.create({url:this.getCheckoutUrl(e),sessionId:this.currentSessionId});this.bridge.connect(t.contentWindow,{allowedOrigin:this.getCheckoutOrigin()})}close(){this.iframeManager.destroy(),this.bridge.disconnect(),this.currentSessionId=null,this.currentOpenOptions=null}destroy(){this.close()}handleIframeEvent(e){switch(e.type){case"LOADED":if(this.currentSessionId&&this.currentOpenOptions){const e={sessionId:this.currentSessionId,publicKey:this.config.publicKey,privyAppId:"cmhx0hub300mgjl0dfm9pg1e6",orderId:this.currentOpenOptions.orderId,amount:this.currentOpenOptions.amount,apiKey:this.config.apiKey};this.bridge.send({type:"INIT",payload:e})}break;case"READY":break;case"RESIZE":this.iframeManager.resize(e.payload.height);break;case"CLOSE":this.close(),this.config.onClose?.();break;case"SUCCESS":case"EVENT":this.config.onEvent?.(e);break;case"ERROR":this.config.onError?.(e.payload);break;case"GET_APP_SIGNATURE":{const{payload:t,requestId:i}=e,r=this.config.getAppSignature;if(!r)return void this.bridge.send({type:"APP_SIGNATURE_RESPONSE",requestId:i,error:"getAppSignature is not configured"});r(t).then(e=>{this.bridge.send({type:"APP_SIGNATURE_RESPONSE",requestId:i,appSignature:e})}).catch(e=>{this.bridge.send({type:"APP_SIGNATURE_RESPONSE",requestId:i,error:e instanceof Error?e.message:String(e)})});break}}}getCheckoutUrl(e){const t=i,r=new URLSearchParams({orderId:e.orderId,session:this.currentSessionId});return e.amount&&r.set("amount",e.amount.toString()),`${t}?${r.toString()}`}getCheckoutOrigin(){const e=i;try{return new URL(e).origin}catch{return"*"}}generateSessionId(){return`sess_${Date.now()}_${Math.random().toString(36).substr(2,9)}`}}window.Yumi?console.warn("Yumi widget already loaded"):window.Yumi={init:function(e){if(!e?.publicKey)throw new Error("Yumi.init: publicKey is required");if("function"!=typeof e.getAppSignature)throw new Error("Yumi.init: getAppSignature is required");return new r(e)},version:"1.0.3"}}();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yumi-finance/widget",
3
- "version": "1.0.2",
3
+ "version": "1.0.3",
4
4
  "description": "Yumi BNPL checkout widget",
5
5
  "type": "module",
6
6
  "main": "dist/yumi-widget.js",