Skip to main content

Quick start

A minimal payment integration: creating an invoice via the API and handling the payment notification. The final section covers order fulfillment. The examples are self-contained; no SDK is used.

tip

Working with an AI agent? Give it the markdown version of this page. It has enough detail to build an integration for your site or store.

Prerequisites

ValueWhere to get it
API keyThe Integrations section
Project IDThe Projects section
Webhook secretProject settings

In the same project settings, set the Webhook URL—the address of the notification handler on your server. HTTPS only; redirects are not followed.

The examples use test values—replace them with your own.

Creating an invoice

POST https://api.bitsby.app/invoices/create, the API key is passed in the Authorization header.

ParameterRequiredValue
projectIdyesProject ID, UUID
amountFiatyesInvoice amount
currencyFiatyesCurrency: USD, EUR, RUB
timeToPayyesPayment deadline in hours: 0.5, 1, 3, 6, 12
descriptionnoDescription, shown to the customer on the payment page
serviceDatanoService data, not shown to the customer. We recommend passing the order number: it comes back in the payment notification
curl -X POST https://api.bitsby.app/invoices/create \
-H "Authorization: Token MSvL2ltaDZdWVjmZURURMVWhqSJLT2NURjhL2Fla1Z1T1IxQTltKs1T3Ay" \
-F "projectId=9deea1e2-0c08-41a3-bdc2-a34eada3892d" \
-F "amountFiat=49.90" \
-F "currencyFiat=USD" \
-F "timeToPay=1" \
-F "description=Order 4172" \
-F "serviceData=order-4172"

Response:

{
"result": "success",
"data": {
"id": "ade9550d-3dc7-4fd3-b94e-3b4c12aaaa0c",
"uid": "MXNj4m8HhcM4",
"createDatetime": "2026-09-14 10:12:03",
"timeToPayDatetime": "2026-09-14 11:12:03",
"commissionFiatUSD": 0.5,
"amountFiatUSD": 49.9,
"url": "https://dash.bitsby.app/invoices/pay/MXNj4m8HhcM4"
}
}

Store data.id with the order and send the customer to data.url—the payment page. Deliver the link any way you like: a redirect, an email, a bot message. The invoice is valid until timeToPayDatetime (UTC).

Handling the notification

When an invoice moves to Paid, the service sends a POST request to the project's Webhook URL. The body is JSON; the signature is in the headers:

HeaderValue
X-TimestampSend time, unix time in seconds
X-Signaturesha256= + hex HMAC-SHA256 of the string <timestamp>.<request body>

The signing key is the webhook secret. The signature is computed over the raw request body, so verify it before parsing the JSON.

<?php
$secret = 'k7QwR2mZ9tXbN4vL8sJpH3dF6yA1cE0u';

$body = file_get_contents('php://input');
$timestamp = $_SERVER['HTTP_X_TIMESTAMP'] ?? '';
$signature = $_SERVER['HTTP_X_SIGNATURE'] ?? '';

// Reject replayed requests: allow up to 5 minutes of clock drift
if (abs(time() - (int)$timestamp) > 300) {
http_response_code(400);
exit;
}

$expected = 'sha256='.hash_hmac('sha256', $timestamp.'.'.$body, $secret);

if (!hash_equals($expected, $signature)) {
http_response_code(403);
exit;
}

$invoice = json_decode($body, true)['invoice'];

if ($invoice['status'] === 'paid') {
// $invoice['serviceData'] — the order id passed at creation: 'order-4172'
// $invoice['amountFiat'] — the original invoice amount: 49.9
// Issue the order here, see the next section
}

http_response_code(200);

Respond with a 2xx code within 10 seconds. Any other code, a redirect, or a timeout counts as a failed delivery: the service retries at increasing intervals from 5 minutes up to 24 hours, then stops. Offload long processing to a queue: respond with 200 first, then work on the order.

The full notification format and field reference are in the Webhook URL section.

Order fulfillment

A verified notification with status paid confirms the payment. Fulfillment steps:

  1. Find the order by invoice.serviceData—the value passed at invoice creation (order-4172).
  2. Check by invoice.id whether this invoice has already been fulfilled. A notification with the same invoice.id can arrive more than once—fulfill the order once and store a processed flag.
  3. Verify the amount and currency against invoice.amountFiat and invoice.currencyFiat—these are the original invoice values and never change. amountFiatUSD is overwritten with the amount actually received. Compare amounts as numbers, not strings: trailing zeros are dropped, so 49.90 arrives as 49.9. If the values don't match the order, route the invoice to manual review instead of fulfillment—such cases are covered in Payment matching and amount mismatches.
  4. Fulfill the order and mark it as processed.

Do not check the payment deadline at fulfillment: the payment may have confirmed on-chain after timeToPayDatetime, and the merchant can match a payment to an invoice manually. The notification itself confirms the payment.

What's next