@micro-cms/crypto-payments 1.0.4 → 1.0.6

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as react from 'react';
2
2
  import react__default from 'react';
3
- import { PaymentIntent, PaymentVerification } from '@micro-cms/types';
3
+ import { PaymentIntent, PaymentVerification, PaymentProvider } from '@micro-cms/types';
4
4
 
5
5
  interface SolanaWindow extends Window {
6
6
  solana?: {
@@ -25,6 +25,23 @@ declare const useSolanaWallet: () => {
25
25
  sendPayment: (intent: PaymentIntent) => Promise<string>;
26
26
  };
27
27
 
28
+ interface EthereumWindow extends Window {
29
+ ethereum?: {
30
+ isMetaMask?: boolean;
31
+ request: (request: {
32
+ method: string;
33
+ params?: any[];
34
+ }) => Promise<any>;
35
+ on: (event: string, callback: (...args: any[]) => void) => void;
36
+ removeListener: (event: string, callback: (...args: any[]) => void) => void;
37
+ };
38
+ }
39
+ declare const useEVMWallet: () => {
40
+ isAvailable: boolean;
41
+ connect: () => Promise<any>;
42
+ sendPayment: (intent: PaymentIntent) => Promise<string>;
43
+ };
44
+
28
45
  declare const PaymentWidget: react__default.FC<PaymentWidgetProps>;
29
46
 
30
47
  interface PaymentWidgetProps {
@@ -33,6 +50,7 @@ interface PaymentWidgetProps {
33
50
  currency?: string;
34
51
  onSuccess?: (verification: PaymentVerification) => void;
35
52
  onError?: (error: Error) => void;
53
+ provider?: PaymentProvider;
36
54
  endpoints?: {
37
55
  initiate?: string;
38
56
  verify?: string;
@@ -49,6 +67,8 @@ declare const usePayment: (props: PaymentWidgetProps) => {
49
67
  setStatus: react.Dispatch<react.SetStateAction<PaymentStatus>>;
50
68
  isSolanaAvailable: boolean;
51
69
  handleSolanaPay: () => Promise<void>;
70
+ isEVMAvailable: boolean;
71
+ handleEVMPay: () => Promise<void>;
52
72
  };
53
73
 
54
- export { type PaymentStatus, PaymentWidget, type PaymentWidgetProps, type SolanaWindow, usePayment, useSolanaWallet };
74
+ export { type EthereumWindow, type PaymentStatus, PaymentWidget, type PaymentWidgetProps, type SolanaWindow, useEVMWallet, usePayment, useSolanaWallet };
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as react from 'react';
2
2
  import react__default from 'react';
3
- import { PaymentIntent, PaymentVerification } from '@micro-cms/types';
3
+ import { PaymentIntent, PaymentVerification, PaymentProvider } from '@micro-cms/types';
4
4
 
5
5
  interface SolanaWindow extends Window {
6
6
  solana?: {
@@ -25,6 +25,23 @@ declare const useSolanaWallet: () => {
25
25
  sendPayment: (intent: PaymentIntent) => Promise<string>;
26
26
  };
27
27
 
28
+ interface EthereumWindow extends Window {
29
+ ethereum?: {
30
+ isMetaMask?: boolean;
31
+ request: (request: {
32
+ method: string;
33
+ params?: any[];
34
+ }) => Promise<any>;
35
+ on: (event: string, callback: (...args: any[]) => void) => void;
36
+ removeListener: (event: string, callback: (...args: any[]) => void) => void;
37
+ };
38
+ }
39
+ declare const useEVMWallet: () => {
40
+ isAvailable: boolean;
41
+ connect: () => Promise<any>;
42
+ sendPayment: (intent: PaymentIntent) => Promise<string>;
43
+ };
44
+
28
45
  declare const PaymentWidget: react__default.FC<PaymentWidgetProps>;
29
46
 
30
47
  interface PaymentWidgetProps {
@@ -33,6 +50,7 @@ interface PaymentWidgetProps {
33
50
  currency?: string;
34
51
  onSuccess?: (verification: PaymentVerification) => void;
35
52
  onError?: (error: Error) => void;
53
+ provider?: PaymentProvider;
36
54
  endpoints?: {
37
55
  initiate?: string;
38
56
  verify?: string;
@@ -49,6 +67,8 @@ declare const usePayment: (props: PaymentWidgetProps) => {
49
67
  setStatus: react.Dispatch<react.SetStateAction<PaymentStatus>>;
50
68
  isSolanaAvailable: boolean;
51
69
  handleSolanaPay: () => Promise<void>;
70
+ isEVMAvailable: boolean;
71
+ handleEVMPay: () => Promise<void>;
52
72
  };
53
73
 
54
- export { type PaymentStatus, PaymentWidget, type PaymentWidgetProps, type SolanaWindow, usePayment, useSolanaWallet };
74
+ export { type EthereumWindow, type PaymentStatus, PaymentWidget, type PaymentWidgetProps, type SolanaWindow, useEVMWallet, usePayment, useSolanaWallet };
package/dist/index.js CHANGED
@@ -21,6 +21,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
23
  PaymentWidget: () => PaymentWidget,
24
+ useEVMWallet: () => useEVMWallet,
24
25
  usePayment: () => usePayment,
25
26
  useSolanaWallet: () => useSolanaWallet
26
27
  });
@@ -60,6 +61,36 @@ var useSolanaWallet = () => {
60
61
  return { isAvailable: !!getProvider(), connect, sendPayment };
61
62
  };
62
63
 
64
+ // src/providers/EVMProvider.ts
65
+ var useEVMWallet = () => {
66
+ const getProvider = () => {
67
+ if (typeof window !== "undefined" && "ethereum" in window) {
68
+ return window.ethereum;
69
+ }
70
+ return void 0;
71
+ };
72
+ const connect = async () => {
73
+ const provider = getProvider();
74
+ if (provider) {
75
+ try {
76
+ const accounts = await provider.request({ method: "eth_requestAccounts" });
77
+ return accounts[0];
78
+ } catch (err) {
79
+ throw new Error("User rejected the connection");
80
+ }
81
+ } else {
82
+ throw new Error("MetaMask or EVM wallet not found");
83
+ }
84
+ };
85
+ const sendPayment = async (intent) => {
86
+ const provider = getProvider();
87
+ if (!provider) throw new Error("Wallet not connected");
88
+ console.log(`Simulating EVM payment to ${intent.paymentAddress} for ${intent.amount} ${intent.currency}`);
89
+ return "simulated_evm_tx_hash_" + Math.random().toString(36).slice(2);
90
+ };
91
+ return { isAvailable: !!getProvider(), connect, sendPayment };
92
+ };
93
+
63
94
  // src/PaymentWidget.tsx
64
95
  var import_lucide_react = require("lucide-react");
65
96
  var import_clsx = require("clsx");
@@ -72,7 +103,18 @@ var PaymentWidget = ({
72
103
  className,
73
104
  ...props
74
105
  }) => {
75
- const { status, intent, error, initiate, verify, setStatus, isSolanaAvailable, handleSolanaPay } = usePayment(props);
106
+ const {
107
+ status,
108
+ intent,
109
+ error,
110
+ initiate,
111
+ verify,
112
+ setStatus,
113
+ isSolanaAvailable,
114
+ handleSolanaPay,
115
+ isEVMAvailable,
116
+ handleEVMPay
117
+ } = usePayment(props);
76
118
  if (status === "success") {
77
119
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: cn("p-6 text-center bg-green-50 rounded-xl border border-green-200", className), children: [
78
120
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.CheckCircle, { className: "mx-auto w-12 h-12 text-green-500 mb-4" }),
@@ -134,6 +176,16 @@ var PaymentWidget = ({
134
176
  "Pay with Phantom"
135
177
  ]
136
178
  }
179
+ ) : (intent.network.toLowerCase().includes("ethereum") || intent.network.toLowerCase().includes("evm") || intent.network.toLowerCase().includes("polygon")) && isEVMAvailable ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
180
+ "button",
181
+ {
182
+ onClick: handleEVMPay,
183
+ className: "w-full py-3 px-4 bg-[#f6851b] hover:bg-[#e2761b] text-white font-medium rounded-xl transition-colors flex items-center justify-center gap-2",
184
+ children: [
185
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("img", { src: "https://upload.wikimedia.org/wikipedia/commons/3/36/MetaMask_Mirror_Logo.svg", className: "w-4 h-4", alt: "MetaMask" }),
186
+ "Pay with MetaMask"
187
+ ]
188
+ }
137
189
  ) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
138
190
  "button",
139
191
  {
@@ -166,6 +218,15 @@ var usePayment = (props) => {
166
218
  try {
167
219
  setStatus("initiating");
168
220
  setError(null);
221
+ if (props.provider) {
222
+ const data2 = await props.provider.initiatePayment(props.orderId, {
223
+ amount: props.amount,
224
+ currency: props.currency
225
+ });
226
+ setIntent(data2);
227
+ setStatus("pending_signature");
228
+ return;
229
+ }
169
230
  const response = await fetch(props.endpoints?.initiate || "/api/orders/initiate", {
170
231
  method: "POST",
171
232
  headers: { "Content-Type": "application/json" },
@@ -187,10 +248,20 @@ var usePayment = (props) => {
187
248
  setStatus("error");
188
249
  props.onError?.(err);
189
250
  }
190
- }, [props.endpoints?.initiate, props.orderId, props.amount, props.currency, props.onError]);
251
+ }, [props.provider, props.endpoints?.initiate, props.orderId, props.amount, props.currency, props.onError]);
191
252
  const verify = (0, import_react.useCallback)(async (txHash) => {
192
253
  try {
193
254
  setStatus("verifying");
255
+ if (props.provider) {
256
+ const data2 = await props.provider.verifyPayment(txHash, props.orderId);
257
+ if (data2.status === "confirmed") {
258
+ setStatus("success");
259
+ props.onSuccess?.(data2);
260
+ } else {
261
+ throw new Error("Payment not confirmed yet, status: " + data2.status);
262
+ }
263
+ return;
264
+ }
194
265
  const response = await fetch(props.endpoints?.verify || "/api/orders/verify-payment", {
195
266
  method: "POST",
196
267
  headers: { "Content-Type": "application/json" },
@@ -215,7 +286,7 @@ var usePayment = (props) => {
215
286
  setStatus("error");
216
287
  props.onError?.(err);
217
288
  }
218
- }, [props.endpoints?.verify, props.orderId, props.onSuccess, props.onError]);
289
+ }, [props.provider, props.endpoints?.verify, props.orderId, props.onSuccess, props.onError]);
219
290
  const { isAvailable: isSolanaAvailable, connect: connectSolana, sendPayment: sendSolanaPayment } = useSolanaWallet();
220
291
  const handleSolanaPay = async () => {
221
292
  try {
@@ -237,11 +308,44 @@ var usePayment = (props) => {
237
308
  props.onError?.(err instanceof Error ? err : new Error(String(err)));
238
309
  }
239
310
  };
240
- return { status, intent, error, initiate, verify, setStatus, isSolanaAvailable, handleSolanaPay };
311
+ const { isAvailable: isEVMAvailable, connect: connectEVM, sendPayment: sendEVMPayment } = useEVMWallet();
312
+ const handleEVMPay = async () => {
313
+ try {
314
+ if (!intent) {
315
+ setError("Payment intent not established.");
316
+ setStatus("error");
317
+ return;
318
+ }
319
+ setStatus("connecting");
320
+ const account = await connectEVM();
321
+ console.log("Connected to EVM with account:", account);
322
+ setStatus("pending_signature");
323
+ const txHash = await sendEVMPayment(intent);
324
+ setStatus("verifying");
325
+ await verify(txHash);
326
+ } catch (err) {
327
+ setError(err.message || "Failed to connect to MetaMask or sign transaction.");
328
+ setStatus("error");
329
+ props.onError?.(err instanceof Error ? err : new Error(String(err)));
330
+ }
331
+ };
332
+ return {
333
+ status,
334
+ intent,
335
+ error,
336
+ initiate,
337
+ verify,
338
+ setStatus,
339
+ isSolanaAvailable,
340
+ handleSolanaPay,
341
+ isEVMAvailable,
342
+ handleEVMPay
343
+ };
241
344
  };
242
345
  // Annotate the CommonJS export names for ESM import in node:
243
346
  0 && (module.exports = {
244
347
  PaymentWidget,
348
+ useEVMWallet,
245
349
  usePayment,
246
350
  useSolanaWallet
247
351
  });
package/dist/index.mjs CHANGED
@@ -34,6 +34,36 @@ var useSolanaWallet = () => {
34
34
  return { isAvailable: !!getProvider(), connect, sendPayment };
35
35
  };
36
36
 
37
+ // src/providers/EVMProvider.ts
38
+ var useEVMWallet = () => {
39
+ const getProvider = () => {
40
+ if (typeof window !== "undefined" && "ethereum" in window) {
41
+ return window.ethereum;
42
+ }
43
+ return void 0;
44
+ };
45
+ const connect = async () => {
46
+ const provider = getProvider();
47
+ if (provider) {
48
+ try {
49
+ const accounts = await provider.request({ method: "eth_requestAccounts" });
50
+ return accounts[0];
51
+ } catch (err) {
52
+ throw new Error("User rejected the connection");
53
+ }
54
+ } else {
55
+ throw new Error("MetaMask or EVM wallet not found");
56
+ }
57
+ };
58
+ const sendPayment = async (intent) => {
59
+ const provider = getProvider();
60
+ if (!provider) throw new Error("Wallet not connected");
61
+ console.log(`Simulating EVM payment to ${intent.paymentAddress} for ${intent.amount} ${intent.currency}`);
62
+ return "simulated_evm_tx_hash_" + Math.random().toString(36).slice(2);
63
+ };
64
+ return { isAvailable: !!getProvider(), connect, sendPayment };
65
+ };
66
+
37
67
  // src/PaymentWidget.tsx
38
68
  import { Wallet, CheckCircle, Loader2, AlertCircle, ExternalLink, Cpu } from "lucide-react";
39
69
  import { clsx } from "clsx";
@@ -46,7 +76,18 @@ var PaymentWidget = ({
46
76
  className,
47
77
  ...props
48
78
  }) => {
49
- const { status, intent, error, initiate, verify, setStatus, isSolanaAvailable, handleSolanaPay } = usePayment(props);
79
+ const {
80
+ status,
81
+ intent,
82
+ error,
83
+ initiate,
84
+ verify,
85
+ setStatus,
86
+ isSolanaAvailable,
87
+ handleSolanaPay,
88
+ isEVMAvailable,
89
+ handleEVMPay
90
+ } = usePayment(props);
50
91
  if (status === "success") {
51
92
  return /* @__PURE__ */ jsxs("div", { className: cn("p-6 text-center bg-green-50 rounded-xl border border-green-200", className), children: [
52
93
  /* @__PURE__ */ jsx(CheckCircle, { className: "mx-auto w-12 h-12 text-green-500 mb-4" }),
@@ -108,6 +149,16 @@ var PaymentWidget = ({
108
149
  "Pay with Phantom"
109
150
  ]
110
151
  }
152
+ ) : (intent.network.toLowerCase().includes("ethereum") || intent.network.toLowerCase().includes("evm") || intent.network.toLowerCase().includes("polygon")) && isEVMAvailable ? /* @__PURE__ */ jsxs(
153
+ "button",
154
+ {
155
+ onClick: handleEVMPay,
156
+ className: "w-full py-3 px-4 bg-[#f6851b] hover:bg-[#e2761b] text-white font-medium rounded-xl transition-colors flex items-center justify-center gap-2",
157
+ children: [
158
+ /* @__PURE__ */ jsx("img", { src: "https://upload.wikimedia.org/wikipedia/commons/3/36/MetaMask_Mirror_Logo.svg", className: "w-4 h-4", alt: "MetaMask" }),
159
+ "Pay with MetaMask"
160
+ ]
161
+ }
111
162
  ) : /* @__PURE__ */ jsx(
112
163
  "button",
113
164
  {
@@ -140,6 +191,15 @@ var usePayment = (props) => {
140
191
  try {
141
192
  setStatus("initiating");
142
193
  setError(null);
194
+ if (props.provider) {
195
+ const data2 = await props.provider.initiatePayment(props.orderId, {
196
+ amount: props.amount,
197
+ currency: props.currency
198
+ });
199
+ setIntent(data2);
200
+ setStatus("pending_signature");
201
+ return;
202
+ }
143
203
  const response = await fetch(props.endpoints?.initiate || "/api/orders/initiate", {
144
204
  method: "POST",
145
205
  headers: { "Content-Type": "application/json" },
@@ -161,10 +221,20 @@ var usePayment = (props) => {
161
221
  setStatus("error");
162
222
  props.onError?.(err);
163
223
  }
164
- }, [props.endpoints?.initiate, props.orderId, props.amount, props.currency, props.onError]);
224
+ }, [props.provider, props.endpoints?.initiate, props.orderId, props.amount, props.currency, props.onError]);
165
225
  const verify = useCallback(async (txHash) => {
166
226
  try {
167
227
  setStatus("verifying");
228
+ if (props.provider) {
229
+ const data2 = await props.provider.verifyPayment(txHash, props.orderId);
230
+ if (data2.status === "confirmed") {
231
+ setStatus("success");
232
+ props.onSuccess?.(data2);
233
+ } else {
234
+ throw new Error("Payment not confirmed yet, status: " + data2.status);
235
+ }
236
+ return;
237
+ }
168
238
  const response = await fetch(props.endpoints?.verify || "/api/orders/verify-payment", {
169
239
  method: "POST",
170
240
  headers: { "Content-Type": "application/json" },
@@ -189,7 +259,7 @@ var usePayment = (props) => {
189
259
  setStatus("error");
190
260
  props.onError?.(err);
191
261
  }
192
- }, [props.endpoints?.verify, props.orderId, props.onSuccess, props.onError]);
262
+ }, [props.provider, props.endpoints?.verify, props.orderId, props.onSuccess, props.onError]);
193
263
  const { isAvailable: isSolanaAvailable, connect: connectSolana, sendPayment: sendSolanaPayment } = useSolanaWallet();
194
264
  const handleSolanaPay = async () => {
195
265
  try {
@@ -211,10 +281,43 @@ var usePayment = (props) => {
211
281
  props.onError?.(err instanceof Error ? err : new Error(String(err)));
212
282
  }
213
283
  };
214
- return { status, intent, error, initiate, verify, setStatus, isSolanaAvailable, handleSolanaPay };
284
+ const { isAvailable: isEVMAvailable, connect: connectEVM, sendPayment: sendEVMPayment } = useEVMWallet();
285
+ const handleEVMPay = async () => {
286
+ try {
287
+ if (!intent) {
288
+ setError("Payment intent not established.");
289
+ setStatus("error");
290
+ return;
291
+ }
292
+ setStatus("connecting");
293
+ const account = await connectEVM();
294
+ console.log("Connected to EVM with account:", account);
295
+ setStatus("pending_signature");
296
+ const txHash = await sendEVMPayment(intent);
297
+ setStatus("verifying");
298
+ await verify(txHash);
299
+ } catch (err) {
300
+ setError(err.message || "Failed to connect to MetaMask or sign transaction.");
301
+ setStatus("error");
302
+ props.onError?.(err instanceof Error ? err : new Error(String(err)));
303
+ }
304
+ };
305
+ return {
306
+ status,
307
+ intent,
308
+ error,
309
+ initiate,
310
+ verify,
311
+ setStatus,
312
+ isSolanaAvailable,
313
+ handleSolanaPay,
314
+ isEVMAvailable,
315
+ handleEVMPay
316
+ };
215
317
  };
216
318
  export {
217
319
  PaymentWidget,
320
+ useEVMWallet,
218
321
  usePayment,
219
322
  useSolanaWallet
220
323
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@micro-cms/crypto-payments",
3
- "version": "1.0.4",
3
+ "version": "1.0.6",
4
4
  "description": "Composable Crypto Payment Widget for Micro-CMS",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",