Automatic POSTBACK
The method allows you to automatically receive notifications about invoice confirmation.
Last updated
Was this helpful?
Was this helpful?
{
"status": "success",
"invoice_id": "ILRAJE1Q",
"amount_crypto": 0.000113,
"currency": "BNB",
"order_id": null,
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJ<...>hPirF9kHCx3ZXEspVfGNGuK7cZ8",
"invoice_info": {
"uuid": "INV-ILRAJE1Q",
"created": "2026-01-27 10:20:04.222439",
"address": "0x635DBa9c6e5E9<...>6f8Bed2Ec029fc555af05d2",
"currency": {
"id": 20,
"code": "BNB",
"fullcode": "BNB",
"network": {
"code": "BSC",
"id": 20,
"icon": "https://cdn.trybit.com/img/network/BSC.svg",
"fullname": "BNB Smart Chain"
},
"name": "BNB",
"is_email_required": false,
"stablecoin": false,
"icon_base": "https://cdn.trybit.com/img/currency/BNB.svg",
"icon_network": "https://cdn.trybit.com/img/currency/BNB.svg",
"icon_qr": "https://cdn.trybit.com/img/stroke/BNB_STROKE.svg",
"order": 13
},
"date_finished": "2026-01-01 10:23:14.000940",
"expiry_date": "2026-01-02 10:20:04.197389",
"side_commission": "client",
"type_payments": "crypto",
"amount": 0.000113,
"amount_": 0.000113,
"status": "paid",
"invoice_status": "success",
"is_email_required": false,
"project": {
"id": 1,
"name": "Test",
"fail": "https://test.com/failed-payment",
"success": "https://test.com/successful-payment",
"logo": ""
},
"tx_list": [
"0x12fb777c41b58b8304278416e5a<...>c1e7708b2160745af56403641522"
],
"amount_in_crypto": null,
"amount_in_fiat": 0.1,
"amount_usd": 0.1,
"amount_to_pay": 0.000113,
"amount_to_pay_usd": 0.1,
"amount_paid": 0.000113,
"amount_paid_usd": 0.1,
"fee": 0,
"fee_usd": 0,
"service_fee": 0.000002,
"service_fee_usd": 0,
"received": 0.000111,
"received_usd": 0.1,
"to_surcharge": 0,
"to_surcharge_usd": 0,
"total_rub": 0,
"step": 3,
"test_mode": false,
"type": "up",
"aml_enabled": false,
"aml_side": "merchant",
"aml_checks": [],
"links_invoice": null
}
}{
"status": "success",
"invoice_id": "ILRAJE1Q",
"amount_crypto": 0.000113,
"currency": "BNB",
"order_id": null,
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJ<...>hPirF9kHCx3ZXEspVfGNGuK7cZ8",
}from flask import Flask, request, jsonify
import os
import jwt # pip install PyJWT
app = Flask(__name__)
JWT_SECRET = os.getenv("JWT_SECRET", "super-secret")
JWT_ALGORITHMS = ["HS256"]
def validate_token(token: str) -> bool:
"""
Validate HS256 JWT only if token is present and not empty.
Token is expected to be a raw JWT string (no "Bearer " prefix).
"""
if token is None:
return True
if not isinstance(token, str):
return False
token = token.strip()
if token == "":
return True
try:
jwt.decode(token, JWT_SECRET, algorithms=JWT_ALGORITHMS)
return True
except jwt.PyJWTError:
return False
@app.route("/postback", methods=["POST"])
def handle_postback():
# 1) Read JSON body
data = request.get_json(silent=True)
if not isinstance(data, dict):
return jsonify({"error": "Invalid JSON"}), 400
# 2) Top-level fields
status = data.get("status")
invoice_id = data.get("invoice_id")
amount_crypto = data.get("amount_crypto")
currency = data.get("currency")
order_id = data.get("order_id")
token = data.get("token")
# Basic validation
if not invoice_id or not order_id:
return jsonify({"error": "Missing invoice_id or order_id"}), 400
# 3) Validate JWT token (only if provided)
if not validate_token(token):
return jsonify({"error": "Invalid token"}), 401
# 4) Nested invoice_info object
invoice_info = data.get("invoice_info") or {}
uuid = invoice_info.get("uuid")
created = invoice_info.get("created")
address = invoice_info.get("address")
invoice_status = invoice_info.get("invoice_status")
invoice_amount = invoice_info.get("amount")
amount_paid = invoice_info.get("amount_paid")
fee = invoice_info.get("fee")
# 5) Deeper nesting: invoice_info.currency.network
inv_currency = invoice_info.get("currency") or {}
network = inv_currency.get("network") or {}
network_code = network.get("code")
# 6) Project info
project = invoice_info.get("project") or {}
project_name = project.get("name")
# 7) Business logic should be implemented here:
# - idempotency by invoice_id / uuid
# - update invoice status in the database
# - amount / currency validation
# - logging (avoid logging token)
return jsonify({"message": "Postback received"}), 200
if __name__ == "__main__":
app.run(port=5000, debug=True)const express = require("express");
const jwt = require("jsonwebtoken");
const app = express();
app.use(express.json());
// HS256 secret (store in ENV in production)
const JWT_SECRET = process.env.JWT_SECRET || "super-secret";
/**
* Validate HS256 JWT token only if it is present and not empty.
* Token is expected to be a raw JWT string (no "Bearer " prefix).
*/
function validateToken(token) {
// Token is optional
if (token === undefined || token === null) return true;
if (typeof token !== "string") return false;
const t = token.trim();
if (t === "") return true;
try {
jwt.verify(t, JWT_SECRET, { algorithms: ["HS256"] });
return true;
} catch (err) {
return false;
}
}
app.post("/postback", (req, res) => {
// 1) Read JSON body
const data = req.body;
if (!data || typeof data !== "object" || Array.isArray(data)) {
return res.status(400).json({ error: "Invalid JSON" });
}
// 2) Basic required fields (adjust as needed)
const invoice_id = data.invoice_id;
const order_id = data.order_id;
if (!invoice_id || !order_id) {
return res.status(400).json({ error: "Missing invoice_id or order_id" });
}
// 3) Validate JWT token (only if provided)
const token = data.token;
if (!validateToken(token)) {
return res.status(401).json({ error: "Invalid token" });
}
// 4) Nested invoice_info object
const invoice_info = data.invoice_info || {};
const uuid = invoice_info.uuid;
const created = invoice_info.created;
const address = invoice_info.address;
const invoice_status = invoice_info.invoice_status;
const invoice_amount = invoice_info.amount;
const amount_paid = invoice_info.amount_paid;
const fee = invoice_info.fee;
// 5) Deeper nesting: invoice_info.currency.network
const inv_currency = invoice_info.currency || {};
const network = inv_currency.network || {};
const network_code = network.code;
// 6) Project info
const project = invoice_info.project || {};
const project_name = project.name;
// 7) Business logic goes here (examples):
// - idempotency by invoice_id / uuid
// - update invoice status in DB
// - validate amounts/currency
// - logging (avoid logging token)
//
// Example (pseudo):
// await db.invoices.upsert({ invoice_id, order_id, uuid, invoice_status, amount_paid, fee, network_code, project_name, created, address })
// 8) Minimal response (as requested)
return res.status(200).json({ message: "Postback received" });
});
app.listen(5000, () => {
console.log("Server running on http://127.0.0.1:5000");
});<?php
// postback.php
header('Content-Type: application/json; charset=utf-8');
$JWT_SECRET = getenv('JWT_SECRET') ?: 'super-secret';
/**
* Base64URL decode (JWT uses base64url without padding)
*/
function base64url_decode($data) {
$remainder = strlen($data) % 4;
if ($remainder) {
$data .= str_repeat('=', 4 - $remainder);
}
$data = strtr($data, '-_', '+/');
return base64_decode($data);
}
/**
* Validate HS256 JWT only if token is present and not empty.
* Token is expected to be a raw JWT string (no "Bearer " prefix).
*/
function validateToken($token, $secret) {
// Token is optional
if (!isset($token)) return true;
if (!is_string($token)) return false;
$t = trim($token);
if ($t === '') return true;
$parts = explode('.', $t);
if (count($parts) !== 3) return false;
[$headerB64, $payloadB64, $sigB64] = $parts;
$headerJson = base64url_decode($headerB64);
$payloadJson = base64url_decode($payloadB64);
if ($headerJson === false || $payloadJson === false) return false;
$header = json_decode($headerJson, true);
$payload = json_decode($payloadJson, true);
if (!is_array($header) || !is_array($payload)) return false;
// Ensure HS256
if (($header['alg'] ?? null) !== 'HS256') return false;
// Verify signature
$dataToSign = $headerB64 . '.' . $payloadB64;
$expectedSigRaw = hash_hmac('sha256', $dataToSign, $secret, true);
$sigRaw = base64url_decode($sigB64);
if ($sigRaw === false) return false;
if (!hash_equals($expectedSigRaw, $sigRaw)) return false;
// Optional: validate exp/nbf if present
$now = time();
if (isset($payload['nbf']) && is_numeric($payload['nbf']) && $now < (int)$payload['nbf']) {
return false;
}
if (isset($payload['exp']) && is_numeric($payload['exp']) && $now >= (int)$payload['exp']) {
return false;
}
return true;
}
// 1) Read raw JSON body
$raw = file_get_contents('php://input');
$data = json_decode($raw, true);
if (!is_array($data)) {
http_response_code(400);
echo json_encode(['error' => 'Invalid JSON'], JSON_UNESCAPED_UNICODE);
exit;
}
// 2) Basic required fields
$invoice_id = $data['invoice_id'] ?? null;
$order_id = $data['order_id'] ?? null;
if (!$invoice_id || !$order_id) {
http_response_code(400);
echo json_encode(['error' => 'Missing invoice_id or order_id'], JSON_UNESCAPED_UNICODE);
exit;
}
// 3) Validate JWT token (only if provided)
$token = $data['token'] ?? null;
if (!validateToken($token, $JWT_SECRET)) {
http_response_code(401);
echo json_encode(['error' => 'Invalid token'], JSON_UNESCAPED_UNICODE);
exit;
}
// 4) Nested invoice_info object
$invoice_info = $data['invoice_info'] ?? [];
$uuid = $invoice_info['uuid'] ?? null;
$created = $invoice_info['created'] ?? null;
$address = $invoice_info['address'] ?? null;
$invoice_status = $invoice_info['invoice_status'] ?? null;
$invoice_amount = $invoice_info['amount'] ?? null;
$amount_paid = $invoice_info['amount_paid'] ?? null;
$fee = $invoice_info['fee'] ?? null;
// 5) Deeper nesting: invoice_info.currency.network
$inv_currency = $invoice_info['currency'] ?? [];
$network = $inv_currency['network'] ?? [];
$network_code = $network['code'] ?? null;
// 6) Project info
$project = $invoice_info['project'] ?? [];
$project_name = $project['name'] ?? null;
// 7) Business logic goes here (examples):
// - idempotency by invoice_id / uuid
// - update invoice status in DB
// - validate amounts/currency
// - logging (avoid logging token)
//
// Example (pseudo):
// save_postback($invoice_id, $order_id, $uuid, $invoice_status, $amount_paid, $fee, $network_code, $project_name, $created, $address);
// 8) Minimal response
http_response_code(200);
echo json_encode(['message' => 'Postback received'], JSON_UNESCAPED_UNICODE);from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route('/postback', methods=['POST'])
def handle_postback():
# Changed to request.form for processing data in x-www-form-urlencoded format
status = request.form.get('status')
invoice_id = request.form.get('invoice_id')
amount_crypto = request.form.get('amount_crypto')
currency = request.form.get('currency')
order_id = request.form.get('order_id')
token = request.form.get('token')
# ... your code for handling the postback ...
return jsonify({'message': 'Postback received'}), 200
if __name__ == '__main__':
app.run(port=5000)const express = require('express');
const app = express();
// Add middleware for processing JSON
app.use(express.json());
// Add middleware for processing x-www-form-urlencoded data
app.use(express.urlencoded({ extended: true }));
app.post('/postback', (req, res) => {
const { status, invoice_id, amount_crypto, currency, order_id, token } = req.body;
// ... your code for handling the postback ...
res.json({ message: 'Postback received' });
});
app.listen(3000, () => {
console.log('Server is running on port 3000');
});
<?php
$body = file_get_contents('php://input');
$data = [];
parse_str($body, $data);
$status = $data['status'] ?? null;
$invoice_id = $data['invoice_id'] ?? null;
$amount_crypto = $data['amount_crypto'] ?? null;
$currency = $data['currency'] ?? null;
$order_id = $data['order_id'] ?? null;
$token = $data['token'] ?? null;
// ... your code for processing postback ...
echo json_encode(['message' => 'Postback received']);
?>