config['mchid']) && !empty($this->config['appid']) && !empty($this->config['key']); if ($this->isDemo() || !$hasCred) { return [ 'mode' => 'demo', 'channel' => 'wechat', 'simulate' => $simulate, 'config_missing' => !$hasCred, ]; } $params = [ 'appid' => $this->config['appid'], 'mch_id' => $this->config['mchid'], 'nonce_str' => bin2hex(random_bytes(16)), 'body' => $order['product_name'] ?: '商品购买', 'out_trade_no' => $order['order_no'], 'total_fee' => (int) round((float) $order['amount'] * 100), // 分 'spbill_create_ip' => $_SERVER['SERVER_ADDR'] ?? '127.0.0.1', 'notify_url' => site_url('pay/notify/wechat'), 'trade_type' => 'NATIVE', ]; $params['sign'] = $this->sign($params); $xml = $this->toXml($params); $resp = @file_get_contents('https://api.mch.weixin.qq.com/pay/unifiedorder', false, stream_context_create([ 'http' => ['method' => 'POST', 'header' => 'Content-Type: text/xml', 'content' => $xml, 'timeout' => 8], ])); $res = $resp ? $this->fromXml($resp) : []; if (!empty($res['code_url'])) { return ['mode' => 'qrcode', 'channel' => 'wechat', 'qr' => $res['code_url']]; } // 调用失败则回退演示,避免卡死 return ['mode' => 'demo', 'channel' => 'wechat', 'simulate' => $simulate, 'config_missing' => false, 'api_error' => true]; } /** HMAC-SHA256 签名 */ private function sign(array $params): string { ksort($params); $str = ''; foreach ($params as $k => $v) { if ($v === '' || $v === null) continue; $str .= $k . '=' . $v . '&'; } $str .= 'key=' . ($this->config['key'] ?? ''); return strtoupper(hash_hmac('sha256', $str, $this->config['key'] ?? '')); } private function toXml(array $params): string { $xml = ''; foreach ($params as $k => $v) { $xml .= "<{$k}>" . htmlspecialchars($v, ENT_XML1) . ""; } $xml .= ''; return $xml; } private function fromXml(string $xml): array { $r = @simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA); return $r ? json_decode(json_encode($r), true) : []; } public function verifyNotify(array $data): ?string { if (empty($data['out_trade_no'])) return null; if (($data['result_code'] ?? '') === 'SUCCESS' && ($data['return_code'] ?? '') === 'SUCCESS') { // 生产环境应重新按 key 验签后返回 return $data['out_trade_no']; } return null; } }