[--amount=金额] [--dry-run]\n" . " php deploy/qyt_consume_push.php --code=<运单唯一流水> --amount=金额\n" . "详细参数见本文件头部注释。\n"); } // ---------------- 读取 .env ---------------- $ini = @parse_ini_file($root . '/.env', true, INI_SCANNER_RAW) ?: []; $db = $ini['DATABASE'] ?? []; $qyt = $ini['QYT'] ?? []; $url = isset($opt['url']) ? (string) $opt['url'] : trim((string) ($qyt['CONSUME_NOTIFY_URL'] ?? '')); if ($url === '') { $url = 'http://127.0.0.1/qytconsume.php'; } // ---------------- 取订单(可选) ---------------- $pdo = null; $order = null; try { $pdo = new PDO( 'mysql:host=' . ($db['HOSTNAME'] ?? '127.0.0.1') . ';port=' . ($db['HOSTPORT'] ?? '3306') . ';dbname=' . ($db['DATABASE'] ?? 'oil') . ';charset=' . ($db['CHARSET'] ?? 'utf8'), $db['USERNAME'] ?? 'root', $db['PASSWORD'] ?? '', [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC] ); } catch (Throwable $e) { echo "提示:连不上数据库({$e->getMessage()}),将只发送推送、不做前后对比\n\n"; } if ($pdo) { $sql = $orderId > 0 ? 'SELECT * FROM yoshop_partners_order WHERE order_id = ? LIMIT 1' : 'SELECT * FROM yoshop_partners_order WHERE waybill_no_code = ? LIMIT 1'; $st = $pdo->prepare($sql); $st->execute([$orderId > 0 ? $orderId : $code]); $order = $st->fetch() ?: null; if (!$order && $orderId > 0) { exit("未找到订单 order_id={$orderId}\n"); } } if ($order) { $code = (string) $order['waybill_no_code']; if ($code === '') { exit("订单 {$order['order_id']} 还没有运单流水号(waybill_no_code 为空),说明 7.7 还没成功下发,不能模拟加油\n"); } } if ($code === '') { exit("缺少 waybill_no_code:请用 --code 指定,或用 --order 指定订单\n"); } // ---------------- 金额 ---------------- $remain = $order ? round((float) $order['total_price'] - (float) $order['used_amount'], 2) : 0.0; $amount = isset($opt['amount']) ? round((float) $opt['amount'], 2) : $remain; if ($amount <= 0) { exit("加油金额必须大于 0(订单剩余未加油金额为 " . number_format($remain, 2) . " 元,可用 --amount 指定)\n"); } if ($order && $amount > $remain + 0.005) { echo "⚠️ 本次金额 {$amount} 元 超过订单剩余未加油金额 " . number_format($remain, 2) . " 元," . "服务端会照常入账并写超额告警日志\n"; } // ---------------- 组装报文(字段与 7.10 文档一致) ---------------- $fen = (int) round($amount * 100); $item = [ 'waybill_no' => $order['waybill_no'] ?? ('SIMW' . $code), 'order_code' => $orderCode !== '' ? $orderCode : ('SIM' . date('YmdHis') . mt_rand(1000, 9999)), 'waybill_no_code' => $code, 'quota_id' => $order['qyt_order_code'] ?? ('SIMQ' . mt_rand(100000, 999999)), 'passageway' => ($order['passageway'] ?? 'alipay') === 'pingan' ? 2 : 1, 'station_code' => '', 'station_name' => $station, 'pay_fee' => $fen, 'pay_time' => date('Y-m-d H:i:s'), 'consume_amount' => $fen, 'litter' => isset($opt['litter']) ? (string) $opt['litter'] : '', 'oil_name' => isset($opt['oil_name']) ? (string) $opt['oil_name'] : '', 'oil_price' => '', 'actual_oil_price' => '', 'scene' => 'oil', ]; $params = json_encode(['order_list' => [$item]], JSON_UNESCAPED_UNICODE); echo "回调地址:{$url}\n"; if ($order) { printf("订单:order_id=%s 油卡号=%s 金额=%s 已加油=%s 剩余=%s 加油次数=%s\n", $order['order_id'], $order['oilCardNo'], $order['total_price'], $order['used_amount'], number_format($remain, 2), $order['used_num']); } echo "本次加油:{$amount} 元({$fen} 分) 消费单号(去重键):{$item['order_code']}\n"; echo "params:{$params}\n\n"; if ($dryRun) { echo "--dry-run:未发送\n"; exit; } // ---------------- 发送:multipart/form-data,params 放表单字段 ---------------- $ch = curl_init(); curl_setopt_array($ch, [ CURLOPT_URL => $url, CURLOPT_POST => true, // 传数组 => cURL 自动用 multipart/form-data 编码,与企油通口径一致 CURLOPT_POSTFIELDS => ['params' => $params], CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 30, CURLOPT_SSL_VERIFYPEER => false, CURLOPT_SSL_VERIFYHOST => false, ]); $resp = curl_exec($ch); $http = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE); $err = curl_error($ch); curl_close($ch); echo "HTTP {$http}\n响应:" . ($resp === false ? "cURL 失败:{$err}" : $resp) . "\n"; $ok = is_string($resp) && strpos($resp, '"100"') !== false; echo $ok ? "接收端已受理(code=100)\n" : "⚠️ 接收端未返回 code=100,请查 runtime/storelog 下当天的 qyt_*.txt\n"; // ---------------- 等队列处理并对比结果 ---------------- if (!$ok || !$pdo || !$order) { echo "\n提示:真正的落库与订单累计由 callback:queue 进程完成,请确认该进程在运行。\n"; exit; } echo "\n等待 callback:queue 处理"; $after = null; for ($i = 0; $i < 10; $i++) { sleep(1); echo '.'; $st = $pdo->prepare('SELECT used_amount, used_num, use_status, refund_status FROM yoshop_partners_order WHERE order_id = ?'); $st->execute([$order['order_id']]); $after = $st->fetch(); if ($after && (float) $after['used_amount'] > (float) $order['used_amount']) { break; } } echo "\n"; if ($after && (float) $after['used_amount'] > (float) $order['used_amount']) { printf("✅ 已入账:已加油 %s -> %s,加油次数 %s -> %s,加油状态=%s\n", $order['used_amount'], $after['used_amount'], $order['used_num'], $after['used_num'], (int) $after['use_status'] === 1 ? '已加油' : '未加油'); $st = $pdo->prepare('SELECT id, order_code, consume_amount, notify_status FROM yoshop_qyt_consume WHERE order_id = ? ORDER BY id DESC LIMIT 3'); $st->execute([$order['order_id']]); echo "最近消费流水:\n"; foreach ($st->fetchAll() as $r) { printf(" #%s %s %s元 通知状态=%s\n", $r['id'], $r['order_code'], number_format($r['consume_amount'] / 100, 2), $r['notify_status'] ? '已通知' : '待通知'); } } else { echo "⚠️ 10 秒内订单没有变化,可能原因:\n" . " 1) callback:queue 进程没在跑(supervisorctl status 看一下)\n" . " 2) 该消费单号已存在,被唯一键判为重复推送(换一个 --order-code 再试)\n" . " 3) 处理异常,查 runtime/storelog 下当天的 " . date('YmdH') . ".txt\n"; }