@soledgic/sdk 0.2.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 +212 -0
- package/dist/index.d.mts +1636 -0
- package/dist/index.d.ts +1636 -0
- package/dist/index.js +2227 -0
- package/dist/index.mjs +2183 -0
- package/package.json +54 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,2183 @@
|
|
|
1
|
+
// src/errors.ts
|
|
2
|
+
var SoledgicError = class extends Error {
|
|
3
|
+
constructor(message, status, details, code) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.status = status;
|
|
6
|
+
this.details = details;
|
|
7
|
+
this.code = code;
|
|
8
|
+
this.name = "SoledgicError";
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
var ValidationError = class extends SoledgicError {
|
|
12
|
+
constructor(message, details, code = "VALIDATION_ERROR") {
|
|
13
|
+
super(message, 400, details, code);
|
|
14
|
+
this.name = "ValidationError";
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
var AuthenticationError = class extends SoledgicError {
|
|
18
|
+
constructor(message = "Invalid API key", details, code = "AUTHENTICATION_ERROR") {
|
|
19
|
+
super(message, 401, details, code);
|
|
20
|
+
this.name = "AuthenticationError";
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
var NotFoundError = class extends SoledgicError {
|
|
24
|
+
constructor(message, details, code = "NOT_FOUND") {
|
|
25
|
+
super(message, 404, details, code);
|
|
26
|
+
this.name = "NotFoundError";
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
var ConflictError = class extends SoledgicError {
|
|
30
|
+
constructor(message, details, code = "CONFLICT") {
|
|
31
|
+
super(message, 409, details, code);
|
|
32
|
+
this.name = "ConflictError";
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
// src/webhooks.ts
|
|
37
|
+
function isArrayBufferView(value) {
|
|
38
|
+
return typeof ArrayBuffer !== "undefined" && ArrayBuffer.isView(value);
|
|
39
|
+
}
|
|
40
|
+
function webhookPayloadToString(payload) {
|
|
41
|
+
if (typeof payload === "string") {
|
|
42
|
+
return payload;
|
|
43
|
+
}
|
|
44
|
+
if (payload instanceof ArrayBuffer) {
|
|
45
|
+
return new TextDecoder().decode(new Uint8Array(payload));
|
|
46
|
+
}
|
|
47
|
+
if (isArrayBufferView(payload)) {
|
|
48
|
+
return new TextDecoder().decode(payload);
|
|
49
|
+
}
|
|
50
|
+
return JSON.stringify(payload);
|
|
51
|
+
}
|
|
52
|
+
function timingSafeEqual(a, b) {
|
|
53
|
+
const aLen = a.length;
|
|
54
|
+
const bLen = b.length;
|
|
55
|
+
const maxLen = Math.max(aLen, bLen);
|
|
56
|
+
let result = aLen ^ bLen;
|
|
57
|
+
for (let i = 0; i < maxLen; i++) {
|
|
58
|
+
const aChar = i < aLen ? a.charCodeAt(i) : 0;
|
|
59
|
+
const bChar = i < bLen ? b.charCodeAt(i) : 0;
|
|
60
|
+
result |= aChar ^ bChar;
|
|
61
|
+
}
|
|
62
|
+
return result === 0;
|
|
63
|
+
}
|
|
64
|
+
async function hmacHex(secret, payload) {
|
|
65
|
+
if (!globalThis.crypto?.subtle) {
|
|
66
|
+
throw new Error("Web Crypto API is not available in this runtime");
|
|
67
|
+
}
|
|
68
|
+
const key = await globalThis.crypto.subtle.importKey(
|
|
69
|
+
"raw",
|
|
70
|
+
new TextEncoder().encode(secret),
|
|
71
|
+
{ name: "HMAC", hash: "SHA-256" },
|
|
72
|
+
false,
|
|
73
|
+
["sign"]
|
|
74
|
+
);
|
|
75
|
+
const signature = await globalThis.crypto.subtle.sign("HMAC", key, new TextEncoder().encode(payload));
|
|
76
|
+
return Array.from(new Uint8Array(signature)).map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
77
|
+
}
|
|
78
|
+
function parseWebhookSignatureHeader(signatureHeader) {
|
|
79
|
+
if (!signatureHeader) {
|
|
80
|
+
return { timestamp: null, v1Signatures: [] };
|
|
81
|
+
}
|
|
82
|
+
let timestamp = null;
|
|
83
|
+
const v1Signatures = [];
|
|
84
|
+
for (const part of signatureHeader.split(",")) {
|
|
85
|
+
const [key, value] = part.trim().split("=");
|
|
86
|
+
if (!key || !value) continue;
|
|
87
|
+
if (key === "t") {
|
|
88
|
+
const numeric = Number(value);
|
|
89
|
+
timestamp = Number.isFinite(numeric) ? numeric : null;
|
|
90
|
+
} else if (key === "v1") {
|
|
91
|
+
v1Signatures.push(value);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return { timestamp, v1Signatures };
|
|
95
|
+
}
|
|
96
|
+
function toEpochSeconds(value) {
|
|
97
|
+
if (value instanceof Date) {
|
|
98
|
+
return Math.floor(value.getTime() / 1e3);
|
|
99
|
+
}
|
|
100
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
101
|
+
return Math.floor(value);
|
|
102
|
+
}
|
|
103
|
+
return Math.floor(Date.now() / 1e3);
|
|
104
|
+
}
|
|
105
|
+
async function verifyWebhookSignature(payload, signatureHeader, secret, options = {}) {
|
|
106
|
+
const payloadString = webhookPayloadToString(payload);
|
|
107
|
+
const parsed = parseWebhookSignatureHeader(signatureHeader);
|
|
108
|
+
if (parsed.timestamp === null || parsed.v1Signatures.length === 0) {
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
const toleranceSeconds = options.toleranceSeconds ?? 300;
|
|
112
|
+
if (toleranceSeconds > 0) {
|
|
113
|
+
const nowSeconds = toEpochSeconds(options.now);
|
|
114
|
+
if (Math.abs(nowSeconds - parsed.timestamp) > toleranceSeconds) {
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
const expected = await hmacHex(secret, `${parsed.timestamp}.${payloadString}`);
|
|
119
|
+
return parsed.v1Signatures.some((signature) => timingSafeEqual(signature, expected));
|
|
120
|
+
}
|
|
121
|
+
function parseWebhookEvent(payload) {
|
|
122
|
+
const payloadString = webhookPayloadToString(payload);
|
|
123
|
+
const parsed = JSON.parse(payloadString);
|
|
124
|
+
const type = typeof parsed.type === "string" ? parsed.type : typeof parsed.event === "string" ? parsed.event : "unknown";
|
|
125
|
+
return {
|
|
126
|
+
id: typeof parsed.id === "string" ? parsed.id : null,
|
|
127
|
+
type,
|
|
128
|
+
createdAt: typeof parsed.created_at === "string" ? parsed.created_at : null,
|
|
129
|
+
livemode: typeof parsed.livemode === "boolean" ? parsed.livemode : null,
|
|
130
|
+
data: parsed.data ?? null,
|
|
131
|
+
raw: parsed
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// src/helpers.ts
|
|
136
|
+
function mapWebhookEndpoint(endpoint) {
|
|
137
|
+
return {
|
|
138
|
+
id: String(endpoint?.id ?? ""),
|
|
139
|
+
url: typeof endpoint?.url === "string" ? endpoint.url : "",
|
|
140
|
+
description: typeof endpoint?.description === "string" ? endpoint.description : null,
|
|
141
|
+
events: Array.isArray(endpoint?.events) ? endpoint.events.filter((event) => typeof event === "string") : [],
|
|
142
|
+
isActive: Boolean(endpoint?.is_active),
|
|
143
|
+
createdAt: typeof endpoint?.created_at === "string" ? endpoint.created_at : "",
|
|
144
|
+
secretRotatedAt: typeof endpoint?.secret_rotated_at === "string" ? endpoint.secret_rotated_at : null
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
function resolveWebhookEndpointUrl(webhookEndpoints, endpointUrl) {
|
|
148
|
+
if (typeof endpointUrl === "string") {
|
|
149
|
+
return endpointUrl;
|
|
150
|
+
}
|
|
151
|
+
if (Array.isArray(webhookEndpoints)) {
|
|
152
|
+
const first = webhookEndpoints[0];
|
|
153
|
+
return typeof first?.url === "string" ? first.url : null;
|
|
154
|
+
}
|
|
155
|
+
if (webhookEndpoints && typeof webhookEndpoints === "object" && typeof webhookEndpoints.url === "string") {
|
|
156
|
+
return webhookEndpoints.url;
|
|
157
|
+
}
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
function mapWebhookDelivery(delivery) {
|
|
161
|
+
return {
|
|
162
|
+
id: String(delivery?.id ?? ""),
|
|
163
|
+
endpointId: typeof delivery?.endpoint_id === "string" ? delivery.endpoint_id : null,
|
|
164
|
+
endpointUrl: resolveWebhookEndpointUrl(delivery?.webhook_endpoints, delivery?.endpoint_url),
|
|
165
|
+
eventType: typeof delivery?.event_type === "string" ? delivery.event_type : "unknown",
|
|
166
|
+
status: typeof delivery?.status === "string" ? delivery.status : "unknown",
|
|
167
|
+
attempts: Number(delivery?.attempts || 0),
|
|
168
|
+
maxAttempts: typeof delivery?.max_attempts === "number" ? delivery.max_attempts : null,
|
|
169
|
+
responseStatus: typeof delivery?.response_status === "number" ? delivery.response_status : null,
|
|
170
|
+
responseBody: typeof delivery?.response_body === "string" ? delivery.response_body : null,
|
|
171
|
+
responseTimeMs: typeof delivery?.response_time_ms === "number" ? delivery.response_time_ms : null,
|
|
172
|
+
createdAt: typeof delivery?.created_at === "string" ? delivery.created_at : "",
|
|
173
|
+
deliveredAt: typeof delivery?.delivered_at === "string" ? delivery.delivered_at : null,
|
|
174
|
+
nextRetryAt: typeof delivery?.next_retry_at === "string" ? delivery.next_retry_at : null,
|
|
175
|
+
payload: delivery?.payload && typeof delivery.payload === "object" ? delivery.payload : null
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// src/client.ts
|
|
180
|
+
var DEFAULT_API_VERSION = "2026-03-01";
|
|
181
|
+
var DEFAULT_BASE_URL = "https://api.soledgic.com/v1";
|
|
182
|
+
var Soledgic = class {
|
|
183
|
+
constructor(config) {
|
|
184
|
+
this.webhooks = {
|
|
185
|
+
verifySignature: (payload, signatureHeader, secret, options) => verifyWebhookSignature(payload, signatureHeader, secret, options),
|
|
186
|
+
parseEvent: (payload) => parseWebhookEvent(payload)
|
|
187
|
+
};
|
|
188
|
+
if (!config.apiKey) {
|
|
189
|
+
throw new Error("apiKey is required");
|
|
190
|
+
}
|
|
191
|
+
let key = config.apiKey;
|
|
192
|
+
this._getKey = () => {
|
|
193
|
+
if (!key) throw new Error("Client has been destroyed");
|
|
194
|
+
return key;
|
|
195
|
+
};
|
|
196
|
+
this._destroyKey = () => {
|
|
197
|
+
key = null;
|
|
198
|
+
};
|
|
199
|
+
this.baseUrl = (config.baseUrl?.trim() || DEFAULT_BASE_URL).replace(/\/$/, "");
|
|
200
|
+
this.timeoutMs = config.timeout ?? 3e4;
|
|
201
|
+
this.apiVersion = (config.apiVersion || "").trim() || DEFAULT_API_VERSION;
|
|
202
|
+
}
|
|
203
|
+
/** Clear the API key from memory. After calling destroy(), all requests will throw. */
|
|
204
|
+
destroy() {
|
|
205
|
+
this._destroyKey?.();
|
|
206
|
+
}
|
|
207
|
+
throwTypedError(message, status, data) {
|
|
208
|
+
const apiCode = typeof data?.error_code === "string" ? data.error_code : typeof data?.code === "string" ? data.code : void 0;
|
|
209
|
+
switch (status) {
|
|
210
|
+
case 400:
|
|
211
|
+
throw new ValidationError(message, data, apiCode);
|
|
212
|
+
case 401:
|
|
213
|
+
throw new AuthenticationError(message, data, apiCode);
|
|
214
|
+
case 404:
|
|
215
|
+
throw new NotFoundError(message, data, apiCode);
|
|
216
|
+
case 409:
|
|
217
|
+
throw new ConflictError(message, data, apiCode);
|
|
218
|
+
default:
|
|
219
|
+
throw new SoledgicError(message, status, data, apiCode);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
async request(endpoint, body) {
|
|
223
|
+
const controller = new AbortController();
|
|
224
|
+
const timer = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
225
|
+
try {
|
|
226
|
+
const response = await fetch(`${this.baseUrl}/${endpoint}`, {
|
|
227
|
+
method: "POST",
|
|
228
|
+
headers: {
|
|
229
|
+
"x-api-key": this._getKey(),
|
|
230
|
+
"Content-Type": "application/json",
|
|
231
|
+
"Soledgic-Version": this.apiVersion
|
|
232
|
+
},
|
|
233
|
+
body: JSON.stringify(body),
|
|
234
|
+
signal: controller.signal
|
|
235
|
+
});
|
|
236
|
+
const data = await response.json();
|
|
237
|
+
if (!response.ok) {
|
|
238
|
+
this.throwTypedError(
|
|
239
|
+
data.error || `Request failed: ${response.status}`,
|
|
240
|
+
response.status,
|
|
241
|
+
data
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
return data;
|
|
245
|
+
} finally {
|
|
246
|
+
clearTimeout(timer);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
async requestGet(endpoint, params) {
|
|
250
|
+
const url = new URL(`${this.baseUrl}/${endpoint}`);
|
|
251
|
+
if (params) {
|
|
252
|
+
for (const [key, value] of Object.entries(params)) {
|
|
253
|
+
if (value !== void 0) url.searchParams.set(key, String(value));
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
const controller = new AbortController();
|
|
257
|
+
const timer = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
258
|
+
try {
|
|
259
|
+
const response = await fetch(url.toString(), {
|
|
260
|
+
method: "GET",
|
|
261
|
+
headers: {
|
|
262
|
+
"x-api-key": this._getKey(),
|
|
263
|
+
"Soledgic-Version": this.apiVersion
|
|
264
|
+
},
|
|
265
|
+
signal: controller.signal
|
|
266
|
+
});
|
|
267
|
+
const data = await response.json();
|
|
268
|
+
if (!response.ok) {
|
|
269
|
+
this.throwTypedError(
|
|
270
|
+
data.error || `Request failed: ${response.status}`,
|
|
271
|
+
response.status,
|
|
272
|
+
data
|
|
273
|
+
);
|
|
274
|
+
}
|
|
275
|
+
return data;
|
|
276
|
+
} finally {
|
|
277
|
+
clearTimeout(timer);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
async requestRaw(endpoint, body) {
|
|
281
|
+
const controller = new AbortController();
|
|
282
|
+
const timer = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
283
|
+
try {
|
|
284
|
+
const response = await fetch(`${this.baseUrl}/${endpoint}`, {
|
|
285
|
+
method: "POST",
|
|
286
|
+
headers: {
|
|
287
|
+
"x-api-key": this._getKey(),
|
|
288
|
+
"Content-Type": "application/json",
|
|
289
|
+
"Soledgic-Version": this.apiVersion
|
|
290
|
+
},
|
|
291
|
+
body: JSON.stringify(body),
|
|
292
|
+
signal: controller.signal
|
|
293
|
+
});
|
|
294
|
+
if (!response.ok) {
|
|
295
|
+
const text = await response.text();
|
|
296
|
+
let parsed;
|
|
297
|
+
try {
|
|
298
|
+
parsed = JSON.parse(text);
|
|
299
|
+
} catch {
|
|
300
|
+
parsed = { error: text };
|
|
301
|
+
}
|
|
302
|
+
this.throwTypedError(
|
|
303
|
+
parsed.error || `Request failed: ${response.status}`,
|
|
304
|
+
response.status,
|
|
305
|
+
parsed
|
|
306
|
+
);
|
|
307
|
+
}
|
|
308
|
+
return response;
|
|
309
|
+
} finally {
|
|
310
|
+
clearTimeout(timer);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
async requestGetRaw(endpoint, params) {
|
|
314
|
+
const url = new URL(`${this.baseUrl}/${endpoint}`);
|
|
315
|
+
if (params) {
|
|
316
|
+
for (const [key, value] of Object.entries(params)) {
|
|
317
|
+
if (value !== void 0) url.searchParams.set(key, String(value));
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
const controller = new AbortController();
|
|
321
|
+
const timer = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
322
|
+
try {
|
|
323
|
+
const response = await fetch(url.toString(), {
|
|
324
|
+
method: "GET",
|
|
325
|
+
headers: {
|
|
326
|
+
"x-api-key": this._getKey(),
|
|
327
|
+
"Soledgic-Version": this.apiVersion
|
|
328
|
+
},
|
|
329
|
+
signal: controller.signal
|
|
330
|
+
});
|
|
331
|
+
if (!response.ok) {
|
|
332
|
+
const text = await response.text();
|
|
333
|
+
let parsed;
|
|
334
|
+
try {
|
|
335
|
+
parsed = JSON.parse(text);
|
|
336
|
+
} catch {
|
|
337
|
+
parsed = { error: text };
|
|
338
|
+
}
|
|
339
|
+
this.throwTypedError(
|
|
340
|
+
parsed.error || `Request failed: ${response.status}`,
|
|
341
|
+
response.status,
|
|
342
|
+
parsed
|
|
343
|
+
);
|
|
344
|
+
}
|
|
345
|
+
return response;
|
|
346
|
+
} finally {
|
|
347
|
+
clearTimeout(timer);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
async requestDelete(endpoint) {
|
|
351
|
+
const controller = new AbortController();
|
|
352
|
+
const timer = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
353
|
+
try {
|
|
354
|
+
const response = await fetch(`${this.baseUrl}/${endpoint}`, {
|
|
355
|
+
method: "DELETE",
|
|
356
|
+
headers: {
|
|
357
|
+
"x-api-key": this._getKey(),
|
|
358
|
+
"Soledgic-Version": this.apiVersion
|
|
359
|
+
},
|
|
360
|
+
signal: controller.signal
|
|
361
|
+
});
|
|
362
|
+
const data = await response.json();
|
|
363
|
+
if (!response.ok) {
|
|
364
|
+
this.throwTypedError(
|
|
365
|
+
data.error || `Request failed: ${response.status}`,
|
|
366
|
+
response.status,
|
|
367
|
+
data
|
|
368
|
+
);
|
|
369
|
+
}
|
|
370
|
+
return data;
|
|
371
|
+
} finally {
|
|
372
|
+
clearTimeout(timer);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
// === STANDARD MODE - INCOME & EXPENSES ===
|
|
376
|
+
async recordIncome(req) {
|
|
377
|
+
return this.request("record-income", {
|
|
378
|
+
reference_id: req.referenceId,
|
|
379
|
+
amount: req.amount,
|
|
380
|
+
description: req.description,
|
|
381
|
+
category: req.category,
|
|
382
|
+
customer_id: req.customerId,
|
|
383
|
+
customer_name: req.customerName,
|
|
384
|
+
received_to: req.receivedTo,
|
|
385
|
+
invoice_id: req.invoiceId,
|
|
386
|
+
transaction_date: req.transactionDate,
|
|
387
|
+
metadata: req.metadata
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
async recordExpense(req) {
|
|
391
|
+
return this.request("record-expense", {
|
|
392
|
+
reference_id: req.referenceId,
|
|
393
|
+
amount: req.amount,
|
|
394
|
+
description: req.description,
|
|
395
|
+
category: req.category,
|
|
396
|
+
vendor_id: req.vendorId,
|
|
397
|
+
vendor_name: req.vendorName,
|
|
398
|
+
paid_from: req.paidFrom,
|
|
399
|
+
receipt_url: req.receiptUrl,
|
|
400
|
+
tax_deductible: req.taxDeductible,
|
|
401
|
+
transaction_date: req.transactionDate,
|
|
402
|
+
metadata: req.metadata,
|
|
403
|
+
authorizing_instrument_id: req.authorizingInstrumentId,
|
|
404
|
+
risk_evaluation_id: req.riskEvaluationId,
|
|
405
|
+
authorization_decision_id: req.authorizationDecisionId
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
async recordBill(req) {
|
|
409
|
+
return this.request("record-bill", {
|
|
410
|
+
amount: req.amount,
|
|
411
|
+
description: req.description,
|
|
412
|
+
vendor_name: req.vendorName,
|
|
413
|
+
vendor_id: req.vendorId,
|
|
414
|
+
reference_id: req.referenceId,
|
|
415
|
+
due_date: req.dueDate,
|
|
416
|
+
expense_category: req.expenseCategory,
|
|
417
|
+
paid: req.paid,
|
|
418
|
+
metadata: req.metadata,
|
|
419
|
+
authorizing_instrument_id: req.authorizingInstrumentId,
|
|
420
|
+
risk_evaluation_id: req.riskEvaluationId,
|
|
421
|
+
authorization_decision_id: req.authorizationDecisionId
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
// === AUTHORIZING INSTRUMENTS ===
|
|
425
|
+
// Register financial authorization instruments for transaction validation
|
|
426
|
+
// Instruments are immutable and ledger-adjacent - they explain WHY money moved
|
|
427
|
+
async registerInstrument(req) {
|
|
428
|
+
return this.request("register-instrument", {
|
|
429
|
+
external_ref: req.externalRef,
|
|
430
|
+
extracted_terms: {
|
|
431
|
+
amount: req.extractedTerms.amount,
|
|
432
|
+
currency: req.extractedTerms.currency,
|
|
433
|
+
cadence: req.extractedTerms.cadence,
|
|
434
|
+
counterparty_name: req.extractedTerms.counterpartyName
|
|
435
|
+
}
|
|
436
|
+
});
|
|
437
|
+
}
|
|
438
|
+
// === SHADOW LEDGER (GHOST ENTRIES) ===
|
|
439
|
+
// Project future obligations based on authorizing instrument terms.
|
|
440
|
+
// Ghost entries NEVER affect balances or entries - only express future intent.
|
|
441
|
+
async projectIntent(req) {
|
|
442
|
+
const response = await this.request("project-intent", {
|
|
443
|
+
authorizing_instrument_id: req.authorizingInstrumentId,
|
|
444
|
+
until_date: req.untilDate,
|
|
445
|
+
horizon_count: req.horizonCount
|
|
446
|
+
});
|
|
447
|
+
return {
|
|
448
|
+
success: response.success,
|
|
449
|
+
instrumentId: response.instrument_id,
|
|
450
|
+
externalRef: response.external_ref,
|
|
451
|
+
cadence: response.cadence,
|
|
452
|
+
projectionsCreated: response.projections_created,
|
|
453
|
+
projectionsRequested: response.projections_requested,
|
|
454
|
+
duplicatesSkipped: response.duplicates_skipped,
|
|
455
|
+
dateRange: response.date_range,
|
|
456
|
+
projectedDates: response.projected_dates
|
|
457
|
+
};
|
|
458
|
+
}
|
|
459
|
+
// === REVERSALS & CORRECTIONS ===
|
|
460
|
+
async reverseTransaction(req) {
|
|
461
|
+
const response = await this.request("reverse-transaction", {
|
|
462
|
+
transaction_id: req.transactionId,
|
|
463
|
+
reason: req.reason,
|
|
464
|
+
partial_amount: req.partialAmount,
|
|
465
|
+
idempotency_key: req.idempotencyKey,
|
|
466
|
+
metadata: req.metadata
|
|
467
|
+
});
|
|
468
|
+
return {
|
|
469
|
+
success: response.success,
|
|
470
|
+
voidType: response.void_type,
|
|
471
|
+
message: response.message,
|
|
472
|
+
transactionId: response.transaction_id ?? response.original_transaction_id ?? req.transactionId,
|
|
473
|
+
reversalId: response.reversal_id ?? null,
|
|
474
|
+
reversedAmount: response.reversed_amount ?? null,
|
|
475
|
+
isPartial: response.is_partial ?? null,
|
|
476
|
+
voidedAt: response.voided_at ?? null,
|
|
477
|
+
reversedAt: response.reversed_at ?? null,
|
|
478
|
+
warning: response.warning ?? null
|
|
479
|
+
};
|
|
480
|
+
}
|
|
481
|
+
// === PERIOD MANAGEMENT ===
|
|
482
|
+
async listPeriods() {
|
|
483
|
+
return this.request("close-period", { action: "list" });
|
|
484
|
+
}
|
|
485
|
+
async createPeriod(req) {
|
|
486
|
+
return this.request("close-period", {
|
|
487
|
+
action: "create",
|
|
488
|
+
start_date: req.startDate,
|
|
489
|
+
end_date: req.endDate,
|
|
490
|
+
name: req.name
|
|
491
|
+
});
|
|
492
|
+
}
|
|
493
|
+
async closePeriod(year, month, quarter) {
|
|
494
|
+
return this.request("close-period", { year, month, quarter });
|
|
495
|
+
}
|
|
496
|
+
// === RECONCILIATION ===
|
|
497
|
+
async matchTransaction(req) {
|
|
498
|
+
const response = await this.request("reconciliations/matches", {
|
|
499
|
+
transaction_id: req.transactionId,
|
|
500
|
+
bank_transaction_id: req.bankTransactionId
|
|
501
|
+
});
|
|
502
|
+
return {
|
|
503
|
+
success: response.success,
|
|
504
|
+
match: {
|
|
505
|
+
id: response.match.id,
|
|
506
|
+
transactionId: response.match.transaction_id,
|
|
507
|
+
bankTransactionId: response.match.bank_transaction_id,
|
|
508
|
+
status: response.match.status,
|
|
509
|
+
matchedAt: response.match.matched_at
|
|
510
|
+
}
|
|
511
|
+
};
|
|
512
|
+
}
|
|
513
|
+
async unmatchTransaction(transactionId) {
|
|
514
|
+
const response = await this.requestDelete(`reconciliations/matches/${transactionId}`);
|
|
515
|
+
return {
|
|
516
|
+
success: response.success,
|
|
517
|
+
deleted: Boolean(response.deleted),
|
|
518
|
+
transactionId: response.transaction_id
|
|
519
|
+
};
|
|
520
|
+
}
|
|
521
|
+
async listUnmatchedTransactions() {
|
|
522
|
+
const response = await this.requestGet("reconciliations/unmatched");
|
|
523
|
+
return {
|
|
524
|
+
success: response.success,
|
|
525
|
+
unmatchedCount: response.unmatched_count ?? 0,
|
|
526
|
+
transactions: (response.transactions || []).map((transaction) => ({
|
|
527
|
+
id: transaction.id,
|
|
528
|
+
referenceId: transaction.reference_id ?? null,
|
|
529
|
+
description: transaction.description ?? null,
|
|
530
|
+
amount: transaction.amount,
|
|
531
|
+
currency: transaction.currency || "USD",
|
|
532
|
+
createdAt: transaction.created_at,
|
|
533
|
+
status: transaction.status,
|
|
534
|
+
metadata: transaction.metadata || {}
|
|
535
|
+
}))
|
|
536
|
+
};
|
|
537
|
+
}
|
|
538
|
+
async createReconciliationSnapshot(req) {
|
|
539
|
+
const response = await this.request("reconciliations/snapshots", {
|
|
540
|
+
period_id: req.periodId,
|
|
541
|
+
as_of_date: req.asOfDate
|
|
542
|
+
});
|
|
543
|
+
return {
|
|
544
|
+
success: response.success,
|
|
545
|
+
snapshot_id: response.snapshot.id,
|
|
546
|
+
integrity_hash: response.snapshot.integrity_hash
|
|
547
|
+
};
|
|
548
|
+
}
|
|
549
|
+
async getReconciliationSnapshot(periodId) {
|
|
550
|
+
const response = await this.requestGet(`reconciliations/snapshots/${periodId}`);
|
|
551
|
+
return {
|
|
552
|
+
success: response.success,
|
|
553
|
+
snapshot: {
|
|
554
|
+
id: response.snapshot.id,
|
|
555
|
+
periodStart: response.snapshot.period_start,
|
|
556
|
+
periodEnd: response.snapshot.period_end,
|
|
557
|
+
integrityHash: response.snapshot.integrity_hash,
|
|
558
|
+
integrityValid: Boolean(response.snapshot.integrity_valid),
|
|
559
|
+
summary: {
|
|
560
|
+
totalMatched: response.snapshot.summary?.total_matched ?? 0,
|
|
561
|
+
totalUnmatched: response.snapshot.summary?.total_unmatched ?? 0,
|
|
562
|
+
matchedAmount: response.snapshot.summary?.matched_amount ?? 0,
|
|
563
|
+
unmatchedAmount: response.snapshot.summary?.unmatched_amount ?? 0
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
};
|
|
567
|
+
}
|
|
568
|
+
async autoMatchBankTransaction(bankAggregatorTransactionId) {
|
|
569
|
+
const response = await this.request("reconciliations/auto-match", {
|
|
570
|
+
bank_aggregator_transaction_id: bankAggregatorTransactionId
|
|
571
|
+
});
|
|
572
|
+
return {
|
|
573
|
+
success: response.success,
|
|
574
|
+
result: {
|
|
575
|
+
matched: Boolean(response.result?.matched),
|
|
576
|
+
matchType: response.result?.match_type ?? null,
|
|
577
|
+
matchedTransactionId: response.result?.matched_transaction_id ?? null,
|
|
578
|
+
bankAggregatorTransactionId: response.result?.bank_aggregator_transaction_id ?? bankAggregatorTransactionId
|
|
579
|
+
}
|
|
580
|
+
};
|
|
581
|
+
}
|
|
582
|
+
// === FROZEN STATEMENTS ===
|
|
583
|
+
async generateFrozenStatements(periodId) {
|
|
584
|
+
return this.request("frozen-statements", {
|
|
585
|
+
action: "generate",
|
|
586
|
+
period_id: periodId
|
|
587
|
+
});
|
|
588
|
+
}
|
|
589
|
+
async getFrozenStatement(periodId, statementType) {
|
|
590
|
+
return this.request("frozen-statements", {
|
|
591
|
+
action: "get",
|
|
592
|
+
period_id: periodId,
|
|
593
|
+
statement_type: statementType
|
|
594
|
+
});
|
|
595
|
+
}
|
|
596
|
+
async listFrozenStatements(periodId) {
|
|
597
|
+
return this.request("frozen-statements", {
|
|
598
|
+
action: "list",
|
|
599
|
+
period_id: periodId
|
|
600
|
+
});
|
|
601
|
+
}
|
|
602
|
+
async verifyFrozenStatements(periodId) {
|
|
603
|
+
return this.request("frozen-statements", {
|
|
604
|
+
action: "verify",
|
|
605
|
+
period_id: periodId
|
|
606
|
+
});
|
|
607
|
+
}
|
|
608
|
+
// === SPLITS MANAGEMENT ===
|
|
609
|
+
async listTiers() {
|
|
610
|
+
return this.request("manage-splits", { action: "list_tiers" });
|
|
611
|
+
}
|
|
612
|
+
async getEffectiveSplit(creatorId) {
|
|
613
|
+
return this.request("manage-splits", { action: "get_effective_split", creator_id: creatorId });
|
|
614
|
+
}
|
|
615
|
+
async setCreatorSplit(creatorId, splitPercent) {
|
|
616
|
+
return this.request("manage-splits", { action: "set_creator_split", creator_id: creatorId, split_percent: splitPercent });
|
|
617
|
+
}
|
|
618
|
+
async clearCreatorSplit(creatorId) {
|
|
619
|
+
return this.request("manage-splits", { action: "clear_creator_split", creator_id: creatorId });
|
|
620
|
+
}
|
|
621
|
+
async autoPromoteCreators() {
|
|
622
|
+
return this.request("manage-splits", { action: "auto_promote" });
|
|
623
|
+
}
|
|
624
|
+
async getSummary() {
|
|
625
|
+
const response = await this.requestGet("participants");
|
|
626
|
+
const participants = Array.isArray(response.participants) ? response.participants : [];
|
|
627
|
+
const summary = participants.reduce((totals, participant) => ({
|
|
628
|
+
total_ledger_balance: totals.total_ledger_balance + Number(participant.ledger_balance || 0),
|
|
629
|
+
total_held_amount: totals.total_held_amount + Number(participant.held_amount || 0),
|
|
630
|
+
total_available_balance: totals.total_available_balance + Number(participant.available_balance || 0)
|
|
631
|
+
}), {
|
|
632
|
+
total_ledger_balance: 0,
|
|
633
|
+
total_held_amount: 0,
|
|
634
|
+
total_available_balance: 0
|
|
635
|
+
});
|
|
636
|
+
return {
|
|
637
|
+
success: response.success,
|
|
638
|
+
data: {
|
|
639
|
+
...summary,
|
|
640
|
+
participant_count: participants.length
|
|
641
|
+
}
|
|
642
|
+
};
|
|
643
|
+
}
|
|
644
|
+
// === REPORTS ===
|
|
645
|
+
async getProfitLoss(startDate, endDate) {
|
|
646
|
+
return this.request("generate-report", { report_type: "profit_loss", start_date: startDate, end_date: endDate });
|
|
647
|
+
}
|
|
648
|
+
async getTrialBalance(asOf) {
|
|
649
|
+
return this.request("generate-report", { report_type: "trial_balance", as_of: asOf });
|
|
650
|
+
}
|
|
651
|
+
async get1099Summary(year) {
|
|
652
|
+
return this.request("generate-report", { report_type: "1099_summary", tax_year: year });
|
|
653
|
+
}
|
|
654
|
+
async getCreatorEarnings(startDate, endDate) {
|
|
655
|
+
return this.request("generate-report", { report_type: "creator_earnings", start_date: startDate, end_date: endDate });
|
|
656
|
+
}
|
|
657
|
+
async getHistoricalEarnings(options = {}) {
|
|
658
|
+
return this.requestGet("earnings", {
|
|
659
|
+
start_date: options.startDate,
|
|
660
|
+
end_date: options.endDate,
|
|
661
|
+
creator_id: options.creatorId,
|
|
662
|
+
granularity: options.granularity
|
|
663
|
+
});
|
|
664
|
+
}
|
|
665
|
+
async getTransactions(startDate, endDate, creatorId) {
|
|
666
|
+
return this.request("generate-report", { report_type: "transaction_history", start_date: startDate, end_date: endDate, creator_id: creatorId });
|
|
667
|
+
}
|
|
668
|
+
// === CREDITS ===
|
|
669
|
+
/** Issue credits to a user. 1000 credits = $1 USD (Soledgic standard rate). */
|
|
670
|
+
async issueCredits(userId, credits, options = {}) {
|
|
671
|
+
return this.request("credits", {
|
|
672
|
+
action: "issue",
|
|
673
|
+
user_id: userId,
|
|
674
|
+
credits,
|
|
675
|
+
reason: options.reason,
|
|
676
|
+
reference_id: options.referenceId
|
|
677
|
+
});
|
|
678
|
+
}
|
|
679
|
+
/** Convert earned credits to spendable balance. Minimum 5000 credits ($5). */
|
|
680
|
+
async convertCredits(userId, credits) {
|
|
681
|
+
return this.request("credits", {
|
|
682
|
+
action: "convert",
|
|
683
|
+
user_id: userId,
|
|
684
|
+
credits
|
|
685
|
+
});
|
|
686
|
+
}
|
|
687
|
+
/** Spend spendable balance on creator content. Amount in cents. Split applies. */
|
|
688
|
+
async redeemCredits(userId, creatorId, amountCents, referenceId, options = {}) {
|
|
689
|
+
return this.request("credits", {
|
|
690
|
+
action: "redeem",
|
|
691
|
+
user_id: userId,
|
|
692
|
+
creator_id: creatorId,
|
|
693
|
+
amount: amountCents,
|
|
694
|
+
reference_id: referenceId,
|
|
695
|
+
description: options.description,
|
|
696
|
+
split_percent: options.splitPercent
|
|
697
|
+
});
|
|
698
|
+
}
|
|
699
|
+
/** Get user's credit balance (unconverted) and spendable balance (converted). */
|
|
700
|
+
async getCreditBalance(userId) {
|
|
701
|
+
return this.request("credits", { action: "balance", user_id: userId });
|
|
702
|
+
}
|
|
703
|
+
// === PDF EXPORTS ===
|
|
704
|
+
async generatePDF(reportType, options = {}) {
|
|
705
|
+
return this.request("generate-pdf", {
|
|
706
|
+
report_type: reportType,
|
|
707
|
+
creator_id: options.creatorId,
|
|
708
|
+
start_date: options.startDate,
|
|
709
|
+
end_date: options.endDate,
|
|
710
|
+
tax_year: options.taxYear,
|
|
711
|
+
period_id: options.periodId
|
|
712
|
+
});
|
|
713
|
+
}
|
|
714
|
+
async getCreatorStatement(creatorId, startDate, endDate) {
|
|
715
|
+
return this.generatePDF("creator_statement", { creatorId, startDate, endDate });
|
|
716
|
+
}
|
|
717
|
+
async getProfitLossPDF(startDate, endDate, periodId) {
|
|
718
|
+
return this.generatePDF("profit_loss", { startDate, endDate, periodId });
|
|
719
|
+
}
|
|
720
|
+
async getTrialBalancePDF() {
|
|
721
|
+
return this.generatePDF("trial_balance", {});
|
|
722
|
+
}
|
|
723
|
+
async get1099PDF(taxYear) {
|
|
724
|
+
return this.generatePDF("1099", { taxYear });
|
|
725
|
+
}
|
|
726
|
+
// === AUTO-EMAIL ===
|
|
727
|
+
async configureEmail(config) {
|
|
728
|
+
return this.request("send-statements", {
|
|
729
|
+
action: "configure",
|
|
730
|
+
email_config: {
|
|
731
|
+
enabled: config.enabled,
|
|
732
|
+
send_day: config.sendDay || 1,
|
|
733
|
+
from_name: config.fromName,
|
|
734
|
+
from_email: config.fromEmail,
|
|
735
|
+
subject_template: config.subjectTemplate,
|
|
736
|
+
body_template: config.bodyTemplate,
|
|
737
|
+
cc_admin: config.ccAdmin,
|
|
738
|
+
admin_email: config.adminEmail
|
|
739
|
+
}
|
|
740
|
+
});
|
|
741
|
+
}
|
|
742
|
+
async sendMonthlyStatements(year, month) {
|
|
743
|
+
return this.request("send-statements", {
|
|
744
|
+
action: "send_monthly_statements",
|
|
745
|
+
year,
|
|
746
|
+
month
|
|
747
|
+
});
|
|
748
|
+
}
|
|
749
|
+
async sendCreatorStatement(creatorId, year, month) {
|
|
750
|
+
return this.request("send-statements", {
|
|
751
|
+
action: "send_single_statement",
|
|
752
|
+
creator_id: creatorId,
|
|
753
|
+
year,
|
|
754
|
+
month
|
|
755
|
+
});
|
|
756
|
+
}
|
|
757
|
+
async previewStatementEmail(creatorId, year, month) {
|
|
758
|
+
return this.request("send-statements", {
|
|
759
|
+
action: "preview",
|
|
760
|
+
creator_id: creatorId,
|
|
761
|
+
year,
|
|
762
|
+
month
|
|
763
|
+
});
|
|
764
|
+
}
|
|
765
|
+
async getEmailHistory() {
|
|
766
|
+
return this.request("send-statements", { action: "get_queue" });
|
|
767
|
+
}
|
|
768
|
+
// === WEBHOOKS ===
|
|
769
|
+
async listWebhookEndpoints() {
|
|
770
|
+
const response = await this.request("webhooks", { action: "list" });
|
|
771
|
+
return {
|
|
772
|
+
success: response.success,
|
|
773
|
+
data: Array.isArray(response.data) ? response.data.map(mapWebhookEndpoint) : []
|
|
774
|
+
};
|
|
775
|
+
}
|
|
776
|
+
async createWebhookEndpoint(config) {
|
|
777
|
+
const response = await this.request("webhooks", {
|
|
778
|
+
action: "create",
|
|
779
|
+
url: config.url,
|
|
780
|
+
description: config.description,
|
|
781
|
+
events: config.events || ["*"]
|
|
782
|
+
});
|
|
783
|
+
return {
|
|
784
|
+
success: response.success,
|
|
785
|
+
data: {
|
|
786
|
+
...mapWebhookEndpoint(response.data || {}),
|
|
787
|
+
secret: typeof response.data?.secret === "string" ? response.data.secret : null
|
|
788
|
+
},
|
|
789
|
+
message: typeof response.message === "string" ? response.message : void 0
|
|
790
|
+
};
|
|
791
|
+
}
|
|
792
|
+
async updateWebhookEndpoint(endpointId, updates) {
|
|
793
|
+
const response = await this.request("webhooks", {
|
|
794
|
+
action: "update",
|
|
795
|
+
endpoint_id: endpointId,
|
|
796
|
+
url: updates.url,
|
|
797
|
+
description: updates.description,
|
|
798
|
+
events: updates.events,
|
|
799
|
+
is_active: updates.isActive
|
|
800
|
+
});
|
|
801
|
+
return {
|
|
802
|
+
success: response.success,
|
|
803
|
+
data: mapWebhookEndpoint(response.data || {})
|
|
804
|
+
};
|
|
805
|
+
}
|
|
806
|
+
async deleteWebhookEndpoint(endpointId) {
|
|
807
|
+
const response = await this.request("webhooks", { action: "delete", endpoint_id: endpointId });
|
|
808
|
+
return {
|
|
809
|
+
success: response.success,
|
|
810
|
+
message: typeof response.message === "string" ? response.message : void 0
|
|
811
|
+
};
|
|
812
|
+
}
|
|
813
|
+
async testWebhookEndpoint(endpointId) {
|
|
814
|
+
const response = await this.request("webhooks", { action: "test", endpoint_id: endpointId });
|
|
815
|
+
return {
|
|
816
|
+
success: response.success,
|
|
817
|
+
error: typeof response.error === "string" ? response.error : void 0,
|
|
818
|
+
data: {
|
|
819
|
+
delivered: Boolean(response.data?.delivered),
|
|
820
|
+
status: typeof response.data?.status === "number" ? response.data.status : null,
|
|
821
|
+
responseTimeMs: typeof response.data?.response_time_ms === "number" ? response.data.response_time_ms : null
|
|
822
|
+
}
|
|
823
|
+
};
|
|
824
|
+
}
|
|
825
|
+
async getWebhookDeliveries(endpointId, limit) {
|
|
826
|
+
const response = await this.request("webhooks", {
|
|
827
|
+
action: "deliveries",
|
|
828
|
+
endpoint_id: endpointId,
|
|
829
|
+
limit
|
|
830
|
+
});
|
|
831
|
+
return {
|
|
832
|
+
success: response.success,
|
|
833
|
+
data: Array.isArray(response.data) ? response.data.map(mapWebhookDelivery) : []
|
|
834
|
+
};
|
|
835
|
+
}
|
|
836
|
+
async retryWebhookDelivery(deliveryId) {
|
|
837
|
+
const response = await this.request("webhooks", { action: "retry", delivery_id: deliveryId });
|
|
838
|
+
return {
|
|
839
|
+
success: response.success,
|
|
840
|
+
message: typeof response.message === "string" ? response.message : void 0
|
|
841
|
+
};
|
|
842
|
+
}
|
|
843
|
+
async rotateWebhookSecret(endpointId) {
|
|
844
|
+
const response = await this.request("webhooks", {
|
|
845
|
+
action: "rotate_secret",
|
|
846
|
+
endpoint_id: endpointId
|
|
847
|
+
});
|
|
848
|
+
return {
|
|
849
|
+
success: response.success,
|
|
850
|
+
data: {
|
|
851
|
+
secret: typeof response.data?.secret === "string" ? response.data.secret : null
|
|
852
|
+
},
|
|
853
|
+
message: typeof response.message === "string" ? response.message : void 0
|
|
854
|
+
};
|
|
855
|
+
}
|
|
856
|
+
// === BREACH ALERTS ===
|
|
857
|
+
// Configure Slack, email, or webhook notifications for breach risk events.
|
|
858
|
+
// Alerts trigger when project-intent creates projections that exceed cash coverage.
|
|
859
|
+
async listAlerts() {
|
|
860
|
+
const response = await this.request("configure-alerts", { action: "list" });
|
|
861
|
+
return {
|
|
862
|
+
success: response.success,
|
|
863
|
+
data: (response.data || []).map((c) => ({
|
|
864
|
+
id: c.id,
|
|
865
|
+
alertType: c.alert_type,
|
|
866
|
+
channel: c.channel,
|
|
867
|
+
config: c.config,
|
|
868
|
+
thresholds: {
|
|
869
|
+
coverageRatioBelow: c.thresholds?.coverage_ratio_below,
|
|
870
|
+
shortfallAbove: c.thresholds?.shortfall_above
|
|
871
|
+
},
|
|
872
|
+
isActive: c.is_active,
|
|
873
|
+
lastTriggeredAt: c.last_triggered_at,
|
|
874
|
+
triggerCount: c.trigger_count,
|
|
875
|
+
createdAt: c.created_at
|
|
876
|
+
}))
|
|
877
|
+
};
|
|
878
|
+
}
|
|
879
|
+
async createAlert(req) {
|
|
880
|
+
const config = {};
|
|
881
|
+
if ("webhookUrl" in req.config) {
|
|
882
|
+
config.webhook_url = req.config.webhookUrl;
|
|
883
|
+
config.channel = req.config.channel;
|
|
884
|
+
} else if ("recipients" in req.config) {
|
|
885
|
+
config.recipients = req.config.recipients;
|
|
886
|
+
}
|
|
887
|
+
const response = await this.request("configure-alerts", {
|
|
888
|
+
action: "create",
|
|
889
|
+
alert_type: req.alertType,
|
|
890
|
+
channel: req.channel,
|
|
891
|
+
config,
|
|
892
|
+
thresholds: req.thresholds ? {
|
|
893
|
+
coverage_ratio_below: req.thresholds.coverageRatioBelow,
|
|
894
|
+
shortfall_above: req.thresholds.shortfallAbove
|
|
895
|
+
} : void 0,
|
|
896
|
+
is_active: req.isActive
|
|
897
|
+
});
|
|
898
|
+
return {
|
|
899
|
+
success: response.success,
|
|
900
|
+
data: {
|
|
901
|
+
id: response.data.id,
|
|
902
|
+
alertType: response.data.alert_type,
|
|
903
|
+
channel: response.data.channel,
|
|
904
|
+
config: response.data.config || {},
|
|
905
|
+
thresholds: {
|
|
906
|
+
coverageRatioBelow: response.data.thresholds?.coverage_ratio_below,
|
|
907
|
+
shortfallAbove: response.data.thresholds?.shortfall_above
|
|
908
|
+
},
|
|
909
|
+
isActive: response.data.is_active,
|
|
910
|
+
triggerCount: response.data.trigger_count || 0,
|
|
911
|
+
createdAt: response.data.created_at
|
|
912
|
+
}
|
|
913
|
+
};
|
|
914
|
+
}
|
|
915
|
+
async updateAlert(req) {
|
|
916
|
+
const config = req.config ? {} : void 0;
|
|
917
|
+
if (req.config) {
|
|
918
|
+
if ("webhookUrl" in req.config && req.config.webhookUrl) {
|
|
919
|
+
config.webhook_url = req.config.webhookUrl;
|
|
920
|
+
}
|
|
921
|
+
if ("channel" in req.config) {
|
|
922
|
+
config.channel = req.config.channel;
|
|
923
|
+
}
|
|
924
|
+
if ("recipients" in req.config) {
|
|
925
|
+
config.recipients = req.config.recipients;
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
const response = await this.request("configure-alerts", {
|
|
929
|
+
action: "update",
|
|
930
|
+
config_id: req.configId,
|
|
931
|
+
config,
|
|
932
|
+
thresholds: req.thresholds ? {
|
|
933
|
+
coverage_ratio_below: req.thresholds.coverageRatioBelow,
|
|
934
|
+
shortfall_above: req.thresholds.shortfallAbove
|
|
935
|
+
} : void 0,
|
|
936
|
+
is_active: req.isActive
|
|
937
|
+
});
|
|
938
|
+
return {
|
|
939
|
+
success: response.success,
|
|
940
|
+
data: {
|
|
941
|
+
id: response.data.id,
|
|
942
|
+
alertType: response.data.alert_type,
|
|
943
|
+
channel: response.data.channel,
|
|
944
|
+
config: response.data.config || {},
|
|
945
|
+
thresholds: {
|
|
946
|
+
coverageRatioBelow: response.data.thresholds?.coverage_ratio_below,
|
|
947
|
+
shortfallAbove: response.data.thresholds?.shortfall_above
|
|
948
|
+
},
|
|
949
|
+
isActive: response.data.is_active,
|
|
950
|
+
triggerCount: response.data.trigger_count ?? 0,
|
|
951
|
+
createdAt: response.data.created_at ?? ""
|
|
952
|
+
}
|
|
953
|
+
};
|
|
954
|
+
}
|
|
955
|
+
async deleteAlert(configId) {
|
|
956
|
+
return this.request("configure-alerts", {
|
|
957
|
+
action: "delete",
|
|
958
|
+
config_id: configId
|
|
959
|
+
});
|
|
960
|
+
}
|
|
961
|
+
async testAlert(configId) {
|
|
962
|
+
return this.request("configure-alerts", {
|
|
963
|
+
action: "test",
|
|
964
|
+
config_id: configId
|
|
965
|
+
});
|
|
966
|
+
}
|
|
967
|
+
// === PREFLIGHT AUTHORIZATION (Phase 3) ===
|
|
968
|
+
// Evaluate whether a proposed transaction should be allowed BEFORE execution.
|
|
969
|
+
// This does NOT move money - only decides if the transaction is permitted.
|
|
970
|
+
async preflightAuthorization(req) {
|
|
971
|
+
const response = await this.request("preflight-authorization", {
|
|
972
|
+
idempotency_key: req.idempotencyKey,
|
|
973
|
+
amount: req.amount,
|
|
974
|
+
currency: req.currency,
|
|
975
|
+
counterparty_name: req.counterpartyName,
|
|
976
|
+
authorizing_instrument_id: req.authorizingInstrumentId,
|
|
977
|
+
expected_date: req.expectedDate,
|
|
978
|
+
category: req.category
|
|
979
|
+
});
|
|
980
|
+
return {
|
|
981
|
+
success: response.success,
|
|
982
|
+
cached: response.cached,
|
|
983
|
+
decision: {
|
|
984
|
+
id: response.decision.id,
|
|
985
|
+
decision: response.decision.decision,
|
|
986
|
+
violatedPolicies: (response.decision.violated_policies || []).map((v) => ({
|
|
987
|
+
policyId: v.policy_id,
|
|
988
|
+
policyType: v.policy_type,
|
|
989
|
+
severity: v.severity,
|
|
990
|
+
reason: v.reason
|
|
991
|
+
})),
|
|
992
|
+
expiresAt: response.decision.expires_at,
|
|
993
|
+
createdAt: response.decision.created_at
|
|
994
|
+
},
|
|
995
|
+
message: response.message
|
|
996
|
+
};
|
|
997
|
+
}
|
|
998
|
+
// Convenience method: preflight check then record expense if allowed
|
|
999
|
+
async preflightAndRecordExpense(preflight, expense) {
|
|
1000
|
+
const preflightResult = await this.preflightAuthorization(preflight);
|
|
1001
|
+
if (preflightResult.decision.decision === "blocked") {
|
|
1002
|
+
return { preflight: preflightResult };
|
|
1003
|
+
}
|
|
1004
|
+
const transaction = await this.recordExpense({
|
|
1005
|
+
...expense,
|
|
1006
|
+
authorizationDecisionId: preflightResult.decision.id
|
|
1007
|
+
});
|
|
1008
|
+
return { preflight: preflightResult, transaction };
|
|
1009
|
+
}
|
|
1010
|
+
// Convenience method: preflight check then record bill if allowed
|
|
1011
|
+
async preflightAndRecordBill(preflight, bill) {
|
|
1012
|
+
const preflightResult = await this.preflightAuthorization(preflight);
|
|
1013
|
+
if (preflightResult.decision.decision === "blocked") {
|
|
1014
|
+
return { preflight: preflightResult };
|
|
1015
|
+
}
|
|
1016
|
+
const transaction = await this.recordBill({
|
|
1017
|
+
...bill,
|
|
1018
|
+
authorizationDecisionId: preflightResult.decision.id
|
|
1019
|
+
});
|
|
1020
|
+
return { preflight: preflightResult, transaction };
|
|
1021
|
+
}
|
|
1022
|
+
// === BANK IMPORT ===
|
|
1023
|
+
async getImportTemplates() {
|
|
1024
|
+
return this.request("import-transactions", { action: "get_templates" });
|
|
1025
|
+
}
|
|
1026
|
+
async parseImportFile(fileBase64, format) {
|
|
1027
|
+
return this.request("import-transactions", {
|
|
1028
|
+
action: "parse_preview",
|
|
1029
|
+
data: fileBase64,
|
|
1030
|
+
format
|
|
1031
|
+
});
|
|
1032
|
+
}
|
|
1033
|
+
async importTransactions(transactions) {
|
|
1034
|
+
return this.request("import-transactions", {
|
|
1035
|
+
action: "import",
|
|
1036
|
+
transactions
|
|
1037
|
+
});
|
|
1038
|
+
}
|
|
1039
|
+
async saveImportTemplate(template) {
|
|
1040
|
+
return this.request("import-transactions", {
|
|
1041
|
+
action: "save_template",
|
|
1042
|
+
template
|
|
1043
|
+
});
|
|
1044
|
+
}
|
|
1045
|
+
// === ESCROW / HELD FUNDS ===
|
|
1046
|
+
async getEscrowSummary() {
|
|
1047
|
+
const response = await this.requestGet("holds/summary");
|
|
1048
|
+
return {
|
|
1049
|
+
success: response.success,
|
|
1050
|
+
summary: response.summary || {}
|
|
1051
|
+
};
|
|
1052
|
+
}
|
|
1053
|
+
async getHeldFunds(options) {
|
|
1054
|
+
return this.requestGet("holds", {
|
|
1055
|
+
venture_id: options?.ventureId,
|
|
1056
|
+
participant_id: options?.creatorId,
|
|
1057
|
+
ready_only: options?.readyOnly,
|
|
1058
|
+
limit: options?.limit
|
|
1059
|
+
});
|
|
1060
|
+
}
|
|
1061
|
+
async releaseFunds(entryId, executeTransfer = true) {
|
|
1062
|
+
const response = await this.request(`holds/${entryId}/release`, {
|
|
1063
|
+
execute_transfer: executeTransfer
|
|
1064
|
+
});
|
|
1065
|
+
return {
|
|
1066
|
+
success: response.success,
|
|
1067
|
+
release_id: response.release?.id ?? null,
|
|
1068
|
+
entry_id: response.release?.hold_id ?? entryId,
|
|
1069
|
+
executed: Boolean(response.release?.executed),
|
|
1070
|
+
transfer_id: response.release?.transfer_id ?? null,
|
|
1071
|
+
transfer_status: response.release?.transfer_status ?? null,
|
|
1072
|
+
amount: response.release?.amount ?? null,
|
|
1073
|
+
currency: response.release?.currency ?? null
|
|
1074
|
+
};
|
|
1075
|
+
}
|
|
1076
|
+
// === PAYOUT ELIGIBILITY ===
|
|
1077
|
+
async checkPayoutEligibility(participantId) {
|
|
1078
|
+
const response = await this.requestGet(`participants/${participantId}/payout-eligibility`);
|
|
1079
|
+
return {
|
|
1080
|
+
success: response.success,
|
|
1081
|
+
participant_id: response.eligibility?.participant_id ?? participantId,
|
|
1082
|
+
eligible: Boolean(response.eligibility?.eligible),
|
|
1083
|
+
available_balance: response.eligibility?.available_balance ?? 0,
|
|
1084
|
+
issues: response.eligibility?.issues || [],
|
|
1085
|
+
requirements: response.eligibility?.requirements || {}
|
|
1086
|
+
};
|
|
1087
|
+
}
|
|
1088
|
+
// === HEALTH CHECKS ===
|
|
1089
|
+
async runHealthCheck() {
|
|
1090
|
+
return this.request("health-check", { action: "run" });
|
|
1091
|
+
}
|
|
1092
|
+
async getHealthStatus() {
|
|
1093
|
+
return this.request("health-check", { action: "status" });
|
|
1094
|
+
}
|
|
1095
|
+
async getHealthHistory() {
|
|
1096
|
+
return this.request("health-check", { action: "history" });
|
|
1097
|
+
}
|
|
1098
|
+
// === FINANCIAL REPORTS (DETAILED) ===
|
|
1099
|
+
async getAPAging(asOfDate) {
|
|
1100
|
+
return this.requestGet("ap-aging", { as_of_date: asOfDate });
|
|
1101
|
+
}
|
|
1102
|
+
async getARAging(asOfDate) {
|
|
1103
|
+
return this.requestGet("ar-aging", { as_of_date: asOfDate });
|
|
1104
|
+
}
|
|
1105
|
+
async getBalanceSheet(asOfDate) {
|
|
1106
|
+
return this.requestGet("balance-sheet", { as_of_date: asOfDate });
|
|
1107
|
+
}
|
|
1108
|
+
async getDetailedTrialBalance(options) {
|
|
1109
|
+
return this.requestGet("trial-balance", {
|
|
1110
|
+
as_of: options?.asOf,
|
|
1111
|
+
snapshot: options?.snapshot ? "true" : void 0
|
|
1112
|
+
});
|
|
1113
|
+
}
|
|
1114
|
+
async getDetailedProfitLoss(options) {
|
|
1115
|
+
return this.requestGet("profit-loss", {
|
|
1116
|
+
year: options?.year,
|
|
1117
|
+
month: options?.month,
|
|
1118
|
+
quarter: options?.quarter,
|
|
1119
|
+
start_date: options?.startDate,
|
|
1120
|
+
end_date: options?.endDate,
|
|
1121
|
+
breakdown: options?.breakdown
|
|
1122
|
+
});
|
|
1123
|
+
}
|
|
1124
|
+
async getRunway() {
|
|
1125
|
+
return this.requestGet("get-runway");
|
|
1126
|
+
}
|
|
1127
|
+
// === TREASURY RESOURCES ===
|
|
1128
|
+
async createParticipant(req) {
|
|
1129
|
+
const response = await this.request("participants", {
|
|
1130
|
+
participant_id: req.participantId,
|
|
1131
|
+
user_id: req.userId,
|
|
1132
|
+
display_name: req.displayName,
|
|
1133
|
+
email: req.email,
|
|
1134
|
+
default_split_percent: req.defaultSplitPercent,
|
|
1135
|
+
tax_info: req.taxInfo ? {
|
|
1136
|
+
tax_id_type: req.taxInfo.taxIdType,
|
|
1137
|
+
tax_id_last4: req.taxInfo.taxIdLast4,
|
|
1138
|
+
legal_name: req.taxInfo.legalName,
|
|
1139
|
+
business_type: req.taxInfo.businessType,
|
|
1140
|
+
address: req.taxInfo.address ? {
|
|
1141
|
+
line1: req.taxInfo.address.line1,
|
|
1142
|
+
line2: req.taxInfo.address.line2,
|
|
1143
|
+
city: req.taxInfo.address.city,
|
|
1144
|
+
state: req.taxInfo.address.state,
|
|
1145
|
+
postal_code: req.taxInfo.address.postalCode,
|
|
1146
|
+
country: req.taxInfo.address.country
|
|
1147
|
+
} : void 0
|
|
1148
|
+
} : void 0,
|
|
1149
|
+
payout_preferences: req.payoutPreferences ? {
|
|
1150
|
+
schedule: req.payoutPreferences.schedule,
|
|
1151
|
+
minimum_amount: req.payoutPreferences.minimumAmount,
|
|
1152
|
+
method: req.payoutPreferences.method
|
|
1153
|
+
} : void 0,
|
|
1154
|
+
metadata: req.metadata
|
|
1155
|
+
});
|
|
1156
|
+
const participant = response.participant || {};
|
|
1157
|
+
return {
|
|
1158
|
+
success: response.success,
|
|
1159
|
+
participant: {
|
|
1160
|
+
id: participant.id,
|
|
1161
|
+
accountId: participant.account_id,
|
|
1162
|
+
linkedUserId: participant.linked_user_id ?? null,
|
|
1163
|
+
displayName: participant.display_name,
|
|
1164
|
+
email: participant.email,
|
|
1165
|
+
defaultSplitPercent: participant.default_split_percent,
|
|
1166
|
+
payoutPreferences: participant.payout_preferences || {},
|
|
1167
|
+
createdAt: participant.created_at
|
|
1168
|
+
}
|
|
1169
|
+
};
|
|
1170
|
+
}
|
|
1171
|
+
async listParticipants() {
|
|
1172
|
+
const response = await this.requestGet("participants");
|
|
1173
|
+
return {
|
|
1174
|
+
success: response.success,
|
|
1175
|
+
participants: (response.participants || []).map((participant) => ({
|
|
1176
|
+
id: participant.id,
|
|
1177
|
+
linkedUserId: participant.linked_user_id ?? null,
|
|
1178
|
+
name: participant.name ?? null,
|
|
1179
|
+
tier: participant.tier ?? null,
|
|
1180
|
+
ledgerBalance: participant.ledger_balance,
|
|
1181
|
+
heldAmount: participant.held_amount,
|
|
1182
|
+
availableBalance: participant.available_balance
|
|
1183
|
+
}))
|
|
1184
|
+
};
|
|
1185
|
+
}
|
|
1186
|
+
async getParticipant(participantId) {
|
|
1187
|
+
const response = await this.requestGet(`participants/${participantId}`);
|
|
1188
|
+
const participant = response.participant || {};
|
|
1189
|
+
return {
|
|
1190
|
+
success: response.success,
|
|
1191
|
+
participant: {
|
|
1192
|
+
id: participant.id,
|
|
1193
|
+
linkedUserId: participant.linked_user_id ?? null,
|
|
1194
|
+
name: participant.name ?? null,
|
|
1195
|
+
tier: participant.tier ?? null,
|
|
1196
|
+
customSplitPercent: participant.custom_split_percent ?? null,
|
|
1197
|
+
ledgerBalance: participant.ledger_balance,
|
|
1198
|
+
heldAmount: participant.held_amount,
|
|
1199
|
+
availableBalance: participant.available_balance,
|
|
1200
|
+
holds: (participant.holds || []).map((hold) => ({
|
|
1201
|
+
amount: hold.amount,
|
|
1202
|
+
reason: hold.reason ?? null,
|
|
1203
|
+
releaseDate: hold.release_date ?? null,
|
|
1204
|
+
status: hold.status
|
|
1205
|
+
}))
|
|
1206
|
+
}
|
|
1207
|
+
};
|
|
1208
|
+
}
|
|
1209
|
+
async getParticipantPayoutEligibility(participantId) {
|
|
1210
|
+
const response = await this.requestGet(`participants/${participantId}/payout-eligibility`);
|
|
1211
|
+
return {
|
|
1212
|
+
success: response.success,
|
|
1213
|
+
eligibility: {
|
|
1214
|
+
participantId: response.eligibility?.participant_id ?? participantId,
|
|
1215
|
+
eligible: Boolean(response.eligibility?.eligible),
|
|
1216
|
+
availableBalance: response.eligibility?.available_balance ?? 0,
|
|
1217
|
+
issues: response.eligibility?.issues || [],
|
|
1218
|
+
requirements: response.eligibility?.requirements || {}
|
|
1219
|
+
}
|
|
1220
|
+
};
|
|
1221
|
+
}
|
|
1222
|
+
// === LEDGER MANAGEMENT ===
|
|
1223
|
+
async createLedger(req) {
|
|
1224
|
+
const response = await this.request("create-ledger", {
|
|
1225
|
+
business_name: req.businessName,
|
|
1226
|
+
owner_email: req.ownerEmail,
|
|
1227
|
+
ledger_mode: req.ledgerMode,
|
|
1228
|
+
settings: req.settings ? {
|
|
1229
|
+
default_tax_rate: req.settings.defaultTaxRate,
|
|
1230
|
+
default_split_percent: req.settings.defaultSplitPercent,
|
|
1231
|
+
platform_fee_percent: req.settings.platformFeePercent,
|
|
1232
|
+
min_payout_amount: req.settings.minPayoutAmount,
|
|
1233
|
+
payout_schedule: req.settings.payoutSchedule,
|
|
1234
|
+
tax_withholding_percent: req.settings.taxWithholdingPercent,
|
|
1235
|
+
currency: req.settings.currency,
|
|
1236
|
+
fiscal_year_start: req.settings.fiscalYearStart,
|
|
1237
|
+
receipt_threshold: req.settings.receiptThreshold
|
|
1238
|
+
} : void 0
|
|
1239
|
+
});
|
|
1240
|
+
return {
|
|
1241
|
+
success: response.success,
|
|
1242
|
+
ledger: {
|
|
1243
|
+
id: response.ledger.id,
|
|
1244
|
+
businessName: response.ledger.business_name,
|
|
1245
|
+
ledgerMode: response.ledger.ledger_mode,
|
|
1246
|
+
apiKey: response.ledger.api_key,
|
|
1247
|
+
status: response.ledger.status,
|
|
1248
|
+
createdAt: response.ledger.created_at
|
|
1249
|
+
},
|
|
1250
|
+
warning: response.warning
|
|
1251
|
+
};
|
|
1252
|
+
}
|
|
1253
|
+
// === ACCOUNTING ADJUSTMENTS ===
|
|
1254
|
+
async recordAdjustment(req) {
|
|
1255
|
+
return this.request("record-adjustment", {
|
|
1256
|
+
adjustment_type: req.adjustmentType,
|
|
1257
|
+
adjustment_date: req.adjustmentDate,
|
|
1258
|
+
entries: req.entries.map((e) => ({
|
|
1259
|
+
account_type: e.accountType,
|
|
1260
|
+
entity_id: e.entityId,
|
|
1261
|
+
entry_type: e.entryType,
|
|
1262
|
+
amount: e.amount
|
|
1263
|
+
})),
|
|
1264
|
+
reason: req.reason,
|
|
1265
|
+
original_transaction_id: req.originalTransactionId,
|
|
1266
|
+
supporting_documentation: req.supportingDocumentation,
|
|
1267
|
+
prepared_by: req.preparedBy
|
|
1268
|
+
});
|
|
1269
|
+
}
|
|
1270
|
+
async recordOpeningBalance(req) {
|
|
1271
|
+
return this.request("record-opening-balance", {
|
|
1272
|
+
as_of_date: req.asOfDate,
|
|
1273
|
+
source: req.source,
|
|
1274
|
+
source_description: req.sourceDescription,
|
|
1275
|
+
balances: req.balances.map((b) => ({
|
|
1276
|
+
account_type: b.accountType,
|
|
1277
|
+
entity_id: b.entityId,
|
|
1278
|
+
balance: b.balance
|
|
1279
|
+
}))
|
|
1280
|
+
});
|
|
1281
|
+
}
|
|
1282
|
+
async recordTransfer(req) {
|
|
1283
|
+
return this.request("record-transfer", {
|
|
1284
|
+
from_account_type: req.fromAccountType,
|
|
1285
|
+
to_account_type: req.toAccountType,
|
|
1286
|
+
amount: req.amount,
|
|
1287
|
+
transfer_type: req.transferType,
|
|
1288
|
+
description: req.description,
|
|
1289
|
+
reference_id: req.referenceId
|
|
1290
|
+
});
|
|
1291
|
+
}
|
|
1292
|
+
// === RISK & POLICY ===
|
|
1293
|
+
async evaluateFraud(req) {
|
|
1294
|
+
const response = await this.request("fraud/evaluations", {
|
|
1295
|
+
idempotency_key: req.idempotencyKey,
|
|
1296
|
+
amount: req.amount,
|
|
1297
|
+
currency: req.currency,
|
|
1298
|
+
counterparty_name: req.counterpartyName,
|
|
1299
|
+
authorizing_instrument_id: req.authorizingInstrumentId,
|
|
1300
|
+
expected_date: req.expectedDate,
|
|
1301
|
+
category: req.category
|
|
1302
|
+
});
|
|
1303
|
+
return {
|
|
1304
|
+
success: response.success,
|
|
1305
|
+
cached: response.cached,
|
|
1306
|
+
evaluation: {
|
|
1307
|
+
id: response.evaluation.id,
|
|
1308
|
+
signal: response.evaluation.signal,
|
|
1309
|
+
riskFactors: (response.evaluation.risk_factors || []).map((f) => ({
|
|
1310
|
+
policyId: f.policy_id,
|
|
1311
|
+
policyType: f.policy_type,
|
|
1312
|
+
severity: f.severity,
|
|
1313
|
+
indicator: f.indicator
|
|
1314
|
+
})),
|
|
1315
|
+
validUntil: response.evaluation.valid_until,
|
|
1316
|
+
createdAt: response.evaluation.created_at,
|
|
1317
|
+
acknowledgedAt: response.evaluation.acknowledged_at
|
|
1318
|
+
}
|
|
1319
|
+
};
|
|
1320
|
+
}
|
|
1321
|
+
async createFraudPolicy(req) {
|
|
1322
|
+
const response = await this.request("fraud/policies", {
|
|
1323
|
+
policy_type: req.policyType,
|
|
1324
|
+
config: req.config,
|
|
1325
|
+
severity: req.severity,
|
|
1326
|
+
priority: req.priority
|
|
1327
|
+
});
|
|
1328
|
+
return {
|
|
1329
|
+
success: response.success,
|
|
1330
|
+
policy: {
|
|
1331
|
+
id: response.policy.id,
|
|
1332
|
+
type: response.policy.type,
|
|
1333
|
+
severity: response.policy.severity,
|
|
1334
|
+
priority: response.policy.priority,
|
|
1335
|
+
isActive: Boolean(response.policy.is_active),
|
|
1336
|
+
config: response.policy.config || {},
|
|
1337
|
+
createdAt: response.policy.created_at ?? null,
|
|
1338
|
+
updatedAt: response.policy.updated_at ?? null
|
|
1339
|
+
}
|
|
1340
|
+
};
|
|
1341
|
+
}
|
|
1342
|
+
async listFraudPolicies() {
|
|
1343
|
+
const response = await this.requestGet("fraud/policies");
|
|
1344
|
+
return {
|
|
1345
|
+
success: response.success,
|
|
1346
|
+
policies: (response.policies || []).map((policy) => ({
|
|
1347
|
+
id: policy.id,
|
|
1348
|
+
type: policy.type,
|
|
1349
|
+
severity: policy.severity,
|
|
1350
|
+
priority: policy.priority,
|
|
1351
|
+
isActive: Boolean(policy.is_active),
|
|
1352
|
+
config: policy.config || {},
|
|
1353
|
+
createdAt: policy.created_at ?? null,
|
|
1354
|
+
updatedAt: policy.updated_at ?? null
|
|
1355
|
+
}))
|
|
1356
|
+
};
|
|
1357
|
+
}
|
|
1358
|
+
async deleteFraudPolicy(policyId) {
|
|
1359
|
+
const response = await this.requestDelete(`fraud/policies/${policyId}`);
|
|
1360
|
+
return {
|
|
1361
|
+
success: response.success,
|
|
1362
|
+
deleted: Boolean(response.deleted),
|
|
1363
|
+
policyId: response.policy_id
|
|
1364
|
+
};
|
|
1365
|
+
}
|
|
1366
|
+
// === TAX DOCUMENTS ===
|
|
1367
|
+
async calculateTaxForParticipant(participantId, taxYear) {
|
|
1368
|
+
const response = await this.requestGet(`tax/calculations/${participantId}`, {
|
|
1369
|
+
tax_year: taxYear
|
|
1370
|
+
});
|
|
1371
|
+
return {
|
|
1372
|
+
success: response.success,
|
|
1373
|
+
calculation: {
|
|
1374
|
+
participantId: response.calculation.participant_id,
|
|
1375
|
+
taxYear: response.calculation.tax_year,
|
|
1376
|
+
grossPayments: response.calculation.gross_payments,
|
|
1377
|
+
transactionCount: response.calculation.transaction_count,
|
|
1378
|
+
requires1099: Boolean(response.calculation.requires_1099),
|
|
1379
|
+
monthlyTotals: response.calculation.monthly_totals || {},
|
|
1380
|
+
threshold: response.calculation.threshold,
|
|
1381
|
+
linkedUserId: response.calculation.linked_user_id ?? null,
|
|
1382
|
+
sharedTaxProfile: response.calculation.shared_tax_profile ? {
|
|
1383
|
+
status: response.calculation.shared_tax_profile.status,
|
|
1384
|
+
legalName: response.calculation.shared_tax_profile.legal_name ?? null,
|
|
1385
|
+
taxIdLast4: response.calculation.shared_tax_profile.tax_id_last4 ?? null
|
|
1386
|
+
} : null
|
|
1387
|
+
}
|
|
1388
|
+
};
|
|
1389
|
+
}
|
|
1390
|
+
async generateAllTaxDocuments(taxYear) {
|
|
1391
|
+
const response = await this.request("tax/documents/generate", { tax_year: taxYear });
|
|
1392
|
+
return {
|
|
1393
|
+
success: response.success,
|
|
1394
|
+
generation: {
|
|
1395
|
+
taxYear: response.generation.tax_year,
|
|
1396
|
+
created: response.generation.created,
|
|
1397
|
+
skipped: response.generation.skipped,
|
|
1398
|
+
totalAmount: response.generation.total_amount
|
|
1399
|
+
}
|
|
1400
|
+
};
|
|
1401
|
+
}
|
|
1402
|
+
async listTaxDocuments(taxYear) {
|
|
1403
|
+
const response = await this.requestGet("tax/documents", { tax_year: taxYear });
|
|
1404
|
+
return {
|
|
1405
|
+
success: response.success,
|
|
1406
|
+
taxYear: response.tax_year,
|
|
1407
|
+
summary: {
|
|
1408
|
+
totalDocuments: response.summary?.total_documents ?? 0,
|
|
1409
|
+
totalAmount: response.summary?.total_amount ?? 0,
|
|
1410
|
+
byStatus: {
|
|
1411
|
+
calculated: response.summary?.by_status?.calculated ?? 0,
|
|
1412
|
+
exported: response.summary?.by_status?.exported ?? 0,
|
|
1413
|
+
filed: response.summary?.by_status?.filed ?? 0
|
|
1414
|
+
}
|
|
1415
|
+
},
|
|
1416
|
+
documents: response.documents || []
|
|
1417
|
+
};
|
|
1418
|
+
}
|
|
1419
|
+
async getTaxDocument(documentId) {
|
|
1420
|
+
const response = await this.requestGet(`tax/documents/${documentId}`);
|
|
1421
|
+
return {
|
|
1422
|
+
success: response.success,
|
|
1423
|
+
document: response.document
|
|
1424
|
+
};
|
|
1425
|
+
}
|
|
1426
|
+
async exportTaxDocuments(taxYear, format = "json") {
|
|
1427
|
+
if (format === "csv") {
|
|
1428
|
+
const response = await this.requestGetRaw("tax/documents/export", { tax_year: taxYear, format });
|
|
1429
|
+
const csv = await response.text();
|
|
1430
|
+
const disposition = response.headers.get("Content-Disposition") || "";
|
|
1431
|
+
const filenameMatch = disposition.match(/filename="?([^"]+)"?/);
|
|
1432
|
+
return { csv, filename: filenameMatch?.[1] || `1099_export_${taxYear}.csv` };
|
|
1433
|
+
}
|
|
1434
|
+
return this.requestGet("tax/documents/export", { tax_year: taxYear, format });
|
|
1435
|
+
}
|
|
1436
|
+
async markTaxDocumentFiled(documentId) {
|
|
1437
|
+
const response = await this.request(`tax/documents/${documentId}/mark-filed`, {});
|
|
1438
|
+
return {
|
|
1439
|
+
success: response.success,
|
|
1440
|
+
document: {
|
|
1441
|
+
id: response.document.id,
|
|
1442
|
+
tax_year: response.document.tax_year,
|
|
1443
|
+
status: response.document.status
|
|
1444
|
+
}
|
|
1445
|
+
};
|
|
1446
|
+
}
|
|
1447
|
+
async generateTaxSummary(taxYear, creatorId) {
|
|
1448
|
+
const response = await this.requestGet(`tax/summaries/${taxYear}`, {
|
|
1449
|
+
participant_id: creatorId
|
|
1450
|
+
});
|
|
1451
|
+
return {
|
|
1452
|
+
success: response.success,
|
|
1453
|
+
taxYear: response.tax_year,
|
|
1454
|
+
note: response.note,
|
|
1455
|
+
summaries: (response.summaries || []).map((summary) => ({
|
|
1456
|
+
participantId: summary.participant_id,
|
|
1457
|
+
linkedUserId: summary.linked_user_id ?? null,
|
|
1458
|
+
grossEarnings: summary.gross_earnings,
|
|
1459
|
+
refundsIssued: summary.refunds_issued,
|
|
1460
|
+
netEarnings: summary.net_earnings,
|
|
1461
|
+
totalPaidOut: summary.total_paid_out,
|
|
1462
|
+
requires1099: Boolean(summary.requires_1099),
|
|
1463
|
+
sharedTaxProfile: summary.shared_tax_profile ? {
|
|
1464
|
+
status: summary.shared_tax_profile.status,
|
|
1465
|
+
legalName: summary.shared_tax_profile.legal_name ?? null,
|
|
1466
|
+
taxIdLast4: summary.shared_tax_profile.tax_id_last4 ?? null
|
|
1467
|
+
} : null
|
|
1468
|
+
})),
|
|
1469
|
+
totals: {
|
|
1470
|
+
totalGross: response.totals.total_gross,
|
|
1471
|
+
totalRefunds: response.totals.total_refunds,
|
|
1472
|
+
totalNet: response.totals.total_net,
|
|
1473
|
+
totalPaid: response.totals.total_paid,
|
|
1474
|
+
participantsRequiring1099: response.totals.participants_requiring_1099
|
|
1475
|
+
}
|
|
1476
|
+
};
|
|
1477
|
+
}
|
|
1478
|
+
async markTaxDocumentsFiledBulk(taxYear) {
|
|
1479
|
+
return this.request("tax/documents/mark-filed", { tax_year: taxYear });
|
|
1480
|
+
}
|
|
1481
|
+
async correctTaxDocument(documentId, params) {
|
|
1482
|
+
return this.request(`tax/documents/${documentId}/correct`, {
|
|
1483
|
+
reason: params.reason,
|
|
1484
|
+
gross_amount: params.grossAmount,
|
|
1485
|
+
federal_withholding: params.federalWithholding,
|
|
1486
|
+
state_withholding: params.stateWithholding
|
|
1487
|
+
});
|
|
1488
|
+
}
|
|
1489
|
+
async deliverTaxDocumentCopyB(taxYear) {
|
|
1490
|
+
return this.request("tax/documents/deliver-copy-b", { tax_year: taxYear });
|
|
1491
|
+
}
|
|
1492
|
+
async generateTaxDocumentPdf(documentId, copyType) {
|
|
1493
|
+
return this.request(`tax/documents/${documentId}/pdf`, {
|
|
1494
|
+
copy_type: copyType
|
|
1495
|
+
});
|
|
1496
|
+
}
|
|
1497
|
+
async generateTaxDocumentPdfBatch(taxYear, copyType) {
|
|
1498
|
+
return this.request("tax/documents/pdf/batch", {
|
|
1499
|
+
tax_year: taxYear,
|
|
1500
|
+
copy_type: copyType
|
|
1501
|
+
});
|
|
1502
|
+
}
|
|
1503
|
+
// === COMPLIANCE MONITORING ===
|
|
1504
|
+
async getComplianceOverview(options) {
|
|
1505
|
+
const response = await this.requestGet("compliance/overview", {
|
|
1506
|
+
days: options?.days,
|
|
1507
|
+
hours: options?.hours
|
|
1508
|
+
});
|
|
1509
|
+
return {
|
|
1510
|
+
success: response.success,
|
|
1511
|
+
overview: {
|
|
1512
|
+
windowDays: response.overview.window_days,
|
|
1513
|
+
accessWindowHours: response.overview.access_window_hours,
|
|
1514
|
+
totalEvents: response.overview.total_events,
|
|
1515
|
+
uniqueIps: response.overview.unique_ips,
|
|
1516
|
+
uniqueActors: response.overview.unique_actors,
|
|
1517
|
+
highRiskEvents: response.overview.high_risk_events,
|
|
1518
|
+
criticalRiskEvents: response.overview.critical_risk_events,
|
|
1519
|
+
failedAuthEvents: response.overview.failed_auth_events,
|
|
1520
|
+
payoutsFailed: response.overview.payouts_failed,
|
|
1521
|
+
refundsRecorded: response.overview.refunds_recorded,
|
|
1522
|
+
disputeEvents: response.overview.dispute_events
|
|
1523
|
+
},
|
|
1524
|
+
note: response.note
|
|
1525
|
+
};
|
|
1526
|
+
}
|
|
1527
|
+
async listComplianceAccessPatterns(options) {
|
|
1528
|
+
const response = await this.requestGet("compliance/access-patterns", {
|
|
1529
|
+
hours: options?.hours,
|
|
1530
|
+
limit: options?.limit
|
|
1531
|
+
});
|
|
1532
|
+
return {
|
|
1533
|
+
success: response.success,
|
|
1534
|
+
windowHours: response.window_hours,
|
|
1535
|
+
count: response.count,
|
|
1536
|
+
patterns: (response.patterns || []).map((pattern) => ({
|
|
1537
|
+
ipAddress: pattern.ip_address,
|
|
1538
|
+
hour: pattern.hour,
|
|
1539
|
+
requestCount: pattern.request_count,
|
|
1540
|
+
uniqueActions: pattern.unique_actions,
|
|
1541
|
+
actions: pattern.actions || [],
|
|
1542
|
+
maxRiskScore: pattern.max_risk_score,
|
|
1543
|
+
failedAuths: pattern.failed_auths
|
|
1544
|
+
}))
|
|
1545
|
+
};
|
|
1546
|
+
}
|
|
1547
|
+
async listComplianceFinancialActivity(options) {
|
|
1548
|
+
const response = await this.requestGet("compliance/financial-activity", {
|
|
1549
|
+
days: options?.days
|
|
1550
|
+
});
|
|
1551
|
+
return {
|
|
1552
|
+
success: response.success,
|
|
1553
|
+
windowDays: response.window_days,
|
|
1554
|
+
activity: (response.activity || []).map((entry) => ({
|
|
1555
|
+
date: entry.date,
|
|
1556
|
+
payoutsInitiated: entry.payouts_initiated,
|
|
1557
|
+
payoutsCompleted: entry.payouts_completed,
|
|
1558
|
+
payoutsFailed: entry.payouts_failed,
|
|
1559
|
+
salesRecorded: entry.sales_recorded,
|
|
1560
|
+
refundsRecorded: entry.refunds_recorded,
|
|
1561
|
+
disputeEvents: entry.dispute_events
|
|
1562
|
+
}))
|
|
1563
|
+
};
|
|
1564
|
+
}
|
|
1565
|
+
async listComplianceSecuritySummary(options) {
|
|
1566
|
+
const response = await this.requestGet("compliance/security-summary", {
|
|
1567
|
+
days: options?.days
|
|
1568
|
+
});
|
|
1569
|
+
return {
|
|
1570
|
+
success: response.success,
|
|
1571
|
+
windowDays: response.window_days,
|
|
1572
|
+
summary: (response.summary || []).map((entry) => ({
|
|
1573
|
+
date: entry.date,
|
|
1574
|
+
action: entry.action,
|
|
1575
|
+
eventCount: entry.event_count,
|
|
1576
|
+
uniqueIps: entry.unique_ips,
|
|
1577
|
+
uniqueActors: entry.unique_actors,
|
|
1578
|
+
avgRiskScore: entry.avg_risk_score,
|
|
1579
|
+
maxRiskScore: entry.max_risk_score,
|
|
1580
|
+
highRiskCount: entry.high_risk_count,
|
|
1581
|
+
criticalRiskCount: entry.critical_risk_count
|
|
1582
|
+
}))
|
|
1583
|
+
};
|
|
1584
|
+
}
|
|
1585
|
+
async exportReport(req) {
|
|
1586
|
+
const body = {
|
|
1587
|
+
report_type: req.reportType,
|
|
1588
|
+
format: req.format,
|
|
1589
|
+
start_date: req.startDate,
|
|
1590
|
+
end_date: req.endDate,
|
|
1591
|
+
creator_id: req.creatorId
|
|
1592
|
+
};
|
|
1593
|
+
if (req.format === "csv") {
|
|
1594
|
+
const response = await this.requestRaw("export-report", body);
|
|
1595
|
+
const csv = await response.text();
|
|
1596
|
+
const disposition = response.headers.get("Content-Disposition") || "";
|
|
1597
|
+
const filenameMatch = disposition.match(/filename="?([^"]+)"?/);
|
|
1598
|
+
return { csv, filename: filenameMatch?.[1] || `${req.reportType}.csv` };
|
|
1599
|
+
}
|
|
1600
|
+
return this.request("export-report", body);
|
|
1601
|
+
}
|
|
1602
|
+
// === RECEIPTS ===
|
|
1603
|
+
async uploadReceipt(req) {
|
|
1604
|
+
return this.request("upload-receipt", {
|
|
1605
|
+
file_url: req.fileUrl,
|
|
1606
|
+
file_name: req.fileName,
|
|
1607
|
+
file_size: req.fileSize,
|
|
1608
|
+
mime_type: req.mimeType,
|
|
1609
|
+
merchant_name: req.merchantName,
|
|
1610
|
+
transaction_date: req.transactionDate,
|
|
1611
|
+
total_amount: req.totalAmount,
|
|
1612
|
+
transaction_id: req.transactionId
|
|
1613
|
+
});
|
|
1614
|
+
}
|
|
1615
|
+
// === PAYMENTS ===
|
|
1616
|
+
async receivePayment(req) {
|
|
1617
|
+
return this.request("receive-payment", {
|
|
1618
|
+
amount: req.amount,
|
|
1619
|
+
invoice_transaction_id: req.invoiceTransactionId,
|
|
1620
|
+
customer_name: req.customerName,
|
|
1621
|
+
customer_id: req.customerId,
|
|
1622
|
+
reference_id: req.referenceId,
|
|
1623
|
+
payment_method: req.paymentMethod,
|
|
1624
|
+
payment_date: req.paymentDate,
|
|
1625
|
+
metadata: req.metadata
|
|
1626
|
+
});
|
|
1627
|
+
}
|
|
1628
|
+
// === BREACH ALERTS ===
|
|
1629
|
+
async sendBreachAlert(req) {
|
|
1630
|
+
const response = await this.request("send-breach-alert", {
|
|
1631
|
+
cash_balance: req.cashBalance,
|
|
1632
|
+
pending_total: req.pendingTotal,
|
|
1633
|
+
shortfall: req.shortfall,
|
|
1634
|
+
coverage_ratio: req.coverageRatio,
|
|
1635
|
+
triggered_by: req.triggeredBy,
|
|
1636
|
+
instrument_id: req.instrumentId,
|
|
1637
|
+
external_ref: req.externalRef,
|
|
1638
|
+
projections_created: req.projectionsCreated,
|
|
1639
|
+
channel: req.channel
|
|
1640
|
+
});
|
|
1641
|
+
return {
|
|
1642
|
+
success: response.success,
|
|
1643
|
+
message: response.message,
|
|
1644
|
+
alertsSent: response.alerts_sent ?? 0,
|
|
1645
|
+
alertsFailed: response.alerts_failed,
|
|
1646
|
+
alertsSkipped: response.alerts_skipped,
|
|
1647
|
+
results: response.results ? response.results.map((r) => ({
|
|
1648
|
+
channel: r.channel,
|
|
1649
|
+
success: r.success,
|
|
1650
|
+
error: r.error
|
|
1651
|
+
})) : void 0
|
|
1652
|
+
};
|
|
1653
|
+
}
|
|
1654
|
+
// === WALLETS ===
|
|
1655
|
+
mapWalletObject(wallet) {
|
|
1656
|
+
return {
|
|
1657
|
+
id: wallet.id,
|
|
1658
|
+
object: "wallet",
|
|
1659
|
+
walletType: wallet.wallet_type,
|
|
1660
|
+
scopeType: wallet.scope_type,
|
|
1661
|
+
ownerId: wallet.owner_id ?? null,
|
|
1662
|
+
ownerType: wallet.owner_type ?? null,
|
|
1663
|
+
participantId: wallet.participant_id ?? null,
|
|
1664
|
+
accountType: wallet.account_type,
|
|
1665
|
+
name: wallet.name ?? null,
|
|
1666
|
+
currency: wallet.currency,
|
|
1667
|
+
status: wallet.status,
|
|
1668
|
+
balance: wallet.balance,
|
|
1669
|
+
heldAmount: wallet.held_amount ?? 0,
|
|
1670
|
+
availableBalance: wallet.available_balance ?? wallet.balance,
|
|
1671
|
+
redeemable: wallet.redeemable === true,
|
|
1672
|
+
transferable: wallet.transferable === true,
|
|
1673
|
+
topupSupported: wallet.topup_supported === true,
|
|
1674
|
+
payoutSupported: wallet.payout_supported === true,
|
|
1675
|
+
createdAt: wallet.created_at ?? null,
|
|
1676
|
+
metadata: wallet.metadata || {}
|
|
1677
|
+
};
|
|
1678
|
+
}
|
|
1679
|
+
async listWallets(filters = {}) {
|
|
1680
|
+
const response = await this.requestGet("wallets", {
|
|
1681
|
+
owner_id: filters.ownerId,
|
|
1682
|
+
owner_type: filters.ownerType,
|
|
1683
|
+
wallet_type: filters.walletType,
|
|
1684
|
+
limit: filters.limit,
|
|
1685
|
+
offset: filters.offset
|
|
1686
|
+
});
|
|
1687
|
+
return {
|
|
1688
|
+
success: response.success,
|
|
1689
|
+
wallets: (response.wallets || []).map((wallet) => this.mapWalletObject(wallet)),
|
|
1690
|
+
total: response.total,
|
|
1691
|
+
limit: response.limit,
|
|
1692
|
+
offset: response.offset
|
|
1693
|
+
};
|
|
1694
|
+
}
|
|
1695
|
+
async createWallet(req) {
|
|
1696
|
+
const response = await this.request("wallets", {
|
|
1697
|
+
owner_id: req.ownerId,
|
|
1698
|
+
participant_id: req.participantId,
|
|
1699
|
+
owner_type: req.ownerType,
|
|
1700
|
+
wallet_type: req.walletType,
|
|
1701
|
+
name: req.name,
|
|
1702
|
+
metadata: req.metadata
|
|
1703
|
+
});
|
|
1704
|
+
return {
|
|
1705
|
+
success: response.success,
|
|
1706
|
+
created: response.created === true,
|
|
1707
|
+
wallet: this.mapWalletObject(response.wallet)
|
|
1708
|
+
};
|
|
1709
|
+
}
|
|
1710
|
+
async getWallet(walletId) {
|
|
1711
|
+
const response = await this.requestGet(`wallets/${walletId}`);
|
|
1712
|
+
return {
|
|
1713
|
+
success: response.success,
|
|
1714
|
+
wallet: this.mapWalletObject(response.wallet)
|
|
1715
|
+
};
|
|
1716
|
+
}
|
|
1717
|
+
async getWalletEntries(walletId, options) {
|
|
1718
|
+
const response = await this.requestGet(`wallets/${walletId}/entries`, {
|
|
1719
|
+
limit: options?.limit,
|
|
1720
|
+
offset: options?.offset
|
|
1721
|
+
});
|
|
1722
|
+
return {
|
|
1723
|
+
success: response.success,
|
|
1724
|
+
wallet: response.wallet ? this.mapWalletObject(response.wallet) : null,
|
|
1725
|
+
entries: (response.entries || []).map((t) => ({
|
|
1726
|
+
entryId: t.entry_id,
|
|
1727
|
+
entryType: t.entry_type,
|
|
1728
|
+
amount: t.amount,
|
|
1729
|
+
transactionId: t.transaction_id,
|
|
1730
|
+
referenceId: t.reference_id,
|
|
1731
|
+
transactionType: t.transaction_type,
|
|
1732
|
+
description: t.description,
|
|
1733
|
+
status: t.status,
|
|
1734
|
+
metadata: t.metadata,
|
|
1735
|
+
createdAt: t.created_at
|
|
1736
|
+
})),
|
|
1737
|
+
total: response.total,
|
|
1738
|
+
limit: response.limit,
|
|
1739
|
+
offset: response.offset
|
|
1740
|
+
};
|
|
1741
|
+
}
|
|
1742
|
+
async topUpWallet(req) {
|
|
1743
|
+
const response = await this.request(`wallets/${req.walletId}/topups`, {
|
|
1744
|
+
amount: req.amount,
|
|
1745
|
+
reference_id: req.referenceId,
|
|
1746
|
+
description: req.description,
|
|
1747
|
+
metadata: req.metadata
|
|
1748
|
+
});
|
|
1749
|
+
const topup = response.topup || response.deposit || response;
|
|
1750
|
+
return {
|
|
1751
|
+
success: response.success,
|
|
1752
|
+
walletId: topup.wallet_id ?? null,
|
|
1753
|
+
ownerId: topup.owner_id ?? null,
|
|
1754
|
+
transactionId: topup.transaction_id ?? null,
|
|
1755
|
+
balance: topup.balance ?? null
|
|
1756
|
+
};
|
|
1757
|
+
}
|
|
1758
|
+
async withdrawFromWallet(req) {
|
|
1759
|
+
const response = await this.request(`wallets/${req.walletId}/withdrawals`, {
|
|
1760
|
+
amount: req.amount,
|
|
1761
|
+
reference_id: req.referenceId,
|
|
1762
|
+
description: req.description,
|
|
1763
|
+
metadata: req.metadata
|
|
1764
|
+
});
|
|
1765
|
+
const withdrawal = response.withdrawal || response;
|
|
1766
|
+
return {
|
|
1767
|
+
success: response.success,
|
|
1768
|
+
walletId: withdrawal.wallet_id ?? null,
|
|
1769
|
+
ownerId: withdrawal.owner_id ?? null,
|
|
1770
|
+
transactionId: withdrawal.transaction_id ?? null,
|
|
1771
|
+
balance: withdrawal.balance ?? null
|
|
1772
|
+
};
|
|
1773
|
+
}
|
|
1774
|
+
async createTransfer(req) {
|
|
1775
|
+
const response = await this.request("transfers", {
|
|
1776
|
+
from_participant_id: req.fromParticipantId,
|
|
1777
|
+
to_participant_id: req.toParticipantId,
|
|
1778
|
+
amount: req.amount,
|
|
1779
|
+
reference_id: req.referenceId,
|
|
1780
|
+
description: req.description,
|
|
1781
|
+
metadata: req.metadata
|
|
1782
|
+
});
|
|
1783
|
+
const transfer = response.transfer || response;
|
|
1784
|
+
return {
|
|
1785
|
+
success: response.success,
|
|
1786
|
+
transfer: {
|
|
1787
|
+
transactionId: transfer.transaction_id,
|
|
1788
|
+
fromParticipantId: req.fromParticipantId,
|
|
1789
|
+
toParticipantId: req.toParticipantId,
|
|
1790
|
+
fromBalance: transfer.from_balance,
|
|
1791
|
+
toBalance: transfer.to_balance
|
|
1792
|
+
}
|
|
1793
|
+
};
|
|
1794
|
+
}
|
|
1795
|
+
async listHolds(options) {
|
|
1796
|
+
const response = await this.requestGet("holds", {
|
|
1797
|
+
participant_id: options?.participantId,
|
|
1798
|
+
venture_id: options?.ventureId,
|
|
1799
|
+
ready_only: options?.readyOnly,
|
|
1800
|
+
limit: options?.limit
|
|
1801
|
+
});
|
|
1802
|
+
return {
|
|
1803
|
+
success: response.success,
|
|
1804
|
+
holds: (response.holds || []).map((hold) => ({
|
|
1805
|
+
id: hold.id,
|
|
1806
|
+
participantId: hold.participant_id ?? null,
|
|
1807
|
+
participantName: hold.participant_name ?? null,
|
|
1808
|
+
amount: hold.amount,
|
|
1809
|
+
currency: hold.currency,
|
|
1810
|
+
heldSince: hold.held_since,
|
|
1811
|
+
daysHeld: hold.days_held,
|
|
1812
|
+
holdReason: hold.hold_reason ?? null,
|
|
1813
|
+
holdUntil: hold.hold_until ?? null,
|
|
1814
|
+
readyForRelease: Boolean(hold.ready_for_release),
|
|
1815
|
+
releaseStatus: hold.release_status,
|
|
1816
|
+
transactionReference: hold.transaction_reference ?? null,
|
|
1817
|
+
productName: hold.product_name ?? null,
|
|
1818
|
+
ventureId: hold.venture_id ?? null,
|
|
1819
|
+
connectedAccountReady: Boolean(hold.connected_account_ready)
|
|
1820
|
+
})),
|
|
1821
|
+
count: response.count ?? 0
|
|
1822
|
+
};
|
|
1823
|
+
}
|
|
1824
|
+
async getHoldSummary() {
|
|
1825
|
+
const response = await this.requestGet("holds/summary");
|
|
1826
|
+
return {
|
|
1827
|
+
success: response.success,
|
|
1828
|
+
summary: response.summary || {}
|
|
1829
|
+
};
|
|
1830
|
+
}
|
|
1831
|
+
async releaseHold(req) {
|
|
1832
|
+
const response = await this.request(`holds/${req.holdId}/release`, {
|
|
1833
|
+
execute_transfer: req.executeTransfer !== false
|
|
1834
|
+
});
|
|
1835
|
+
const release = response.release || response;
|
|
1836
|
+
return {
|
|
1837
|
+
success: response.success,
|
|
1838
|
+
release: {
|
|
1839
|
+
id: release.id,
|
|
1840
|
+
holdId: release.hold_id ?? req.holdId,
|
|
1841
|
+
executed: Boolean(release.executed),
|
|
1842
|
+
transferId: release.transfer_id ?? null,
|
|
1843
|
+
transferStatus: release.transfer_status ?? null,
|
|
1844
|
+
amount: release.amount ?? null,
|
|
1845
|
+
currency: release.currency ?? null
|
|
1846
|
+
}
|
|
1847
|
+
};
|
|
1848
|
+
}
|
|
1849
|
+
async createCheckoutSession(req) {
|
|
1850
|
+
const hasPaymentMethod = "paymentMethodId" in req ? Boolean(req.paymentMethodId || req.sourceId) : false;
|
|
1851
|
+
if (!hasPaymentMethod && !req.successUrl) {
|
|
1852
|
+
throw new Error("Either paymentMethodId/sourceId or successUrl is required");
|
|
1853
|
+
}
|
|
1854
|
+
const response = await this.request("checkout-sessions", {
|
|
1855
|
+
amount: req.amount,
|
|
1856
|
+
participant_id: req.participantId,
|
|
1857
|
+
currency: req.currency,
|
|
1858
|
+
product_id: req.productId,
|
|
1859
|
+
product_name: req.productName,
|
|
1860
|
+
customer_email: req.customerEmail,
|
|
1861
|
+
customer_id: req.customerId,
|
|
1862
|
+
payment_method_id: "paymentMethodId" in req ? req.paymentMethodId : void 0,
|
|
1863
|
+
source_id: "sourceId" in req ? req.sourceId : void 0,
|
|
1864
|
+
success_url: req.successUrl,
|
|
1865
|
+
cancel_url: req.cancelUrl,
|
|
1866
|
+
idempotency_key: "idempotencyKey" in req ? req.idempotencyKey : void 0,
|
|
1867
|
+
metadata: req.metadata
|
|
1868
|
+
});
|
|
1869
|
+
const checkoutSession = response.checkout_session || response;
|
|
1870
|
+
return {
|
|
1871
|
+
success: Boolean(response.success),
|
|
1872
|
+
checkoutSession: {
|
|
1873
|
+
id: checkoutSession.id ?? checkoutSession.payment_id ?? checkoutSession.payment_intent_id,
|
|
1874
|
+
mode: checkoutSession.mode === "session" ? "session" : "direct",
|
|
1875
|
+
checkoutUrl: checkoutSession.checkout_url ?? null,
|
|
1876
|
+
paymentId: checkoutSession.payment_id ?? null,
|
|
1877
|
+
paymentIntentId: checkoutSession.payment_intent_id ?? checkoutSession.payment_id ?? null,
|
|
1878
|
+
status: checkoutSession.status ?? null,
|
|
1879
|
+
requiresAction: Boolean(checkoutSession.requires_action),
|
|
1880
|
+
amount: checkoutSession.amount ?? req.amount,
|
|
1881
|
+
currency: checkoutSession.currency ?? (req.currency || "USD"),
|
|
1882
|
+
expiresAt: checkoutSession.expires_at ?? null,
|
|
1883
|
+
fundingTransactionId: checkoutSession.funding_transaction_id ?? null,
|
|
1884
|
+
saleTransactionId: checkoutSession.sale_transaction_id ?? null,
|
|
1885
|
+
saleReference: checkoutSession.sale_reference ?? null,
|
|
1886
|
+
breakdown: checkoutSession.breakdown ? {
|
|
1887
|
+
grossAmount: checkoutSession.breakdown.gross_amount,
|
|
1888
|
+
creatorAmount: checkoutSession.breakdown.creator_amount,
|
|
1889
|
+
platformAmount: checkoutSession.breakdown.platform_amount,
|
|
1890
|
+
creatorPercent: checkoutSession.breakdown.creator_percent
|
|
1891
|
+
} : null
|
|
1892
|
+
}
|
|
1893
|
+
};
|
|
1894
|
+
}
|
|
1895
|
+
async createPayout(req) {
|
|
1896
|
+
const response = await this.request("payouts", {
|
|
1897
|
+
participant_id: req.participantId,
|
|
1898
|
+
wallet_id: req.walletId,
|
|
1899
|
+
amount: req.amount,
|
|
1900
|
+
reference_id: req.referenceId,
|
|
1901
|
+
reference_type: req.referenceType,
|
|
1902
|
+
description: req.description,
|
|
1903
|
+
payout_method: req.payoutMethod,
|
|
1904
|
+
fees: req.fees,
|
|
1905
|
+
fees_paid_by: req.feesPaidBy,
|
|
1906
|
+
metadata: req.metadata
|
|
1907
|
+
});
|
|
1908
|
+
const payout = response.payout || response;
|
|
1909
|
+
return {
|
|
1910
|
+
success: response.success,
|
|
1911
|
+
payout: {
|
|
1912
|
+
id: payout.id,
|
|
1913
|
+
transactionId: payout.transaction_id,
|
|
1914
|
+
grossAmount: payout.gross_amount ?? null,
|
|
1915
|
+
fees: payout.fees ?? null,
|
|
1916
|
+
netAmount: payout.net_amount ?? null,
|
|
1917
|
+
previousBalance: payout.previous_balance ?? null,
|
|
1918
|
+
newBalance: payout.new_balance ?? null
|
|
1919
|
+
}
|
|
1920
|
+
};
|
|
1921
|
+
}
|
|
1922
|
+
async createRefund(req) {
|
|
1923
|
+
const response = await this.request("refunds", {
|
|
1924
|
+
sale_reference: req.saleReference,
|
|
1925
|
+
reason: req.reason,
|
|
1926
|
+
amount: req.amount,
|
|
1927
|
+
refund_from: req.refundFrom,
|
|
1928
|
+
external_refund_id: req.externalRefundId,
|
|
1929
|
+
idempotency_key: req.idempotencyKey,
|
|
1930
|
+
metadata: req.metadata
|
|
1931
|
+
});
|
|
1932
|
+
const refund = response.refund || response;
|
|
1933
|
+
return {
|
|
1934
|
+
success: response.success,
|
|
1935
|
+
warning: response.warning ?? null,
|
|
1936
|
+
warningCode: response.warning_code ?? null,
|
|
1937
|
+
refund: {
|
|
1938
|
+
id: refund.id ?? refund.reference_id ?? refund.transaction_id ?? "",
|
|
1939
|
+
transactionId: refund.transaction_id ?? null,
|
|
1940
|
+
referenceId: refund.reference_id ?? null,
|
|
1941
|
+
saleReference: refund.sale_reference ?? null,
|
|
1942
|
+
refundedAmount: refund.refunded_amount ?? null,
|
|
1943
|
+
currency: refund.currency ?? null,
|
|
1944
|
+
status: refund.status ?? null,
|
|
1945
|
+
breakdown: refund.breakdown ? {
|
|
1946
|
+
fromCreator: refund.breakdown.from_creator,
|
|
1947
|
+
fromPlatform: refund.breakdown.from_platform
|
|
1948
|
+
} : null,
|
|
1949
|
+
isFullRefund: refund.is_full_refund ?? null,
|
|
1950
|
+
repairPending: refund.repair_pending ?? null
|
|
1951
|
+
}
|
|
1952
|
+
};
|
|
1953
|
+
}
|
|
1954
|
+
async listRefunds(req = {}) {
|
|
1955
|
+
const response = await this.requestGet("refunds", {
|
|
1956
|
+
sale_reference: req.saleReference,
|
|
1957
|
+
limit: req.limit
|
|
1958
|
+
});
|
|
1959
|
+
return {
|
|
1960
|
+
success: response.success,
|
|
1961
|
+
count: response.count ?? (response.refunds || []).length,
|
|
1962
|
+
refunds: (response.refunds || []).map((refund) => ({
|
|
1963
|
+
id: refund.id,
|
|
1964
|
+
transactionId: refund.transaction_id ?? null,
|
|
1965
|
+
referenceId: refund.reference_id ?? null,
|
|
1966
|
+
saleReference: refund.sale_reference ?? null,
|
|
1967
|
+
refundedAmount: refund.refunded_amount,
|
|
1968
|
+
currency: refund.currency,
|
|
1969
|
+
status: refund.status,
|
|
1970
|
+
reason: refund.reason ?? null,
|
|
1971
|
+
refundFrom: refund.refund_from ?? null,
|
|
1972
|
+
externalRefundId: refund.external_refund_id ?? null,
|
|
1973
|
+
createdAt: refund.created_at ?? null,
|
|
1974
|
+
breakdown: refund.breakdown ? {
|
|
1975
|
+
fromCreator: refund.breakdown.from_creator,
|
|
1976
|
+
fromPlatform: refund.breakdown.from_platform
|
|
1977
|
+
} : null,
|
|
1978
|
+
repairPending: refund.repair_pending ?? null,
|
|
1979
|
+
lastError: refund.last_error ?? null
|
|
1980
|
+
}))
|
|
1981
|
+
};
|
|
1982
|
+
}
|
|
1983
|
+
// === INVOICES ===
|
|
1984
|
+
async createInvoice(req) {
|
|
1985
|
+
return this.request("invoices", {
|
|
1986
|
+
customer_name: req.customerName,
|
|
1987
|
+
customer_email: req.customerEmail,
|
|
1988
|
+
customer_id: req.customerId,
|
|
1989
|
+
customer_address: req.customerAddress ? {
|
|
1990
|
+
line1: req.customerAddress.line1,
|
|
1991
|
+
line2: req.customerAddress.line2,
|
|
1992
|
+
city: req.customerAddress.city,
|
|
1993
|
+
state: req.customerAddress.state,
|
|
1994
|
+
postal_code: req.customerAddress.postalCode,
|
|
1995
|
+
country: req.customerAddress.country
|
|
1996
|
+
} : void 0,
|
|
1997
|
+
line_items: req.lineItems.map((item) => ({
|
|
1998
|
+
description: item.description,
|
|
1999
|
+
quantity: item.quantity,
|
|
2000
|
+
unit_price: item.unitPrice,
|
|
2001
|
+
amount: item.amount ?? Math.round(item.quantity * item.unitPrice)
|
|
2002
|
+
})),
|
|
2003
|
+
due_date: req.dueDate,
|
|
2004
|
+
notes: req.notes,
|
|
2005
|
+
terms: req.terms,
|
|
2006
|
+
reference_id: req.referenceId,
|
|
2007
|
+
metadata: req.metadata
|
|
2008
|
+
});
|
|
2009
|
+
}
|
|
2010
|
+
async listInvoices(options) {
|
|
2011
|
+
return this.requestGet("invoices", {
|
|
2012
|
+
status: options?.status,
|
|
2013
|
+
customer_id: options?.customerId,
|
|
2014
|
+
limit: options?.limit,
|
|
2015
|
+
offset: options?.offset
|
|
2016
|
+
});
|
|
2017
|
+
}
|
|
2018
|
+
async getInvoice(invoiceId) {
|
|
2019
|
+
return this.requestGet(`invoices/${invoiceId}`);
|
|
2020
|
+
}
|
|
2021
|
+
async sendInvoice(invoiceId) {
|
|
2022
|
+
return this.request(`invoices/${invoiceId}/send`, {});
|
|
2023
|
+
}
|
|
2024
|
+
async recordInvoicePayment(invoiceId, req) {
|
|
2025
|
+
return this.request(`invoices/${invoiceId}/record-payment`, {
|
|
2026
|
+
amount: req.amount,
|
|
2027
|
+
payment_method: req.paymentMethod,
|
|
2028
|
+
payment_date: req.paymentDate,
|
|
2029
|
+
reference_id: req.referenceId,
|
|
2030
|
+
notes: req.notes
|
|
2031
|
+
});
|
|
2032
|
+
}
|
|
2033
|
+
async voidInvoice(invoiceId, reason) {
|
|
2034
|
+
return this.request(`invoices/${invoiceId}/void`, { reason });
|
|
2035
|
+
}
|
|
2036
|
+
// === PAY BILL ===
|
|
2037
|
+
async payBill(req) {
|
|
2038
|
+
return this.request("pay-bill", {
|
|
2039
|
+
bill_transaction_id: req.billTransactionId,
|
|
2040
|
+
amount: req.amount,
|
|
2041
|
+
vendor_name: req.vendorName,
|
|
2042
|
+
reference_id: req.referenceId,
|
|
2043
|
+
payment_method: req.paymentMethod,
|
|
2044
|
+
payment_date: req.paymentDate,
|
|
2045
|
+
metadata: req.metadata
|
|
2046
|
+
});
|
|
2047
|
+
}
|
|
2048
|
+
// === BUDGETS ===
|
|
2049
|
+
async createBudget(req) {
|
|
2050
|
+
return this.request("manage-budgets", {
|
|
2051
|
+
name: req.name,
|
|
2052
|
+
category_code: req.categoryCode,
|
|
2053
|
+
budget_amount: req.budgetAmount,
|
|
2054
|
+
budget_period: req.budgetPeriod,
|
|
2055
|
+
alert_at_percentage: req.alertAtPercentage
|
|
2056
|
+
});
|
|
2057
|
+
}
|
|
2058
|
+
async listBudgets() {
|
|
2059
|
+
return this.requestGet("manage-budgets");
|
|
2060
|
+
}
|
|
2061
|
+
// === RECURRING EXPENSES ===
|
|
2062
|
+
async createRecurring(req) {
|
|
2063
|
+
return this.request("manage-recurring", {
|
|
2064
|
+
name: req.name,
|
|
2065
|
+
merchant_name: req.merchantName,
|
|
2066
|
+
category_code: req.categoryCode,
|
|
2067
|
+
amount: req.amount,
|
|
2068
|
+
recurrence_interval: req.recurrenceInterval,
|
|
2069
|
+
recurrence_day: req.recurrenceDay,
|
|
2070
|
+
start_date: req.startDate,
|
|
2071
|
+
end_date: req.endDate,
|
|
2072
|
+
business_purpose: req.businessPurpose,
|
|
2073
|
+
is_variable_amount: req.isVariableAmount
|
|
2074
|
+
});
|
|
2075
|
+
}
|
|
2076
|
+
async listRecurring() {
|
|
2077
|
+
return this.requestGet("manage-recurring");
|
|
2078
|
+
}
|
|
2079
|
+
async getDueRecurring(days) {
|
|
2080
|
+
return this.requestGet("manage-recurring/due", { days });
|
|
2081
|
+
}
|
|
2082
|
+
// === CONTRACTORS ===
|
|
2083
|
+
async createContractor(req) {
|
|
2084
|
+
return this.request("manage-contractors", {
|
|
2085
|
+
name: req.name,
|
|
2086
|
+
email: req.email,
|
|
2087
|
+
company_name: req.companyName
|
|
2088
|
+
});
|
|
2089
|
+
}
|
|
2090
|
+
async listContractors() {
|
|
2091
|
+
return this.requestGet("manage-contractors");
|
|
2092
|
+
}
|
|
2093
|
+
async recordContractorPayment(req) {
|
|
2094
|
+
return this.request("manage-contractors/payment", {
|
|
2095
|
+
contractor_id: req.contractorId,
|
|
2096
|
+
amount: req.amount,
|
|
2097
|
+
payment_date: req.paymentDate,
|
|
2098
|
+
payment_method: req.paymentMethod,
|
|
2099
|
+
payment_reference: req.paymentReference,
|
|
2100
|
+
description: req.description
|
|
2101
|
+
});
|
|
2102
|
+
}
|
|
2103
|
+
// === BANK ACCOUNTS ===
|
|
2104
|
+
async createBankAccount(req) {
|
|
2105
|
+
return this.request("manage-bank-accounts", {
|
|
2106
|
+
bank_name: req.bankName,
|
|
2107
|
+
account_name: req.accountName,
|
|
2108
|
+
account_type: req.accountType,
|
|
2109
|
+
account_last_four: req.accountLastFour
|
|
2110
|
+
});
|
|
2111
|
+
}
|
|
2112
|
+
async listBankAccounts() {
|
|
2113
|
+
return this.requestGet("manage-bank-accounts");
|
|
2114
|
+
}
|
|
2115
|
+
// === LEDGER LISTING ===
|
|
2116
|
+
async listLedgers() {
|
|
2117
|
+
return this.requestGet("list-ledgers");
|
|
2118
|
+
}
|
|
2119
|
+
// === DELETE CREATOR ===
|
|
2120
|
+
async deleteCreator(creatorId) {
|
|
2121
|
+
return this.request("delete-creator", {
|
|
2122
|
+
creator_id: creatorId
|
|
2123
|
+
});
|
|
2124
|
+
}
|
|
2125
|
+
// === TAX INFO ===
|
|
2126
|
+
async submitTaxInfo(req) {
|
|
2127
|
+
return this.request("submit-tax-info", {
|
|
2128
|
+
participant_id: req.participantId,
|
|
2129
|
+
legal_name: req.legalName,
|
|
2130
|
+
tax_id_type: req.taxIdType,
|
|
2131
|
+
tax_id_last4: req.taxIdLast4,
|
|
2132
|
+
business_type: req.businessType,
|
|
2133
|
+
address: req.address ? {
|
|
2134
|
+
line1: req.address.line1,
|
|
2135
|
+
line2: req.address.line2,
|
|
2136
|
+
city: req.address.city,
|
|
2137
|
+
state: req.address.state,
|
|
2138
|
+
postal_code: req.address.postalCode,
|
|
2139
|
+
country: req.address.country
|
|
2140
|
+
} : void 0,
|
|
2141
|
+
certify: req.certify
|
|
2142
|
+
});
|
|
2143
|
+
}
|
|
2144
|
+
// === BANK STATEMENT IMPORT ===
|
|
2145
|
+
async importBankStatement(req) {
|
|
2146
|
+
return this.request("import-bank-statement", {
|
|
2147
|
+
bank_account_id: req.bankAccountId,
|
|
2148
|
+
lines: req.lines.map((line) => ({
|
|
2149
|
+
transaction_date: line.transactionDate,
|
|
2150
|
+
post_date: line.postDate,
|
|
2151
|
+
description: line.description,
|
|
2152
|
+
amount: line.amount,
|
|
2153
|
+
reference_number: line.referenceNumber,
|
|
2154
|
+
check_number: line.checkNumber,
|
|
2155
|
+
merchant_name: line.merchantName,
|
|
2156
|
+
category_hint: line.categoryHint
|
|
2157
|
+
})),
|
|
2158
|
+
auto_match: req.autoMatch
|
|
2159
|
+
});
|
|
2160
|
+
}
|
|
2161
|
+
};
|
|
2162
|
+
var client_default = Soledgic;
|
|
2163
|
+
export {
|
|
2164
|
+
AuthenticationError,
|
|
2165
|
+
ConflictError,
|
|
2166
|
+
DEFAULT_API_VERSION,
|
|
2167
|
+
DEFAULT_BASE_URL,
|
|
2168
|
+
NotFoundError,
|
|
2169
|
+
Soledgic,
|
|
2170
|
+
SoledgicError,
|
|
2171
|
+
ValidationError,
|
|
2172
|
+
client_default as default,
|
|
2173
|
+
hmacHex,
|
|
2174
|
+
isArrayBufferView,
|
|
2175
|
+
mapWebhookDelivery,
|
|
2176
|
+
mapWebhookEndpoint,
|
|
2177
|
+
parseWebhookEvent,
|
|
2178
|
+
parseWebhookSignatureHeader,
|
|
2179
|
+
resolveWebhookEndpointUrl,
|
|
2180
|
+
timingSafeEqual,
|
|
2181
|
+
verifyWebhookSignature,
|
|
2182
|
+
webhookPayloadToString
|
|
2183
|
+
};
|