@swype-org/deposit 0.3.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 +175 -0
- package/dist/chunk-RG2MWY2W.js +583 -0
- package/dist/chunk-RG2MWY2W.js.map +1 -0
- package/dist/index.cjs +594 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +78 -0
- package/dist/index.d.ts +78 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/react.cjs +633 -0
- package/dist/react.cjs.map +1 -0
- package/dist/react.d.cts +54 -0
- package/dist/react.d.ts +54 -0
- package/dist/react.js +48 -0
- package/dist/react.js.map +1 -0
- package/dist/types-pHp6PcIz.d.cts +171 -0
- package/dist/types-pHp6PcIz.d.ts +171 -0
- package/package.json +65 -0
|
@@ -0,0 +1,583 @@
|
|
|
1
|
+
// src/errors.ts
|
|
2
|
+
var DepositError = class extends Error {
|
|
3
|
+
code;
|
|
4
|
+
constructor(code, message) {
|
|
5
|
+
super(message);
|
|
6
|
+
this.name = "DepositError";
|
|
7
|
+
this.code = code;
|
|
8
|
+
}
|
|
9
|
+
};
|
|
10
|
+
var CheckoutError2 = DepositError;
|
|
11
|
+
var DISPLAY_MESSAGES = {
|
|
12
|
+
DEPOSIT_DISMISSED: "The deposit was dismissed before the transfer completed.",
|
|
13
|
+
CHECKOUT_DISMISSED: "The checkout was dismissed before the transfer completed.",
|
|
14
|
+
SIGNER_REQUEST_FAILED: "Unable to start the payment. Please try again.",
|
|
15
|
+
SIGNER_NETWORK_ERROR: "Unable to reach the payment server. Check your connection and try again.",
|
|
16
|
+
SIGNER_RESPONSE_INVALID: "The payment server returned an unexpected response.",
|
|
17
|
+
SIGNER_TIMEOUT: "The payment server took too long to respond. Please try again.",
|
|
18
|
+
FLOW_TIMEOUT: "The payment flow timed out. Please try again.",
|
|
19
|
+
INVALID_REQUEST: "Invalid payment request. Please check your input."
|
|
20
|
+
};
|
|
21
|
+
function getDisplayMessage(error) {
|
|
22
|
+
return DISPLAY_MESSAGES[error.code] ?? error.message;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// src/messages.ts
|
|
26
|
+
function parseTransferComplete(data) {
|
|
27
|
+
if (!data || typeof data !== "object") {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
const msg = data;
|
|
31
|
+
if (msg.type !== "blink:transfer-complete") {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
const transfer = msg.transfer;
|
|
35
|
+
if (!transfer || typeof transfer !== "object") {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
const t = transfer;
|
|
39
|
+
if (typeof t.id !== "string" || typeof t.status !== "string") {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
type: "blink:transfer-complete",
|
|
44
|
+
transfer: {
|
|
45
|
+
id: t.id,
|
|
46
|
+
status: t.status,
|
|
47
|
+
amount: parseAmount(t.amount),
|
|
48
|
+
destinations: parseDestinations(t.destinations)
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
function parseAmount(value) {
|
|
53
|
+
if (!value || typeof value !== "object") {
|
|
54
|
+
return void 0;
|
|
55
|
+
}
|
|
56
|
+
const a = value;
|
|
57
|
+
return {
|
|
58
|
+
amount: Number(a.amount ?? 0),
|
|
59
|
+
currency: String(a.currency ?? "")
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
function parseDestinations(value) {
|
|
63
|
+
if (!Array.isArray(value)) {
|
|
64
|
+
return void 0;
|
|
65
|
+
}
|
|
66
|
+
return value.filter((entry) => !!entry && typeof entry === "object").map((entry) => ({
|
|
67
|
+
chainId: String(entry.chainId ?? ""),
|
|
68
|
+
address: String(entry.address ?? ""),
|
|
69
|
+
token: entry.token && typeof entry.token === "object" ? {
|
|
70
|
+
address: String(entry.token.address ?? ""),
|
|
71
|
+
symbol: String(entry.token.symbol ?? "")
|
|
72
|
+
} : void 0
|
|
73
|
+
}));
|
|
74
|
+
}
|
|
75
|
+
function buildAckMessage(transferId) {
|
|
76
|
+
return {
|
|
77
|
+
type: "blink:transfer-complete-ack",
|
|
78
|
+
transferId
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
function parseCloseRequest(data) {
|
|
82
|
+
if (!data || typeof data !== "object") {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
const msg = data;
|
|
86
|
+
if (msg.type !== "blink:close-request") {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
return { type: "blink:close-request" };
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// src/iframe.ts
|
|
93
|
+
var STYLE_ID = "blink-deposit-styles";
|
|
94
|
+
var CLOSE_DURATION_MS = 280;
|
|
95
|
+
var STYLES = `
|
|
96
|
+
@keyframes blink-fade-in{from{opacity:0}to{opacity:1}}
|
|
97
|
+
@keyframes blink-fade-out{from{opacity:1}to{opacity:0}}
|
|
98
|
+
@keyframes blink-slide-up{from{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}}
|
|
99
|
+
@keyframes blink-slide-up-full{from{transform:translateY(100%)}to{transform:translateY(0)}}
|
|
100
|
+
@keyframes blink-slide-down-full{from{transform:translateY(0)}to{transform:translateY(100%)}}
|
|
101
|
+
[data-blink-overlay]{position:fixed;inset:0;background:rgba(0,0,0,.6);display:flex;align-items:center;justify-content:center;z-index:2147483647;animation:blink-fade-in .2s ease-out;backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px)}
|
|
102
|
+
[data-blink-overlay][data-blink-closing]{animation:blink-fade-out ${CLOSE_DURATION_MS}ms ease-in forwards;pointer-events:none}
|
|
103
|
+
[data-blink-container]{width:min(440px,90vw);height:min(600px,85vh);border-radius:20px;overflow:hidden;box-shadow:0 24px 80px rgba(0,0,0,.4);animation:blink-slide-up .25s ease-out;display:flex;flex-direction:column;background:#f9fdff}
|
|
104
|
+
[data-blink-overlay][data-blink-closing] [data-blink-container]{opacity:0;transition:opacity ${CLOSE_DURATION_MS}ms ease-in}
|
|
105
|
+
[data-blink-handle]{display:none;justify-content:center;padding:10px 0 2px;background:#f9fdff;flex-shrink:0}
|
|
106
|
+
[data-blink-handle]::after{content:'';width:36px;height:4px;border-radius:2px;background:#d1d5db}
|
|
107
|
+
[data-blink-container] iframe{width:100%;flex:1;border:none;display:block;background:#f9fdff}
|
|
108
|
+
@media(max-width:480px){[data-blink-overlay]{align-items:flex-end;touch-action:none;overscroll-behavior:contain}[data-blink-container]{width:100%;max-width:100%;height:66vh;border-radius:20px 20px 0 0;box-shadow:0 -8px 40px rgba(0,0,0,.25);animation:blink-slide-up-full .35s cubic-bezier(.32,.72,0,1);padding-bottom:env(safe-area-inset-bottom,0px)}[data-blink-overlay][data-blink-closing] [data-blink-container]{opacity:1;animation:blink-slide-down-full ${CLOSE_DURATION_MS}ms ease-in forwards}[data-blink-handle]{display:flex}}
|
|
109
|
+
`;
|
|
110
|
+
function createIframe(url, containerElement) {
|
|
111
|
+
ensureStyles();
|
|
112
|
+
let closed = false;
|
|
113
|
+
let closeCallback = null;
|
|
114
|
+
const overlay = document.createElement("div");
|
|
115
|
+
overlay.setAttribute("data-blink-overlay", "");
|
|
116
|
+
const container = document.createElement("div");
|
|
117
|
+
container.setAttribute("data-blink-container", "");
|
|
118
|
+
const iframe = document.createElement("iframe");
|
|
119
|
+
iframe.src = url;
|
|
120
|
+
const iframeOrigin = new URL(url).origin;
|
|
121
|
+
iframe.allow = `publickey-credentials-get ${iframeOrigin}; publickey-credentials-create ${iframeOrigin}`;
|
|
122
|
+
const handle = document.createElement("div");
|
|
123
|
+
handle.setAttribute("data-blink-handle", "");
|
|
124
|
+
container.appendChild(handle);
|
|
125
|
+
container.appendChild(iframe);
|
|
126
|
+
overlay.appendChild(container);
|
|
127
|
+
const mountTarget = containerElement ?? document.body;
|
|
128
|
+
mountTarget.appendChild(overlay);
|
|
129
|
+
const savedOverflow = document.body.style.overflow;
|
|
130
|
+
document.body.style.overflow = "hidden";
|
|
131
|
+
const onBackdropClick = (event) => {
|
|
132
|
+
if (event.target === overlay) triggerClose();
|
|
133
|
+
};
|
|
134
|
+
const onKeyDown = (event) => {
|
|
135
|
+
if (event.key === "Escape") triggerClose();
|
|
136
|
+
};
|
|
137
|
+
const onTouchMove = (event) => {
|
|
138
|
+
if (event.target === overlay) event.preventDefault();
|
|
139
|
+
};
|
|
140
|
+
overlay.addEventListener("click", onBackdropClick);
|
|
141
|
+
overlay.addEventListener("touchmove", onTouchMove, { passive: false });
|
|
142
|
+
document.addEventListener("keydown", onKeyDown);
|
|
143
|
+
function removeListeners() {
|
|
144
|
+
overlay.removeEventListener("click", onBackdropClick);
|
|
145
|
+
overlay.removeEventListener("touchmove", onTouchMove);
|
|
146
|
+
document.removeEventListener("keydown", onKeyDown);
|
|
147
|
+
}
|
|
148
|
+
function unlockScroll() {
|
|
149
|
+
document.body.style.overflow = savedOverflow;
|
|
150
|
+
}
|
|
151
|
+
function triggerClose() {
|
|
152
|
+
if (closed) return;
|
|
153
|
+
closed = true;
|
|
154
|
+
removeListeners();
|
|
155
|
+
overlay.setAttribute("data-blink-closing", "");
|
|
156
|
+
let removed = false;
|
|
157
|
+
const removeOverlay = () => {
|
|
158
|
+
if (removed) return;
|
|
159
|
+
removed = true;
|
|
160
|
+
overlay.remove();
|
|
161
|
+
unlockScroll();
|
|
162
|
+
closeCallback?.();
|
|
163
|
+
};
|
|
164
|
+
container.addEventListener("animationend", removeOverlay, { once: true });
|
|
165
|
+
setTimeout(removeOverlay, CLOSE_DURATION_MS + 50);
|
|
166
|
+
}
|
|
167
|
+
return {
|
|
168
|
+
get contentWindow() {
|
|
169
|
+
return iframe.contentWindow;
|
|
170
|
+
},
|
|
171
|
+
close() {
|
|
172
|
+
triggerClose();
|
|
173
|
+
},
|
|
174
|
+
isClosed() {
|
|
175
|
+
return closed;
|
|
176
|
+
},
|
|
177
|
+
onClose(callback) {
|
|
178
|
+
closeCallback = callback;
|
|
179
|
+
},
|
|
180
|
+
destroy() {
|
|
181
|
+
closeCallback = null;
|
|
182
|
+
if (!closed) {
|
|
183
|
+
closed = true;
|
|
184
|
+
removeListeners();
|
|
185
|
+
overlay.remove();
|
|
186
|
+
unlockScroll();
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
function ensureStyles() {
|
|
192
|
+
if (document.getElementById(STYLE_ID)) return;
|
|
193
|
+
const style = document.createElement("style");
|
|
194
|
+
style.id = STYLE_ID;
|
|
195
|
+
style.textContent = STYLES;
|
|
196
|
+
document.head.appendChild(style);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// src/signer.ts
|
|
200
|
+
var DEFAULT_SIGNER_TIMEOUT_MS = 15e3;
|
|
201
|
+
async function callSigner(signer, request, timeoutMs) {
|
|
202
|
+
const timeout = timeoutMs ?? DEFAULT_SIGNER_TIMEOUT_MS;
|
|
203
|
+
const body = typeof signer === "function" ? await invokeSignerFunction(signer, request, timeout) : await invokeSignerUrl(signer, request, timeout);
|
|
204
|
+
validateSignerResponse(body);
|
|
205
|
+
return body;
|
|
206
|
+
}
|
|
207
|
+
async function invokeSignerUrl(signerUrl, request, timeoutMs) {
|
|
208
|
+
let response;
|
|
209
|
+
const controller = new AbortController();
|
|
210
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
211
|
+
try {
|
|
212
|
+
response = await fetch(signerUrl, {
|
|
213
|
+
method: "POST",
|
|
214
|
+
headers: { "Content-Type": "application/json" },
|
|
215
|
+
body: JSON.stringify(request),
|
|
216
|
+
signal: controller.signal
|
|
217
|
+
});
|
|
218
|
+
} catch (err) {
|
|
219
|
+
if (controller.signal.aborted) {
|
|
220
|
+
throw new CheckoutError(
|
|
221
|
+
"SIGNER_TIMEOUT",
|
|
222
|
+
`Signer did not respond within ${timeoutMs}ms.`
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
throw new CheckoutError(
|
|
226
|
+
"SIGNER_NETWORK_ERROR",
|
|
227
|
+
"Unable to reach the merchant signer. Check the signer URL and network connectivity."
|
|
228
|
+
);
|
|
229
|
+
} finally {
|
|
230
|
+
clearTimeout(timer);
|
|
231
|
+
}
|
|
232
|
+
const body = await response.json().catch(() => null);
|
|
233
|
+
if (!response.ok) {
|
|
234
|
+
const detail = body?.error ?? `Signer returned HTTP ${response.status}.`;
|
|
235
|
+
throw new CheckoutError("SIGNER_REQUEST_FAILED", detail);
|
|
236
|
+
}
|
|
237
|
+
return body;
|
|
238
|
+
}
|
|
239
|
+
async function invokeSignerFunction(signer, request, timeoutMs) {
|
|
240
|
+
return new Promise((resolve, reject) => {
|
|
241
|
+
const timer = setTimeout(
|
|
242
|
+
() => reject(new CheckoutError("SIGNER_TIMEOUT", `Signer did not respond within ${timeoutMs}ms.`)),
|
|
243
|
+
timeoutMs
|
|
244
|
+
);
|
|
245
|
+
signer(request).then(
|
|
246
|
+
(result) => {
|
|
247
|
+
clearTimeout(timer);
|
|
248
|
+
resolve(result);
|
|
249
|
+
},
|
|
250
|
+
(err) => {
|
|
251
|
+
clearTimeout(timer);
|
|
252
|
+
if (err instanceof DepositError) {
|
|
253
|
+
reject(err);
|
|
254
|
+
} else {
|
|
255
|
+
reject(new CheckoutError(
|
|
256
|
+
"SIGNER_REQUEST_FAILED",
|
|
257
|
+
err instanceof Error ? err.message : "Signer function threw an error."
|
|
258
|
+
));
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
);
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
function validateSignerResponse(body) {
|
|
265
|
+
const obj = body;
|
|
266
|
+
if (!obj || typeof obj.merchantId !== "string" || typeof obj.payload !== "string" || typeof obj.signature !== "string" || !obj.preview || typeof obj.preview !== "object") {
|
|
267
|
+
throw new CheckoutError(
|
|
268
|
+
"SIGNER_RESPONSE_INVALID",
|
|
269
|
+
"Signer response is missing required fields (merchantId, payload, signature, preview)."
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// src/validation.ts
|
|
275
|
+
var EVM_ADDRESS_RE = /^0x[a-fA-F0-9]{40}$/;
|
|
276
|
+
var TOKEN_ADDRESS_RE = /^0x[a-fA-F0-9]{1,40}$/;
|
|
277
|
+
function validateDepositRequest(request) {
|
|
278
|
+
if (!Number.isFinite(request.amount) || request.amount <= 0) {
|
|
279
|
+
throw new DepositError("INVALID_REQUEST", "amount must be a positive number.");
|
|
280
|
+
}
|
|
281
|
+
if (!Number.isInteger(request.chainId) || request.chainId <= 0) {
|
|
282
|
+
throw new DepositError("INVALID_REQUEST", "chainId must be a positive integer.");
|
|
283
|
+
}
|
|
284
|
+
if (!EVM_ADDRESS_RE.test(request.address)) {
|
|
285
|
+
throw new DepositError(
|
|
286
|
+
"INVALID_REQUEST",
|
|
287
|
+
"address must be a 0x-prefixed, 40-character hex string."
|
|
288
|
+
);
|
|
289
|
+
}
|
|
290
|
+
if (typeof request.token !== "string" || !TOKEN_ADDRESS_RE.test(request.token)) {
|
|
291
|
+
throw new DepositError("INVALID_REQUEST", "token must be a 0x-prefixed hex address.");
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
function buildSignerRequest(request, webviewBaseUrl) {
|
|
295
|
+
const base = {
|
|
296
|
+
amount: request.amount,
|
|
297
|
+
chainId: request.chainId,
|
|
298
|
+
address: request.address,
|
|
299
|
+
token: request.token,
|
|
300
|
+
callbackScheme: request.callbackScheme ?? null,
|
|
301
|
+
url: webviewBaseUrl,
|
|
302
|
+
version: "v1"
|
|
303
|
+
};
|
|
304
|
+
if (request.reference) base.reference = request.reference;
|
|
305
|
+
if (request.metadata && Object.keys(request.metadata).length > 0) {
|
|
306
|
+
base.metadata = request.metadata;
|
|
307
|
+
}
|
|
308
|
+
return base;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// src/checkout.ts
|
|
312
|
+
var DEFAULT_WEBVIEW_BASE_URL = "https://pay.tryblink.xyz";
|
|
313
|
+
var Deposit = class {
|
|
314
|
+
config;
|
|
315
|
+
iframe = null;
|
|
316
|
+
hostedOrigin = null;
|
|
317
|
+
messageListener = null;
|
|
318
|
+
requestId = 0;
|
|
319
|
+
destroyed = false;
|
|
320
|
+
_status = "idle";
|
|
321
|
+
_result = null;
|
|
322
|
+
_error = null;
|
|
323
|
+
flowTimer = null;
|
|
324
|
+
lastSignerResponse = null;
|
|
325
|
+
listeners = {
|
|
326
|
+
complete: /* @__PURE__ */ new Set(),
|
|
327
|
+
error: /* @__PURE__ */ new Set(),
|
|
328
|
+
close: /* @__PURE__ */ new Set(),
|
|
329
|
+
"status-change": /* @__PURE__ */ new Set()
|
|
330
|
+
};
|
|
331
|
+
/** Current phase of the deposit flow. */
|
|
332
|
+
get status() {
|
|
333
|
+
return this._status;
|
|
334
|
+
}
|
|
335
|
+
/** Last successful {@link DepositResult}, available when `status === 'completed'`. */
|
|
336
|
+
get result() {
|
|
337
|
+
return this._result;
|
|
338
|
+
}
|
|
339
|
+
/** Last {@link DepositError}, available when `status === 'error'`. */
|
|
340
|
+
get error() {
|
|
341
|
+
return this._error;
|
|
342
|
+
}
|
|
343
|
+
/** Whether a deposit flow is currently in progress (signer loading or iframe open). */
|
|
344
|
+
get isActive() {
|
|
345
|
+
return this._status === "signer-loading" || this._status === "iframe-active";
|
|
346
|
+
}
|
|
347
|
+
constructor(config) {
|
|
348
|
+
if (!config.signer || typeof config.signer !== "string" && typeof config.signer !== "function") {
|
|
349
|
+
throw new DepositError("INVALID_REQUEST", "DepositConfig.signer is required (URL string or SignerFunction).");
|
|
350
|
+
}
|
|
351
|
+
this.config = config;
|
|
352
|
+
this.log("Deposit instance created", {
|
|
353
|
+
signer: typeof config.signer === "string" ? config.signer : "<function>"
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* Open the hosted payment flow for the given deposit.
|
|
358
|
+
*
|
|
359
|
+
* The returned Promise resolves when the user completes the payment and
|
|
360
|
+
* rejects with a {@link DepositError} on failure.
|
|
361
|
+
*/
|
|
362
|
+
requestDeposit(request) {
|
|
363
|
+
if (this.destroyed) {
|
|
364
|
+
return Promise.reject(
|
|
365
|
+
new DepositError("INVALID_REQUEST", "This Deposit instance has been destroyed.")
|
|
366
|
+
);
|
|
367
|
+
}
|
|
368
|
+
validateDepositRequest(request);
|
|
369
|
+
this.log("requestDeposit called", { amount: request.amount, token: request.token, chainId: request.chainId });
|
|
370
|
+
this.requestId += 1;
|
|
371
|
+
const currentRequestId = this.requestId;
|
|
372
|
+
this.cleanup();
|
|
373
|
+
this._result = null;
|
|
374
|
+
this._error = null;
|
|
375
|
+
this.setStatus("signer-loading");
|
|
376
|
+
return new Promise((resolve, reject) => {
|
|
377
|
+
const settle = (fn) => {
|
|
378
|
+
if (this.requestId !== currentRequestId) return;
|
|
379
|
+
fn();
|
|
380
|
+
};
|
|
381
|
+
const onComplete = (result) => {
|
|
382
|
+
settle(() => {
|
|
383
|
+
this.cleanup();
|
|
384
|
+
this._result = result;
|
|
385
|
+
this._error = null;
|
|
386
|
+
this.setStatus("completed");
|
|
387
|
+
this.emit("complete", result);
|
|
388
|
+
resolve(result);
|
|
389
|
+
});
|
|
390
|
+
};
|
|
391
|
+
const onError = (error) => {
|
|
392
|
+
settle(() => {
|
|
393
|
+
this.cleanup();
|
|
394
|
+
this._error = error;
|
|
395
|
+
this.setStatus("error");
|
|
396
|
+
this.emit("error", error);
|
|
397
|
+
reject(error);
|
|
398
|
+
});
|
|
399
|
+
};
|
|
400
|
+
if (this.config.flowTimeoutMs != null && this.config.flowTimeoutMs > 0) {
|
|
401
|
+
this.flowTimer = setTimeout(() => {
|
|
402
|
+
onError(
|
|
403
|
+
new DepositError(
|
|
404
|
+
"FLOW_TIMEOUT",
|
|
405
|
+
`The payment flow did not complete within ${this.config.flowTimeoutMs}ms.`
|
|
406
|
+
)
|
|
407
|
+
);
|
|
408
|
+
}, this.config.flowTimeoutMs);
|
|
409
|
+
}
|
|
410
|
+
this.runSignerFlow(request, currentRequestId, onComplete, onError);
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
/** Register an event listener. */
|
|
414
|
+
on(event, handler) {
|
|
415
|
+
this.listeners[event].add(handler);
|
|
416
|
+
return this;
|
|
417
|
+
}
|
|
418
|
+
/** Remove a previously registered event listener. */
|
|
419
|
+
off(event, handler) {
|
|
420
|
+
this.listeners[event].delete(handler);
|
|
421
|
+
return this;
|
|
422
|
+
}
|
|
423
|
+
/** No-op — retained for API compatibility with the popup-based SDK. */
|
|
424
|
+
focus() {
|
|
425
|
+
}
|
|
426
|
+
/** Close the deposit iframe without waiting for completion. */
|
|
427
|
+
close() {
|
|
428
|
+
this.log("close() called");
|
|
429
|
+
this.cleanup();
|
|
430
|
+
this.setStatus("idle");
|
|
431
|
+
this.emit("close");
|
|
432
|
+
}
|
|
433
|
+
/** Tear down the instance and release all resources. */
|
|
434
|
+
destroy() {
|
|
435
|
+
this.log("destroy() called");
|
|
436
|
+
this.cleanup();
|
|
437
|
+
this.setStatus("idle");
|
|
438
|
+
this.destroyed = true;
|
|
439
|
+
for (const set of Object.values(this.listeners)) {
|
|
440
|
+
set.clear();
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
// ── Private ──────────────────────────────────────────────────────────
|
|
444
|
+
log(message, data) {
|
|
445
|
+
if (!this.config.debug) return;
|
|
446
|
+
if (data) {
|
|
447
|
+
console.debug(`[BlinkDeposit] ${message}`, data);
|
|
448
|
+
} else {
|
|
449
|
+
console.debug(`[BlinkDeposit] ${message}`);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
setStatus(status) {
|
|
453
|
+
if (this._status === status) return;
|
|
454
|
+
this.log(`Status: ${this._status} \u2192 ${status}`);
|
|
455
|
+
this._status = status;
|
|
456
|
+
this.emit("status-change", status);
|
|
457
|
+
}
|
|
458
|
+
async runSignerFlow(request, requestId, onComplete, onError) {
|
|
459
|
+
try {
|
|
460
|
+
const webviewBaseUrl = this.config.webviewBaseUrl ?? DEFAULT_WEBVIEW_BASE_URL;
|
|
461
|
+
this.log("Calling signer", {
|
|
462
|
+
signer: typeof this.config.signer === "string" ? this.config.signer : "<function>"
|
|
463
|
+
});
|
|
464
|
+
const signerRequest = buildSignerRequest(request, webviewBaseUrl);
|
|
465
|
+
const signerResponse = await callSigner(
|
|
466
|
+
this.config.signer,
|
|
467
|
+
signerRequest,
|
|
468
|
+
this.config.signerTimeoutMs
|
|
469
|
+
);
|
|
470
|
+
this.lastSignerResponse = signerResponse;
|
|
471
|
+
this.log("Signer responded", { merchantId: signerResponse.merchantId });
|
|
472
|
+
if (this.requestId !== requestId) {
|
|
473
|
+
this.log("Request superseded after signer response");
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
476
|
+
const hostedUrl = new URL(webviewBaseUrl);
|
|
477
|
+
hostedUrl.searchParams.set("merchantId", signerResponse.merchantId);
|
|
478
|
+
hostedUrl.searchParams.set("payload", signerResponse.payload);
|
|
479
|
+
hostedUrl.searchParams.set("signature", signerResponse.signature);
|
|
480
|
+
const targetUrl = hostedUrl.toString();
|
|
481
|
+
this.hostedOrigin = this.config.hostedFlowOrigin ?? hostedUrl.origin;
|
|
482
|
+
const iframeHandle = createIframe(targetUrl, this.config.containerElement);
|
|
483
|
+
this.iframe = iframeHandle;
|
|
484
|
+
iframeHandle.onClose(() => {
|
|
485
|
+
onError(
|
|
486
|
+
new DepositError(
|
|
487
|
+
"DEPOSIT_DISMISSED",
|
|
488
|
+
"The deposit was dismissed before the transfer completed."
|
|
489
|
+
)
|
|
490
|
+
);
|
|
491
|
+
this.emit("close");
|
|
492
|
+
});
|
|
493
|
+
this.startMessageListener(iframeHandle, onComplete);
|
|
494
|
+
this.setStatus("iframe-active");
|
|
495
|
+
this.log("Iframe opened with hosted flow", { url: targetUrl });
|
|
496
|
+
} catch (err) {
|
|
497
|
+
if (this.requestId !== requestId) return;
|
|
498
|
+
this.log("Signer flow failed", { error: err instanceof Error ? err.message : String(err) });
|
|
499
|
+
if (err instanceof DepositError) {
|
|
500
|
+
onError(err);
|
|
501
|
+
} else {
|
|
502
|
+
onError(
|
|
503
|
+
new DepositError(
|
|
504
|
+
"SIGNER_NETWORK_ERROR",
|
|
505
|
+
err instanceof Error ? err.message : "Unknown signer error."
|
|
506
|
+
)
|
|
507
|
+
);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
startMessageListener(iframeHandle, onComplete) {
|
|
512
|
+
const handler = (event) => {
|
|
513
|
+
if (this.hostedOrigin && event.origin !== this.hostedOrigin) {
|
|
514
|
+
return;
|
|
515
|
+
}
|
|
516
|
+
const contentWindow = iframeHandle.contentWindow;
|
|
517
|
+
if (event.source && contentWindow && event.source !== contentWindow) {
|
|
518
|
+
return;
|
|
519
|
+
}
|
|
520
|
+
if (parseCloseRequest(event.data)) {
|
|
521
|
+
this.log("Close request received from hosted flow");
|
|
522
|
+
iframeHandle.close();
|
|
523
|
+
return;
|
|
524
|
+
}
|
|
525
|
+
const message = parseTransferComplete(event.data);
|
|
526
|
+
if (!message) {
|
|
527
|
+
return;
|
|
528
|
+
}
|
|
529
|
+
this.log("Transfer complete received", { transferId: message.transfer.id, status: message.transfer.status });
|
|
530
|
+
if (contentWindow) {
|
|
531
|
+
try {
|
|
532
|
+
contentWindow.postMessage(
|
|
533
|
+
buildAckMessage(message.transfer.id),
|
|
534
|
+
this.hostedOrigin ?? "*"
|
|
535
|
+
);
|
|
536
|
+
} catch {
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
const depositResult = { transfer: message.transfer };
|
|
540
|
+
if (this.lastSignerResponse?.preview) {
|
|
541
|
+
depositResult.idempotencyKey = this.lastSignerResponse.preview.idempotencyKey;
|
|
542
|
+
depositResult.preview = {
|
|
543
|
+
amount: this.lastSignerResponse.preview.amount,
|
|
544
|
+
chainId: this.lastSignerResponse.preview.chainId,
|
|
545
|
+
address: this.lastSignerResponse.preview.address,
|
|
546
|
+
token: this.lastSignerResponse.preview.token
|
|
547
|
+
};
|
|
548
|
+
}
|
|
549
|
+
onComplete(depositResult);
|
|
550
|
+
};
|
|
551
|
+
this.messageListener = handler;
|
|
552
|
+
window.addEventListener("message", handler);
|
|
553
|
+
}
|
|
554
|
+
cleanup() {
|
|
555
|
+
if (this.flowTimer) {
|
|
556
|
+
clearTimeout(this.flowTimer);
|
|
557
|
+
this.flowTimer = null;
|
|
558
|
+
}
|
|
559
|
+
if (this.messageListener) {
|
|
560
|
+
window.removeEventListener("message", this.messageListener);
|
|
561
|
+
this.messageListener = null;
|
|
562
|
+
}
|
|
563
|
+
if (this.iframe) {
|
|
564
|
+
this.iframe.destroy();
|
|
565
|
+
this.iframe = null;
|
|
566
|
+
}
|
|
567
|
+
this.hostedOrigin = null;
|
|
568
|
+
this.lastSignerResponse = null;
|
|
569
|
+
}
|
|
570
|
+
emit(event, ...args) {
|
|
571
|
+
for (const handler of this.listeners[event]) {
|
|
572
|
+
try {
|
|
573
|
+
handler(...args);
|
|
574
|
+
} catch {
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
};
|
|
579
|
+
var Checkout = Deposit;
|
|
580
|
+
|
|
581
|
+
export { Checkout, CheckoutError2 as CheckoutError, DEFAULT_WEBVIEW_BASE_URL, Deposit, DepositError, getDisplayMessage };
|
|
582
|
+
//# sourceMappingURL=chunk-RG2MWY2W.js.map
|
|
583
|
+
//# sourceMappingURL=chunk-RG2MWY2W.js.map
|