================================================================================ BITUPGO Partner API Integration Guide Version 2.0 Date: 2026-03-18 ================================================================================ TABLE OF CONTENTS ----------------- 1. Overview 2. Authentication 3. Partner API Endpoints 4. Seamless Wallet Callbacks 5. Signature Verification 6. Error Codes 7. Integration Flow 8. Code Examples ================================================================================ 1. OVERVIEW ================================================================================ Base URL: https://bet2.bitupgo.com Key Features: - Token-based iframe login (no password exposure) - Real-time balance synchronization via callbacks - Seamless wallet (partner manages user balance) ================================================================================ 2. AUTHENTICATION ================================================================================ All API requests require authentication via HTTP header: Header Name: X-Partner-Key Header Value: Your callback_secret_key (from partner dashboard) Example: X-Partner-Key: sk_live_abc123def456... ================================================================================ 3. PARTNER API ENDPOINTS ================================================================================ ------------------------------------------------------------------------------ 3.1 LOGIN - Get Token URL for iframe ------------------------------------------------------------------------------ POST /api/partner.php?action=login Description: Creates a temporary login token and returns URL for iframe embedding. User is auto-created if not exists. Request Headers: Content-Type: application/json X-Partner-Key: {your_secret_key} Request Body: { "user_id": "player123", // Required: Unique user identifier "user_pw": "any_password", // Required: Any string (not stored) "nickname": "CoolPlayer" // Optional: Display name } Success Response (200): { "success": true, "login_url": "https://bet2.bitupgo.com/auth/token_login.php?token=eyJ...", "expires_in": 60 } Error Response: { "success": false, "message": "Invalid partner key" } Usage: Set login_url as iframe src. Token expires in 60 seconds. ------------------------------------------------------------------------------ 3.2 BALANCE - Get User Balance ------------------------------------------------------------------------------ GET /api/partner.php?action=balance&user_id={user_id} Description: Returns current balance for a user. Request Headers: X-Partner-Key: {your_secret_key} Query Parameters: user_id - Required: User identifier Success Response (200): { "success": true, "data": { "user_id": "player123", "nickname": "CoolPlayer", "balance": 50000.00 } } ------------------------------------------------------------------------------ 3.3 RESULT - Get Single Bet Result ------------------------------------------------------------------------------ GET /api/partner.php?action=result&tx_id={transaction_id} Description: Returns result of a specific bet by transaction ID. Request Headers: X-Partner-Key: {your_secret_key} Query Parameters: tx_id - Required: Transaction ID from debit callback Success Response (200): { "success": true, "data": { "tx_id": "tx_abc123", "user_id": "player123", "game_id": "202603180001", "bet_type": "UP", "amount": 10000, "approved_amount": 10000, "returned_amount": 0, "win_amount": 19500, "profit": 9500, "result": "win", "status": "settled", "created_at": "2026-03-18 15:30:00" } } Result Values: - "win": User won the bet - "lose": User lost the bet - "returned": Bet was cancelled/refunded Status Values: - "pending": Bet placed, waiting for round lock - "approved": Bet approved, round locked - "settled": Round finished, result determined ------------------------------------------------------------------------------ 3.4 HISTORY - Get User Bet History ------------------------------------------------------------------------------ GET /api/partner.php?action=history&user_id={user_id}&limit={limit}&offset={offset} Description: Returns paginated bet history for a user. Request Headers: X-Partner-Key: {your_secret_key} Query Parameters: user_id - Required: User identifier limit - Optional: Number of records (default: 20, max: 100) offset - Optional: Skip records (default: 0) Success Response (200): { "success": true, "data": [ { "tx_id": "tx_abc123", "game_id": "202603180001", "bet_type": "UP", "amount": 10000, "approved_amount": 10000, "win_amount": 19500, "profit": 9500, "result": "win", "status": "settled", "created_at": "2026-03-18 15:30:00" }, ... ], "pagination": { "limit": 20, "offset": 0, "total": 150 } } ------------------------------------------------------------------------------ 3.5 CREATE SUB-PARTNER ------------------------------------------------------------------------------ POST /api/partner.php?action=create_partner Description: Creates a new partner (sub-agent) under the calling partner's hierarchy. The new partner is automatically added to the caller's sub-tree. Request Headers: Content-Type: application/json X-Partner-Key: {your_secret_key} Request Body: { "user_id": "subtotal1", // Required: Login ID "user_pw": "password123", // Required: Password (min 4 chars) "nickname": "SubAgent1", // Optional: Display name (default: user_id) "commission_rate": 2.0, "payout_rate": 1.93, // Optional: Payout rate (max: parent rate, default 1.95) // Optional: Commission rate (must be <= caller's rate) "prefix": "SUBTOT" // Required: Member prefix (3-16 alphanumeric/_, unique) } Success Response (200): { "success": true, "partner": { "user_id": 11, "username": "PREFIX_subtotal1", "nickname": "SubAgent1", "partner_code": "SUBTOT_a3f2", "commission_rate": 2.0 } } Error Response: { "success": false, "error": "Commission rate must be between 0 and 3%" } Notes: - prefix is required and must be unique (cannot be changed after creation) - prefix is auto-prepended to sub-members' usernames (e.g., SUBTOT_player123) - commission_rate cannot exceed the calling partner's rate - Created partner has is_partner=1, can_invite=1 - Partner code is auto-generated - Parent's prefix is auto-prepended to username ------------------------------------------------------------------------------ 3.6 COMMISSION HISTORY ------------------------------------------------------------------------------ GET /api/partner.php?action=commission&date_from={start}&date_to={end} Description: Returns commission earnings history for the calling partner. Request Headers: X-Partner-Key: {your_secret_key} Query Parameters: date_from - Optional: Start date (default: 7 days ago, format: YYYY-MM-DD) date_to - Optional: End date (default: today, format: YYYY-MM-DD) game_type - Optional: Game type filter (30s / 60s, default: all) Success Response (200): { "success": true, "summary": { "total_commission": 15000, "total_bet_amount": 500000, "total_count": 25 }, "data": [ { "round_number": 2490, "game_type": "30s", "bet_user": "devtest4", "bet_amount": 10000, "commission_rate": 3.0, "sub_rate": 2.0, "actual_rate": 1.0, "commission_amount": 100, "created_at": "2026-04-25 20:45:00" }, ... ] } ================================================================================ 4. SEAMLESS WALLET CALLBACKS ================================================================================ BITUPGO sends HTTP POST requests to your server when game events occur. You must implement these callback endpoints on your server. Configure callback URLs in Partner Dashboard: - Balance URL: Called to check user balance - Debit URL: Called when user places a bet - Credit URL: Called when user wins - Cancel URL: Called when bet is cancelled Request Headers (from BITUPGO): Content-Type: application/json X-Signature: {hmac_sha256_signature} X-Partner-Id: {partner_id} ------------------------------------------------------------------------------ 4.1 BALANCE CALLBACK ------------------------------------------------------------------------------ POST {your_callback_balance_url} Description: BITUPGO requests current balance when user loads the game. Request Body: { "action": "balance", "user_id": "player123", "timestamp": 1710752400 } Expected Response: { "success": true, "balance": 50000.00 } ------------------------------------------------------------------------------ 4.2 DEBIT CALLBACK (Bet Placement) ------------------------------------------------------------------------------ POST {your_callback_debit_url} Description: Called when user places a bet. Deduct bet amount from user balance. Request Body: { "action": "debit", "user_id": "player123", "game_id": "202603180001", "game_type": "30s", "amount": 10000.00, "tx_id": "tx_1710752400_123", "timestamp": 1710752400 } Success Response: { "success": true, "balance": 40000.00, "tx_id": "tx_1710752400_123" } Insufficient Balance Response: { "success": false, "error": "insufficient_balance", "balance": 5000.00 } IMPORTANT: - Store tx_id for reconciliation - Return updated balance after deduction - If balance insufficient, bet will be cancelled ------------------------------------------------------------------------------ 4.3 CREDIT CALLBACK (Payout) ------------------------------------------------------------------------------ POST {your_callback_credit_url} Description: Called when user wins. Add win_amount to user balance. Request Body: { "action": "credit", "user_id": "player123", "game_id": "202603180001", "game_type": "30s", "win_amount": 19500.00, "tx_id": "tx_1710752400_123", "start_price": 84521.50, "end_price": 84650.30, "result": "UP", "timestamp": 1710752430 } Expected Response: { "success": true, "balance": 59500.00 } Notes: - win_amount = approved_amount × 1.95 (standard payout rate) - tx_id matches the original debit transaction - result can be "win" or "returned" (for cancelled bets) ------------------------------------------------------------------------------ 4.4 CANCEL CALLBACK (Refund) ------------------------------------------------------------------------------ POST {your_callback_cancel_url} Description: Called when bet is cancelled (e.g., one-sided betting, system error). Refund the original bet amount. Request Body: { "action": "cancel", "user_id": "player123", "game_id": "202603180001", "amount": 10000.00, "tx_id": "tx_1710752400_123", "void_reason": "No match - full refund", "timestamp": 1710752430 } Expected Response: { "success": true, "balance": 50000.00 } ================================================================================ 5. SIGNATURE VERIFICATION ================================================================================ All callback requests include X-Signature header for security. VERIFY THIS SIGNATURE to prevent fraudulent requests. Signature Algorithm: HMAC-SHA256 Secret Key: Your callback_secret_key Message: Raw JSON request body PHP Example: ```php $requestBody = file_get_contents('php://input'); $receivedSignature = $_SERVER['HTTP_X_SIGNATURE'] ?? ''; $expectedSignature = hash_hmac('sha256', $requestBody, $your_secret_key); if (!hash_equals($expectedSignature, $receivedSignature)) { http_response_code(401); die(json_encode(['success' => false, 'error' => 'Invalid signature'])); } ``` Node.js Example: ```javascript const crypto = require('crypto'); const requestBody = JSON.stringify(req.body); const receivedSignature = req.headers['x-signature']; const expectedSignature = crypto .createHmac('sha256', yourSecretKey) .update(requestBody) .digest('hex'); if (receivedSignature !== expectedSignature) { return res.status(401).json({ success: false, error: 'Invalid signature' }); } ``` ================================================================================ 6. ERROR CODES ================================================================================ HTTP Status Codes: 200 - Success 400 - Bad Request (invalid parameters) 401 - Unauthorized (invalid partner key or signature) 404 - Not Found (user or transaction not found) 500 - Server Error Error Response Format: { "success": false, "message": "Error description", "error_code": "ERROR_CODE" } Common Error Codes: INVALID_PARTNER_KEY - Partner key not found or disabled INVALID_SIGNATURE - HMAC signature verification failed USER_NOT_FOUND - User does not exist INSUFFICIENT_BALANCE - Not enough balance for bet INVALID_TX_ID - Transaction ID not found DUPLICATE_TX - Transaction already processed ================================================================================ 7. INTEGRATION FLOW ================================================================================ STEP 1: Setup (One-time) ------------------------ 1. Get partner account from BITUPGO admin 2. Configure callback URLs in partner dashboard 3. Save your callback_secret_key securely STEP 2: User Login Flow ----------------------- 1. User clicks "Play" on your site 2. Your server calls POST /api/partner.php?action=login 3. Receive login_url with temporary token 4. Load login_url in iframe 5. User is logged in and can play STEP 3: Game Flow (Fixed Payout Mode) -------------------------------------- 1. User places bet (UP or DOWN) 2. At bet placement: BITUPGO calls your DEBIT callback (deduct balance) 3. At round end (30s/60s): - Win: BITUPGO calls your CREDIT callback (payout) Payout = bet_amount × partner_payout_rate (e.g., 10,000 × 1.97 = 19,700) - Lose: No callback (deduction already confirmed) - System error: BITUPGO calls your CANCEL callback (full refund) Note: - No matching required: one-sided bets are settled normally - Payout rate is set in partner dashboard (default 1.95) - Payout rate includes house margin (no separate fee) STEP 4: Balance Sync -------------------- - BITUPGO calls BALANCE callback periodically - Your response updates the displayed balance - Keep balances in sync between systems ================================================================================ 8. CODE EXAMPLES ================================================================================ PHP - Login API Call: ```php $ch = curl_init('https://bet2.bitupgo.com/api/partner.php?action=login'); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode([ 'user_id' => 'player123', 'user_pw' => 'temp_password', 'nickname' => 'Player Name' ]), CURLOPT_HTTPHEADER => [ 'Content-Type: application/json', 'X-Partner-Key: ' . $your_secret_key ], CURLOPT_RETURNTRANSFER => true ]); $response = json_decode(curl_exec($ch), true); $iframeUrl = $response['login_url']; ``` PHP - Callback Handler: ```php false, 'error' => 'Invalid signature'])); } $data = json_decode($body, true); switch ($data['action']) { case 'balance': $balance = getUserBalance($data['user_id']); echo json_encode(['success' => true, 'balance' => $balance]); break; case 'debit': $result = deductBalance($data['user_id'], $data['amount'], $data['tx_id']); echo json_encode($result); break; case 'credit': $result = addBalance($data['user_id'], $data['win_amount'], $data['tx_id']); echo json_encode($result); break; case 'cancel': $result = refundBalance($data['user_id'], $data['amount'], $data['tx_id']); echo json_encode($result); break; } ``` ================================================================================