Skip to content

Docs — integrate in 60 seconds

Everything you need to accept Bitcoin through LibrePay: hosted checkout, WooCommerce, one HTML snippet, or a clean REST API with signed webhooks. All amounts settle directly to your wallet — or instantly to your own Lightning node.

1 · Supported platforms

LibrePay meets your stack where it is: a hosted link for DMs and email, an HTML button for static sites, a WooCommerce plugin for WordPress, and a first-class REST API for everything else. All rails end in the same place — your wallet.

Hosted checkout link

Email, DMs, social bios, PDF invoices

HTML snippet

Static sites, landing pages, WordPress (no backend)

WooCommerce

WordPress stores — orders & statuses automated

REST API + webhooks

Any stack: SaaS, apps, POS, custom checkouts

2 · Hosted checkout — zero code

Create an invoice in the dashboard (or via API), share the /pay/<id> link or its QR anywhere — Instagram bio, invoice PDF, WhatsApp, email. The page is public-by-link, mobile-first, language-aware and branded with your store name and color.

3 · HTML snippet — one line

Drop the snippet into any site (WordPress, Shopify page, static site). The button opens the hosted checkout in a popup and works with zero backend.

<!-- LibrePay payment button -->
<script src="https://your-domain.com/librepay.js"></script>
<button
  class="librepay-button"
  data-amount-sats="42000"
  data-order-id="order-1042"
  data-description="Cairo Coffee">
  Pay 42,000 sats
</button>

4 · WooCommerce (WordPress)

Official plugin — orders, statuses and webhooks handled for you.

v1.0.0 · WC 7.0+ · HPOS compatible · PHP 7.4+
  1. In WordPress: Plugins → Add New → Upload Plugin → choose the zip → Install → Activate.
  2. WooCommerce → Settings → Payments → “Bitcoin (LibrePay)” → Manage.
  3. Paste your API key (Dashboard → API Keys) and your webhook secret. Enable only if your site runs HTTPS.
  4. Choose when orders are marked paid: on first confirmation (faster delivery) or on settlement (maximum safety).
  5. Test with a small live order. The order note records the invoice id and payment txid for audit.

Your webhook URL (paste it in LibrePay → Dashboard → Webhooks, then copy the secret into the plugin settings):

https://your-store.com/wc-api/librepay_webhook

5 · REST API

Create an API key in Dashboard → API Keys, then call the v1 API server-side. Raw keys are shown once — store them safely.

curl -X POST https://your-domain.com/api/v1/invoices \
  -H "Authorization: Bearer lp_live_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "amountSats": 42000,
    "orderId": "order-1042",
    "description": "Cairo Coffee — 3 bags",
    "expiresInMinutes": 15,
    "metadata": { "customer_ref": "c-88" }
  }'

6 · Webhooks

Add an endpoint in Dashboard → Webhooks. LibrePay signs every delivery with HMAC-SHA256 using the endpoint secret shown once at creation. Verify before trusting:

import crypto from "node:crypto";

export function verifyWebhook(secret: string, rawBody: string, header: string) {
  const expected = "sha256=" + crypto.createHmac("sha256", secret).update(rawBody).digest("hex");
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(header || ""));
}

Example delivery:

POST https://your-server.com/webhooks/librepay
X-LibrePay-Event: invoice.confirmed
X-LibrePay-Signature: sha256=7f2a…(HMAC-SHA256 of raw body with endpoint secret)

{
  "event": "invoice.confirmed",
  "invoiceId": "inv_x7Kd2mPq9L",
  "timestamp": 1737000000000,
  "data": {
    "invoice": {
      "id": "inv_x7Kd2mPq9L",
      "status": "confirmed",
      "amountSats": "42000",
      "receivedSats": "42000",
      "txid": "a1b2c3…",
      "confirmations": 2,
      "orderId": "order-1042"
    }
  }
}
waitingInvoice created, one-time stealth address derived (+ bolt11 if the merchant runs a Phoenixd node). Waiting for buyer.
detectedOn-chain transaction seen in mempool (0 confirmations). Webhook: invoice.detected.
confirmedReached the merchant-configured N confirmations (default 2). Webhook: invoice.confirmed.
settledOn-chain: deep confirmation (≥6, or N+4). Lightning: INSTANT, no blocks. Webhook: invoice.settled (data.paymentMethod = onchain | lightning).
expired15-minute window passed with no payment (default, configurable 5–120 min).
underpaidConfirmed transaction paid less than the requested amount. Webhook: invoice.underpaid.
refund.requestedBuyer asked for a refund on the invoice page. Webhook: refund.requested.
refundingMerchant approved — refund amount locked, send guide displayed in the dashboard.
refundedMerchant sent the refund from THEIR wallet and pasted the txid. Webhook: refund.completed.
declinedMerchant refused the request (optional reason shown to the buyer). Webhook: refund.declined.

Failed deliveries retry with exponential backoff (1m, 5m, 15m, 1h, 6h, 24h — 7 attempts total), then park as dead in the delivery log.

7 · Lightning (Phoenixd) in 5 steps

Connect YOUR own Phoenixd node (ACINQ's minimal Lightning server) and every new invoice gets a second ⚡ tab: the buyer can pay on-chain or instantly over Lightning. Your keys, your channels, your node — the same non-custodial promise.

  1. Rent any small VPS and open TCP 9740 (phoenixd's API port) — or keep it private and front it with a reverse proxy.
  2. Download phoenixd from ACINQ's GitHub releases, unzip, and run it once to generate your node keys and http-password.
  3. Put phoenixd behind HTTPS (Caddy: two lines of config with an automatic certificate) and note the URL.
  4. Dashboard → Settings → Lightning: paste the URL and the http-password, press “Connect & test node”.
  5. Done. Every new invoice now shows the ⚡ Lightning tab; funds arrive to YOUR node in seconds.
Caddy reverse proxy (one file, automatic HTTPS):
phoenix.mystore.com {
  reverse_proxy 127.0.0.1:9740
}
Then paste https://phoenix.mystore.com + the http-password in Dashboard → Settings → Lightning. We encrypt the password (AES-256-GCM) and only call invoice/create, invoice/lookup and getinfo — never the spending endpoint.

8 · Receiving & spending (self-custody)

Your browser generates a BIP39 seed (12 or 24 words) and derives a BIP47-style payment code. Only PUBLIC keys reach our servers, so we can derive one-time stealth addresses for invoices but can never spend. To spend received funds, open Dashboard → Wallet, unlock, and use Export spending keys — it computes per-invoice WIF keys locally (ECDH stealth reveal) for import into Electrum, Sparrow, or any wallet. Prefer external wallets? Use Watch-only mode with an xpub/zpub instead.

Chain watching: our cron polls mempool.space Esplora for each open invoice address. No node required, no address reuse, no KYC. Confirmations policy is merchant-configurable (1–6, default 2).

9 · Refunds — manual by design

LibrePay cannot refund programmatically — and that is the point: if we could push a refund, we could also push a withdrawal. When a buyer requests a refund on the invoice page, you approve it in Dashboard → Invoices, the amount is locked, and a send guide shows the destination address, the exact sats and a QR. You send from your wallet and paste the txid — the buyer then sees a Refunded badge with an on-chain link. Configure refunds (on/off, request window) in Dashboard → Settings. Tip: prefer the buyer's fresh address over the payment-origin address — exchanges may not credit refunds.

Pricing, for transparency: plans are fixed in USD and paid in sats computed live (sats = USD ÷ BTC_USD × 10⁸), locked at checkout with the rate snapshot stored for audit. Price source chain: mempool.space → CoinGecko → blockchain.info — the live source is shown on the pricing page.