PHP端:购物车发货前material验证 + 退款接收接口

- shop.php buy_cart: 扣款前验证每件商品material为Java可识别值(合法Material或附魔书id+ENCHANTED_BOOK组合)
- sync.php: 新增write_shop_refund action接收Java退款请求(恢复bond_cache+写refund交易)
This commit is contained in:
ypsdf1
2026-07-09 10:54:59 +08:00
parent c1234c6700
commit 1e97285459
2 changed files with 95 additions and 0 deletions
+74
View File
@@ -284,6 +284,9 @@ switch ($action) {
case 'clear_admin_prices':
clearAdminPrices();
break;
case 'write_shop_refund':
writeShopRefund();
break;
case 'check_shop_prices_changed':
checkShopPricesChanged();
break;
@@ -2941,6 +2944,77 @@ function clearAdminPrices() {
success([], '已清除管理员价格标记');
}
// ===== Java端写入购物车退款请求(发货失败时触发) =====
function writeShopRefund() {
$secret = getParam('secret');
if (!$secret || $secret !== SECRET_KEY) error('认证失败');
$rawInput = file_get_contents('php://input');
$data = json_decode($rawInput, true);
if (!$data) error('无效JSON');
$playerName = $data['player_name'] ?? '';
$amount = (int)($data['amount'] ?? 0);
$origTxId = $data['orig_tx_id'] ?? '';
$reason = $data['reason'] ?? '';
$payMode = $data['pay_mode'] ?? 'bond';
if (!$playerName || $amount <= 0) error('退款参数缺失');
if ($payMode !== 'cash') {
// 非现金模式:退还债券
$db = getDB();
$db->exec('BEGIN IMMEDIATE');
try {
$stmt = $db->prepare("UPDATE bond_cache SET amount = amount + :amt WHERE player_name = :player");
$stmt->bindValue(':amt', $amount, SQLITE3_INTEGER);
$stmt->bindValue(':player', $playerName, SQLITE3_TEXT);
$stmt->execute();
if ($stmt->changes() === 0) {
// 玩家不在缓存中,插入
$ins = $db->prepare("INSERT OR IGNORE INTO bond_cache (player_name, amount) VALUES (:player, :amt)");
$ins->bindValue(':player', $playerName, SQLITE3_TEXT);
$ins->bindValue(':amt', $amount, SQLITE3_INTEGER);
$ins->execute();
}
// 记录退款交易
$refStmt = $db->prepare("INSERT INTO web_transactions (player_name, type, amount, reason, detail, status, created_at) VALUES (:player, 'shop_refund', :amount, :reason, :detail, 'completed', :time)");
$refStmt->bindValue(':player', $playerName, SQLITE3_TEXT);
$refStmt->bindValue(':amount', $amount, SQLITE3_INTEGER);
$refStmt->bindValue(':reason', "购物车自动退款(发货失败): " . $reason . " [原交易#" . $origTxId . "]", SQLITE3_TEXT);
$refStmt->bindValue(':detail', json_encode(['orig_tx_id' => $origTxId, 'refund_reason' => $reason, 'pay_mode' => $payMode], JSON_UNESCAPED_UNICODE), SQLITE3_TEXT);
$refStmt->bindValue(':time', time(), SQLITE3_INTEGER);
$refStmt->execute();
// 标记原交易为已退款(避免重复处理)
$markStmt = $db->prepare("UPDATE web_transactions SET status = 'refunded' WHERE id = :txid OR reason LIKE :txidLike");
$markStmt->bindValue(':txid', $origTxId, SQLITE3_TEXT);
$markStmt->bindValue(':txidLike', '%' . $origTxId . '%', SQLITE3_TEXT);
$markStmt->execute();
$db->exec('COMMIT');
debugLog('[shop_refund] 退款成功: 玩家=' . $playerName . ' 金额=' . $amount . ' 原因=' . $reason);
success(['refunded_amount' => $amount, 'player' => $playerName], '退款成功');
} catch (\Throwable $e) {
try { $db->exec('ROLLBACK'); } catch (\Throwable $_) {}
error('退款处理失败: ' . $e->getMessage(), 500);
}
} else {
// 现金模式:仅记账退款标记,不操作债券
$db = getDB();
try {
$refStmt = $db->prepare("INSERT INTO web_transactions (player_name, type, amount, reason, detail, status, created_at) VALUES (:player, 'shop_refund', 0, :reason, :detail, 'completed', :time)");
$refStmt->bindValue(':player', $playerName, SQLITE3_TEXT);
$refStmt->bindValue(':reason', "购物车退款标记(现金模式/发货失败): " . $reason, SQLITE3_TEXT);
$refStmt->bindValue(':detail', json_encode(['orig_tx_id' => $origTxId, 'refund_reason' => $reason, 'pay_mode' => 'cash'], JSON_UNESCAPED_UNICODE), SQLITE3_TEXT);
$refStmt->bindValue(':time', time(), SQLITE3_INTEGER);
$refStmt->execute();
success(['refunded_amount' => 0, 'player' => $playerName, 'note' => '现金模式仅记账'], '退款标记已记录(现金模式不扣债券)');
} catch (\Throwable $e) {
error('退款标记失败: ' . $e->getMessage(), 500);
}
}
}
// ===== 高频轮询:检测价格是否有改动(轻量级接口) =====
function checkShopPricesChanged() {
$secret = getParam('secret');