## Overview Checkout Deposits allow you to create reusable deposit links where customers can make multiple payments over time. Unlike one-time payments, deposit links remain active and can receive multiple transactions from different users or the same user making recurring deposits. This makes it perfect for: - **Account funding** - Let users add funds to their account balance - **Donations** - Accept multiple donations from supporters - **Recurring payments** - Allow customers to make regular deposits - **Static payment links** - Create permanent payment addresses for your business > **INFO** >**Estimated time:** 5-10 minutes for a basic implementation ## Setup Before you can start accepting deposits, you need to configure your BlockBee account and get your API credentials. ### 1. Create a BlockBee Account If you haven't already, [sign up for a BlockBee account](https://dash.blockbee.io/register) to get access to the dashboard. ### 2. Configure Addresses Set up the cryptocurrency addresses where you want to receive deposits at [Addresses](https://dash.blockbee.io/profile/addresses). > **INFO** >Check this handy [tutorial](/kb/tutorials/dashboard/how-to-set-up-the-addresses) on how to setup your addresses. ### 3. Configure Payment Settings 1. **Set Default Currency:** Go to [Payment Settings](https://dash.blockbee.io/profile/checkout) and configure your default FIAT currency (USD, EUR, GBP, etc.) 2. **Customize Deposit Page:** Optionally customize the appearance and branding of your deposit pages ### 4. Generate API Key 1. Navigate to the [API Keys section](https://dash.blockbee.io/profile/api-keys) in your dashboard 2. Click "Generate New API Key" 3. Copy the generated API key - you'll need this for all API requests > **INFO** >Check this handy [tutorial](/kb/tutorials/dashboard/how-to-generate-a-new-api-key) on how to generate an API Key. > **WARNING** >**Keep your API key secure:** Never expose your API key in client-side code or public repositories. Store it securely in environment variables or server-side configuration. ## Step 1: Create Deposit Link First, make a request to our Deposit API. This will generate a unique deposit link that can receive multiple payments. ### API Endpoint **Method:** `GET` **URL:** `https://api.blockbee.io/deposit/request/` > **INFO: Tracking Deposits with Custom Parameters** >You can add your own query parameters to `notify_url` to track deposits. For example, adding `?user_id=123` will ensure that `user_id=123` is included in the webhook payload for each deposit. This is the recommended way to link deposits to specific users or accounts in your system. > > **Important:** Always include a unique identifier (like `user_id`, `account_id`, or `deposit_id`) in your `notify_url` to properly track which user/account the deposit belongs to. This identifier will be returned in the webhook payload, allowing you to credit the correct user account in your database. > **WARNING: Harden your integration with a secret** >Beyond tracking identifiers, add an unguessable value unique to each deposit link (for example `?nonce=a1b2c3...`) to your `notify_url`, and store it on your side. BlockBee returns it unchanged in every webhook, so you verify it in your webhook handler and reject any notification whose value you don't recognize. Use this together with [webhook signature verification](/webhooks/verify-webhook-signature), not instead of it. > **TIP** >**API Reference:** For complete parameter documentation, see our [Deposit Request API Reference](/api/create-deposit). ### Code Examples ```javascript // Create a deposit link const createDepositLink = async (userId) => { // Generate an unguessable nonce and store it so you can verify it in the webhook const crypto = require('crypto'); const nonce = crypto.randomBytes(16).toString('hex'); storeNonce(userId, nonce); const params = new URLSearchParams({ apikey: 'YOUR_API_KEY', notify_url: `https://yoursite.com/webhook/?user_id=${userId}&nonce=${nonce}`, currency: 'usd', item_description: 'Account Deposit', post: '1' }); const response = await fetch(`https://api.blockbee.io/deposit/request/?${params}`); const result = await response.json(); if (result.status === 'success') { return { paymentId: result.payment_id, paymentUrl: result.payment_url }; } else { throw new Error('Failed to create deposit link: ' + result.message); } }; ``` ```php 'YOUR_API_KEY', 'notify_url' => 'https://yoursite.com/webhook/?user_id=' . $userId . '&nonce=' . $nonce, 'currency' => 'usd', 'item_description' => 'Account Deposit', 'post' => '1' ]; $url = 'https://api.blockbee.io/deposit/request/?' . http_build_query($params); $response = file_get_contents($url); $result = json_decode($response, true); if ($result['status'] === 'success') { return [ 'paymentId' => $result['payment_id'], 'paymentUrl' => $result['payment_url'] ]; } else { throw new Exception('Failed to create deposit link: ' . $result['message']); } } ?> ``` ```python import requests import secrets def create_deposit_link(user_id): # Generate an unguessable nonce and store it so you can verify it in the webhook nonce = secrets.token_hex(16) store_nonce(user_id, nonce) params = { 'apikey': 'YOUR_API_KEY', 'notify_url': f'https://yoursite.com/webhook/?user_id={user_id}&nonce={nonce}', 'currency': 'usd', 'item_description': 'Account Deposit', 'post': '1' } response = requests.get('https://api.blockbee.io/deposit/request/', params=params) result = response.json() if result['status'] == 'success': return { 'paymentId': result['payment_id'], 'paymentUrl': result['payment_url'] } else: raise Exception(f'Failed to create deposit link: {result.get("message", "Unknown error")}') ``` ```ruby require 'net/http' require 'json' require 'uri' require 'securerandom' def create_deposit_link(user_id) # Generate an unguessable nonce and store it so you can verify it in the webhook nonce = SecureRandom.hex(16) store_nonce(user_id, nonce) params = { 'apikey' => 'YOUR_API_KEY', 'notify_url' => "https://yoursite.com/webhook/?user_id=#{user_id}&nonce=#{nonce}", 'currency' => 'usd', 'item_description' => 'Account Deposit', 'post' => '1' } uri = URI('https://api.blockbee.io/deposit/request/') uri.query = URI.encode_www_form(params) response = Net::HTTP.get_response(uri) result = JSON.parse(response.body) if result['status'] == 'success' { paymentId: result['payment_id'], paymentUrl: result['payment_url'] } else raise "Failed to create deposit link: #{result['message']}" end end ``` ```csharp using System; using System.Collections.Generic; using System.Linq; using System.Net.Http; using System.Threading.Tasks; using Newtonsoft.Json; public class DepositService { private static readonly HttpClient client = new HttpClient(); public async Task CreateDepositLink(string userId) { // Generate an unguessable nonce and store it so you can verify it in the webhook var nonce = Convert.ToHexString(System.Security.Cryptography.RandomNumberGenerator.GetBytes(16)).ToLower(); StoreNonce(userId, nonce); var parameters = new Dictionary { ["apikey"] = "YOUR_API_KEY", ["notify_url"] = $"https://yoursite.com/webhook/?user_id={userId}&nonce={nonce}", ["currency"] = "usd", ["item_description"] = "Account Deposit", ["post"] = "1" }; var queryString = string.Join("&", parameters.Select(p => $"{p.Key}={Uri.EscapeDataString(p.Value)}")); var url = $"https://api.blockbee.io/deposit/request/?{queryString}"; var response = await client.GetStringAsync(url); var result = JsonConvert.DeserializeObject>(response); if (result["status"].ToString() == "success") { return new DepositResult { PaymentId = result["payment_id"].ToString(), PaymentUrl = result["payment_url"].ToString() }; } else { throw new Exception($"Failed to create deposit link: {result.GetValueOrDefault("message", "Unknown error")}"); } } } public class DepositResult { public string PaymentId { get; set; } public string PaymentUrl { get; set; } } ``` ```java import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.net.URI; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.util.Map; import com.fasterxml.jackson.databind.ObjectMapper; public class DepositLink { private static final HttpClient client = HttpClient.newHttpClient(); private static final ObjectMapper mapper = new ObjectMapper(); public DepositResult createDepositLink(String userId) throws Exception { String baseUrl = "https://api.blockbee.io/deposit/request/"; // Generate an unguessable nonce and store it so you can verify it in the webhook byte[] nonceBytes = new byte[16]; new java.security.SecureRandom().nextBytes(nonceBytes); String nonce = java.util.HexFormat.of().formatHex(nonceBytes); storeNonce(userId, nonce); String queryParams = String.format( "apikey=%s¬ify_url=%s¤cy=usd&item_description=%s&post=1", "YOUR_API_KEY", URLEncoder.encode("https://yoursite.com/webhook/?user_id=" + userId + "&nonce=" + nonce, StandardCharsets.UTF_8), URLEncoder.encode("Account Deposit", StandardCharsets.UTF_8) ); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(baseUrl + "?" + queryParams)) .GET() .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); Map result = mapper.readValue(response.body(), Map.class); if ("success".equals(result.get("status"))) { return new DepositResult( (String) result.get("payment_id"), (String) result.get("payment_url") ); } else { throw new Exception("Failed to create deposit link: " + result.getOrDefault("message", "Unknown error")); } } } class DepositResult { private String paymentId; private String paymentUrl; public DepositResult(String paymentId, String paymentUrl) { this.paymentId = paymentId; this.paymentUrl = paymentUrl; } // Getters public String getPaymentId() { return paymentId; } public String getPaymentUrl() { return paymentUrl; } } ``` ```go package main import ( "crypto/rand" "encoding/hex" "encoding/json" "fmt" "net/http" "net/url" ) type DepositResult struct { PaymentID string `json:"payment_id"` PaymentURL string `json:"payment_url"` } func createDepositLink(userID string) (*DepositResult, error) { baseURL := "https://api.blockbee.io/deposit/request/" // Generate an unguessable nonce and store it so you can verify it in the webhook nb := make([]byte, 16) rand.Read(nb) nonce := hex.EncodeToString(nb) storeNonce(userID, nonce) params := url.Values{} params.Add("apikey", "YOUR_API_KEY") params.Add("notify_url", fmt.Sprintf("https://yoursite.com/webhook/?user_id=%s&nonce=%s", userID, nonce)) params.Add("currency", "usd") params.Add("item_description", "Account Deposit") params.Add("post", "1") resp, err := http.Get(baseURL + "?" + params.Encode()) if err != nil { return nil, err } defer resp.Body.Close() var result map[string]interface{} if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { return nil, err } if result["status"] == "success" { return &DepositResult{ PaymentID: result["payment_id"].(string), PaymentURL: result["payment_url"].(string), }, nil } message := "Unknown error" if msg, ok := result["message"].(string); ok { message = msg } return nil, fmt.Errorf("failed to create deposit link: %s", message) } ``` ```bash # Create deposit link # Generate an unguessable nonce and store it so you can verify it in the webhook nonce=$(openssl rand -hex 16) curl -G "https://api.blockbee.io/deposit/request/" \ --data-urlencode "apikey=YOUR_API_KEY" \ --data-urlencode "notify_url=https://yoursite.com/webhook/?user_id=12345&nonce=$nonce" \ --data-urlencode "currency=usd" \ --data-urlencode "item_description=Account Deposit" \ --data-urlencode "post=1" ``` > **INFO: Using Our Official Libraries** >For a simpler integration, you can also use our official libraries. Here's how you would create a deposit link: > > ```javascript > // Using the official Node.js library > const BlockBee = require('@blockbee/api'); > const crypto = require('crypto'); > > // Generate an unguessable nonce and store it so you can verify it in the webhook > const nonce = crypto.randomBytes(16).toString('hex'); > storeNonce(userId, nonce); > > const params = { > user_id: userId, > nonce, > }; > > const blockbeeParams = { > currency: 'usd', > item_description: 'Account Deposit', > post: '1' > }; > > const deposit = await BlockBee.depositRequest( > 'https://yoursite.com/webhook/', > 'YOUR_API_KEY', > params, > blockbeeParams > ); > // deposit.payment_url and deposit.payment_id > ``` > > ```php > // Using the official PHP library > // Generate an unguessable nonce and store it so you can verify it in the webhook > $nonce = bin2hex(random_bytes(16)); > storeNonce($userId, $nonce); > > $params = ['user_id' => $userId, 'nonce' => $nonce]; > $blockbeeParams = [ > 'currency' => 'usd', > 'item_description' => 'Account Deposit', > 'post' => '1' > ]; > > $bb = new BlockBee\Checkout('YOUR_API_KEY', $params, $blockbeeParams); > $deposit = $bb->deposit_request('https://yoursite.com/webhook/'); > // $deposit->payment_url and $deposit->payment_id > ``` > > ```python > # Using the official Python library > from BlockBee import BlockBeeCheckoutHelper > import secrets > > # Generate an unguessable nonce and store it so you can verify it in the webhook > nonce = secrets.token_hex(16) > store_nonce(userId, nonce) > > params = {'user_id': userId, 'nonce': nonce} > bb_params = { > 'currency': 'usd', > 'item_description': 'Account Deposit', > 'post': '1' > } > > bb = BlockBeeCheckoutHelper('YOUR_API_KEY', params, bb_params); > deposit = bb.deposit_request('https://yoursite.com/webhook/'); > # deposit.payment_url and deposit.payment_id > ``` > > You can find the full documentation for our libraries here: > - [Node.js Library](/libraries/nodejs) > - [PHP Library](/libraries/php) > - [Python Library](/libraries/python) ### Required Parameters - **`apikey`** - Your BlockBee API key from the [Dashboard](https://dash.blockbee.io/) - **`notify_url`** - Webhook URL where BlockBee sends payment notifications for each deposit. ### Optional Parameters - **`currency`** - Override the default FIAT currency set in your [Payment Settings](https://dash.blockbee.io/profile/checkout) (USD, EUR, GBP, etc.). - **`item_description`** - Description that appears on the deposit page. - **`post`** - Set to `1` to receive webhooks as POST requests. - **`suggested_value`** - Suggested amount for deposits (optional, users can still send any amount). ### Response The API returns a JSON response containing the `payment_url` and `payment_id`. ```json { "status": "success", "payment_url": "https://pay.blockbee.io/deposit/fG78jtx96ugjtu0eIbeLmFB9z0feJf9N/", "payment_id": "fG78jtx96ugjtu0eIbeLmFB9z0feJf9N" } ``` > **TIP** >**API Reference:** For complete response field documentation, see our [Deposit Request API Reference](/api/create-deposit). ## Step 2: Share the Deposit Link After creating the deposit link in Step 1, you will receive a `payment_url` in the API response. You can share this URL with your customers in multiple ways: - **Direct link** - Send the URL directly to customers - **QR code** - Generate a QR code for the deposit URL - **Embed in your app** - Display the deposit link in your application - **Static page** - Create a permanent deposit page using this URL ```javascript // Share deposit link with customer const shareDepositLink = (depositResult) => { // Option 1: Direct link console.log(`Share this link: ${depositResult.paymentUrl}`); // Option 2: Generate QR code (using a QR library) // const qrCode = generateQRCode(depositResult.paymentUrl); // Option 3: Embed in your app // document.getElementById('deposit-link').href = depositResult.paymentUrl; }; ``` ```php // Share deposit link with customer function shareDepositLink($depositResult) { // Option 1: Direct link echo "Share this link: " . $depositResult['paymentUrl']; // Option 2: Generate QR code // $qrCode = generateQRCode($depositResult['paymentUrl']); // Option 3: Embed in your app // echo 'Make a Deposit'; } ``` ```python # Share deposit link with customer def share_deposit_link(deposit_result): # Option 1: Direct link print(f"Share this link: {deposit_result['paymentUrl']}") # Option 2: Generate QR code # qr_code = generate_qr_code(deposit_result['paymentUrl']) # Option 3: Embed in your app # return f'Make a Deposit' ``` > **TIP** >**Pre-select cryptocurrency:** You can append `?coin=btc` to the deposit URL to pre-select Bitcoin (or any other supported cryptocurrency) on the checkout page. ## Step 3: Handle the Webhooks This is the most important step. When a deposit is made, BlockBee will send a notification (webhook) to the `notify_url` you provided. Your application needs to listen for these webhooks to process each deposit. ### Webhook Endpoint Create an endpoint in your application to receive the webhook. The payload will contain all the details of the transaction. ```javascript // Express.js webhook handler app.post('/webhook', express.json(), (req, res) => { // 1. Verify the webhook signature if (!verifyWebhookSignature(req)) { return res.status(401).send('Unauthorized'); } // 2. Extract data from the payload const { is_paid, uuid, nonce, paid_amount, paid_coin, txid, status, user_id } = req.body; // Note: user_id comes from the custom parameter you added to notify_url when creating the deposit link // Verify the nonce matches the one you stored when creating the deposit link if (nonce !== getStoredNonce(user_id)) { return res.status(401).send('Unauthorized'); } // 3. Check for duplicates using UUID if (isWebhookAlreadyProcessed(uuid)) { return res.status(200).send('*ok*'); } // 4. Check if the payment is complete and the status is 'done' if (is_paid === 1 && status === 'done') { // 5. Find the user in your database using the user_id console.log(`User ${user_id} has made a deposit: ${paid_amount} ${paid_coin}`); // 6. Update the user's account balance // e.g., db.users.update({ id: user_id }, { // $inc: { balance: paid_amount }, // $push: { transactions: { amount: paid_amount, coin: paid_coin, txid: txid } } // }); // 7. Send confirmation to the user console.log(`Crediting ${paid_amount} ${paid_coin} to user ${user_id}...`); } // 8. Mark this webhook as processed using its unique UUID markWebhookAsProcessed(uuid); // Respond with *ok* to let BlockBee know you've received the webhook res.status(200).send('*ok*'); }); ``` ```php query("UPDATE users SET balance = balance + '{$paid_amount}' WHERE id = '{$user_id}'"); // 7. Send confirmation to the user error_log("Crediting {$paid_amount} {$paid_coin} to user {$user_id}..."); } // 8. Mark this webhook as processed using its unique uuid markWebhookAsProcessed($uuid); // Respond with *ok* to let BlockBee know you've received the webhook http_response_code(200); echo '*ok*'; ?> ``` ```python # Flask webhook handler @app.route('/webhook', methods=['POST']) def webhook(): # 1. Verify the webhook signature if not verify_webhook_signature(request): return 'Unauthorized', 401 # 2. Extract data from the payload data = request.get_json() is_paid = data.get('is_paid') uuid = data.get('uuid') user_id = data.get('user_id') # This comes from the custom parameter you added to notify_url nonce = data.get('nonce') paid_amount = data.get('paid_amount') paid_coin = data.get('paid_coin') txid = data.get('txid') status = data.get('status') # Verify the nonce matches the one you stored when creating the deposit link if nonce != get_stored_nonce(user_id): return 'Unauthorized', 401 # 3. Use the uuid to prevent processing the same webhook twice if is_webhook_already_processed(uuid): return '*ok*', 200 # 4. Check if the payment is complete and status is 'done' if is_paid == 1 and status == 'done': # 5. Find the user in your database print(f"User {user_id} has made a deposit: {paid_amount} {paid_coin}") # 6. Update the user's account balance # e.g., User.objects.filter(id=user_id).update( # balance=F('balance') + paid_amount # ) # 7. Send confirmation to the user print(f"Crediting {paid_amount} {paid_coin} to user {user_id}...") # 8. Mark this webhook as processed using its unique uuid mark_webhook_as_processed(uuid) # Respond with *ok* to let BlockBee know you've received the webhook return '*ok*', 200 ``` > **TIP** >**Full Webhook Fields:** For a complete list of all fields provided in the webhook payload, see our [Checkout Deposits Webhook reference](/webhooks/checkout-deposits-webhook). ### Best Practices - **Verify Signatures:** Always verify the webhook signature to ensure the request is from BlockBee. - **Verify Your Secret:** Confirm the nonce you added to `notify_url` matches the value you stored, and reject any webhook where it doesn't. - **Idempotency:** Use the `uuid` from the webhook payload to ensure you only process each transaction once. - **Respond Quickly:** Always respond with a `*ok*` and a `200` status code to prevent BlockBee from resending the webhook. - **Asynchronous Processing:** For long-running tasks (like calling other APIs), process them in a background job after responding to the webhook. > **TIP** >**Implementation Guide:** For detailed instructions and code examples on how to verify webhook signatures, see our [Verify Webhook Signature guide](/webhooks/verify-webhook-signature). ### Alternative: Checking Logs Manually While webhooks are recommended, you can also check the deposit status by polling our [Deposit Logs API endpoint](/api/get-deposit-logs). This can be useful for reconciliation or as a backup if your webhook endpoint fails. You will need the `payment_id` returned in Step 1. > **INFO: Using Our Official Libraries** >You can also check deposit logs using our official libraries: > > ```javascript > // Using the official Node.js library > const BlockBee = require('@blockbee/api'); > > const logs = await BlockBee.depositLogs(paymentId, 'YOUR_API_KEY'); > // logs.notifications contains the deposit history > ``` > > ```php > // Using the official PHP library > $logs = BlockBee\Checkout::deposit_logs($paymentId, 'YOUR_API_KEY'); > // $logs->notifications contains the deposit history > ``` > > ```python > # Using the official Python library > from BlockBee import BlockBeeCheckoutHelper > > logs = BlockBeeCheckoutHelper.deposit_logs(payment_id, 'YOUR_API_KEY') > # logs['notifications'] contains the deposit history > ``` ## Testing Your Integration Test your deposit flow using real cryptocurrency with minimal cost. ### 1. Use Litecoin for Testing We recommend using **Litecoin (LTC)** for testing because: - Low transaction fees (typically under $0.01) - Fast confirmation times (2.5 minutes average) - Real blockchain testing without high costs ### 2. Test with a Small Amount - Create a deposit link - Make a small deposit (e.g., $2.00) using Litecoin - This covers all fees with minimal cost (typically under $0.10) - Test multiple deposits to the same link ### 3. Testing Checklist - ✅ Deposit link creation works as expected - ✅ Webhooks are received for each deposit - ✅ User account balance is updated correctly - ✅ Duplicate webhooks are properly handled - ✅ Multiple deposits to the same link work ### 4. Local Webhook Testing To test webhooks on your local machine, you need to expose your server to the internet. `ngrok` is a great tool for this. ```bash # Use ngrok for local webhook testing ngrok http 3000 # Your test webhook URL: https://abc12345.ngrok.io/webhook # Use this as the notify_url when creating the deposit link. ``` > **SUCCESS** >**Ready for production?** Once testing is complete, switch to your production API Key and ensure your `notify_url` points to your live production server.