false,'error'=>'Forbidden']); exit; } $adminId = (int)$_SESSION['user_id']; $method = $_SERVER['REQUEST_METHOD']; $action = $_GET['action'] ?? 'list_settings'; // ── GET actions ────────────────────────────────────────── if ($method === 'GET') { if ($action === 'list_settings') { $rows = db()->query("SELECT * FROM admin_payout_settings ORDER BY sort_order ASC, id ASC")->fetchAll(); echo json_encode(['success'=>true, 'settings'=>$rows]); exit; } if ($action === 'cashout_detail') { $id = (int)($_GET['id'] ?? 0); $stmt = db()->prepare(" SELECT cr.*, u.username, u.alias AS user_alias, u.email, pm.method_type AS saved_payout_type, pm.account_handle AS saved_payout_handle, pm.label AS saved_payout_label FROM cashout_requests cr JOIN users u ON cr.user_id = u.id LEFT JOIN payout_methods pm ON pm.user_id = cr.user_id AND pm.is_default = 1 WHERE cr.id = ? "); $stmt->execute([$id]); $row = $stmt->fetch(); echo json_encode(['success'=>true, 'cashout'=>$row]); exit; } echo json_encode(['success'=>false,'error'=>'Unknown action']); exit; } if ($method !== 'POST') { echo json_encode(['success'=>false,'error'=>'Method not allowed']); exit; } $d = json_decode(file_get_contents('php://input'), true); // ── Update payout settings ──────────────────────────────── if ($action === 'update_setting') { $id = (int)($d['id'] ?? 0); $handle = substr(trim($d['handle']??''), 0, 200); $instr = substr(trim($d['instructions']??''), 0, 500); $enabled = (int)(bool)($d['is_enabled']??1); $label = substr(trim($d['label']??''), 0, 100); $sort = (int)($d['sort_order']??0); db()->prepare("UPDATE admin_payout_settings SET label=?,handle=?,instructions=?,is_enabled=?,sort_order=? WHERE id=?") ->execute([$label,$handle,$instr,$enabled,$sort,$id]); echo json_encode(['success'=>true]); exit; } // ── Process cashout payout ──────────────────────────────── if ($action === 'process_payout') { $cashoutId = (int)($d['cashout_id'] ?? 0); $payoutKey = trim($d['payout_method_key'] ?? ''); $payoutType = trim($d['payout_type'] ?? 'manual'); // 'manual' or 'square_gift_card' $note = substr(trim($d['note'] ?? ''), 0, 500); // Load cashout $stmt = db()->prepare("SELECT cr.*, u.username, u.alias, u.email FROM cashout_requests cr JOIN users u ON cr.user_id=u.id WHERE cr.id=? AND cr.status IN ('pending','locked')"); $stmt->execute([$cashoutId]); $cashout = $stmt->fetch(); if (!$cashout) { echo json_encode(['success'=>false,'error'=>'Cashout not found or already processed']); exit; } // Amount in cents (1 token = $1) $amountCents = (int)round($cashout['tokens'] * 100); // Load payout setting $pset = db()->prepare("SELECT * FROM admin_payout_settings WHERE method_key=? AND is_enabled=1"); $pset->execute([$payoutKey]); $setting = $pset->fetch(); $squareTxnId = null; $giftCardGan = null; $giftCardBal = null; $txnStatus = 'completed'; if ($payoutType === 'square_gift_card') { // ── Square Gift Card Flow ───────────────────────── try { // 1. Create gift card $gcRes = SquareClient::post('/v2/gift-cards', [ 'idempotency_key' => 'gc-create-' . $cashoutId . '-' . time(), 'location_id' => SQUARE_LOCATION_ID, 'gift_card' => ['type' => 'DIGITAL'], ]); if (empty($gcRes['gift_card']['id'])) { throw new Exception('Failed to create gift card: ' . json_encode($gcRes)); } $gcId = $gcRes['gift_card']['id']; $gcGan = $gcRes['gift_card']['gan'] ?? ''; // 2. Activate gift card with balance $actRes = SquareClient::post('/v2/gift-card-activities', [ 'idempotency_key' => 'gc-activate-' . $cashoutId . '-' . time(), 'gift_card_activity' => [ 'type' => 'ACTIVATE', 'location_id' => SQUARE_LOCATION_ID, 'gift_card_id' => $gcId, 'activate_activity_details' => [ 'amount_money' => [ 'amount' => $amountCents, 'currency' => 'USD', ], ], ], ]); if (empty($actRes['gift_card_activity']['id'])) { throw new Exception('Failed to activate gift card: ' . json_encode($actRes)); } $giftCardGan = $gcGan; $giftCardBal = $amountCents; $squareTxnId = $actRes['gift_card_activity']['id']; } catch (Exception $e) { echo json_encode(['success'=>false,'error'=>'Square error: ' . $e->getMessage()]); exit; } } // ── Mark cashout as sent ────────────────────────────── db()->beginTransaction(); try { db()->prepare("UPDATE cashout_requests SET status='sent', admin_note=?, resolved_at=NOW() WHERE id=?") ->execute([$note, $cashoutId]); db()->prepare("INSERT INTO cashout_transactions (cashout_id,admin_id,payout_method,payout_type,amount_cents,square_txn_id,gift_card_gan,gift_card_balance,note,status) VALUES (?,?,?,?,?,?,?,?,?,'completed')") ->execute([$cashoutId, $adminId, $payoutKey, $payoutType, $amountCents, $squareTxnId, $giftCardGan, $giftCardBal, $note]); db()->commit(); } catch (Exception $e) { db()->rollBack(); echo json_encode(['success'=>false,'error'=>'DB error: '.$e->getMessage()]); exit; } $response = ['success'=>true, 'status'=>'sent', 'amount_cents'=>$amountCents]; if ($giftCardGan) { $response['gift_card_gan'] = $giftCardGan; $response['gift_card_balance'] = $giftCardBal; } echo json_encode($response); exit; } echo json_encode(['success'=>false,'error'=>'Unknown action']);