统一支付是JSAPI/NATIVE/APP各种支付场景下生成支付订单,返回预支付订单号的接口,目前微信支付所有场景均使用这一接口。下面介绍的是其中NATIVE的支付实现流程与PC端实现扫码支付流程
前言
统一支付是JSAPI/NATIVE/APP各种支付场景下生成支付订单,返回预支付订单号的接口,目前微信支付所有场景均使用这一接口。下面介绍的是其中NATIVE的支付实现流程与PC端实现扫码支付流程
流程实现(后端)(PHP)
- 创建Wechatpay.php文件,放到指定文件目录下(我是放到了extend目录)
| <?php |
| |
| class Wechatpay{ |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| function curl_https($url, $xml='', $useCert=false){ |
| |
| $ch = curl_init(); |
| |
| curl_setopt($ch, CURLOPT_TIMEOUT, 30); |
| curl_setopt($ch,CURLOPT_URL, $url); |
| |
| |
| curl_setopt($ch, CURLOPT_HEADER, FALSE); |
| |
| curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE); |
| |
| if(stripos($url,"https://")!==FALSE){ |
| curl_setopt($ch, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1); |
| curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE); |
| curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE); |
| }else{ |
| curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,TRUE); |
| curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,2); |
| } |
| if($useCert == true){ |
| |
| |
| curl_setopt($ch,CURLOPT_SSLCERTTYPE,'PEM'); |
| curl_setopt($ch,CURLOPT_SSLCERT,""); |
| curl_setopt($ch,CURLOPT_SSLKEYTYPE,'PEM'); |
| curl_setopt($ch,CURLOPT_SSLKEY,""); |
| } |
| |
| curl_setopt($ch, CURLOPT_POST, TRUE); |
| curl_setopt($ch, CURLOPT_POSTFIELDS, $xml); |
| |
| $data = curl_exec($ch); |
| |
| if($data){ |
| curl_close($ch); |
| return $data; |
| } else { |
| $error = curl_errno($ch); |
| curl_close($ch); |
| |
| } |
| |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| function ToUrlParams($urlObj) |
| { |
| $buff = ""; |
| foreach ($urlObj as $k => $v) |
| { |
| if($k != "sign"){ |
| $buff .= $k . "=" . $v . "&"; |
| } |
| } |
| |
| $buff = trim($buff, "&"); |
| return $buff; |
| } |
| |
| |
| |
| function arrayToXml($arr) |
| { |
| $xml = "<xml>"; |
| foreach ($arr as $key=>$val) |
| { if (is_numeric($val)){ |
| $xml.="<".$key.">".$val."</".$key.">"; |
| }else{ |
| $xml.="<".$key."><![CDATA[".$val."]]></".$key.">"; |
| |
| } |
| |
| } |
| $xml.="</xml>"; |
| return $xml; |
| } |
| |
| |
| function xmlToArray($xml) |
| { |
| |
| libxml_disable_entity_loader(true); |
| $values = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true); |
| return $values; |
| } |
| |
| |
| |
| |
| |
| function getRandString($len=12,$str='ABCDEFGHIJKLMNOPQRSTUVWYXZabcdefghijklmnopqrstuvwyxz1234567890'){ |
| $strlen=strlen($str)-1; |
| $string=''; |
| for ($i=0; $i < $len; $i++) { |
| $r=rand(1,$strlen); |
| $string=$string.$str[$r]; |
| } |
| return $string; |
| } |
| |
| |
| } |
2.定义公共变量
| private $config = array( |
| 'appid' => "wxa******", |
| 'appid_app' => "wx******", |
| 'mch_id' => "*******", |
| 'api_key' => "*************", |
| 'notify_url' => 'https://***', |
| ); |
3.支付接口
| public function NativePay($id,$price,$body,$type,$attach){ |
| |
| Loader::import('wechatpay.Wechatpay', EXTEND_PATH,".php"); |
| $wechatpay = new \Wechatpay(); |
| $url='https://api.mch.weixin.qq.com/pay/unifiedorder'; |
| $orderID='XXX'.time().rand(0,9).rand(0,9).rand(0,9); |
| $parameters=array( |
| 'appid'=>$this->config["appid"], |
| 'mch_id'=>$this->config['mch_id'], |
| 'nonce_str'=>$wechatpay->getRandString(30), |
| 'body'=>$body, |
| 'out_trade_no'=>$orderID, |
| 'total_fee'=>$price*100, |
| 'spbill_create_ip'=>$this->get_client_ip(), |
| 'notify_url'=>$this->config["notify_url"], |
| 'trade_type'=>'NATIVE', |
| 'attach'=>$attach |
| ); |
| |
| ksort($parameters); |
| |
| $String = $wechatpay->ToUrlParams($parameters); |
| |
| $String = $String."&key=".$this->config['api_key']; |
| |
| $parameters['sign']=strtoupper(md5($String)); |
| $xmlData=$wechatpay->arrayToXml($parameters); |
| $return=$wechatpay->xmlToArray($wechatpay->curl_https($url,$xmlData)); |
| if($return["return_code"]=="SUCCESS" && $return["result_code"]=="SUCCESS"){ |
| |
| |
| $returnData=[ |
| 'code_url'=>$return["code_url"], |
| 'orderID'=>$orderID |
| ]; |
| |
| $this->result($returnData,'1','支付二维码生成成功!','json'); |
| }else{ |
| echo json_encode(array("status"=>false,"msg"=>$return)); |
| } |
| |
| |
| } |
| |
| |
| |
| |
| public function get_client_ip(){ |
| if ($_SERVER['REMOTE_ADDR']) { |
| $cip = $_SERVER['REMOTE_ADDR']; |
| } elseif (getenv("REMOTE_ADDR")) { |
| $cip = getenv("REMOTE_ADDR"); |
| } elseif (getenv("HTTP_CLIENT_IP")) { |
| $cip = getenv("HTTP_CLIENT_IP"); |
| } else { |
| $cip = "unknown"; |
| } |
| return $cip; |
| } |
- 支付回调
| |
| public function wxpaynotify(){ |
| $xml = file_get_contents('php://input'); |
| Loader::import('wechatpay.Wechatpay', EXTEND_PATH,".php"); |
| $wechatpay = new \Wechatpay(); |
| |
| $data = $wechatpay->xmlToArray($xml); |
| |
| $data_sign = $data['sign']; |
| |
| unset($data['sign']); |
| $sign = $wechatpay->ToUrlParams($data); |
| $payData=$sign; |
| $sign=strtoupper(md5($sign."&key=".$this->config["api_key"])); |
| |
| if ( ($sign===$data_sign) && ($data['result_code']=='SUCCESS') ) { |
| |
| |
| |
| $result = true; |
| }else{ |
| file_put_contents('payResult.tex','验签失败!'); |
| $result = false; |
| } |
| |
| if ($result) { |
| $str='<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>'; |
| }else{ |
| $str='<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[签名失败]]></return_msg></xml>'; |
| } |
| echo $str; |
| |
| } |
流程实现(前端)(Vue)
| |
| |
| <canvas id="qrcode" ref="qrcode"></canvas> |
| |
| |
| //导入二维码组件QRCode |
| import QRCode from 'qrcode'; |
| |
| let qrcodeUrl = res.data.code_url |
| const canvas = this.$refs.qrcode |
| QRCode.toCanvas(canvas, qrcodeUrl, function (error) { |
| if (error) console.error(error) |
| console.log('QR code generated successfully.') |
| }) |
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 推荐几款开源且免费的 .NET MAUI 组件库
· 实操Deepseek接入个人知识库
· 易语言 —— 开山篇
· Trae初体验