@seedhape/sdk 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/dist/index.cjs +220 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.mts +81 -0
- package/dist/index.d.ts +81 -0
- package/dist/index.global.js +218 -0
- package/dist/index.global.js.map +1 -0
- package/dist/index.mjs +193 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +35 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,220 @@
|
|
|
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 index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
SeedhaPe: () => SeedhaPe
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(index_exports);
|
|
26
|
+
|
|
27
|
+
// src/client.ts
|
|
28
|
+
var DEFAULT_BASE_URL = "https://api.seedhape.com";
|
|
29
|
+
var POLL_INTERVAL_MS = 3e3;
|
|
30
|
+
var TERMINAL_STATUSES = ["VERIFIED", "EXPIRED", "REJECTED", "RESOLVED"];
|
|
31
|
+
var SeedhaPe = class {
|
|
32
|
+
apiKey;
|
|
33
|
+
baseUrl;
|
|
34
|
+
constructor(config) {
|
|
35
|
+
if (!config.apiKey) throw new Error("SeedhaPe: apiKey is required");
|
|
36
|
+
this.apiKey = config.apiKey;
|
|
37
|
+
this.baseUrl = config.baseUrl ?? DEFAULT_BASE_URL;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Create a new payment order.
|
|
41
|
+
*
|
|
42
|
+
* If `expectedSenderName` is provided it is forwarded as
|
|
43
|
+
* `metadata.expectedSenderName` so the matching engine can verify the
|
|
44
|
+
* payment using the payer's name even when the UPI transaction note
|
|
45
|
+
* doesn't contain the order ID.
|
|
46
|
+
*/
|
|
47
|
+
async createOrder(options) {
|
|
48
|
+
const { expectedSenderName, metadata, ...rest } = options;
|
|
49
|
+
const mergedMetadata = expectedSenderName ? { ...metadata, expectedSenderName } : metadata;
|
|
50
|
+
const res = await fetch(`${this.baseUrl}/v1/orders`, {
|
|
51
|
+
method: "POST",
|
|
52
|
+
headers: this.headers(),
|
|
53
|
+
body: JSON.stringify(mergedMetadata ? { ...rest, metadata: mergedMetadata } : rest)
|
|
54
|
+
});
|
|
55
|
+
if (!res.ok) {
|
|
56
|
+
const body = await res.json().catch(() => ({}));
|
|
57
|
+
throw new Error(`SeedhaPe: createOrder failed \u2014 ${body.error ?? res.status}`);
|
|
58
|
+
}
|
|
59
|
+
return res.json();
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Get order status.
|
|
63
|
+
*/
|
|
64
|
+
async getOrderStatus(orderId) {
|
|
65
|
+
const res = await fetch(`${this.baseUrl}/v1/orders/${orderId}/status`, {
|
|
66
|
+
headers: this.headers()
|
|
67
|
+
});
|
|
68
|
+
if (!res.ok) throw new Error(`SeedhaPe: getOrderStatus failed \u2014 ${res.status}`);
|
|
69
|
+
return res.json();
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Show payment modal in the browser with QR code + polling.
|
|
73
|
+
* Returns a promise that resolves when payment is verified or expires.
|
|
74
|
+
*/
|
|
75
|
+
async showPayment(options) {
|
|
76
|
+
return new Promise((resolve, reject) => {
|
|
77
|
+
const modal = this.renderModal(options, resolve, reject);
|
|
78
|
+
document.body.appendChild(modal);
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
renderModal(options, resolve, reject) {
|
|
82
|
+
const overlay = document.createElement("div");
|
|
83
|
+
overlay.style.cssText = `
|
|
84
|
+
position: fixed; inset: 0; background: rgba(0,0,0,0.5);
|
|
85
|
+
display: flex; align-items: center; justify-content: center;
|
|
86
|
+
z-index: 99999; font-family: system-ui, sans-serif;
|
|
87
|
+
`;
|
|
88
|
+
const card = document.createElement("div");
|
|
89
|
+
card.style.cssText = `
|
|
90
|
+
background: white; border-radius: 16px; padding: 32px;
|
|
91
|
+
width: 360px; max-width: calc(100vw - 32px); text-align: center;
|
|
92
|
+
box-shadow: 0 25px 50px rgba(0,0,0,0.25);
|
|
93
|
+
`;
|
|
94
|
+
card.innerHTML = `
|
|
95
|
+
<div id="sp-status" style="margin-bottom: 16px;">
|
|
96
|
+
<p style="font-size: 18px; font-weight: 600; color: #111;">Loading payment...</p>
|
|
97
|
+
</div>
|
|
98
|
+
<div id="sp-qr" style="display: flex; justify-content: center; margin-bottom: 16px;"></div>
|
|
99
|
+
<div id="sp-actions"></div>
|
|
100
|
+
<button id="sp-close" style="
|
|
101
|
+
margin-top: 16px; background: none; border: none; color: #999;
|
|
102
|
+
font-size: 13px; cursor: pointer;
|
|
103
|
+
">Close</button>
|
|
104
|
+
`;
|
|
105
|
+
overlay.appendChild(card);
|
|
106
|
+
let pollTimer = null;
|
|
107
|
+
const cleanup = () => {
|
|
108
|
+
if (pollTimer) clearInterval(pollTimer);
|
|
109
|
+
overlay.remove();
|
|
110
|
+
};
|
|
111
|
+
card.querySelector("#sp-close").addEventListener("click", () => {
|
|
112
|
+
cleanup();
|
|
113
|
+
options.onClose?.();
|
|
114
|
+
reject(new Error("Payment closed by user"));
|
|
115
|
+
});
|
|
116
|
+
this.loadOrder(options.orderId, card, options, resolve, reject, cleanup);
|
|
117
|
+
return overlay;
|
|
118
|
+
}
|
|
119
|
+
async loadOrder(orderId, card, options, resolve, reject, cleanup) {
|
|
120
|
+
try {
|
|
121
|
+
const res = await fetch(`${this.baseUrl}/v1/pay/${orderId}`);
|
|
122
|
+
if (!res.ok) throw new Error("Order not found");
|
|
123
|
+
const order = await res.json();
|
|
124
|
+
this.updateModal(card, order);
|
|
125
|
+
if (TERMINAL_STATUSES.includes(order.status)) {
|
|
126
|
+
this.handleTerminal(order.status, order, options, resolve, cleanup);
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
const pollTimer = setInterval(async () => {
|
|
130
|
+
try {
|
|
131
|
+
const statusRes = await fetch(`${this.baseUrl}/v1/pay/${orderId}`);
|
|
132
|
+
if (!statusRes.ok) return;
|
|
133
|
+
const updated = await statusRes.json();
|
|
134
|
+
this.updateModal(card, updated);
|
|
135
|
+
if (TERMINAL_STATUSES.includes(updated.status)) {
|
|
136
|
+
clearInterval(pollTimer);
|
|
137
|
+
this.handleTerminal(updated.status, updated, options, resolve, cleanup);
|
|
138
|
+
}
|
|
139
|
+
} catch {
|
|
140
|
+
}
|
|
141
|
+
}, POLL_INTERVAL_MS);
|
|
142
|
+
} catch (err) {
|
|
143
|
+
reject(err instanceof Error ? err : new Error(String(err)));
|
|
144
|
+
cleanup();
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
updateModal(card, order) {
|
|
148
|
+
const statusEl = card.querySelector("#sp-status");
|
|
149
|
+
const qrEl = card.querySelector("#sp-qr");
|
|
150
|
+
const actionsEl = card.querySelector("#sp-actions");
|
|
151
|
+
if (order.status === "VERIFIED" || order.status === "RESOLVED") {
|
|
152
|
+
statusEl.innerHTML = `
|
|
153
|
+
<div style="font-size: 48px;">\u2705</div>
|
|
154
|
+
<p style="font-size: 18px; font-weight: 600; color: #16a34a; margin-top: 8px;">Payment Verified!</p>
|
|
155
|
+
<p style="font-size: 28px; font-weight: 700; color: #111; margin-top: 4px;">
|
|
156
|
+
\u20B9${(order.amount / 100).toFixed(2)}
|
|
157
|
+
</p>
|
|
158
|
+
`;
|
|
159
|
+
qrEl.innerHTML = "";
|
|
160
|
+
actionsEl.innerHTML = "";
|
|
161
|
+
} else if (order.status === "EXPIRED") {
|
|
162
|
+
statusEl.innerHTML = `
|
|
163
|
+
<p style="font-size: 18px; font-weight: 600; color: #dc2626;">Payment Expired</p>
|
|
164
|
+
`;
|
|
165
|
+
qrEl.innerHTML = "";
|
|
166
|
+
} else {
|
|
167
|
+
statusEl.innerHTML = `
|
|
168
|
+
<p style="font-size: 18px; font-weight: 600; color: #111;">${order.description ?? "Complete Payment"}</p>
|
|
169
|
+
<p style="font-size: 28px; font-weight: 700; color: #111; margin: 8px 0;">
|
|
170
|
+
\u20B9${(order.amount / 100).toFixed(2)}
|
|
171
|
+
</p>
|
|
172
|
+
`;
|
|
173
|
+
if (order.qrCode) {
|
|
174
|
+
qrEl.innerHTML = `<img src="${order.qrCode}" width="200" height="200" style="border-radius: 8px;" />`;
|
|
175
|
+
}
|
|
176
|
+
actionsEl.innerHTML = `
|
|
177
|
+
<a href="${order.upiUri}" style="
|
|
178
|
+
display: inline-block; background: #16a34a; color: white;
|
|
179
|
+
padding: 12px 24px; border-radius: 10px; text-decoration: none;
|
|
180
|
+
font-weight: 600; font-size: 15px; margin-bottom: 8px;
|
|
181
|
+
">Open UPI App</a>
|
|
182
|
+
<p style="font-size: 12px; color: #999; margin-top: 8px;">
|
|
183
|
+
\u23F3 Waiting for payment...
|
|
184
|
+
</p>
|
|
185
|
+
`;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
handleTerminal(status, order, options, resolve, cleanup) {
|
|
189
|
+
const result = {
|
|
190
|
+
orderId: order.id,
|
|
191
|
+
status: order.status,
|
|
192
|
+
amount: order.amount
|
|
193
|
+
};
|
|
194
|
+
if (status === "VERIFIED" || status === "RESOLVED") {
|
|
195
|
+
options.onSuccess?.(result);
|
|
196
|
+
setTimeout(() => {
|
|
197
|
+
cleanup();
|
|
198
|
+
resolve(result);
|
|
199
|
+
}, 2e3);
|
|
200
|
+
} else if (status === "EXPIRED") {
|
|
201
|
+
options.onExpired?.(order.id);
|
|
202
|
+
cleanup();
|
|
203
|
+
resolve(result);
|
|
204
|
+
} else {
|
|
205
|
+
cleanup();
|
|
206
|
+
resolve(result);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
headers() {
|
|
210
|
+
return {
|
|
211
|
+
"Content-Type": "application/json",
|
|
212
|
+
Authorization: `Bearer ${this.apiKey}`
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
};
|
|
216
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
217
|
+
0 && (module.exports = {
|
|
218
|
+
SeedhaPe
|
|
219
|
+
});
|
|
220
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/client.ts"],"sourcesContent":["export { SeedhaPe } from './client.js';\nexport type {\n SeedhaPeConfig,\n CreateOrderOptions,\n PaymentResult,\n OrderStatus,\n OrderData,\n ShowPaymentOptions,\n} from './types.js';\n","import type {\n SeedhaPeConfig,\n CreateOrderOptions,\n OrderData,\n PaymentResult,\n ShowPaymentOptions,\n} from './types.js';\n\nconst DEFAULT_BASE_URL = 'https://api.seedhape.com';\nconst POLL_INTERVAL_MS = 3000;\nconst TERMINAL_STATUSES = ['VERIFIED', 'EXPIRED', 'REJECTED', 'RESOLVED'] as const;\n\nexport class SeedhaPe {\n private readonly apiKey: string;\n private readonly baseUrl: string;\n\n constructor(config: SeedhaPeConfig) {\n if (!config.apiKey) throw new Error('SeedhaPe: apiKey is required');\n this.apiKey = config.apiKey;\n this.baseUrl = config.baseUrl ?? DEFAULT_BASE_URL;\n }\n\n /**\n * Create a new payment order.\n *\n * If `expectedSenderName` is provided it is forwarded as\n * `metadata.expectedSenderName` so the matching engine can verify the\n * payment using the payer's name even when the UPI transaction note\n * doesn't contain the order ID.\n */\n async createOrder(options: CreateOrderOptions): Promise<OrderData> {\n const { expectedSenderName, metadata, ...rest } = options;\n const mergedMetadata = expectedSenderName\n ? { ...metadata, expectedSenderName }\n : metadata;\n\n const res = await fetch(`${this.baseUrl}/v1/orders`, {\n method: 'POST',\n headers: this.headers(),\n body: JSON.stringify(mergedMetadata ? { ...rest, metadata: mergedMetadata } : rest),\n });\n\n if (!res.ok) {\n const body = await res.json().catch(() => ({}));\n throw new Error(`SeedhaPe: createOrder failed — ${body.error ?? res.status}`);\n }\n\n return res.json() as Promise<OrderData>;\n }\n\n /**\n * Get order status.\n */\n async getOrderStatus(orderId: string): Promise<PaymentResult> {\n const res = await fetch(`${this.baseUrl}/v1/orders/${orderId}/status`, {\n headers: this.headers(),\n });\n\n if (!res.ok) throw new Error(`SeedhaPe: getOrderStatus failed — ${res.status}`);\n return res.json() as Promise<PaymentResult>;\n }\n\n /**\n * Show payment modal in the browser with QR code + polling.\n * Returns a promise that resolves when payment is verified or expires.\n */\n async showPayment(options: ShowPaymentOptions): Promise<PaymentResult> {\n return new Promise((resolve, reject) => {\n const modal = this.renderModal(options, resolve, reject);\n document.body.appendChild(modal);\n });\n }\n\n private renderModal(\n options: ShowPaymentOptions,\n resolve: (result: PaymentResult) => void,\n reject: (err: Error) => void,\n ): HTMLElement {\n const overlay = document.createElement('div');\n overlay.style.cssText = `\n position: fixed; inset: 0; background: rgba(0,0,0,0.5);\n display: flex; align-items: center; justify-content: center;\n z-index: 99999; font-family: system-ui, sans-serif;\n `;\n\n const card = document.createElement('div');\n card.style.cssText = `\n background: white; border-radius: 16px; padding: 32px;\n width: 360px; max-width: calc(100vw - 32px); text-align: center;\n box-shadow: 0 25px 50px rgba(0,0,0,0.25);\n `;\n\n card.innerHTML = `\n <div id=\"sp-status\" style=\"margin-bottom: 16px;\">\n <p style=\"font-size: 18px; font-weight: 600; color: #111;\">Loading payment...</p>\n </div>\n <div id=\"sp-qr\" style=\"display: flex; justify-content: center; margin-bottom: 16px;\"></div>\n <div id=\"sp-actions\"></div>\n <button id=\"sp-close\" style=\"\n margin-top: 16px; background: none; border: none; color: #999;\n font-size: 13px; cursor: pointer;\n \">Close</button>\n `;\n\n overlay.appendChild(card);\n\n let pollTimer: ReturnType<typeof setInterval> | null = null;\n\n const cleanup = () => {\n if (pollTimer) clearInterval(pollTimer);\n overlay.remove();\n };\n\n card.querySelector('#sp-close')!.addEventListener('click', () => {\n cleanup();\n options.onClose?.();\n reject(new Error('Payment closed by user'));\n });\n\n // Load order and start polling\n this.loadOrder(options.orderId, card, options, resolve, reject, cleanup);\n\n return overlay;\n }\n\n private async loadOrder(\n orderId: string,\n card: HTMLElement,\n options: ShowPaymentOptions,\n resolve: (result: PaymentResult) => void,\n reject: (err: Error) => void,\n cleanup: () => void,\n ) {\n try {\n // Fetch order data (public endpoint)\n const res = await fetch(`${this.baseUrl}/v1/pay/${orderId}`);\n if (!res.ok) throw new Error('Order not found');\n const order = await res.json() as OrderData;\n\n this.updateModal(card, order);\n\n if (TERMINAL_STATUSES.includes(order.status as (typeof TERMINAL_STATUSES)[number])) {\n this.handleTerminal(order.status, order, options, resolve, cleanup);\n return;\n }\n\n // Start polling\n const pollTimer = setInterval(async () => {\n try {\n const statusRes = await fetch(`${this.baseUrl}/v1/pay/${orderId}`);\n if (!statusRes.ok) return;\n const updated = await statusRes.json() as OrderData;\n\n this.updateModal(card, updated);\n\n if (TERMINAL_STATUSES.includes(updated.status as (typeof TERMINAL_STATUSES)[number])) {\n clearInterval(pollTimer);\n this.handleTerminal(updated.status, updated, options, resolve, cleanup);\n }\n } catch {\n // ignore\n }\n }, POLL_INTERVAL_MS);\n } catch (err) {\n reject(err instanceof Error ? err : new Error(String(err)));\n cleanup();\n }\n }\n\n private updateModal(card: HTMLElement, order: OrderData) {\n const statusEl = card.querySelector('#sp-status')!;\n const qrEl = card.querySelector('#sp-qr')!;\n const actionsEl = card.querySelector('#sp-actions')!;\n\n if (order.status === 'VERIFIED' || order.status === 'RESOLVED') {\n statusEl.innerHTML = `\n <div style=\"font-size: 48px;\">✅</div>\n <p style=\"font-size: 18px; font-weight: 600; color: #16a34a; margin-top: 8px;\">Payment Verified!</p>\n <p style=\"font-size: 28px; font-weight: 700; color: #111; margin-top: 4px;\">\n ₹${(order.amount / 100).toFixed(2)}\n </p>\n `;\n qrEl.innerHTML = '';\n actionsEl.innerHTML = '';\n } else if (order.status === 'EXPIRED') {\n statusEl.innerHTML = `\n <p style=\"font-size: 18px; font-weight: 600; color: #dc2626;\">Payment Expired</p>\n `;\n qrEl.innerHTML = '';\n } else {\n statusEl.innerHTML = `\n <p style=\"font-size: 18px; font-weight: 600; color: #111;\">${order.description ?? 'Complete Payment'}</p>\n <p style=\"font-size: 28px; font-weight: 700; color: #111; margin: 8px 0;\">\n ₹${(order.amount / 100).toFixed(2)}\n </p>\n `;\n\n if (order.qrCode) {\n qrEl.innerHTML = `<img src=\"${order.qrCode}\" width=\"200\" height=\"200\" style=\"border-radius: 8px;\" />`;\n }\n\n actionsEl.innerHTML = `\n <a href=\"${order.upiUri}\" style=\"\n display: inline-block; background: #16a34a; color: white;\n padding: 12px 24px; border-radius: 10px; text-decoration: none;\n font-weight: 600; font-size: 15px; margin-bottom: 8px;\n \">Open UPI App</a>\n <p style=\"font-size: 12px; color: #999; margin-top: 8px;\">\n ⏳ Waiting for payment...\n </p>\n `;\n }\n }\n\n private handleTerminal(\n status: string,\n order: OrderData,\n options: ShowPaymentOptions,\n resolve: (result: PaymentResult) => void,\n cleanup: () => void,\n ) {\n const result: PaymentResult = {\n orderId: order.id,\n status: order.status,\n amount: order.amount,\n };\n\n if (status === 'VERIFIED' || status === 'RESOLVED') {\n options.onSuccess?.(result);\n setTimeout(() => {\n cleanup();\n resolve(result);\n }, 2000);\n } else if (status === 'EXPIRED') {\n options.onExpired?.(order.id);\n cleanup();\n resolve(result);\n } else {\n cleanup();\n resolve(result);\n }\n }\n\n private headers(): Record<string, string> {\n return {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.apiKey}`,\n };\n }\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
interface SeedhaPeConfig {
|
|
2
|
+
apiKey: string;
|
|
3
|
+
baseUrl?: string;
|
|
4
|
+
}
|
|
5
|
+
interface CreateOrderOptions {
|
|
6
|
+
amount: number;
|
|
7
|
+
externalOrderId?: string;
|
|
8
|
+
description?: string;
|
|
9
|
+
customerEmail?: string;
|
|
10
|
+
customerPhone?: string;
|
|
11
|
+
/**
|
|
12
|
+
* The payer's name exactly as it appears in their UPI app.
|
|
13
|
+
* Strongly recommended — used by the matching engine to verify payments
|
|
14
|
+
* when the transaction note doesn't contain the order ID.
|
|
15
|
+
* Maps to `metadata.expectedSenderName` on the order.
|
|
16
|
+
*/
|
|
17
|
+
expectedSenderName?: string;
|
|
18
|
+
expiresInMinutes?: number;
|
|
19
|
+
metadata?: Record<string, unknown>;
|
|
20
|
+
}
|
|
21
|
+
interface OrderData {
|
|
22
|
+
id: string;
|
|
23
|
+
amount: number;
|
|
24
|
+
originalAmount: number;
|
|
25
|
+
currency: string;
|
|
26
|
+
description: string | null;
|
|
27
|
+
status: OrderStatus;
|
|
28
|
+
upiUri: string;
|
|
29
|
+
qrCode: string;
|
|
30
|
+
expiresAt: string;
|
|
31
|
+
createdAt: string;
|
|
32
|
+
}
|
|
33
|
+
type OrderStatus = 'CREATED' | 'PENDING' | 'VERIFIED' | 'DISPUTED' | 'RESOLVED' | 'EXPIRED' | 'REJECTED';
|
|
34
|
+
interface PaymentResult {
|
|
35
|
+
orderId: string;
|
|
36
|
+
status: OrderStatus;
|
|
37
|
+
amount: number;
|
|
38
|
+
verifiedAt?: string;
|
|
39
|
+
}
|
|
40
|
+
interface ShowPaymentOptions {
|
|
41
|
+
orderId: string;
|
|
42
|
+
onSuccess?: (result: PaymentResult) => void;
|
|
43
|
+
onExpired?: (orderId: string) => void;
|
|
44
|
+
onClose?: () => void;
|
|
45
|
+
containerEl?: HTMLElement;
|
|
46
|
+
theme?: {
|
|
47
|
+
primaryColor?: string;
|
|
48
|
+
borderRadius?: string;
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
declare class SeedhaPe {
|
|
53
|
+
private readonly apiKey;
|
|
54
|
+
private readonly baseUrl;
|
|
55
|
+
constructor(config: SeedhaPeConfig);
|
|
56
|
+
/**
|
|
57
|
+
* Create a new payment order.
|
|
58
|
+
*
|
|
59
|
+
* If `expectedSenderName` is provided it is forwarded as
|
|
60
|
+
* `metadata.expectedSenderName` so the matching engine can verify the
|
|
61
|
+
* payment using the payer's name even when the UPI transaction note
|
|
62
|
+
* doesn't contain the order ID.
|
|
63
|
+
*/
|
|
64
|
+
createOrder(options: CreateOrderOptions): Promise<OrderData>;
|
|
65
|
+
/**
|
|
66
|
+
* Get order status.
|
|
67
|
+
*/
|
|
68
|
+
getOrderStatus(orderId: string): Promise<PaymentResult>;
|
|
69
|
+
/**
|
|
70
|
+
* Show payment modal in the browser with QR code + polling.
|
|
71
|
+
* Returns a promise that resolves when payment is verified or expires.
|
|
72
|
+
*/
|
|
73
|
+
showPayment(options: ShowPaymentOptions): Promise<PaymentResult>;
|
|
74
|
+
private renderModal;
|
|
75
|
+
private loadOrder;
|
|
76
|
+
private updateModal;
|
|
77
|
+
private handleTerminal;
|
|
78
|
+
private headers;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export { type CreateOrderOptions, type OrderData, type OrderStatus, type PaymentResult, SeedhaPe, type SeedhaPeConfig, type ShowPaymentOptions };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
interface SeedhaPeConfig {
|
|
2
|
+
apiKey: string;
|
|
3
|
+
baseUrl?: string;
|
|
4
|
+
}
|
|
5
|
+
interface CreateOrderOptions {
|
|
6
|
+
amount: number;
|
|
7
|
+
externalOrderId?: string;
|
|
8
|
+
description?: string;
|
|
9
|
+
customerEmail?: string;
|
|
10
|
+
customerPhone?: string;
|
|
11
|
+
/**
|
|
12
|
+
* The payer's name exactly as it appears in their UPI app.
|
|
13
|
+
* Strongly recommended — used by the matching engine to verify payments
|
|
14
|
+
* when the transaction note doesn't contain the order ID.
|
|
15
|
+
* Maps to `metadata.expectedSenderName` on the order.
|
|
16
|
+
*/
|
|
17
|
+
expectedSenderName?: string;
|
|
18
|
+
expiresInMinutes?: number;
|
|
19
|
+
metadata?: Record<string, unknown>;
|
|
20
|
+
}
|
|
21
|
+
interface OrderData {
|
|
22
|
+
id: string;
|
|
23
|
+
amount: number;
|
|
24
|
+
originalAmount: number;
|
|
25
|
+
currency: string;
|
|
26
|
+
description: string | null;
|
|
27
|
+
status: OrderStatus;
|
|
28
|
+
upiUri: string;
|
|
29
|
+
qrCode: string;
|
|
30
|
+
expiresAt: string;
|
|
31
|
+
createdAt: string;
|
|
32
|
+
}
|
|
33
|
+
type OrderStatus = 'CREATED' | 'PENDING' | 'VERIFIED' | 'DISPUTED' | 'RESOLVED' | 'EXPIRED' | 'REJECTED';
|
|
34
|
+
interface PaymentResult {
|
|
35
|
+
orderId: string;
|
|
36
|
+
status: OrderStatus;
|
|
37
|
+
amount: number;
|
|
38
|
+
verifiedAt?: string;
|
|
39
|
+
}
|
|
40
|
+
interface ShowPaymentOptions {
|
|
41
|
+
orderId: string;
|
|
42
|
+
onSuccess?: (result: PaymentResult) => void;
|
|
43
|
+
onExpired?: (orderId: string) => void;
|
|
44
|
+
onClose?: () => void;
|
|
45
|
+
containerEl?: HTMLElement;
|
|
46
|
+
theme?: {
|
|
47
|
+
primaryColor?: string;
|
|
48
|
+
borderRadius?: string;
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
declare class SeedhaPe {
|
|
53
|
+
private readonly apiKey;
|
|
54
|
+
private readonly baseUrl;
|
|
55
|
+
constructor(config: SeedhaPeConfig);
|
|
56
|
+
/**
|
|
57
|
+
* Create a new payment order.
|
|
58
|
+
*
|
|
59
|
+
* If `expectedSenderName` is provided it is forwarded as
|
|
60
|
+
* `metadata.expectedSenderName` so the matching engine can verify the
|
|
61
|
+
* payment using the payer's name even when the UPI transaction note
|
|
62
|
+
* doesn't contain the order ID.
|
|
63
|
+
*/
|
|
64
|
+
createOrder(options: CreateOrderOptions): Promise<OrderData>;
|
|
65
|
+
/**
|
|
66
|
+
* Get order status.
|
|
67
|
+
*/
|
|
68
|
+
getOrderStatus(orderId: string): Promise<PaymentResult>;
|
|
69
|
+
/**
|
|
70
|
+
* Show payment modal in the browser with QR code + polling.
|
|
71
|
+
* Returns a promise that resolves when payment is verified or expires.
|
|
72
|
+
*/
|
|
73
|
+
showPayment(options: ShowPaymentOptions): Promise<PaymentResult>;
|
|
74
|
+
private renderModal;
|
|
75
|
+
private loadOrder;
|
|
76
|
+
private updateModal;
|
|
77
|
+
private handleTerminal;
|
|
78
|
+
private headers;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export { type CreateOrderOptions, type OrderData, type OrderStatus, type PaymentResult, SeedhaPe, type SeedhaPeConfig, type ShowPaymentOptions };
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var SeedhaPe = (() => {
|
|
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/index.ts
|
|
22
|
+
var index_exports = {};
|
|
23
|
+
__export(index_exports, {
|
|
24
|
+
SeedhaPe: () => SeedhaPe
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
// src/client.ts
|
|
28
|
+
var DEFAULT_BASE_URL = "https://api.seedhape.com";
|
|
29
|
+
var POLL_INTERVAL_MS = 3e3;
|
|
30
|
+
var TERMINAL_STATUSES = ["VERIFIED", "EXPIRED", "REJECTED", "RESOLVED"];
|
|
31
|
+
var SeedhaPe = class {
|
|
32
|
+
apiKey;
|
|
33
|
+
baseUrl;
|
|
34
|
+
constructor(config) {
|
|
35
|
+
if (!config.apiKey) throw new Error("SeedhaPe: apiKey is required");
|
|
36
|
+
this.apiKey = config.apiKey;
|
|
37
|
+
this.baseUrl = config.baseUrl ?? DEFAULT_BASE_URL;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Create a new payment order.
|
|
41
|
+
*
|
|
42
|
+
* If `expectedSenderName` is provided it is forwarded as
|
|
43
|
+
* `metadata.expectedSenderName` so the matching engine can verify the
|
|
44
|
+
* payment using the payer's name even when the UPI transaction note
|
|
45
|
+
* doesn't contain the order ID.
|
|
46
|
+
*/
|
|
47
|
+
async createOrder(options) {
|
|
48
|
+
const { expectedSenderName, metadata, ...rest } = options;
|
|
49
|
+
const mergedMetadata = expectedSenderName ? { ...metadata, expectedSenderName } : metadata;
|
|
50
|
+
const res = await fetch(`${this.baseUrl}/v1/orders`, {
|
|
51
|
+
method: "POST",
|
|
52
|
+
headers: this.headers(),
|
|
53
|
+
body: JSON.stringify(mergedMetadata ? { ...rest, metadata: mergedMetadata } : rest)
|
|
54
|
+
});
|
|
55
|
+
if (!res.ok) {
|
|
56
|
+
const body = await res.json().catch(() => ({}));
|
|
57
|
+
throw new Error(`SeedhaPe: createOrder failed \u2014 ${body.error ?? res.status}`);
|
|
58
|
+
}
|
|
59
|
+
return res.json();
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Get order status.
|
|
63
|
+
*/
|
|
64
|
+
async getOrderStatus(orderId) {
|
|
65
|
+
const res = await fetch(`${this.baseUrl}/v1/orders/${orderId}/status`, {
|
|
66
|
+
headers: this.headers()
|
|
67
|
+
});
|
|
68
|
+
if (!res.ok) throw new Error(`SeedhaPe: getOrderStatus failed \u2014 ${res.status}`);
|
|
69
|
+
return res.json();
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Show payment modal in the browser with QR code + polling.
|
|
73
|
+
* Returns a promise that resolves when payment is verified or expires.
|
|
74
|
+
*/
|
|
75
|
+
async showPayment(options) {
|
|
76
|
+
return new Promise((resolve, reject) => {
|
|
77
|
+
const modal = this.renderModal(options, resolve, reject);
|
|
78
|
+
document.body.appendChild(modal);
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
renderModal(options, resolve, reject) {
|
|
82
|
+
const overlay = document.createElement("div");
|
|
83
|
+
overlay.style.cssText = `
|
|
84
|
+
position: fixed; inset: 0; background: rgba(0,0,0,0.5);
|
|
85
|
+
display: flex; align-items: center; justify-content: center;
|
|
86
|
+
z-index: 99999; font-family: system-ui, sans-serif;
|
|
87
|
+
`;
|
|
88
|
+
const card = document.createElement("div");
|
|
89
|
+
card.style.cssText = `
|
|
90
|
+
background: white; border-radius: 16px; padding: 32px;
|
|
91
|
+
width: 360px; max-width: calc(100vw - 32px); text-align: center;
|
|
92
|
+
box-shadow: 0 25px 50px rgba(0,0,0,0.25);
|
|
93
|
+
`;
|
|
94
|
+
card.innerHTML = `
|
|
95
|
+
<div id="sp-status" style="margin-bottom: 16px;">
|
|
96
|
+
<p style="font-size: 18px; font-weight: 600; color: #111;">Loading payment...</p>
|
|
97
|
+
</div>
|
|
98
|
+
<div id="sp-qr" style="display: flex; justify-content: center; margin-bottom: 16px;"></div>
|
|
99
|
+
<div id="sp-actions"></div>
|
|
100
|
+
<button id="sp-close" style="
|
|
101
|
+
margin-top: 16px; background: none; border: none; color: #999;
|
|
102
|
+
font-size: 13px; cursor: pointer;
|
|
103
|
+
">Close</button>
|
|
104
|
+
`;
|
|
105
|
+
overlay.appendChild(card);
|
|
106
|
+
let pollTimer = null;
|
|
107
|
+
const cleanup = () => {
|
|
108
|
+
if (pollTimer) clearInterval(pollTimer);
|
|
109
|
+
overlay.remove();
|
|
110
|
+
};
|
|
111
|
+
card.querySelector("#sp-close").addEventListener("click", () => {
|
|
112
|
+
cleanup();
|
|
113
|
+
options.onClose?.();
|
|
114
|
+
reject(new Error("Payment closed by user"));
|
|
115
|
+
});
|
|
116
|
+
this.loadOrder(options.orderId, card, options, resolve, reject, cleanup);
|
|
117
|
+
return overlay;
|
|
118
|
+
}
|
|
119
|
+
async loadOrder(orderId, card, options, resolve, reject, cleanup) {
|
|
120
|
+
try {
|
|
121
|
+
const res = await fetch(`${this.baseUrl}/v1/pay/${orderId}`);
|
|
122
|
+
if (!res.ok) throw new Error("Order not found");
|
|
123
|
+
const order = await res.json();
|
|
124
|
+
this.updateModal(card, order);
|
|
125
|
+
if (TERMINAL_STATUSES.includes(order.status)) {
|
|
126
|
+
this.handleTerminal(order.status, order, options, resolve, cleanup);
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
const pollTimer = setInterval(async () => {
|
|
130
|
+
try {
|
|
131
|
+
const statusRes = await fetch(`${this.baseUrl}/v1/pay/${orderId}`);
|
|
132
|
+
if (!statusRes.ok) return;
|
|
133
|
+
const updated = await statusRes.json();
|
|
134
|
+
this.updateModal(card, updated);
|
|
135
|
+
if (TERMINAL_STATUSES.includes(updated.status)) {
|
|
136
|
+
clearInterval(pollTimer);
|
|
137
|
+
this.handleTerminal(updated.status, updated, options, resolve, cleanup);
|
|
138
|
+
}
|
|
139
|
+
} catch {
|
|
140
|
+
}
|
|
141
|
+
}, POLL_INTERVAL_MS);
|
|
142
|
+
} catch (err) {
|
|
143
|
+
reject(err instanceof Error ? err : new Error(String(err)));
|
|
144
|
+
cleanup();
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
updateModal(card, order) {
|
|
148
|
+
const statusEl = card.querySelector("#sp-status");
|
|
149
|
+
const qrEl = card.querySelector("#sp-qr");
|
|
150
|
+
const actionsEl = card.querySelector("#sp-actions");
|
|
151
|
+
if (order.status === "VERIFIED" || order.status === "RESOLVED") {
|
|
152
|
+
statusEl.innerHTML = `
|
|
153
|
+
<div style="font-size: 48px;">\u2705</div>
|
|
154
|
+
<p style="font-size: 18px; font-weight: 600; color: #16a34a; margin-top: 8px;">Payment Verified!</p>
|
|
155
|
+
<p style="font-size: 28px; font-weight: 700; color: #111; margin-top: 4px;">
|
|
156
|
+
\u20B9${(order.amount / 100).toFixed(2)}
|
|
157
|
+
</p>
|
|
158
|
+
`;
|
|
159
|
+
qrEl.innerHTML = "";
|
|
160
|
+
actionsEl.innerHTML = "";
|
|
161
|
+
} else if (order.status === "EXPIRED") {
|
|
162
|
+
statusEl.innerHTML = `
|
|
163
|
+
<p style="font-size: 18px; font-weight: 600; color: #dc2626;">Payment Expired</p>
|
|
164
|
+
`;
|
|
165
|
+
qrEl.innerHTML = "";
|
|
166
|
+
} else {
|
|
167
|
+
statusEl.innerHTML = `
|
|
168
|
+
<p style="font-size: 18px; font-weight: 600; color: #111;">${order.description ?? "Complete Payment"}</p>
|
|
169
|
+
<p style="font-size: 28px; font-weight: 700; color: #111; margin: 8px 0;">
|
|
170
|
+
\u20B9${(order.amount / 100).toFixed(2)}
|
|
171
|
+
</p>
|
|
172
|
+
`;
|
|
173
|
+
if (order.qrCode) {
|
|
174
|
+
qrEl.innerHTML = `<img src="${order.qrCode}" width="200" height="200" style="border-radius: 8px;" />`;
|
|
175
|
+
}
|
|
176
|
+
actionsEl.innerHTML = `
|
|
177
|
+
<a href="${order.upiUri}" style="
|
|
178
|
+
display: inline-block; background: #16a34a; color: white;
|
|
179
|
+
padding: 12px 24px; border-radius: 10px; text-decoration: none;
|
|
180
|
+
font-weight: 600; font-size: 15px; margin-bottom: 8px;
|
|
181
|
+
">Open UPI App</a>
|
|
182
|
+
<p style="font-size: 12px; color: #999; margin-top: 8px;">
|
|
183
|
+
\u23F3 Waiting for payment...
|
|
184
|
+
</p>
|
|
185
|
+
`;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
handleTerminal(status, order, options, resolve, cleanup) {
|
|
189
|
+
const result = {
|
|
190
|
+
orderId: order.id,
|
|
191
|
+
status: order.status,
|
|
192
|
+
amount: order.amount
|
|
193
|
+
};
|
|
194
|
+
if (status === "VERIFIED" || status === "RESOLVED") {
|
|
195
|
+
options.onSuccess?.(result);
|
|
196
|
+
setTimeout(() => {
|
|
197
|
+
cleanup();
|
|
198
|
+
resolve(result);
|
|
199
|
+
}, 2e3);
|
|
200
|
+
} else if (status === "EXPIRED") {
|
|
201
|
+
options.onExpired?.(order.id);
|
|
202
|
+
cleanup();
|
|
203
|
+
resolve(result);
|
|
204
|
+
} else {
|
|
205
|
+
cleanup();
|
|
206
|
+
resolve(result);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
headers() {
|
|
210
|
+
return {
|
|
211
|
+
"Content-Type": "application/json",
|
|
212
|
+
Authorization: `Bearer ${this.apiKey}`
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
};
|
|
216
|
+
return __toCommonJS(index_exports);
|
|
217
|
+
})();
|
|
218
|
+
//# sourceMappingURL=index.global.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/client.ts"],"sourcesContent":["export { SeedhaPe } from './client.js';\nexport type {\n SeedhaPeConfig,\n CreateOrderOptions,\n PaymentResult,\n OrderStatus,\n OrderData,\n ShowPaymentOptions,\n} from './types.js';\n","import type {\n SeedhaPeConfig,\n CreateOrderOptions,\n OrderData,\n PaymentResult,\n ShowPaymentOptions,\n} from './types.js';\n\nconst DEFAULT_BASE_URL = 'https://api.seedhape.com';\nconst POLL_INTERVAL_MS = 3000;\nconst TERMINAL_STATUSES = ['VERIFIED', 'EXPIRED', 'REJECTED', 'RESOLVED'] as const;\n\nexport class SeedhaPe {\n private readonly apiKey: string;\n private readonly baseUrl: string;\n\n constructor(config: SeedhaPeConfig) {\n if (!config.apiKey) throw new Error('SeedhaPe: apiKey is required');\n this.apiKey = config.apiKey;\n this.baseUrl = config.baseUrl ?? DEFAULT_BASE_URL;\n }\n\n /**\n * Create a new payment order.\n *\n * If `expectedSenderName` is provided it is forwarded as\n * `metadata.expectedSenderName` so the matching engine can verify the\n * payment using the payer's name even when the UPI transaction note\n * doesn't contain the order ID.\n */\n async createOrder(options: CreateOrderOptions): Promise<OrderData> {\n const { expectedSenderName, metadata, ...rest } = options;\n const mergedMetadata = expectedSenderName\n ? { ...metadata, expectedSenderName }\n : metadata;\n\n const res = await fetch(`${this.baseUrl}/v1/orders`, {\n method: 'POST',\n headers: this.headers(),\n body: JSON.stringify(mergedMetadata ? { ...rest, metadata: mergedMetadata } : rest),\n });\n\n if (!res.ok) {\n const body = await res.json().catch(() => ({}));\n throw new Error(`SeedhaPe: createOrder failed — ${body.error ?? res.status}`);\n }\n\n return res.json() as Promise<OrderData>;\n }\n\n /**\n * Get order status.\n */\n async getOrderStatus(orderId: string): Promise<PaymentResult> {\n const res = await fetch(`${this.baseUrl}/v1/orders/${orderId}/status`, {\n headers: this.headers(),\n });\n\n if (!res.ok) throw new Error(`SeedhaPe: getOrderStatus failed — ${res.status}`);\n return res.json() as Promise<PaymentResult>;\n }\n\n /**\n * Show payment modal in the browser with QR code + polling.\n * Returns a promise that resolves when payment is verified or expires.\n */\n async showPayment(options: ShowPaymentOptions): Promise<PaymentResult> {\n return new Promise((resolve, reject) => {\n const modal = this.renderModal(options, resolve, reject);\n document.body.appendChild(modal);\n });\n }\n\n private renderModal(\n options: ShowPaymentOptions,\n resolve: (result: PaymentResult) => void,\n reject: (err: Error) => void,\n ): HTMLElement {\n const overlay = document.createElement('div');\n overlay.style.cssText = `\n position: fixed; inset: 0; background: rgba(0,0,0,0.5);\n display: flex; align-items: center; justify-content: center;\n z-index: 99999; font-family: system-ui, sans-serif;\n `;\n\n const card = document.createElement('div');\n card.style.cssText = `\n background: white; border-radius: 16px; padding: 32px;\n width: 360px; max-width: calc(100vw - 32px); text-align: center;\n box-shadow: 0 25px 50px rgba(0,0,0,0.25);\n `;\n\n card.innerHTML = `\n <div id=\"sp-status\" style=\"margin-bottom: 16px;\">\n <p style=\"font-size: 18px; font-weight: 600; color: #111;\">Loading payment...</p>\n </div>\n <div id=\"sp-qr\" style=\"display: flex; justify-content: center; margin-bottom: 16px;\"></div>\n <div id=\"sp-actions\"></div>\n <button id=\"sp-close\" style=\"\n margin-top: 16px; background: none; border: none; color: #999;\n font-size: 13px; cursor: pointer;\n \">Close</button>\n `;\n\n overlay.appendChild(card);\n\n let pollTimer: ReturnType<typeof setInterval> | null = null;\n\n const cleanup = () => {\n if (pollTimer) clearInterval(pollTimer);\n overlay.remove();\n };\n\n card.querySelector('#sp-close')!.addEventListener('click', () => {\n cleanup();\n options.onClose?.();\n reject(new Error('Payment closed by user'));\n });\n\n // Load order and start polling\n this.loadOrder(options.orderId, card, options, resolve, reject, cleanup);\n\n return overlay;\n }\n\n private async loadOrder(\n orderId: string,\n card: HTMLElement,\n options: ShowPaymentOptions,\n resolve: (result: PaymentResult) => void,\n reject: (err: Error) => void,\n cleanup: () => void,\n ) {\n try {\n // Fetch order data (public endpoint)\n const res = await fetch(`${this.baseUrl}/v1/pay/${orderId}`);\n if (!res.ok) throw new Error('Order not found');\n const order = await res.json() as OrderData;\n\n this.updateModal(card, order);\n\n if (TERMINAL_STATUSES.includes(order.status as (typeof TERMINAL_STATUSES)[number])) {\n this.handleTerminal(order.status, order, options, resolve, cleanup);\n return;\n }\n\n // Start polling\n const pollTimer = setInterval(async () => {\n try {\n const statusRes = await fetch(`${this.baseUrl}/v1/pay/${orderId}`);\n if (!statusRes.ok) return;\n const updated = await statusRes.json() as OrderData;\n\n this.updateModal(card, updated);\n\n if (TERMINAL_STATUSES.includes(updated.status as (typeof TERMINAL_STATUSES)[number])) {\n clearInterval(pollTimer);\n this.handleTerminal(updated.status, updated, options, resolve, cleanup);\n }\n } catch {\n // ignore\n }\n }, POLL_INTERVAL_MS);\n } catch (err) {\n reject(err instanceof Error ? err : new Error(String(err)));\n cleanup();\n }\n }\n\n private updateModal(card: HTMLElement, order: OrderData) {\n const statusEl = card.querySelector('#sp-status')!;\n const qrEl = card.querySelector('#sp-qr')!;\n const actionsEl = card.querySelector('#sp-actions')!;\n\n if (order.status === 'VERIFIED' || order.status === 'RESOLVED') {\n statusEl.innerHTML = `\n <div style=\"font-size: 48px;\">✅</div>\n <p style=\"font-size: 18px; font-weight: 600; color: #16a34a; margin-top: 8px;\">Payment Verified!</p>\n <p style=\"font-size: 28px; font-weight: 700; color: #111; margin-top: 4px;\">\n ₹${(order.amount / 100).toFixed(2)}\n </p>\n `;\n qrEl.innerHTML = '';\n actionsEl.innerHTML = '';\n } else if (order.status === 'EXPIRED') {\n statusEl.innerHTML = `\n <p style=\"font-size: 18px; font-weight: 600; color: #dc2626;\">Payment Expired</p>\n `;\n qrEl.innerHTML = '';\n } else {\n statusEl.innerHTML = `\n <p style=\"font-size: 18px; font-weight: 600; color: #111;\">${order.description ?? 'Complete Payment'}</p>\n <p style=\"font-size: 28px; font-weight: 700; color: #111; margin: 8px 0;\">\n ₹${(order.amount / 100).toFixed(2)}\n </p>\n `;\n\n if (order.qrCode) {\n qrEl.innerHTML = `<img src=\"${order.qrCode}\" width=\"200\" height=\"200\" style=\"border-radius: 8px;\" />`;\n }\n\n actionsEl.innerHTML = `\n <a href=\"${order.upiUri}\" style=\"\n display: inline-block; background: #16a34a; color: white;\n padding: 12px 24px; border-radius: 10px; text-decoration: none;\n font-weight: 600; font-size: 15px; margin-bottom: 8px;\n \">Open UPI App</a>\n <p style=\"font-size: 12px; color: #999; margin-top: 8px;\">\n ⏳ Waiting for payment...\n </p>\n `;\n }\n }\n\n private handleTerminal(\n status: string,\n order: OrderData,\n options: ShowPaymentOptions,\n resolve: (result: PaymentResult) => void,\n cleanup: () => void,\n ) {\n const result: PaymentResult = {\n orderId: order.id,\n status: order.status,\n amount: order.amount,\n };\n\n if (status === 'VERIFIED' || status === 'RESOLVED') {\n options.onSuccess?.(result);\n setTimeout(() => {\n cleanup();\n resolve(result);\n }, 2000);\n } else if (status === 'EXPIRED') {\n options.onExpired?.(order.id);\n cleanup();\n resolve(result);\n } else {\n cleanup();\n resolve(result);\n }\n }\n\n private headers(): Record<string, string> {\n return {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.apiKey}`,\n };\n }\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
// src/client.ts
|
|
2
|
+
var DEFAULT_BASE_URL = "https://api.seedhape.com";
|
|
3
|
+
var POLL_INTERVAL_MS = 3e3;
|
|
4
|
+
var TERMINAL_STATUSES = ["VERIFIED", "EXPIRED", "REJECTED", "RESOLVED"];
|
|
5
|
+
var SeedhaPe = class {
|
|
6
|
+
apiKey;
|
|
7
|
+
baseUrl;
|
|
8
|
+
constructor(config) {
|
|
9
|
+
if (!config.apiKey) throw new Error("SeedhaPe: apiKey is required");
|
|
10
|
+
this.apiKey = config.apiKey;
|
|
11
|
+
this.baseUrl = config.baseUrl ?? DEFAULT_BASE_URL;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Create a new payment order.
|
|
15
|
+
*
|
|
16
|
+
* If `expectedSenderName` is provided it is forwarded as
|
|
17
|
+
* `metadata.expectedSenderName` so the matching engine can verify the
|
|
18
|
+
* payment using the payer's name even when the UPI transaction note
|
|
19
|
+
* doesn't contain the order ID.
|
|
20
|
+
*/
|
|
21
|
+
async createOrder(options) {
|
|
22
|
+
const { expectedSenderName, metadata, ...rest } = options;
|
|
23
|
+
const mergedMetadata = expectedSenderName ? { ...metadata, expectedSenderName } : metadata;
|
|
24
|
+
const res = await fetch(`${this.baseUrl}/v1/orders`, {
|
|
25
|
+
method: "POST",
|
|
26
|
+
headers: this.headers(),
|
|
27
|
+
body: JSON.stringify(mergedMetadata ? { ...rest, metadata: mergedMetadata } : rest)
|
|
28
|
+
});
|
|
29
|
+
if (!res.ok) {
|
|
30
|
+
const body = await res.json().catch(() => ({}));
|
|
31
|
+
throw new Error(`SeedhaPe: createOrder failed \u2014 ${body.error ?? res.status}`);
|
|
32
|
+
}
|
|
33
|
+
return res.json();
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Get order status.
|
|
37
|
+
*/
|
|
38
|
+
async getOrderStatus(orderId) {
|
|
39
|
+
const res = await fetch(`${this.baseUrl}/v1/orders/${orderId}/status`, {
|
|
40
|
+
headers: this.headers()
|
|
41
|
+
});
|
|
42
|
+
if (!res.ok) throw new Error(`SeedhaPe: getOrderStatus failed \u2014 ${res.status}`);
|
|
43
|
+
return res.json();
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Show payment modal in the browser with QR code + polling.
|
|
47
|
+
* Returns a promise that resolves when payment is verified or expires.
|
|
48
|
+
*/
|
|
49
|
+
async showPayment(options) {
|
|
50
|
+
return new Promise((resolve, reject) => {
|
|
51
|
+
const modal = this.renderModal(options, resolve, reject);
|
|
52
|
+
document.body.appendChild(modal);
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
renderModal(options, resolve, reject) {
|
|
56
|
+
const overlay = document.createElement("div");
|
|
57
|
+
overlay.style.cssText = `
|
|
58
|
+
position: fixed; inset: 0; background: rgba(0,0,0,0.5);
|
|
59
|
+
display: flex; align-items: center; justify-content: center;
|
|
60
|
+
z-index: 99999; font-family: system-ui, sans-serif;
|
|
61
|
+
`;
|
|
62
|
+
const card = document.createElement("div");
|
|
63
|
+
card.style.cssText = `
|
|
64
|
+
background: white; border-radius: 16px; padding: 32px;
|
|
65
|
+
width: 360px; max-width: calc(100vw - 32px); text-align: center;
|
|
66
|
+
box-shadow: 0 25px 50px rgba(0,0,0,0.25);
|
|
67
|
+
`;
|
|
68
|
+
card.innerHTML = `
|
|
69
|
+
<div id="sp-status" style="margin-bottom: 16px;">
|
|
70
|
+
<p style="font-size: 18px; font-weight: 600; color: #111;">Loading payment...</p>
|
|
71
|
+
</div>
|
|
72
|
+
<div id="sp-qr" style="display: flex; justify-content: center; margin-bottom: 16px;"></div>
|
|
73
|
+
<div id="sp-actions"></div>
|
|
74
|
+
<button id="sp-close" style="
|
|
75
|
+
margin-top: 16px; background: none; border: none; color: #999;
|
|
76
|
+
font-size: 13px; cursor: pointer;
|
|
77
|
+
">Close</button>
|
|
78
|
+
`;
|
|
79
|
+
overlay.appendChild(card);
|
|
80
|
+
let pollTimer = null;
|
|
81
|
+
const cleanup = () => {
|
|
82
|
+
if (pollTimer) clearInterval(pollTimer);
|
|
83
|
+
overlay.remove();
|
|
84
|
+
};
|
|
85
|
+
card.querySelector("#sp-close").addEventListener("click", () => {
|
|
86
|
+
cleanup();
|
|
87
|
+
options.onClose?.();
|
|
88
|
+
reject(new Error("Payment closed by user"));
|
|
89
|
+
});
|
|
90
|
+
this.loadOrder(options.orderId, card, options, resolve, reject, cleanup);
|
|
91
|
+
return overlay;
|
|
92
|
+
}
|
|
93
|
+
async loadOrder(orderId, card, options, resolve, reject, cleanup) {
|
|
94
|
+
try {
|
|
95
|
+
const res = await fetch(`${this.baseUrl}/v1/pay/${orderId}`);
|
|
96
|
+
if (!res.ok) throw new Error("Order not found");
|
|
97
|
+
const order = await res.json();
|
|
98
|
+
this.updateModal(card, order);
|
|
99
|
+
if (TERMINAL_STATUSES.includes(order.status)) {
|
|
100
|
+
this.handleTerminal(order.status, order, options, resolve, cleanup);
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
const pollTimer = setInterval(async () => {
|
|
104
|
+
try {
|
|
105
|
+
const statusRes = await fetch(`${this.baseUrl}/v1/pay/${orderId}`);
|
|
106
|
+
if (!statusRes.ok) return;
|
|
107
|
+
const updated = await statusRes.json();
|
|
108
|
+
this.updateModal(card, updated);
|
|
109
|
+
if (TERMINAL_STATUSES.includes(updated.status)) {
|
|
110
|
+
clearInterval(pollTimer);
|
|
111
|
+
this.handleTerminal(updated.status, updated, options, resolve, cleanup);
|
|
112
|
+
}
|
|
113
|
+
} catch {
|
|
114
|
+
}
|
|
115
|
+
}, POLL_INTERVAL_MS);
|
|
116
|
+
} catch (err) {
|
|
117
|
+
reject(err instanceof Error ? err : new Error(String(err)));
|
|
118
|
+
cleanup();
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
updateModal(card, order) {
|
|
122
|
+
const statusEl = card.querySelector("#sp-status");
|
|
123
|
+
const qrEl = card.querySelector("#sp-qr");
|
|
124
|
+
const actionsEl = card.querySelector("#sp-actions");
|
|
125
|
+
if (order.status === "VERIFIED" || order.status === "RESOLVED") {
|
|
126
|
+
statusEl.innerHTML = `
|
|
127
|
+
<div style="font-size: 48px;">\u2705</div>
|
|
128
|
+
<p style="font-size: 18px; font-weight: 600; color: #16a34a; margin-top: 8px;">Payment Verified!</p>
|
|
129
|
+
<p style="font-size: 28px; font-weight: 700; color: #111; margin-top: 4px;">
|
|
130
|
+
\u20B9${(order.amount / 100).toFixed(2)}
|
|
131
|
+
</p>
|
|
132
|
+
`;
|
|
133
|
+
qrEl.innerHTML = "";
|
|
134
|
+
actionsEl.innerHTML = "";
|
|
135
|
+
} else if (order.status === "EXPIRED") {
|
|
136
|
+
statusEl.innerHTML = `
|
|
137
|
+
<p style="font-size: 18px; font-weight: 600; color: #dc2626;">Payment Expired</p>
|
|
138
|
+
`;
|
|
139
|
+
qrEl.innerHTML = "";
|
|
140
|
+
} else {
|
|
141
|
+
statusEl.innerHTML = `
|
|
142
|
+
<p style="font-size: 18px; font-weight: 600; color: #111;">${order.description ?? "Complete Payment"}</p>
|
|
143
|
+
<p style="font-size: 28px; font-weight: 700; color: #111; margin: 8px 0;">
|
|
144
|
+
\u20B9${(order.amount / 100).toFixed(2)}
|
|
145
|
+
</p>
|
|
146
|
+
`;
|
|
147
|
+
if (order.qrCode) {
|
|
148
|
+
qrEl.innerHTML = `<img src="${order.qrCode}" width="200" height="200" style="border-radius: 8px;" />`;
|
|
149
|
+
}
|
|
150
|
+
actionsEl.innerHTML = `
|
|
151
|
+
<a href="${order.upiUri}" style="
|
|
152
|
+
display: inline-block; background: #16a34a; color: white;
|
|
153
|
+
padding: 12px 24px; border-radius: 10px; text-decoration: none;
|
|
154
|
+
font-weight: 600; font-size: 15px; margin-bottom: 8px;
|
|
155
|
+
">Open UPI App</a>
|
|
156
|
+
<p style="font-size: 12px; color: #999; margin-top: 8px;">
|
|
157
|
+
\u23F3 Waiting for payment...
|
|
158
|
+
</p>
|
|
159
|
+
`;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
handleTerminal(status, order, options, resolve, cleanup) {
|
|
163
|
+
const result = {
|
|
164
|
+
orderId: order.id,
|
|
165
|
+
status: order.status,
|
|
166
|
+
amount: order.amount
|
|
167
|
+
};
|
|
168
|
+
if (status === "VERIFIED" || status === "RESOLVED") {
|
|
169
|
+
options.onSuccess?.(result);
|
|
170
|
+
setTimeout(() => {
|
|
171
|
+
cleanup();
|
|
172
|
+
resolve(result);
|
|
173
|
+
}, 2e3);
|
|
174
|
+
} else if (status === "EXPIRED") {
|
|
175
|
+
options.onExpired?.(order.id);
|
|
176
|
+
cleanup();
|
|
177
|
+
resolve(result);
|
|
178
|
+
} else {
|
|
179
|
+
cleanup();
|
|
180
|
+
resolve(result);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
headers() {
|
|
184
|
+
return {
|
|
185
|
+
"Content-Type": "application/json",
|
|
186
|
+
Authorization: `Bearer ${this.apiKey}`
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
export {
|
|
191
|
+
SeedhaPe
|
|
192
|
+
};
|
|
193
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/client.ts"],"sourcesContent":["import type {\n SeedhaPeConfig,\n CreateOrderOptions,\n OrderData,\n PaymentResult,\n ShowPaymentOptions,\n} from './types.js';\n\nconst DEFAULT_BASE_URL = 'https://api.seedhape.com';\nconst POLL_INTERVAL_MS = 3000;\nconst TERMINAL_STATUSES = ['VERIFIED', 'EXPIRED', 'REJECTED', 'RESOLVED'] as const;\n\nexport class SeedhaPe {\n private readonly apiKey: string;\n private readonly baseUrl: string;\n\n constructor(config: SeedhaPeConfig) {\n if (!config.apiKey) throw new Error('SeedhaPe: apiKey is required');\n this.apiKey = config.apiKey;\n this.baseUrl = config.baseUrl ?? DEFAULT_BASE_URL;\n }\n\n /**\n * Create a new payment order.\n *\n * If `expectedSenderName` is provided it is forwarded as\n * `metadata.expectedSenderName` so the matching engine can verify the\n * payment using the payer's name even when the UPI transaction note\n * doesn't contain the order ID.\n */\n async createOrder(options: CreateOrderOptions): Promise<OrderData> {\n const { expectedSenderName, metadata, ...rest } = options;\n const mergedMetadata = expectedSenderName\n ? { ...metadata, expectedSenderName }\n : metadata;\n\n const res = await fetch(`${this.baseUrl}/v1/orders`, {\n method: 'POST',\n headers: this.headers(),\n body: JSON.stringify(mergedMetadata ? { ...rest, metadata: mergedMetadata } : rest),\n });\n\n if (!res.ok) {\n const body = await res.json().catch(() => ({}));\n throw new Error(`SeedhaPe: createOrder failed — ${body.error ?? res.status}`);\n }\n\n return res.json() as Promise<OrderData>;\n }\n\n /**\n * Get order status.\n */\n async getOrderStatus(orderId: string): Promise<PaymentResult> {\n const res = await fetch(`${this.baseUrl}/v1/orders/${orderId}/status`, {\n headers: this.headers(),\n });\n\n if (!res.ok) throw new Error(`SeedhaPe: getOrderStatus failed — ${res.status}`);\n return res.json() as Promise<PaymentResult>;\n }\n\n /**\n * Show payment modal in the browser with QR code + polling.\n * Returns a promise that resolves when payment is verified or expires.\n */\n async showPayment(options: ShowPaymentOptions): Promise<PaymentResult> {\n return new Promise((resolve, reject) => {\n const modal = this.renderModal(options, resolve, reject);\n document.body.appendChild(modal);\n });\n }\n\n private renderModal(\n options: ShowPaymentOptions,\n resolve: (result: PaymentResult) => void,\n reject: (err: Error) => void,\n ): HTMLElement {\n const overlay = document.createElement('div');\n overlay.style.cssText = `\n position: fixed; inset: 0; background: rgba(0,0,0,0.5);\n display: flex; align-items: center; justify-content: center;\n z-index: 99999; font-family: system-ui, sans-serif;\n `;\n\n const card = document.createElement('div');\n card.style.cssText = `\n background: white; border-radius: 16px; padding: 32px;\n width: 360px; max-width: calc(100vw - 32px); text-align: center;\n box-shadow: 0 25px 50px rgba(0,0,0,0.25);\n `;\n\n card.innerHTML = `\n <div id=\"sp-status\" style=\"margin-bottom: 16px;\">\n <p style=\"font-size: 18px; font-weight: 600; color: #111;\">Loading payment...</p>\n </div>\n <div id=\"sp-qr\" style=\"display: flex; justify-content: center; margin-bottom: 16px;\"></div>\n <div id=\"sp-actions\"></div>\n <button id=\"sp-close\" style=\"\n margin-top: 16px; background: none; border: none; color: #999;\n font-size: 13px; cursor: pointer;\n \">Close</button>\n `;\n\n overlay.appendChild(card);\n\n let pollTimer: ReturnType<typeof setInterval> | null = null;\n\n const cleanup = () => {\n if (pollTimer) clearInterval(pollTimer);\n overlay.remove();\n };\n\n card.querySelector('#sp-close')!.addEventListener('click', () => {\n cleanup();\n options.onClose?.();\n reject(new Error('Payment closed by user'));\n });\n\n // Load order and start polling\n this.loadOrder(options.orderId, card, options, resolve, reject, cleanup);\n\n return overlay;\n }\n\n private async loadOrder(\n orderId: string,\n card: HTMLElement,\n options: ShowPaymentOptions,\n resolve: (result: PaymentResult) => void,\n reject: (err: Error) => void,\n cleanup: () => void,\n ) {\n try {\n // Fetch order data (public endpoint)\n const res = await fetch(`${this.baseUrl}/v1/pay/${orderId}`);\n if (!res.ok) throw new Error('Order not found');\n const order = await res.json() as OrderData;\n\n this.updateModal(card, order);\n\n if (TERMINAL_STATUSES.includes(order.status as (typeof TERMINAL_STATUSES)[number])) {\n this.handleTerminal(order.status, order, options, resolve, cleanup);\n return;\n }\n\n // Start polling\n const pollTimer = setInterval(async () => {\n try {\n const statusRes = await fetch(`${this.baseUrl}/v1/pay/${orderId}`);\n if (!statusRes.ok) return;\n const updated = await statusRes.json() as OrderData;\n\n this.updateModal(card, updated);\n\n if (TERMINAL_STATUSES.includes(updated.status as (typeof TERMINAL_STATUSES)[number])) {\n clearInterval(pollTimer);\n this.handleTerminal(updated.status, updated, options, resolve, cleanup);\n }\n } catch {\n // ignore\n }\n }, POLL_INTERVAL_MS);\n } catch (err) {\n reject(err instanceof Error ? err : new Error(String(err)));\n cleanup();\n }\n }\n\n private updateModal(card: HTMLElement, order: OrderData) {\n const statusEl = card.querySelector('#sp-status')!;\n const qrEl = card.querySelector('#sp-qr')!;\n const actionsEl = card.querySelector('#sp-actions')!;\n\n if (order.status === 'VERIFIED' || order.status === 'RESOLVED') {\n statusEl.innerHTML = `\n <div style=\"font-size: 48px;\">✅</div>\n <p style=\"font-size: 18px; font-weight: 600; color: #16a34a; margin-top: 8px;\">Payment Verified!</p>\n <p style=\"font-size: 28px; font-weight: 700; color: #111; margin-top: 4px;\">\n ₹${(order.amount / 100).toFixed(2)}\n </p>\n `;\n qrEl.innerHTML = '';\n actionsEl.innerHTML = '';\n } else if (order.status === 'EXPIRED') {\n statusEl.innerHTML = `\n <p style=\"font-size: 18px; font-weight: 600; color: #dc2626;\">Payment Expired</p>\n `;\n qrEl.innerHTML = '';\n } else {\n statusEl.innerHTML = `\n <p style=\"font-size: 18px; font-weight: 600; color: #111;\">${order.description ?? 'Complete Payment'}</p>\n <p style=\"font-size: 28px; font-weight: 700; color: #111; margin: 8px 0;\">\n ₹${(order.amount / 100).toFixed(2)}\n </p>\n `;\n\n if (order.qrCode) {\n qrEl.innerHTML = `<img src=\"${order.qrCode}\" width=\"200\" height=\"200\" style=\"border-radius: 8px;\" />`;\n }\n\n actionsEl.innerHTML = `\n <a href=\"${order.upiUri}\" style=\"\n display: inline-block; background: #16a34a; color: white;\n padding: 12px 24px; border-radius: 10px; text-decoration: none;\n font-weight: 600; font-size: 15px; margin-bottom: 8px;\n \">Open UPI App</a>\n <p style=\"font-size: 12px; color: #999; margin-top: 8px;\">\n ⏳ Waiting for payment...\n </p>\n `;\n }\n }\n\n private handleTerminal(\n status: string,\n order: OrderData,\n options: ShowPaymentOptions,\n resolve: (result: PaymentResult) => void,\n cleanup: () => void,\n ) {\n const result: PaymentResult = {\n orderId: order.id,\n status: order.status,\n amount: order.amount,\n };\n\n if (status === 'VERIFIED' || status === 'RESOLVED') {\n options.onSuccess?.(result);\n setTimeout(() => {\n cleanup();\n resolve(result);\n }, 2000);\n } else if (status === 'EXPIRED') {\n options.onExpired?.(order.id);\n cleanup();\n resolve(result);\n } else {\n cleanup();\n resolve(result);\n }\n }\n\n private headers(): Record<string, string> {\n return {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.apiKey}`,\n };\n }\n}\n"],"mappings":"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","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@seedhape/sdk",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "SeedhaPe JavaScript SDK — drop-in UPI payment integration",
|
|
5
|
+
"exports": {
|
|
6
|
+
".": {
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"import": "./dist/index.mjs",
|
|
9
|
+
"require": "./dist/index.cjs"
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
"main": "./dist/index.cjs",
|
|
13
|
+
"module": "./dist/index.mjs",
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"files": [
|
|
16
|
+
"dist"
|
|
17
|
+
],
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"@seedhape/shared": "0.0.0"
|
|
20
|
+
},
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"tsup": "^8.3.5",
|
|
23
|
+
"typescript": "^5.7.2",
|
|
24
|
+
"@seedhape/typescript-config": "0.0.0"
|
|
25
|
+
},
|
|
26
|
+
"peerDependencies": {
|
|
27
|
+
"zod": "^3.0.0"
|
|
28
|
+
},
|
|
29
|
+
"scripts": {
|
|
30
|
+
"build": "tsup",
|
|
31
|
+
"dev": "tsup --watch",
|
|
32
|
+
"typecheck": "tsc --noEmit",
|
|
33
|
+
"clean": "rm -rf dist"
|
|
34
|
+
}
|
|
35
|
+
}
|