Skip to main content

Quickstart

Accept your first Bitcoin payment in 15 minutes. This guide walks you through the exact flow your integration will use in production.

What you'll build

1. Get API key → 2. Create invoice → 3. Show QR code

5. Handle webhook ← 4. Customer pays ←─────────┘

Prerequisites

  • A Coinsnap account with a store created
  • Your Store ID and API Key from the dashboard

Step 1 — Get your API key

  1. Log in to app.coinsnap.io
  2. Go to Settings → Store
  3. Scroll to the API Keys section, enter a label, and click Create API key
  4. Copy the key from the table

Your Store ID is on the same page, or in the URL when viewing your store.

Your credentials
Store ID: 7CVKXVxM7BtbkMEie8yoNeR8EetExpQhJUYEFY3ftfwR
API Key: cs_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxx

Step 2 — Create an invoice

An invoice is a Bitcoin payment request. You create one for a specific amount, and Coinsnap generates a Lightning invoice and Bitcoin address for your customer.

const response = await fetch(
'https://app.coinsnap.io/api/v1/stores/YOUR_STORE_ID/invoices',
{
method: 'POST',
headers: {
'x-api-key': 'YOUR_API_KEY',
'Content-Type': 'application/json',
},
body: JSON.stringify({
amount: 10.00, // in your store's fiat currency (e.g. EUR)
currency: 'EUR',
orderId: 'order-123', // your internal order reference
buyerEmail: 'customer@example.com',
redirectUrl: 'https://yoursite.com/thank-you',
}),
}
);

const invoice = await response.json();
console.log(invoice.id); // inv_xxxxxxxxxxxx
console.log(invoice.checkoutLink); // https://app.coinsnap.io/i/...

Response

{
"id": "inv_4Kz9mXpQ2rNvBtYwLs8cDf",
"status": "New",
"amount": 10.00,
"currency": "EUR",
"checkoutLink": "https://app.coinsnap.io/i/4Kz9mXpQ2rNvBtYwLs8cDf",
"lightningInvoice": "lnbc100u1p...",
"onchainAddress": "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh",
"bip21": "bitcoin:bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh?amount=0.00010000",
"createdAt": 1705312800
}

Step 3 — Display the payment UI

The simplest approach is to redirect your customer to checkoutLink. Coinsnap hosts the payment page, including QR code display and real-time status updates.

// Redirect customer to the hosted checkout page
window.location.href = invoice.checkoutLink;

The page automatically handles:

  • Lightning QR code display
  • On-chain fallback
  • Real-time payment detection
  • Redirect to your redirectUrl after payment

Step 4 — Handle the webhook

When the customer pays, Coinsnap sends a POST request to your webhook endpoint. This is how you confirm payment server-side — never rely on the redirect alone.

First, register your endpoint in the dashboard:

  1. Go to Coinsnap Dashboard → Webhooks
  2. Click Add Webhook and enter your endpoint URL (e.g. https://yoursite.com/webhooks/coinsnap)
  3. Select the Settled event at minimum
  4. Click Save — copy the Signing Secret shown and store it as COINSNAP_WEBHOOK_SECRET

Then handle the request in your server:

server.js (Express)
import crypto from 'crypto';
import express from 'express';

const app = express();
app.use(express.raw({ type: 'application/json' }));

app.post('/webhooks/coinsnap', async (req, res) => {
const signature = req.headers['x-coinsnap-sig'] ?? '';
const expected = 'sha256=' + crypto
.createHmac('sha256', process.env.COINSNAP_WEBHOOK_SECRET)
.update(req.body)
.digest('hex');

const sigBuf = Buffer.from(signature.padEnd(expected.length));
const expBuf = Buffer.from(expected);
if (sigBuf.length !== expBuf.length || !crypto.timingSafeEqual(sigBuf, expBuf)) {
return res.status(401).send('Invalid signature');
}

const event = JSON.parse(req.body);

if (event.type === 'Settled') {
const invoiceId = event.invoiceId;
const orderId = event.metadata?.orderId;
await markOrderAsPaid(orderId, invoiceId);
}

res.status(200).send('OK');
});
webhook.php
<?php
$payload = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_COINSNAP_SIG'] ?? '';
$secret = getenv('COINSNAP_WEBHOOK_SECRET');

if (!hash_equals('sha256=' . hash_hmac('sha256', $payload, $secret), $signature)) {
http_response_code(401);
exit('Invalid signature');
}

$event = json_decode($payload, true);

if ($event['type'] === 'Settled') {
$invoiceId = $event['invoiceId'];
$orderId = $event['metadata']['orderId'] ?? null;
// mark order as paid in your DB
}

http_response_code(200);
echo 'OK';

Step 5 — Test it end to end

  1. Start your server and make sure your webhook endpoint is reachable (use ngrok or npx cloudflared tunnel --url http://localhost:3000 if testing locally)
  2. Create a test invoice through your flow and complete the payment using a Lightning wallet (e.g. Phoenix, Breez)
  3. Check your server logs — you should see the Settled webhook arrive
  4. Verify your order status updated in the database

If the webhook doesn't arrive:

  • Check Dashboard → Webhooks → [your webhook] → Deliveries for delivery attempts and error responses
  • Make sure your server is returning 200 OK
  • Confirm the signing secret is correct and you're reading the raw request body (not parsed JSON)

You're done

Your integration covers:

  • Create an invoice for any fiat amount
  • Redirect customers to the hosted payment page
  • Verify payments server-side via signed webhooks

Next steps