300 || !preg_match('/^[A-Za-z0-9_-]{16,64}$/D', $nonce) || !preg_match('/^[0-9a-f]{64}$/iD', $received)) { callbackReply(401, '回调鉴权失败'); return; } $expected = signature(requiredEnv('ABCPAY_API_KEY'), 'POST', $path, $timestamp, $nonce, $body); if (!hash_equals($expected, strtolower($received))) { callbackReply(401, '回调签名错误'); return; } $event = json_decode($body, true); if (!is_array($event) || empty($event['noticeId'])) { callbackReply(400, '回调报文不正确'); return; } // 演示接收:正式业务应先持久化 noticeId 并完成幂等处理,再返回 ACK。 error_log('ABCPAY 回调 ' . $event['noticeId']); callbackReply(200, 'ACK'); } function sendRequest(array $args): void { if (!extension_loaded('curl')) { throw new RuntimeException('请启用 PHP cURL 扩展'); } $paths = [ '/abcpay/collection/create', '/abcpay/payout/create', '/abcpay/wallet/balance', '/abcpay/upi/query', '/abcpay/utr/query', '/abcpay/order/query' ]; $path = $args[1] ?? '/abcpay/wallet/balance'; if (!in_array($path, $paths, true)) { throw new InvalidArgumentException('接口路径不在示例范围内'); } $body = isset($args[2]) ? file_get_contents($args[2]) : '{}'; if ($body === false) throw new RuntimeException('无法读取请求报文文件'); $base = getenv('ABCPAY_BASE_URL') ?: 'https://abc.yamipay.cc'; $timestamp = (string) time(); $nonce = bin2hex(random_bytes(16)); $headers = [ 'Content-Type: application/json; charset=UTF-8', 'Abcpay-Merchant: ' . requiredEnv('ABCPAY_MERCHANT'), 'Abcpay-Timestamp: ' . $timestamp, 'Abcpay-Nonce: ' . $nonce, 'Abcpay-Signature: ' . signature(requiredEnv('ABCPAY_API_KEY'), 'POST', $path, $timestamp, $nonce, $body) ]; $handle = curl_init(rtrim($base, '/') . $path); curl_setopt_array($handle, [ CURLOPT_POST => true, CURLOPT_POSTFIELDS => $body, CURLOPT_HTTPHEADER => $headers, CURLOPT_RETURNTRANSFER => true, CURLOPT_CONNECTTIMEOUT => 10, CURLOPT_TIMEOUT => 20 ]); $response = curl_exec($handle); if ($response === false) { $error = curl_error($handle); curl_close($handle); throw new RuntimeException('请求失败:' . $error); } $status = curl_getinfo($handle, CURLINFO_HTTP_CODE); curl_close($handle); echo 'HTTP ' . $status . PHP_EOL . $response . PHP_EOL; } if (PHP_SAPI === 'cli-server') { handleCallback(); } elseif (PHP_SAPI === 'cli') { sendRequest($argv); } else { throw new RuntimeException('请使用 PHP 命令行或内置 Web 服务运行'); }