@bloque/payments 0.0.9 → 0.0.11
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 +134 -584
- package/dist/index.cjs +13 -12
- package/dist/index.js +13 -12
- package/dist/types/checkout.d.ts +15 -13
- package/dist/types/common.d.ts +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,14 +1,6 @@
|
|
|
1
1
|
# Bloque Payments SDK
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
## Features
|
|
6
|
-
|
|
7
|
-
- **TypeScript First**: Built with TypeScript for complete type safety
|
|
8
|
-
- **Simple API**: Intuitive interface for creating and managing checkouts
|
|
9
|
-
- **Multiple Payment Methods**: Support for cards, PSE, and cash payments
|
|
10
|
-
- **Fully Async**: Promise-based API for modern JavaScript workflows
|
|
11
|
-
- **Lightweight**: Minimal dependencies for optimal bundle size
|
|
3
|
+
Official TypeScript/JavaScript SDK for integrating Bloque payments.
|
|
12
4
|
|
|
13
5
|
## Installation
|
|
14
6
|
|
|
@@ -16,630 +8,188 @@ The official TypeScript/JavaScript SDK for integrating [Bloque](https://www.bloq
|
|
|
16
8
|
bun add @bloque/payments
|
|
17
9
|
```
|
|
18
10
|
|
|
19
|
-
##
|
|
20
|
-
|
|
21
|
-
```typescript
|
|
22
|
-
import { Bloque, type PaymentSubmitPayload } from '@bloque/payments';
|
|
23
|
-
|
|
24
|
-
// Initialize the SDK (server-side only)
|
|
25
|
-
const bloque = new Bloque({
|
|
26
|
-
apiKey: process.env.BLOQUE_API_KEY!,
|
|
27
|
-
mode: 'production', // or 'sandbox' for testing
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
app.post('/api/payments', async (req, res) => {
|
|
31
|
-
try {
|
|
32
|
-
const payload: PaymentSubmitPayload = req.body;
|
|
33
|
-
|
|
34
|
-
// Create payment using SDK
|
|
35
|
-
const payment = await bloque.payments.create({
|
|
36
|
-
payment: payload,
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
res.json({ success: true, payment });
|
|
40
|
-
} catch (error) {
|
|
41
|
-
res.status(500).json({ success: false, error: error.message });
|
|
42
|
-
}
|
|
43
|
-
});
|
|
44
|
-
```
|
|
11
|
+
## Initialize
|
|
45
12
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
### Initialize the SDK
|
|
49
|
-
|
|
50
|
-
```typescript
|
|
13
|
+
```ts
|
|
51
14
|
import { Bloque } from '@bloque/payments';
|
|
52
15
|
|
|
53
16
|
const bloque = new Bloque({
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
### Environment Options
|
|
60
|
-
|
|
61
|
-
- **`sandbox`**: For testing and development
|
|
62
|
-
- **`production`**: For live payments
|
|
63
|
-
|
|
64
|
-
## API Reference
|
|
65
|
-
|
|
66
|
-
### Payments
|
|
67
|
-
|
|
68
|
-
The payments resource allows you to create payments for existing checkout sessions.
|
|
69
|
-
|
|
70
|
-
#### Create a Payment
|
|
71
|
-
|
|
72
|
-
```typescript
|
|
73
|
-
const payment = await bloque.payments.create({
|
|
74
|
-
checkoutId: string; // Required: Checkout ID from an existing checkout
|
|
75
|
-
payment: {
|
|
76
|
-
type: 'card' | 'pse' | 'cash',
|
|
77
|
-
data: {
|
|
78
|
-
// Payment method specific data
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
});
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
**Payment Types**:
|
|
85
|
-
|
|
86
|
-
**Card Payment**:
|
|
87
|
-
```typescript
|
|
88
|
-
{
|
|
89
|
-
type: 'card',
|
|
90
|
-
data: {
|
|
91
|
-
cardNumber: string; // 16-digit card number
|
|
92
|
-
cardholderName: string; // Name on the card
|
|
93
|
-
expiryMonth: string; // MM format
|
|
94
|
-
expiryYear: string; // YY or YYYY format
|
|
95
|
-
cvv: string; // 3-4 digit CVV
|
|
96
|
-
email?: string; // Optional customer email
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
**PSE Payment** (Colombian online banking):
|
|
102
|
-
```typescript
|
|
103
|
-
{
|
|
104
|
-
type: 'pse',
|
|
105
|
-
data: {
|
|
106
|
-
personType: 'natural' | 'juridica'; // Person type
|
|
107
|
-
documentType: string; // Document type (e.g., 'CC', 'NIT')
|
|
108
|
-
documentNumber: string; // Document number
|
|
109
|
-
bankCode: string; // Bank code
|
|
110
|
-
email: string; // Customer email
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
**Cash Payment**:
|
|
116
|
-
```typescript
|
|
117
|
-
{
|
|
118
|
-
type: 'cash',
|
|
119
|
-
data: {
|
|
120
|
-
email: string; // Customer email
|
|
121
|
-
documentType: string; // Document type
|
|
122
|
-
documentNumber: string; // Document number
|
|
123
|
-
fullName: string; // Full name
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
**Payment Response**:
|
|
129
|
-
```typescript
|
|
130
|
-
{
|
|
131
|
-
id: string; // Payment ID
|
|
132
|
-
object: 'payment'; // Object type
|
|
133
|
-
status: 'pending' | 'processing' | 'completed' | 'failed';
|
|
134
|
-
checkout: Checkout; // Associated checkout
|
|
135
|
-
payment_method: 'card' | 'pse' | 'cash';
|
|
136
|
-
amount: number; // Payment amount
|
|
137
|
-
currency: string; // Currency
|
|
138
|
-
created_at: string; // Creation timestamp
|
|
139
|
-
updated_at: string; // Last update timestamp
|
|
140
|
-
}
|
|
141
|
-
```
|
|
142
|
-
|
|
143
|
-
### Checkout
|
|
144
|
-
|
|
145
|
-
The checkout resource allows you to create payment sessions.
|
|
146
|
-
|
|
147
|
-
#### Create a Checkout
|
|
148
|
-
|
|
149
|
-
```typescript
|
|
150
|
-
const checkout = await bloque.checkout.create({
|
|
151
|
-
name: string; // Required: Name of the checkout
|
|
152
|
-
description?: string; // Optional: Description
|
|
153
|
-
image_url?: string; // Optional: Product/checkout image URL
|
|
154
|
-
items: CheckoutItem[]; // Required: Items to be purchased
|
|
155
|
-
success_url: string; // Required: Redirect URL after success
|
|
156
|
-
cancel_url: string; // Required: Redirect URL after cancellation
|
|
157
|
-
metadata?: Record<string, string | number | boolean>; // Optional: Custom metadata
|
|
158
|
-
expires_at?: string; // Optional: Expiration date (ISO 8601)
|
|
159
|
-
});
|
|
160
|
-
```
|
|
161
|
-
|
|
162
|
-
**Checkout Item**:
|
|
163
|
-
```typescript
|
|
164
|
-
{
|
|
165
|
-
name: string; // Item name
|
|
166
|
-
amount: number; // Price in smallest currency unit (e.g., cents)
|
|
167
|
-
quantity: number; // Quantity
|
|
168
|
-
image_url?: string; // Item image URL
|
|
169
|
-
}
|
|
170
|
-
```
|
|
171
|
-
|
|
172
|
-
#### Checkout Response
|
|
173
|
-
|
|
174
|
-
```typescript
|
|
175
|
-
{
|
|
176
|
-
id: string; // Unique checkout identifier
|
|
177
|
-
object: 'checkout'; // Object type
|
|
178
|
-
url: string; // Public payment URL for the customer
|
|
179
|
-
status: string; // Current checkout status
|
|
180
|
-
amount_total: number; // Total amount
|
|
181
|
-
amount_subtotal: number; // Subtotal amount
|
|
182
|
-
currency: 'USD'; // Currency
|
|
183
|
-
items: CheckoutItem[]; // Items in the checkout
|
|
184
|
-
metadata?: Metadata; // Custom metadata
|
|
185
|
-
created_at: string; // Creation timestamp (ISO 8601)
|
|
186
|
-
updated_at: string; // Last update timestamp (ISO 8601)
|
|
187
|
-
expires_at: string; // Expiration timestamp (ISO 8601)
|
|
188
|
-
}
|
|
189
|
-
```
|
|
190
|
-
|
|
191
|
-
#### Retrieve a Checkout
|
|
192
|
-
|
|
193
|
-
Retrieve the details of an existing checkout by its ID.
|
|
194
|
-
|
|
195
|
-
```typescript
|
|
196
|
-
const checkout = await bloque.checkout.retrieve('checkout_id_here');
|
|
197
|
-
```
|
|
198
|
-
|
|
199
|
-
**Parameters**:
|
|
200
|
-
- `checkoutId` (string): The ID of the checkout to retrieve
|
|
201
|
-
|
|
202
|
-
**Returns**: A `Checkout` object with all the checkout details.
|
|
203
|
-
|
|
204
|
-
#### Cancel a Checkout
|
|
205
|
-
|
|
206
|
-
Cancel an existing checkout that hasn't been completed yet.
|
|
207
|
-
|
|
208
|
-
```typescript
|
|
209
|
-
const checkout = await bloque.checkout.cancel('checkout_id_here');
|
|
210
|
-
```
|
|
211
|
-
|
|
212
|
-
**Parameters**:
|
|
213
|
-
- `checkoutId` (string): The ID of the checkout to cancel
|
|
214
|
-
|
|
215
|
-
**Returns**: A `Checkout` object with updated status reflecting the cancellation.
|
|
216
|
-
|
|
217
|
-
### Webhooks
|
|
218
|
-
|
|
219
|
-
The webhooks resource allows you to verify the authenticity of webhook events sent from Bloque.
|
|
220
|
-
|
|
221
|
-
#### Verify Webhook Signature
|
|
222
|
-
|
|
223
|
-
Verify that a webhook event was actually sent by Bloque using HMAC-SHA256 signature verification.
|
|
224
|
-
|
|
225
|
-
```typescript
|
|
226
|
-
const isValid = bloque.webhooks.verify(
|
|
227
|
-
requestBody,
|
|
228
|
-
signatureHeader,
|
|
229
|
-
{ secret: process.env.WEBHOOK_SECRET }
|
|
230
|
-
);
|
|
231
|
-
|
|
232
|
-
if (isValid) {
|
|
233
|
-
// Process the webhook event
|
|
234
|
-
} else {
|
|
235
|
-
// Reject the webhook
|
|
236
|
-
}
|
|
237
|
-
```
|
|
238
|
-
|
|
239
|
-
**Parameters**:
|
|
240
|
-
- `body` (string | object): The raw webhook request body
|
|
241
|
-
- `signature` (string): The signature from the `x-bloque-signature` header
|
|
242
|
-
- `options` (optional): Verification options
|
|
243
|
-
- `secret` (string): Webhook secret key. Can be set during SDK initialization or passed here
|
|
244
|
-
|
|
245
|
-
**Returns**: `boolean` - `true` if the webhook signature is valid, `false` otherwise
|
|
246
|
-
|
|
247
|
-
**Configuration**:
|
|
248
|
-
|
|
249
|
-
You can set the webhook secret during SDK initialization:
|
|
250
|
-
|
|
251
|
-
```typescript
|
|
252
|
-
const bloque = new Bloque({
|
|
253
|
-
apiKey: process.env.BLOQUE_API_KEY!,
|
|
254
|
-
mode: 'production',
|
|
255
|
-
webhookSecret: process.env.BLOQUE_WEBHOOK_SECRET,
|
|
256
|
-
});
|
|
257
|
-
|
|
258
|
-
// Then you can verify without passing the secret each time
|
|
259
|
-
const isValid = bloque.webhooks.verify(body, signature);
|
|
260
|
-
```
|
|
261
|
-
|
|
262
|
-
Or pass it directly during verification:
|
|
263
|
-
|
|
264
|
-
```typescript
|
|
265
|
-
const isValid = bloque.webhooks.verify(body, signature, {
|
|
266
|
-
secret: process.env.BLOQUE_WEBHOOK_SECRET,
|
|
17
|
+
mode: 'sandbox', // 'sandbox' | 'production'
|
|
18
|
+
accessToken: process.env.BLOQUE_ACCESS_TOKEN!,
|
|
19
|
+
webhookSecret: process.env.BLOQUE_WEBHOOK_SECRET, // optional
|
|
267
20
|
});
|
|
268
21
|
```
|
|
269
22
|
|
|
270
|
-
##
|
|
23
|
+
## Quick Start: Create Checkout
|
|
271
24
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
```typescript
|
|
275
|
-
import { Bloque, type PaymentSubmitPayload } from '@bloque/payments';
|
|
276
|
-
|
|
277
|
-
// Initialize SDK with your API key
|
|
278
|
-
const bloque = new Bloque({
|
|
279
|
-
apiKey: process.env.BLOQUE_API_KEY!,
|
|
280
|
-
mode: 'production',
|
|
281
|
-
});
|
|
282
|
-
|
|
283
|
-
// API endpoint handler
|
|
284
|
-
app.post('/api/payments', async (req, res) => {
|
|
285
|
-
try {
|
|
286
|
-
// Receive payment data from frontend
|
|
287
|
-
const payload: PaymentSubmitPayload = req.body;
|
|
288
|
-
|
|
289
|
-
// Create payment using SDK
|
|
290
|
-
const payment = await bloque.payments.create({
|
|
291
|
-
payment: payload,
|
|
292
|
-
});
|
|
293
|
-
|
|
294
|
-
res.json({ success: true, payment });
|
|
295
|
-
} catch (error) {
|
|
296
|
-
res.status(500).json({ success: false, error: error.message });
|
|
297
|
-
}
|
|
298
|
-
});
|
|
299
|
-
```
|
|
300
|
-
|
|
301
|
-
### Payment Methods Examples
|
|
302
|
-
|
|
303
|
-
#### Card Payment
|
|
304
|
-
|
|
305
|
-
```typescript
|
|
306
|
-
// Receive payload from frontend
|
|
307
|
-
const cardPayment: PaymentSubmitPayload = req.body;
|
|
308
|
-
// Example payload:
|
|
309
|
-
// {
|
|
310
|
-
// type: 'card',
|
|
311
|
-
// data: {
|
|
312
|
-
// cardNumber: '4111111111111111',
|
|
313
|
-
// cardholderName: 'John Doe',
|
|
314
|
-
// expiryMonth: '12',
|
|
315
|
-
// expiryYear: '2025',
|
|
316
|
-
// cvv: '123',
|
|
317
|
-
// email: 'john@example.com'
|
|
318
|
-
// }
|
|
319
|
-
// }
|
|
320
|
-
|
|
321
|
-
const payment = await bloque.payments.create({
|
|
322
|
-
payment: cardPayment,
|
|
323
|
-
});
|
|
324
|
-
```
|
|
325
|
-
|
|
326
|
-
#### PSE Payment (Colombian online banking)
|
|
327
|
-
|
|
328
|
-
```typescript
|
|
329
|
-
// Receive payload from frontend
|
|
330
|
-
const psePayment: PaymentSubmitPayload = req.body;
|
|
331
|
-
// Example payload:
|
|
332
|
-
// {
|
|
333
|
-
// type: 'pse',
|
|
334
|
-
// data: {
|
|
335
|
-
// personType: 'natural',
|
|
336
|
-
// documentType: 'CC',
|
|
337
|
-
// documentNumber: '1234567890',
|
|
338
|
-
// bankCode: '1022',
|
|
339
|
-
// email: 'maria@example.com'
|
|
340
|
-
// }
|
|
341
|
-
// }
|
|
342
|
-
|
|
343
|
-
const payment = await bloque.payments.create({
|
|
344
|
-
payment: psePayment,
|
|
345
|
-
});
|
|
346
|
-
```
|
|
347
|
-
|
|
348
|
-
#### Cash Payment
|
|
349
|
-
|
|
350
|
-
```typescript
|
|
351
|
-
// Receive payload from frontend
|
|
352
|
-
const cashPayment: PaymentSubmitPayload = req.body;
|
|
353
|
-
// Example payload:
|
|
354
|
-
// {
|
|
355
|
-
// type: 'cash',
|
|
356
|
-
// data: {
|
|
357
|
-
// email: 'carlos@example.com',
|
|
358
|
-
// documentType: 'CC',
|
|
359
|
-
// documentNumber: '9876543210',
|
|
360
|
-
// fullName: 'Carlos García'
|
|
361
|
-
// }
|
|
362
|
-
// }
|
|
363
|
-
|
|
364
|
-
const payment = await bloque.payments.create({
|
|
365
|
-
payment: cashPayment,
|
|
366
|
-
});
|
|
367
|
-
```
|
|
368
|
-
|
|
369
|
-
### Type-Safe Payment Creation
|
|
370
|
-
|
|
371
|
-
The `PaymentSubmitPayload` type is a discriminated union that provides automatic type narrowing:
|
|
372
|
-
|
|
373
|
-
```typescript
|
|
374
|
-
// Backend API handler
|
|
375
|
-
async function handlePayment(payload: PaymentSubmitPayload) {
|
|
376
|
-
// TypeScript automatically narrows the type based on the 'type' field
|
|
377
|
-
switch (payload.type) {
|
|
378
|
-
case 'card':
|
|
379
|
-
// payload.data is CardPaymentFormData
|
|
380
|
-
console.log('Processing card ending in:', payload.data.cardNumber.slice(-4));
|
|
381
|
-
break;
|
|
382
|
-
|
|
383
|
-
case 'pse':
|
|
384
|
-
// payload.data is PSEPaymentFormData
|
|
385
|
-
console.log('Processing PSE for bank:', payload.data.bankCode);
|
|
386
|
-
break;
|
|
387
|
-
|
|
388
|
-
case 'cash':
|
|
389
|
-
// payload.data is CashPaymentFormData
|
|
390
|
-
console.log('Processing cash payment for:', payload.data.fullName);
|
|
391
|
-
break;
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
// Create payment using SDK
|
|
395
|
-
return await bloque.payments.create({
|
|
396
|
-
payment: payload,
|
|
397
|
-
});
|
|
398
|
-
}
|
|
399
|
-
```
|
|
400
|
-
|
|
401
|
-
### Webhook Handler
|
|
402
|
-
|
|
403
|
-
Handle incoming webhook events from Bloque and verify their authenticity:
|
|
404
|
-
|
|
405
|
-
```typescript
|
|
25
|
+
```ts
|
|
406
26
|
import { Bloque } from '@bloque/payments';
|
|
407
27
|
|
|
408
|
-
// Initialize SDK with webhook secret
|
|
409
28
|
const bloque = new Bloque({
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
webhookSecret: process.env.BLOQUE_WEBHOOK_SECRET,
|
|
29
|
+
mode: 'sandbox',
|
|
30
|
+
accessToken: process.env.BLOQUE_ACCESS_TOKEN!,
|
|
413
31
|
});
|
|
414
32
|
|
|
415
|
-
// Webhook endpoint
|
|
416
|
-
app.post(
|
|
417
|
-
'/webhooks/bloque',
|
|
418
|
-
(req, res) => {
|
|
419
|
-
const signature = req.headers['x-bloque-signature'] as string;
|
|
420
|
-
|
|
421
|
-
try {
|
|
422
|
-
// Verify the webhook signature
|
|
423
|
-
const isValid = bloque.webhooks.verify(req.body.toString(), signature);
|
|
424
|
-
|
|
425
|
-
if (!isValid) {
|
|
426
|
-
return res.status(400).send('Invalid signature');
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
// Parse and process the event
|
|
430
|
-
const event = JSON.parse(req.body.toString());
|
|
431
|
-
|
|
432
|
-
switch (event.type) {
|
|
433
|
-
case 'checkout.completed':
|
|
434
|
-
console.log('Checkout completed:', event.data);
|
|
435
|
-
// Update database, send confirmation, etc.
|
|
436
|
-
break;
|
|
437
|
-
|
|
438
|
-
case 'payment.succeeded':
|
|
439
|
-
console.log('Payment succeeded:', event.data);
|
|
440
|
-
break;
|
|
441
|
-
|
|
442
|
-
case 'payment.failed':
|
|
443
|
-
console.log('Payment failed:', event.data);
|
|
444
|
-
break;
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
res.json({ received: true });
|
|
448
|
-
} catch (error) {
|
|
449
|
-
console.error('Webhook error:', error);
|
|
450
|
-
res.status(400).send('Webhook error');
|
|
451
|
-
}
|
|
452
|
-
},
|
|
453
|
-
);
|
|
454
|
-
```
|
|
455
|
-
|
|
456
|
-
### Basic Checkout with Single Item
|
|
457
|
-
|
|
458
|
-
```typescript
|
|
459
33
|
const checkout = await bloque.checkout.create({
|
|
460
|
-
name: '
|
|
461
|
-
description:
|
|
34
|
+
name: 'Pack Profesional de Productividad',
|
|
35
|
+
description:
|
|
36
|
+
'Periféricos premium para trabajo intensivo y alto rendimiento',
|
|
37
|
+
asset: 'COPM/2',
|
|
38
|
+
image_url: 'https://cdn.bloque.app/checkouts/productivity-pack.png',
|
|
462
39
|
items: [
|
|
463
40
|
{
|
|
464
|
-
name: '
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
},
|
|
468
|
-
],
|
|
469
|
-
success_url: 'https://yourapp.com/success',
|
|
470
|
-
cancel_url: 'https://yourapp.com/cancel',
|
|
471
|
-
});
|
|
472
|
-
```
|
|
473
|
-
|
|
474
|
-
### Checkout with Multiple Items
|
|
475
|
-
|
|
476
|
-
```typescript
|
|
477
|
-
const checkout = await bloque.checkout.create({
|
|
478
|
-
name: 'Shopping Cart',
|
|
479
|
-
description: 'Your selected items',
|
|
480
|
-
items: [
|
|
481
|
-
{
|
|
482
|
-
name: 'Wireless Mouse',
|
|
483
|
-
amount: 25_00,
|
|
484
|
-
quantity: 1,
|
|
485
|
-
image_url: 'https://example.com/mouse.jpg',
|
|
486
|
-
},
|
|
487
|
-
{
|
|
488
|
-
name: 'USB Cable',
|
|
489
|
-
amount: 10_00,
|
|
41
|
+
name: 'Teclado mecánico Keychron K2',
|
|
42
|
+
description: 'Wireless, RGB, switches Blue',
|
|
43
|
+
amount: 450_000_00,
|
|
490
44
|
quantity: 2,
|
|
491
|
-
image_url: 'https://
|
|
45
|
+
image_url: 'https://cdn.bloque.app/items/keychron-k2.png',
|
|
492
46
|
},
|
|
493
|
-
],
|
|
494
|
-
success_url: 'https://yourapp.com/success',
|
|
495
|
-
cancel_url: 'https://yourapp.com/cancel',
|
|
496
|
-
});
|
|
497
|
-
```
|
|
498
|
-
|
|
499
|
-
### Checkout with Metadata and Expiration
|
|
500
|
-
|
|
501
|
-
```typescript
|
|
502
|
-
const checkout = await bloque.checkout.create({
|
|
503
|
-
name: 'Limited Time Offer',
|
|
504
|
-
items: [
|
|
505
47
|
{
|
|
506
|
-
name: '
|
|
507
|
-
|
|
48
|
+
name: 'Mouse Logitech MX Master 3S',
|
|
49
|
+
description: 'Inalámbrico, sensor 8K DPI',
|
|
50
|
+
amount: 380_000_00,
|
|
508
51
|
quantity: 1,
|
|
52
|
+
image_url: 'https://cdn.bloque.app/items/mx-master-3s.png',
|
|
509
53
|
},
|
|
510
54
|
],
|
|
511
|
-
success_url: 'https://
|
|
512
|
-
cancel_url: 'https://
|
|
513
|
-
metadata: {
|
|
514
|
-
user_id: '12345',
|
|
515
|
-
campaign: 'summer_sale',
|
|
516
|
-
discount_applied: true,
|
|
517
|
-
},
|
|
518
|
-
expires_at: new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString(), // 24 hours
|
|
55
|
+
success_url: 'https://bloque.app/success',
|
|
56
|
+
cancel_url: 'https://bloque.app/cancel',
|
|
519
57
|
});
|
|
520
|
-
```
|
|
521
58
|
|
|
59
|
+
console.log('Checkout URL:', checkout.url);
|
|
60
|
+
```
|
|
522
61
|
|
|
523
|
-
##
|
|
62
|
+
## API
|
|
524
63
|
|
|
525
|
-
|
|
64
|
+
### `Bloque` config
|
|
526
65
|
|
|
527
|
-
```
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
} catch (error) {
|
|
536
|
-
console.error('Failed to create checkout:', error);
|
|
537
|
-
}
|
|
66
|
+
```ts
|
|
67
|
+
type BloqueConfig = {
|
|
68
|
+
mode: 'sandbox' | 'production';
|
|
69
|
+
accessToken: string;
|
|
70
|
+
timeout?: number;
|
|
71
|
+
maxRetries?: number;
|
|
72
|
+
webhookSecret?: string;
|
|
73
|
+
};
|
|
538
74
|
```
|
|
539
75
|
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
This SDK is written in TypeScript and includes complete type definitions. You'll get full autocomplete and type checking when using TypeScript or modern editors like VS Code:
|
|
543
|
-
|
|
544
|
-
```typescript
|
|
545
|
-
import type {
|
|
546
|
-
// Payment types
|
|
547
|
-
PaymentSubmitPayload,
|
|
548
|
-
PaymentResponse,
|
|
549
|
-
CreatePaymentParams,
|
|
550
|
-
// Checkout types
|
|
551
|
-
Checkout,
|
|
552
|
-
CheckoutStatus,
|
|
553
|
-
CheckoutItem,
|
|
554
|
-
CheckoutParams,
|
|
555
|
-
// Webhook types
|
|
556
|
-
WebhookVerifyOptions,
|
|
557
|
-
} from '@bloque/payments';
|
|
558
|
-
|
|
559
|
-
const item: CheckoutItem = {
|
|
560
|
-
name: 'Product',
|
|
561
|
-
amount: 5000,
|
|
562
|
-
quantity: 1,
|
|
563
|
-
};
|
|
76
|
+
### Checkout
|
|
564
77
|
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
78
|
+
#### `bloque.checkout.create(params)`
|
|
79
|
+
|
|
80
|
+
```ts
|
|
81
|
+
type CheckoutParams = {
|
|
82
|
+
name: string;
|
|
83
|
+
description?: string;
|
|
84
|
+
image_url?: string;
|
|
85
|
+
asset?: 'COPM/2' | 'DUSD/6';
|
|
86
|
+
items: {
|
|
87
|
+
name: string;
|
|
88
|
+
description?: string;
|
|
89
|
+
amount: number;
|
|
90
|
+
quantity: number;
|
|
91
|
+
image_url?: string;
|
|
92
|
+
}[];
|
|
93
|
+
success_url: string;
|
|
94
|
+
cancel_url: string;
|
|
95
|
+
metadata?: Record<string, string | number | boolean>;
|
|
96
|
+
expires_at?: string;
|
|
575
97
|
};
|
|
576
98
|
```
|
|
577
99
|
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
100
|
+
Notes:
|
|
101
|
+
- `asset` defaults to `DUSD/6` when omitted.
|
|
102
|
+
- Creation request is sent using `redirect_url` derived from `success_url`.
|
|
103
|
+
|
|
104
|
+
Response shape:
|
|
105
|
+
|
|
106
|
+
```ts
|
|
107
|
+
type Checkout = {
|
|
108
|
+
id: string;
|
|
109
|
+
object: 'checkout';
|
|
110
|
+
url: string;
|
|
111
|
+
status: 'pending' | 'completed' | 'expired' | 'canceled';
|
|
112
|
+
amount_total: number;
|
|
113
|
+
amount_subtotal: number;
|
|
114
|
+
asset: 'COPM/2' | 'DUSD/6';
|
|
115
|
+
items: {
|
|
116
|
+
name: string;
|
|
117
|
+
description?: string;
|
|
118
|
+
amount: number;
|
|
119
|
+
quantity: number;
|
|
120
|
+
image_url?: string;
|
|
121
|
+
}[];
|
|
122
|
+
metadata?: Record<string, string | number | boolean>;
|
|
123
|
+
created_at: string;
|
|
124
|
+
updated_at: string;
|
|
125
|
+
expires_at: string | null;
|
|
126
|
+
};
|
|
602
127
|
```
|
|
603
128
|
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
### Building the SDK
|
|
129
|
+
#### `bloque.checkout.retrieve(checkoutId)`
|
|
607
130
|
|
|
608
|
-
```
|
|
609
|
-
|
|
610
|
-
bun run build
|
|
131
|
+
```ts
|
|
132
|
+
const checkout = await bloque.checkout.retrieve('checkout_123');
|
|
611
133
|
```
|
|
612
134
|
|
|
613
|
-
|
|
135
|
+
#### `bloque.checkout.cancel(checkoutId)`
|
|
614
136
|
|
|
615
|
-
```
|
|
616
|
-
|
|
137
|
+
```ts
|
|
138
|
+
const checkout = await bloque.checkout.cancel('checkout_123');
|
|
617
139
|
```
|
|
618
140
|
|
|
619
|
-
###
|
|
141
|
+
### Payments
|
|
620
142
|
|
|
621
|
-
|
|
622
|
-
bun run typecheck
|
|
623
|
-
```
|
|
143
|
+
#### `bloque.payments.create(params)`
|
|
624
144
|
|
|
625
|
-
|
|
145
|
+
Current supported submit payload is card:
|
|
626
146
|
|
|
627
|
-
```
|
|
628
|
-
|
|
147
|
+
```ts
|
|
148
|
+
const payment = await bloque.payments.create({
|
|
149
|
+
checkoutId: 'checkout_123',
|
|
150
|
+
payment: {
|
|
151
|
+
type: 'card',
|
|
152
|
+
data: {
|
|
153
|
+
cardNumber: '4111111111111111',
|
|
154
|
+
cardholderName: 'John Doe',
|
|
155
|
+
expiryMonth: '12',
|
|
156
|
+
expiryYear: '2028',
|
|
157
|
+
cvv: '123',
|
|
158
|
+
email: 'john@example.com',
|
|
159
|
+
},
|
|
160
|
+
},
|
|
161
|
+
});
|
|
629
162
|
```
|
|
630
163
|
|
|
631
|
-
|
|
164
|
+
Payment response:
|
|
165
|
+
|
|
166
|
+
```ts
|
|
167
|
+
type PaymentResponse = {
|
|
168
|
+
id: string;
|
|
169
|
+
object: 'payment';
|
|
170
|
+
status: 'pending' | 'processing' | 'completed' | 'failed';
|
|
171
|
+
amount: number;
|
|
172
|
+
currency: string;
|
|
173
|
+
created_at: string;
|
|
174
|
+
updated_at: string;
|
|
175
|
+
};
|
|
176
|
+
```
|
|
632
177
|
|
|
633
|
-
|
|
634
|
-
- TypeScript 5.x or higher (for TypeScript projects)
|
|
178
|
+
### Webhooks
|
|
635
179
|
|
|
636
|
-
|
|
180
|
+
Verify webhook signature (`HMAC-SHA256`):
|
|
637
181
|
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
182
|
+
```ts
|
|
183
|
+
const isValid = bloque.webhooks.verify(
|
|
184
|
+
rawBody,
|
|
185
|
+
signature,
|
|
186
|
+
{ secret: process.env.BLOQUE_WEBHOOK_SECRET! },
|
|
187
|
+
);
|
|
188
|
+
```
|
|
641
189
|
|
|
642
|
-
|
|
190
|
+
You can also set `webhookSecret` during `Bloque` initialization and omit `options` in `verify`.
|
|
643
191
|
|
|
644
|
-
|
|
192
|
+
## Example
|
|
645
193
|
|
|
194
|
+
Full example available at:
|
|
195
|
+
- `examples/basic-checkout.ts`
|
package/dist/index.cjs
CHANGED
|
@@ -26,7 +26,7 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
26
26
|
__webpack_require__.d(__webpack_exports__, {
|
|
27
27
|
Bloque: ()=>Bloque
|
|
28
28
|
});
|
|
29
|
-
var package_namespaceObject = JSON.parse('{"UU":"@bloque/payments","rE":"0.0.
|
|
29
|
+
var package_namespaceObject = JSON.parse('{"UU":"@bloque/payments","rE":"0.0.11"}');
|
|
30
30
|
class BloqueError extends Error {
|
|
31
31
|
constructor(message){
|
|
32
32
|
super(message);
|
|
@@ -102,11 +102,12 @@ class HttpClient {
|
|
|
102
102
|
throw lastError ?? new BloqueError('Request failed after retries');
|
|
103
103
|
}
|
|
104
104
|
buildURL(path, params) {
|
|
105
|
-
const url =
|
|
105
|
+
const url = `${this.baseURL}${path.startsWith('/') ? path : `/${path}`}`;
|
|
106
|
+
const urlObj = new URL(url);
|
|
106
107
|
if (params) {
|
|
107
|
-
for (const [key, value] of Object.entries(params))if (null != value)
|
|
108
|
+
for (const [key, value] of Object.entries(params))if (null != value) urlObj.searchParams.append(key, String(value));
|
|
108
109
|
}
|
|
109
|
-
return
|
|
110
|
+
return urlObj.toString();
|
|
110
111
|
}
|
|
111
112
|
buildHeaders(customHeaders, idempotencyKey) {
|
|
112
113
|
return {
|
|
@@ -200,14 +201,14 @@ class CheckoutResource extends BaseResource {
|
|
|
200
201
|
async create(params) {
|
|
201
202
|
const items = params.items.map((item)=>({
|
|
202
203
|
name: item.name,
|
|
203
|
-
|
|
204
|
-
|
|
204
|
+
amount: item.amount.toString(),
|
|
205
|
+
quantity: item.quantity,
|
|
205
206
|
image_url: item.image_url
|
|
206
207
|
}));
|
|
207
208
|
const payload = {
|
|
208
209
|
name: params.name,
|
|
209
210
|
description: params.description,
|
|
210
|
-
asset: '
|
|
211
|
+
asset: params.asset ?? 'DUSD/6',
|
|
211
212
|
payment_type: 'shopping_cart',
|
|
212
213
|
image_url: params.image_url,
|
|
213
214
|
items: items,
|
|
@@ -215,20 +216,20 @@ class CheckoutResource extends BaseResource {
|
|
|
215
216
|
expires_at: params.expires_at,
|
|
216
217
|
metadata: params.metadata
|
|
217
218
|
};
|
|
218
|
-
const response = await this.http.post('/
|
|
219
|
+
const response = await this.http.post('/', payload);
|
|
219
220
|
return {
|
|
220
221
|
id: response.payment.urn,
|
|
221
222
|
object: 'checkout',
|
|
222
223
|
url: response.payment.url,
|
|
223
224
|
status: response.payment.summary.status,
|
|
224
|
-
amount_total: response.payment.
|
|
225
|
-
amount_subtotal: response.payment.
|
|
226
|
-
|
|
225
|
+
amount_total: response.payment.amount,
|
|
226
|
+
amount_subtotal: response.payment.amount,
|
|
227
|
+
asset: response.payment.asset,
|
|
227
228
|
items: params.items,
|
|
228
229
|
created_at: response.payment.created_at,
|
|
229
230
|
updated_at: response.payment.updated_at,
|
|
230
231
|
expires_at: response.payment.expires_at,
|
|
231
|
-
metadata: response.payment.metadata
|
|
232
|
+
metadata: response.payment.metadata ?? void 0
|
|
232
233
|
};
|
|
233
234
|
}
|
|
234
235
|
async retrieve(checkoutId) {
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createHmac } from "node:crypto";
|
|
2
|
-
var package_namespaceObject = JSON.parse('{"UU":"@bloque/payments","rE":"0.0.
|
|
2
|
+
var package_namespaceObject = JSON.parse('{"UU":"@bloque/payments","rE":"0.0.11"}');
|
|
3
3
|
class BloqueError extends Error {
|
|
4
4
|
constructor(message){
|
|
5
5
|
super(message);
|
|
@@ -75,11 +75,12 @@ class HttpClient {
|
|
|
75
75
|
throw lastError ?? new BloqueError('Request failed after retries');
|
|
76
76
|
}
|
|
77
77
|
buildURL(path, params) {
|
|
78
|
-
const url =
|
|
78
|
+
const url = `${this.baseURL}${path.startsWith('/') ? path : `/${path}`}`;
|
|
79
|
+
const urlObj = new URL(url);
|
|
79
80
|
if (params) {
|
|
80
|
-
for (const [key, value] of Object.entries(params))if (null != value)
|
|
81
|
+
for (const [key, value] of Object.entries(params))if (null != value) urlObj.searchParams.append(key, String(value));
|
|
81
82
|
}
|
|
82
|
-
return
|
|
83
|
+
return urlObj.toString();
|
|
83
84
|
}
|
|
84
85
|
buildHeaders(customHeaders, idempotencyKey) {
|
|
85
86
|
return {
|
|
@@ -173,14 +174,14 @@ class CheckoutResource extends BaseResource {
|
|
|
173
174
|
async create(params) {
|
|
174
175
|
const items = params.items.map((item)=>({
|
|
175
176
|
name: item.name,
|
|
176
|
-
|
|
177
|
-
|
|
177
|
+
amount: item.amount.toString(),
|
|
178
|
+
quantity: item.quantity,
|
|
178
179
|
image_url: item.image_url
|
|
179
180
|
}));
|
|
180
181
|
const payload = {
|
|
181
182
|
name: params.name,
|
|
182
183
|
description: params.description,
|
|
183
|
-
asset: '
|
|
184
|
+
asset: params.asset ?? 'DUSD/6',
|
|
184
185
|
payment_type: 'shopping_cart',
|
|
185
186
|
image_url: params.image_url,
|
|
186
187
|
items: items,
|
|
@@ -188,20 +189,20 @@ class CheckoutResource extends BaseResource {
|
|
|
188
189
|
expires_at: params.expires_at,
|
|
189
190
|
metadata: params.metadata
|
|
190
191
|
};
|
|
191
|
-
const response = await this.http.post('/
|
|
192
|
+
const response = await this.http.post('/', payload);
|
|
192
193
|
return {
|
|
193
194
|
id: response.payment.urn,
|
|
194
195
|
object: 'checkout',
|
|
195
196
|
url: response.payment.url,
|
|
196
197
|
status: response.payment.summary.status,
|
|
197
|
-
amount_total: response.payment.
|
|
198
|
-
amount_subtotal: response.payment.
|
|
199
|
-
|
|
198
|
+
amount_total: response.payment.amount,
|
|
199
|
+
amount_subtotal: response.payment.amount,
|
|
200
|
+
asset: response.payment.asset,
|
|
200
201
|
items: params.items,
|
|
201
202
|
created_at: response.payment.created_at,
|
|
202
203
|
updated_at: response.payment.updated_at,
|
|
203
204
|
expires_at: response.payment.expires_at,
|
|
204
|
-
metadata: response.payment.metadata
|
|
205
|
+
metadata: response.payment.metadata ?? void 0
|
|
205
206
|
};
|
|
206
207
|
}
|
|
207
208
|
async retrieve(checkoutId) {
|
package/dist/types/checkout.d.ts
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ASSETS, Metadata } from './common';
|
|
2
2
|
import type { Customer } from './customer';
|
|
3
3
|
export interface CreateCheckoutPayload {
|
|
4
4
|
name: string;
|
|
5
5
|
description?: string;
|
|
6
6
|
image_url?: string;
|
|
7
|
-
asset:
|
|
7
|
+
asset: ASSETS;
|
|
8
8
|
payment_type: 'shopping_cart';
|
|
9
9
|
items: {
|
|
10
10
|
name: string;
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
amount: string;
|
|
12
|
+
quantity: number;
|
|
13
13
|
image_url?: string;
|
|
14
14
|
}[];
|
|
15
15
|
redirect_url: string;
|
|
@@ -20,16 +20,18 @@ export interface CreateCheckoutResponse {
|
|
|
20
20
|
payment: {
|
|
21
21
|
urn: string;
|
|
22
22
|
url: string;
|
|
23
|
-
|
|
23
|
+
asset: ASSETS;
|
|
24
|
+
amount: number;
|
|
24
25
|
image_url?: string | null;
|
|
25
|
-
metadata?: Metadata;
|
|
26
|
+
metadata?: Metadata | null;
|
|
26
27
|
created_at: string;
|
|
27
28
|
updated_at: string;
|
|
28
|
-
expires_at: string;
|
|
29
|
+
expires_at: string | null;
|
|
29
30
|
summary: {
|
|
30
31
|
status: CheckoutStatus;
|
|
31
32
|
};
|
|
32
33
|
};
|
|
34
|
+
url_id: string;
|
|
33
35
|
}
|
|
34
36
|
/**
|
|
35
37
|
* Represents a single item included in a checkout.
|
|
@@ -79,11 +81,11 @@ export interface CheckoutParams {
|
|
|
79
81
|
*/
|
|
80
82
|
items: CheckoutItem[];
|
|
81
83
|
/**
|
|
82
|
-
*
|
|
84
|
+
* Asset used for the checkout.
|
|
83
85
|
*
|
|
84
|
-
* @default '
|
|
86
|
+
* @default 'dUSD/6'
|
|
85
87
|
*/
|
|
86
|
-
|
|
88
|
+
asset?: ASSETS;
|
|
87
89
|
/**
|
|
88
90
|
* URL the customer will be redirected to after a successful payment.
|
|
89
91
|
*/
|
|
@@ -143,9 +145,9 @@ export interface Checkout {
|
|
|
143
145
|
*/
|
|
144
146
|
amount_subtotal: number;
|
|
145
147
|
/**
|
|
146
|
-
*
|
|
148
|
+
* Asset used for the checkout.
|
|
147
149
|
*/
|
|
148
|
-
|
|
150
|
+
asset: ASSETS;
|
|
149
151
|
/**
|
|
150
152
|
* Customer associated with the checkout, if any.
|
|
151
153
|
*/
|
|
@@ -169,5 +171,5 @@ export interface Checkout {
|
|
|
169
171
|
/**
|
|
170
172
|
* Checkout expiration timestamp in ISO 8601 format.
|
|
171
173
|
*/
|
|
172
|
-
expires_at: string;
|
|
174
|
+
expires_at: string | null;
|
|
173
175
|
}
|
package/dist/types/common.d.ts
CHANGED