开发者社区> 代码汇> 正文

php开发微信APP支付接口

简介: php开发微信APP支付接口
+关注继续查看

之前在开发APP中用到了微信支付,因为是第一次用,所以中途也遇到了好多问题,通过查看文档和搜集资料,终于完成了该功能的实现。在这里简单分享一下后台php接口的开发实例。
个人博客:代码汇 http://www.codehui.net

开发流程

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
然后我们先把统一下单所需要的参数列出来

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

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

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

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

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

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

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

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

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

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

    //一次签名的函数
    private function get_sign($data){
        ksort($data);
        $str = '';
        foreach ($data as $key => $value) {
            $str .= !$str ? $key . '=' . $value : '&' . $key . '=' . $value;
        }
        $str.='&key='.C('WX_KEY');
        $sign = strtoupper(md5($str));
        return $sign;
    }
    //二次签名的函数
    private function get_twosign($data){
        $sign_data = array(
            "appid"=>$data['appid'],
            "partnerid"=>$data['partnerid'],
            "prepayid"=>$data['prepayid'],
            "noncestr"=>$data['noncestr'],
            "timestamp"=>$data['timestamp'],
            "package"=>$data['package'],
        );
        return $this -> get_sign($sign_data);
    }
    //生成xml格式的函数
    private function set_xmldata($data) {
        $xmlData = "<xml>";
          foreach ($data as $key => $value) {
           $xmlData.="<".$key."><![CDATA[".$value."]]></".$key.">";
          }
          $xmlData = $xmlData."</xml>";
          return $xmlData;
    }
    //通过curl发送数据给微信接口的函数
    private function send_prePaycurl($xmlData) {
        $url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
        $header[] = "Content-type: text/xml";
        $curl = curl_init();
        curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
        curl_setopt($curl, CURLOPT_URL, $url);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($curl, CURLOPT_POST, 1);
        curl_setopt($curl, CURLOPT_POSTFIELDS, $xmlData);
        $data = curl_exec($curl);
        if (curl_errno($curl)) {
            print curl_error($curl);
        }
        curl_close($curl);
        return $this -> _xmldataparse($data);
    }
    //xml数据解析函数
    private function _xmldataparse($data){
        $msg = array();
        $msg = (array)simplexml_load_string($data, 'SimpleXMLElement', LIBXML_NOCDATA);
        return $msg;
    }

微信回调

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

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

回调用到的方法如下

// 接收post数据  
    /*  
    *  微信是用$GLOBALS['HTTP_RAW_POST_DATA'];这个函数接收post数据的  
    */  
    public function post_data(){  
        $receipt = $_REQUEST;  
        if($receipt==null){  
            $receipt = file_get_contents("php://input");  
            if($receipt == null){  
                $receipt = $GLOBALS['HTTP_RAW_POST_DATA'];  
            }  
        }  
        return $receipt;  
    }  
      
    //把对象转成数组  
    public function object_toarray($arr) {  
        if(is_object($arr)) {  
            $arr = (array)$arr;  
        } if(is_array($arr)) {  
            foreach($arr as $key=>$value) {  
                $arr[$key] = $this->object_toarray($value);  
            }  
        }  
        return $arr;  
    }  
      
      
     /**  
     * 格式化参数格式化成url参数  
     */  
    private function params_tourl($arr)  
    {  
        $weipay_key = C('WX_KEY');//微信的key,这个是微信支付给你的key,不要瞎填。  
        $buff = "";  
        foreach ($arr as $k => $v)  
        {  
            if($k != "sign" && $v != "" && !is_array($v)){  
                $buff .= $k . "=" . $v . "&";  
            }  
        }  
        $buff = trim($buff, "&");  
        return $buff.'&key='.$weipay_key;  
    }      

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

下面分享一下全部的代码

<?php
namespace Home\Controller;
use Think\Controller;
/**
 * php开发微信app支付接口
 * @global  WX_APPID  开放平台->移动应用appid
 * @global  WX_MCHID  微信支付商户号            
 * @global  WX_KEY    商户支付秘钥              
 * @author codehi <admin@codehui.net> 2017-12-23
 */
class WxpayController extends Controller
{
    /**
     * 微信支付统一下单 >>> 生成预支付交易单
     */
    public function wx_pay(){

       $request_data = array(
             'appid' => C('WX_APPID'),                         #应用APPID
             'mch_id' => C('WX_MCHID'),                        #商户号
             'trade_type' => 'APP',                            #支付类型
             'nonce_str' => \Org\Util\String::randString(30),  #随机字符串 不长于32位
             'body' => '商品名称',                             #商品名称
             'out_trade_no' => '12345678912456',                    #商户后台订单号
             'total_fee' => '1',                             #商品价格
             'spbill_create_ip' => get_client_ip(),            #用户端实际ip
             'notify_url' => 'http://shop.lsmrsd.com/app/index.php/Home/Wxpay/wx_notify', #异步通知回调地址
        );        
        // 获取签名
        $request_data['sign'] = $this -> get_sign($request_data);
        // 拼装数据
        $xml_data = $this -> set_xmldata($request_data);
        

        // 发送请求
        $res = $this -> send_prePaycurl($xml_data);
        $this->ajaxReturn($res);
        if($res['return_code'] == 'SUCCESS' && $res['result_code'] == 'SUCCESS'){
            $two_data['appid'] = C('WX_APPID');  #APPID
            $two_data['partnerid'] = C('WX_MCHID');  #商户号
            $two_data['prepayid'] = $res['prepay_id'];  //预支付交易会话标识
            $two_data['noncestr'] = \Org\Util\String::randString(30);  
            $two_data['timestamp'] = time();
            $two_data['package'] = "Sign=WXPay";
            $two_data['sign'] = $this->get_twosign($two_data);
            $this->ajaxReturn(array('code'=>200,'info'=>$two_data));
        }else{
            $this->ajaxReturn(array('code'=>201,'info'=>$res['err_code_des']));
        }
    }
 
    //通过curl发送数据给微信接口的函数
    private function send_prePaycurl($xmlData) {
        $url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
        $header[] = "Content-type: text/xml";
        $curl = curl_init();
        curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
        curl_setopt($curl, CURLOPT_URL, $url);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($curl, CURLOPT_POST, 1);
        curl_setopt($curl, CURLOPT_POSTFIELDS, $xmlData);
        $data = curl_exec($curl);
        if (curl_errno($curl)) {
            print curl_error($curl);
        }
        curl_close($curl);
        return $this->_xmldataparse($data);
    }
 
    //xml数据解析函数
    private function _xmldataparse($data){
        $msg = array();
        $msg = (array)simplexml_load_string($data, 'SimpleXMLElement', LIBXML_NOCDATA);
        return $msg;
    }
 
    //生成xml格式的函数
    private function set_xmldata($data) {
        $xmlData = "<xml>";
          foreach ($data as $key => $value) {
           $xmlData.="<".$key."><![CDATA[".$value."]]></".$key.">";
          }
          $xmlData = $xmlData."</xml>";
          return $xmlData;
    }
 
    //一次签名的函数
    private function get_sign($data){
        ksort($data);
        $str = '';
        foreach ($data as $key => $value) {
            $str .= !$str ? $key . '=' . $value : '&' . $key . '=' . $value;
        }
        $str.='&key='.C('WX_KEY');
        $sign = strtoupper(md5($str));
        return $sign;
    }
 
    //二次签名的函数
    private function get_twosign($data){
        $sign_data = array(
            "appid"=>$data['appid'],
            "partnerid"=>$data['partnerid'],
            "prepayid"=>$data['prepayid'],
            "noncestr"=>$data['noncestr'],
            "timestamp"=>$data['timestamp'],
            "package"=>$data['package'],
        );
        return $this->get_sign($sign_data);
    }

    //微信回调
    public function wx_notify(){  
       //允许从外部加载XML实体(防止XML注入攻击)
        libxml_disable_entity_loader(true);  
        $postStr = $this -> post_data();//接收post数据  
        $postObj = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);  
        $arr = $this -> object_toarray($postObj);//对象转成数组  
        ksort($arr);// 对数据进行排序  
        $str = $this -> params_tourl($arr);//对数据拼接成字符串 
        $user_sign = strtoupper(md5($str));  
        if($user_sign == $arr['sign']){//验证签名 
            return '<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>';
        }else{
            return '<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>';
        }
    }  
      
    // 接收post数据  
    /*  
    *  微信是用$GLOBALS['HTTP_RAW_POST_DATA'];这个函数接收post数据的  
    */  
    public function post_data(){  
        $receipt = $_REQUEST;  
        if($receipt==null){  
            $receipt = file_get_contents("php://input");  
            if($receipt == null){  
                $receipt = $GLOBALS['HTTP_RAW_POST_DATA'];  
            }  
        }  
        return $receipt;  
    }  
      
    //把对象转成数组  
    public function object_toarray($arr) {  
        if(is_object($arr)) {  
            $arr = (array)$arr;  
        } if(is_array($arr)) {  
            foreach($arr as $key=>$value) {  
                $arr[$key] = $this->object_toarray($value);  
            }  
        }  
        return $arr;  
    }  
      
      
     /**  
     * 格式化参数格式化成url参数  
     */  
    private function params_tourl($arr)  
    {  
        $weipay_key = C('WX_KEY');//微信的key,这个是微信支付给你的key,不要瞎填。  
        $buff = "";  
        foreach ($arr as $k => $v)  
        {  
            if($k != "sign" && $v != "" && !is_array($v)){  
                $buff .= $k . "=" . $v . "&";  
            }  
        }  
        $buff = trim($buff, "&");  
        return $buff.'&key='.$weipay_key;  
    }      

}

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
众筹app商城开发详细,众筹app商城系统开发(案例及方案),众筹商城系统源码功能
The new retail model refers to a model in which individuals and enterprises, relying on the Internet, upgrade and transform the production, circulation and sales process of goods by using advanced technology such as big data and artificial intelligence, and then reshape the business structure and ec
33 0
《云原生时代下的App开发》电子版地址
2021年12月,阿里云携10+技术专家亮相年度顶级云原生开源技术峰会 ,并带来阿里云云原生专场,不仅汇聚行业发展方向的精彩主题演讲,在云基础设施、可观察性等云原生与开源技术等各大专题中,从阿里云真实业务场景中 走出来的云原生技术最佳实践也向全球开发者一一呈现。
18 0
PHP:APP接口开发
PHP:APP接口开发
22 0
量化交易APP开发量化策略交易软件源码开发智能交易系统
区块链技术已经逐渐渗透到各个领域中,其应用场景也越来越多样化。其中,量化交易是一个极具吸引力的领域,许多投资者都希望通过定量交易来获得更多的利润。然而,传统的量化交易系统存在一些局限性,如交易平台的不透明性、数据源的不可靠性等等。区块链技术的出现为量化交易提供了新的解决方案,区块链跟单合约量化系统就是其中之一。
46 0
《去哪儿网快速App开发及问题解决平台实践》电子版地址
去哪儿网快速App开发及问题解决平台实践
17 0
相亲app开发,性能优化技术并不是突发奇想
相亲app开发,性能优化技术并不是突发奇想
33 0
基于kotlin开发的验证码发送注册的app
”麻雀虽小五脏俱全”就是它了,设计后端即springboot的开发,app处理网络请求的开发,appUI界面的设计(虽然只有一步,但也还是吧),数据库查询相关,app搭建相关架构的实现等等,值得学习一波。
46 0
语音聊天app开发,不同场景下的存储读写方法
语音聊天app开发,不同场景下的存储读写方法
43 0
PHP实现极光推送jpush/jpush 手机APP消息推送
PHP实现极光推送jpush/jpush 手机APP消息推送
70 0
[app,Http,helpers,php]laravel框架中如何添加helpers.php?(步骤详解)
  本篇文章给大家带来的内容是关于laravel框架中如何添加helpers.php?(步骤详解),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。
55 0
+关注
代码汇
主要分享编程开发、数据库、服务器方面的技术文章和学习心得。 个人博客网站:https://www.codehui.net
文章
问答
文章排行榜
最热
最新
相关电子书
更多
移动App持续交付之路
立即下载
移动App研发加速—跨平台解决方案
立即下载
云原生时代下的App开发
立即下载