php开发微信APP支付接口

开发流程

1:用户在商户APP中选择商品,提交订单,选择微信支付。
2:商户后台收到用户支付单,调用微信支付统一下单接口。参见【统一下单API】。
3:统一下单接口返回正常的prepay_id,再按签名规范重新生成签名后,将数据传输给APP。参与签名的字段名为appid,partnerid,prepayid,noncestr,timestamp,package。注意:package的值格式为Sign=WXPay
4:商户APP调起微信支付。api参见本章节【app端开发步骤说明】
5:商户后台接收支付通知。api参见【支付结果通知API】
6:商户后台查询支付结果。,api参见【查询订单API】

开发中

首先呢我们需要拿到三个参数(appid,mch_id,key),这三个参数分别对应的是 在微信开发平台中创建的移动应用appid,微信支付商户号商户支付秘钥,详情看参考【支付结果通知API】
然后我们先把统一下单所需要的参数列出来


 
  1. $request_data = array(
  2. 'appid' => C('WX_APPID'), #应用APPID
  3. 'mch_id' => C('WX_MCHID'), #商户号
  4. 'trade_type' => 'APP', #支付类型
  5. 'nonce_str' => OrgUtilString::randString(30), #随机字符串 不长于32位
  6. 'body' => '商品名称', #商品名称
  7. 'out_trade_no' => '12345678912456', #商户后台订单号
  8. 'total_fee' => '1', #商品价格
  9. 'spbill_create_ip' => get_client_ip(), #用户端实际ip
  10. 'notify_url' => 'http://***/app/index.php/Home/Wxpay/wx_notify', #异步通知回调地址
  11. );

这些都是请求参数必填项,其他参数请查看文档
下来我们就要使用这些参数生成签名了


 
  1. $request_data['sign'] = $this -> get_sign($request_data);

我们下来需要把微信请求的数据拼装成 xml格式,注意:xml数据要使用<![CDATA[]]>包括


 
  1. $xml_data = $this -> set_xmldata($request_data);
  2. 打印$xml_data结果如下
  3. <xml>
  4. <appid><![CDATA[wx7ad3cc6c6111111]]></appid>
  5. <mch_id><![CDATA[1494741111]]></mch_id>
  6. <trade_type><![CDATA[APP]]></trade_type>
  7. <nonce_str><![CDATA[WXXWkMDOgLIqhUnITfNrBbJEVGQdRO]]></nonce_str>
  8. <body><![CDATA[u5546u54c1u540du79f0]]></body>
  9. <out_trade_no><![CDATA[12345678912456]]></out_trade_no>
  10. <total_fee><![CDATA[1]]></total_fee>
  11. <spbill_create_ip><![CDATA[1.86.242.193]]></spbill_create_ip>
  12. <notify_url><![CDATA[http://***/app/index.php/Home/Wxpay/wx_notify]]></notify_url>
  13. <sign><![CDATA[EC0BFB3434A72F20C2CA3378BF07264C]]></sign>
  14. </xml>

现在就可以向微信发送请求了


 
  1. $res = $this -> send_prePaycurl($xml_data);
  2. 这是请求的返回值
  3. {
  4. return_code: "SUCCESS", #业务结果 只有这里返回SUCCESS才会有prepay_id
  5. return_msg: "OK", #返回结果描述
  6. appid: "wx7ad3cc6c6111111", #应用APPID
  7. mch_id: "1494741111", #商户号
  8. nonce_str: "jkh9mmRlmSHBJxO0", #随机字符串
  9. sign: "AF3B26B1E58591D6565E61DDFBB7837B", #签名
  10. result_code: "SUCCESS", #也是业务结果
  11. prepay_id: "wx20171226005556c5c65b325a0132782836", #预支付交易会话标识,用于APP请求微信支付调用,有效期两小时
  12. trade_type: "APP" #支付类型
  13. }

到这里拿到prepay_id还没完我们还需要对返回的数据进行二次签名


 
  1. if($res['return_code'] == 'SUCCESS' && $res['result_code'] == 'SUCCESS'){
  2. $two_data['appid'] = C('WX_APPID'); #APPID
  3. $two_data['partnerid'] = C('WX_MCHID'); #商户号
  4. $two_data['prepayid'] = $res['prepay_id']; //预支付交易会话标识
  5. $two_data['noncestr'] = OrgUtilString::randString(30);
  6. $two_data['timestamp'] = time(); #时间戳
  7. $two_data['package'] = "Sign=WXPay"; #固定值
  8. $two_data['sign'] = $this -> get_twosign($two_data); #二次签名
  9. $this->ajaxReturn(array('code'=>200,'info'=>$two_data));
  10. }else{
  11. $this->ajaxReturn(array('code'=>201,'info'=>$res['err_code_des']));
  12. }

然后就可以在商户APP端通过prepayid进行支付了
下面我们来列出上面调用的几个公共方法


 
  1. //一次签名的函数
  2. private function get_sign($data){
  3. ksort($data);
  4. $str = '';
  5. foreach ($data as $key => $value) {
  6. $str .= !$str ? $key . '=' . $value : '&' . $key . '=' . $value;
  7. }
  8. $str.='&key='.C('WX_KEY');
  9. $sign = strtoupper(md5($str));
  10. return $sign;
  11. }
  12. //二次签名的函数
  13. private function get_twosign($data){
  14. $sign_data = array(
  15. "appid"=>$data['appid'],
  16. "partnerid"=>$data['partnerid'],
  17. "prepayid"=>$data['prepayid'],
  18. "noncestr"=>$data['noncestr'],
  19. "timestamp"=>$data['timestamp'],
  20. "package"=>$data['package'],
  21. );
  22. return $this -> get_sign($sign_data);
  23. }
  24. //生成xml格式的函数
  25. private function set_xmldata($data) {
  26. $xmlData = "<xml>";
  27. foreach ($data as $key => $value) {
  28. $xmlData.="<".$key."><![CDATA[".$value."]]></".$key.">";
  29. }
  30. $xmlData = $xmlData."</xml>";
  31. return $xmlData;
  32. }
  33. //通过curl发送数据给微信接口的函数
  34. private function send_prePaycurl($xmlData) {
  35. $url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
  36. $header[] = "Content-type: text/xml";
  37. $curl = curl_init();
  38. curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
  39. curl_setopt($curl, CURLOPT_URL, $url);
  40. curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
  41. curl_setopt($curl, CURLOPT_POST, 1);
  42. curl_setopt($curl, CURLOPT_POSTFIELDS, $xmlData);
  43. $data = curl_exec($curl);
  44. if (curl_errno($curl)) {
  45. print curl_error($curl);
  46. }
  47. curl_close($curl);
  48. return $this -> _xmldataparse($data);
  49. }
  50. //xml数据解析函数
  51. private function _xmldataparse($data){
  52. $msg = array();
  53. $msg = (array)simplexml_load_string($data, 'SimpleXMLElement', LIBXML_NOCDATA);
  54. return $msg;
  55. }

微信回调

支付有了,肯定还得有回调


 
  1. //微信回调
  2. public function wx_notify(){
  3. //允许从外部加载XML实体(防止XML注入攻击)
  4. libxml_disable_entity_loader(true);
  5. $postStr = $this -> post_data(); #接收微信返回数据xml格式
  6. $postObj = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA); #xml格式数据转换成对象
  7. $arr = $this -> object_toarray($postObj); #对象转成数组
  8. ksort($arr); # 对数据进行排序
  9. $str = $this -> params_tourl($arr); #对数据拼接成字符串
  10. $user_sign = strtoupper(md5($str)); //把微信返回的数据进行再次签名
  11. //验证签名
  12. if($user_sign == $arr['sign']){
  13. //验证签名成功 处理商户订单逻辑
  14. //给微信返回接收到数据通知
  15. return '<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>';
  16. }else{
  17. //签名验证失败 微信会再次访问回调方法
  18. return '<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>';
  19. }
  20. }

回调用到的方法如下


 
  1. // 接收post数据
  2. /*
  3. * 微信是用$GLOBALS['HTTP_RAW_POST_DATA'];这个函数接收post数据的
  4. */
  5. public function post_data(){
  6. $receipt = $_REQUEST;
  7. if($receipt==null){
  8. $receipt = file_get_contents("php://input");
  9. if($receipt == null){
  10. $receipt = $GLOBALS['HTTP_RAW_POST_DATA'];
  11. }
  12. }
  13. return $receipt;
  14. }
  15.  
  16. //把对象转成数组
  17. public function object_toarray($arr) {
  18. if(is_object($arr)) {
  19. $arr = (array)$arr;
  20. } if(is_array($arr)) {
  21. foreach($arr as $key=>$value) {
  22. $arr[$key] = $this->object_toarray($value);
  23. }
  24. }
  25. return $arr;
  26. }
  27.  
  28.  
  29. /**
  30. * 格式化参数格式化成url参数
  31. */
  32. private function params_tourl($arr)
  33. {
  34. $weipay_key = C('WX_KEY');//微信的key,这个是微信支付给你的key,不要瞎填。
  35. $buff = "";
  36. foreach ($arr as $k => $v)
  37. {
  38. if($k != "sign" && $v != "" && !is_array($v)){
  39. $buff .= $k . "=" . $v . "&";
  40. }
  41. }
  42. $buff = trim($buff, "&");
  43. return $buff.'&key='.$weipay_key;
  44. }
/

总结:首先微信支付的流程比较多,公众号 ,开放平台微信商户,配置参数的时候要看仔细,不要后面的坑特别多,因为是第一次写微信支付,可能会存在部分问题,欢迎大家可以在下面留言反馈。

这是最后完成的功能


下面分享一下全部的代码


 
  1. <?php
  2. namespace HomeController;
  3. use ThinkController;
  4. /**
  5. * php开发微信app支付接口
  6. * @global WX_APPID 开放平台->移动应用appid
  7. * @global WX_MCHID 微信支付商户号
  8. * @global WX_KEY 商户支付秘钥
  9. * @author codehi <admin@codehui.net> 2017-12-23
  10. */
  11. class WxpayController extends Controller
  12. {
  13. /**
  14. * 微信支付统一下单 >>> 生成预支付交易单
  15. */
  16. public function wx_pay(){
  17.  
  18. $request_data = array(
  19. 'appid' => C('WX_APPID'), #应用APPID
  20. 'mch_id' => C('WX_MCHID'), #商户号
  21. 'trade_type' => 'APP', #支付类型
  22. 'nonce_str' => OrgUtilString::randString(30), #随机字符串 不长于32位
  23. 'body' => '商品名称', #商品名称
  24. 'out_trade_no' => '12345678912456', #商户后台订单号
  25. 'total_fee' => '1', #商品价格
  26. 'spbill_create_ip' => get_client_ip(), #用户端实际ip
  27. 'notify_url' => 'http://***/app/index.php/Home/Wxpay/wx_notify', #异步通知回调地址
  28. );
  29. // 获取签名
  30. $request_data['sign'] = $this -> get_sign($request_data);
  31. // 拼装数据
  32. $xml_data = $this -> set_xmldata($request_data);
  33.  
  34.  
  35. // 发送请求
  36. $res = $this -> send_prePaycurl($xml_data);
  37. $this->ajaxReturn($res);
  38. if($res['return_code'] == 'SUCCESS' && $res['result_code'] == 'SUCCESS'){
  39. $two_data['appid'] = C('WX_APPID'); #APPID
  40. $two_data['partnerid'] = C('WX_MCHID'); #商户号
  41. $two_data['prepayid'] = $res['prepay_id']; //预支付交易会话标识
  42. $two_data['noncestr'] = OrgUtilString::randString(30);
  43. $two_data['timestamp'] = time();
  44. $two_data['package'] = "Sign=WXPay";
  45. $two_data['sign'] = $this->get_twosign($two_data);
  46. $this->ajaxReturn(array('code'=>200,'info'=>$two_data));
  47. }else{
  48. $this->ajaxReturn(array('code'=>201,'info'=>$res['err_code_des']));
  49. }
  50. }
  51.  
  52. //通过curl发送数据给微信接口的函数
  53. private function send_prePaycurl($xmlData) {
  54. $url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
  55. $header[] = "Content-type: text/xml";
  56. $curl = curl_init();
  57. curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
  58. curl_setopt($curl, CURLOPT_URL, $url);
  59. curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
  60. curl_setopt($curl, CURLOPT_POST, 1);
  61. curl_setopt($curl, CURLOPT_POSTFIELDS, $xmlData);
  62. $data = curl_exec($curl);
  63. if (curl_errno($curl)) {
  64. print curl_error($curl);
  65. }
  66. curl_close($curl);
  67. return $this->_xmldataparse($data);
  68. }
  69.  
  70. //xml数据解析函数
  71. private function _xmldataparse($data){
  72. $msg = array();
  73. $msg = (array)simplexml_load_string($data, 'SimpleXMLElement', LIBXML_NOCDATA);
  74. return $msg;
  75. }
  76.  
  77. //生成xml格式的函数
  78. private function set_xmldata($data) {
  79. $xmlData = "<xml>";
  80. foreach ($data as $key => $value) {
  81. $xmlData.="<".$key."><![CDATA[".$value."]]></".$key.">";
  82. }
  83. $xmlData = $xmlData."</xml>";
  84. return $xmlData;
  85. }
  86.  
  87. //一次签名的函数
  88. private function get_sign($data){
  89. ksort($data);
  90. $str = '';
  91. foreach ($data as $key => $value) {
  92. $str .= !$str ? $key . '=' . $value : '&' . $key . '=' . $value;
  93. }
  94. $str.='&key='.C('WX_KEY');
  95. $sign = strtoupper(md5($str));
  96. return $sign;
  97. }
  98.  
  99. //二次签名的函数
  100. private function get_twosign($data){
  101. $sign_data = array(
  102. "appid"=>$data['appid'],
  103. "partnerid"=>$data['partnerid'],
  104. "prepayid"=>$data['prepayid'],
  105. "noncestr"=>$data['noncestr'],
  106. "timestamp"=>$data['timestamp'],
  107. "package"=>$data['package'],
  108. );
  109. return $this->get_sign($sign_data);
  110. }
  111.  
  112. //微信回调
  113. public function wx_notify(){
  114. //允许从外部加载XML实体(防止XML注入攻击)
  115. libxml_disable_entity_loader(true);
  116. $postStr = $this -> post_data();//接收post数据
  117. $postObj = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);
  118. $arr = $this -> object_toarray($postObj);//对象转成数组
  119. ksort($arr);// 对数据进行排序
  120. $str = $this -> params_tourl($arr);//对数据拼接成字符串
  121. $user_sign = strtoupper(md5($str));
  122. if($user_sign == $arr['sign']){//验证签名
  123. return '<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>';
  124. }else{
  125. return '<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>';
  126. }
  127. }
  128.  
  129. // 接收post数据
  130. /*
  131. * 微信是用$GLOBALS['HTTP_RAW_POST_DATA'];这个函数接收post数据的
  132. */
  133. public function post_data(){
  134. $receipt = $_REQUEST;
  135. if($receipt==null){
  136. $receipt = file_get_contents("php://input");
  137. if($receipt == null){
  138. $receipt = $GLOBALS['HTTP_RAW_POST_DATA'];
  139. }
  140. }
  141. return $receipt;
  142. }
  143.  
  144. //把对象转成数组
  145. public function object_toarray($arr) {
  146. if(is_object($arr)) {
  147. $arr = (array)$arr;
  148. } if(is_array($arr)) {
  149. foreach($arr as $key=>$value) {
  150. $arr[$key] = $this->object_toarray($value);
  151. }
  152. }
  153. return $arr;
  154. }
  155.  
  156.  
  157. /**
  158. * 格式化参数格式化成url参数
  159. */
  160. private function params_tourl($arr)
  161. {
  162. $weipay_key = C('WX_KEY');//微信的key,这个是微信支付给你的key,不要瞎填。
  163. $buff = "";
  164. foreach ($arr as $k => $v)
  165. {
  166. if($k != "sign" && $v != "" && !is_array($v)){
  167. $buff .= $k . "=" . $v . "&";
  168. }
  169. }
  170. $buff = trim($buff, "&");
  171. return $buff.'&key='.$weipay_key;
  172. }
  173.  
  174. }

你可能感兴趣的