Custom Website Integration

Any website — PHP, Node.js, Python, Ruby, .NET, static + serverless — can accept Bitcoin (and multi-coin) payments through the Jamesinvoicing REST API. Create an invoice from your server, redirect the customer to our hosted payment page, and receive a signed webhook when payment confirms.

How it works

  1. Sign up at jamesinvoicing.com/auth and add a verified xPub or approved receiving address.
  2. Generate an API key in Dashboard → API Keys and store the key + webhook secret as environment variables on your server.
  3. From your checkout, call POST /merchant/generate-address to create an invoice.
  4. Redirect the customer to the returned pay_url.
  5. Receive a signed webhook at your endpoint when the invoice becomes Paid.

Base URL: https://jamesinvoicing.com/api/public/v1

1. Create an invoice & redirect the customer

The response includes payment_id, pay_url, all offered coin addresses, and expires_at. Send the customer to pay_url.

curl -X POST https://jamesinvoicing.com/api/public/v1/merchant/generate-address \
  -H "Authorization: Bearer pch_live_xxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "order_number": "ORD-1001",
    "customer_email": "customer@example.com",
    "customer_name": "Jane Doe",
    "description": "Pro plan — 1 month",
    "amount_usd": 49.00,
    "amount_btc": 0.00072,
    "btc_rate": 68000,
    "expires_in_minutes": 60
  }'

2. Check invoice status (optional)

Webhooks are the recommended source of truth, but you can poll the invoice status by payment_id at any time.

curl https://jamesinvoicing.com/api/public/v1/merchant/invoice-status/PAYMENT_ID \
  -H "Authorization: Bearer pch_live_xxxxxxxxxxxxxxxxxxxxxxxxx"

3. Receive & verify webhooks

Every webhook is signed with HMAC-SHA256 over the raw request body using your webhook_secret. The signature is sent in the X-PCH-Signature header. Reject any request whose signature does not match.

<?php
// webhook.php — set this URL in Jamesinvoicing → Settings → Webhooks
$secret = getenv('PCH_WEBHOOK_SECRET');
$raw    = file_get_contents('php://input');
$sig    = $_SERVER['HTTP_X_PCH_SIGNATURE'] ?? '';

$expected = hash_hmac('sha256', $raw, $secret);
if (!hash_equals($expected, $sig)) {
  http_response_code(401);
  exit('invalid signature');
}

$event = json_decode($raw, true);
// $event['type']  => 'invoice.paid' | 'invoice.confirming' | 'invoice.expired' | ...
// $event['data']  => full invoice object

if ($event['type'] === 'invoice.paid') {
  markOrderPaid($event['data']['order_number']);
}
http_response_code(200);
echo 'ok';

Event types & recommended order status

  • invoice.confirming — payment detected on-chain. Flip your order to Processing and start fulfilment.
  • invoice.paid — required confirmations reached. Flip your order to Completed.
  • invoice.expired — customer did not pay before expires_at. Mark as Cancelled / Failed.
  • invoice.cancelled — invoice cancelled by the merchant. Mark as Cancelled.

Same behaviour as our WordPress plugin: detected → Processing, confirmed → Completed. Never mark an order paid on invoice.confirming alone if you need on-chain finality — wait for invoice.paid.

Security checklist

  • Store pch_live_… keys and PCH_WEBHOOK_SECRET as environment variables — never in client code or git.
  • Always verify X-PCH-Signature before trusting a webhook payload.
  • Use hash_equals / timingSafeEqual / hmac.compare_digest — not ==.
  • Treat order_number as the join key back to your own order table.
  • Serve your webhook over HTTPS only.

Full endpoint reference lives in the API reference.