@paybetaby/node-sdk 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,313 @@
1
+ 'use strict';
2
+
3
+ var crypto = require('crypto');
4
+
5
+ // src/errors.ts
6
+ var PaybetaError = class extends Error {
7
+ constructor(message) {
8
+ super(message);
9
+ this.name = "PaybetaError";
10
+ Object.setPrototypeOf(this, new.target.prototype);
11
+ }
12
+ };
13
+ var PaybetaApiError = class extends PaybetaError {
14
+ constructor(status, code, message, traceId, timestamp) {
15
+ super(message);
16
+ this.name = "PaybetaApiError";
17
+ this.status = status;
18
+ this.code = code;
19
+ this.traceId = traceId;
20
+ this.timestamp = timestamp;
21
+ Object.setPrototypeOf(this, new.target.prototype);
22
+ }
23
+ };
24
+
25
+ // src/http.ts
26
+ var HttpClient = class {
27
+ constructor(config) {
28
+ this.config = config;
29
+ }
30
+ async request(method, path, options) {
31
+ const url = new URL(`/v1${path}`, this.config.baseUrl);
32
+ if (options?.query) {
33
+ for (const [key, value] of Object.entries(options.query)) {
34
+ if (value !== void 0 && value !== null) {
35
+ url.searchParams.set(key, String(value));
36
+ }
37
+ }
38
+ }
39
+ const headers = {
40
+ "X-API-Key": this.config.apiKey,
41
+ Accept: "application/json"
42
+ };
43
+ let body;
44
+ if (options?.body !== void 0) {
45
+ headers["Content-Type"] = "application/json";
46
+ body = JSON.stringify(options.body);
47
+ }
48
+ const controller = new AbortController();
49
+ const timer = setTimeout(() => controller.abort(), this.config.timeout);
50
+ let response;
51
+ try {
52
+ response = await fetch(url.toString(), {
53
+ method,
54
+ headers,
55
+ body,
56
+ signal: controller.signal
57
+ });
58
+ } catch (err) {
59
+ if (err instanceof Error && err.name === "AbortError") {
60
+ throw new PaybetaError(`Request timed out after ${this.config.timeout}ms`);
61
+ }
62
+ throw err;
63
+ } finally {
64
+ clearTimeout(timer);
65
+ }
66
+ let payload;
67
+ const contentType = response.headers.get("content-type") ?? "";
68
+ if (contentType.includes("application/json")) {
69
+ payload = await response.json().catch(() => null);
70
+ } else {
71
+ payload = await response.text().catch(() => null);
72
+ }
73
+ if (!response.ok) {
74
+ const errBody = payload ?? {};
75
+ const errDetail = errBody.error ?? {};
76
+ throw new PaybetaApiError(
77
+ response.status,
78
+ errDetail.code ?? String(response.status),
79
+ errDetail.message ?? errBody.message ?? "Request failed",
80
+ errDetail.traceId ?? "",
81
+ errDetail.timestamp ?? (/* @__PURE__ */ new Date()).toISOString()
82
+ );
83
+ }
84
+ return payload;
85
+ }
86
+ get(path, query) {
87
+ return this.request("GET", path, { query });
88
+ }
89
+ post(path, body) {
90
+ return this.request("POST", path, { body });
91
+ }
92
+ patch(path, body) {
93
+ return this.request("PATCH", path, { body });
94
+ }
95
+ delete(path) {
96
+ return this.request("DELETE", path);
97
+ }
98
+ };
99
+
100
+ // src/resources/transactions.ts
101
+ var TransactionsResource = class {
102
+ constructor(http) {
103
+ this.http = http;
104
+ }
105
+ create(params, opts) {
106
+ const body = opts?.idempotencyKey ? { ...params, idempotencyKey: opts.idempotencyKey } : params;
107
+ return this.http.post("/transactions", body);
108
+ }
109
+ /**
110
+ * Lists transactions. The bare `/transactions` endpoint is platform-role
111
+ * only — an API-key (merchant) caller gets a 403 there — so passing
112
+ * `merchantId` routes to `/transactions/merchant/:merchantId` instead.
113
+ */
114
+ list(params) {
115
+ const { merchantId, ...rest } = params ?? {};
116
+ const path = merchantId ? `/transactions/merchant/${merchantId}` : "/transactions";
117
+ return this.http.get(path, rest);
118
+ }
119
+ retrieve(id) {
120
+ return this.http.get(`/transactions/${id}`);
121
+ }
122
+ listHistory(id) {
123
+ return this.http.get(`/transactions/${id}/history`);
124
+ }
125
+ };
126
+
127
+ // src/resources/payments.ts
128
+ var PaymentsResource = class {
129
+ constructor(http) {
130
+ this.http = http;
131
+ }
132
+ initiate(params) {
133
+ const { idempotencyKey, ...rest } = params;
134
+ const body = idempotencyKey ? { ...rest, idempotencyKey } : rest;
135
+ return this.http.post("/payments", body);
136
+ }
137
+ /**
138
+ * Lists payments. The bare `/payments` endpoint is platform-role only —
139
+ * an API-key (merchant) caller gets a 403 there — so passing `merchantId`
140
+ * (which every merchant API key call needs) routes to
141
+ * `/payments/merchant/:merchantId` instead, matching what the API
142
+ * actually allows a merchant credential to call.
143
+ */
144
+ list(params) {
145
+ const { merchantId, ...rest } = params ?? {};
146
+ const path = merchantId ? `/payments/merchant/${merchantId}` : "/payments";
147
+ return this.http.get(path, rest);
148
+ }
149
+ retrieve(id) {
150
+ return this.http.get(`/payments/${id}`);
151
+ }
152
+ verify(id) {
153
+ return this.http.post(`/payments/${id}/verify`);
154
+ }
155
+ retry(id) {
156
+ return this.http.post(`/payments/${id}/retry`);
157
+ }
158
+ listAttempts(id) {
159
+ return this.http.get(`/payments/${id}/attempts`);
160
+ }
161
+ };
162
+
163
+ // src/resources/escrows.ts
164
+ var EscrowsResource = class {
165
+ constructor(http) {
166
+ this.http = http;
167
+ }
168
+ create(params, opts) {
169
+ const body = opts?.idempotencyKey ? { ...params, idempotencyKey: opts.idempotencyKey } : params;
170
+ return this.http.post("/escrows", body);
171
+ }
172
+ /**
173
+ * Lists escrows. The bare `/escrows` endpoint is platform-role only — an
174
+ * API-key (merchant) caller gets a 403 there — so passing `merchantId`
175
+ * routes to `/escrows/merchant/:merchantId` instead. Unlike
176
+ * payments/transactions/disputes, this returns a `{ escrows, total,
177
+ * limit, offset }` envelope rather than a bare array.
178
+ */
179
+ list(params) {
180
+ const { merchantId, ...rest } = params ?? {};
181
+ const path = merchantId ? `/escrows/merchant/${merchantId}` : "/escrows";
182
+ return this.http.get(path, rest);
183
+ }
184
+ retrieve(id) {
185
+ return this.http.get(`/escrows/${id}`);
186
+ }
187
+ retrieveBalance(id) {
188
+ return this.http.get(`/escrows/${id}/balance`);
189
+ }
190
+ retrieveConditions(id) {
191
+ return this.http.get(`/escrows/${id}/conditions`);
192
+ }
193
+ release(id, params) {
194
+ return this.http.post(`/escrows/${id}/release`, params);
195
+ }
196
+ refund(id) {
197
+ return this.http.post(`/escrows/${id}/refund`);
198
+ }
199
+ dispute(id) {
200
+ return this.http.post(`/escrows/${id}/dispute`);
201
+ }
202
+ cancel(id) {
203
+ return this.http.post(`/escrows/${id}/cancel`);
204
+ }
205
+ confirmDelivery(id, params) {
206
+ return this.http.post(`/escrows/${id}/confirm-delivery`, params);
207
+ }
208
+ confirmBuyer(id, params) {
209
+ return this.http.post(`/escrows/${id}/confirm-buyer`, params);
210
+ }
211
+ };
212
+
213
+ // src/resources/disputes.ts
214
+ var DisputesResource = class {
215
+ constructor(http) {
216
+ this.http = http;
217
+ }
218
+ open(params) {
219
+ return this.http.post("/disputes", params);
220
+ }
221
+ /**
222
+ * Lists disputes. The bare `/disputes` endpoint is platform-role only —
223
+ * an API-key (merchant) caller gets a 403 there — so passing
224
+ * `merchantId` routes to `/disputes/merchant/:merchantId` instead.
225
+ */
226
+ list(params) {
227
+ const { merchantId, ...rest } = params ?? {};
228
+ const path = merchantId ? `/disputes/merchant/${merchantId}` : "/disputes";
229
+ return this.http.get(path, rest);
230
+ }
231
+ retrieve(id) {
232
+ return this.http.get(`/disputes/${id}`);
233
+ }
234
+ listEvidence(id) {
235
+ return this.http.get(`/disputes/${id}/evidence`);
236
+ }
237
+ uploadEvidence(id, params) {
238
+ return this.http.post(`/disputes/${id}/evidence`, params);
239
+ }
240
+ resolve(id, params) {
241
+ return this.http.post(`/disputes/${id}/resolve`, params);
242
+ }
243
+ cancel(id, params) {
244
+ return this.http.post(`/disputes/${id}/cancel`, params);
245
+ }
246
+ };
247
+ var WebhooksResource = class {
248
+ constructor(secret) {
249
+ this.secret = secret;
250
+ }
251
+ /**
252
+ * Verifies the signature on an incoming webhook and parses the payload.
253
+ *
254
+ * PayBeta signs the concatenation of the delivery timestamp and the raw
255
+ * request body — `HMAC-SHA256(secret, "${timestamp}.${rawBody}")` — and
256
+ * sends the result hex-encoded, prefixed with `sha256=`, in the
257
+ * `X-PayBeta-Signature` header (see OutboundWebhookService). The
258
+ * timestamp itself arrives separately in `X-PayBeta-Timestamp`, so both
259
+ * headers are required here, not just the signature.
260
+ *
261
+ * Pass the raw request body (before JSON parsing), the value of the
262
+ * `X-PayBeta-Signature` header, and the value of the `X-PayBeta-Timestamp`
263
+ * header. Throws `PaybetaError` if the signature is invalid or the
264
+ * secret was not configured.
265
+ */
266
+ constructEvent(rawBody, signature, timestamp) {
267
+ if (!this.secret) {
268
+ throw new PaybetaError(
269
+ "webhookSecret must be set on PaybetaClient to verify webhook signatures"
270
+ );
271
+ }
272
+ const body = typeof rawBody === "string" ? rawBody : rawBody.toString("utf8");
273
+ const hexSignature = signature.startsWith("sha256=") ? signature.slice("sha256=".length) : signature;
274
+ const expected = crypto.createHmac("sha256", this.secret).update(`${timestamp}.${body}`).digest("hex");
275
+ let actual;
276
+ let expectedBuf;
277
+ try {
278
+ actual = Buffer.from(hexSignature, "hex");
279
+ expectedBuf = Buffer.from(expected, "hex");
280
+ } catch {
281
+ throw new PaybetaError("Invalid signature format");
282
+ }
283
+ if (expectedBuf.length !== actual.length || !crypto.timingSafeEqual(expectedBuf, actual)) {
284
+ throw new PaybetaError("Webhook signature verification failed");
285
+ }
286
+ return JSON.parse(body);
287
+ }
288
+ };
289
+
290
+ // src/client.ts
291
+ var PaybetaClient = class {
292
+ constructor(config) {
293
+ if (!config.apiKey) {
294
+ throw new Error("PaybetaClient: apiKey is required");
295
+ }
296
+ const http = new HttpClient({
297
+ apiKey: config.apiKey,
298
+ baseUrl: config.baseUrl ?? "https://api.usepaybeta.com",
299
+ timeout: config.timeout ?? 3e4
300
+ });
301
+ this.transactions = new TransactionsResource(http);
302
+ this.payments = new PaymentsResource(http);
303
+ this.escrows = new EscrowsResource(http);
304
+ this.disputes = new DisputesResource(http);
305
+ this.webhooks = new WebhooksResource(config.webhookSecret ?? "");
306
+ }
307
+ };
308
+
309
+ exports.PaybetaApiError = PaybetaApiError;
310
+ exports.PaybetaClient = PaybetaClient;
311
+ exports.PaybetaError = PaybetaError;
312
+ //# sourceMappingURL=index.js.map
313
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/errors.ts","../src/http.ts","../src/resources/transactions.ts","../src/resources/payments.ts","../src/resources/escrows.ts","../src/resources/disputes.ts","../src/resources/webhooks.ts","../src/client.ts"],"names":["createHmac","timingSafeEqual"],"mappings":"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","file":"index.js","sourcesContent":["export class PaybetaError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'PaybetaError';\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\nexport class PaybetaApiError extends PaybetaError {\n readonly status: number;\n readonly code: string;\n readonly traceId: string;\n readonly timestamp: string;\n\n constructor(status: number, code: string, message: string, traceId: string, timestamp: string) {\n super(message);\n this.name = 'PaybetaApiError';\n this.status = status;\n this.code = code;\n this.traceId = traceId;\n this.timestamp = timestamp;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n","import { PaybetaApiError, PaybetaError } from './errors.js';\n\nexport interface HttpClientConfig {\n apiKey: string;\n baseUrl: string;\n timeout: number;\n}\n\ninterface ApiErrorBody {\n error?: {\n code?: string;\n message?: string;\n traceId?: string;\n timestamp?: string;\n };\n message?: string;\n}\n\nexport class HttpClient {\n constructor(private readonly config: HttpClientConfig) {}\n\n async request<T>(\n method: string,\n path: string,\n options?: {\n body?: unknown;\n query?: Record<string, string | number | boolean | undefined | null>;\n },\n ): Promise<T> {\n // The public API is served behind api-gateway under a /v1 prefix — the\n // gateway's route table only matches /v1/... and 404s anything else, so\n // every request must go there regardless of how `path` is written by\n // the resource classes below.\n const url = new URL(`/v1${path}`, this.config.baseUrl);\n\n if (options?.query) {\n for (const [key, value] of Object.entries(options.query)) {\n if (value !== undefined && value !== null) {\n url.searchParams.set(key, String(value));\n }\n }\n }\n\n const headers: Record<string, string> = {\n 'X-API-Key': this.config.apiKey,\n Accept: 'application/json',\n };\n\n let body: string | undefined;\n if (options?.body !== undefined) {\n headers['Content-Type'] = 'application/json';\n body = JSON.stringify(options.body);\n }\n\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.config.timeout);\n\n let response: Response;\n try {\n response = await fetch(url.toString(), {\n method,\n headers,\n body,\n signal: controller.signal,\n });\n } catch (err) {\n if (err instanceof Error && err.name === 'AbortError') {\n throw new PaybetaError(`Request timed out after ${this.config.timeout}ms`);\n }\n throw err;\n } finally {\n clearTimeout(timer);\n }\n\n let payload: unknown;\n const contentType = response.headers.get('content-type') ?? '';\n if (contentType.includes('application/json')) {\n payload = await response.json().catch(() => null);\n } else {\n payload = await response.text().catch(() => null);\n }\n\n if (!response.ok) {\n const errBody = (payload ?? {}) as ApiErrorBody;\n const errDetail = errBody.error ?? {};\n throw new PaybetaApiError(\n response.status,\n errDetail.code ?? String(response.status),\n errDetail.message ?? errBody.message ?? 'Request failed',\n errDetail.traceId ?? '',\n errDetail.timestamp ?? new Date().toISOString(),\n );\n }\n\n return payload as T;\n }\n\n get<T>(path: string, query?: Record<string, string | number | boolean | undefined | null>): Promise<T> {\n return this.request<T>('GET', path, { query });\n }\n\n post<T>(path: string, body?: unknown): Promise<T> {\n return this.request<T>('POST', path, { body });\n }\n\n patch<T>(path: string, body?: unknown): Promise<T> {\n return this.request<T>('PATCH', path, { body });\n }\n\n delete<T>(path: string): Promise<T> {\n return this.request<T>('DELETE', path);\n }\n}\n","import type { HttpClient } from '../http.js';\nimport type {\n Transaction,\n CreateTransactionParams,\n ListTransactionsParams,\n TransactionEvent,\n} from '../types/transactions.js';\nimport type { RequestOptions } from '../types/common.js';\n\nexport class TransactionsResource {\n constructor(private readonly http: HttpClient) {}\n\n create(params: CreateTransactionParams, opts?: RequestOptions): Promise<Transaction> {\n const body = opts?.idempotencyKey\n ? { ...params, idempotencyKey: opts.idempotencyKey }\n : params;\n return this.http.post<Transaction>('/transactions', body);\n }\n\n /**\n * Lists transactions. The bare `/transactions` endpoint is platform-role\n * only — an API-key (merchant) caller gets a 403 there — so passing\n * `merchantId` routes to `/transactions/merchant/:merchantId` instead.\n */\n list(params?: ListTransactionsParams): Promise<Transaction[]> {\n const { merchantId, ...rest } = params ?? {};\n const path = merchantId ? `/transactions/merchant/${merchantId}` : '/transactions';\n return this.http.get<Transaction[]>(path, rest as Record<string, string | number | undefined>);\n }\n\n retrieve(id: string): Promise<Transaction> {\n return this.http.get<Transaction>(`/transactions/${id}`);\n }\n\n listHistory(id: string): Promise<TransactionEvent[]> {\n return this.http.get<TransactionEvent[]>(`/transactions/${id}/history`);\n }\n}\n","import type { HttpClient } from '../http.js';\nimport type {\n Payment,\n PaymentAttempt,\n InitiatePaymentParams,\n ListPaymentsParams,\n} from '../types/payments.js';\n\nexport class PaymentsResource {\n constructor(private readonly http: HttpClient) {}\n\n initiate(params: InitiatePaymentParams): Promise<Payment> {\n const { idempotencyKey, ...rest } = params;\n const body = idempotencyKey ? { ...rest, idempotencyKey } : rest;\n return this.http.post<Payment>('/payments', body);\n }\n\n /**\n * Lists payments. The bare `/payments` endpoint is platform-role only —\n * an API-key (merchant) caller gets a 403 there — so passing `merchantId`\n * (which every merchant API key call needs) routes to\n * `/payments/merchant/:merchantId` instead, matching what the API\n * actually allows a merchant credential to call.\n */\n list(params?: ListPaymentsParams): Promise<Payment[]> {\n const { merchantId, ...rest } = params ?? {};\n const path = merchantId ? `/payments/merchant/${merchantId}` : '/payments';\n return this.http.get<Payment[]>(path, rest as Record<string, string | number | undefined>);\n }\n\n retrieve(id: string): Promise<Payment> {\n return this.http.get<Payment>(`/payments/${id}`);\n }\n\n verify(id: string): Promise<Payment> {\n return this.http.post<Payment>(`/payments/${id}/verify`);\n }\n\n retry(id: string): Promise<Payment> {\n return this.http.post<Payment>(`/payments/${id}/retry`);\n }\n\n listAttempts(id: string): Promise<PaymentAttempt[]> {\n return this.http.get<PaymentAttempt[]>(`/payments/${id}/attempts`);\n }\n}\n","import type { HttpClient } from '../http.js';\nimport type {\n Escrow,\n EscrowBalance,\n EscrowListResponse,\n EscrowConditionsResponse,\n CreateEscrowParams,\n ListEscrowsParams,\n ReleaseEscrowParams,\n ConfirmDeliveryParams,\n ConfirmBuyerParams,\n} from '../types/escrows.js';\nimport type { RequestOptions } from '../types/common.js';\n\nexport class EscrowsResource {\n constructor(private readonly http: HttpClient) {}\n\n create(params: CreateEscrowParams, opts?: RequestOptions): Promise<Escrow> {\n const body = opts?.idempotencyKey\n ? { ...params, idempotencyKey: opts.idempotencyKey }\n : params;\n return this.http.post<Escrow>('/escrows', body);\n }\n\n /**\n * Lists escrows. The bare `/escrows` endpoint is platform-role only — an\n * API-key (merchant) caller gets a 403 there — so passing `merchantId`\n * routes to `/escrows/merchant/:merchantId` instead. Unlike\n * payments/transactions/disputes, this returns a `{ escrows, total,\n * limit, offset }` envelope rather than a bare array.\n */\n list(params?: ListEscrowsParams): Promise<EscrowListResponse> {\n const { merchantId, ...rest } = params ?? {};\n const path = merchantId ? `/escrows/merchant/${merchantId}` : '/escrows';\n return this.http.get<EscrowListResponse>(path, rest as Record<string, string | number | undefined>);\n }\n\n retrieve(id: string): Promise<Escrow> {\n return this.http.get<Escrow>(`/escrows/${id}`);\n }\n\n retrieveBalance(id: string): Promise<EscrowBalance> {\n return this.http.get<EscrowBalance>(`/escrows/${id}/balance`);\n }\n\n retrieveConditions(id: string): Promise<EscrowConditionsResponse> {\n return this.http.get<EscrowConditionsResponse>(`/escrows/${id}/conditions`);\n }\n\n release(id: string, params?: ReleaseEscrowParams): Promise<Escrow> {\n return this.http.post<Escrow>(`/escrows/${id}/release`, params);\n }\n\n refund(id: string): Promise<Escrow> {\n return this.http.post<Escrow>(`/escrows/${id}/refund`);\n }\n\n dispute(id: string): Promise<Escrow> {\n return this.http.post<Escrow>(`/escrows/${id}/dispute`);\n }\n\n cancel(id: string): Promise<Escrow> {\n return this.http.post<Escrow>(`/escrows/${id}/cancel`);\n }\n\n confirmDelivery(id: string, params?: ConfirmDeliveryParams): Promise<Escrow> {\n return this.http.post<Escrow>(`/escrows/${id}/confirm-delivery`, params);\n }\n\n confirmBuyer(id: string, params?: ConfirmBuyerParams): Promise<Escrow> {\n return this.http.post<Escrow>(`/escrows/${id}/confirm-buyer`, params);\n }\n}\n","import type { HttpClient } from '../http.js';\nimport type {\n Dispute,\n Evidence,\n OpenDisputeParams,\n ListDisputesParams,\n UploadEvidenceParams,\n ResolveDisputeParams,\n CancelDisputeParams,\n} from '../types/disputes.js';\n\nexport class DisputesResource {\n constructor(private readonly http: HttpClient) {}\n\n open(params: OpenDisputeParams): Promise<Dispute> {\n return this.http.post<Dispute>('/disputes', params);\n }\n\n /**\n * Lists disputes. The bare `/disputes` endpoint is platform-role only —\n * an API-key (merchant) caller gets a 403 there — so passing\n * `merchantId` routes to `/disputes/merchant/:merchantId` instead.\n */\n list(params?: ListDisputesParams): Promise<Dispute[]> {\n const { merchantId, ...rest } = params ?? {};\n const path = merchantId ? `/disputes/merchant/${merchantId}` : '/disputes';\n return this.http.get<Dispute[]>(path, rest as Record<string, string | number | undefined>);\n }\n\n retrieve(id: string): Promise<Dispute> {\n return this.http.get<Dispute>(`/disputes/${id}`);\n }\n\n listEvidence(id: string): Promise<Evidence[]> {\n return this.http.get<Evidence[]>(`/disputes/${id}/evidence`);\n }\n\n uploadEvidence(id: string, params: UploadEvidenceParams): Promise<Evidence> {\n return this.http.post<Evidence>(`/disputes/${id}/evidence`, params);\n }\n\n resolve(id: string, params: ResolveDisputeParams): Promise<Dispute> {\n return this.http.post<Dispute>(`/disputes/${id}/resolve`, params);\n }\n\n cancel(id: string, params: CancelDisputeParams): Promise<Dispute> {\n return this.http.post<Dispute>(`/disputes/${id}/cancel`, params);\n }\n}\n","import { createHmac, timingSafeEqual } from 'crypto';\nimport { PaybetaError } from '../errors.js';\nimport type { WebhookEvent } from '../types/webhooks.js';\n\nexport class WebhooksResource {\n constructor(private readonly secret: string) {}\n\n /**\n * Verifies the signature on an incoming webhook and parses the payload.\n *\n * PayBeta signs the concatenation of the delivery timestamp and the raw\n * request body — `HMAC-SHA256(secret, \"${timestamp}.${rawBody}\")` — and\n * sends the result hex-encoded, prefixed with `sha256=`, in the\n * `X-PayBeta-Signature` header (see OutboundWebhookService). The\n * timestamp itself arrives separately in `X-PayBeta-Timestamp`, so both\n * headers are required here, not just the signature.\n *\n * Pass the raw request body (before JSON parsing), the value of the\n * `X-PayBeta-Signature` header, and the value of the `X-PayBeta-Timestamp`\n * header. Throws `PaybetaError` if the signature is invalid or the\n * secret was not configured.\n */\n constructEvent<T = unknown>(\n rawBody: string | Buffer,\n signature: string,\n timestamp: string,\n ): WebhookEvent<T> {\n if (!this.secret) {\n throw new PaybetaError(\n 'webhookSecret must be set on PaybetaClient to verify webhook signatures',\n );\n }\n\n const body = typeof rawBody === 'string' ? rawBody : rawBody.toString('utf8');\n\n // Accept the header verbatim (`sha256=<hex>`) or a bare hex digest, so\n // callers who've already stripped the prefix don't get a confusing\n // \"invalid signature format\" error.\n const hexSignature = signature.startsWith('sha256=') ? signature.slice('sha256='.length) : signature;\n\n const expected = createHmac('sha256', this.secret)\n .update(`${timestamp}.${body}`)\n .digest('hex');\n\n let actual: Buffer;\n let expectedBuf: Buffer;\n try {\n actual = Buffer.from(hexSignature, 'hex');\n expectedBuf = Buffer.from(expected, 'hex');\n } catch {\n throw new PaybetaError('Invalid signature format');\n }\n\n if (expectedBuf.length !== actual.length || !timingSafeEqual(expectedBuf, actual)) {\n throw new PaybetaError('Webhook signature verification failed');\n }\n\n return JSON.parse(body) as WebhookEvent<T>;\n }\n}\n","import { HttpClient } from './http.js';\nimport { TransactionsResource } from './resources/transactions.js';\nimport { PaymentsResource } from './resources/payments.js';\nimport { EscrowsResource } from './resources/escrows.js';\nimport { DisputesResource } from './resources/disputes.js';\nimport { WebhooksResource } from './resources/webhooks.js';\n\nexport interface PaybetaClientConfig {\n /** API key obtained from the Paybeta dashboard (pb_live_* or pb_test_*) */\n apiKey: string;\n /** Override the default API base URL. Defaults to https://api.usepaybeta.com */\n baseUrl?: string;\n /** Webhook signing secret used to verify incoming webhook payloads */\n webhookSecret?: string;\n /** Request timeout in milliseconds. Defaults to 30000 */\n timeout?: number;\n}\n\nexport class PaybetaClient {\n readonly transactions: TransactionsResource;\n readonly payments: PaymentsResource;\n readonly escrows: EscrowsResource;\n readonly disputes: DisputesResource;\n readonly webhooks: WebhooksResource;\n\n constructor(config: PaybetaClientConfig) {\n if (!config.apiKey) {\n throw new Error('PaybetaClient: apiKey is required');\n }\n\n const http = new HttpClient({\n apiKey: config.apiKey,\n baseUrl: config.baseUrl ?? 'https://api.usepaybeta.com',\n timeout: config.timeout ?? 30_000,\n });\n\n this.transactions = new TransactionsResource(http);\n this.payments = new PaymentsResource(http);\n this.escrows = new EscrowsResource(http);\n this.disputes = new DisputesResource(http);\n this.webhooks = new WebhooksResource(config.webhookSecret ?? '');\n }\n}\n"]}
package/dist/index.mjs ADDED
@@ -0,0 +1,309 @@
1
+ import { createHmac, timingSafeEqual } from 'crypto';
2
+
3
+ // src/errors.ts
4
+ var PaybetaError = class extends Error {
5
+ constructor(message) {
6
+ super(message);
7
+ this.name = "PaybetaError";
8
+ Object.setPrototypeOf(this, new.target.prototype);
9
+ }
10
+ };
11
+ var PaybetaApiError = class extends PaybetaError {
12
+ constructor(status, code, message, traceId, timestamp) {
13
+ super(message);
14
+ this.name = "PaybetaApiError";
15
+ this.status = status;
16
+ this.code = code;
17
+ this.traceId = traceId;
18
+ this.timestamp = timestamp;
19
+ Object.setPrototypeOf(this, new.target.prototype);
20
+ }
21
+ };
22
+
23
+ // src/http.ts
24
+ var HttpClient = class {
25
+ constructor(config) {
26
+ this.config = config;
27
+ }
28
+ async request(method, path, options) {
29
+ const url = new URL(`/v1${path}`, this.config.baseUrl);
30
+ if (options?.query) {
31
+ for (const [key, value] of Object.entries(options.query)) {
32
+ if (value !== void 0 && value !== null) {
33
+ url.searchParams.set(key, String(value));
34
+ }
35
+ }
36
+ }
37
+ const headers = {
38
+ "X-API-Key": this.config.apiKey,
39
+ Accept: "application/json"
40
+ };
41
+ let body;
42
+ if (options?.body !== void 0) {
43
+ headers["Content-Type"] = "application/json";
44
+ body = JSON.stringify(options.body);
45
+ }
46
+ const controller = new AbortController();
47
+ const timer = setTimeout(() => controller.abort(), this.config.timeout);
48
+ let response;
49
+ try {
50
+ response = await fetch(url.toString(), {
51
+ method,
52
+ headers,
53
+ body,
54
+ signal: controller.signal
55
+ });
56
+ } catch (err) {
57
+ if (err instanceof Error && err.name === "AbortError") {
58
+ throw new PaybetaError(`Request timed out after ${this.config.timeout}ms`);
59
+ }
60
+ throw err;
61
+ } finally {
62
+ clearTimeout(timer);
63
+ }
64
+ let payload;
65
+ const contentType = response.headers.get("content-type") ?? "";
66
+ if (contentType.includes("application/json")) {
67
+ payload = await response.json().catch(() => null);
68
+ } else {
69
+ payload = await response.text().catch(() => null);
70
+ }
71
+ if (!response.ok) {
72
+ const errBody = payload ?? {};
73
+ const errDetail = errBody.error ?? {};
74
+ throw new PaybetaApiError(
75
+ response.status,
76
+ errDetail.code ?? String(response.status),
77
+ errDetail.message ?? errBody.message ?? "Request failed",
78
+ errDetail.traceId ?? "",
79
+ errDetail.timestamp ?? (/* @__PURE__ */ new Date()).toISOString()
80
+ );
81
+ }
82
+ return payload;
83
+ }
84
+ get(path, query) {
85
+ return this.request("GET", path, { query });
86
+ }
87
+ post(path, body) {
88
+ return this.request("POST", path, { body });
89
+ }
90
+ patch(path, body) {
91
+ return this.request("PATCH", path, { body });
92
+ }
93
+ delete(path) {
94
+ return this.request("DELETE", path);
95
+ }
96
+ };
97
+
98
+ // src/resources/transactions.ts
99
+ var TransactionsResource = class {
100
+ constructor(http) {
101
+ this.http = http;
102
+ }
103
+ create(params, opts) {
104
+ const body = opts?.idempotencyKey ? { ...params, idempotencyKey: opts.idempotencyKey } : params;
105
+ return this.http.post("/transactions", body);
106
+ }
107
+ /**
108
+ * Lists transactions. The bare `/transactions` endpoint is platform-role
109
+ * only — an API-key (merchant) caller gets a 403 there — so passing
110
+ * `merchantId` routes to `/transactions/merchant/:merchantId` instead.
111
+ */
112
+ list(params) {
113
+ const { merchantId, ...rest } = params ?? {};
114
+ const path = merchantId ? `/transactions/merchant/${merchantId}` : "/transactions";
115
+ return this.http.get(path, rest);
116
+ }
117
+ retrieve(id) {
118
+ return this.http.get(`/transactions/${id}`);
119
+ }
120
+ listHistory(id) {
121
+ return this.http.get(`/transactions/${id}/history`);
122
+ }
123
+ };
124
+
125
+ // src/resources/payments.ts
126
+ var PaymentsResource = class {
127
+ constructor(http) {
128
+ this.http = http;
129
+ }
130
+ initiate(params) {
131
+ const { idempotencyKey, ...rest } = params;
132
+ const body = idempotencyKey ? { ...rest, idempotencyKey } : rest;
133
+ return this.http.post("/payments", body);
134
+ }
135
+ /**
136
+ * Lists payments. The bare `/payments` endpoint is platform-role only —
137
+ * an API-key (merchant) caller gets a 403 there — so passing `merchantId`
138
+ * (which every merchant API key call needs) routes to
139
+ * `/payments/merchant/:merchantId` instead, matching what the API
140
+ * actually allows a merchant credential to call.
141
+ */
142
+ list(params) {
143
+ const { merchantId, ...rest } = params ?? {};
144
+ const path = merchantId ? `/payments/merchant/${merchantId}` : "/payments";
145
+ return this.http.get(path, rest);
146
+ }
147
+ retrieve(id) {
148
+ return this.http.get(`/payments/${id}`);
149
+ }
150
+ verify(id) {
151
+ return this.http.post(`/payments/${id}/verify`);
152
+ }
153
+ retry(id) {
154
+ return this.http.post(`/payments/${id}/retry`);
155
+ }
156
+ listAttempts(id) {
157
+ return this.http.get(`/payments/${id}/attempts`);
158
+ }
159
+ };
160
+
161
+ // src/resources/escrows.ts
162
+ var EscrowsResource = class {
163
+ constructor(http) {
164
+ this.http = http;
165
+ }
166
+ create(params, opts) {
167
+ const body = opts?.idempotencyKey ? { ...params, idempotencyKey: opts.idempotencyKey } : params;
168
+ return this.http.post("/escrows", body);
169
+ }
170
+ /**
171
+ * Lists escrows. The bare `/escrows` endpoint is platform-role only — an
172
+ * API-key (merchant) caller gets a 403 there — so passing `merchantId`
173
+ * routes to `/escrows/merchant/:merchantId` instead. Unlike
174
+ * payments/transactions/disputes, this returns a `{ escrows, total,
175
+ * limit, offset }` envelope rather than a bare array.
176
+ */
177
+ list(params) {
178
+ const { merchantId, ...rest } = params ?? {};
179
+ const path = merchantId ? `/escrows/merchant/${merchantId}` : "/escrows";
180
+ return this.http.get(path, rest);
181
+ }
182
+ retrieve(id) {
183
+ return this.http.get(`/escrows/${id}`);
184
+ }
185
+ retrieveBalance(id) {
186
+ return this.http.get(`/escrows/${id}/balance`);
187
+ }
188
+ retrieveConditions(id) {
189
+ return this.http.get(`/escrows/${id}/conditions`);
190
+ }
191
+ release(id, params) {
192
+ return this.http.post(`/escrows/${id}/release`, params);
193
+ }
194
+ refund(id) {
195
+ return this.http.post(`/escrows/${id}/refund`);
196
+ }
197
+ dispute(id) {
198
+ return this.http.post(`/escrows/${id}/dispute`);
199
+ }
200
+ cancel(id) {
201
+ return this.http.post(`/escrows/${id}/cancel`);
202
+ }
203
+ confirmDelivery(id, params) {
204
+ return this.http.post(`/escrows/${id}/confirm-delivery`, params);
205
+ }
206
+ confirmBuyer(id, params) {
207
+ return this.http.post(`/escrows/${id}/confirm-buyer`, params);
208
+ }
209
+ };
210
+
211
+ // src/resources/disputes.ts
212
+ var DisputesResource = class {
213
+ constructor(http) {
214
+ this.http = http;
215
+ }
216
+ open(params) {
217
+ return this.http.post("/disputes", params);
218
+ }
219
+ /**
220
+ * Lists disputes. The bare `/disputes` endpoint is platform-role only —
221
+ * an API-key (merchant) caller gets a 403 there — so passing
222
+ * `merchantId` routes to `/disputes/merchant/:merchantId` instead.
223
+ */
224
+ list(params) {
225
+ const { merchantId, ...rest } = params ?? {};
226
+ const path = merchantId ? `/disputes/merchant/${merchantId}` : "/disputes";
227
+ return this.http.get(path, rest);
228
+ }
229
+ retrieve(id) {
230
+ return this.http.get(`/disputes/${id}`);
231
+ }
232
+ listEvidence(id) {
233
+ return this.http.get(`/disputes/${id}/evidence`);
234
+ }
235
+ uploadEvidence(id, params) {
236
+ return this.http.post(`/disputes/${id}/evidence`, params);
237
+ }
238
+ resolve(id, params) {
239
+ return this.http.post(`/disputes/${id}/resolve`, params);
240
+ }
241
+ cancel(id, params) {
242
+ return this.http.post(`/disputes/${id}/cancel`, params);
243
+ }
244
+ };
245
+ var WebhooksResource = class {
246
+ constructor(secret) {
247
+ this.secret = secret;
248
+ }
249
+ /**
250
+ * Verifies the signature on an incoming webhook and parses the payload.
251
+ *
252
+ * PayBeta signs the concatenation of the delivery timestamp and the raw
253
+ * request body — `HMAC-SHA256(secret, "${timestamp}.${rawBody}")` — and
254
+ * sends the result hex-encoded, prefixed with `sha256=`, in the
255
+ * `X-PayBeta-Signature` header (see OutboundWebhookService). The
256
+ * timestamp itself arrives separately in `X-PayBeta-Timestamp`, so both
257
+ * headers are required here, not just the signature.
258
+ *
259
+ * Pass the raw request body (before JSON parsing), the value of the
260
+ * `X-PayBeta-Signature` header, and the value of the `X-PayBeta-Timestamp`
261
+ * header. Throws `PaybetaError` if the signature is invalid or the
262
+ * secret was not configured.
263
+ */
264
+ constructEvent(rawBody, signature, timestamp) {
265
+ if (!this.secret) {
266
+ throw new PaybetaError(
267
+ "webhookSecret must be set on PaybetaClient to verify webhook signatures"
268
+ );
269
+ }
270
+ const body = typeof rawBody === "string" ? rawBody : rawBody.toString("utf8");
271
+ const hexSignature = signature.startsWith("sha256=") ? signature.slice("sha256=".length) : signature;
272
+ const expected = createHmac("sha256", this.secret).update(`${timestamp}.${body}`).digest("hex");
273
+ let actual;
274
+ let expectedBuf;
275
+ try {
276
+ actual = Buffer.from(hexSignature, "hex");
277
+ expectedBuf = Buffer.from(expected, "hex");
278
+ } catch {
279
+ throw new PaybetaError("Invalid signature format");
280
+ }
281
+ if (expectedBuf.length !== actual.length || !timingSafeEqual(expectedBuf, actual)) {
282
+ throw new PaybetaError("Webhook signature verification failed");
283
+ }
284
+ return JSON.parse(body);
285
+ }
286
+ };
287
+
288
+ // src/client.ts
289
+ var PaybetaClient = class {
290
+ constructor(config) {
291
+ if (!config.apiKey) {
292
+ throw new Error("PaybetaClient: apiKey is required");
293
+ }
294
+ const http = new HttpClient({
295
+ apiKey: config.apiKey,
296
+ baseUrl: config.baseUrl ?? "https://api.usepaybeta.com",
297
+ timeout: config.timeout ?? 3e4
298
+ });
299
+ this.transactions = new TransactionsResource(http);
300
+ this.payments = new PaymentsResource(http);
301
+ this.escrows = new EscrowsResource(http);
302
+ this.disputes = new DisputesResource(http);
303
+ this.webhooks = new WebhooksResource(config.webhookSecret ?? "");
304
+ }
305
+ };
306
+
307
+ export { PaybetaApiError, PaybetaClient, PaybetaError };
308
+ //# sourceMappingURL=index.mjs.map
309
+ //# sourceMappingURL=index.mjs.map