@alphadevs_labs/upay-sdk 0.0.1
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/dist/index.d.mts +57 -0
- package/dist/index.d.ts +57 -0
- package/dist/index.js +289 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +251 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +41 -0
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
interface UPayPayment {
|
|
4
|
+
id: string;
|
|
5
|
+
sessionId: string;
|
|
6
|
+
payerAddress: string;
|
|
7
|
+
settleToken: string;
|
|
8
|
+
settleChain: string;
|
|
9
|
+
amount: string;
|
|
10
|
+
txHashes: string[];
|
|
11
|
+
settledAt: string;
|
|
12
|
+
}
|
|
13
|
+
interface UPayCheckoutSession {
|
|
14
|
+
id: string;
|
|
15
|
+
checkoutUrl: string;
|
|
16
|
+
amount: string;
|
|
17
|
+
settleToken: string;
|
|
18
|
+
settleChain: string;
|
|
19
|
+
status: "pending" | "paid" | "failed" | "expired";
|
|
20
|
+
expiresAt: string;
|
|
21
|
+
}
|
|
22
|
+
interface UPayButtonProps {
|
|
23
|
+
apiKey: string;
|
|
24
|
+
baseUrl?: string;
|
|
25
|
+
amount: string;
|
|
26
|
+
token?: string;
|
|
27
|
+
chain?: string;
|
|
28
|
+
metadata?: Record<string, unknown>;
|
|
29
|
+
onSuccess?: (payment: UPayPayment) => void;
|
|
30
|
+
onError?: (err: Error) => void;
|
|
31
|
+
label?: string;
|
|
32
|
+
className?: string;
|
|
33
|
+
}
|
|
34
|
+
interface UPayClientOptions {
|
|
35
|
+
apiKey: string;
|
|
36
|
+
baseUrl?: string;
|
|
37
|
+
}
|
|
38
|
+
interface CreateCheckoutOptions {
|
|
39
|
+
amount: string;
|
|
40
|
+
token?: string;
|
|
41
|
+
chain?: string;
|
|
42
|
+
metadata?: Record<string, unknown>;
|
|
43
|
+
successUrl?: string;
|
|
44
|
+
cancelUrl?: string;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
declare function UPayButton({ apiKey, baseUrl, amount, token, chain, metadata, onSuccess, onError, label, className, }: UPayButtonProps): React.JSX.Element;
|
|
48
|
+
|
|
49
|
+
declare class UPay {
|
|
50
|
+
private apiKey;
|
|
51
|
+
private baseUrl;
|
|
52
|
+
constructor(options: UPayClientOptions);
|
|
53
|
+
createCheckout(options: CreateCheckoutOptions): Promise<UPayCheckoutSession>;
|
|
54
|
+
openCheckout(sessionId: string): void;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export { type CreateCheckoutOptions, UPay, UPayButton, type UPayButtonProps, type UPayCheckoutSession, type UPayClientOptions, type UPayPayment };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
interface UPayPayment {
|
|
4
|
+
id: string;
|
|
5
|
+
sessionId: string;
|
|
6
|
+
payerAddress: string;
|
|
7
|
+
settleToken: string;
|
|
8
|
+
settleChain: string;
|
|
9
|
+
amount: string;
|
|
10
|
+
txHashes: string[];
|
|
11
|
+
settledAt: string;
|
|
12
|
+
}
|
|
13
|
+
interface UPayCheckoutSession {
|
|
14
|
+
id: string;
|
|
15
|
+
checkoutUrl: string;
|
|
16
|
+
amount: string;
|
|
17
|
+
settleToken: string;
|
|
18
|
+
settleChain: string;
|
|
19
|
+
status: "pending" | "paid" | "failed" | "expired";
|
|
20
|
+
expiresAt: string;
|
|
21
|
+
}
|
|
22
|
+
interface UPayButtonProps {
|
|
23
|
+
apiKey: string;
|
|
24
|
+
baseUrl?: string;
|
|
25
|
+
amount: string;
|
|
26
|
+
token?: string;
|
|
27
|
+
chain?: string;
|
|
28
|
+
metadata?: Record<string, unknown>;
|
|
29
|
+
onSuccess?: (payment: UPayPayment) => void;
|
|
30
|
+
onError?: (err: Error) => void;
|
|
31
|
+
label?: string;
|
|
32
|
+
className?: string;
|
|
33
|
+
}
|
|
34
|
+
interface UPayClientOptions {
|
|
35
|
+
apiKey: string;
|
|
36
|
+
baseUrl?: string;
|
|
37
|
+
}
|
|
38
|
+
interface CreateCheckoutOptions {
|
|
39
|
+
amount: string;
|
|
40
|
+
token?: string;
|
|
41
|
+
chain?: string;
|
|
42
|
+
metadata?: Record<string, unknown>;
|
|
43
|
+
successUrl?: string;
|
|
44
|
+
cancelUrl?: string;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
declare function UPayButton({ apiKey, baseUrl, amount, token, chain, metadata, onSuccess, onError, label, className, }: UPayButtonProps): React.JSX.Element;
|
|
48
|
+
|
|
49
|
+
declare class UPay {
|
|
50
|
+
private apiKey;
|
|
51
|
+
private baseUrl;
|
|
52
|
+
constructor(options: UPayClientOptions);
|
|
53
|
+
createCheckout(options: CreateCheckoutOptions): Promise<UPayCheckoutSession>;
|
|
54
|
+
openCheckout(sessionId: string): void;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export { type CreateCheckoutOptions, UPay, UPayButton, type UPayButtonProps, type UPayCheckoutSession, type UPayClientOptions, type UPayPayment };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
UPay: () => UPay,
|
|
34
|
+
UPayButton: () => UPayButton
|
|
35
|
+
});
|
|
36
|
+
module.exports = __toCommonJS(index_exports);
|
|
37
|
+
|
|
38
|
+
// src/UPayButton.tsx
|
|
39
|
+
var import_react = __toESM(require("react"));
|
|
40
|
+
|
|
41
|
+
// src/client.ts
|
|
42
|
+
var DEFAULT_BASE_URL = typeof window !== "undefined" ? window.location.origin : "http://localhost:3000";
|
|
43
|
+
var UPay = class {
|
|
44
|
+
apiKey;
|
|
45
|
+
baseUrl;
|
|
46
|
+
constructor(options) {
|
|
47
|
+
this.apiKey = options.apiKey;
|
|
48
|
+
this.baseUrl = options.baseUrl ?? DEFAULT_BASE_URL;
|
|
49
|
+
}
|
|
50
|
+
async createCheckout(options) {
|
|
51
|
+
const res = await fetch(`${this.baseUrl}/api/v1/sessions`, {
|
|
52
|
+
method: "POST",
|
|
53
|
+
headers: {
|
|
54
|
+
"Content-Type": "application/json",
|
|
55
|
+
Authorization: `Bearer ${this.apiKey}`
|
|
56
|
+
},
|
|
57
|
+
body: JSON.stringify({
|
|
58
|
+
amount: options.amount,
|
|
59
|
+
token: options.token,
|
|
60
|
+
chain: options.chain,
|
|
61
|
+
metadata: options.metadata,
|
|
62
|
+
success_url: options.successUrl,
|
|
63
|
+
cancel_url: options.cancelUrl
|
|
64
|
+
})
|
|
65
|
+
});
|
|
66
|
+
if (!res.ok) {
|
|
67
|
+
const err = await res.json().catch(() => ({ error: "Unknown error" }));
|
|
68
|
+
throw new Error(err.error ?? `HTTP ${res.status}`);
|
|
69
|
+
}
|
|
70
|
+
return res.json();
|
|
71
|
+
}
|
|
72
|
+
openCheckout(sessionId) {
|
|
73
|
+
const url = `${this.baseUrl}/checkout/${sessionId}`;
|
|
74
|
+
if (typeof window === "undefined") return;
|
|
75
|
+
const existingModal = document.getElementById("upay-modal-container");
|
|
76
|
+
if (existingModal) existingModal.remove();
|
|
77
|
+
const container = document.createElement("div");
|
|
78
|
+
container.id = "upay-modal-container";
|
|
79
|
+
container.style.cssText = [
|
|
80
|
+
"position:fixed",
|
|
81
|
+
"inset:0",
|
|
82
|
+
"z-index:2147483647",
|
|
83
|
+
"display:flex",
|
|
84
|
+
"align-items:center",
|
|
85
|
+
"justify-content:center",
|
|
86
|
+
"background:rgba(0,0,0,0.6)",
|
|
87
|
+
"backdrop-filter:blur(4px)"
|
|
88
|
+
].join(";");
|
|
89
|
+
const spinner = document.createElement("div");
|
|
90
|
+
spinner.style.cssText = [
|
|
91
|
+
"width:min(480px,100vw)",
|
|
92
|
+
"height:min(700px,100vh)",
|
|
93
|
+
"display:flex",
|
|
94
|
+
"align-items:center",
|
|
95
|
+
"justify-content:center",
|
|
96
|
+
"border-radius:16px",
|
|
97
|
+
"background:#fff"
|
|
98
|
+
].join(";");
|
|
99
|
+
spinner.innerHTML = `
|
|
100
|
+
<style>@keyframes upay-modal-spin{to{transform:rotate(360deg)}}</style>
|
|
101
|
+
<svg width="36" height="36" viewBox="0 0 36 36" fill="none"
|
|
102
|
+
style="animation:upay-modal-spin 0.8s linear infinite">
|
|
103
|
+
<circle cx="18" cy="18" r="14" stroke="#e5e7eb" stroke-width="3"/>
|
|
104
|
+
<path d="M18 4a14 14 0 0 1 14 14" stroke="#6366f1"
|
|
105
|
+
stroke-width="3" stroke-linecap="round"/>
|
|
106
|
+
</svg>`;
|
|
107
|
+
const iframe = document.createElement("iframe");
|
|
108
|
+
iframe.src = url;
|
|
109
|
+
iframe.style.cssText = [
|
|
110
|
+
"display:none",
|
|
111
|
+
"width:min(480px,100vw)",
|
|
112
|
+
"height:0",
|
|
113
|
+
"max-height:90vh",
|
|
114
|
+
"border:none",
|
|
115
|
+
"border-radius:16px",
|
|
116
|
+
"box-shadow:0 25px 50px rgba(0,0,0,0.4)",
|
|
117
|
+
"transition:height 0.2s ease",
|
|
118
|
+
"overflow:hidden"
|
|
119
|
+
].join(";");
|
|
120
|
+
iframe.allow = "payment";
|
|
121
|
+
iframe.onload = () => {
|
|
122
|
+
spinner.remove();
|
|
123
|
+
iframe.style.display = "block";
|
|
124
|
+
};
|
|
125
|
+
const close = () => container.remove();
|
|
126
|
+
container.addEventListener("click", (e) => {
|
|
127
|
+
if (e.target === container) close();
|
|
128
|
+
});
|
|
129
|
+
window.addEventListener(
|
|
130
|
+
"message",
|
|
131
|
+
(e) => {
|
|
132
|
+
if (e.data?.type === "upay:close") close();
|
|
133
|
+
if (e.data?.type === "upay:height") {
|
|
134
|
+
const maxPx = window.innerHeight * 0.9;
|
|
135
|
+
iframe.style.height = `${Math.min(e.data.height, maxPx)}px`;
|
|
136
|
+
}
|
|
137
|
+
if (e.data?.type === "upay:success") {
|
|
138
|
+
close();
|
|
139
|
+
window.dispatchEvent(
|
|
140
|
+
new CustomEvent("upay:payment-success", { detail: e.data.payment })
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
},
|
|
144
|
+
{ once: false }
|
|
145
|
+
);
|
|
146
|
+
container.appendChild(spinner);
|
|
147
|
+
container.appendChild(iframe);
|
|
148
|
+
document.body.appendChild(container);
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
// src/UPayButton.tsx
|
|
153
|
+
var defaultStyles = {
|
|
154
|
+
button: {
|
|
155
|
+
display: "inline-flex",
|
|
156
|
+
alignItems: "center",
|
|
157
|
+
gap: "8px",
|
|
158
|
+
padding: "12px 24px",
|
|
159
|
+
background: "linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)",
|
|
160
|
+
color: "#fff",
|
|
161
|
+
border: "none",
|
|
162
|
+
borderRadius: "10px",
|
|
163
|
+
fontSize: "15px",
|
|
164
|
+
fontWeight: "600",
|
|
165
|
+
fontFamily: "inherit",
|
|
166
|
+
cursor: "pointer",
|
|
167
|
+
transition: "opacity 0.15s, transform 0.1s",
|
|
168
|
+
minHeight: "44px",
|
|
169
|
+
letterSpacing: "-0.01em"
|
|
170
|
+
},
|
|
171
|
+
loading: {
|
|
172
|
+
opacity: 0.7,
|
|
173
|
+
cursor: "not-allowed"
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
var UPayLogo = () => /* @__PURE__ */ import_react.default.createElement(
|
|
177
|
+
"svg",
|
|
178
|
+
{
|
|
179
|
+
width: "18",
|
|
180
|
+
height: "18",
|
|
181
|
+
viewBox: "0 0 18 18",
|
|
182
|
+
fill: "none",
|
|
183
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
184
|
+
"aria-hidden": "true"
|
|
185
|
+
},
|
|
186
|
+
/* @__PURE__ */ import_react.default.createElement("circle", { cx: "9", cy: "9", r: "8.5", stroke: "rgba(255,255,255,0.6)" }),
|
|
187
|
+
/* @__PURE__ */ import_react.default.createElement(
|
|
188
|
+
"path",
|
|
189
|
+
{
|
|
190
|
+
d: "M5.5 6.5L9 11.5L12.5 6.5",
|
|
191
|
+
stroke: "white",
|
|
192
|
+
strokeWidth: "1.8",
|
|
193
|
+
strokeLinecap: "round",
|
|
194
|
+
strokeLinejoin: "round"
|
|
195
|
+
}
|
|
196
|
+
)
|
|
197
|
+
);
|
|
198
|
+
var Spinner = () => /* @__PURE__ */ import_react.default.createElement(
|
|
199
|
+
"svg",
|
|
200
|
+
{
|
|
201
|
+
width: "16",
|
|
202
|
+
height: "16",
|
|
203
|
+
viewBox: "0 0 16 16",
|
|
204
|
+
fill: "none",
|
|
205
|
+
"aria-hidden": "true",
|
|
206
|
+
style: { animation: "upay-spin 0.8s linear infinite" }
|
|
207
|
+
},
|
|
208
|
+
/* @__PURE__ */ import_react.default.createElement(
|
|
209
|
+
"circle",
|
|
210
|
+
{
|
|
211
|
+
cx: "8",
|
|
212
|
+
cy: "8",
|
|
213
|
+
r: "6",
|
|
214
|
+
stroke: "rgba(255,255,255,0.35)",
|
|
215
|
+
strokeWidth: "2"
|
|
216
|
+
}
|
|
217
|
+
),
|
|
218
|
+
/* @__PURE__ */ import_react.default.createElement(
|
|
219
|
+
"path",
|
|
220
|
+
{
|
|
221
|
+
d: "M8 2a6 6 0 0 1 6 6",
|
|
222
|
+
stroke: "white",
|
|
223
|
+
strokeWidth: "2",
|
|
224
|
+
strokeLinecap: "round"
|
|
225
|
+
}
|
|
226
|
+
),
|
|
227
|
+
/* @__PURE__ */ import_react.default.createElement("style", null, `@keyframes upay-spin{to{transform:rotate(360deg)}}`)
|
|
228
|
+
);
|
|
229
|
+
function UPayButton({
|
|
230
|
+
apiKey,
|
|
231
|
+
baseUrl,
|
|
232
|
+
amount,
|
|
233
|
+
token,
|
|
234
|
+
chain,
|
|
235
|
+
metadata,
|
|
236
|
+
onSuccess,
|
|
237
|
+
onError,
|
|
238
|
+
label = "Pay with UPay",
|
|
239
|
+
className
|
|
240
|
+
}) {
|
|
241
|
+
const [loading, setLoading] = (0, import_react.useState)(false);
|
|
242
|
+
const handleClick = (0, import_react.useCallback)(async () => {
|
|
243
|
+
if (loading) return;
|
|
244
|
+
setLoading(true);
|
|
245
|
+
try {
|
|
246
|
+
const client = new UPay({ apiKey, baseUrl });
|
|
247
|
+
const session = await client.createCheckout({
|
|
248
|
+
amount,
|
|
249
|
+
token,
|
|
250
|
+
chain,
|
|
251
|
+
metadata
|
|
252
|
+
});
|
|
253
|
+
client.openCheckout(session.id);
|
|
254
|
+
const handleSuccess = (e) => {
|
|
255
|
+
const payment = e.detail;
|
|
256
|
+
onSuccess?.(payment);
|
|
257
|
+
window.removeEventListener("upay:payment-success", handleSuccess);
|
|
258
|
+
};
|
|
259
|
+
window.addEventListener("upay:payment-success", handleSuccess);
|
|
260
|
+
} catch (err) {
|
|
261
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
262
|
+
onError?.(error);
|
|
263
|
+
} finally {
|
|
264
|
+
setLoading(false);
|
|
265
|
+
}
|
|
266
|
+
}, [loading, apiKey, amount, token, chain, metadata, onSuccess, onError]);
|
|
267
|
+
return /* @__PURE__ */ import_react.default.createElement(
|
|
268
|
+
"button",
|
|
269
|
+
{
|
|
270
|
+
onClick: handleClick,
|
|
271
|
+
disabled: loading,
|
|
272
|
+
className,
|
|
273
|
+
style: className ? void 0 : {
|
|
274
|
+
...defaultStyles.button,
|
|
275
|
+
...loading ? defaultStyles.loading : {}
|
|
276
|
+
},
|
|
277
|
+
"aria-label": loading ? "Processing payment\u2026" : label,
|
|
278
|
+
"aria-busy": loading
|
|
279
|
+
},
|
|
280
|
+
loading ? /* @__PURE__ */ import_react.default.createElement(Spinner, null) : /* @__PURE__ */ import_react.default.createElement(UPayLogo, null),
|
|
281
|
+
/* @__PURE__ */ import_react.default.createElement("span", null, loading ? "Opening checkout\u2026" : label)
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
285
|
+
0 && (module.exports = {
|
|
286
|
+
UPay,
|
|
287
|
+
UPayButton
|
|
288
|
+
});
|
|
289
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/UPayButton.tsx","../src/client.ts"],"sourcesContent":["export { UPayButton } from \"./UPayButton\";\nexport { UPay } from \"./client\";\nexport type {\n UPayButtonProps,\n UPayPayment,\n UPayCheckoutSession,\n UPayClientOptions,\n CreateCheckoutOptions,\n} from \"./types\";\n","\"use client\";\n\nimport React, { useState, useCallback } from \"react\";\nimport { UPay } from \"./client\";\nimport type { UPayButtonProps, UPayPayment } from \"./types\";\n\nconst defaultStyles = {\n button: {\n display: \"inline-flex\",\n alignItems: \"center\",\n gap: \"8px\",\n padding: \"12px 24px\",\n background: \"linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)\",\n color: \"#fff\",\n border: \"none\",\n borderRadius: \"10px\",\n fontSize: \"15px\",\n fontWeight: \"600\",\n fontFamily: \"inherit\",\n cursor: \"pointer\",\n transition: \"opacity 0.15s, transform 0.1s\",\n minHeight: \"44px\",\n letterSpacing: \"-0.01em\",\n } as React.CSSProperties,\n loading: {\n opacity: 0.7,\n cursor: \"not-allowed\",\n } as React.CSSProperties,\n};\n\nconst UPayLogo = () => (\n <svg\n width=\"18\"\n height=\"18\"\n viewBox=\"0 0 18 18\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n aria-hidden=\"true\"\n >\n <circle cx=\"9\" cy=\"9\" r=\"8.5\" stroke=\"rgba(255,255,255,0.6)\" />\n <path\n d=\"M5.5 6.5L9 11.5L12.5 6.5\"\n stroke=\"white\"\n strokeWidth=\"1.8\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n);\n\nconst Spinner = () => (\n <svg\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n aria-hidden=\"true\"\n style={{ animation: \"upay-spin 0.8s linear infinite\" }}\n >\n <circle\n cx=\"8\"\n cy=\"8\"\n r=\"6\"\n stroke=\"rgba(255,255,255,0.35)\"\n strokeWidth=\"2\"\n />\n <path\n d=\"M8 2a6 6 0 0 1 6 6\"\n stroke=\"white\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n />\n <style>{`@keyframes upay-spin{to{transform:rotate(360deg)}}`}</style>\n </svg>\n);\n\nexport function UPayButton({\n apiKey,\n baseUrl,\n amount,\n token,\n chain,\n metadata,\n onSuccess,\n onError,\n label = \"Pay with UPay\",\n className,\n}: UPayButtonProps) {\n const [loading, setLoading] = useState(false);\n\n const handleClick = useCallback(async () => {\n if (loading) return;\n setLoading(true);\n\n try {\n const client = new UPay({ apiKey, baseUrl });\n const session = await client.createCheckout({\n amount,\n token,\n chain,\n metadata,\n });\n client.openCheckout(session.id);\n\n const handleSuccess = (e: Event) => {\n const payment = (e as CustomEvent<UPayPayment>).detail;\n onSuccess?.(payment);\n window.removeEventListener(\"upay:payment-success\", handleSuccess);\n };\n window.addEventListener(\"upay:payment-success\", handleSuccess);\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n onError?.(error);\n } finally {\n setLoading(false);\n }\n }, [loading, apiKey, amount, token, chain, metadata, onSuccess, onError]);\n\n return (\n <button\n onClick={handleClick}\n disabled={loading}\n className={className}\n style={\n className\n ? undefined\n : {\n ...defaultStyles.button,\n ...(loading ? defaultStyles.loading : {}),\n }\n }\n aria-label={loading ? \"Processing payment…\" : label}\n aria-busy={loading}\n >\n {loading ? <Spinner /> : <UPayLogo />}\n <span>{loading ? \"Opening checkout…\" : label}</span>\n </button>\n );\n}\n","import type {\n UPayClientOptions,\n CreateCheckoutOptions,\n UPayCheckoutSession,\n} from \"./types\";\n\nconst DEFAULT_BASE_URL =\n typeof window !== \"undefined\"\n ? window.location.origin\n : \"http://localhost:3000\";\n\nexport class UPay {\n private apiKey: string;\n private baseUrl: string;\n\n constructor(options: UPayClientOptions) {\n this.apiKey = options.apiKey;\n this.baseUrl = options.baseUrl ?? DEFAULT_BASE_URL;\n }\n\n async createCheckout(\n options: CreateCheckoutOptions\n ): Promise<UPayCheckoutSession> {\n const res = await fetch(`${this.baseUrl}/api/v1/sessions`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${this.apiKey}`,\n },\n body: JSON.stringify({\n amount: options.amount,\n token: options.token,\n chain: options.chain,\n metadata: options.metadata,\n success_url: options.successUrl,\n cancel_url: options.cancelUrl,\n }),\n });\n\n if (!res.ok) {\n const err = await res.json().catch(() => ({ error: \"Unknown error\" }));\n throw new Error(err.error ?? `HTTP ${res.status}`);\n }\n\n return res.json();\n }\n\n openCheckout(sessionId: string): void {\n const url = `${this.baseUrl}/checkout/${sessionId}`;\n\n if (typeof window === \"undefined\") return;\n\n const existingModal = document.getElementById(\"upay-modal-container\");\n if (existingModal) existingModal.remove();\n\n const container = document.createElement(\"div\");\n container.id = \"upay-modal-container\";\n container.style.cssText = [\n \"position:fixed\",\n \"inset:0\",\n \"z-index:2147483647\",\n \"display:flex\",\n \"align-items:center\",\n \"justify-content:center\",\n \"background:rgba(0,0,0,0.6)\",\n \"backdrop-filter:blur(4px)\",\n ].join(\";\");\n\n // Spinner shown while iframe loads\n const spinner = document.createElement(\"div\");\n spinner.style.cssText = [\n \"width:min(480px,100vw)\",\n \"height:min(700px,100vh)\",\n \"display:flex\",\n \"align-items:center\",\n \"justify-content:center\",\n \"border-radius:16px\",\n \"background:#fff\",\n ].join(\";\");\n spinner.innerHTML = `\n <style>@keyframes upay-modal-spin{to{transform:rotate(360deg)}}</style>\n <svg width=\"36\" height=\"36\" viewBox=\"0 0 36 36\" fill=\"none\"\n style=\"animation:upay-modal-spin 0.8s linear infinite\">\n <circle cx=\"18\" cy=\"18\" r=\"14\" stroke=\"#e5e7eb\" stroke-width=\"3\"/>\n <path d=\"M18 4a14 14 0 0 1 14 14\" stroke=\"#6366f1\"\n stroke-width=\"3\" stroke-linecap=\"round\"/>\n </svg>`;\n\n const iframe = document.createElement(\"iframe\");\n iframe.src = url;\n iframe.style.cssText = [\n \"display:none\",\n \"width:min(480px,100vw)\",\n \"height:0\",\n \"max-height:90vh\",\n \"border:none\",\n \"border-radius:16px\",\n \"box-shadow:0 25px 50px rgba(0,0,0,0.4)\",\n \"transition:height 0.2s ease\",\n \"overflow:hidden\",\n ].join(\";\");\n iframe.allow = \"payment\";\n iframe.onload = () => {\n spinner.remove();\n iframe.style.display = \"block\";\n };\n\n const close = (): void => container.remove();\n\n container.addEventListener(\"click\", (e) => {\n if (e.target === container) close();\n });\n\n window.addEventListener(\n \"message\",\n (e) => {\n if (e.data?.type === \"upay:close\") close();\n if (e.data?.type === \"upay:height\") {\n const maxPx = window.innerHeight * 0.9;\n iframe.style.height = `${Math.min(e.data.height, maxPx)}px`;\n }\n if (e.data?.type === \"upay:success\") {\n close();\n window.dispatchEvent(\n new CustomEvent(\"upay:payment-success\", { detail: e.data.payment })\n );\n }\n },\n { once: false }\n );\n\n container.appendChild(spinner);\n container.appendChild(iframe);\n document.body.appendChild(container);\n }\n}\n"],"mappings":"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","names":["React"]}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
// src/UPayButton.tsx
|
|
2
|
+
import React, { useState, useCallback } from "react";
|
|
3
|
+
|
|
4
|
+
// src/client.ts
|
|
5
|
+
var DEFAULT_BASE_URL = typeof window !== "undefined" ? window.location.origin : "http://localhost:3000";
|
|
6
|
+
var UPay = class {
|
|
7
|
+
apiKey;
|
|
8
|
+
baseUrl;
|
|
9
|
+
constructor(options) {
|
|
10
|
+
this.apiKey = options.apiKey;
|
|
11
|
+
this.baseUrl = options.baseUrl ?? DEFAULT_BASE_URL;
|
|
12
|
+
}
|
|
13
|
+
async createCheckout(options) {
|
|
14
|
+
const res = await fetch(`${this.baseUrl}/api/v1/sessions`, {
|
|
15
|
+
method: "POST",
|
|
16
|
+
headers: {
|
|
17
|
+
"Content-Type": "application/json",
|
|
18
|
+
Authorization: `Bearer ${this.apiKey}`
|
|
19
|
+
},
|
|
20
|
+
body: JSON.stringify({
|
|
21
|
+
amount: options.amount,
|
|
22
|
+
token: options.token,
|
|
23
|
+
chain: options.chain,
|
|
24
|
+
metadata: options.metadata,
|
|
25
|
+
success_url: options.successUrl,
|
|
26
|
+
cancel_url: options.cancelUrl
|
|
27
|
+
})
|
|
28
|
+
});
|
|
29
|
+
if (!res.ok) {
|
|
30
|
+
const err = await res.json().catch(() => ({ error: "Unknown error" }));
|
|
31
|
+
throw new Error(err.error ?? `HTTP ${res.status}`);
|
|
32
|
+
}
|
|
33
|
+
return res.json();
|
|
34
|
+
}
|
|
35
|
+
openCheckout(sessionId) {
|
|
36
|
+
const url = `${this.baseUrl}/checkout/${sessionId}`;
|
|
37
|
+
if (typeof window === "undefined") return;
|
|
38
|
+
const existingModal = document.getElementById("upay-modal-container");
|
|
39
|
+
if (existingModal) existingModal.remove();
|
|
40
|
+
const container = document.createElement("div");
|
|
41
|
+
container.id = "upay-modal-container";
|
|
42
|
+
container.style.cssText = [
|
|
43
|
+
"position:fixed",
|
|
44
|
+
"inset:0",
|
|
45
|
+
"z-index:2147483647",
|
|
46
|
+
"display:flex",
|
|
47
|
+
"align-items:center",
|
|
48
|
+
"justify-content:center",
|
|
49
|
+
"background:rgba(0,0,0,0.6)",
|
|
50
|
+
"backdrop-filter:blur(4px)"
|
|
51
|
+
].join(";");
|
|
52
|
+
const spinner = document.createElement("div");
|
|
53
|
+
spinner.style.cssText = [
|
|
54
|
+
"width:min(480px,100vw)",
|
|
55
|
+
"height:min(700px,100vh)",
|
|
56
|
+
"display:flex",
|
|
57
|
+
"align-items:center",
|
|
58
|
+
"justify-content:center",
|
|
59
|
+
"border-radius:16px",
|
|
60
|
+
"background:#fff"
|
|
61
|
+
].join(";");
|
|
62
|
+
spinner.innerHTML = `
|
|
63
|
+
<style>@keyframes upay-modal-spin{to{transform:rotate(360deg)}}</style>
|
|
64
|
+
<svg width="36" height="36" viewBox="0 0 36 36" fill="none"
|
|
65
|
+
style="animation:upay-modal-spin 0.8s linear infinite">
|
|
66
|
+
<circle cx="18" cy="18" r="14" stroke="#e5e7eb" stroke-width="3"/>
|
|
67
|
+
<path d="M18 4a14 14 0 0 1 14 14" stroke="#6366f1"
|
|
68
|
+
stroke-width="3" stroke-linecap="round"/>
|
|
69
|
+
</svg>`;
|
|
70
|
+
const iframe = document.createElement("iframe");
|
|
71
|
+
iframe.src = url;
|
|
72
|
+
iframe.style.cssText = [
|
|
73
|
+
"display:none",
|
|
74
|
+
"width:min(480px,100vw)",
|
|
75
|
+
"height:0",
|
|
76
|
+
"max-height:90vh",
|
|
77
|
+
"border:none",
|
|
78
|
+
"border-radius:16px",
|
|
79
|
+
"box-shadow:0 25px 50px rgba(0,0,0,0.4)",
|
|
80
|
+
"transition:height 0.2s ease",
|
|
81
|
+
"overflow:hidden"
|
|
82
|
+
].join(";");
|
|
83
|
+
iframe.allow = "payment";
|
|
84
|
+
iframe.onload = () => {
|
|
85
|
+
spinner.remove();
|
|
86
|
+
iframe.style.display = "block";
|
|
87
|
+
};
|
|
88
|
+
const close = () => container.remove();
|
|
89
|
+
container.addEventListener("click", (e) => {
|
|
90
|
+
if (e.target === container) close();
|
|
91
|
+
});
|
|
92
|
+
window.addEventListener(
|
|
93
|
+
"message",
|
|
94
|
+
(e) => {
|
|
95
|
+
if (e.data?.type === "upay:close") close();
|
|
96
|
+
if (e.data?.type === "upay:height") {
|
|
97
|
+
const maxPx = window.innerHeight * 0.9;
|
|
98
|
+
iframe.style.height = `${Math.min(e.data.height, maxPx)}px`;
|
|
99
|
+
}
|
|
100
|
+
if (e.data?.type === "upay:success") {
|
|
101
|
+
close();
|
|
102
|
+
window.dispatchEvent(
|
|
103
|
+
new CustomEvent("upay:payment-success", { detail: e.data.payment })
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
{ once: false }
|
|
108
|
+
);
|
|
109
|
+
container.appendChild(spinner);
|
|
110
|
+
container.appendChild(iframe);
|
|
111
|
+
document.body.appendChild(container);
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
// src/UPayButton.tsx
|
|
116
|
+
var defaultStyles = {
|
|
117
|
+
button: {
|
|
118
|
+
display: "inline-flex",
|
|
119
|
+
alignItems: "center",
|
|
120
|
+
gap: "8px",
|
|
121
|
+
padding: "12px 24px",
|
|
122
|
+
background: "linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)",
|
|
123
|
+
color: "#fff",
|
|
124
|
+
border: "none",
|
|
125
|
+
borderRadius: "10px",
|
|
126
|
+
fontSize: "15px",
|
|
127
|
+
fontWeight: "600",
|
|
128
|
+
fontFamily: "inherit",
|
|
129
|
+
cursor: "pointer",
|
|
130
|
+
transition: "opacity 0.15s, transform 0.1s",
|
|
131
|
+
minHeight: "44px",
|
|
132
|
+
letterSpacing: "-0.01em"
|
|
133
|
+
},
|
|
134
|
+
loading: {
|
|
135
|
+
opacity: 0.7,
|
|
136
|
+
cursor: "not-allowed"
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
var UPayLogo = () => /* @__PURE__ */ React.createElement(
|
|
140
|
+
"svg",
|
|
141
|
+
{
|
|
142
|
+
width: "18",
|
|
143
|
+
height: "18",
|
|
144
|
+
viewBox: "0 0 18 18",
|
|
145
|
+
fill: "none",
|
|
146
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
147
|
+
"aria-hidden": "true"
|
|
148
|
+
},
|
|
149
|
+
/* @__PURE__ */ React.createElement("circle", { cx: "9", cy: "9", r: "8.5", stroke: "rgba(255,255,255,0.6)" }),
|
|
150
|
+
/* @__PURE__ */ React.createElement(
|
|
151
|
+
"path",
|
|
152
|
+
{
|
|
153
|
+
d: "M5.5 6.5L9 11.5L12.5 6.5",
|
|
154
|
+
stroke: "white",
|
|
155
|
+
strokeWidth: "1.8",
|
|
156
|
+
strokeLinecap: "round",
|
|
157
|
+
strokeLinejoin: "round"
|
|
158
|
+
}
|
|
159
|
+
)
|
|
160
|
+
);
|
|
161
|
+
var Spinner = () => /* @__PURE__ */ React.createElement(
|
|
162
|
+
"svg",
|
|
163
|
+
{
|
|
164
|
+
width: "16",
|
|
165
|
+
height: "16",
|
|
166
|
+
viewBox: "0 0 16 16",
|
|
167
|
+
fill: "none",
|
|
168
|
+
"aria-hidden": "true",
|
|
169
|
+
style: { animation: "upay-spin 0.8s linear infinite" }
|
|
170
|
+
},
|
|
171
|
+
/* @__PURE__ */ React.createElement(
|
|
172
|
+
"circle",
|
|
173
|
+
{
|
|
174
|
+
cx: "8",
|
|
175
|
+
cy: "8",
|
|
176
|
+
r: "6",
|
|
177
|
+
stroke: "rgba(255,255,255,0.35)",
|
|
178
|
+
strokeWidth: "2"
|
|
179
|
+
}
|
|
180
|
+
),
|
|
181
|
+
/* @__PURE__ */ React.createElement(
|
|
182
|
+
"path",
|
|
183
|
+
{
|
|
184
|
+
d: "M8 2a6 6 0 0 1 6 6",
|
|
185
|
+
stroke: "white",
|
|
186
|
+
strokeWidth: "2",
|
|
187
|
+
strokeLinecap: "round"
|
|
188
|
+
}
|
|
189
|
+
),
|
|
190
|
+
/* @__PURE__ */ React.createElement("style", null, `@keyframes upay-spin{to{transform:rotate(360deg)}}`)
|
|
191
|
+
);
|
|
192
|
+
function UPayButton({
|
|
193
|
+
apiKey,
|
|
194
|
+
baseUrl,
|
|
195
|
+
amount,
|
|
196
|
+
token,
|
|
197
|
+
chain,
|
|
198
|
+
metadata,
|
|
199
|
+
onSuccess,
|
|
200
|
+
onError,
|
|
201
|
+
label = "Pay with UPay",
|
|
202
|
+
className
|
|
203
|
+
}) {
|
|
204
|
+
const [loading, setLoading] = useState(false);
|
|
205
|
+
const handleClick = useCallback(async () => {
|
|
206
|
+
if (loading) return;
|
|
207
|
+
setLoading(true);
|
|
208
|
+
try {
|
|
209
|
+
const client = new UPay({ apiKey, baseUrl });
|
|
210
|
+
const session = await client.createCheckout({
|
|
211
|
+
amount,
|
|
212
|
+
token,
|
|
213
|
+
chain,
|
|
214
|
+
metadata
|
|
215
|
+
});
|
|
216
|
+
client.openCheckout(session.id);
|
|
217
|
+
const handleSuccess = (e) => {
|
|
218
|
+
const payment = e.detail;
|
|
219
|
+
onSuccess?.(payment);
|
|
220
|
+
window.removeEventListener("upay:payment-success", handleSuccess);
|
|
221
|
+
};
|
|
222
|
+
window.addEventListener("upay:payment-success", handleSuccess);
|
|
223
|
+
} catch (err) {
|
|
224
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
225
|
+
onError?.(error);
|
|
226
|
+
} finally {
|
|
227
|
+
setLoading(false);
|
|
228
|
+
}
|
|
229
|
+
}, [loading, apiKey, amount, token, chain, metadata, onSuccess, onError]);
|
|
230
|
+
return /* @__PURE__ */ React.createElement(
|
|
231
|
+
"button",
|
|
232
|
+
{
|
|
233
|
+
onClick: handleClick,
|
|
234
|
+
disabled: loading,
|
|
235
|
+
className,
|
|
236
|
+
style: className ? void 0 : {
|
|
237
|
+
...defaultStyles.button,
|
|
238
|
+
...loading ? defaultStyles.loading : {}
|
|
239
|
+
},
|
|
240
|
+
"aria-label": loading ? "Processing payment\u2026" : label,
|
|
241
|
+
"aria-busy": loading
|
|
242
|
+
},
|
|
243
|
+
loading ? /* @__PURE__ */ React.createElement(Spinner, null) : /* @__PURE__ */ React.createElement(UPayLogo, null),
|
|
244
|
+
/* @__PURE__ */ React.createElement("span", null, loading ? "Opening checkout\u2026" : label)
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
export {
|
|
248
|
+
UPay,
|
|
249
|
+
UPayButton
|
|
250
|
+
};
|
|
251
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/UPayButton.tsx","../src/client.ts"],"sourcesContent":["\"use client\";\n\nimport React, { useState, useCallback } from \"react\";\nimport { UPay } from \"./client\";\nimport type { UPayButtonProps, UPayPayment } from \"./types\";\n\nconst defaultStyles = {\n button: {\n display: \"inline-flex\",\n alignItems: \"center\",\n gap: \"8px\",\n padding: \"12px 24px\",\n background: \"linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)\",\n color: \"#fff\",\n border: \"none\",\n borderRadius: \"10px\",\n fontSize: \"15px\",\n fontWeight: \"600\",\n fontFamily: \"inherit\",\n cursor: \"pointer\",\n transition: \"opacity 0.15s, transform 0.1s\",\n minHeight: \"44px\",\n letterSpacing: \"-0.01em\",\n } as React.CSSProperties,\n loading: {\n opacity: 0.7,\n cursor: \"not-allowed\",\n } as React.CSSProperties,\n};\n\nconst UPayLogo = () => (\n <svg\n width=\"18\"\n height=\"18\"\n viewBox=\"0 0 18 18\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n aria-hidden=\"true\"\n >\n <circle cx=\"9\" cy=\"9\" r=\"8.5\" stroke=\"rgba(255,255,255,0.6)\" />\n <path\n d=\"M5.5 6.5L9 11.5L12.5 6.5\"\n stroke=\"white\"\n strokeWidth=\"1.8\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n);\n\nconst Spinner = () => (\n <svg\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n aria-hidden=\"true\"\n style={{ animation: \"upay-spin 0.8s linear infinite\" }}\n >\n <circle\n cx=\"8\"\n cy=\"8\"\n r=\"6\"\n stroke=\"rgba(255,255,255,0.35)\"\n strokeWidth=\"2\"\n />\n <path\n d=\"M8 2a6 6 0 0 1 6 6\"\n stroke=\"white\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n />\n <style>{`@keyframes upay-spin{to{transform:rotate(360deg)}}`}</style>\n </svg>\n);\n\nexport function UPayButton({\n apiKey,\n baseUrl,\n amount,\n token,\n chain,\n metadata,\n onSuccess,\n onError,\n label = \"Pay with UPay\",\n className,\n}: UPayButtonProps) {\n const [loading, setLoading] = useState(false);\n\n const handleClick = useCallback(async () => {\n if (loading) return;\n setLoading(true);\n\n try {\n const client = new UPay({ apiKey, baseUrl });\n const session = await client.createCheckout({\n amount,\n token,\n chain,\n metadata,\n });\n client.openCheckout(session.id);\n\n const handleSuccess = (e: Event) => {\n const payment = (e as CustomEvent<UPayPayment>).detail;\n onSuccess?.(payment);\n window.removeEventListener(\"upay:payment-success\", handleSuccess);\n };\n window.addEventListener(\"upay:payment-success\", handleSuccess);\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n onError?.(error);\n } finally {\n setLoading(false);\n }\n }, [loading, apiKey, amount, token, chain, metadata, onSuccess, onError]);\n\n return (\n <button\n onClick={handleClick}\n disabled={loading}\n className={className}\n style={\n className\n ? undefined\n : {\n ...defaultStyles.button,\n ...(loading ? defaultStyles.loading : {}),\n }\n }\n aria-label={loading ? \"Processing payment…\" : label}\n aria-busy={loading}\n >\n {loading ? <Spinner /> : <UPayLogo />}\n <span>{loading ? \"Opening checkout…\" : label}</span>\n </button>\n );\n}\n","import type {\n UPayClientOptions,\n CreateCheckoutOptions,\n UPayCheckoutSession,\n} from \"./types\";\n\nconst DEFAULT_BASE_URL =\n typeof window !== \"undefined\"\n ? window.location.origin\n : \"http://localhost:3000\";\n\nexport class UPay {\n private apiKey: string;\n private baseUrl: string;\n\n constructor(options: UPayClientOptions) {\n this.apiKey = options.apiKey;\n this.baseUrl = options.baseUrl ?? DEFAULT_BASE_URL;\n }\n\n async createCheckout(\n options: CreateCheckoutOptions\n ): Promise<UPayCheckoutSession> {\n const res = await fetch(`${this.baseUrl}/api/v1/sessions`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${this.apiKey}`,\n },\n body: JSON.stringify({\n amount: options.amount,\n token: options.token,\n chain: options.chain,\n metadata: options.metadata,\n success_url: options.successUrl,\n cancel_url: options.cancelUrl,\n }),\n });\n\n if (!res.ok) {\n const err = await res.json().catch(() => ({ error: \"Unknown error\" }));\n throw new Error(err.error ?? `HTTP ${res.status}`);\n }\n\n return res.json();\n }\n\n openCheckout(sessionId: string): void {\n const url = `${this.baseUrl}/checkout/${sessionId}`;\n\n if (typeof window === \"undefined\") return;\n\n const existingModal = document.getElementById(\"upay-modal-container\");\n if (existingModal) existingModal.remove();\n\n const container = document.createElement(\"div\");\n container.id = \"upay-modal-container\";\n container.style.cssText = [\n \"position:fixed\",\n \"inset:0\",\n \"z-index:2147483647\",\n \"display:flex\",\n \"align-items:center\",\n \"justify-content:center\",\n \"background:rgba(0,0,0,0.6)\",\n \"backdrop-filter:blur(4px)\",\n ].join(\";\");\n\n // Spinner shown while iframe loads\n const spinner = document.createElement(\"div\");\n spinner.style.cssText = [\n \"width:min(480px,100vw)\",\n \"height:min(700px,100vh)\",\n \"display:flex\",\n \"align-items:center\",\n \"justify-content:center\",\n \"border-radius:16px\",\n \"background:#fff\",\n ].join(\";\");\n spinner.innerHTML = `\n <style>@keyframes upay-modal-spin{to{transform:rotate(360deg)}}</style>\n <svg width=\"36\" height=\"36\" viewBox=\"0 0 36 36\" fill=\"none\"\n style=\"animation:upay-modal-spin 0.8s linear infinite\">\n <circle cx=\"18\" cy=\"18\" r=\"14\" stroke=\"#e5e7eb\" stroke-width=\"3\"/>\n <path d=\"M18 4a14 14 0 0 1 14 14\" stroke=\"#6366f1\"\n stroke-width=\"3\" stroke-linecap=\"round\"/>\n </svg>`;\n\n const iframe = document.createElement(\"iframe\");\n iframe.src = url;\n iframe.style.cssText = [\n \"display:none\",\n \"width:min(480px,100vw)\",\n \"height:0\",\n \"max-height:90vh\",\n \"border:none\",\n \"border-radius:16px\",\n \"box-shadow:0 25px 50px rgba(0,0,0,0.4)\",\n \"transition:height 0.2s ease\",\n \"overflow:hidden\",\n ].join(\";\");\n iframe.allow = \"payment\";\n iframe.onload = () => {\n spinner.remove();\n iframe.style.display = \"block\";\n };\n\n const close = (): void => container.remove();\n\n container.addEventListener(\"click\", (e) => {\n if (e.target === container) close();\n });\n\n window.addEventListener(\n \"message\",\n (e) => {\n if (e.data?.type === \"upay:close\") close();\n if (e.data?.type === \"upay:height\") {\n const maxPx = window.innerHeight * 0.9;\n iframe.style.height = `${Math.min(e.data.height, maxPx)}px`;\n }\n if (e.data?.type === \"upay:success\") {\n close();\n window.dispatchEvent(\n new CustomEvent(\"upay:payment-success\", { detail: e.data.payment })\n );\n }\n },\n { once: false }\n );\n\n container.appendChild(spinner);\n container.appendChild(iframe);\n document.body.appendChild(container);\n }\n}\n"],"mappings":"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","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@alphadevs_labs/upay-sdk",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "UPay SDK — UPayButton React component and imperative client",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"publishConfig": {
|
|
7
|
+
"access": "public"
|
|
8
|
+
},
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "https://github.com/0xyshv/upay"
|
|
12
|
+
},
|
|
13
|
+
"main": "./dist/index.js",
|
|
14
|
+
"module": "./dist/index.mjs",
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"types": "./dist/index.d.ts",
|
|
19
|
+
"import": "./dist/index.mjs",
|
|
20
|
+
"require": "./dist/index.js"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"files": ["dist"],
|
|
24
|
+
"scripts": {
|
|
25
|
+
"build": "tsup",
|
|
26
|
+
"dev": "tsup --watch",
|
|
27
|
+
"type-check": "tsc --noEmit"
|
|
28
|
+
},
|
|
29
|
+
"peerDependencies": {
|
|
30
|
+
"react": ">=18",
|
|
31
|
+
"react-dom": ">=18"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@types/react": "^18.2.0",
|
|
35
|
+
"@types/react-dom": "^18.2.0",
|
|
36
|
+
"react": "^18.2.0",
|
|
37
|
+
"react-dom": "^18.2.0",
|
|
38
|
+
"tsup": "^8.0.0",
|
|
39
|
+
"typescript": "^5.3.3"
|
|
40
|
+
}
|
|
41
|
+
}
|