基于BSC链的收款系统。
只需修改两处:
第8行:在 www.bscscan.com 注册,免费申请个KEY,填入第8行。
第9行:改为自己的收款地址
运行会自动生成个SQLite数据库usdt_payments.db储存订单信息,可以修改代码更改数据库路径。充值成功后的结合现有业务逻辑,在175行。


<?php
header('Content-Type: text/html; charset=utf-8');
// BSC 配置
//修改此处KEY和收款地址
const BSC_API_KEY = "KEY";//币安BSC链API KEY
const WALLET_ADDRESS = "ADDRESS"; //收款地址
//BSC信息不可更改
const USDT_CONTRACT = "0x55d398326f99059fF775485246999027B3197955";//币安合约地址,不可更改
const BSC_API_URL = "https://api.bscscan.com/api?module=account&action=tokentx&contractaddress=%s&address=%s&startblock=0&endblock=99999999&sort=desc&apikey=%s"; //币安BSC链API请求地址
/*
订单号和金额变量可以从get或者post传递,变量名称见html部分表单,可以删除html表单,使用现有业务的变量传递。
*/
// 设置默认时区为 UTC
date_default_timezone_set('UTC');
// 变量定义
$order_id = isset($_POST['order_id']) ? $_POST['order_id'] : (isset($_GET['order_id']) ? $_GET['order_id'] : '');
$amount = isset($_POST['amount']) ? floatval($_POST['amount']) : (isset($_GET['amount']) ? floatval($_GET['amount']) : 0);
$payment_status = null;
$order_id = date('YmdHis') . mt_rand(1000, 9999);
/**
* 生成严格的小数补位(首位为0,末位非0)
*/
function generatePadding($digits) {
if ($digits <= 0) return '';
// 生成格式:0 + (n-1位随机) + 1位非0
$middle = $digits > 1
? str_pad(mt_rand(0, pow(10, $digits-1)-1), $digits-1, '0', STR_PAD_LEFT)
: '';
$last = mt_rand(1, 9);
return '0' . $middle . $last; // 强制首位为0
}
/**
* 智能金额格式化
*/
function formatDecimal($amount) {
if (!is_numeric($amount) || $amount == 0) return '';
$parts = explode('.', (string)floatval($amount));
$intPart = $parts[0];
$decimal = $parts[1] ?? '';
if (strlen($decimal) >= 4) {
$decimal = substr($decimal, 0, 4);
} else {
$needed = 4 - strlen($decimal);
$padding = substr(generatePadding($needed), 0, $needed); // 截取所需位数
$decimal .= $padding;
}
return $intPart . '.' . $decimal;
}
// 金额变量
$inputAmount = $_POST['amount'] ?? $_GET['amount'] ?? '';
$amount = is_numeric($inputAmount) && $inputAmount != 0
? formatDecimal($inputAmount)
: '';
// 初始化 SQLite 数据库
$db = new SQLite3('usdt_payments.db');
$db->exec("CREATE TABLE IF NOT EXISTS payments (
id INTEGER PRIMARY KEY AUTOINCREMENT,
order_id TEXT UNIQUE,
amount REAL,
wallet_address TEXT,
tx_hash TEXT,
status TEXT DEFAULT 'pending',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
processed INTEGER DEFAULT 0
)");
function checkPaymentStatus($db, $order_id) {
$stmt = $db->prepare("SELECT * FROM payments WHERE order_id = :order_id");
$stmt->bindValue(':order_id', $order_id, SQLITE3_TEXT);
$result = $stmt->execute()->fetchArray(SQLITE3_ASSOC);
if ($result && $result['status'] === 'pending') {
$created_at = strtotime($result['created_at'] . ' UTC');
if ($created_at === false) {
file_put_contents('payment_error.log', "订单 $order_id 创建时间转换失败: " . $result['created_at'] . "\n", FILE_APPEND);
return $result;
}
$url = sprintf(BSC_API_URL, USDT_CONTRACT, WALLET_ADDRESS, BSC_API_KEY);
$response = @file_get_contents($url);
if ($response === false) {
file_put_contents('payment_error.log', "API 请求失败: $url\n", FILE_APPEND);
return $result;
}
$data = json_decode($response, true);
if (json_last_error() !== JSON_ERROR_NONE) {
file_put_contents('payment_error.log', "JSON 解析失败: " . json_last_error_msg() . "\n", FILE_APPEND);
return $result;
}
if ($data['status'] == "1" && !empty($data['result'])) {
foreach ($data['result'] as $tx) {
$tx_timestamp = intval($tx['timeStamp']);
$value = floatval($tx['value']) / 1e18;
if ($tx_timestamp <= $created_at) {
continue;
}
if ($tx['to'] === strtolower(WALLET_ADDRESS) &&
abs($value - $result['amount']) < 0.01 &&
$tx['contractAddress'] === strtolower(USDT_CONTRACT)) {
$check_tx = $db->prepare("SELECT COUNT(*) FROM payments WHERE tx_hash = :tx_hash AND order_id != :order_id");
$check_tx->bindValue(':tx_hash', $tx['hash'], SQLITE3_TEXT);
$check_tx->bindValue(':order_id', $order_id, SQLITE3_TEXT);
$tx_exists = $check_tx->execute()->fetchArray(SQLITE3_NUM)[0];
if ($tx_exists == 0) {
$update = $db->prepare("UPDATE payments SET status = 'completed', tx_hash = :tx_hash WHERE order_id = :order_id AND status = 'pending'");
$update->bindValue(':tx_hash', $tx['hash'], SQLITE3_TEXT);
$update->bindValue(':order_id', $order_id, SQLITE3_TEXT);
$update->execute();
$updated = $db->prepare("SELECT * FROM payments WHERE order_id = :order_id");
$updated->bindValue(':order_id', $order_id, SQLITE3_TEXT);
$result = $updated->execute()->fetchArray(SQLITE3_ASSOC);
break;
}
}
}
} else {
file_put_contents('payment_error.log', "API 返回错误: " . $data['message'] . "\n", FILE_APPEND);
}
}
return $result;
}
function handlePaymentSuccess($db, $order_id, $amount, $tx_hash) {
// 数据库中标记订单为已处理
$markProcessed = $db->prepare("UPDATE payments SET processed = 1 WHERE order_id = :order_id AND processed = 0");
$markProcessed->bindValue(':order_id', $order_id, SQLITE3_TEXT);
$result = $markProcessed->execute();
if ($db->changes() == 0) {
file_put_contents('payment_error.log', "订单 $order_id 标记 processed 失败\n", FILE_APPEND);
}
/*
此处充值成功逻辑,不得包含任何echo或者其它显示
可以对原业务数据库进行update比如支付状态,和跳转、发送邮件通知、短信等等。
*/
}
// 处理 AJAX 检查请求
session_start();
if (isset($_POST['ajax_check']) && isset($_POST['order_id'])) {
header('Content-Type: application/json');
if (!isset($_POST['csrf_token']) || $_POST['csrf_token'] !== $_SESSION['csrf_token']) {
http_response_code(403);
echo json_encode(['error' => 'Invalid CSRF token']);
exit;
}
$payment_status = checkPaymentStatus($db, $_POST['order_id']);
if ($payment_status && $payment_status['status'] === 'completed' && $payment_status['processed'] == 0) {
handlePaymentSuccess($db, $payment_status['order_id'], $payment_status['amount'], $payment_status['tx_hash']);
}
echo json_encode($payment_status ?: ['error' => 'No payment found']);
exit;
}
// 处理订单创建(仅当订单不存在时插入)
if ($amount > 0 && !empty($order_id) && !isset($_POST['ajax_check'])) {
$check_stmt = $db->prepare("SELECT COUNT(*) FROM payments WHERE order_id = :order_id");
$check_stmt->bindValue(':order_id', $order_id, SQLITE3_TEXT);
$exists = $check_stmt->execute()->fetchArray(SQLITE3_NUM)[0];
if ($exists == 0) {
$stmt = $db->prepare("INSERT INTO payments (order_id, amount, wallet_address, status, created_at)
VALUES (:order_id, :amount, :wallet_address, 'pending', CURRENT_TIMESTAMP)");
$stmt->bindValue(':order_id', $order_id, SQLITE3_TEXT);
$stmt->bindValue(':amount', $amount, SQLITE3_FLOAT);
$stmt->bindValue(':wallet_address', WALLET_ADDRESS, SQLITE3_TEXT);
$stmt->execute();
}
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
// 检查支付状态
if (!empty($order_id)) {
$payment_status = checkPaymentStatus($db, $order_id);
if ($payment_status && $payment_status['status'] === 'completed' && $payment_status['processed'] == 0) {
handlePaymentSuccess($db, $payment_status['order_id'], $payment_status['amount'], $payment_status['tx_hash']);
}
}
?>
<!DOCTYPE html>
<html>
<head>
<title>USDT 收款系统 (BSC)</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no" />
<style>
body { font-family: Arial, sans-serif; max-width: 800px; margin: 20px auto; }
.qr-code { margin: 20px 0; }
canvas { border: 1px solid #ccc; }
</style>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/qrcode.min.js"></script>
<style>
/* 基础重置 */
body {
font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
line-height: 1.5;
color: #e0e0e0;
background-color: #;
max-width: 800px;
margin: 0 auto;
padding: 2rem;
-webkit-font-smoothing: antialiased;
}
/* 表单容器 */
#paymentForm {
background: #1e293b;
padding: 2rem;
border-radius: 12px;
box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.3);
margin-bottom: 2rem;
border: 1px solid #2e3a4e;
}
/* 输入框样式 */
#paymentForm label {
display: block;
margin-bottom: 0.75rem;
font-weight: 500;
color: #94a3b8;
font-size: 0.95rem;
}
#paymentForm input[type="text"],
#paymentForm input[type="number"] {
width: 100%;
padding: 0.875rem 1rem;
margin-bottom: 1.5rem;
border: 1px solid #334155;
border-radius: 8px;
font-size: 1rem;
background-color: #1e293b;
color: #f8fafc;
transition: all 0.2s ease;
}
#paymentForm input[type="text"]:focus,
#paymentForm input[type="number"]:focus {
border-color: #3b82f6;
outline: none;
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.2);
}
/* 按钮样式 */
#paymentForm button {
background: linear-gradient(135deg, #3b82f6, #6366f1);
color: white;
border: none;
padding: 1rem;
font-size: 1rem;
font-weight: 600;
border-radius: 8px;
cursor: pointer;
width: 100%;
transition: all 0.2s ease;
}
#paymentForm button:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px -2px rgba(59, 130, 246, 0.4);
}
/* 支付信息卡片 */
.payment-info {
background: #1e293b;
padding: 2rem;
border-radius: 6px;
box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.3);
margin-top: 1.5rem;
border: 1px solid #2e3a4e;
}
.payment-info h2 {
color: #ffffff;
margin-bottom: 1.5rem;
font-weight: 600;
font-size: 1.5rem;
border-bottom: 1px solid #334155;
padding-bottom: 0.75rem;
text-align: center; /* 文字水平居中 */
}
.payment-info p {
margin: 1.25rem 0;
font-size: 1rem;
color: #e2e8f0;
}
/* 金额显示 */
#amount {
color: #10b981;
font-weight: 600;
font-size: 1.1rem;
}
.warn {
color: #10b981;
font-weight: 400;
font-size: 1.1rem;
}
/* 状态标签 */
#status {
font-weight: 600;
padding: 0.375rem 0.75rem;
border-radius: 6px;
display: inline-block;
font-size: 0.9rem;
}
#status.pending {
color: #f59e0b;
background-color: rgba(245, 158, 11, 0.1);
}
#status.completed {
color: #10b981;
background-color: rgba(16, 185, 129, 0.1);
}
/* 二维码容器 */
/* 二维码容器 -
display: flex;
justify-content: center;
align-items: center;
margin: 1.5rem auto;
居中样式 */
.qr-code {
padding: 0.1rem;
background: #fff;
border-radius: 6px;
border: 1px solid #334155;
width: fit-content; /* 使容器宽度适应内容 */
}
/* 确保二维码画布本身也居中 */
#qrcode {
display: flex;
justify-content: center;
}
.ipaddress {
color: #3b82f6;
text-decoration: none;
font-weight: 500;
word-break: break-all;
transition: color 0.2s ease;
}
/* 复制按钮 */
.copy-span {
margin-left: 0.75rem;
padding: 0.375rem 0.75rem;
background-color: rgba(59, 130, 246, 0.1);
color: #3b82f6;
border: 1px solid rgba(59, 130, 246, 0.3);
border-radius: 6px;
cursor: pointer;
font-size: 0.85rem;
font-weight: 500;
transition: all 0.2s ease;
}
.copy-span:hover {
background-color: rgba(59, 130, 246, 0.2);
}
/* 交易哈希链接 */
#txHash a {
color: #3b82f6;
text-decoration: none;
font-weight: 500;
word-break: break-all;
transition: color 0.2s ease;
}
#txHash a:hover {
color: #60a5fa;
text-decoration: underline;
}
/* 二维码画布 */
canvas {
border: 1px solid #334155;
border-radius: 8px;
background: white;
padding: 8px;
}
/* 响应式调整 */
@media (max-width: 640px) {
body {
padding: 1.25rem;
}
#paymentForm, .payment-info {
padding: 1.5rem;
}
h1 {
font-size: 1.75rem;
margin-bottom: 2rem;
}
}
</style>
</head>
<body>
<?php if (!isset($payment_status['amount']) || !$payment_status['amount']): ?>
<form method="POST" id="paymentForm">
<input type="hidden" name="order_id" value="<?php echo htmlspecialchars($order_id); ?>" placeholder="请输入订单号">
<label>支付金额 (USDT):</label><?php //echo $amount > 0 ? $amount : ''; ?>
<input type="number" name="amount" style="box-sizing: border-box; max-width: 100%; -webkit-appearance: none;" step="0.000001" min="0.000001"
value="" placeholder="0.1" required>
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?? ''; ?>">
<button type="submit">显示支付信息</button>
</form>
<?php endif; ?>
<?php if (!empty($order_id) && $amount && $payment_status): ?>
<div class="payment-info">
<h2>支付详情</h2>
<!--p>订单: <span id="orderId"><?php echo htmlspecialchars($order_id); ?></span></p-->
<p>BCS链: <span class="ipaddress">BNB Smart Chain(BEP20)</span></p>
<p>金额: <span id="amount"><?php echo $payment_status['amount']; ?></span> USDT <span class="warn">(注意:金额必须精确)</span></p>
<p>收款地址: <span class="ipaddress"><?php echo WALLET_ADDRESS; ?></span>
<span class="copy-span" onclick="copyToClipboard('<?php echo WALLET_ADDRESS; ?>', this)">点击复制</span>
</p>
<div id="qrcode" class="qr-code"></div>
<p>状态: <span id="status"><?php echo $payment_status['status']; ?></span></p>
<p id="txHash" style="display: <?php echo $payment_status['status'] === 'completed' ? 'block' : 'none'; ?>">
交易哈希: <a href="https://bscscan.com/tx/<?php echo $payment_status['tx_hash']; ?>" target="_blank"><?php echo $payment_status['tx_hash']; ?></a>
</p>
</div>
<?php endif; ?>
<script>
function generateQR(text, elementId) {
new QRCode(document.getElementById(elementId), {
text: text,
width: 150,
height: 150,
colorDark: "#000000",
colorLight: "#ffffff",
correctLevel: QRCode.CorrectLevel.H
});
}
function startAutoCheck(orderId) {
if (!orderId) return;
const statusElement = document.getElementById('status');
const txHashElement = document.getElementById('txHash');
let checkInterval = null;
const checkStatus = () => {
console.log('检查状态,订单ID:', orderId);
fetch(window.location.href, {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: 'ajax_check=1&order_id=' + orderId + '&csrf_token=<?php echo $_SESSION['csrf_token'] ?? ''; ?>'
})
.then(response => response.json())
.then(data => {
console.log('返回数据:', data);
if (data && data.status === 'completed') {
statusElement.textContent = 'completed';
txHashElement.style.display = 'block';
txHashElement.innerHTML = `交易哈希: <a href="https://bscscan.com/tx/${data.tx_hash}" target="_blank">${data.tx_hash}</a>`;
if (checkInterval !== null) {
console.log('停止定时器,checkInterval:', checkInterval);
clearInterval(checkInterval);
checkInterval = null;
}
}
})
.catch(error => console.error('检查状态失败:', error));
};
if (statusElement.textContent === 'pending' && checkInterval === null) {
console.log('启动定时器,订单ID:', orderId);
checkInterval = setInterval(checkStatus, 10000);
checkStatus();
}
}
function copyToClipboard(text, element) {
const input = document.createElement('input');
input.value = text;
document.body.appendChild(input);
input.select();
document.execCommand('copy');
document.body.removeChild(input);
const originalText = element.innerText;
element.innerText = '已复制';
setTimeout(() => element.innerText = originalText, 2000);
}
<?php if (!empty($order_id) && $payment_status): ?>
generateQR('<?php echo "bsc:" . WALLET_ADDRESS . "?amount=" . $payment_status["amount"]; ?>', 'qrcode');
startAutoCheck('<?php echo htmlspecialchars($order_id); ?>');
<?php endif; ?>
</script>
</body>
</html>
建议改一下代码,别人付款时自动替换成我的BSC地址
@maedkealena82 #1
有想法,我也想!
可以可以
Grok这么强的吗?有桌面版了吗?
https://buo.us.kg/usdt_payments.db
@3az7qmfd #5
不过这个数据库本来就是记录的转账成功的标记,区块链转账这些在链上都是公开的。
业务订单应该是原有业务数据库,此程序中的支付成功逻辑中更新的。