banksi 0.1.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 ADDED
@@ -0,0 +1,84 @@
1
+ # banksi
2
+
3
+ Crypto payment module for vibe coders. Add USDT/USDC payments to any app in one prompt.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install banksi
9
+ ```
10
+
11
+ ## Environment Variables
12
+
13
+ ```env
14
+ BANKSI_API_KEY=bks_your_key_here # from banksi.io/merchant/settings
15
+ BANKSI_URL=https://banksi.io # optional, defaults to https://banksi.io
16
+ ```
17
+
18
+ ## Next.js — Paywall Middleware
19
+
20
+ ```typescript
21
+ // app/api/premium/route.ts
22
+ import { createBanksiPaywall } from 'banksi/next';
23
+
24
+ const paywall = createBanksiPaywall({ amount: 0.10 });
25
+
26
+ export async function GET(request: Request) {
27
+ const blocked = await paywall(request);
28
+ if (blocked) return blocked; // 402 + payment instructions
29
+ return Response.json({ data: 'premium content' });
30
+ }
31
+ ```
32
+
33
+ ## Express — Paywall Middleware
34
+
35
+ ```typescript
36
+ import { createBanksiPaywall } from 'banksi/express';
37
+
38
+ app.use('/api/premium', createBanksiPaywall({ amount: 0.10 }));
39
+ ```
40
+
41
+ ## React — Pay Button
42
+
43
+ ```tsx
44
+ import { BanksiPayButton } from 'banksi/react';
45
+
46
+ <BanksiPayButton
47
+ amount={4.50}
48
+ onPaymentConfirmed={(id) => console.log('Paid!', id)}
49
+ />
50
+ ```
51
+
52
+ ## Client API
53
+
54
+ ```typescript
55
+ import { BanksiClient } from 'banksi';
56
+
57
+ const client = new BanksiClient();
58
+
59
+ const chains = await client.listChains();
60
+ const payment = await client.createPayment({ chainId: 'arbitrum', tokenId: '...', amount: 4.50 });
61
+ const status = await client.verifyPayment(payment.paymentId);
62
+ const confirmed = await client.isPaymentConfirmed(payment.paymentId);
63
+ ```
64
+
65
+ ## MCP Server
66
+
67
+ AI agents can integrate Banksi using the MCP server:
68
+
69
+ ```json
70
+ {
71
+ "mcpServers": {
72
+ "banksi": {
73
+ "command": "npx",
74
+ "args": ["banksi-mcp"]
75
+ }
76
+ }
77
+ }
78
+ ```
79
+
80
+ ## Links
81
+
82
+ - [Docs](https://banksi.io/docs)
83
+ - [Dashboard](https://banksi.io/merchant)
84
+ - [Demo](https://banksi.io/examples/cafe)
@@ -0,0 +1,62 @@
1
+ // src/client.ts
2
+ function getConfig(override) {
3
+ return {
4
+ baseUrl: override?.baseUrl || process.env.BANKSI_URL || "https://banksi.io",
5
+ merchantSlug: override?.merchantSlug || process.env.BANKSI_MERCHANT_SLUG || void 0,
6
+ apiKey: override?.apiKey || process.env.BANKSI_API_KEY || void 0
7
+ };
8
+ }
9
+ var BanksiClient = class {
10
+ constructor(config) {
11
+ const c = getConfig(config);
12
+ this.baseUrl = c.baseUrl.replace(/\/$/, "");
13
+ this.merchantSlug = c.merchantSlug;
14
+ this.apiKey = c.apiKey;
15
+ }
16
+ headers() {
17
+ const h = { "Content-Type": "application/json" };
18
+ if (this.apiKey) h["Authorization"] = `Bearer ${this.apiKey}`;
19
+ return h;
20
+ }
21
+ async listChains() {
22
+ const res = await fetch(`${this.baseUrl}/api/chains`);
23
+ if (!res.ok) throw new Error(`Banksi API error: ${res.status}`);
24
+ const data = await res.json();
25
+ return data.chains;
26
+ }
27
+ async createPayment(opts) {
28
+ const body = {
29
+ chainId: opts.chainId,
30
+ tokenId: opts.tokenId,
31
+ amount: opts.amount
32
+ };
33
+ if (!this.apiKey) {
34
+ body.merchantSlug = opts.merchantSlug || this.merchantSlug;
35
+ }
36
+ const res = await fetch(`${this.baseUrl}/api/x402/pay`, {
37
+ method: "POST",
38
+ headers: this.headers(),
39
+ body: JSON.stringify(body)
40
+ });
41
+ if (!res.ok) {
42
+ const data = await res.json().catch(() => ({}));
43
+ throw new Error(data.error || `Banksi API error: ${res.status}`);
44
+ }
45
+ return res.json();
46
+ }
47
+ async verifyPayment(paymentId) {
48
+ const res = await fetch(`${this.baseUrl}/api/payments/${paymentId}/status`, {
49
+ headers: this.headers()
50
+ });
51
+ if (!res.ok) throw new Error(`Banksi API error: ${res.status}`);
52
+ return res.json();
53
+ }
54
+ async isPaymentConfirmed(paymentId) {
55
+ const status = await this.verifyPayment(paymentId);
56
+ return ["CONFIRMED", "SWEPT"].includes(status.status);
57
+ }
58
+ };
59
+
60
+ export {
61
+ BanksiClient
62
+ };
@@ -0,0 +1,49 @@
1
+ import {
2
+ BanksiClient
3
+ } from "./chunk-7MKH6B3K.mjs";
4
+
5
+ // src/next.ts
6
+ function createBanksiPaywall(config) {
7
+ const client = new BanksiClient(config);
8
+ return async function paywall(request) {
9
+ const paymentId = request.headers.get("X-Payment");
10
+ if (paymentId) {
11
+ try {
12
+ const confirmed = await client.isPaymentConfirmed(paymentId);
13
+ if (confirmed) return null;
14
+ } catch {
15
+ }
16
+ }
17
+ const chains = await client.listChains();
18
+ const banksiUrl = config.baseUrl || process.env.BANKSI_URL || "http://localhost:3001";
19
+ const merchantSlug = config.merchantSlug || process.env.BANKSI_MERCHANT_SLUG || "";
20
+ return Response.json(
21
+ {
22
+ status: 402,
23
+ paymentRequired: {
24
+ scheme: "x402",
25
+ description: config.description || "Payment required.",
26
+ merchant: merchantSlug,
27
+ amount: config.amount,
28
+ currency: "USD",
29
+ chains: chains.map((c) => ({
30
+ id: c.id,
31
+ name: c.name,
32
+ tokens: c.tokens.map((t) => ({ id: t.id, symbol: t.symbol }))
33
+ })),
34
+ payUrl: `${banksiUrl}/api/x402/pay`,
35
+ howToPay: [
36
+ `POST ${banksiUrl}/api/x402/pay with { "merchantSlug": "${merchantSlug}", "chainId": "<chain>", "tokenId": "<token>", "amount": ${config.amount} }`,
37
+ "Send the stablecoin to the returned address.",
38
+ "Retry with header: X-Payment: <paymentId>"
39
+ ]
40
+ }
41
+ },
42
+ { status: 402 }
43
+ );
44
+ };
45
+ }
46
+
47
+ export {
48
+ createBanksiPaywall
49
+ };
@@ -0,0 +1,50 @@
1
+ interface BanksiConfig {
2
+ /** Banksi instance URL */
3
+ baseUrl: string;
4
+ /** Merchant store slug (optional if apiKey is set) */
5
+ merchantSlug?: string;
6
+ /** API key from banksi.io dashboard (bks_xxx) */
7
+ apiKey?: string;
8
+ }
9
+ interface PaymentResult {
10
+ paymentId: string;
11
+ address: string;
12
+ amountExpected: string;
13
+ tokenSymbol: string;
14
+ chainName: string;
15
+ expiresAt: string;
16
+ }
17
+ interface PaymentStatus {
18
+ id: string;
19
+ status: 'PENDING' | 'CONFIRMING' | 'CONFIRMED' | 'SWEPT' | 'EXPIRED' | 'FAILED';
20
+ txHash: string | null;
21
+ amountReceived: string | null;
22
+ paidAt: string | null;
23
+ }
24
+ interface Chain {
25
+ id: string;
26
+ name: string;
27
+ tokens: {
28
+ id: string;
29
+ symbol: string;
30
+ name: string;
31
+ }[];
32
+ }
33
+ declare class BanksiClient {
34
+ private baseUrl;
35
+ private merchantSlug?;
36
+ private apiKey?;
37
+ constructor(config?: Partial<BanksiConfig>);
38
+ private headers;
39
+ listChains(): Promise<Chain[]>;
40
+ createPayment(opts: {
41
+ chainId: string;
42
+ tokenId: string;
43
+ amount: number;
44
+ merchantSlug?: string;
45
+ }): Promise<PaymentResult>;
46
+ verifyPayment(paymentId: string): Promise<PaymentStatus>;
47
+ isPaymentConfirmed(paymentId: string): Promise<boolean>;
48
+ }
49
+
50
+ export { type BanksiConfig as B, type Chain as C, type PaymentResult as P, BanksiClient as a, type PaymentStatus as b };
@@ -0,0 +1,50 @@
1
+ interface BanksiConfig {
2
+ /** Banksi instance URL */
3
+ baseUrl: string;
4
+ /** Merchant store slug (optional if apiKey is set) */
5
+ merchantSlug?: string;
6
+ /** API key from banksi.io dashboard (bks_xxx) */
7
+ apiKey?: string;
8
+ }
9
+ interface PaymentResult {
10
+ paymentId: string;
11
+ address: string;
12
+ amountExpected: string;
13
+ tokenSymbol: string;
14
+ chainName: string;
15
+ expiresAt: string;
16
+ }
17
+ interface PaymentStatus {
18
+ id: string;
19
+ status: 'PENDING' | 'CONFIRMING' | 'CONFIRMED' | 'SWEPT' | 'EXPIRED' | 'FAILED';
20
+ txHash: string | null;
21
+ amountReceived: string | null;
22
+ paidAt: string | null;
23
+ }
24
+ interface Chain {
25
+ id: string;
26
+ name: string;
27
+ tokens: {
28
+ id: string;
29
+ symbol: string;
30
+ name: string;
31
+ }[];
32
+ }
33
+ declare class BanksiClient {
34
+ private baseUrl;
35
+ private merchantSlug?;
36
+ private apiKey?;
37
+ constructor(config?: Partial<BanksiConfig>);
38
+ private headers;
39
+ listChains(): Promise<Chain[]>;
40
+ createPayment(opts: {
41
+ chainId: string;
42
+ tokenId: string;
43
+ amount: number;
44
+ merchantSlug?: string;
45
+ }): Promise<PaymentResult>;
46
+ verifyPayment(paymentId: string): Promise<PaymentStatus>;
47
+ isPaymentConfirmed(paymentId: string): Promise<boolean>;
48
+ }
49
+
50
+ export { type BanksiConfig as B, type Chain as C, type PaymentResult as P, BanksiClient as a, type PaymentStatus as b };
@@ -0,0 +1,27 @@
1
+ import { B as BanksiConfig } from './client-jLNKFIPq.mjs';
2
+ export { a as BanksiClient } from './client-jLNKFIPq.mjs';
3
+
4
+ type Request = {
5
+ headers: Record<string, string | string[] | undefined>;
6
+ };
7
+ type Response = {
8
+ status(code: number): Response;
9
+ json(body: unknown): void;
10
+ };
11
+ type NextFunction = () => void;
12
+ interface PaywallConfig extends Partial<BanksiConfig> {
13
+ amount: number;
14
+ description?: string;
15
+ }
16
+ /**
17
+ * Express paywall middleware.
18
+ *
19
+ * Usage:
20
+ * ```ts
21
+ * import { createBanksiPaywall } from 'banksi/express';
22
+ * app.use('/api/premium', createBanksiPaywall({ amount: 0.10 }));
23
+ * ```
24
+ */
25
+ declare function createBanksiPaywall(config: PaywallConfig): (req: Request, res: Response, next: NextFunction) => Promise<void>;
26
+
27
+ export { createBanksiPaywall };
@@ -0,0 +1,27 @@
1
+ import { B as BanksiConfig } from './client-jLNKFIPq.js';
2
+ export { a as BanksiClient } from './client-jLNKFIPq.js';
3
+
4
+ type Request = {
5
+ headers: Record<string, string | string[] | undefined>;
6
+ };
7
+ type Response = {
8
+ status(code: number): Response;
9
+ json(body: unknown): void;
10
+ };
11
+ type NextFunction = () => void;
12
+ interface PaywallConfig extends Partial<BanksiConfig> {
13
+ amount: number;
14
+ description?: string;
15
+ }
16
+ /**
17
+ * Express paywall middleware.
18
+ *
19
+ * Usage:
20
+ * ```ts
21
+ * import { createBanksiPaywall } from 'banksi/express';
22
+ * app.use('/api/premium', createBanksiPaywall({ amount: 0.10 }));
23
+ * ```
24
+ */
25
+ declare function createBanksiPaywall(config: PaywallConfig): (req: Request, res: Response, next: NextFunction) => Promise<void>;
26
+
27
+ export { createBanksiPaywall };
@@ -0,0 +1,129 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/express.ts
21
+ var express_exports = {};
22
+ __export(express_exports, {
23
+ BanksiClient: () => BanksiClient,
24
+ createBanksiPaywall: () => createBanksiPaywall
25
+ });
26
+ module.exports = __toCommonJS(express_exports);
27
+
28
+ // src/client.ts
29
+ function getConfig(override) {
30
+ return {
31
+ baseUrl: override?.baseUrl || process.env.BANKSI_URL || "https://banksi.io",
32
+ merchantSlug: override?.merchantSlug || process.env.BANKSI_MERCHANT_SLUG || void 0,
33
+ apiKey: override?.apiKey || process.env.BANKSI_API_KEY || void 0
34
+ };
35
+ }
36
+ var BanksiClient = class {
37
+ constructor(config) {
38
+ const c = getConfig(config);
39
+ this.baseUrl = c.baseUrl.replace(/\/$/, "");
40
+ this.merchantSlug = c.merchantSlug;
41
+ this.apiKey = c.apiKey;
42
+ }
43
+ headers() {
44
+ const h = { "Content-Type": "application/json" };
45
+ if (this.apiKey) h["Authorization"] = `Bearer ${this.apiKey}`;
46
+ return h;
47
+ }
48
+ async listChains() {
49
+ const res = await fetch(`${this.baseUrl}/api/chains`);
50
+ if (!res.ok) throw new Error(`Banksi API error: ${res.status}`);
51
+ const data = await res.json();
52
+ return data.chains;
53
+ }
54
+ async createPayment(opts) {
55
+ const body = {
56
+ chainId: opts.chainId,
57
+ tokenId: opts.tokenId,
58
+ amount: opts.amount
59
+ };
60
+ if (!this.apiKey) {
61
+ body.merchantSlug = opts.merchantSlug || this.merchantSlug;
62
+ }
63
+ const res = await fetch(`${this.baseUrl}/api/x402/pay`, {
64
+ method: "POST",
65
+ headers: this.headers(),
66
+ body: JSON.stringify(body)
67
+ });
68
+ if (!res.ok) {
69
+ const data = await res.json().catch(() => ({}));
70
+ throw new Error(data.error || `Banksi API error: ${res.status}`);
71
+ }
72
+ return res.json();
73
+ }
74
+ async verifyPayment(paymentId) {
75
+ const res = await fetch(`${this.baseUrl}/api/payments/${paymentId}/status`, {
76
+ headers: this.headers()
77
+ });
78
+ if (!res.ok) throw new Error(`Banksi API error: ${res.status}`);
79
+ return res.json();
80
+ }
81
+ async isPaymentConfirmed(paymentId) {
82
+ const status = await this.verifyPayment(paymentId);
83
+ return ["CONFIRMED", "SWEPT"].includes(status.status);
84
+ }
85
+ };
86
+
87
+ // src/express.ts
88
+ function createBanksiPaywall(config) {
89
+ const client = new BanksiClient(config);
90
+ return async function paywall(req, res, next) {
91
+ const paymentId = req.headers["x-payment"];
92
+ if (paymentId) {
93
+ try {
94
+ const confirmed = await client.isPaymentConfirmed(paymentId);
95
+ if (confirmed) return next();
96
+ } catch {
97
+ }
98
+ }
99
+ const chains = await client.listChains();
100
+ const banksiUrl = config.baseUrl || process.env.BANKSI_URL || "http://localhost:3001";
101
+ const merchantSlug = config.merchantSlug || process.env.BANKSI_MERCHANT_SLUG || "";
102
+ res.status(402).json({
103
+ status: 402,
104
+ paymentRequired: {
105
+ scheme: "x402",
106
+ description: config.description || "Payment required.",
107
+ merchant: merchantSlug,
108
+ amount: config.amount,
109
+ currency: "USD",
110
+ chains: chains.map((c) => ({
111
+ id: c.id,
112
+ name: c.name,
113
+ tokens: c.tokens.map((t) => ({ id: t.id, symbol: t.symbol }))
114
+ })),
115
+ payUrl: `${banksiUrl}/api/x402/pay`,
116
+ howToPay: [
117
+ `POST ${banksiUrl}/api/x402/pay with { "merchantSlug": "${merchantSlug}", "chainId": "<chain>", "tokenId": "<token>", "amount": ${config.amount} }`,
118
+ "Send the stablecoin to the returned address.",
119
+ "Retry with header: X-Payment: <paymentId>"
120
+ ]
121
+ }
122
+ });
123
+ };
124
+ }
125
+ // Annotate the CommonJS export names for ESM import in node:
126
+ 0 && (module.exports = {
127
+ BanksiClient,
128
+ createBanksiPaywall
129
+ });
@@ -0,0 +1,46 @@
1
+ import {
2
+ BanksiClient
3
+ } from "./chunk-7MKH6B3K.mjs";
4
+
5
+ // src/express.ts
6
+ function createBanksiPaywall(config) {
7
+ const client = new BanksiClient(config);
8
+ return async function paywall(req, res, next) {
9
+ const paymentId = req.headers["x-payment"];
10
+ if (paymentId) {
11
+ try {
12
+ const confirmed = await client.isPaymentConfirmed(paymentId);
13
+ if (confirmed) return next();
14
+ } catch {
15
+ }
16
+ }
17
+ const chains = await client.listChains();
18
+ const banksiUrl = config.baseUrl || process.env.BANKSI_URL || "http://localhost:3001";
19
+ const merchantSlug = config.merchantSlug || process.env.BANKSI_MERCHANT_SLUG || "";
20
+ res.status(402).json({
21
+ status: 402,
22
+ paymentRequired: {
23
+ scheme: "x402",
24
+ description: config.description || "Payment required.",
25
+ merchant: merchantSlug,
26
+ amount: config.amount,
27
+ currency: "USD",
28
+ chains: chains.map((c) => ({
29
+ id: c.id,
30
+ name: c.name,
31
+ tokens: c.tokens.map((t) => ({ id: t.id, symbol: t.symbol }))
32
+ })),
33
+ payUrl: `${banksiUrl}/api/x402/pay`,
34
+ howToPay: [
35
+ `POST ${banksiUrl}/api/x402/pay with { "merchantSlug": "${merchantSlug}", "chainId": "<chain>", "tokenId": "<token>", "amount": ${config.amount} }`,
36
+ "Send the stablecoin to the returned address.",
37
+ "Retry with header: X-Payment: <paymentId>"
38
+ ]
39
+ }
40
+ });
41
+ };
42
+ }
43
+ export {
44
+ BanksiClient,
45
+ createBanksiPaywall
46
+ };
@@ -0,0 +1,2 @@
1
+ export { a as BanksiClient, B as BanksiConfig, C as Chain, P as PaymentResult, b as PaymentStatus } from './client-jLNKFIPq.mjs';
2
+ export { createBanksiPaywall } from './next.mjs';
@@ -0,0 +1,2 @@
1
+ export { a as BanksiClient, B as BanksiConfig, C as Chain, P as PaymentResult, b as PaymentStatus } from './client-jLNKFIPq.js';
2
+ export { createBanksiPaywall } from './next.js';
package/dist/index.js ADDED
@@ -0,0 +1,132 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var src_exports = {};
22
+ __export(src_exports, {
23
+ BanksiClient: () => BanksiClient,
24
+ createBanksiPaywall: () => createBanksiPaywall
25
+ });
26
+ module.exports = __toCommonJS(src_exports);
27
+
28
+ // src/client.ts
29
+ function getConfig(override) {
30
+ return {
31
+ baseUrl: override?.baseUrl || process.env.BANKSI_URL || "https://banksi.io",
32
+ merchantSlug: override?.merchantSlug || process.env.BANKSI_MERCHANT_SLUG || void 0,
33
+ apiKey: override?.apiKey || process.env.BANKSI_API_KEY || void 0
34
+ };
35
+ }
36
+ var BanksiClient = class {
37
+ constructor(config) {
38
+ const c = getConfig(config);
39
+ this.baseUrl = c.baseUrl.replace(/\/$/, "");
40
+ this.merchantSlug = c.merchantSlug;
41
+ this.apiKey = c.apiKey;
42
+ }
43
+ headers() {
44
+ const h = { "Content-Type": "application/json" };
45
+ if (this.apiKey) h["Authorization"] = `Bearer ${this.apiKey}`;
46
+ return h;
47
+ }
48
+ async listChains() {
49
+ const res = await fetch(`${this.baseUrl}/api/chains`);
50
+ if (!res.ok) throw new Error(`Banksi API error: ${res.status}`);
51
+ const data = await res.json();
52
+ return data.chains;
53
+ }
54
+ async createPayment(opts) {
55
+ const body = {
56
+ chainId: opts.chainId,
57
+ tokenId: opts.tokenId,
58
+ amount: opts.amount
59
+ };
60
+ if (!this.apiKey) {
61
+ body.merchantSlug = opts.merchantSlug || this.merchantSlug;
62
+ }
63
+ const res = await fetch(`${this.baseUrl}/api/x402/pay`, {
64
+ method: "POST",
65
+ headers: this.headers(),
66
+ body: JSON.stringify(body)
67
+ });
68
+ if (!res.ok) {
69
+ const data = await res.json().catch(() => ({}));
70
+ throw new Error(data.error || `Banksi API error: ${res.status}`);
71
+ }
72
+ return res.json();
73
+ }
74
+ async verifyPayment(paymentId) {
75
+ const res = await fetch(`${this.baseUrl}/api/payments/${paymentId}/status`, {
76
+ headers: this.headers()
77
+ });
78
+ if (!res.ok) throw new Error(`Banksi API error: ${res.status}`);
79
+ return res.json();
80
+ }
81
+ async isPaymentConfirmed(paymentId) {
82
+ const status = await this.verifyPayment(paymentId);
83
+ return ["CONFIRMED", "SWEPT"].includes(status.status);
84
+ }
85
+ };
86
+
87
+ // src/next.ts
88
+ function createBanksiPaywall(config) {
89
+ const client = new BanksiClient(config);
90
+ return async function paywall(request) {
91
+ const paymentId = request.headers.get("X-Payment");
92
+ if (paymentId) {
93
+ try {
94
+ const confirmed = await client.isPaymentConfirmed(paymentId);
95
+ if (confirmed) return null;
96
+ } catch {
97
+ }
98
+ }
99
+ const chains = await client.listChains();
100
+ const banksiUrl = config.baseUrl || process.env.BANKSI_URL || "http://localhost:3001";
101
+ const merchantSlug = config.merchantSlug || process.env.BANKSI_MERCHANT_SLUG || "";
102
+ return Response.json(
103
+ {
104
+ status: 402,
105
+ paymentRequired: {
106
+ scheme: "x402",
107
+ description: config.description || "Payment required.",
108
+ merchant: merchantSlug,
109
+ amount: config.amount,
110
+ currency: "USD",
111
+ chains: chains.map((c) => ({
112
+ id: c.id,
113
+ name: c.name,
114
+ tokens: c.tokens.map((t) => ({ id: t.id, symbol: t.symbol }))
115
+ })),
116
+ payUrl: `${banksiUrl}/api/x402/pay`,
117
+ howToPay: [
118
+ `POST ${banksiUrl}/api/x402/pay with { "merchantSlug": "${merchantSlug}", "chainId": "<chain>", "tokenId": "<token>", "amount": ${config.amount} }`,
119
+ "Send the stablecoin to the returned address.",
120
+ "Retry with header: X-Payment: <paymentId>"
121
+ ]
122
+ }
123
+ },
124
+ { status: 402 }
125
+ );
126
+ };
127
+ }
128
+ // Annotate the CommonJS export names for ESM import in node:
129
+ 0 && (module.exports = {
130
+ BanksiClient,
131
+ createBanksiPaywall
132
+ });
package/dist/index.mjs ADDED
@@ -0,0 +1,10 @@
1
+ import {
2
+ createBanksiPaywall
3
+ } from "./chunk-QHCD73JW.mjs";
4
+ import {
5
+ BanksiClient
6
+ } from "./chunk-7MKH6B3K.mjs";
7
+ export {
8
+ BanksiClient,
9
+ createBanksiPaywall
10
+ };
@@ -0,0 +1,25 @@
1
+ import { B as BanksiConfig } from './client-jLNKFIPq.mjs';
2
+ export { a as BanksiClient } from './client-jLNKFIPq.mjs';
3
+
4
+ interface PaywallConfig extends Partial<BanksiConfig> {
5
+ amount: number;
6
+ description?: string;
7
+ }
8
+ /**
9
+ * Next.js App Router paywall middleware.
10
+ *
11
+ * Usage in a route handler:
12
+ * ```ts
13
+ * import { createBanksiPaywall } from 'banksi/next';
14
+ * const paywall = createBanksiPaywall({ amount: 0.10, description: 'Premium API' });
15
+ *
16
+ * export async function GET(request: NextRequest) {
17
+ * const blocked = await paywall(request);
18
+ * if (blocked) return blocked;
19
+ * return NextResponse.json({ data: 'premium content' });
20
+ * }
21
+ * ```
22
+ */
23
+ declare function createBanksiPaywall(config: PaywallConfig): (request: Request) => Promise<Response | null>;
24
+
25
+ export { createBanksiPaywall };
package/dist/next.d.ts ADDED
@@ -0,0 +1,25 @@
1
+ import { B as BanksiConfig } from './client-jLNKFIPq.js';
2
+ export { a as BanksiClient } from './client-jLNKFIPq.js';
3
+
4
+ interface PaywallConfig extends Partial<BanksiConfig> {
5
+ amount: number;
6
+ description?: string;
7
+ }
8
+ /**
9
+ * Next.js App Router paywall middleware.
10
+ *
11
+ * Usage in a route handler:
12
+ * ```ts
13
+ * import { createBanksiPaywall } from 'banksi/next';
14
+ * const paywall = createBanksiPaywall({ amount: 0.10, description: 'Premium API' });
15
+ *
16
+ * export async function GET(request: NextRequest) {
17
+ * const blocked = await paywall(request);
18
+ * if (blocked) return blocked;
19
+ * return NextResponse.json({ data: 'premium content' });
20
+ * }
21
+ * ```
22
+ */
23
+ declare function createBanksiPaywall(config: PaywallConfig): (request: Request) => Promise<Response | null>;
24
+
25
+ export { createBanksiPaywall };
package/dist/next.js ADDED
@@ -0,0 +1,132 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/next.ts
21
+ var next_exports = {};
22
+ __export(next_exports, {
23
+ BanksiClient: () => BanksiClient,
24
+ createBanksiPaywall: () => createBanksiPaywall
25
+ });
26
+ module.exports = __toCommonJS(next_exports);
27
+
28
+ // src/client.ts
29
+ function getConfig(override) {
30
+ return {
31
+ baseUrl: override?.baseUrl || process.env.BANKSI_URL || "https://banksi.io",
32
+ merchantSlug: override?.merchantSlug || process.env.BANKSI_MERCHANT_SLUG || void 0,
33
+ apiKey: override?.apiKey || process.env.BANKSI_API_KEY || void 0
34
+ };
35
+ }
36
+ var BanksiClient = class {
37
+ constructor(config) {
38
+ const c = getConfig(config);
39
+ this.baseUrl = c.baseUrl.replace(/\/$/, "");
40
+ this.merchantSlug = c.merchantSlug;
41
+ this.apiKey = c.apiKey;
42
+ }
43
+ headers() {
44
+ const h = { "Content-Type": "application/json" };
45
+ if (this.apiKey) h["Authorization"] = `Bearer ${this.apiKey}`;
46
+ return h;
47
+ }
48
+ async listChains() {
49
+ const res = await fetch(`${this.baseUrl}/api/chains`);
50
+ if (!res.ok) throw new Error(`Banksi API error: ${res.status}`);
51
+ const data = await res.json();
52
+ return data.chains;
53
+ }
54
+ async createPayment(opts) {
55
+ const body = {
56
+ chainId: opts.chainId,
57
+ tokenId: opts.tokenId,
58
+ amount: opts.amount
59
+ };
60
+ if (!this.apiKey) {
61
+ body.merchantSlug = opts.merchantSlug || this.merchantSlug;
62
+ }
63
+ const res = await fetch(`${this.baseUrl}/api/x402/pay`, {
64
+ method: "POST",
65
+ headers: this.headers(),
66
+ body: JSON.stringify(body)
67
+ });
68
+ if (!res.ok) {
69
+ const data = await res.json().catch(() => ({}));
70
+ throw new Error(data.error || `Banksi API error: ${res.status}`);
71
+ }
72
+ return res.json();
73
+ }
74
+ async verifyPayment(paymentId) {
75
+ const res = await fetch(`${this.baseUrl}/api/payments/${paymentId}/status`, {
76
+ headers: this.headers()
77
+ });
78
+ if (!res.ok) throw new Error(`Banksi API error: ${res.status}`);
79
+ return res.json();
80
+ }
81
+ async isPaymentConfirmed(paymentId) {
82
+ const status = await this.verifyPayment(paymentId);
83
+ return ["CONFIRMED", "SWEPT"].includes(status.status);
84
+ }
85
+ };
86
+
87
+ // src/next.ts
88
+ function createBanksiPaywall(config) {
89
+ const client = new BanksiClient(config);
90
+ return async function paywall(request) {
91
+ const paymentId = request.headers.get("X-Payment");
92
+ if (paymentId) {
93
+ try {
94
+ const confirmed = await client.isPaymentConfirmed(paymentId);
95
+ if (confirmed) return null;
96
+ } catch {
97
+ }
98
+ }
99
+ const chains = await client.listChains();
100
+ const banksiUrl = config.baseUrl || process.env.BANKSI_URL || "http://localhost:3001";
101
+ const merchantSlug = config.merchantSlug || process.env.BANKSI_MERCHANT_SLUG || "";
102
+ return Response.json(
103
+ {
104
+ status: 402,
105
+ paymentRequired: {
106
+ scheme: "x402",
107
+ description: config.description || "Payment required.",
108
+ merchant: merchantSlug,
109
+ amount: config.amount,
110
+ currency: "USD",
111
+ chains: chains.map((c) => ({
112
+ id: c.id,
113
+ name: c.name,
114
+ tokens: c.tokens.map((t) => ({ id: t.id, symbol: t.symbol }))
115
+ })),
116
+ payUrl: `${banksiUrl}/api/x402/pay`,
117
+ howToPay: [
118
+ `POST ${banksiUrl}/api/x402/pay with { "merchantSlug": "${merchantSlug}", "chainId": "<chain>", "tokenId": "<token>", "amount": ${config.amount} }`,
119
+ "Send the stablecoin to the returned address.",
120
+ "Retry with header: X-Payment: <paymentId>"
121
+ ]
122
+ }
123
+ },
124
+ { status: 402 }
125
+ );
126
+ };
127
+ }
128
+ // Annotate the CommonJS export names for ESM import in node:
129
+ 0 && (module.exports = {
130
+ BanksiClient,
131
+ createBanksiPaywall
132
+ });
package/dist/next.mjs ADDED
@@ -0,0 +1,10 @@
1
+ import {
2
+ createBanksiPaywall
3
+ } from "./chunk-QHCD73JW.mjs";
4
+ import {
5
+ BanksiClient
6
+ } from "./chunk-7MKH6B3K.mjs";
7
+ export {
8
+ BanksiClient,
9
+ createBanksiPaywall
10
+ };
@@ -0,0 +1,16 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { B as BanksiConfig, P as PaymentResult } from './client-jLNKFIPq.mjs';
3
+ export { a as BanksiClient } from './client-jLNKFIPq.mjs';
4
+
5
+ interface BanksiPayButtonProps extends Partial<BanksiConfig> {
6
+ amount: number;
7
+ chainId?: string;
8
+ tokenId?: string;
9
+ onPaymentCreated?: (payment: PaymentResult) => void;
10
+ onPaymentConfirmed?: (paymentId: string) => void;
11
+ children?: React.ReactNode;
12
+ className?: string;
13
+ }
14
+ declare function BanksiPayButton({ amount, chainId, tokenId, onPaymentCreated, onPaymentConfirmed, children, className, ...config }: BanksiPayButtonProps): react_jsx_runtime.JSX.Element;
15
+
16
+ export { BanksiPayButton };
@@ -0,0 +1,16 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { B as BanksiConfig, P as PaymentResult } from './client-jLNKFIPq.js';
3
+ export { a as BanksiClient } from './client-jLNKFIPq.js';
4
+
5
+ interface BanksiPayButtonProps extends Partial<BanksiConfig> {
6
+ amount: number;
7
+ chainId?: string;
8
+ tokenId?: string;
9
+ onPaymentCreated?: (payment: PaymentResult) => void;
10
+ onPaymentConfirmed?: (paymentId: string) => void;
11
+ children?: React.ReactNode;
12
+ className?: string;
13
+ }
14
+ declare function BanksiPayButton({ amount, chainId, tokenId, onPaymentCreated, onPaymentConfirmed, children, className, ...config }: BanksiPayButtonProps): react_jsx_runtime.JSX.Element;
15
+
16
+ export { BanksiPayButton };
package/dist/react.js ADDED
@@ -0,0 +1,213 @@
1
+ "use strict";
2
+ "use client";
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
+
21
+ // src/react.tsx
22
+ var react_exports = {};
23
+ __export(react_exports, {
24
+ BanksiClient: () => BanksiClient,
25
+ BanksiPayButton: () => BanksiPayButton
26
+ });
27
+ module.exports = __toCommonJS(react_exports);
28
+ var import_react = require("react");
29
+
30
+ // src/client.ts
31
+ function getConfig(override) {
32
+ return {
33
+ baseUrl: override?.baseUrl || process.env.BANKSI_URL || "https://banksi.io",
34
+ merchantSlug: override?.merchantSlug || process.env.BANKSI_MERCHANT_SLUG || void 0,
35
+ apiKey: override?.apiKey || process.env.BANKSI_API_KEY || void 0
36
+ };
37
+ }
38
+ var BanksiClient = class {
39
+ constructor(config) {
40
+ const c = getConfig(config);
41
+ this.baseUrl = c.baseUrl.replace(/\/$/, "");
42
+ this.merchantSlug = c.merchantSlug;
43
+ this.apiKey = c.apiKey;
44
+ }
45
+ headers() {
46
+ const h = { "Content-Type": "application/json" };
47
+ if (this.apiKey) h["Authorization"] = `Bearer ${this.apiKey}`;
48
+ return h;
49
+ }
50
+ async listChains() {
51
+ const res = await fetch(`${this.baseUrl}/api/chains`);
52
+ if (!res.ok) throw new Error(`Banksi API error: ${res.status}`);
53
+ const data = await res.json();
54
+ return data.chains;
55
+ }
56
+ async createPayment(opts) {
57
+ const body = {
58
+ chainId: opts.chainId,
59
+ tokenId: opts.tokenId,
60
+ amount: opts.amount
61
+ };
62
+ if (!this.apiKey) {
63
+ body.merchantSlug = opts.merchantSlug || this.merchantSlug;
64
+ }
65
+ const res = await fetch(`${this.baseUrl}/api/x402/pay`, {
66
+ method: "POST",
67
+ headers: this.headers(),
68
+ body: JSON.stringify(body)
69
+ });
70
+ if (!res.ok) {
71
+ const data = await res.json().catch(() => ({}));
72
+ throw new Error(data.error || `Banksi API error: ${res.status}`);
73
+ }
74
+ return res.json();
75
+ }
76
+ async verifyPayment(paymentId) {
77
+ const res = await fetch(`${this.baseUrl}/api/payments/${paymentId}/status`, {
78
+ headers: this.headers()
79
+ });
80
+ if (!res.ok) throw new Error(`Banksi API error: ${res.status}`);
81
+ return res.json();
82
+ }
83
+ async isPaymentConfirmed(paymentId) {
84
+ const status = await this.verifyPayment(paymentId);
85
+ return ["CONFIRMED", "SWEPT"].includes(status.status);
86
+ }
87
+ };
88
+
89
+ // src/react.tsx
90
+ var import_jsx_runtime = require("react/jsx-runtime");
91
+ function BanksiPayButton({
92
+ amount,
93
+ chainId,
94
+ tokenId,
95
+ onPaymentCreated,
96
+ onPaymentConfirmed,
97
+ children,
98
+ className,
99
+ ...config
100
+ }) {
101
+ const [client] = (0, import_react.useState)(() => new BanksiClient(config));
102
+ const [chains, setChains] = (0, import_react.useState)([]);
103
+ const [step, setStep] = (0, import_react.useState)("idle");
104
+ const [selectedChain, setSelectedChain] = (0, import_react.useState)(chainId || "");
105
+ const [selectedToken, setSelectedToken] = (0, import_react.useState)(tokenId || "");
106
+ const [payment, setPayment] = (0, import_react.useState)(null);
107
+ const [error, setError] = (0, import_react.useState)("");
108
+ const [copied, setCopied] = (0, import_react.useState)(false);
109
+ (0, import_react.useEffect)(() => {
110
+ if (step === "select" && chains.length === 0) {
111
+ client.listChains().then(setChains).catch(() => setError("Failed to load chains"));
112
+ }
113
+ }, [step, chains.length, client]);
114
+ (0, import_react.useEffect)(() => {
115
+ if (!payment || step !== "paying") return;
116
+ const iv = setInterval(async () => {
117
+ try {
118
+ const confirmed = await client.isPaymentConfirmed(payment.paymentId);
119
+ if (confirmed) {
120
+ setStep("done");
121
+ onPaymentConfirmed?.(payment.paymentId);
122
+ clearInterval(iv);
123
+ }
124
+ } catch {
125
+ }
126
+ }, 5e3);
127
+ return () => clearInterval(iv);
128
+ }, [payment, step, client, onPaymentConfirmed]);
129
+ const handlePay = (0, import_react.useCallback)(async () => {
130
+ if (!selectedChain || !selectedToken) return;
131
+ setError("");
132
+ try {
133
+ const result = await client.createPayment({ chainId: selectedChain, tokenId: selectedToken, amount });
134
+ setPayment(result);
135
+ setStep("paying");
136
+ onPaymentCreated?.(result);
137
+ } catch (err) {
138
+ setError(err instanceof Error ? err.message : "Payment failed");
139
+ }
140
+ }, [client, selectedChain, selectedToken, amount, onPaymentCreated]);
141
+ const handleCopy = (0, import_react.useCallback)(async () => {
142
+ if (!payment) return;
143
+ await navigator.clipboard.writeText(payment.address);
144
+ setCopied(true);
145
+ setTimeout(() => setCopied(false), 2e3);
146
+ }, [payment]);
147
+ if (step === "idle") {
148
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { onClick: () => chainId && tokenId ? handlePay() : setStep("select"), className: className || "rounded-lg bg-indigo-600 px-4 py-2 text-sm font-medium text-white hover:bg-indigo-700", children: children || `Pay $${amount.toFixed(2)}` });
149
+ }
150
+ const selectedChainData = chains.find((c) => c.id === selectedChain);
151
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "w-full max-w-sm rounded-xl border border-gray-200 bg-white p-5 shadow-lg space-y-4", children: [
152
+ error && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-sm text-red-600", children: error }),
153
+ step === "select" && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
154
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-sm font-medium text-gray-700", children: "Select network & token" }),
155
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "flex flex-wrap gap-2", children: chains.map((c) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
156
+ "button",
157
+ {
158
+ onClick: () => {
159
+ setSelectedChain(c.id);
160
+ setSelectedToken("");
161
+ },
162
+ className: `rounded-lg border px-3 py-1.5 text-xs font-medium ${selectedChain === c.id ? "border-indigo-500 bg-indigo-50 text-indigo-700" : "border-gray-200 text-gray-600"}`,
163
+ children: c.name
164
+ },
165
+ c.id
166
+ )) }),
167
+ selectedChainData && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "flex gap-2", children: selectedChainData.tokens.map((tk) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
168
+ "button",
169
+ {
170
+ onClick: () => setSelectedToken(tk.id),
171
+ className: `rounded-lg border px-3 py-1.5 text-xs font-medium ${selectedToken === tk.id ? "border-indigo-500 bg-indigo-50 text-indigo-700" : "border-gray-200 text-gray-600"}`,
172
+ children: tk.symbol
173
+ },
174
+ tk.id
175
+ )) }),
176
+ selectedToken && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { onClick: handlePay, className: "w-full rounded-lg bg-indigo-600 py-2 text-sm font-medium text-white hover:bg-indigo-700", children: [
177
+ "Pay $",
178
+ amount.toFixed(2)
179
+ ] }),
180
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { onClick: () => setStep("idle"), className: "text-xs text-gray-400 hover:text-gray-600", children: "Cancel" })
181
+ ] }),
182
+ step === "paying" && payment && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
183
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-sm text-gray-500", children: "Send exactly" }),
184
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: "text-lg font-bold text-gray-900", children: [
185
+ payment.amountExpected,
186
+ " ",
187
+ payment.tokenSymbol
188
+ ] }),
189
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: "text-xs text-gray-500", children: [
190
+ "on ",
191
+ payment.chainName
192
+ ] }),
193
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { onClick: handleCopy, className: "w-full rounded-lg bg-gray-50 border border-gray-200 px-3 py-2.5 font-mono text-xs text-gray-800 break-all text-left hover:bg-gray-100", children: payment.address }),
194
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-xs text-center text-gray-400", children: copied ? "Copied!" : "Tap to copy" }),
195
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center gap-2 text-xs text-gray-500", children: [
196
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "relative flex h-2 w-2", children: [
197
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "absolute h-full w-full animate-ping rounded-full bg-amber-400 opacity-75" }),
198
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "relative h-2 w-2 rounded-full bg-amber-400" })
199
+ ] }),
200
+ "Waiting for payment..."
201
+ ] })
202
+ ] }),
203
+ step === "done" && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "text-center py-2", children: [
204
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "mx-auto flex h-12 w-12 items-center justify-center rounded-full bg-green-100 mb-3", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { className: "h-6 w-6 text-green-600", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2.5, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M5 13l4 4L19 7" }) }) }),
205
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-sm font-semibold text-green-700", children: "Payment Confirmed" })
206
+ ] })
207
+ ] });
208
+ }
209
+ // Annotate the CommonJS export names for ESM import in node:
210
+ 0 && (module.exports = {
211
+ BanksiClient,
212
+ BanksiPayButton
213
+ });
package/dist/react.mjs ADDED
@@ -0,0 +1,130 @@
1
+ "use client";
2
+ import {
3
+ BanksiClient
4
+ } from "./chunk-7MKH6B3K.mjs";
5
+
6
+ // src/react.tsx
7
+ import { useState, useEffect, useCallback } from "react";
8
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
9
+ function BanksiPayButton({
10
+ amount,
11
+ chainId,
12
+ tokenId,
13
+ onPaymentCreated,
14
+ onPaymentConfirmed,
15
+ children,
16
+ className,
17
+ ...config
18
+ }) {
19
+ const [client] = useState(() => new BanksiClient(config));
20
+ const [chains, setChains] = useState([]);
21
+ const [step, setStep] = useState("idle");
22
+ const [selectedChain, setSelectedChain] = useState(chainId || "");
23
+ const [selectedToken, setSelectedToken] = useState(tokenId || "");
24
+ const [payment, setPayment] = useState(null);
25
+ const [error, setError] = useState("");
26
+ const [copied, setCopied] = useState(false);
27
+ useEffect(() => {
28
+ if (step === "select" && chains.length === 0) {
29
+ client.listChains().then(setChains).catch(() => setError("Failed to load chains"));
30
+ }
31
+ }, [step, chains.length, client]);
32
+ useEffect(() => {
33
+ if (!payment || step !== "paying") return;
34
+ const iv = setInterval(async () => {
35
+ try {
36
+ const confirmed = await client.isPaymentConfirmed(payment.paymentId);
37
+ if (confirmed) {
38
+ setStep("done");
39
+ onPaymentConfirmed?.(payment.paymentId);
40
+ clearInterval(iv);
41
+ }
42
+ } catch {
43
+ }
44
+ }, 5e3);
45
+ return () => clearInterval(iv);
46
+ }, [payment, step, client, onPaymentConfirmed]);
47
+ const handlePay = useCallback(async () => {
48
+ if (!selectedChain || !selectedToken) return;
49
+ setError("");
50
+ try {
51
+ const result = await client.createPayment({ chainId: selectedChain, tokenId: selectedToken, amount });
52
+ setPayment(result);
53
+ setStep("paying");
54
+ onPaymentCreated?.(result);
55
+ } catch (err) {
56
+ setError(err instanceof Error ? err.message : "Payment failed");
57
+ }
58
+ }, [client, selectedChain, selectedToken, amount, onPaymentCreated]);
59
+ const handleCopy = useCallback(async () => {
60
+ if (!payment) return;
61
+ await navigator.clipboard.writeText(payment.address);
62
+ setCopied(true);
63
+ setTimeout(() => setCopied(false), 2e3);
64
+ }, [payment]);
65
+ if (step === "idle") {
66
+ return /* @__PURE__ */ jsx("button", { onClick: () => chainId && tokenId ? handlePay() : setStep("select"), className: className || "rounded-lg bg-indigo-600 px-4 py-2 text-sm font-medium text-white hover:bg-indigo-700", children: children || `Pay $${amount.toFixed(2)}` });
67
+ }
68
+ const selectedChainData = chains.find((c) => c.id === selectedChain);
69
+ return /* @__PURE__ */ jsxs("div", { className: "w-full max-w-sm rounded-xl border border-gray-200 bg-white p-5 shadow-lg space-y-4", children: [
70
+ error && /* @__PURE__ */ jsx("p", { className: "text-sm text-red-600", children: error }),
71
+ step === "select" && /* @__PURE__ */ jsxs(Fragment, { children: [
72
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-gray-700", children: "Select network & token" }),
73
+ /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-2", children: chains.map((c) => /* @__PURE__ */ jsx(
74
+ "button",
75
+ {
76
+ onClick: () => {
77
+ setSelectedChain(c.id);
78
+ setSelectedToken("");
79
+ },
80
+ className: `rounded-lg border px-3 py-1.5 text-xs font-medium ${selectedChain === c.id ? "border-indigo-500 bg-indigo-50 text-indigo-700" : "border-gray-200 text-gray-600"}`,
81
+ children: c.name
82
+ },
83
+ c.id
84
+ )) }),
85
+ selectedChainData && /* @__PURE__ */ jsx("div", { className: "flex gap-2", children: selectedChainData.tokens.map((tk) => /* @__PURE__ */ jsx(
86
+ "button",
87
+ {
88
+ onClick: () => setSelectedToken(tk.id),
89
+ className: `rounded-lg border px-3 py-1.5 text-xs font-medium ${selectedToken === tk.id ? "border-indigo-500 bg-indigo-50 text-indigo-700" : "border-gray-200 text-gray-600"}`,
90
+ children: tk.symbol
91
+ },
92
+ tk.id
93
+ )) }),
94
+ selectedToken && /* @__PURE__ */ jsxs("button", { onClick: handlePay, className: "w-full rounded-lg bg-indigo-600 py-2 text-sm font-medium text-white hover:bg-indigo-700", children: [
95
+ "Pay $",
96
+ amount.toFixed(2)
97
+ ] }),
98
+ /* @__PURE__ */ jsx("button", { onClick: () => setStep("idle"), className: "text-xs text-gray-400 hover:text-gray-600", children: "Cancel" })
99
+ ] }),
100
+ step === "paying" && payment && /* @__PURE__ */ jsxs(Fragment, { children: [
101
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-gray-500", children: "Send exactly" }),
102
+ /* @__PURE__ */ jsxs("p", { className: "text-lg font-bold text-gray-900", children: [
103
+ payment.amountExpected,
104
+ " ",
105
+ payment.tokenSymbol
106
+ ] }),
107
+ /* @__PURE__ */ jsxs("p", { className: "text-xs text-gray-500", children: [
108
+ "on ",
109
+ payment.chainName
110
+ ] }),
111
+ /* @__PURE__ */ jsx("button", { onClick: handleCopy, className: "w-full rounded-lg bg-gray-50 border border-gray-200 px-3 py-2.5 font-mono text-xs text-gray-800 break-all text-left hover:bg-gray-100", children: payment.address }),
112
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-center text-gray-400", children: copied ? "Copied!" : "Tap to copy" }),
113
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-xs text-gray-500", children: [
114
+ /* @__PURE__ */ jsxs("span", { className: "relative flex h-2 w-2", children: [
115
+ /* @__PURE__ */ jsx("span", { className: "absolute h-full w-full animate-ping rounded-full bg-amber-400 opacity-75" }),
116
+ /* @__PURE__ */ jsx("span", { className: "relative h-2 w-2 rounded-full bg-amber-400" })
117
+ ] }),
118
+ "Waiting for payment..."
119
+ ] })
120
+ ] }),
121
+ step === "done" && /* @__PURE__ */ jsxs("div", { className: "text-center py-2", children: [
122
+ /* @__PURE__ */ jsx("div", { className: "mx-auto flex h-12 w-12 items-center justify-center rounded-full bg-green-100 mb-3", children: /* @__PURE__ */ jsx("svg", { className: "h-6 w-6 text-green-600", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2.5, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M5 13l4 4L19 7" }) }) }),
123
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-semibold text-green-700", children: "Payment Confirmed" })
124
+ ] })
125
+ ] });
126
+ }
127
+ export {
128
+ BanksiClient,
129
+ BanksiPayButton
130
+ };
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "banksi",
3
+ "version": "0.1.0",
4
+ "description": "Crypto payment module for vibe coders. Add USDT/USDC payments to any app in one prompt.",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/index.d.ts",
10
+ "import": "./dist/index.mjs",
11
+ "require": "./dist/index.js"
12
+ },
13
+ "./next": {
14
+ "types": "./dist/next.d.ts",
15
+ "import": "./dist/next.mjs",
16
+ "require": "./dist/next.js"
17
+ },
18
+ "./express": {
19
+ "types": "./dist/express.d.ts",
20
+ "import": "./dist/express.mjs",
21
+ "require": "./dist/express.js"
22
+ },
23
+ "./react": {
24
+ "types": "./dist/react.d.ts",
25
+ "import": "./dist/react.mjs",
26
+ "require": "./dist/react.js"
27
+ }
28
+ },
29
+ "files": ["dist", "README.md"],
30
+ "scripts": {
31
+ "build": "tsup",
32
+ "prepublishOnly": "npm run build"
33
+ },
34
+ "keywords": ["crypto", "payments", "usdt", "usdc", "x402", "stablecoin", "mcp", "vibe-coding"],
35
+ "license": "MIT",
36
+ "peerDependencies": {
37
+ "react": ">=18",
38
+ "react-dom": ">=18"
39
+ },
40
+ "peerDependenciesMeta": {
41
+ "react": { "optional": true },
42
+ "react-dom": { "optional": true }
43
+ },
44
+ "devDependencies": {
45
+ "tsup": "^8.0.0",
46
+ "typescript": "^5.0.0",
47
+ "@types/react": "^19.0.0"
48
+ }
49
+ }