B2BINPAY offers a comprehensive API for crypto deposits and payouts. One important feature in its asynchronous architecture is the use of callbacks—server-to-server notifications sent to your callback_url to update you on the status of a transaction.
Among these, the “No Transfer” callback deserves special attention. Below is a practical overview of what it is, how it works, and how you should handle it securely.
1. 📡 Callback Mechanism Overview
When you create a deposit B2BINPAY, you can supply a callback_url. B2BINPAY then sends HTTP POST requests to that URL each time the transaction status changes, such as:
confirmedfailedblockedcancelledno transfer
Most of these callbacks contain a signed JSON payload, verified using HMAC-SHA256 with your secret key. This allows you to ensure data integrity and source authenticity (No transfer callback doesn't contain Han MAC-SHA256 signature, and it will be discussed below).
2. 🛑 What is a “No Transfer” Callback?
A “No Transfer” callback is sent when:
A deposit expired without receiving any funds
(→ deposit status changed fromCreatedtoCanceled).A deposit was created but never received a valid transfer due to an invalid amount (either below or above the allowed range), (→ deposit status changed from
CreatedtoUnresolved).A deposit reached a
Paidstatus, but then received an additional transfer outside the allowed limits (causing a status change fromPaidtoUnresolved).
3. ⚠️ No HMAC Signature on “No Transfer” Callbacks
One critical distinction:
"No Transfer" callbacks do not include an HMAC-SHA256 signature.
This is explicitly noted in B2BinPay’s documentation. Since no transaction or transfer object was created, these callbacks skip the usual signature process.
🚨 Security Implications
| Concern | Implication |
|---|---|
| No cryptographic proof | You can't validate the callback using your HMAC key. |
| Higher spoofing risk | You must verify the source by other means (e.g., IP allowlisting). |
| Should not trigger irreversible actions | Avoid performing withdrawals, refunds, or state changes solely based on this type of callback. |
✅ Best Practices
- Log the event, including IP, headers, and payload.
- Validate source IPs against B2BINPAY's documented callback IP ranges.
- Use internal timeouts or logic to double-check whether the deposit/payout really expired or failed.
- Avoid sensitive automation on these callbacks (like auto-rejection of payments or user bans).
4. 🔄 Example: "No Transfer" Flow in a Deposit Scenario
You create a deposit with:
{ "data": { "type": "deposit", "id": "7383", "attributes": { "status": 2, "address": "TVQMvDdWBN7FiDHdJVrwqHuoHvVtT26SgP", "address_type": "", "label": null, "tracking_id": null, "confirmations_needed": null, "time_limit": null, "callback_url": "https://1b780322-7074-404b-923d-93f4ee24868a-00-1jstgloaemst2.picard.replit.dev:5000/callback", "inaccuracy": null, "target_amount_requested": null, "rate_requested": "1.000000000000000000", "rate_expired_at": null, "invoice_updated_at": "2025-06-10T12:12:15.441066Z", "payment_page": "https://pay-sandbox.b2binpay.com//en/9edc890c-7c91-4423-894d-23643eb6caf8", "target_paid": "0.00000000", "source_amount_requested": "0.000000", "target_paid_pending": "0.00000000", "assets": { "USDT-TRX": { "wallet_id": 628, "target_paid": "20.00000000", "enrolled": "13.97147700", "target_paid_pending": "0.00000000", "blockchain_balance": "0.00000000" } }, "destination": { "address_type": null, "address": "TVQMvDdWBN7FiDHdJVrwqHuoHvVtT26SgP" }, "payment_page_redirect_url": null, "payment_page_button_text": null, "is_active": true }, "relationships": { "currency": { "data": { "type": "currency", "id": "2145" } }, "wallet": { "data": { "type": "wallet", "id": "628" } } } } }- The deposit waits for incoming payment (
status = 2 (created)). - If the user sends crypto in time, the deposit is marked as Paid (status = 3).
A
no transfercallback is sent—unsigned—to your callback URL:{ "data": { "type": "deposit", "id": "7383", "attributes": { "address": "TVQMvDdWBN7FiDHdJVrwqHuoHvVtT26SgP", "created_at": "2024-11-28T15:39:37.807456Z", "tracking_id": null, "target_paid": "20.00000000", "source_amount_requested": "0.000000", "target_amount_requested": null, "status": 3, "time_limit": null, "inaccuracy": null, "destination": { "address_type": null, "address": "TVQMvDdWBN7FiDHdJVrwqHuoHvVtT26SgP" } }, "relationships": { "currency": { "data": { "type": "currency", "id": "2145" } }, "wallet": { "data": { "type": "wallet", "id": "628" } } } }, "included": [ { "type": "currency", "id": "2145", "attributes": { "blockchain_name": "", "iso": 2145, "name": "TetherUS", "alpha": "USDT-TRX", "alias": "USDT", "tags": "", "exp": 6, "confirmation_blocks": 20, "minimal_transfer_amount": "1.000000", "block_delay": 75 } } ] }
5. 🛠️ Final Recommendations
To build a secure, robust B2BINPAY integration:
- Always verify signatures when present.
- Handle “No Transfer” separately—log, notify users, but avoid irreversible actions based solely on it.
- Monitor for missing callbacks in case your endpoint was down or misconfigured.
Comments
0 commentsArticle is closed for comments.