### Install @taloon/btcpay-middleware and express Source: https://github.com/diegopherlt/btcpay-middleware/blob/main/docs/quick-start.mdx Install the necessary packages using pnpm. ```bash pnpm add @taloon/btcpay-middleware express ``` -------------------------------- ### Install btcpay-middleware and express Source: https://github.com/diegopherlt/btcpay-middleware/blob/main/README.md Install the necessary packages for btcpay-middleware and its peer dependency, express. ```bash pnpm add @taloon/btcpay-middleware # peer dependency pnpm add express ``` -------------------------------- ### Install Local Package from Tarball with pnpm pack Source: https://github.com/diegopherlt/btcpay-middleware/blob/main/local-installation.md Create a tarball of the package using `pnpm pack` and then install it in your consumer project using the absolute path to the generated `.tgz` file. Re-pack and re-install after source changes. ```bash pnpm run build pnpm pack # produces taloon-btcpay-middleware-0.1.0.tgz ``` ```bash pnpm add /absolute/path/to/taloon-btcpay-middleware-0.1.0.tgz ``` -------------------------------- ### Configure BTCPayMiddleware with no overrides Source: https://github.com/diegopherlt/btcpay-middleware/blob/main/docs/configuration-examples.mdx When environment variables are present at process start, calling configure() with no overrides is an explicit signal. BTCPAY_BASE_URL, BTCPAY_API_KEY, BTCPAY_STORE_ID, and BTCPAY_WEBHOOK_SECRET are read on first construction. ```typescript BTCPayMiddleware.configure({}); ``` -------------------------------- ### Invoice Creation with Buyer Data and Order ID Source: https://github.com/diegopherlt/btcpay-middleware/blob/main/docs/invoice-examples.mdx This example demonstrates creating an invoice with additional buyer information such as email, order ID, and product description. It also includes a custom response transformation to return specific invoice details. ```typescript app.post( '/checkout', BTCPayMiddleware.createInvoice({ mapRequest: req => ({ amount: req.body.total, currency: 'USD', orderId: req.body.orderId, buyerEmail: req.body.email, description: req.body.productName, metadata: { productSku: req.body.sku, sessionId: req.body.sessionId, }, }), transformResponse: invoice => ({ checkoutUrl: invoice.checkoutLink, invoiceId: invoice.id, expiresAt: invoice.expirationTime, }), }), (_req, res) => res.json(res.locals.btcpayResponse) ); ``` -------------------------------- ### Reconciling Invoices with BTCPayClient Source: https://github.com/diegopherlt/btcpay-middleware/blob/main/docs/invoice-examples.mdx This example shows how to poll an invoice using the BTCPayClient to check its status (SETTLED, EXPIRED, INVALID) for reconciliation purposes. Ensure BTCPayConfiguration is set up. ```typescript import { BTCPayClient, BTCPayConfiguration, InvoiceStatus, } from '@taloon/btcpay-middleware'; const client = BTCPayClient.fromConfig(BTCPayConfiguration.getConfig()); export async function reconcile(invoiceId: string) { const invoice = await client.getInvoice(invoiceId); if (invoice.status === InvoiceStatus.SETTLED) return 'paid'; if (invoice.status === InvoiceStatus.EXPIRED) return 'expired'; if (invoice.status === InvoiceStatus.INVALID) return 'invalid'; return 'open'; } ``` -------------------------------- ### Express Error Middleware Example Source: https://github.com/diegopherlt/btcpay-middleware/blob/main/docs/error-handling.mdx An example of an Express error handler that specifically catches and formats BTCPayWebhookSignatureError and BTCPayApiError instances, falling back to a generic internal error for others. ```typescript import { ErrorRequestHandler } from 'express'; import { BTCPayApiError, BTCPayWebhookSignatureError } from '@taloon/btcpay-middleware'; const errorHandler: ErrorRequestHandler = (err, _req, res, _next) => { if (err instanceof BTCPayWebhookSignatureError) { return res.status(401).json({ error: err.code }); } if (err instanceof BTCPayApiError) { return res.status(err.statusCode ?? 502).json({ error: err.code, message: err.message, }); } return res.status(500).json({ error: 'INTERNAL_ERROR' }); }; app.use(errorHandler); ``` -------------------------------- ### Build and Development Commands Source: https://github.com/diegopherlt/btcpay-middleware/blob/main/AGENTS.md Standard npm scripts for building, type checking, testing, and linting the project. ```bash pnpm run build # tsc + tsc-alias pnpm run build:watch pnpm run type-check pnpm test pnpm test:watch pnpm test:coverage pnpm run lint pnpm run lint:fix pnpm run format ``` -------------------------------- ### Initialize BTCPayMiddleware Source: https://github.com/diegopherlt/btcpay-middleware/blob/main/docs/api-reference.mdx Import the namespace and access core methods for invoice and payout management. ```ts import { BTCPayMiddleware } from '@taloon/btcpay-middleware'; BTCPayMiddleware.configure(config); BTCPayMiddleware.createInvoice(options); BTCPayMiddleware.getInvoice(options); BTCPayMiddleware.invoiceWebhook(options); BTCPayMiddleware.createPayout(options); BTCPayMiddleware.getPayout(options); BTCPayMiddleware.payoutWebhook(options); ``` -------------------------------- ### Configure BTCPayMiddleware Source: https://github.com/diegopherlt/btcpay-middleware/blob/main/docs/quick-start.mdx Configure the middleware with your BTCPay Server details. This should be called once on startup. The required keys are validated lazily on the first middleware call. ```typescript import { BTCPayMiddleware } from '@taloon/btcpay-middleware'; BTCPayMiddleware.configure({ baseURL: 'https://btcpay.example.com', apiKey: process.env.BTCPAY_API_KEY!, storeId: process.env.BTCPAY_STORE_ID!, webhookSecret: process.env.BTCPAY_WEBHOOK_SECRET, // optional, recommended }); ``` -------------------------------- ### Import BTCPay Middleware Utilities Source: https://github.com/diegopherlt/btcpay-middleware/blob/main/docs/api-reference.mdx Imports the core client and signature verification utilities from the package. ```ts import { BTCPayClient, computeSignature, verifyWebhookSignature, WEBHOOK_SIGNATURE_HEADER, } from '@taloon/btcpay-middleware'; ``` -------------------------------- ### Project Build and Maintenance Commands Source: https://github.com/diegopherlt/btcpay-middleware/blob/main/AGENTS.md Standard CLI commands for building, testing, and maintaining the project codebase. ```bash pnpm run build ``` ```bash pnpm test ``` ```bash pnpm test:coverage ``` ```bash pnpm run lint ``` ```bash pnpm run format ``` ```bash pnpm run type-check ``` -------------------------------- ### BTCPayMiddleware.configure(config) Source: https://github.com/diegopherlt/btcpay-middleware/blob/main/docs/api-reference.mdx Configures the middleware with the necessary BTCPay Server credentials and settings. This method merges the provided configuration with existing settings. ```APIDOC ## configure(config) ### Description Configures the BTCPayMiddleware instance. Required keys (baseURL, apiKey, storeId) are validated lazily upon the first middleware call. ### Signature `BTCPayMiddleware.configure(partial: Partial): void;` ### Parameters - **baseURL** (string) - Required - BTCPay Server URL (e.g., https://btcpay.example.com) - **apiKey** (string) - Required - API key for the Greenfield API - **storeId** (string) - Required - Store ID - **webhookSecret** (string) - Optional - HMAC secret matching the BTCPay webhook UI - **errorHandling** ('next' | 'direct') - Optional - Fallback strategy for errors. 'next' (default) calls next(error); 'direct' writes a JSON error body directly. - **onError** (ErrorHandler) - Optional - Global error handler function. ``` -------------------------------- ### Link Local Package with pnpm link Source: https://github.com/diegopherlt/btcpay-middleware/blob/main/local-installation.md Use `pnpm link --global` to link the package from its source directory to the global pnpm store. Then, link it into your consumer project. Re-run `pnpm run build` in the source directory after making changes. ```bash pnpm run build pnpm link --global ``` ```bash pnpm link --global @taloon/btcpay-middleware ``` -------------------------------- ### Create an invoice with createInvoice middleware Source: https://github.com/diegopherlt/btcpay-middleware/blob/main/docs/quick-start.mdx Use the `createInvoice` middleware to generate a new invoice. Map your request data to the required invoice parameters and transform the response to include checkout details. Redirect the buyer to the returned `checkoutUrl`. ```typescript import express from 'express'; import { BTCPayMiddleware } from '@taloon/btcpay-middleware'; const app = express(); app.use(express.json()); app.post( '/checkout', BTCPayMiddleware.createInvoice({ mapRequest: req => ({ amount: req.body.total, currency: 'USD', orderId: req.body.orderId, buyerEmail: req.body.email, }), transformResponse: invoice => ({ checkoutUrl: invoice.checkoutLink, invoiceId: invoice.id, expiresAt: invoice.expirationTime, }), }), (_req, res) => res.json(res.locals.btcpayResponse) ); ``` -------------------------------- ### Coexisting BTCPayMiddleware with NowPaymentsMiddleware Source: https://github.com/diegopherlt/btcpay-middleware/blob/main/docs/configuration-examples.mdx Configure both BTCPayMiddleware and NowPaymentsMiddleware independently. Both packages namespace their res.locals to prevent collisions. ```typescript import { NowPaymentsMiddleware } from '@taloon/nowpayments-middleware'; import { BTCPayMiddleware } from '@taloon/btcpay-middleware'; NowPaymentsMiddleware.configure({ apiKey: process.env.NOWPAYMENTS_API_KEY! }); BTCPayMiddleware.configure({ baseURL: process.env.BTCPAY_BASE_URL!, apiKey: process.env.BTCPAY_API_KEY!, storeId: process.env.BTCPAY_STORE_ID!, }); ``` -------------------------------- ### Configure BTCPayMiddleware Source: https://github.com/diegopherlt/btcpay-middleware/blob/main/README.md Configure the BTCPayMiddleware with your BTCPay Server instance details, API key, store ID, and optional webhook secret. The error handling strategy can also be set. ```typescript import { BTCPayMiddleware } from '@taloon/btcpay-middleware'; BTCPayMiddleware.configure({ baseURL: process.env.BTCPAY_BASE_URL!, // e.g. https://btcpay.example.com apiKey: process.env.BTCPAY_API_KEY!, // BTCPay API key (Greenfield) storeId: process.env.BTCPAY_STORE_ID!, webhookSecret: process.env.BTCPAY_WEBHOOK_SECRET, // optional but recommended errorHandling: 'next', // 'next' | 'direct' }); ``` -------------------------------- ### createInvoice(options) Source: https://github.com/diegopherlt/btcpay-middleware/blob/main/docs/api-reference.mdx Middleware to create a new invoice by mapping request data to the BTCPay API. ```APIDOC ## createInvoice(options) ### Description Creates a new invoice by calling POST /api/v1/stores/{storeId}/invoices. The result is stored on res.locals.btcpayResponse. ### Parameters - **options** (CreateInvoiceMiddlewareOptions) - Required - Configuration object containing mapRequest, transformResponse, and onError handlers. ### Request Body - **amount** (number | string) - Required - Invoice amount. - **currency** (string) - Required - Invoice currency. - **orderId** (string) - Optional - Order identifier. - **buyerEmail** (string) - Optional - Buyer email address. - **description** (string) - Optional - Invoice description. - **checkout** (InvoiceCheckoutOptions) - Optional - Checkout configuration. - **metadata** (Record) - Optional - Additional metadata. ``` -------------------------------- ### Define CreatePayoutMiddlewareOptions and CreatePayoutRequest interfaces Source: https://github.com/diegopherlt/btcpay-middleware/blob/main/docs/api-reference.mdx Defines the structure for middleware configuration and the required payout request body fields. ```ts interface CreatePayoutMiddlewareOptions { mapRequest: (req: Request, res: Response) => CreatePayoutRequest; transformResponse?: (payout: BTCPayPayout) => unknown; onError?: ErrorHandler; } interface CreatePayoutRequest { destination: string; // wallet address (or BIP21 url) amount: string; // amount in the store's default currency payoutMethodId: PayoutMethod; // 'BTC-CHAIN' | 'BTC-LN' approved?: boolean; // defaults to true (auto-approve) metadata?: Record; } ``` -------------------------------- ### invoiceWebhook(options) Source: https://github.com/diegopherlt/btcpay-middleware/blob/main/docs/api-reference.mdx Middleware to handle incoming BTCPay webhooks. ```APIDOC ## invoiceWebhook(options) ### Description Processes incoming webhooks from BTCPay. Verifies the BTCPay-Sig header if a webhookSecret is configured and dispatches events to specific handlers. ### Parameters - **options** (InvoiceWebhookMiddlewareOptions) - Required - Event handlers for various webhook states (onCreated, onReceivedPayment, onProcessing, onSettled, onPaymentSettled, onExpired, onInvalid) and an onError handler. ``` -------------------------------- ### getPayout(options) Source: https://github.com/diegopherlt/btcpay-middleware/blob/main/docs/api-reference.mdx Middleware to fetch payout details from BTCPay Server and store them in the response locals. ```APIDOC ## getPayout(options) ### Description Reads `GET /api/v1/stores/{storeId}/payouts/{payoutId}` and stores the result on `res.locals.btcpayResponse`. Use this to inspect payout state and payment proof for reconciliation. ### Parameters - **options** (GetPayoutMiddlewareOptions) - Required - Configuration object containing `mapRequest` (to extract payoutId), `transformResponse` (optional), and `onError` (optional). ``` -------------------------------- ### createPayout(options) Source: https://github.com/diegopherlt/btcpay-middleware/blob/main/docs/api-reference.mdx The createPayout middleware function allows for the creation of payouts in BTCPay Server by mapping request data to the required payout structure. ```APIDOC ## createPayout(options) ### Description Creates a payout in BTCPay Server by mapping an incoming request to a payout request. The middleware handles the communication with `POST /api/v1/stores/{storeId}/payouts`. ### Parameters #### Options - **mapRequest** (function) - Required - A function that takes (req, res) and returns a `CreatePayoutRequest` object. - **transformResponse** (function) - Optional - A function to transform the `BTCPayPayout` response. - **onError** (function) - Optional - A custom error handler. ### CreatePayoutRequest Fields - **destination** (string) - Required - The wallet address or BIP21 URL. - **amount** (string) - Required - The amount in the store's default currency. - **payoutMethodId** (string) - Required - The payout method, e.g., 'BTC-CHAIN' or 'BTC-LN'. - **approved** (boolean) - Optional - Defaults to true. If false, the payout remains in 'AwaitingApproval'. - **metadata** (object) - Optional - A record of key-value pairs for internal identifiers. ### Usage Result is stored on `res.locals.btcpayResponse`. ``` -------------------------------- ### payoutWebhook(options) Source: https://github.com/diegopherlt/btcpay-middleware/blob/main/docs/api-reference.mdx Middleware to handle incoming BTCPay Server payout webhooks. ```APIDOC ## payoutWebhook(options) ### Description Handles BTCPay payout webhooks. Should be mounted with `express.raw({ type: 'application/json' })`. Supports events: `PayoutCreated`, `PayoutApproved`, and `PayoutUpdated`. ### Parameters - **options** (PayoutWebhookMiddlewareOptions) - Required - Configuration object containing event handlers `onPayoutCreated`, `onPayoutApproved`, `onPayoutUpdated`, and `onError`. ``` -------------------------------- ### Project Directory Structure Source: https://github.com/diegopherlt/btcpay-middleware/blob/main/AGENTS.md The file system layout of the source code, highlighting the separation of clients, middlewares, and utility functions. ```text src/ ├── client/BTCPayClient.ts # axios wrapper + Authorization: token ├── config/BTCPayConfig.ts # singleton + reset() for tests ├── constants/statuses.ts # InvoiceStatus, PayoutState, PayoutMethod, WebhookEventType enums ├── middlewares/ │ ├── base/BaseMiddleware.ts # 3-tier handleError + saveToLocals │ ├── webhooks/ │ │ ├── invoiceWebhook.ts # HMAC verify + invoice event dispatch │ │ └── payoutWebhook.ts # HMAC verify + payout event dispatch │ ├── createInvoice.ts │ ├── getInvoice.ts │ ├── createPayout.ts │ └── getPayout.ts ├── types/ # api.types, middleware.types, index ├── utils/ │ ├── errors.ts # BTCPayError + 5 subclasses │ └── webhookSignature.ts # HMAC SHA-256 (BTCPay-Sig) └── index.ts # public API: BTCPayMiddleware namespace ``` -------------------------------- ### Define InvoiceWebhookMiddlewareOptions Source: https://github.com/diegopherlt/btcpay-middleware/blob/main/docs/api-reference.mdx Configuration interface for handling various BTCPay invoice webhook events. ```ts interface InvoiceWebhookMiddlewareOptions { onCreated?: (payload: BTCPayWebhookPayload) => void | Promise; onReceivedPayment?: (payload: BTCPayWebhookPayload) => void | Promise; onProcessing?: (payload: BTCPayWebhookPayload) => void | Promise; onSettled?: (payload: BTCPayWebhookPayload) => void | Promise; onPaymentSettled?: (payload: BTCPayWebhookPayload) => void | Promise; onExpired?: (payload: BTCPayWebhookPayload) => void | Promise; onInvalid?: (payload: BTCPayWebhookPayload) => void | Promise; onError?: ErrorHandler; } ``` -------------------------------- ### Override BTCPayMiddleware per environment Source: https://github.com/diegopherlt/btcpay-middleware/blob/main/docs/configuration-examples.mdx Conditionally configure middleware settings based on the environment. Ensure required environment variables like BTCPAY_API_KEY and BTCPAY_STORE_ID are set. ```typescript const isProduction = process.env.NODE_ENV === 'production'; BTCPayMiddleware.configure({ baseURL: isProduction ? 'https://btcpay.example.com' : 'https://btcpay.staging.example.com', apiKey: process.env.BTCPAY_API_KEY!, storeId: process.env.BTCPAY_STORE_ID!, webhookSecret: process.env.BTCPAY_WEBHOOK_SECRET, errorHandling: isProduction ? 'next' : 'direct', }); ``` -------------------------------- ### Define payoutWebhook options Source: https://github.com/diegopherlt/btcpay-middleware/blob/main/docs/api-reference.mdx Configuration interface for payout webhook handlers, supporting creation, approval, and update events. ```ts interface PayoutWebhookMiddlewareOptions { onPayoutCreated?: (payload: BTCPayPayoutWebhookPayload) => void | Promise; onPayoutApproved?: (payload: BTCPayPayoutWebhookPayload) => void | Promise; onPayoutUpdated?: (payload: BTCPayPayoutWebhookPayload) => void | Promise; onError?: ErrorHandler; } ``` -------------------------------- ### Listen for invoice events with invoiceWebhook middleware Source: https://github.com/diegopherlt/btcpay-middleware/blob/main/docs/quick-start.mdx Use the `invoiceWebhook` middleware to handle incoming events from BTCPay Server. Ensure you capture the raw request body to validate the signature. The middleware verifies the signature (if `webhookSecret` is set), parses the body, and dispatches to the appropriate callback. ```typescript app.post( '/webhooks/btcpay', express.raw({ type: 'application/json' }), BTCPayMiddleware.invoiceWebhook({ onSettled: payload => console.log('paid', payload.invoiceId), onExpired: payload => console.log('expired', payload.invoiceId), onInvalid: payload => console.log('invalid', payload.invoiceId), }) ); ``` -------------------------------- ### Minimal Invoice Creation Source: https://github.com/diegopherlt/btcpay-middleware/blob/main/docs/invoice-examples.mdx Use this for a basic checkout flow where only the amount and currency are required. It sets up a POST endpoint to handle invoice creation. ```typescript app.post( '/checkout', BTCPayMiddleware.createInvoice({ mapRequest: req => ({ amount: req.body.total, currency: 'USD', }), }), (_req, res) => res.json(res.locals.btcpayResponse) ); ``` -------------------------------- ### Declare Workspace Dependency in pnpm-workspace.yaml Source: https://github.com/diegopherlt/btcpay-middleware/blob/main/local-installation.md If your consumer project is in the same monorepo and included in `pnpm-workspace.yaml`, declare the package as a workspace dependency. Ensure the package is built (`pnpm run build`) before the consumer imports it. ```json "dependencies": { "@taloon/btcpay-middleware": "workspace:*" } ``` -------------------------------- ### Create a minimal auto-approved payout Source: https://github.com/diegopherlt/btcpay-middleware/blob/main/docs/payout-examples.mdx Uses the createPayout middleware to automatically approve and process a payout on-chain. ```ts app.post( '/withdrawals', BTCPayMiddleware.createPayout({ mapRequest: req => ({ destination: req.body.walletAddress, amount: req.body.amount, payoutMethodId: 'BTC-CHAIN', }), }), (_req, res) => res.json(res.locals.btcpayResponse) ); ``` -------------------------------- ### Define CreateInvoiceMiddlewareOptions Source: https://github.com/diegopherlt/btcpay-middleware/blob/main/docs/api-reference.mdx Configuration interface for the createInvoice middleware, requiring a mapRequest function to define the invoice payload. ```ts interface CreateInvoiceMiddlewareOptions { mapRequest: (req: Request, res: Response) => CreateInvoiceRequest; transformResponse?: (invoice: BTCPayInvoice) => unknown; onError?: ErrorHandler; } ``` -------------------------------- ### Create a manual approval payout Source: https://github.com/diegopherlt/btcpay-middleware/blob/main/docs/payout-examples.mdx Sets approved to false to hold the payout in AwaitingApproval status until manually reviewed. ```ts BTCPayMiddleware.createPayout({ mapRequest: req => ({ destination: req.body.wallet, amount: req.body.amount, payoutMethodId: 'BTC-CHAIN', approved: false, }), }); ``` -------------------------------- ### Async Handlers for Webhooks Source: https://github.com/diegopherlt/btcpay-middleware/blob/main/docs/webhook-examples.mdx Callbacks can return Promises, and the middleware will await them before sending a 200 OK response. This ensures BTCPay considers the delivery successful only after your asynchronous operations are complete. ```typescript BTCPayMiddleware.invoiceWebhook({ onSettled: async payload => { await db.transaction(async trx => { await markOrderPaid(trx, payload.invoiceId); await sendReceipt(trx, payload.invoiceId); }); }, }); ``` -------------------------------- ### Create a Lightning payout Source: https://github.com/diegopherlt/btcpay-middleware/blob/main/docs/payout-examples.mdx Uses the BTC-LN payout method to process payments via BOLT11, LNURL, or Lightning address. ```ts BTCPayMiddleware.createPayout({ mapRequest: req => ({ destination: req.body.lnurlOrBolt11, amount: req.body.amount, payoutMethodId: 'BTC-LN', }), }); ``` -------------------------------- ### Define getPayout options Source: https://github.com/diegopherlt/btcpay-middleware/blob/main/docs/api-reference.mdx Configuration interface for the getPayout middleware, requiring a payoutId mapping function. ```ts interface GetPayoutMiddlewareOptions { mapRequest: (req: Request, res: Response) => string; // returns payoutId transformResponse?: (payout: BTCPayPayout) => unknown; onError?: ErrorHandler; } ``` -------------------------------- ### Implement Guard Clauses Source: https://github.com/diegopherlt/btcpay-middleware/blob/main/AGENTS.md Validates inputs at the entry point to ensure required parameters exist before executing main logic. ```ts if (!options.mapRequest) { throw new BTCPayValidationError('createInvoice: mapRequest function is required'); } ``` -------------------------------- ### Invoice Creation with Restricted Payment Methods Source: https://github.com/diegopherlt/btcpay-middleware/blob/main/docs/invoice-examples.mdx Configure the invoice checkout to only allow specific payment methods like BTC and Lightning Network. It also sets a default language and a redirect URL. ```typescript BTCPayMiddleware.createInvoice({ mapRequest: req => ({ amount: req.body.total, currency: 'USD', checkout: { paymentMethods: ['BTC', 'BTC-LightningNetwork'], redirectURL: `${process.env.APP_URL}/orders/${req.body.orderId}`, defaultLanguage: 'es-ES', }, }), }); ``` -------------------------------- ### Create BTCPay Invoice Endpoint Source: https://github.com/diegopherlt/btcpay-middleware/blob/main/README.md Set up an Express route to create a BTCPay invoice. This middleware maps request data to invoice parameters and transforms the BTCPay API response into a desired format for the client. ```typescript import express from 'express'; import { BTCPayMiddleware } from '@taloon/btcpay-middleware'; const app = express(); app.use(express.json()); app.post( '/checkout', BTCPayMiddleware.createInvoice({ mapRequest: req => ({ amount: req.body.total, currency: 'USD', orderId: req.body.orderId, buyerEmail: req.body.email, }), transformResponse: invoice => ({ checkoutUrl: invoice.checkoutLink, invoiceId: invoice.id, status: invoice.status, }), }), (_req, res) => res.json(res.locals.btcpayResponse) ); ``` -------------------------------- ### getInvoice(options) Source: https://github.com/diegopherlt/btcpay-middleware/blob/main/docs/api-reference.mdx Middleware to retrieve an existing invoice by its ID. ```APIDOC ## getInvoice(options) ### Description Reads an invoice from GET /api/v1/stores/{storeId}/invoices/{invoiceId} and stores the result on res.locals.btcpayResponse. ### Parameters - **options** (GetInvoiceMiddlewareOptions) - Required - Configuration object containing mapRequest (returns invoiceId), transformResponse, and onError handlers. ``` -------------------------------- ### Handle All Invoice Event Callbacks Source: https://github.com/diegopherlt/btcpay-middleware/blob/main/docs/webhook-examples.mdx Define handlers for various invoice events. Some events like `onReceivedPayment` may indicate a partial payment, while `onSettled` signifies a complete payment. ```typescript BTCPayMiddleware.invoiceWebhook({ onCreated: payload => analytics.invoiceCreated(payload), onReceivedPayment: payload => log('partial', payload), // payment seen, may not be enough onProcessing: payload => log('mempool', payload), // payment in mempool onSettled: payload => fulfilOrder(payload.invoiceId), onPaymentSettled: payload => log('payment-settled', payload), // sub-event per payment onExpired: payload => releaseHold(payload.invoiceId), onInvalid: payload => alertAccounting(payload), }); ``` -------------------------------- ### Webhook Endpoint Configuration Source: https://github.com/diegopherlt/btcpay-middleware/blob/main/AGENTS.md Configures an Express route to handle raw JSON webhook payloads from BTCPay. ```ts app.post( '/webhooks/btcpay', express.raw({ type: 'application/json' }), BTCPayMiddleware.invoiceWebhook({ onSettled, onExpired }) ); ``` -------------------------------- ### Define BTCPay Middleware Types Source: https://github.com/diegopherlt/btcpay-middleware/blob/main/docs/api-reference.mdx Enums and interfaces representing invoice, webhook, and payout data structures. ```ts enum InvoiceStatus { NEW = 'New', PROCESSING = 'Processing', EXPIRED = 'Expired', INVALID = 'Invalid', SETTLED = 'Settled', } enum WebhookEventType { INVOICE_CREATED = 'InvoiceCreated', INVOICE_RECEIVED_PAYMENT = 'InvoiceReceivedPayment', INVOICE_PROCESSING = 'InvoiceProcessing', INVOICE_SETTLED = 'InvoiceSettled', INVOICE_PAYMENT_SETTLED = 'InvoicePaymentSettled', INVOICE_EXPIRED = 'InvoiceExpired', INVOICE_INVALID = 'InvoiceInvalid', PAYOUT_CREATED = 'PayoutCreated', PAYOUT_APPROVED = 'PayoutApproved', PAYOUT_UPDATED = 'PayoutUpdated', } enum PayoutState { AWAITING_APPROVAL = 'AwaitingApproval', AWAITING_PAYMENT = 'AwaitingPayment', IN_PROGRESS = 'InProgress', COMPLETED = 'Completed', CANCELLED = 'Cancelled', } enum PayoutMethod { BTC_CHAIN = 'BTC-CHAIN', BTC_LN = 'BTC-LN', } interface BTCPayInvoice { id: string; storeId: string; amount: string; currency: string; status: InvoiceStatus; additionalStatus?: InvoiceAdditionalStatus; checkoutLink: string; monitoringExpiration: number; expirationTime: number; createdTime: number; metadata?: Record; } interface BTCPayWebhookPayload { deliveryId: string; webhookId: string; originalDeliveryId: string; isRedelivery: boolean; type: WebhookEventType; timestamp: number; storeId: string; invoiceId: string; metadata?: Record; } interface BTCPayPayout { id: string; revision: number; pullPaymentId?: string; date: string; destination: string; originalCurrency: string; originalAmount: string; payoutCurrency: string; payoutAmount?: string | null; payoutMethodId: PayoutMethod; state: PayoutState; paymentProof?: PayoutPaymentProof | null; metadata?: Record; } interface BTCPayPayoutWebhookPayload { deliveryId: string; webhookId: string; originalDeliveryId: string; isRedelivery: boolean; type: WebhookEventType; timestamp: number; storeId: string; payoutId: string; payoutState?: PayoutState; [key: string]: unknown; // permissive — see "payload shape note" above } ``` -------------------------------- ### Handle BTCPay Webhook Source: https://github.com/diegopherlt/btcpay-middleware/blob/main/README.md Implement an Express route to receive and validate BTCPay webhooks. This middleware verifies the HMAC signature and dispatches events like 'paid' or 'expired' to their respective handlers. ```typescript // HMAC-validated webhook — note the raw body parser app.post( '/webhooks/btcpay', express.raw({ type: 'application/json' }), BTCPayMiddleware.invoiceWebhook({ onSettled: payload => console.log('paid', payload.invoiceId), onExpired: payload => console.log('expired', payload.invoiceId), }) ); app.listen(3000); ``` -------------------------------- ### Configure Webhook Secret Source: https://github.com/diegopherlt/btcpay-middleware/blob/main/docs/webhook-examples.mdx Configure the webhook secret in BTCPayMiddleware to enable signature verification. When set, mismatches will result in a 401 error. Skipping this is only suitable for local development. ```typescript BTCPayMiddleware.configure({ webhookSecret: process.env.BTCPAY_WEBHOOK_SECRET, }); ``` -------------------------------- ### Listen to payout webhook events Source: https://github.com/diegopherlt/btcpay-middleware/blob/main/docs/payout-examples.mdx Handles PayoutCreated, PayoutApproved, and PayoutUpdated events to update internal application state. ```ts import express from 'express'; import { BTCPayMiddleware, BTCPayPayoutWebhookPayload, PayoutState, } from '@taloon/btcpay-middleware'; app.post( '/webhooks/btcpay/payouts', express.raw({ type: 'application/json' }), BTCPayMiddleware.payoutWebhook({ onPayoutCreated: async payload => { await db.payouts.markCreated(payload.payoutId, payload.metadata); }, onPayoutApproved: async payload => { await db.payouts.markApproved(payload.payoutId); }, onPayoutUpdated: async (payload: BTCPayPayoutWebhookPayload) => { switch (payload.payoutState) { case PayoutState.IN_PROGRESS: await db.payouts.markBroadcasting(payload.payoutId); return; case PayoutState.COMPLETED: await db.payouts.markCompleted(payload.payoutId); return; case PayoutState.CANCELLED: await db.payouts.markCancelled(payload.payoutId); return; } }, }) ); ``` -------------------------------- ### Mount Invoice Webhook Route Source: https://github.com/diegopherlt/btcpay-middleware/blob/main/docs/webhook-examples.mdx Mount the invoice webhook route using express. The body must be a raw Buffer for correct HMAC computation. This parser should only be applied to the webhook route. ```typescript import express from 'express'; import { BTCPayMiddleware } from '@taloon/btcpay-middleware'; const app = express(); // Route-scoped raw parser. Don't apply this to the rest of your API. app.post( '/webhooks/btcpay', express.raw({ type: 'application/json' }), BTCPayMiddleware.invoiceWebhook({ onSettled: payload => markPaid(payload.invoiceId), onExpired: payload => markExpired(payload.invoiceId), onInvalid: payload => markInvalid(payload.invoiceId), }) ); ``` -------------------------------- ### Configure Path Aliases Source: https://github.com/diegopherlt/btcpay-middleware/blob/main/AGENTS.md Defines module path mappings in tsconfig.json to simplify imports, requiring tsc-alias for build output. ```json "paths": { "@/*": ["./*"], "@/types": ["./types"], "@/config": ["./config"], "@/client": ["./client"], "@/middlewares": ["./middlewares"], "@/utils": ["./utils"], "@/constants": ["./constants"] } ``` -------------------------------- ### Define GetInvoiceMiddlewareOptions Source: https://github.com/diegopherlt/btcpay-middleware/blob/main/docs/api-reference.mdx Configuration interface for the getInvoice middleware, requiring a mapRequest function that returns the invoiceId. ```ts interface GetInvoiceMiddlewareOptions { mapRequest: (req: Request, res: Response) => string; // returns invoiceId transformResponse?: (invoice: BTCPayInvoice) => unknown; onError?: ErrorHandler; } ``` -------------------------------- ### Create Higher-Order Middleware Factory Source: https://github.com/diegopherlt/btcpay-middleware/blob/main/AGENTS.md Implements a factory pattern that caches a singleton middleware instance per process. ```ts export const createInvoice = ( options: CreateInvoiceMiddlewareOptions ): ExpressMiddleware => getCreateInvoiceMiddleware().create(options); ``` -------------------------------- ### Import BTCPay Error Classes Source: https://github.com/diegopherlt/btcpay-middleware/blob/main/docs/error-handling.mdx Imports all specific error classes that extend BTCPayError. These are used for type checking and handling different error scenarios. ```typescript import { BTCPayError, BTCPayApiError, BTCPayConfigError, BTCPayValidationError, BTCPayNetworkError, BTCPayWebhookSignatureError, } from '@taloon/btcpay-middleware'; ``` -------------------------------- ### Test Invoice Settlement with Supertest Source: https://github.com/diegopherlt/btcpay-middleware/blob/main/docs/webhook-examples.mdx Test webhook handling by simulating an `InvoiceSettled` event using supertest. This verifies that the middleware correctly processes the incoming request and signature. ```typescript import { createHmac } from 'crypto'; import request from 'supertest'; it('settles an invoice on InvoiceSettled', async () => { const body = JSON.stringify({ type: 'InvoiceSettled', invoiceId: 'inv-1', storeId: 'store-1', }); const sig = `sha256=${createHmac('sha256', process.env.BTCPAY_WEBHOOK_SECRET!) .update(body) .digest('hex')}`; await request(app) .post('/webhooks/btcpay') .set('content-type', 'application/json') .set('btcpay-sig', sig) .send(body) .expect(200); }); ``` -------------------------------- ### Reset BTCPayMiddleware configuration between tests Source: https://github.com/diegopherlt/btcpay-middleware/blob/main/docs/configuration-examples.mdx Reset the middleware configuration to re-read environment variables and clear any applied overrides. This is useful for ensuring test isolation. ```typescript import { BTCPayConfiguration } from '@taloon/btcpay-middleware'; afterEach(() => { BTCPayConfiguration.reset(); // re-reads env vars and clears overrides }); ``` -------------------------------- ### Define CreateInvoiceRequest Source: https://github.com/diegopherlt/btcpay-middleware/blob/main/docs/api-reference.mdx Structure of the invoice request body, where amount and currency are mandatory fields. ```ts interface CreateInvoiceRequest { amount: number | string; currency: string; orderId?: string; buyerEmail?: string; description?: string; checkout?: InvoiceCheckoutOptions; metadata?: Record; } ``` -------------------------------- ### Reconcile invoice state with getInvoice middleware Source: https://github.com/diegopherlt/btcpay-middleware/blob/main/docs/quick-start.mdx Use the `getInvoice` middleware to retrieve the current state of an invoice. This is useful for cron jobs, support tooling, or polling. ```typescript app.get( '/invoices/:id', BTCPayMiddleware.getInvoice({ mapRequest: req => req.params.id, }), (_req, res) => res.json(res.locals.btcpayResponse) ); ``` -------------------------------- ### Idempotency Key for Webhook Deliveries Source: https://github.com/diegopherlt/btcpay-middleware/blob/main/docs/webhook-examples.mdx Use `originalDeliveryId` combined with `invoiceId` and `type` as an idempotency key to handle BTCPay's delivery retries. This prevents duplicate processing of webhook events. ```typescript const handled = new Set(); BTCPayMiddleware.invoiceWebhook({ onSettled: async payload => { const key = `${payload.invoiceId}:${payload.type}:${payload.originalDeliveryId}`; if (handled.has(key)) return; handled.add(key); await fulfilOrder(payload.invoiceId); }, }); ``` -------------------------------- ### Enable Direct Error Handling Mode Source: https://github.com/diegopherlt/btcpay-middleware/blob/main/docs/error-handling.mdx Configures the middleware to use 'direct' error handling mode. In this mode, the middleware itself writes the JSON error body. ```typescript BTCPayMiddleware.configure({ errorHandling: 'direct' }); ``` -------------------------------- ### verifyWebhookSignature(rawBody, header, secret) Source: https://github.com/diegopherlt/btcpay-middleware/blob/main/docs/api-reference.mdx Verifies the webhook signature provided in the request headers. ```APIDOC ## verifyWebhookSignature(rawBody, header, secret) ### Description Validates the signature of an incoming webhook request. Throws a `BTCPayWebhookSignatureError` if the signature does not match. ### Parameters - **rawBody** (string) - The raw request body content. - **header** (string) - The signature value extracted from the request header. - **secret** (string) - The shared secret key used for verification. ### Errors - **BTCPayWebhookSignatureError** - Thrown when the provided signature does not match the computed signature. ``` -------------------------------- ### Centralized error handler for BTCPayMiddleware Source: https://github.com/diegopherlt/btcpay-middleware/blob/main/docs/configuration-examples.mdx Implement a single observability hook for all middleware errors. This handler logs specific BTCPayError details before passing the error to the next handler. ```typescript import { BTCPayError } from '@taloon/btcpay-middleware'; BTCPayMiddleware.configure({ onError: (err, req, res, next) => { if (err instanceof BTCPayError) { logger.warn('btcpay', { code: err.code, status: err.statusCode, path: req.path }); } next(err); }, }); ``` -------------------------------- ### Custom Invoice Response Handling Source: https://github.com/diegopherlt/btcpay-middleware/blob/main/docs/invoice-examples.mdx This snippet illustrates how to bypass the default `transformResponse` to gain full control over the HTTP response. It manually redirects the user to the invoice checkout link. ```typescript import { BTCPayInvoice } from '@taloon/btcpay-middleware'; app.post( '/checkout', BTCPayMiddleware.createInvoice({ mapRequest: req => ({ amount: req.body.total, currency: 'USD' }), }), (_req, res) => { const invoice = res.locals.btcpayResponse as BTCPayInvoice; return res.redirect(303, invoice.checkoutLink); } ); ``` -------------------------------- ### Stamp metadata for reconciliation Source: https://github.com/diegopherlt/btcpay-middleware/blob/main/docs/payout-examples.mdx Attaches custom metadata to a payout to correlate it with internal database records via webhooks. ```ts BTCPayMiddleware.createPayout({ mapRequest: req => ({ destination: req.user.btcAddress, amount: req.body.amount, payoutMethodId: 'BTC-CHAIN', metadata: { userId: req.user.id, referralMilestoneId: req.body.milestoneId, source: 'referral-payout', }, }), transformResponse: payout => ({ payoutId: payout.id, state: payout.state, }), }); ``` -------------------------------- ### Poll a payout for reconciliation Source: https://github.com/diegopherlt/btcpay-middleware/blob/main/docs/payout-examples.mdx Uses the BTCPayClient to manually fetch and check the status of a specific payout. ```ts import { BTCPayClient, BTCPayConfiguration, PayoutState } from '@taloon/btcpay-middleware'; const client = BTCPayClient.fromConfig(BTCPayConfiguration.getConfig()); export async function reconcile(payoutId: string) { const payout = await client.getPayout(payoutId); switch (payout.state) { case PayoutState.COMPLETED: return 'paid'; case PayoutState.CANCELLED: return 'cancelled'; case PayoutState.AWAITING_APPROVAL: return 'pending-approval'; default: return 'in-flight'; } } ``` -------------------------------- ### Define Error Hierarchy Source: https://github.com/diegopherlt/btcpay-middleware/blob/main/AGENTS.md Visual representation of the custom error class structure used for handling API, configuration, and network failures. ```text BTCPayError ├── BTCPayApiError // 4xx / 5xx from BTCPay (statusCode set) ├── BTCPayConfigError // missing/invalid config ├── BTCPayValidationError // bad caller input ├── BTCPayNetworkError // axios network failure └── BTCPayWebhookSignatureError // HMAC mismatch (statusCode 401) ``` -------------------------------- ### Override Error Handler Per Route Source: https://github.com/diegopherlt/btcpay-middleware/blob/main/docs/error-handling.mdx Demonstrates how to provide a route-specific onError handler for the createInvoice middleware. This handler bypasses the global handler and sends a custom JSON response. ```typescript app.post( '/checkout', BTCPayMiddleware.createInvoice({ mapRequest: req => ({ amount: req.body.total, currency: 'USD' }), onError: (err, _req, res, _next) => { // Route-specific shape — bypass the global handler res.status(502).json({ error: 'gateway_unavailable' }); }, }) ); ``` -------------------------------- ### Manual Webhook Signature Verification Source: https://github.com/diegopherlt/btcpay-middleware/blob/main/docs/webhook-examples.mdx Manually verify webhook signatures outside of Express, such as in a queue worker. This function throws `BTCPayWebhookSignatureError` on mismatch. ```typescript import { verifyWebhookSignature, WEBHOOK_SIGNATURE_HEADER, } from '@taloon/btcpay-middleware'; verifyWebhookSignature( rawBuffer, request.headers[WEBHOOK_SIGNATURE_HEADER] as string, process.env.BTCPAY_WEBHOOK_SECRET! ); ``` -------------------------------- ### computeSignature(rawBody, secret) Source: https://github.com/diegopherlt/btcpay-middleware/blob/main/docs/api-reference.mdx Computes the hex SHA-256 HMAC signature for a given request body and secret. ```APIDOC ## computeSignature(rawBody, secret) ### Description Calculates the hex SHA-256 HMAC signature, typically used for generating signatures for webhook verification. ### Parameters - **rawBody** (string) - The raw request body content. - **secret** (string) - The shared secret key used for HMAC calculation. ### Returns - **string** - The computed hex SHA-256 HMAC signature. ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.