@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 +65 -307
- package/dist/react-types/react/yumi-context.d.ts +1 -2
- package/dist/react.js +47 -47
- package/dist/yumi-widget.js +1 -1
- package/dist/yumi-widget.min.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,68 +1,55 @@
|
|
|
1
1
|
# Yumi Widget
|
|
2
2
|
|
|
3
|
-
|
|
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
|
-
|
|
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
|
-
|
|
7
|
+
## Install
|
|
16
8
|
|
|
17
|
-
|
|
9
|
+
```bash
|
|
10
|
+
npm install @yumi-finance/widget
|
|
11
|
+
```
|
|
18
12
|
|
|
19
|
-
|
|
13
|
+
CDN (no build):
|
|
20
14
|
|
|
21
15
|
```html
|
|
22
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
-
##
|
|
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
|
-
|
|
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: '
|
|
79
|
+
publicKey: 'YOUR_PUBLIC_KEY',
|
|
178
80
|
getAppSignature: async payload => {
|
|
179
|
-
const res = await fetch('/
|
|
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
|
|
185
|
-
if (!res.ok
|
|
186
|
-
|
|
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
|
-
##
|
|
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
|
-
|
|
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
|
-
|
|
107
|
+
---
|
|
271
108
|
|
|
272
|
-
|
|
109
|
+
## Backend signing
|
|
273
110
|
|
|
274
|
-
|
|
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
|
-
|
|
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
|
-
|
|
115
|
+
Example (Node; deps: `tweetnacl`, `tweetnacl-util`, `json-canonicalize`):
|
|
291
116
|
|
|
292
117
|
```js
|
|
293
|
-
const
|
|
294
|
-
|
|
295
|
-
const
|
|
296
|
-
|
|
297
|
-
|
|
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('/
|
|
129
|
+
app.post('/api/sign', (req, res) => {
|
|
301
130
|
try {
|
|
302
|
-
|
|
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,
|
|
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
|
|
2
|
-
import { createContext as
|
|
3
|
-
const
|
|
4
|
-
function
|
|
5
|
-
return new Promise((
|
|
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
|
-
|
|
7
|
+
t(new Error("Document is not available"));
|
|
8
8
|
return;
|
|
9
9
|
}
|
|
10
|
-
const
|
|
11
|
-
if (
|
|
12
|
-
const
|
|
13
|
-
if (
|
|
14
|
-
|
|
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
|
|
18
|
-
const
|
|
19
|
-
|
|
17
|
+
let c = !1;
|
|
18
|
+
const o = (d) => {
|
|
19
|
+
c || (c = !0, d ? t(d) : u());
|
|
20
20
|
};
|
|
21
|
-
|
|
22
|
-
|
|
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
|
|
27
|
-
|
|
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
|
|
31
|
-
const t =
|
|
32
|
-
|
|
33
|
-
let
|
|
34
|
-
return
|
|
35
|
-
if (
|
|
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
|
-
|
|
38
|
+
c(new Error("Yumi widget not available"));
|
|
39
39
|
return;
|
|
40
40
|
}
|
|
41
|
-
t.current = e.init(
|
|
41
|
+
t.current = e.init(o.current), r(!0);
|
|
42
42
|
}).catch((e) => {
|
|
43
|
-
|
|
43
|
+
i || c(e instanceof Error ? e : new Error(String(e)));
|
|
44
44
|
}), () => {
|
|
45
45
|
var e;
|
|
46
|
-
|
|
46
|
+
i = !0, (e = t.current) == null || e.destroy(), t.current = null, r(!1);
|
|
47
47
|
};
|
|
48
|
-
}, [
|
|
49
|
-
const
|
|
50
|
-
const
|
|
51
|
-
if (!
|
|
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
|
|
54
|
-
}, []),
|
|
55
|
-
var
|
|
56
|
-
(
|
|
57
|
-
}, []),
|
|
58
|
-
open:
|
|
59
|
-
close:
|
|
60
|
-
isReady:
|
|
61
|
-
error:
|
|
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__ */
|
|
63
|
+
return /* @__PURE__ */ y(w.Provider, { value: E, children: u });
|
|
64
64
|
}
|
|
65
|
-
function
|
|
66
|
-
const n =
|
|
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
|
-
|
|
73
|
-
|
|
74
|
-
|
|
72
|
+
w as YumiContext,
|
|
73
|
+
P as YumiProvider,
|
|
74
|
+
S as useYumi
|
|
75
75
|
};
|
package/dist/yumi-widget.js
CHANGED
package/dist/yumi-widget.min.js
CHANGED
|
@@ -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.
|
|
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"}}();
|