@bloque/payments 0.0.2 → 0.0.4

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 CHANGED
@@ -24,7 +24,7 @@ import { Bloque, type PaymentSubmitPayload } from '@bloque/payments';
24
24
  // Initialize the SDK (server-side only)
25
25
  const bloque = new Bloque({
26
26
  apiKey: process.env.BLOQUE_API_KEY!,
27
- server: 'production', // or 'sandbox' for testing
27
+ mode: 'production', // or 'sandbox' for testing
28
28
  });
29
29
 
30
30
  app.post('/api/payments', async (req, res) => {
@@ -52,7 +52,7 @@ import { Bloque } from '@bloque/payments';
52
52
 
53
53
  const bloque = new Bloque({
54
54
  apiKey: 'your-api-key-here', // Required: Your Bloque API key
55
- server: 'sandbox', // Required: 'sandbox' or 'production'
55
+ mode: 'sandbox', // Required: 'sandbox' or 'production'
56
56
  });
57
57
  ```
58
58
 
@@ -214,6 +214,59 @@ const checkout = await bloque.checkout.cancel('checkout_id_here');
214
214
 
215
215
  **Returns**: A `Checkout` object with updated status reflecting the cancellation.
216
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,
267
+ });
268
+ ```
269
+
217
270
  ## Examples
218
271
 
219
272
  ### Processing Payments
@@ -224,7 +277,7 @@ import { Bloque, type PaymentSubmitPayload } from '@bloque/payments';
224
277
  // Initialize SDK with your API key
225
278
  const bloque = new Bloque({
226
279
  apiKey: process.env.BLOQUE_API_KEY!,
227
- server: 'production',
280
+ mode: 'production',
228
281
  });
229
282
 
230
283
  // API endpoint handler
@@ -345,6 +398,61 @@ async function handlePayment(payload: PaymentSubmitPayload) {
345
398
  }
346
399
  ```
347
400
 
401
+ ### Webhook Handler
402
+
403
+ Handle incoming webhook events from Bloque and verify their authenticity:
404
+
405
+ ```typescript
406
+ import { Bloque } from '@bloque/payments';
407
+
408
+ // Initialize SDK with webhook secret
409
+ const bloque = new Bloque({
410
+ apiKey: process.env.BLOQUE_API_KEY!,
411
+ mode: 'production',
412
+ webhookSecret: process.env.BLOQUE_WEBHOOK_SECRET,
413
+ });
414
+
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
+
348
456
  ### Basic Checkout with Single Item
349
457
 
350
458
  ```typescript
@@ -444,6 +552,8 @@ import type {
444
552
  CheckoutStatus,
445
553
  CheckoutItem,
446
554
  CheckoutParams,
555
+ // Webhook types
556
+ WebhookVerifyOptions,
447
557
  } from '@bloque/payments';
448
558
 
449
559
  const item: CheckoutItem = {
@@ -526,8 +636,8 @@ bun run check
526
636
  ## Links
527
637
 
528
638
  - [Homepage](https://www.bloque.app)
529
- - [GitHub Repository](git+https://github.com/bloque-app/bloque-payments.git)
530
- - [Issue Tracker](git+https://github.com/bloque-app/bloque-payments.git/issues)
639
+ - [GitHub Repository](git+https://github.com/bloque-app/payments.git)
640
+ - [Issue Tracker](git+https://github.com/bloque-app/payments.git/issues)
531
641
 
532
642
  ## License
533
643
 
package/dist/client.d.ts CHANGED
@@ -1,15 +1,18 @@
1
1
  import { CheckoutResource } from './resources/checkout';
2
2
  import { PaymentResource } from './resources/payment';
3
+ import { WebhookResource } from './resources/webhook';
3
4
  export type BloqueConfig = {
4
- server: 'sandbox' | 'production';
5
+ mode: 'sandbox' | 'production';
5
6
  apiKey: string;
6
7
  timeout?: number;
7
8
  maxRetries?: number;
9
+ webhookSecret?: string;
8
10
  };
9
11
  export declare class Bloque {
10
12
  #private;
11
13
  checkout: CheckoutResource;
12
14
  payments: PaymentResource;
15
+ webhooks: WebhookResource;
13
16
  constructor(config: BloqueConfig);
14
17
  private initializeResources;
15
18
  }
package/dist/index.cjs CHANGED
@@ -13,7 +13,7 @@ var __webpack_require__ = {};
13
13
  })();
14
14
  (()=>{
15
15
  __webpack_require__.r = (exports1)=>{
16
- if ('undefined' != typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
16
+ if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
17
17
  value: 'Module'
18
18
  });
19
19
  Object.defineProperty(exports1, '__esModule', {
@@ -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.2"}');
29
+ var package_namespaceObject = JSON.parse('{"UU":"@bloque/payments","rE":"0.0.4"}');
30
30
  class BloqueError extends Error {
31
31
  constructor(message){
32
32
  super(message);
@@ -282,16 +282,46 @@ class PaymentResource extends BaseResource {
282
282
  };
283
283
  }
284
284
  }
285
+ const external_node_crypto_namespaceObject = require("node:crypto");
286
+ class WebhookResource {
287
+ #secret;
288
+ constructor(secret){
289
+ this.#secret = secret || '';
290
+ }
291
+ verify(body, signature, options) {
292
+ const secret = options?.secret || this.#secret;
293
+ if (!secret) throw new Error('Webhook secret is required. Pass it in options or set it during Bloque initialization.');
294
+ if (!signature) return false;
295
+ const payload = 'string' == typeof body ? body : JSON.stringify(body);
296
+ const computedSignature = this.#computeSignature(payload, secret);
297
+ return this.#secureCompare(signature, computedSignature);
298
+ }
299
+ setSecret(secret) {
300
+ this.#secret = secret;
301
+ }
302
+ #computeSignature(payload, secret) {
303
+ const hmac = (0, external_node_crypto_namespaceObject.createHmac)('sha256', secret);
304
+ hmac.update(payload);
305
+ return hmac.digest('hex');
306
+ }
307
+ #secureCompare(a, b) {
308
+ if (a.length !== b.length) return false;
309
+ let result = 0;
310
+ for(let i = 0; i < a.length; i++)result |= a.charCodeAt(i) ^ b.charCodeAt(i);
311
+ return 0 === result;
312
+ }
313
+ }
285
314
  class Bloque {
286
315
  #httpClient;
287
316
  #config;
288
317
  checkout;
289
318
  payments;
319
+ webhooks;
290
320
  constructor(config){
291
321
  if (!config.apiKey) throw new Error('API key is required');
292
322
  this.#config = config;
293
323
  this.#httpClient = new HttpClient({
294
- baseURL: 'https: //api.bloque.app/api/payments',
324
+ baseURL: 'sandbox' === this.#config.mode ? 'https://dev.bloque.app/api/payments' : 'https://api.bloque.app/api/payments',
295
325
  apiKey: this.#config.apiKey,
296
326
  timeout: this.#config.timeout,
297
327
  maxRetries: this.#config.maxRetries
@@ -301,6 +331,7 @@ class Bloque {
301
331
  initializeResources() {
302
332
  this.checkout = new CheckoutResource(this.#httpClient);
303
333
  this.payments = new PaymentResource(this.#httpClient);
334
+ this.webhooks = new WebhookResource(this.#config.webhookSecret);
304
335
  }
305
336
  }
306
337
  exports.Bloque = __webpack_exports__.Bloque;
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  export type { CreatePaymentParams, PaymentResponse, PaymentSubmitPayload, } from '@bloque/payments-core';
2
2
  export { Bloque, type BloqueConfig } from './client';
3
+ export type { WebhookVerifyOptions } from './resources/webhook';
3
4
  export type { Checkout, CheckoutItem, CheckoutParams, CheckoutStatus, } from './types/checkout';
package/dist/index.js CHANGED
@@ -1,4 +1,5 @@
1
- var package_namespaceObject = JSON.parse('{"UU":"@bloque/payments","rE":"0.0.2"}');
1
+ import { createHmac } from "node:crypto";
2
+ var package_namespaceObject = JSON.parse('{"UU":"@bloque/payments","rE":"0.0.4"}');
2
3
  class BloqueError extends Error {
3
4
  constructor(message){
4
5
  super(message);
@@ -254,16 +255,45 @@ class PaymentResource extends BaseResource {
254
255
  };
255
256
  }
256
257
  }
258
+ class WebhookResource {
259
+ #secret;
260
+ constructor(secret){
261
+ this.#secret = secret || '';
262
+ }
263
+ verify(body, signature, options) {
264
+ const secret = options?.secret || this.#secret;
265
+ if (!secret) throw new Error('Webhook secret is required. Pass it in options or set it during Bloque initialization.');
266
+ if (!signature) return false;
267
+ const payload = 'string' == typeof body ? body : JSON.stringify(body);
268
+ const computedSignature = this.#computeSignature(payload, secret);
269
+ return this.#secureCompare(signature, computedSignature);
270
+ }
271
+ setSecret(secret) {
272
+ this.#secret = secret;
273
+ }
274
+ #computeSignature(payload, secret) {
275
+ const hmac = createHmac('sha256', secret);
276
+ hmac.update(payload);
277
+ return hmac.digest('hex');
278
+ }
279
+ #secureCompare(a, b) {
280
+ if (a.length !== b.length) return false;
281
+ let result = 0;
282
+ for(let i = 0; i < a.length; i++)result |= a.charCodeAt(i) ^ b.charCodeAt(i);
283
+ return 0 === result;
284
+ }
285
+ }
257
286
  class Bloque {
258
287
  #httpClient;
259
288
  #config;
260
289
  checkout;
261
290
  payments;
291
+ webhooks;
262
292
  constructor(config){
263
293
  if (!config.apiKey) throw new Error('API key is required');
264
294
  this.#config = config;
265
295
  this.#httpClient = new HttpClient({
266
- baseURL: 'https: //api.bloque.app/api/payments',
296
+ baseURL: 'sandbox' === this.#config.mode ? 'https://dev.bloque.app/api/payments' : 'https://api.bloque.app/api/payments',
267
297
  apiKey: this.#config.apiKey,
268
298
  timeout: this.#config.timeout,
269
299
  maxRetries: this.#config.maxRetries
@@ -273,6 +303,7 @@ class Bloque {
273
303
  initializeResources() {
274
304
  this.checkout = new CheckoutResource(this.#httpClient);
275
305
  this.payments = new PaymentResource(this.#httpClient);
306
+ this.webhooks = new WebhookResource(this.#config.webhookSecret);
276
307
  }
277
308
  }
278
309
  export { Bloque };
@@ -0,0 +1,26 @@
1
+ export interface WebhookVerifyOptions {
2
+ secret: string;
3
+ }
4
+ export declare class WebhookResource {
5
+ #private;
6
+ constructor(secret?: string);
7
+ /**
8
+ * Verify the authenticity of a webhook payload using HMAC-SHA256
9
+ *
10
+ * @param body - The raw webhook body (string or object)
11
+ * @param signature - The signature from the webhook header
12
+ * @param options - Optional verification options
13
+ * @returns True if the webhook is valid
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * const isValid = bloque.webhooks.verify(
18
+ * req.body,
19
+ * req.headers['x-bloque-signature'],
20
+ * { secret: process.env.WEBHOOK_SECRET }
21
+ * );
22
+ * ```
23
+ */
24
+ verify(body: string | Record<string, unknown>, signature: string, options?: WebhookVerifyOptions): boolean;
25
+ setSecret(secret: string): void;
26
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bloque/payments",
3
- "version": "0.0.2",
3
+ "version": "0.0.4",
4
4
  "description": "Official Bloque SDK for creating and managing payments and checkouts.",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -36,20 +36,7 @@
36
36
  "engines": {
37
37
  "node": ">=22"
38
38
  },
39
- "scripts": {
40
- "build": "rslib build",
41
- "dev": "rslib build --watch",
42
- "clean": "rm -rf node_modules && rm -rf dist",
43
- "check": "biome check --write",
44
- "typecheck": "tsgo --noEmit"
45
- },
46
39
  "dependencies": {
47
- "@bloque/payments-core": "workspace:*"
48
- },
49
- "devDependencies": {
50
- "@rslib/core": "catalog:",
51
- "@types/node": "catalog:",
52
- "@typescript/native-preview": "catalog:",
53
- "typescript": "catalog:"
40
+ "@bloque/payments-core": "0.0.4"
54
41
  }
55
42
  }