@proofrails/sdk 1.0.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 +412 -0
- package/dist/index.d.mts +546 -0
- package/dist/index.d.ts +546 -0
- package/dist/index.js +751 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +730 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +66 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,730 @@
|
|
|
1
|
+
// src/types/common.ts
|
|
2
|
+
var ProofRailsError = class extends Error {
|
|
3
|
+
constructor(message, code, statusCode, details) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.code = code;
|
|
6
|
+
this.statusCode = statusCode;
|
|
7
|
+
this.details = details;
|
|
8
|
+
this.name = "ProofRailsError";
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
// src/client.ts
|
|
13
|
+
var APIClient = class {
|
|
14
|
+
constructor(config = {}) {
|
|
15
|
+
this.rateLimitInfo = null;
|
|
16
|
+
const envBaseUrl = typeof process !== "undefined" && process.env?.NEXT_PUBLIC_PROOFRAILS_BASE_URL;
|
|
17
|
+
this.baseUrl = config.baseUrl || envBaseUrl || "http://127.0.0.1:8000";
|
|
18
|
+
this.apiKey = config.apiKey;
|
|
19
|
+
this.adminToken = config.adminToken;
|
|
20
|
+
this.timeout = config.timeout || 3e4;
|
|
21
|
+
this.retries = config.retries ?? 3;
|
|
22
|
+
this.retryDelay = config.retryDelay ?? 1e3;
|
|
23
|
+
}
|
|
24
|
+
getRateLimitInfo() {
|
|
25
|
+
return this.rateLimitInfo;
|
|
26
|
+
}
|
|
27
|
+
async get(endpoint, options = {}) {
|
|
28
|
+
return this.request("GET", endpoint, void 0, options);
|
|
29
|
+
}
|
|
30
|
+
async post(endpoint, body, options = {}) {
|
|
31
|
+
return this.request("POST", endpoint, body, options);
|
|
32
|
+
}
|
|
33
|
+
async put(endpoint, body, options = {}) {
|
|
34
|
+
return this.request("PUT", endpoint, body, options);
|
|
35
|
+
}
|
|
36
|
+
async delete(endpoint, options = {}) {
|
|
37
|
+
return this.request("DELETE", endpoint, void 0, options);
|
|
38
|
+
}
|
|
39
|
+
async request(method, endpoint, body, options = {}, attempt = 0) {
|
|
40
|
+
const url = `${this.baseUrl}${endpoint}`;
|
|
41
|
+
const headers = {
|
|
42
|
+
"Content-Type": "application/json",
|
|
43
|
+
...options.headers || {}
|
|
44
|
+
};
|
|
45
|
+
if (this.apiKey) {
|
|
46
|
+
headers["X-API-Key"] = this.apiKey;
|
|
47
|
+
}
|
|
48
|
+
if (this.adminToken) {
|
|
49
|
+
headers["Authorization"] = `Bearer ${this.adminToken}`;
|
|
50
|
+
}
|
|
51
|
+
const controller = new AbortController();
|
|
52
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
53
|
+
try {
|
|
54
|
+
console.log(`[ProofRails SDK] ${method} ${url}${attempt > 0 ? ` (retry ${attempt}/${this.retries})` : ""}`);
|
|
55
|
+
const response = await fetch(url, {
|
|
56
|
+
method,
|
|
57
|
+
headers,
|
|
58
|
+
body: body ? JSON.stringify(body) : void 0,
|
|
59
|
+
signal: controller.signal,
|
|
60
|
+
...options
|
|
61
|
+
});
|
|
62
|
+
clearTimeout(timeoutId);
|
|
63
|
+
this.extractRateLimitInfo(response);
|
|
64
|
+
if (!response.ok) {
|
|
65
|
+
const errorData = await response.json().catch(() => ({}));
|
|
66
|
+
const error = new ProofRailsError(
|
|
67
|
+
errorData.message || errorData.detail || `HTTP ${response.status}: ${response.statusText}`,
|
|
68
|
+
errorData.code,
|
|
69
|
+
response.status,
|
|
70
|
+
errorData
|
|
71
|
+
);
|
|
72
|
+
if (this.shouldRetry(response.status, attempt)) {
|
|
73
|
+
return this.retryRequest(method, endpoint, body, options, attempt);
|
|
74
|
+
}
|
|
75
|
+
throw error;
|
|
76
|
+
}
|
|
77
|
+
return await response.json();
|
|
78
|
+
} catch (error) {
|
|
79
|
+
clearTimeout(timeoutId);
|
|
80
|
+
if (error instanceof ProofRailsError) {
|
|
81
|
+
if (this.shouldRetry(0, attempt)) {
|
|
82
|
+
return this.retryRequest(method, endpoint, body, options, attempt);
|
|
83
|
+
}
|
|
84
|
+
throw error;
|
|
85
|
+
}
|
|
86
|
+
if (error instanceof Error) {
|
|
87
|
+
if (error.name === "AbortError") {
|
|
88
|
+
throw new ProofRailsError(`Request timeout after ${this.timeout}ms`, "TIMEOUT");
|
|
89
|
+
}
|
|
90
|
+
if (this.shouldRetry(0, attempt)) {
|
|
91
|
+
return this.retryRequest(method, endpoint, body, options, attempt);
|
|
92
|
+
}
|
|
93
|
+
throw new ProofRailsError(error.message, "NETWORK_ERROR");
|
|
94
|
+
}
|
|
95
|
+
throw new ProofRailsError("Unknown error occurred", "UNKNOWN_ERROR");
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
shouldRetry(statusCode, attempt) {
|
|
99
|
+
if (attempt >= this.retries) return false;
|
|
100
|
+
if (statusCode === 0) return true;
|
|
101
|
+
if (statusCode >= 500 && statusCode < 600) return true;
|
|
102
|
+
if (statusCode === 429) return true;
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
async retryRequest(method, endpoint, body, options, attempt) {
|
|
106
|
+
const delay = this.retryDelay * Math.pow(2, attempt);
|
|
107
|
+
console.log(`[ProofRails SDK] Retrying in ${delay}ms...`);
|
|
108
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
109
|
+
return this.request(method, endpoint, body, options, attempt + 1);
|
|
110
|
+
}
|
|
111
|
+
extractRateLimitInfo(response) {
|
|
112
|
+
const limit = response.headers.get("X-RateLimit-Limit");
|
|
113
|
+
const remaining = response.headers.get("X-RateLimit-Remaining");
|
|
114
|
+
const reset = response.headers.get("X-RateLimit-Reset");
|
|
115
|
+
if (limit && remaining && reset) {
|
|
116
|
+
const resetTimestamp = parseInt(reset, 10);
|
|
117
|
+
this.rateLimitInfo = {
|
|
118
|
+
limit: parseInt(limit, 10),
|
|
119
|
+
remaining: parseInt(remaining, 10),
|
|
120
|
+
reset: resetTimestamp,
|
|
121
|
+
resetDate: new Date(resetTimestamp * 1e3)
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
setApiKey(apiKey) {
|
|
126
|
+
this.apiKey = apiKey;
|
|
127
|
+
}
|
|
128
|
+
setAdminToken(adminToken) {
|
|
129
|
+
this.adminToken = adminToken;
|
|
130
|
+
}
|
|
131
|
+
getBaseUrl() {
|
|
132
|
+
return this.baseUrl;
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
// src/modules/projects.ts
|
|
137
|
+
var ProjectsModule = class {
|
|
138
|
+
constructor(client) {
|
|
139
|
+
this.client = client;
|
|
140
|
+
}
|
|
141
|
+
async getInfo() {
|
|
142
|
+
return this.client.get("/v1/whoami");
|
|
143
|
+
}
|
|
144
|
+
async rotateKey() {
|
|
145
|
+
return this.client.post("/v1/api-keys/rotate");
|
|
146
|
+
}
|
|
147
|
+
async create(options = {}) {
|
|
148
|
+
return this.client.post("/v1/public/api-keys", options);
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
var AdminModule = class {
|
|
152
|
+
constructor(client) {
|
|
153
|
+
this.client = client;
|
|
154
|
+
}
|
|
155
|
+
async createKey(options) {
|
|
156
|
+
return this.client.post("/v1/admin/api-keys", options);
|
|
157
|
+
}
|
|
158
|
+
async deleteKey(keyId) {
|
|
159
|
+
return this.client.delete(`/v1/admin/api-keys/${keyId}`);
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
// src/modules/receipts.ts
|
|
164
|
+
var ReceiptsModule = class {
|
|
165
|
+
constructor(client) {
|
|
166
|
+
this.client = client;
|
|
167
|
+
}
|
|
168
|
+
async create(options) {
|
|
169
|
+
const payload = {
|
|
170
|
+
tip_tx_hash: options.transactionHash,
|
|
171
|
+
chain: options.chain,
|
|
172
|
+
amount: String(options.amount),
|
|
173
|
+
currency: options.currency,
|
|
174
|
+
sender_wallet: options.sender,
|
|
175
|
+
receiver_wallet: options.receiver,
|
|
176
|
+
reference: options.reference,
|
|
177
|
+
callback_url: options.callbackUrl
|
|
178
|
+
};
|
|
179
|
+
const response = await this.client.post(
|
|
180
|
+
"/v1/iso/record-tip",
|
|
181
|
+
payload
|
|
182
|
+
);
|
|
183
|
+
return {
|
|
184
|
+
id: response.receipt_id,
|
|
185
|
+
status: response.status,
|
|
186
|
+
transactionHash: options.transactionHash,
|
|
187
|
+
chain: options.chain,
|
|
188
|
+
amount: String(options.amount),
|
|
189
|
+
currency: options.currency,
|
|
190
|
+
sender: options.sender,
|
|
191
|
+
receiver: options.receiver,
|
|
192
|
+
reference: options.reference,
|
|
193
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
async get(receiptId) {
|
|
197
|
+
return this.client.get(`/v1/iso/receipts/${receiptId}`);
|
|
198
|
+
}
|
|
199
|
+
async list(options = {}) {
|
|
200
|
+
const params = new URLSearchParams();
|
|
201
|
+
if (options.limit) params.append("limit", String(options.limit));
|
|
202
|
+
if (options.page) params.append("page", String(options.page));
|
|
203
|
+
if (options.status) params.append("status", options.status);
|
|
204
|
+
const query = params.toString();
|
|
205
|
+
const endpoint = `/v1/iso/receipts${query ? `?${query}` : ""}`;
|
|
206
|
+
const response = await this.client.get(endpoint);
|
|
207
|
+
const items = Array.isArray(response) ? response : response.items || [];
|
|
208
|
+
return {
|
|
209
|
+
items,
|
|
210
|
+
total: items.length,
|
|
211
|
+
page: options.page || 1,
|
|
212
|
+
limit: options.limit || 10
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
async getArtifacts(receiptId) {
|
|
216
|
+
const messages = await this.client.get(`/v1/iso/messages/${receiptId}`);
|
|
217
|
+
const baseUrl = this.client.getBaseUrl();
|
|
218
|
+
const artifacts = {};
|
|
219
|
+
messages.forEach((msg) => {
|
|
220
|
+
switch (msg.type) {
|
|
221
|
+
case "pain.001":
|
|
222
|
+
artifacts.pain001Url = msg.url;
|
|
223
|
+
break;
|
|
224
|
+
case "pain.002":
|
|
225
|
+
artifacts.pain002Url = msg.url;
|
|
226
|
+
break;
|
|
227
|
+
case "pain.007":
|
|
228
|
+
artifacts.pain007Url = msg.url;
|
|
229
|
+
break;
|
|
230
|
+
case "pain.008":
|
|
231
|
+
artifacts.pain008Url = msg.url;
|
|
232
|
+
break;
|
|
233
|
+
case "camt.054":
|
|
234
|
+
artifacts.camt054Url = msg.url;
|
|
235
|
+
break;
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
artifacts.bundleUrl = `${baseUrl}/files/${receiptId}/evidence.zip`;
|
|
239
|
+
artifacts.manifestUrl = `${baseUrl}/files/${receiptId}/manifest.json`;
|
|
240
|
+
return artifacts;
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
// src/modules/verification.ts
|
|
245
|
+
var VerificationModule = class {
|
|
246
|
+
constructor(client) {
|
|
247
|
+
this.client = client;
|
|
248
|
+
}
|
|
249
|
+
async byReceiptId(receiptId) {
|
|
250
|
+
const receipt = await this.client.get(`/v1/iso/receipts/${receiptId}`);
|
|
251
|
+
return {
|
|
252
|
+
valid: receipt.status === "anchored",
|
|
253
|
+
bundleHash: receipt.bundle_hash || receipt.bundleHash || "",
|
|
254
|
+
onChain: !!receipt.anchor_tx || !!receipt.anchorTx,
|
|
255
|
+
anchorTx: receipt.anchor_tx || receipt.anchorTx,
|
|
256
|
+
timestamp: receipt.anchored_at || receipt.anchoredAt
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
async byUrl(bundleUrl) {
|
|
260
|
+
return this.client.post("/v1/iso/verify", {
|
|
261
|
+
bundle_url: bundleUrl
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
async byHash(bundleHash) {
|
|
265
|
+
return this.client.post("/v1/iso/verify", {
|
|
266
|
+
bundle_hash: bundleHash
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
async getProof(receiptId) {
|
|
270
|
+
const receipt = await this.client.get(`/v1/iso/receipts/${receiptId}`);
|
|
271
|
+
return {
|
|
272
|
+
receiptId,
|
|
273
|
+
bundleHash: receipt.bundle_hash || receipt.bundleHash,
|
|
274
|
+
anchorTx: receipt.anchor_tx || receipt.anchorTx,
|
|
275
|
+
blockNumber: receipt.block_number || 0,
|
|
276
|
+
timestamp: receipt.anchored_at || receipt.anchoredAt,
|
|
277
|
+
signature: receipt.signature || "",
|
|
278
|
+
network: receipt.chain,
|
|
279
|
+
contractAddress: receipt.contract_address || ""
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
// src/modules/statements.ts
|
|
285
|
+
var StatementsModule = class {
|
|
286
|
+
constructor(client) {
|
|
287
|
+
this.client = client;
|
|
288
|
+
}
|
|
289
|
+
async intraday(options = {}) {
|
|
290
|
+
const response = await this.client.post("/v1/iso/statement/camt052", {
|
|
291
|
+
date_from: options.dateFrom,
|
|
292
|
+
date_to: options.dateTo,
|
|
293
|
+
account_id: options.accountId
|
|
294
|
+
});
|
|
295
|
+
return {
|
|
296
|
+
type: "camt.052",
|
|
297
|
+
url: response.url,
|
|
298
|
+
downloadUrl: response.download_url || response.url,
|
|
299
|
+
messageId: response.message_id || response.messageId,
|
|
300
|
+
createdAt: response.created_at || (/* @__PURE__ */ new Date()).toISOString()
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
async endOfDay(options = {}) {
|
|
304
|
+
const response = await this.client.post("/v1/iso/statement/camt053", {
|
|
305
|
+
date: options.dateTo || options.dateFrom,
|
|
306
|
+
account_id: options.accountId
|
|
307
|
+
});
|
|
308
|
+
return {
|
|
309
|
+
type: "camt.053",
|
|
310
|
+
url: response.url,
|
|
311
|
+
downloadUrl: response.download_url || response.url,
|
|
312
|
+
messageId: response.message_id || response.messageId,
|
|
313
|
+
createdAt: response.created_at || (/* @__PURE__ */ new Date()).toISOString()
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
};
|
|
317
|
+
|
|
318
|
+
// src/modules/events.ts
|
|
319
|
+
var EventsModule = class {
|
|
320
|
+
constructor(client) {
|
|
321
|
+
this.client = client;
|
|
322
|
+
}
|
|
323
|
+
listen(receiptId, callback) {
|
|
324
|
+
const baseUrl = this.client.getBaseUrl();
|
|
325
|
+
const url = `${baseUrl}/v1/iso/events/${receiptId}`;
|
|
326
|
+
const eventSource = new EventSource(url);
|
|
327
|
+
eventSource.onmessage = (event) => {
|
|
328
|
+
try {
|
|
329
|
+
const data = JSON.parse(event.data);
|
|
330
|
+
callback({
|
|
331
|
+
id: receiptId,
|
|
332
|
+
status: data.status,
|
|
333
|
+
anchorTx: data.anchor_tx || data.anchorTx,
|
|
334
|
+
bundleHash: data.bundle_hash || data.bundleHash,
|
|
335
|
+
timestamp: data.timestamp || (/* @__PURE__ */ new Date()).toISOString()
|
|
336
|
+
});
|
|
337
|
+
} catch (error) {
|
|
338
|
+
console.error("Failed to parse SSE event:", error);
|
|
339
|
+
}
|
|
340
|
+
};
|
|
341
|
+
eventSource.onerror = (error) => {
|
|
342
|
+
console.error("SSE connection error:", error);
|
|
343
|
+
eventSource.close();
|
|
344
|
+
};
|
|
345
|
+
return {
|
|
346
|
+
stop: () => eventSource.close()
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
};
|
|
350
|
+
|
|
351
|
+
// src/modules/embed.ts
|
|
352
|
+
var EmbedModule = class {
|
|
353
|
+
constructor(client) {
|
|
354
|
+
this.client = client;
|
|
355
|
+
}
|
|
356
|
+
widget(receiptId, options = {}) {
|
|
357
|
+
const baseUrl = this.client.getBaseUrl();
|
|
358
|
+
const theme = options.theme || "light";
|
|
359
|
+
const width = options.width || "100%";
|
|
360
|
+
const height = options.height || "400px";
|
|
361
|
+
const embedUrl = `${baseUrl}/embed/receipt?rid=${receiptId}&theme=${theme}`;
|
|
362
|
+
const iframeHtml = `<iframe src="${embedUrl}" width="${width}" height="${height}" frameborder="0" style="border: none;"></iframe>`;
|
|
363
|
+
return {
|
|
364
|
+
iframeHtml,
|
|
365
|
+
embedUrl
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
fullPage(receiptId) {
|
|
369
|
+
const baseUrl = this.client.getBaseUrl();
|
|
370
|
+
return `${baseUrl}/receipt/${receiptId}`;
|
|
371
|
+
}
|
|
372
|
+
};
|
|
373
|
+
|
|
374
|
+
// src/templates/payment.ts
|
|
375
|
+
async function createPaymentReceipt(receiptsModule, options) {
|
|
376
|
+
return receiptsModule.create({
|
|
377
|
+
transactionHash: options.transactionHash,
|
|
378
|
+
chain: options.chain || "coston2",
|
|
379
|
+
// Smart default
|
|
380
|
+
amount: options.amount,
|
|
381
|
+
currency: options.currency || "C2FLR",
|
|
382
|
+
// Smart default
|
|
383
|
+
sender: options.senderWallet || options.from,
|
|
384
|
+
receiver: options.receiverWallet || options.to,
|
|
385
|
+
reference: `Payment: ${options.purpose} (Tx: ${options.transactionHash})`
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// src/templates/donation.ts
|
|
390
|
+
async function createDonationReceipt(receiptsModule, options) {
|
|
391
|
+
return receiptsModule.create({
|
|
392
|
+
transactionHash: options.transactionHash,
|
|
393
|
+
chain: "coston2",
|
|
394
|
+
amount: options.amount,
|
|
395
|
+
currency: "FLR",
|
|
396
|
+
sender: options.donorWallet || options.donor,
|
|
397
|
+
receiver: options.organizationWallet || options.organization,
|
|
398
|
+
reference: `Donation: ${options.campaign} (Donor: ${options.donor}, Organization: ${options.organization})`
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// src/templates/escrow.ts
|
|
403
|
+
async function createEscrowReceipt(receiptsModule, options) {
|
|
404
|
+
return receiptsModule.create({
|
|
405
|
+
transactionHash: options.transactionHash,
|
|
406
|
+
chain: "coston2",
|
|
407
|
+
amount: options.amount,
|
|
408
|
+
currency: "FLR",
|
|
409
|
+
sender: options.buyerWallet || options.buyer,
|
|
410
|
+
receiver: options.sellerWallet || options.seller,
|
|
411
|
+
reference: `Escrow Release: ${options.escrowId} - ${options.releaseReason} (Buyer: ${options.buyer}, Seller: ${options.seller})`
|
|
412
|
+
});
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// src/templates/grant.ts
|
|
416
|
+
async function createGrantReceipt(receiptsModule, options) {
|
|
417
|
+
return receiptsModule.create({
|
|
418
|
+
transactionHash: options.transactionHash,
|
|
419
|
+
chain: "coston2",
|
|
420
|
+
amount: options.amount,
|
|
421
|
+
currency: "FLR",
|
|
422
|
+
sender: options.grantorWallet || options.grantor,
|
|
423
|
+
receiver: options.granteeWallet || options.grantee,
|
|
424
|
+
reference: `Grant: ${options.grantId} - ${options.purpose} (Grantor: ${options.grantor}, Grantee: ${options.grantee})`
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
// src/templates/refund.ts
|
|
429
|
+
async function createRefundReceipt(receiptsModule, options) {
|
|
430
|
+
return receiptsModule.create({
|
|
431
|
+
transactionHash: options.transactionHash,
|
|
432
|
+
chain: "coston2",
|
|
433
|
+
amount: options.amount,
|
|
434
|
+
currency: "FLR",
|
|
435
|
+
sender: options.businessWallet || "Business",
|
|
436
|
+
receiver: options.customerWallet || options.customer,
|
|
437
|
+
reference: `Refund: ${options.originalPayment} - ${options.reason} (Customer: ${options.customer})`
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
// src/sdk.ts
|
|
442
|
+
var ProofRails = class _ProofRails {
|
|
443
|
+
constructor(config = {}) {
|
|
444
|
+
this.client = new APIClient(config);
|
|
445
|
+
this.project = new ProjectsModule(this.client);
|
|
446
|
+
this.admin = new AdminModule(this.client);
|
|
447
|
+
this.receipts = new ReceiptsModule(this.client);
|
|
448
|
+
this.verify = new VerificationModule(this.client);
|
|
449
|
+
this.statements = new StatementsModule(this.client);
|
|
450
|
+
this.events = new EventsModule(this.client);
|
|
451
|
+
this.embed = new EmbedModule(this.client);
|
|
452
|
+
this.templates = {
|
|
453
|
+
payment: (options) => createPaymentReceipt(this.receipts, options),
|
|
454
|
+
donation: (options) => createDonationReceipt(this.receipts, options),
|
|
455
|
+
escrow: (options) => createEscrowReceipt(this.receipts, options),
|
|
456
|
+
grant: (options) => createGrantReceipt(this.receipts, options),
|
|
457
|
+
refund: (options) => createRefundReceipt(this.receipts, options)
|
|
458
|
+
};
|
|
459
|
+
}
|
|
460
|
+
/**
|
|
461
|
+
* Create a new project with API key (self-serve)
|
|
462
|
+
* This is a convenience method for beginners
|
|
463
|
+
*/
|
|
464
|
+
static async createProject(options = {}) {
|
|
465
|
+
const tempClient = new _ProofRails({ network: options.network, baseUrl: options.baseUrl });
|
|
466
|
+
const result = await tempClient.project.create({ label: options.label });
|
|
467
|
+
const apiKey = result.api_key || result.apiKey;
|
|
468
|
+
const projectId = result.project_id || result.projectId;
|
|
469
|
+
const client = new _ProofRails({
|
|
470
|
+
apiKey,
|
|
471
|
+
network: options.network,
|
|
472
|
+
baseUrl: options.baseUrl
|
|
473
|
+
// Propagate baseUrl
|
|
474
|
+
});
|
|
475
|
+
return {
|
|
476
|
+
client,
|
|
477
|
+
apiKey,
|
|
478
|
+
projectId
|
|
479
|
+
};
|
|
480
|
+
}
|
|
481
|
+
/**
|
|
482
|
+
* Update the API key for this client
|
|
483
|
+
*/
|
|
484
|
+
setApiKey(apiKey) {
|
|
485
|
+
this.client.setApiKey(apiKey);
|
|
486
|
+
}
|
|
487
|
+
/**
|
|
488
|
+
* Update the admin token for this client
|
|
489
|
+
*/
|
|
490
|
+
setAdminToken(adminToken) {
|
|
491
|
+
this.client.setAdminToken(adminToken);
|
|
492
|
+
}
|
|
493
|
+
};
|
|
494
|
+
|
|
495
|
+
// src/utils/errors.ts
|
|
496
|
+
function getFriendlyError(error) {
|
|
497
|
+
const errorMsg = error?.message || error?.detail || String(error);
|
|
498
|
+
const statusCode = error?.statusCode || error?.status;
|
|
499
|
+
if (errorMsg.includes("UNIQUE constraint") || errorMsg.includes("already exists")) {
|
|
500
|
+
return {
|
|
501
|
+
title: "\u274C Duplicate Receipt",
|
|
502
|
+
message: "A receipt with this transaction hash already exists.",
|
|
503
|
+
solution: "This transaction has already been recorded. Check your receipts list.",
|
|
504
|
+
learnMore: "https://docs.proofrails.com/errors/duplicate"
|
|
505
|
+
};
|
|
506
|
+
}
|
|
507
|
+
if (errorMsg.includes("insufficient funds") || errorMsg.includes("gas")) {
|
|
508
|
+
return {
|
|
509
|
+
title: "\u274C Middleware Wallet Needs Funding",
|
|
510
|
+
message: "Your ProofRails middleware wallet doesn't have enough tokens to pay gas fees.",
|
|
511
|
+
solution: "Send 0.5-1.0 C2FLR tokens to your middleware wallet address (check your .env ANCHOR_PRIVATE_KEY).",
|
|
512
|
+
learnMore: "https://docs.proofrails.com/setup/funding"
|
|
513
|
+
};
|
|
514
|
+
}
|
|
515
|
+
if (errorMsg.includes("fetch") || errorMsg.includes("network") || errorMsg.includes("ECONNREFUSED")) {
|
|
516
|
+
return {
|
|
517
|
+
title: "\u274C Cannot Connect to Middleware",
|
|
518
|
+
message: "Unable to reach the ProofRails middleware server.",
|
|
519
|
+
solution: "Make sure your middleware is running on the correct port (default: 8000). Check your baseUrl configuration.",
|
|
520
|
+
learnMore: "https://docs.proofrails.com/troubleshooting/connection"
|
|
521
|
+
};
|
|
522
|
+
}
|
|
523
|
+
if (errorMsg.includes("timeout") || errorMsg.includes("TIMEOUT")) {
|
|
524
|
+
return {
|
|
525
|
+
title: "\u23F1\uFE0F Request Timed Out",
|
|
526
|
+
message: "The middleware took too long to respond.",
|
|
527
|
+
solution: "The middleware might be processing a large request or experiencing issues. Try again in a moment.",
|
|
528
|
+
learnMore: "https://docs.proofrails.com/troubleshooting/timeout"
|
|
529
|
+
};
|
|
530
|
+
}
|
|
531
|
+
if (statusCode === 401 || statusCode === 403 || errorMsg.includes("unauthorized") || errorMsg.includes("forbidden")) {
|
|
532
|
+
return {
|
|
533
|
+
title: "\u{1F510} Authentication Failed",
|
|
534
|
+
message: "Your API key is invalid or missing.",
|
|
535
|
+
solution: "Check that you've set your API key correctly. You can create a new one using ProofRails.createProject().",
|
|
536
|
+
learnMore: "https://docs.proofrails.com/setup/api-keys"
|
|
537
|
+
};
|
|
538
|
+
}
|
|
539
|
+
if (statusCode === 404 || errorMsg.includes("not found")) {
|
|
540
|
+
return {
|
|
541
|
+
title: "\u{1F50D} Not Found",
|
|
542
|
+
message: "The requested resource doesn't exist.",
|
|
543
|
+
solution: "Double-check the receipt ID or resource identifier you're trying to access.",
|
|
544
|
+
learnMore: "https://docs.proofrails.com/troubleshooting/not-found"
|
|
545
|
+
};
|
|
546
|
+
}
|
|
547
|
+
if (statusCode === 422 || errorMsg.includes("validation") || errorMsg.includes("invalid")) {
|
|
548
|
+
return {
|
|
549
|
+
title: "\u26A0\uFE0F Invalid Data",
|
|
550
|
+
message: "The data you provided is invalid or incomplete.",
|
|
551
|
+
solution: "Check that all required fields are filled correctly (amount, addresses, transaction hash, etc.).",
|
|
552
|
+
learnMore: "https://docs.proofrails.com/api/validation"
|
|
553
|
+
};
|
|
554
|
+
}
|
|
555
|
+
if (statusCode === 500 || statusCode === 502 || statusCode === 503) {
|
|
556
|
+
return {
|
|
557
|
+
title: "\u{1F527} Server Error",
|
|
558
|
+
message: "The middleware encountered an internal error.",
|
|
559
|
+
solution: "Check the middleware logs for details. The issue might be with database access or blockchain connectivity.",
|
|
560
|
+
learnMore: "https://docs.proofrails.com/troubleshooting/server-errors"
|
|
561
|
+
};
|
|
562
|
+
}
|
|
563
|
+
return {
|
|
564
|
+
title: "\u274C Error",
|
|
565
|
+
message: errorMsg,
|
|
566
|
+
solution: "If this error persists, please check the documentation or contact support.",
|
|
567
|
+
learnMore: "https://docs.proofrails.com/troubleshooting"
|
|
568
|
+
};
|
|
569
|
+
}
|
|
570
|
+
function formatErrorForDisplay(error) {
|
|
571
|
+
const friendly = getFriendlyError(error);
|
|
572
|
+
let output = `${friendly.title}
|
|
573
|
+
${friendly.message}`;
|
|
574
|
+
if (friendly.solution) {
|
|
575
|
+
output += `
|
|
576
|
+
|
|
577
|
+
\u{1F4A1} Solution: ${friendly.solution}`;
|
|
578
|
+
}
|
|
579
|
+
if (friendly.learnMore) {
|
|
580
|
+
output += `
|
|
581
|
+
\u{1F4D6} Learn more: ${friendly.learnMore}`;
|
|
582
|
+
}
|
|
583
|
+
return output;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
// src/utils/chain.ts
|
|
587
|
+
var SUPPORTED_CHAINS = {
|
|
588
|
+
14: {
|
|
589
|
+
id: 14,
|
|
590
|
+
name: "Flare",
|
|
591
|
+
network: "flare",
|
|
592
|
+
currency: "FLR",
|
|
593
|
+
rpcUrl: "https://flare-api.flare.network/ext/C/rpc",
|
|
594
|
+
explorerUrl: "https://flare-explorer.flare.network"
|
|
595
|
+
},
|
|
596
|
+
114: {
|
|
597
|
+
id: 114,
|
|
598
|
+
name: "Coston2",
|
|
599
|
+
network: "coston2",
|
|
600
|
+
currency: "C2FLR",
|
|
601
|
+
rpcUrl: "https://coston2-api.flare.network/ext/C/rpc",
|
|
602
|
+
explorerUrl: "https://coston2-explorer.flare.network"
|
|
603
|
+
}
|
|
604
|
+
};
|
|
605
|
+
function detectNetwork(chainId) {
|
|
606
|
+
if (!chainId) return "coston2";
|
|
607
|
+
const chain = SUPPORTED_CHAINS[chainId];
|
|
608
|
+
return chain?.network || "coston2";
|
|
609
|
+
}
|
|
610
|
+
function detectCurrency(chainId) {
|
|
611
|
+
if (!chainId) return "C2FLR";
|
|
612
|
+
const chain = SUPPORTED_CHAINS[chainId];
|
|
613
|
+
return chain?.currency || "C2FLR";
|
|
614
|
+
}
|
|
615
|
+
function getChainInfo(chainId) {
|
|
616
|
+
return SUPPORTED_CHAINS[chainId] || null;
|
|
617
|
+
}
|
|
618
|
+
function isSupportedChain(chainId) {
|
|
619
|
+
return chainId in SUPPORTED_CHAINS;
|
|
620
|
+
}
|
|
621
|
+
function getExplorerUrl(chainId, txHash) {
|
|
622
|
+
const chain = SUPPORTED_CHAINS[chainId];
|
|
623
|
+
if (!chain) return "";
|
|
624
|
+
return `${chain.explorerUrl}/tx/${txHash}`;
|
|
625
|
+
}
|
|
626
|
+
function getAddressExplorerUrl(chainId, address) {
|
|
627
|
+
const chain = SUPPORTED_CHAINS[chainId];
|
|
628
|
+
if (!chain) return "";
|
|
629
|
+
return `${chain.explorerUrl}/address/${address}`;
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
// src/utils/validation.ts
|
|
633
|
+
function validateAddress(address) {
|
|
634
|
+
if (!address) {
|
|
635
|
+
return { isValid: false, error: "Address is required" };
|
|
636
|
+
}
|
|
637
|
+
if (typeof address !== "string") {
|
|
638
|
+
return { isValid: false, error: "Address must be a string" };
|
|
639
|
+
}
|
|
640
|
+
if (!address.startsWith("0x")) {
|
|
641
|
+
return { isValid: false, error: "Address must start with 0x" };
|
|
642
|
+
}
|
|
643
|
+
if (address.length !== 42) {
|
|
644
|
+
return { isValid: false, error: "Address must be 42 characters long (0x + 40 hex digits)" };
|
|
645
|
+
}
|
|
646
|
+
const hexRegex = /^0x[0-9a-fA-F]{40}$/;
|
|
647
|
+
if (!hexRegex.test(address)) {
|
|
648
|
+
return { isValid: false, error: "Address contains invalid characters (must be hexadecimal)" };
|
|
649
|
+
}
|
|
650
|
+
return { isValid: true };
|
|
651
|
+
}
|
|
652
|
+
function validateAmount(amount) {
|
|
653
|
+
if (amount === "" || amount === null || amount === void 0) {
|
|
654
|
+
return { isValid: false, error: "Amount is required" };
|
|
655
|
+
}
|
|
656
|
+
const numAmount = typeof amount === "string" ? parseFloat(amount) : amount;
|
|
657
|
+
if (isNaN(numAmount)) {
|
|
658
|
+
return { isValid: false, error: "Amount must be a valid number" };
|
|
659
|
+
}
|
|
660
|
+
if (numAmount <= 0) {
|
|
661
|
+
return { isValid: false, error: "Amount must be greater than 0" };
|
|
662
|
+
}
|
|
663
|
+
if (numAmount > Number.MAX_SAFE_INTEGER) {
|
|
664
|
+
return { isValid: false, error: "Amount is too large" };
|
|
665
|
+
}
|
|
666
|
+
return { isValid: true };
|
|
667
|
+
}
|
|
668
|
+
function validateTransactionHash(hash) {
|
|
669
|
+
if (!hash) {
|
|
670
|
+
return { isValid: false, error: "Transaction hash is required" };
|
|
671
|
+
}
|
|
672
|
+
if (typeof hash !== "string") {
|
|
673
|
+
return { isValid: false, error: "Transaction hash must be a string" };
|
|
674
|
+
}
|
|
675
|
+
if (!hash.startsWith("0x")) {
|
|
676
|
+
return { isValid: false, error: "Transaction hash must start with 0x" };
|
|
677
|
+
}
|
|
678
|
+
if (hash.length !== 66) {
|
|
679
|
+
return { isValid: false, error: "Transaction hash must be 66 characters long (0x + 64 hex digits)" };
|
|
680
|
+
}
|
|
681
|
+
const hexRegex = /^0x[0-9a-fA-F]{64}$/;
|
|
682
|
+
if (!hexRegex.test(hash)) {
|
|
683
|
+
return { isValid: false, error: "Transaction hash contains invalid characters (must be hexadecimal)" };
|
|
684
|
+
}
|
|
685
|
+
return { isValid: true };
|
|
686
|
+
}
|
|
687
|
+
function validateApiKey(apiKey) {
|
|
688
|
+
if (!apiKey) {
|
|
689
|
+
return { isValid: false, error: "API key is required" };
|
|
690
|
+
}
|
|
691
|
+
if (typeof apiKey !== "string") {
|
|
692
|
+
return { isValid: false, error: "API key must be a string" };
|
|
693
|
+
}
|
|
694
|
+
if (apiKey.length < 10) {
|
|
695
|
+
return { isValid: false, error: "API key is too short" };
|
|
696
|
+
}
|
|
697
|
+
return { isValid: true };
|
|
698
|
+
}
|
|
699
|
+
function validatePurpose(purpose) {
|
|
700
|
+
if (!purpose) {
|
|
701
|
+
return { isValid: false, error: "Purpose is required" };
|
|
702
|
+
}
|
|
703
|
+
if (typeof purpose !== "string") {
|
|
704
|
+
return { isValid: false, error: "Purpose must be a string" };
|
|
705
|
+
}
|
|
706
|
+
if (purpose.trim().length === 0) {
|
|
707
|
+
return { isValid: false, error: "Purpose cannot be empty" };
|
|
708
|
+
}
|
|
709
|
+
if (purpose.length > 500) {
|
|
710
|
+
return { isValid: false, error: "Purpose is too long (max 500 characters)" };
|
|
711
|
+
}
|
|
712
|
+
return { isValid: true };
|
|
713
|
+
}
|
|
714
|
+
function validatePayment(params) {
|
|
715
|
+
const amountCheck = validateAmount(params.amount);
|
|
716
|
+
if (!amountCheck.isValid) return amountCheck;
|
|
717
|
+
const addressCheck = validateAddress(params.to);
|
|
718
|
+
if (!addressCheck.isValid) return addressCheck;
|
|
719
|
+
const purposeCheck = validatePurpose(params.purpose);
|
|
720
|
+
if (!purposeCheck.isValid) return purposeCheck;
|
|
721
|
+
if (params.transactionHash) {
|
|
722
|
+
const hashCheck = validateTransactionHash(params.transactionHash);
|
|
723
|
+
if (!hashCheck.isValid) return hashCheck;
|
|
724
|
+
}
|
|
725
|
+
return { isValid: true };
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
export { ProofRails, ProofRailsError, SUPPORTED_CHAINS, ProofRails as default, detectCurrency, detectNetwork, formatErrorForDisplay, getAddressExplorerUrl, getChainInfo, getExplorerUrl, getFriendlyError, isSupportedChain, validateAddress, validateAmount, validateApiKey, validatePayment, validatePurpose, validateTransactionHash };
|
|
729
|
+
//# sourceMappingURL=index.mjs.map
|
|
730
|
+
//# sourceMappingURL=index.mjs.map
|