@bitalcer/partner-api 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/index.d.ts +156 -0
  2. package/index.js +292 -0
  3. package/package.json +17 -0
package/index.d.ts ADDED
@@ -0,0 +1,156 @@
1
+ /**
2
+ * Bitalcer Partner API — TypeScript type definitions v1.0.0
3
+ */
4
+
5
+ declare module '@bitalcer/partner-api' {
6
+ export interface ClientConfig {
7
+ apiKey: string;
8
+ hmacSecret: string;
9
+ baseUrl?: string;
10
+ }
11
+
12
+ export interface PingResponse {
13
+ status: string;
14
+ partner_id: number;
15
+ environment: string;
16
+ }
17
+
18
+ export interface UserResponse {
19
+ id: number;
20
+ partner_id: number;
21
+ external_user_id: string;
22
+ internal_user_id: number | null;
23
+ status: string;
24
+ }
25
+
26
+ export interface WalletResponse {
27
+ external_user_id: string;
28
+ address: string;
29
+ chain: string;
30
+ }
31
+
32
+ export interface BalanceResponse {
33
+ external_user_id: string;
34
+ token: string;
35
+ chain: string;
36
+ balance: string;
37
+ address: string;
38
+ }
39
+
40
+ export interface WithdrawParams {
41
+ externalUserId: string;
42
+ toAddress: string;
43
+ amount: string;
44
+ token?: string;
45
+ chain?: 'bsc' | 'polygon';
46
+ }
47
+
48
+ export interface WithdrawResponse {
49
+ withdraw_id: string;
50
+ tx_hash: string;
51
+ status: string;
52
+ }
53
+
54
+ export interface TransferParams {
55
+ senderExternalUserId: string;
56
+ receiverExternalUserId: string;
57
+ amount: string;
58
+ token?: string;
59
+ }
60
+
61
+ export interface TransferResponse {
62
+ transfer_id: string;
63
+ status: string;
64
+ }
65
+
66
+ export interface QuoteParams {
67
+ externalUserId: string;
68
+ baseAsset: 'GTQ' | 'USDT';
69
+ quoteAsset: 'GTQ' | 'USDT';
70
+ amount: number;
71
+ }
72
+
73
+ export interface QuoteResponse {
74
+ pair: string;
75
+ estimated_amount_out: number;
76
+ price_applied: number;
77
+ fee_amount: number;
78
+ fee_bps: number;
79
+ }
80
+
81
+ export interface CreateOrderParams {
82
+ externalUserId: string;
83
+ baseAsset: 'GTQ' | 'USDT';
84
+ quoteAsset: 'GTQ' | 'USDT';
85
+ requestedAmount: number;
86
+ side: 'buy' | 'sell';
87
+ }
88
+
89
+ export interface OrderResponse {
90
+ order_id: string;
91
+ status: string;
92
+ quote_expires_at: string;
93
+ }
94
+
95
+ export interface ExecuteResponse {
96
+ order_id: string;
97
+ status: string;
98
+ executed_at: string;
99
+ }
100
+
101
+ export interface OrderStatusResponse {
102
+ order_id: string;
103
+ status: string;
104
+ executed_amount: number;
105
+ price_applied: number;
106
+ }
107
+
108
+ export interface TreasuryBalanceResponse {
109
+ partner_id: number;
110
+ available_balance_usd: number;
111
+ reserved_balance_usd: number;
112
+ tier: string;
113
+ }
114
+
115
+ export interface ApiError extends Error {
116
+ status: number;
117
+ code: string;
118
+ body: any;
119
+ }
120
+
121
+ export class UsersClient {
122
+ create(externalUserId: string): Promise<UserResponse>;
123
+ }
124
+
125
+ export class WalletsClient {
126
+ create(externalUserId: string, chain?: 'bsc' | 'polygon' | 'btc'): Promise<WalletResponse>;
127
+ getBalance(externalUserId: string, opts?: { token?: string; chain?: string }): Promise<BalanceResponse>;
128
+ getAddress(externalUserId: string, opts?: { chain?: string }): Promise<WalletResponse>;
129
+ }
130
+
131
+ export class TransactionsClient {
132
+ withdraw(params: WithdrawParams): Promise<WithdrawResponse>;
133
+ transfer(params: TransferParams): Promise<TransferResponse>;
134
+ }
135
+
136
+ export class ConversionClient {
137
+ quote(params: QuoteParams): Promise<QuoteResponse>;
138
+ createOrder(params: CreateOrderParams): Promise<OrderResponse>;
139
+ execute(externalUserId: string, orderId: string): Promise<ExecuteResponse>;
140
+ getStatus(externalUserId: string, orderId: string): Promise<OrderStatusResponse>;
141
+ }
142
+
143
+ export class TreasuryClient {
144
+ getBalance(): Promise<TreasuryBalanceResponse>;
145
+ }
146
+
147
+ export class Client {
148
+ constructor(config: ClientConfig);
149
+ ping(): Promise<PingResponse>;
150
+ users: UsersClient;
151
+ wallets: WalletsClient;
152
+ transactions: TransactionsClient;
153
+ conversion: ConversionClient;
154
+ treasury: TreasuryClient;
155
+ }
156
+ }
package/index.js ADDED
@@ -0,0 +1,292 @@
1
+ /**
2
+ * Bitalcer Partner API — JavaScript SDK v1.0.0
3
+ *
4
+ * Cliente oficial para la Bitalcer Custody-as-a-Service Partner API.
5
+ * Firma HMAC-SHA256 automática. Sin dependencias externas.
6
+ *
7
+ * @example
8
+ * const { Client } = require('@bitalcer/partner-api');
9
+ * const bitalcer = new Client({ apiKey: 'bak_...', hmacSecret: '...' });
10
+ * await bitalcer.ping();
11
+ * const user = await bitalcer.users.create('cust-00001');
12
+ */
13
+
14
+ 'use strict';
15
+
16
+ const crypto = require('crypto');
17
+
18
+ // ── Helpers ───────────────────────────────────────────────────────────────
19
+
20
+ /**
21
+ * Calcula la firma HMAC-SHA256 para un request.
22
+ * @param {string} secret - HMAC secret en hex
23
+ * @param {string} timestamp - Epoch en ms como string
24
+ * @param {string} body - Body crudo (string vacío para GET)
25
+ * @returns {string} Firma en hexadecimal
26
+ */
27
+ function sign(secret, timestamp, body) {
28
+ return crypto
29
+ .createHmac('sha256', secret)
30
+ .update(timestamp + ':' + body)
31
+ .digest('hex');
32
+ }
33
+
34
+ /**
35
+ * Realiza un request firmado a la Partner API.
36
+ * @param {object} opts
37
+ * @param {string} opts.baseUrl
38
+ * @param {string} opts.apiKey
39
+ * @param {string} opts.hmacSecret
40
+ * @param {string} opts.method
41
+ * @param {string} opts.path
42
+ * @param {object} [opts.body]
43
+ * @returns {Promise<object>} Respuesta parseada como JSON
44
+ */
45
+ async function request(opts) {
46
+ const { baseUrl, apiKey, hmacSecret, method, path, body } = opts;
47
+ const rawBody = body ? JSON.stringify(body) : '';
48
+ const timestamp = String(Date.now());
49
+ const signature = sign(hmacSecret, timestamp, rawBody);
50
+
51
+ const headers = {
52
+ 'x-api-key': apiKey,
53
+ 'x-timestamp': timestamp,
54
+ 'x-signature': signature,
55
+ };
56
+ if (body) {
57
+ headers['Content-Type'] = 'application/json';
58
+ }
59
+
60
+ const url = baseUrl.replace(/\/+$/, '') + '/' + path.replace(/^\/+/, '');
61
+ const res = await fetch(url, {
62
+ method,
63
+ headers,
64
+ body: rawBody || undefined,
65
+ });
66
+
67
+ const data = await res.json();
68
+
69
+ if (!res.ok) {
70
+ const err = new Error(data.error || data.message || `HTTP ${res.status}`);
71
+ err.status = res.status;
72
+ err.code = data.error;
73
+ err.body = data;
74
+ throw err;
75
+ }
76
+
77
+ return data;
78
+ }
79
+
80
+ // ── Client ────────────────────────────────────────────────────────────────
81
+
82
+ class Client {
83
+ /**
84
+ * @param {object} config
85
+ * @param {string} config.apiKey - API key de sandbox o production
86
+ * @param {string} config.hmacSecret - HMAC secret correspondiente
87
+ * @param {string} [config.baseUrl='https://api.bitalcer.com']
88
+ */
89
+ constructor({ apiKey, hmacSecret, baseUrl = 'https://api.bitalcer.com' }) {
90
+ if (!apiKey || !hmacSecret) {
91
+ throw new Error('apiKey y hmacSecret son obligatorios.');
92
+ }
93
+ /** @private */ this._baseUrl = baseUrl;
94
+ /** @private */ this._apiKey = apiKey;
95
+ /** @private */ this._hmacSecret = hmacSecret;
96
+
97
+ this.users = new UsersClient(this);
98
+ this.wallets = new WalletsClient(this);
99
+ this.transactions = new TransactionsClient(this);
100
+ this.conversion = new ConversionClient(this);
101
+ this.treasury = new TreasuryClient(this);
102
+ }
103
+
104
+ /**
105
+ * Verifica conectividad y credenciales.
106
+ * @returns {Promise<{status: string, partner_id: number, environment: string}>}
107
+ */
108
+ async ping() {
109
+ return this._req('GET', '/partner/v1/ping');
110
+ }
111
+
112
+ /** @private */
113
+ _req(method, path, body) {
114
+ return request({
115
+ baseUrl: this._baseUrl,
116
+ apiKey: this._apiKey,
117
+ hmacSecret: this._hmacSecret,
118
+ method,
119
+ path,
120
+ body,
121
+ });
122
+ }
123
+ }
124
+
125
+ // ── Sub-clientes ──────────────────────────────────────────────────────────
126
+
127
+ class UsersClient {
128
+ /** @private */ constructor(client) { this._c = client; }
129
+
130
+ /**
131
+ * Registra (u obtiene) un usuario del partner.
132
+ * Idempotente: si el external_user_id ya existe, devuelve el registro existente.
133
+ * @param {string} externalUserId - Identificador del usuario en tu sistema
134
+ * @returns {Promise<{id: number, partner_id: number, external_user_id: string, internal_user_id: number|null, status: string}>}
135
+ */
136
+ async create(externalUserId) {
137
+ return this._c._req('POST', '/partner/v1/users', { external_user_id: externalUserId });
138
+ }
139
+ }
140
+
141
+ class WalletsClient {
142
+ /** @private */ constructor(client) { this._c = client; }
143
+
144
+ /**
145
+ * Crea una wallet on-chain para un usuario.
146
+ * @param {string} externalUserId
147
+ * @param {'bsc'|'polygon'|'btc'} [chain='bsc']
148
+ * @returns {Promise<{external_user_id: string, address: string, chain: string}>}
149
+ */
150
+ async create(externalUserId, chain = 'bsc') {
151
+ return this._c._req('POST', '/partner/v1/wallets', { external_user_id: externalUserId, chain });
152
+ }
153
+
154
+ /**
155
+ * Consulta el saldo de un token en la wallet de un usuario.
156
+ * @param {string} externalUserId
157
+ * @param {{token?: string, chain?: string}} [opts]
158
+ * @returns {Promise<{external_user_id: string, token: string, chain: string, balance: string, address: string}>}
159
+ */
160
+ async getBalance(externalUserId, { token = 'USDT', chain = 'bsc' } = {}) {
161
+ const qs = `token=${token}&chain=${chain}`;
162
+ return this._c._req('GET', `/partner/v1/wallets/${externalUserId}/balance?${qs}`);
163
+ }
164
+
165
+ /**
166
+ * Obtiene la dirección de depósito. Si no existe wallet, la crea automáticamente.
167
+ * @param {string} externalUserId
168
+ * @param {{chain?: string}} [opts]
169
+ * @returns {Promise<{external_user_id: string, address: string, chain: string}>}
170
+ */
171
+ async getAddress(externalUserId, { chain = 'bsc' } = {}) {
172
+ return this._c._req('GET', `/partner/v1/wallets/${externalUserId}/address?chain=${chain}`);
173
+ }
174
+ }
175
+
176
+ class TransactionsClient {
177
+ /** @private */ constructor(client) { this._c = client; }
178
+
179
+ /**
180
+ * Retira fondos hacia una dirección externa. Descuenta del Partner Treasury.
181
+ * @param {object} params
182
+ * @param {string} params.externalUserId
183
+ * @param {string} params.toAddress - Dirección de destino
184
+ * @param {string} params.amount - Monto (ej. "100.00")
185
+ * @param {string} [params.token='USDT']
186
+ * @param {'bsc'|'polygon'} [params.chain='bsc']
187
+ * @returns {Promise<{withdraw_id: string, tx_hash: string, status: string}>}
188
+ */
189
+ async withdraw({ externalUserId, toAddress, amount, token = 'USDT', chain = 'bsc' }) {
190
+ return this._c._req('POST', '/partner/v1/transactions/withdraw', {
191
+ external_user_id: externalUserId,
192
+ to_address: toAddress,
193
+ amount,
194
+ token,
195
+ chain,
196
+ });
197
+ }
198
+
199
+ /**
200
+ * Transferencia interna instantánea entre dos usuarios del partner.
201
+ * @param {object} params
202
+ * @param {string} params.senderExternalUserId
203
+ * @param {string} params.receiverExternalUserId
204
+ * @param {string} params.amount
205
+ * @param {string} [params.token='USDT']
206
+ * @returns {Promise<{transfer_id: string, status: string}>}
207
+ */
208
+ async transfer({ senderExternalUserId, receiverExternalUserId, amount, token = 'USDT' }) {
209
+ return this._c._req('POST', '/partner/v1/transactions/transfer', {
210
+ sender_external_user_id: senderExternalUserId,
211
+ receiver_external_user_id: receiverExternalUserId,
212
+ amount,
213
+ token,
214
+ });
215
+ }
216
+ }
217
+
218
+ class ConversionClient {
219
+ /** @private */ constructor(client) { this._c = client; }
220
+
221
+ /**
222
+ * Paso 1/4: Cotiza conversión GTQ↔USDT sin crear orden.
223
+ * @param {object} params
224
+ * @param {string} params.externalUserId
225
+ * @param {'GTQ'|'USDT'} params.baseAsset
226
+ * @param {'GTQ'|'USDT'} params.quoteAsset
227
+ * @param {number} params.amount
228
+ * @returns {Promise<{pair: string, estimated_amount_out: number, price_applied: number}>}
229
+ */
230
+ async quote({ externalUserId, baseAsset, quoteAsset, amount }) {
231
+ const qs = `external_user_id=${externalUserId}&base_asset=${baseAsset}&quote_asset=${quoteAsset}&amount=${amount}`;
232
+ return this._c._req('GET', `/partner/v1/conversion/quote?${qs}`);
233
+ }
234
+
235
+ /**
236
+ * Paso 2/4: Crea una orden de conversión en estado quoted.
237
+ * @param {object} params
238
+ * @param {string} params.externalUserId
239
+ * @param {'GTQ'|'USDT'} params.baseAsset
240
+ * @param {'GTQ'|'USDT'} params.quoteAsset
241
+ * @param {number} params.requestedAmount
242
+ * @param {'buy'|'sell'} params.side
243
+ * @returns {Promise<{order_id: string, status: string, quote_expires_at: string}>}
244
+ */
245
+ async createOrder({ externalUserId, baseAsset, quoteAsset, requestedAmount, side }) {
246
+ return this._c._req('POST', '/partner/v1/conversion/orders', {
247
+ external_user_id: externalUserId,
248
+ base_asset: baseAsset,
249
+ quote_asset: quoteAsset,
250
+ requested_amount: requestedAmount,
251
+ side,
252
+ });
253
+ }
254
+
255
+ /**
256
+ * Paso 3/4: Ejecuta una orden de conversión.
257
+ * @param {string} externalUserId
258
+ * @param {string} orderId
259
+ * @returns {Promise<{order_id: string, status: string, executed_at: string}>}
260
+ */
261
+ async execute(externalUserId, orderId) {
262
+ return this._c._req('POST', '/partner/v1/conversion/execute', {
263
+ external_user_id: externalUserId,
264
+ order_id: orderId,
265
+ });
266
+ }
267
+
268
+ /**
269
+ * Paso 4/4: Consulta el estado de una orden.
270
+ * @param {string} externalUserId
271
+ * @param {string} orderId
272
+ * @returns {Promise<{order_id: string, status: string, executed_amount: number}>}
273
+ */
274
+ async getStatus(externalUserId, orderId) {
275
+ const qs = `external_user_id=${externalUserId}&order_id=${orderId}`;
276
+ return this._c._req('GET', `/partner/v1/conversion/status?${qs}`);
277
+ }
278
+ }
279
+
280
+ class TreasuryClient {
281
+ /** @private */ constructor(client) { this._c = client; }
282
+
283
+ /**
284
+ * Consulta el saldo del Partner Treasury (tu saldo prepagado en USD).
285
+ * @returns {Promise<{partner_id: number, available_balance_usd: number, reserved_balance_usd: number, tier: string}>}
286
+ */
287
+ async getBalance() {
288
+ return this._c._req('GET', '/partner/v1/treasury/balance');
289
+ }
290
+ }
291
+
292
+ module.exports = { Client };
package/package.json ADDED
@@ -0,0 +1,17 @@
1
+ {
2
+ "name": "@bitalcer/partner-api",
3
+ "version": "1.0.0",
4
+ "description": "Bitalcer Custody-as-a-Service Partner API — SDK oficial para JavaScript/TypeScript. Firma HMAC-SHA256 automática, tipado completo.",
5
+ "main": "index.js",
6
+ "types": "index.d.ts",
7
+ "files": ["index.js", "index.d.ts", "README.md"],
8
+ "keywords": ["bitalcer", "crypto", "custody", "wallet", "api", "partner", "hmac", "usdt", "remesas"],
9
+ "license": "MIT",
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "https://github.com/bitalcer/partner-api-js"
13
+ },
14
+ "engines": {
15
+ "node": ">=16"
16
+ }
17
+ }