@pabilo/sdk 0.1.5 → 0.1.7
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 +490 -65
- package/dist/index.cjs +46 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +70 -11
- package/dist/index.d.ts +70 -11
- package/dist/index.js +46 -3
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -13,8 +13,6 @@ TypeScript/JavaScript SDK for [pabilo.app](https://pabilo.app) — the Venezuela
|
|
|
13
13
|
npm install @pabilo/sdk
|
|
14
14
|
# or
|
|
15
15
|
pnpm add @pabilo/sdk
|
|
16
|
-
# or
|
|
17
|
-
yarn add @pabilo/sdk
|
|
18
16
|
```
|
|
19
17
|
|
|
20
18
|
## Quick start
|
|
@@ -25,41 +23,168 @@ import { PabiloClient } from '@pabilo/sdk';
|
|
|
25
23
|
const pabilo = new PabiloClient({ apiKey: 'YOUR_API_KEY' });
|
|
26
24
|
```
|
|
27
25
|
|
|
26
|
+
The client exposes four namespaces:
|
|
27
|
+
|
|
28
|
+
| Namespace | Description |
|
|
29
|
+
|---|---|
|
|
30
|
+
| `pabilo.me` | Current user and plan info |
|
|
31
|
+
| `pabilo.bankAccounts` | Manage connected bank accounts |
|
|
32
|
+
| `pabilo.paymentLinks` | Create and manage payment links |
|
|
33
|
+
| `pabilo.payments` | Verify payments by bank reference |
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## `pabilo.me`
|
|
38
|
+
|
|
39
|
+
### `me.getMe(): Promise<User>`
|
|
40
|
+
|
|
41
|
+
Returns the authenticated user's profile.
|
|
42
|
+
|
|
43
|
+
```typescript
|
|
44
|
+
const user = await pabilo.me.getMe();
|
|
45
|
+
|
|
46
|
+
console.log(user.id); // string
|
|
47
|
+
console.log(user.email); // string | undefined
|
|
48
|
+
console.log(user.username); // string | undefined
|
|
49
|
+
console.log(user.fullName); // string | undefined
|
|
50
|
+
console.log(user.companyName); // string | undefined
|
|
51
|
+
console.log(user.credits); // number | undefined
|
|
52
|
+
console.log(user.planIsActive);// boolean | undefined
|
|
53
|
+
console.log(user.isDemo); // boolean | undefined
|
|
54
|
+
console.log(user.userType); // 'user' | 'admin' | 'commerce' | ... | undefined
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
**Returns:** `User`
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
interface User {
|
|
61
|
+
id: string;
|
|
62
|
+
email?: string;
|
|
63
|
+
username?: string;
|
|
64
|
+
fullName?: string;
|
|
65
|
+
companyName?: string;
|
|
66
|
+
credits?: number;
|
|
67
|
+
planIsActive?: boolean;
|
|
68
|
+
isDemo?: boolean;
|
|
69
|
+
userType?: UserType;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
type UserType = 'system' | 'user' | 'admin' | 'test' | 'commerce' | string;
|
|
73
|
+
```
|
|
74
|
+
|
|
28
75
|
---
|
|
29
76
|
|
|
30
|
-
|
|
77
|
+
### `me.getPlan(): Promise<Plan>`
|
|
31
78
|
|
|
32
|
-
|
|
79
|
+
Returns the current subscription plan for the authenticated account.
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
const plan = await pabilo.me.getPlan();
|
|
83
|
+
|
|
84
|
+
console.log(plan.name); // e.g. 'Pro'
|
|
85
|
+
console.log(plan.planType); // 'credit' | 'unlimited' | 'counter'
|
|
86
|
+
console.log(plan.period); // 'month' | 'six_months' | 'year'
|
|
87
|
+
console.log(plan.requestLimit); // number (-1 = unlimited)
|
|
88
|
+
console.log(plan.bankAccountLimit); // number (-1 = unlimited)
|
|
89
|
+
console.log(plan.initialCredits); // number
|
|
90
|
+
console.log(plan.price); // number
|
|
91
|
+
console.log(plan.benefits); // PlanBenefit[]
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
**Returns:** `Plan`
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
interface Plan {
|
|
98
|
+
id?: string;
|
|
99
|
+
name: string;
|
|
100
|
+
description?: string;
|
|
101
|
+
planType?: PlanType; // 'credit' | 'unlimited' | 'counter'
|
|
102
|
+
period?: PlanPeriod; // 'month' | 'six_months' | 'year'
|
|
103
|
+
price?: number;
|
|
104
|
+
requestLimit?: number;
|
|
105
|
+
bankAccountLimit?: number;
|
|
106
|
+
initialCredits?: number;
|
|
107
|
+
maxAcumulatedCredits?: number;
|
|
108
|
+
benefits?: PlanBenefit[];
|
|
109
|
+
salient?: boolean;
|
|
110
|
+
hidden?: boolean;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
interface PlanBenefit {
|
|
114
|
+
description: string;
|
|
115
|
+
contain: boolean;
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## `pabilo.bankAccounts`
|
|
122
|
+
|
|
123
|
+
### `bankAccounts.list(): Promise<UserBank[]>`
|
|
124
|
+
|
|
125
|
+
Returns all bank accounts connected to the authenticated user.
|
|
33
126
|
|
|
34
127
|
```typescript
|
|
35
128
|
const banks = await pabilo.bankAccounts.list();
|
|
36
129
|
|
|
37
130
|
for (const bank of banks) {
|
|
38
|
-
console.log(bank.id
|
|
39
|
-
|
|
131
|
+
console.log(bank.id); // MongoDB ObjectId string
|
|
132
|
+
console.log(bank.provider); // 'VE_BAN' | 'VE_BAN_EMP_V2' | 'BANK_TEST' | ...
|
|
133
|
+
console.log(bank.description); // display name set during creation
|
|
134
|
+
console.log(bank.bank_accounts); // BankAccountEntry[]
|
|
135
|
+
console.log(bank.to_trash); // boolean — true if soft-deleted
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
**Returns:** `UserBank[]`
|
|
140
|
+
|
|
141
|
+
```typescript
|
|
142
|
+
interface UserBank {
|
|
143
|
+
id: string;
|
|
144
|
+
description: string;
|
|
145
|
+
provider: BankProvider;
|
|
146
|
+
bank_accounts: BankAccountEntry[];
|
|
147
|
+
payment_link?: boolean;
|
|
148
|
+
to_trash?: boolean;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
interface BankAccountEntry {
|
|
152
|
+
account_number: string;
|
|
153
|
+
account_type: string; // e.g. 'AHORRO', 'CORRIENTE'
|
|
40
154
|
}
|
|
155
|
+
|
|
156
|
+
type BankProvider =
|
|
157
|
+
| 'VE_BAN' // BDV personal
|
|
158
|
+
| 'VE_BAN_EMP' // BDV empresa v1
|
|
159
|
+
| 'VE_BAN_EMP_V2' // BDV empresa v2
|
|
160
|
+
| 'VE_PROV' // Provincial personal
|
|
161
|
+
| 'VE_PROV_EMP' // Provincial empresa
|
|
162
|
+
| 'MERCANTIL_EMP_V1' // Mercantil empresa
|
|
163
|
+
| 'MERCANTIL_EMP_TEST_V1' // Mercantil test
|
|
164
|
+
| 'BANK_TEST' // Sandbox
|
|
165
|
+
| string;
|
|
41
166
|
```
|
|
42
167
|
|
|
43
|
-
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
### `bankAccounts.create(req): Promise<UserBank>`
|
|
44
171
|
|
|
45
|
-
Each provider has its own typed request
|
|
172
|
+
Creates a new bank account connection. Each provider has its own typed request — TypeScript enforces required fields per provider via a discriminated union on `bankProvider`.
|
|
46
173
|
|
|
47
|
-
**BDV personal
|
|
174
|
+
**BDV personal — `VE_BAN`**
|
|
48
175
|
|
|
49
176
|
```typescript
|
|
50
177
|
const bank = await pabilo.bankAccounts.create({
|
|
51
178
|
bankProvider: 'VE_BAN',
|
|
52
|
-
description: 'Mi cuenta BDV',
|
|
179
|
+
description: 'Mi cuenta BDV personal',
|
|
53
180
|
userBankPhone: '04241234567',
|
|
54
181
|
userBankDni: '12345678',
|
|
55
182
|
username: 'usuario_portal_bdv',
|
|
56
183
|
password: 'contraseña_portal_bdv',
|
|
57
184
|
});
|
|
58
|
-
|
|
59
|
-
console.log(bank.id); // use this id when creating payment links
|
|
60
185
|
```
|
|
61
186
|
|
|
62
|
-
**BDV empresa
|
|
187
|
+
**BDV empresa — `VE_BAN_EMP_V2`**
|
|
63
188
|
|
|
64
189
|
```typescript
|
|
65
190
|
const bank = await pabilo.bankAccounts.create({
|
|
@@ -67,7 +192,7 @@ const bank = await pabilo.bankAccounts.create({
|
|
|
67
192
|
description: 'Cuenta empresa BDV',
|
|
68
193
|
userBankPhone: '04241234567',
|
|
69
194
|
userBankDni: '12345678',
|
|
70
|
-
accountNumber: '
|
|
195
|
+
accountNumber: '01020000000000000000',
|
|
71
196
|
apiKey: 'bdv_api_key_here',
|
|
72
197
|
metadata: [
|
|
73
198
|
{ key_name: 'SHOW_DATE_IN_GENERIC_MOVEMENTS', key_value: 'true' },
|
|
@@ -75,13 +200,19 @@ const bank = await pabilo.bankAccounts.create({
|
|
|
75
200
|
});
|
|
76
201
|
```
|
|
77
202
|
|
|
78
|
-
**Test bank
|
|
203
|
+
**Test bank — `BANK_TEST`**
|
|
204
|
+
|
|
205
|
+
A sandboxed bank for testing. Always has a payment with reference `67890` available.
|
|
79
206
|
|
|
80
207
|
```typescript
|
|
81
208
|
const bank = await pabilo.bankAccounts.create({ bankProvider: 'BANK_TEST' });
|
|
82
209
|
```
|
|
83
210
|
|
|
84
|
-
|
|
211
|
+
---
|
|
212
|
+
|
|
213
|
+
### `bankAccounts.delete(id): Promise<void>`
|
|
214
|
+
|
|
215
|
+
Soft-deletes a bank account. The bank no longer appears in `list()` results.
|
|
85
216
|
|
|
86
217
|
```typescript
|
|
87
218
|
await pabilo.bankAccounts.delete(bank.id);
|
|
@@ -89,9 +220,53 @@ await pabilo.bankAccounts.delete(bank.id);
|
|
|
89
220
|
|
|
90
221
|
---
|
|
91
222
|
|
|
92
|
-
##
|
|
223
|
+
## `pabilo.paymentLinks`
|
|
224
|
+
|
|
225
|
+
### `paymentLinks.list(req?): Promise<PaymentLinksPage>`
|
|
226
|
+
|
|
227
|
+
Returns a paginated list of the authenticated user's payment links.
|
|
93
228
|
|
|
94
|
-
|
|
229
|
+
```typescript
|
|
230
|
+
const page = await pabilo.paymentLinks.list({
|
|
231
|
+
limit: 10,
|
|
232
|
+
page: 1,
|
|
233
|
+
status: 'pending', // filter by status
|
|
234
|
+
type: 'default', // filter by type
|
|
235
|
+
search: 'orden', // search by name/description
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
console.log(page.total); // total count
|
|
239
|
+
console.log(page.page); // current page
|
|
240
|
+
console.log(page.limit); // page size
|
|
241
|
+
console.log(page.items); // PaymentLink[]
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
**Request fields (all optional):**
|
|
245
|
+
|
|
246
|
+
| Field | Type | Default | Description |
|
|
247
|
+
|---|---|---|---|
|
|
248
|
+
| `page` | `number` | `1` | Page number |
|
|
249
|
+
| `limit` | `number` | `10` | Items per page |
|
|
250
|
+
| `status` | `PaymentLinkStatus` | — | Filter by status |
|
|
251
|
+
| `type` | `PaymentLinkType` | — | Filter by type |
|
|
252
|
+
| `search` | `string` | — | Text search |
|
|
253
|
+
|
|
254
|
+
**Returns:** `PaymentLinksPage`
|
|
255
|
+
|
|
256
|
+
```typescript
|
|
257
|
+
interface PaymentLinksPage {
|
|
258
|
+
items: PaymentLink[];
|
|
259
|
+
total: number;
|
|
260
|
+
page: number;
|
|
261
|
+
limit: number;
|
|
262
|
+
}
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
---
|
|
266
|
+
|
|
267
|
+
### `paymentLinks.create(req): Promise<PaymentLink>`
|
|
268
|
+
|
|
269
|
+
Creates a new payment link that customers can use to pay via Pago Móvil.
|
|
95
270
|
|
|
96
271
|
```typescript
|
|
97
272
|
const link = await pabilo.paymentLinks.create({
|
|
@@ -99,38 +274,102 @@ const link = await pabilo.paymentLinks.create({
|
|
|
99
274
|
description: 'Orden #1234',
|
|
100
275
|
userBankId: bank.id,
|
|
101
276
|
// optional
|
|
277
|
+
name: 'Servicio mensual',
|
|
102
278
|
redirectUrl: 'https://myapp.com/gracias',
|
|
103
279
|
webhookUrl: 'https://myapp.com/webhook/pabilo',
|
|
104
280
|
notificationByWhatsapp: true,
|
|
105
|
-
name: 'Pago por servicio',
|
|
106
281
|
isUsd: false,
|
|
282
|
+
currency: 'VES',
|
|
107
283
|
});
|
|
108
284
|
|
|
109
285
|
console.log(link.url); // https://pabilo.app/pay/...
|
|
110
286
|
console.log(link.id);
|
|
111
|
-
console.log(link.status); // 'pending' | 'paid' | '
|
|
287
|
+
console.log(link.status); // 'pending' | 'active' | 'paid' | 'failed' | 'canceled' | 'expired' | 'stopped'
|
|
112
288
|
```
|
|
113
289
|
|
|
114
|
-
|
|
290
|
+
**Request fields:**
|
|
291
|
+
|
|
292
|
+
| Field | Type | Required | Description |
|
|
293
|
+
|---|---|---|---|
|
|
294
|
+
| `amount` | `number` | yes | Amount in the payment currency |
|
|
295
|
+
| `description` | `string` | yes | Internal description |
|
|
296
|
+
| `userBankId` | `string` | yes | ID of the bank account to receive payment |
|
|
297
|
+
| `name` | `string` | no | Customer-facing name shown on the payment page |
|
|
298
|
+
| `currency` | `string` | no | Defaults to `'VES'` |
|
|
299
|
+
| `isUsd` | `boolean` | no | Whether the amount is in USD |
|
|
300
|
+
| `redirectUrl` | `string` | no | URL to redirect after payment |
|
|
301
|
+
| `webhookUrl` | `string` | no | URL to receive payment event webhooks |
|
|
302
|
+
| `notificationByWhatsapp` | `boolean` | no | Send WhatsApp notification on payment |
|
|
303
|
+
| `metadata` | `Record<string, unknown>` | no | Arbitrary key/value data |
|
|
304
|
+
|
|
305
|
+
**Returns:** `PaymentLink`
|
|
115
306
|
|
|
116
307
|
```typescript
|
|
117
|
-
|
|
118
|
-
|
|
308
|
+
interface PaymentLink {
|
|
309
|
+
id: string;
|
|
310
|
+
url: string;
|
|
311
|
+
amount?: number;
|
|
312
|
+
status?: PaymentLinkStatus;
|
|
313
|
+
statusDetail?: string;
|
|
314
|
+
type?: PaymentLinkType;
|
|
315
|
+
userId?: string;
|
|
316
|
+
userBankId?: string;
|
|
317
|
+
withSubscriptionId?: string;
|
|
318
|
+
name?: string;
|
|
319
|
+
description?: string;
|
|
320
|
+
isUsd?: boolean;
|
|
321
|
+
redirectUrl?: string;
|
|
322
|
+
webhookUrl?: string;
|
|
323
|
+
webhookMethod?: string;
|
|
324
|
+
notificationByWhatsapp?: boolean;
|
|
325
|
+
expirationTime?: number;
|
|
326
|
+
paymentLinkOrigin?: PaymentLinkOrigin;
|
|
327
|
+
createdAt?: string;
|
|
328
|
+
updatedAt?: string;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
type PaymentLinkStatus = 'pending' | 'active' | 'paid' | 'failed' | 'canceled' | 'expired' | 'stopped' | string;
|
|
332
|
+
type PaymentLinkType = 'default' | 'fixed' | 'subscription' | 'donation' | string;
|
|
333
|
+
type PaymentLinkOrigin = 'pabilo' | 'api' | string;
|
|
119
334
|
```
|
|
120
335
|
|
|
121
|
-
|
|
336
|
+
---
|
|
337
|
+
|
|
338
|
+
### `paymentLinks.getInfo(id): Promise<PaymentLink>`
|
|
339
|
+
|
|
340
|
+
Fetches current state of a payment link.
|
|
122
341
|
|
|
123
342
|
```typescript
|
|
124
|
-
const
|
|
343
|
+
const link = await pabilo.paymentLinks.getInfo('69d0083b691af50b78e07921');
|
|
344
|
+
|
|
345
|
+
console.log(link.status); // 'paid'
|
|
346
|
+
console.log(link.statusDetail); // string — reason for current status
|
|
347
|
+
console.log(link.amount);
|
|
348
|
+
console.log(link.createdAt);
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
---
|
|
352
|
+
|
|
353
|
+
### `paymentLinks.isPaid(id): Promise<boolean>`
|
|
354
|
+
|
|
355
|
+
Returns `true` if the payment link's status is `'paid'`.
|
|
356
|
+
|
|
357
|
+
```typescript
|
|
358
|
+
const paid = await pabilo.paymentLinks.isPaid(link.id);
|
|
359
|
+
|
|
125
360
|
if (paid) {
|
|
126
361
|
// fulfill the order
|
|
127
362
|
}
|
|
128
363
|
```
|
|
129
364
|
|
|
130
|
-
|
|
365
|
+
---
|
|
366
|
+
|
|
367
|
+
### `paymentLinks.update(id, req): Promise<PaymentLink>`
|
|
368
|
+
|
|
369
|
+
Updates a payment link's amount or description.
|
|
131
370
|
|
|
132
371
|
```typescript
|
|
133
|
-
const updated = await pabilo.paymentLinks.update(
|
|
372
|
+
const updated = await pabilo.paymentLinks.update(link.id, {
|
|
134
373
|
amount: 5000,
|
|
135
374
|
description: 'Orden actualizada',
|
|
136
375
|
});
|
|
@@ -138,29 +377,51 @@ const updated = await pabilo.paymentLinks.update(linkId, {
|
|
|
138
377
|
|
|
139
378
|
---
|
|
140
379
|
|
|
141
|
-
##
|
|
380
|
+
## `pabilo.payments`
|
|
142
381
|
|
|
143
|
-
|
|
382
|
+
### `payments.verify(userBankId, req): Promise<VerifyPaymentResult>`
|
|
383
|
+
|
|
384
|
+
Checks whether a specific bank transfer exists in a connected bank account.
|
|
144
385
|
|
|
145
386
|
```typescript
|
|
146
|
-
const result = await pabilo.payments.verify(
|
|
387
|
+
const result = await pabilo.payments.verify(bank.id, {
|
|
147
388
|
amount: 4000,
|
|
148
389
|
bankReference: '12345678',
|
|
149
|
-
movementType: 'GENERIC', //
|
|
390
|
+
movementType: 'GENERIC', // 'GENERIC' | 'MOVIL_PAY' | 'TRANSFER' | 'C2P'
|
|
150
391
|
});
|
|
151
392
|
|
|
152
393
|
if (result.found) {
|
|
153
|
-
console.log(
|
|
154
|
-
console.log(result.isNew); // true if first time seen
|
|
394
|
+
console.log(result.isNew);
|
|
155
395
|
console.log(result.data.credit_cost);
|
|
156
|
-
|
|
396
|
+
|
|
397
|
+
const payment = result.data.user_bank_payment;
|
|
398
|
+
if (payment) {
|
|
399
|
+
console.log(payment.id);
|
|
400
|
+
console.log(payment.amount);
|
|
401
|
+
console.log(payment.status); // 'pending' | 'paid' | 'failed'
|
|
402
|
+
console.log(payment.bank_reference_id);
|
|
403
|
+
console.log(payment.payment_params.fecha_pago);
|
|
404
|
+
console.log(payment.payment_params.banco_origen);
|
|
405
|
+
console.log(payment.payment_params.telefono_pagador);
|
|
406
|
+
}
|
|
157
407
|
} else {
|
|
158
408
|
// result.reason → 'BANK_NOT_AVAILABLE' | 'PAYMENT_NOT_FOUND'
|
|
159
|
-
|
|
409
|
+
if (result.reason === 'BANK_NOT_AVAILABLE') {
|
|
410
|
+
// bank is offline or credentials expired
|
|
411
|
+
}
|
|
160
412
|
}
|
|
161
413
|
```
|
|
162
414
|
|
|
163
|
-
|
|
415
|
+
**`movementType` values:**
|
|
416
|
+
|
|
417
|
+
| Value | Description |
|
|
418
|
+
|---|---|
|
|
419
|
+
| `'GENERIC'` | Default — any movement type (default) |
|
|
420
|
+
| `'MOVIL_PAY'` | Pago Móvil |
|
|
421
|
+
| `'TRANSFER'` | Bank transfer |
|
|
422
|
+
| `'C2P'` | Cuenta a Persona (C2P) |
|
|
423
|
+
|
|
424
|
+
**Returns:** `VerifyPaymentResult` — a discriminated union on `found`:
|
|
164
425
|
|
|
165
426
|
```typescript
|
|
166
427
|
type VerifyPaymentResult =
|
|
@@ -168,49 +429,123 @@ type VerifyPaymentResult =
|
|
|
168
429
|
| { found: true; isNew: boolean; data: PaymentData };
|
|
169
430
|
```
|
|
170
431
|
|
|
432
|
+
> The API returns HTTP 200 for `BANK_NOT_AVAILABLE` and `PAYMENT_NOT_FOUND`. The SDK handles this transparently — these cases are never thrown as errors.
|
|
433
|
+
|
|
434
|
+
---
|
|
435
|
+
|
|
436
|
+
## Webhooks
|
|
437
|
+
|
|
438
|
+
When a payment link is paid, the API sends a `POST` to the `webhookUrl` with the following payload. Import `PaymentLinkWebhookPayload` to type your webhook handler:
|
|
439
|
+
|
|
440
|
+
```typescript
|
|
441
|
+
import type { PaymentLinkWebhookPayload } from '@pabilo/sdk';
|
|
442
|
+
|
|
443
|
+
// Next.js App Router
|
|
444
|
+
export async function POST(req: Request) {
|
|
445
|
+
const payload: PaymentLinkWebhookPayload = await req.json();
|
|
446
|
+
|
|
447
|
+
console.log(payload.payment_link_id);
|
|
448
|
+
console.log(payload.status); // 'paid'
|
|
449
|
+
console.log(payload.credit_balance);
|
|
450
|
+
|
|
451
|
+
const payment = payload.user_bank_payment;
|
|
452
|
+
if (payment) {
|
|
453
|
+
console.log(payment.amount);
|
|
454
|
+
console.log(payment.bank_reference_id);
|
|
455
|
+
console.log(payment.payment_params.fecha_pago);
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
return Response.json({ ok: true });
|
|
459
|
+
}
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
**`PaymentLinkWebhookPayload` shape:**
|
|
463
|
+
|
|
464
|
+
```typescript
|
|
465
|
+
interface PaymentLinkWebhookPayload {
|
|
466
|
+
id: string;
|
|
467
|
+
created_at: string;
|
|
468
|
+
updated_at: string;
|
|
469
|
+
payment_link_id: string;
|
|
470
|
+
status: PaymentLinkStatus;
|
|
471
|
+
payment_link?: PaymentLink;
|
|
472
|
+
user_bank_payment?: UserBankPayment;
|
|
473
|
+
credit_balance: number;
|
|
474
|
+
metadata: Array<{ key: string; value: string }>;
|
|
475
|
+
}
|
|
476
|
+
```
|
|
477
|
+
|
|
171
478
|
---
|
|
172
479
|
|
|
173
480
|
## Error handling
|
|
174
481
|
|
|
175
|
-
|
|
482
|
+
All methods throw `PabiloError` on API errors or network failures.
|
|
176
483
|
|
|
177
484
|
```typescript
|
|
178
485
|
import { PabiloClient, PabiloError } from '@pabilo/sdk';
|
|
179
486
|
|
|
180
487
|
try {
|
|
181
|
-
await pabilo.paymentLinks.create({ ... });
|
|
488
|
+
const link = await pabilo.paymentLinks.create({ ... });
|
|
182
489
|
} catch (err) {
|
|
183
490
|
if (err instanceof PabiloError) {
|
|
184
|
-
console.error(err.code);
|
|
185
|
-
console.error(err.message);
|
|
186
|
-
console.error(err.
|
|
491
|
+
console.error(err.code); // PabiloErrorCode string
|
|
492
|
+
console.error(err.message); // human-readable message from the API
|
|
493
|
+
console.error(err.statusCode); // HTTP status code
|
|
494
|
+
console.error(err.raw); // raw API response body
|
|
187
495
|
}
|
|
188
496
|
}
|
|
189
497
|
```
|
|
190
498
|
|
|
191
|
-
Common error codes
|
|
499
|
+
**Common error codes:**
|
|
500
|
+
|
|
501
|
+
| Code | Description |
|
|
502
|
+
|---|---|
|
|
503
|
+
| `BAD_REQUEST` | Invalid request body or parameters |
|
|
504
|
+
| `UNAUTHORIZED` | API key is missing or invalid |
|
|
505
|
+
| `FORBIDDEN` | Action not permitted |
|
|
506
|
+
| `NOT_FOUND` | Resource not found |
|
|
507
|
+
| `USER_BANK_ALREADY_EXISTS` | Bank account is already connected |
|
|
508
|
+
| `USER_BANCK_BAD_PASSWORD` | Wrong bank portal credentials |
|
|
509
|
+
| `USER_BANCK_PASSWORD_EXPIRED` | Bank portal password expired |
|
|
510
|
+
| `NOT_ENOUGH_CREDITS` | Insufficient credits in the account |
|
|
511
|
+
| `PLAN_IS_NOT_ACTIVE` | Account plan is inactive |
|
|
512
|
+
| `REQUEST_LIMIT_REACHED` | Plan request limit exceeded |
|
|
513
|
+
| `BANK_ACCOUNT_LIMIT_REACHED` | Plan bank account limit exceeded |
|
|
514
|
+
| `BANK_NOT_AVAILABLE` | Bank is temporarily offline |
|
|
515
|
+
| `BANK_TEMPORARILY_INACTIVE` | Bank is temporarily disabled |
|
|
516
|
+
| `BANK_TOO_MANY_REQUESTS` | Rate limit from the bank |
|
|
517
|
+
| `PAYMENT_NOT_FOUND` | Transfer reference not found |
|
|
518
|
+
| `PAYMENT_ALREADY_EXISTS` | Transfer already verified |
|
|
519
|
+
| `INTERNAL_ERROR` | API server error |
|
|
520
|
+
| `NETWORK_ERROR` | Request could not reach the API |
|
|
192
521
|
|
|
193
522
|
---
|
|
194
523
|
|
|
195
|
-
##
|
|
524
|
+
## Testing with BANK_TEST
|
|
196
525
|
|
|
197
|
-
|
|
526
|
+
Use `BANK_TEST` to test the full payment flow without connecting a real bank. Reference `67890` always returns a found payment.
|
|
198
527
|
|
|
199
528
|
```typescript
|
|
200
|
-
|
|
529
|
+
// 1. Create test bank
|
|
530
|
+
const bank = await pabilo.bankAccounts.create({ bankProvider: 'BANK_TEST' });
|
|
201
531
|
|
|
202
|
-
//
|
|
203
|
-
const
|
|
532
|
+
// 2. Create a payment link
|
|
533
|
+
const link = await pabilo.paymentLinks.create({
|
|
534
|
+
amount: 100,
|
|
535
|
+
description: 'Test payment',
|
|
536
|
+
userBankId: bank.id,
|
|
537
|
+
webhookUrl: 'https://myapp.com/webhook',
|
|
538
|
+
});
|
|
204
539
|
|
|
205
|
-
//
|
|
206
|
-
const
|
|
207
|
-
|
|
208
|
-
|
|
540
|
+
// 3. Verify the test payment
|
|
541
|
+
const result = await pabilo.payments.verify(bank.id, {
|
|
542
|
+
amount: 0,
|
|
543
|
+
bankReference: '67890',
|
|
209
544
|
});
|
|
545
|
+
// result.found === true
|
|
210
546
|
|
|
211
|
-
//
|
|
212
|
-
|
|
213
|
-
const pabilo = new PabiloClient({ apiKey: 'YOUR_API_KEY', httpClient: new MyHttpClient() });
|
|
547
|
+
// 4. Clean up
|
|
548
|
+
await pabilo.bankAccounts.delete(bank.id);
|
|
214
549
|
```
|
|
215
550
|
|
|
216
551
|
---
|
|
@@ -229,20 +564,73 @@ export const pabilo = new PabiloClient({
|
|
|
229
564
|
```typescript
|
|
230
565
|
// app/api/checkout/route.ts
|
|
231
566
|
import { pabilo } from '@/lib/pabilo';
|
|
567
|
+
import { PabiloError } from '@pabilo/sdk';
|
|
232
568
|
|
|
233
569
|
export async function POST(req: Request) {
|
|
234
570
|
const { bankId, amount } = await req.json();
|
|
235
571
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
572
|
+
try {
|
|
573
|
+
const link = await pabilo.paymentLinks.create({
|
|
574
|
+
amount,
|
|
575
|
+
description: 'Checkout',
|
|
576
|
+
userBankId: bankId,
|
|
577
|
+
redirectUrl: `${process.env.NEXT_PUBLIC_URL}/gracias`,
|
|
578
|
+
webhookUrl: `${process.env.NEXT_PUBLIC_URL}/api/webhook/pabilo`,
|
|
579
|
+
});
|
|
580
|
+
|
|
581
|
+
return Response.json({ url: link.url, id: link.id });
|
|
582
|
+
} catch (err) {
|
|
583
|
+
if (err instanceof PabiloError) {
|
|
584
|
+
return Response.json({ error: err.message }, { status: 400 });
|
|
585
|
+
}
|
|
586
|
+
throw err;
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
```
|
|
590
|
+
|
|
591
|
+
```typescript
|
|
592
|
+
// app/api/webhook/pabilo/route.ts
|
|
593
|
+
import type { PaymentLinkWebhookPayload } from '@pabilo/sdk';
|
|
594
|
+
import { pabilo } from '@/lib/pabilo';
|
|
595
|
+
|
|
596
|
+
export async function POST(req: Request) {
|
|
597
|
+
const payload: PaymentLinkWebhookPayload = await req.json();
|
|
598
|
+
|
|
599
|
+
if (payload.status === 'paid') {
|
|
600
|
+
// fulfill the order linked to payload.payment_link_id
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
return Response.json({ ok: true });
|
|
604
|
+
}
|
|
605
|
+
```
|
|
606
|
+
|
|
607
|
+
---
|
|
608
|
+
|
|
609
|
+
## Advanced: custom HTTP client
|
|
610
|
+
|
|
611
|
+
```typescript
|
|
612
|
+
import { PabiloClient, FetchHttpClient, type RequestOptions } from '@pabilo/sdk';
|
|
243
613
|
|
|
244
|
-
|
|
614
|
+
class LoggingHttpClient extends FetchHttpClient {
|
|
615
|
+
async request<T>(opts: RequestOptions): Promise<T> {
|
|
616
|
+
console.log('[pabilo]', opts.method, opts.path);
|
|
617
|
+
return super.request<T>(opts);
|
|
618
|
+
}
|
|
245
619
|
}
|
|
620
|
+
|
|
621
|
+
const pabilo = new PabiloClient({
|
|
622
|
+
apiKey: 'YOUR_API_KEY',
|
|
623
|
+
httpClient: new LoggingHttpClient(),
|
|
624
|
+
});
|
|
625
|
+
```
|
|
626
|
+
|
|
627
|
+
Custom base URL:
|
|
628
|
+
|
|
629
|
+
```typescript
|
|
630
|
+
const pabilo = new PabiloClient({
|
|
631
|
+
apiKey: 'YOUR_API_KEY',
|
|
632
|
+
baseUrl: 'https://staging.api.pabilo.app',
|
|
633
|
+
});
|
|
246
634
|
```
|
|
247
635
|
|
|
248
636
|
---
|
|
@@ -251,19 +639,56 @@ export async function POST(req: Request) {
|
|
|
251
639
|
|
|
252
640
|
```typescript
|
|
253
641
|
import type {
|
|
642
|
+
// Client
|
|
643
|
+
PabiloClientOptions,
|
|
644
|
+
|
|
645
|
+
// Resources
|
|
646
|
+
User,
|
|
647
|
+
Plan,
|
|
648
|
+
PlanBenefit,
|
|
254
649
|
UserBank,
|
|
255
650
|
BankAccountEntry,
|
|
256
651
|
PaymentLink,
|
|
257
|
-
|
|
258
|
-
PaymentLinkType, // 'default' | 'fixed'
|
|
259
|
-
VerifyPaymentResult,
|
|
652
|
+
PaymentLinksPage,
|
|
260
653
|
PaymentData,
|
|
261
654
|
UserBankPayment,
|
|
655
|
+
PaymentParams,
|
|
656
|
+
|
|
657
|
+
// Requests
|
|
262
658
|
CreateUserBankRequest,
|
|
263
659
|
CreateVeBanRequest,
|
|
264
660
|
CreateVeBanEmpV2Request,
|
|
265
661
|
CreateBankTestRequest,
|
|
662
|
+
UserBankMetadataEntry,
|
|
663
|
+
CreatePaymentLinkRequest,
|
|
664
|
+
UpdatePaymentLinkRequest,
|
|
665
|
+
ListPaymentLinksRequest,
|
|
666
|
+
VerifyPaymentRequest,
|
|
667
|
+
|
|
668
|
+
// Results & Webhooks
|
|
669
|
+
VerifyPaymentResult,
|
|
670
|
+
PaymentLinkWebhookPayload,
|
|
671
|
+
|
|
672
|
+
// Literals / enums
|
|
673
|
+
PaymentLinkStatus, // 'pending' | 'active' | 'paid' | 'failed' | 'canceled' | 'expired' | 'stopped'
|
|
674
|
+
PaymentLinkType, // 'default' | 'fixed' | 'subscription' | 'donation'
|
|
675
|
+
PaymentLinkOrigin, // 'pabilo' | 'api'
|
|
676
|
+
BankProvider, // 'VE_BAN' | 'VE_BAN_EMP_V2' | ...
|
|
677
|
+
MovementType, // 'GENERIC' | 'MOVIL_PAY' | 'TRANSFER' | 'C2P'
|
|
678
|
+
UserBankPaymentStatus,// 'pending' | 'paid' | 'failed'
|
|
679
|
+
UserType, // 'user' | 'admin' | 'commerce' | ...
|
|
680
|
+
PlanType, // 'credit' | 'unlimited' | 'counter'
|
|
681
|
+
PlanPeriod, // 'month' | 'six_months' | 'year'
|
|
682
|
+
AccountType,
|
|
266
683
|
PabiloErrorCode,
|
|
684
|
+
|
|
685
|
+
// Ports (for DI / testing)
|
|
686
|
+
IHttpClient,
|
|
687
|
+
RequestOptions,
|
|
688
|
+
IBankAccountsPort,
|
|
689
|
+
IPaymentLinksPort,
|
|
690
|
+
IPaymentsPort,
|
|
691
|
+
IMePort,
|
|
267
692
|
} from '@pabilo/sdk';
|
|
268
693
|
```
|
|
269
694
|
|