微信APP支付
来源:互联网 发布:edm邮件营销 知乎 编辑:程序博客网 时间:2024/04/30 08:48
最近写APP的时候重新研究了一下微信APP支付,一直也没时间总结。借着今天不算忙,趁机总结一下。
一、基本流程
1、微信官方文档图:https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=8_1
2、我们自己解释一下:
(1)用户下单—》访问统一下单接口,生成微信返回的数据。由于APP端要用这些数据再次访问微信服务器,因此需要进行二次签名,把二次签名的结果返回到APP
(2)用户跳转到微信的输入密码页面,输入密码之后,微信会服务器会访问我们的回调网址
(3)回调地址中,我们把微信传过来的数据重新生成签名,并且和原来的签名对比,如果签名一致的话,
就返回success的xml数据。这里必须要用微信官方要求的xml格式的数据
这里的支付流程和微信公众号支付很像,只不过一个是APP端,一个是公众号。
大家可以参考我写的公众号支付:http://blog.csdn.net/ljfphp/article/details/76963026
二、用代码演示过程
1、我这里Libs中引用的还是之前微信公众号支付的SDK,只不过新封装了一个WechatAppPay.php类。所以后续的很多方法都在这个类中,具体的代码请往下继续观看。
2、把微信SDK引入到我们的项目中。这边用的是laravel框架
这边先引入微信支付的SDK,里面有我们写的一些方法,是能够用得上的。
3、用户下单—》统一下单接口
(1)我们先走自己写好的路由。处理传过来的参数(钱,商品id等),然后我们生成一个订单号,再把订单号,钱,body等信息传到统一下单接口那边
这边注意按照微信官方文档的要求,传递参数。
(2)统一下单接口代码
/** * 生成App所需预订单参数 * @param string body 商品名称 * @param string out_trade_no 订单号 * @param int total_fee 价格,单位分 * @param array APP端所需的数据 * 示例返回 * [ * 'appid' => 'wx6e9cb610a916f841', * 'partnerid' => '123456',dsd * 'noncestr' => 'ydM3lFIJzk3TFgL7', * 'prepayid' => 'wx201710302056228799e6af3f0129228464', * 'timestamp' => '1509368182', * 'package' => 'Sign=WXPay', * 'sign' => '4F51BC7BFDD5A8D005554D1D206DE12D', * ] */ public function getPrePayOrder($body, $out_trade_no, $total_fee){ $request_url = "https://api.mch.weixin.qq.com/pay/unifiedorder"; $notify_url = $this->config["notify_url"]; $onoce_str = $this->create_noncestr(); $data["appid"] = $this->config["appid"]; $data["body"] = $body; $data["mch_id"] = $this->config['mch_id']; $data["nonce_str"] = $onoce_str; $data["notify_url"] = $notify_url; $data["out_trade_no"] = $out_trade_no; $data["spbill_create_ip"] = $this->get_client_ip(); $data["total_fee"] = $total_fee; $data["trade_type"] = "APP"; $sign = $this->get_sign($data); $data["sign"] = $sign; $xml = $this->array_to_xml($data); $response = $this->post_xml_curl($xml, $request_url); // 将微信返回的结果xml转成数组 $response = $this->xml_to_array($response); // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // 这数据要App请求微信的时候用,又因为请求微信的都需要签名 // 所以又要签名了。这就是二次签名 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ $info['appid'] = $response['appid']; $info['partnerid'] = $response['mch_id']; $info['noncestr'] = $response['nonce_str']; $info['prepayid'] = $response['prepay_id']; $info['timestamp'] = ''. time() .''; $info['package'] = 'Sign=WXPay'; // 官方默认值 // 因为这是新的要用到的数据,所以又要签名了。这就是二次签名 $info['sign'] = $this->get_sign($info); // 返回APP可直接用的数据 return $info; }
这里我们定义了构造函数,用来加载一些必要的参数
/** * 构造函数,完成初始化配置 */ public function __construct(){ $this->config = [ 'appid' => env('wechat_appid'), 'mch_id' => env('wechat_mchid'), 'api_key' => env('wechat_api_key'), 'notify_url' => env('wechat_notify_url') ]; }
解释:按照统一下单接口要求的参数,我们来一一生成这些参数,文档地址:https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_1。
特别注意:我们在调用统一接口之后,这里又进行了二次签名,因为APP客户端每次访问微信服务器都需要这个签名数据。所以我们提前进行二次签名,并且把签名后的数据返回给客户端。注意返回数据的格式需要时json格式的。一定要把数组json_encode一下。
以上的一些方法我也都给出大家
/** * 产生随机字符串,不长于32位 * @param int len 随机字符串长度 * @return string */ private function create_noncestr($len = 32 ){ $str = '0123456789qwertyuiopasdfghjklzxcvbnm'; return substr( str_shuffle($str) , 0 , $len ); } /** * 以POST方式提交xml到对应的接口url * @param string xml XML字符串 * @param string url 请求的对应接口地址 * @param int second 超时设置 * @param string */ private function post_xml_curl($xml, $url, $second=30){ //初始化curl $ch = curl_init(); //设置超时 curl_setopt($ch, CURLOPT_TIMEOUT, $second); // 这里设置代理,如果有的话 // curl_setopt($ch,CURLOPT_PROXY, '8.8.8.8'); // curl_setopt($ch,CURLOPT_PROXYPORT, 8080); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER,FALSE); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST,FALSE); //设置header curl_setopt($ch, CURLOPT_HEADER, FALSE); //要求结果为字符串且输出到屏幕上 curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE); //post提交方式 curl_setopt($ch, CURLOPT_POST, TRUE); curl_setopt($ch, CURLOPT_POSTFIELDS, $xml); //运行curl $data = curl_exec($ch); curl_close($ch); //返回结果 return $data; } /** * 获取当前服务器的IP * @return string */ private 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; } /** * 数组 转 XML字符串 * @param array arr 待转的数组 * @return string */ public function array_to_xml($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; } /** * XML字符串 转 数组 * @param string xml 待转的XML * @return array */ public function xml_to_array($xml){ //将XML转为array $array_data = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true); return $array_data; }
4、统一下单成功之后。用户会在APP端进行输入密码,确认支付等操作。这个时候,微信服务器会调用咱们的回调函数地址。以下是具体代码
(1)回调函数
public function anyPayWechatCallback(Request $request){ // 测试 价格待修改 $xml_string = file_get_contents('php://input'); Log::info('来自微信的通知'); Log::info($xml_string); return $this->wechat->any_pay_callback($xml_string); }
这里通过file_get_contents(‘php://input’);获取微信服务器自带的xml数据,关于php://input的用法,请参考我的博客:http://blog.csdn.net/ljfphp/article/details/78552961
然后我们进入回调函数的逻辑页
(2)回调函数
这边注意看标注
(3)具体的回调逻辑
/** * 生成支付消息,异步通知 * @param string xml 微信端发来的XML通知 * @return array 返回接收通知结果false表示失败,与对应的xml * 示例返回:签名验证结果 * 失败时 ["status": false, "xml": 对应XML字符串 ] * 成功时 ["status": true, "xml": 对应XML字符串 ] */ public function handleOrderNotify($xml){ //先把xml数据转变为数组格式的 $tmp_arr = $this->xml_to_array($xml); //我们把微信带过来的数据进行重新签名,得到新的签名数据$sign $sign= $this->get_sign($tmp_arr); //这边是把新的$sign和微信传过来的签名进行对比。如果一致的话,代表支付成功 if ( $sign === $tmp_arr['sign']) { //支付成功,此时我们需要返回成功 $res['return_code'] = 'SUCCESS'; $res['return_msg'] = 'OK'; $arr['status'] = true; }else{ //支付失败,我们返回失败。必须按格式 $res['return_code'] = ''; $res['return_msg'] = ''; $return = ['return_code'=>'FAIL','return_msg'=>'签名失败']; $arr['status'] = false; } // 拼接 XML //把数据拼接为xml格式的。因为微信服务器只能识别xml格式数据的结果。 $arr['xml'] = '<xml>'; foreach($res as $k=>$v){ $arr['xml'].='<'.$k.'><![CDATA['.$v.']]></'.$k.'>'; } $arr['xml'] .= '</xml>'; //返回拼接好的xml数据 return $arr; }
这边具体的看注释吧。需要注意的是,我们要在返回xml数据给微信服务器之前,把我们需要进行的业务逻辑都写好。进行日志操作等。
5、此时微信服务器收到我们返回给它的xml数据。一般来说,走到这一步就已经成功了。记得加上自己的业务逻辑。
这边建议最好是先看微信给的官方文档,然后知道大致的步骤之后,再按照我的这篇博客进行支付操作。给出的代码都是经过我自己测试的,不会有什么问题。
有什么问题的话,请给我留言。谢谢
end
- 支付--微信APP支付
- 微信支付app
- 微信支付app
- 微信APP支付
- app微信支付
- APP微信支付
- app微信支付
- 微信APP支付
- 支付--微信APP
- 微信APP支付
- 微信app支付
- 微信APP支付
- 微信APP支付
- APP微信支付集成
- 微信app支付问题
- 微信APP支付填坑记
- 微信支付 app 服务器
- 微信APP支付 C#
- 一元二次方程的因式分解 X的二次方+X-12=0如何用因式分解
- Vue.js源码解析(七)【聊聊Vue.js的template编译】
- 链表(单向、双向、单向循环、双向循环)
- 【rabbitmq】Linux下面安装包形式安装
- centos7.2+k8s1.4.1+docker1.12集群部署
- 微信APP支付
- 在Linux(Ubuntu kylin 16.04 LTS)环境下安装L4re内核以及Fiasco,并在其上模拟ARM32位架构环境
- linux安装Tomcat
- Python编程从入门到实践:习题4-3~4-9
- 因式分解法解一元二次方程
- Kaggle笔记:Porto Seguro’s Safe Driver Prediction(1)
- 网游服务端架构
- 【转】PowerPC DPAA平台启动方式
- 数据库与asm实例的通讯问题