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', #异步通知回调地址
  );
AI 代码解读

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

$request_data['sign'] = $this -> get_sign($request_data);
AI 代码解读

我们下来需要把微信请求的数据拼装成 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>
AI 代码解读

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

$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"  #支付类型
}
AI 代码解读

到这里拿到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']));
 }
AI 代码解读

然后就可以在商户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;
    }
AI 代码解读

微信回调

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

    //微信回调
    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>';
        }
    } 
AI 代码解读

回调用到的方法如下

// 接收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;  
    }      
AI 代码解读

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

下面分享一下全部的代码

<?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;  
    }      

}
AI 代码解读
目录
打赏
0
0
0
1
1
分享
相关文章
Wordpress主题开发之index.php
本文介绍了 WordPress 主题开发中页面结构与模板文件的使用方法。通过 header.php、sidebar.php、footer.php 和 index.php 等模板文件,实现网站模块化设计,便于统一管理和代码重用。Header 部分包含 logo、导航条等;Content 展示主体内容;Side bar 显示推荐信息或广告;Footer 则呈现版权和备案信息等内容。文章还提供了各模板文件的具体代码示例,帮助开发者快速理解和应用 WordPress 模板机制。
鸿蒙NEXT开发App相关工具类(ArkTs)
这段代码展示了一个名为鸿蒙NEXT开发 `AppUtil` 的工具类,主要用于管理鸿蒙应用的上下文、窗口、状态栏、导航栏等配置。它提供了多种功能,例如设置灰阶模式、颜色模式、字体类型、屏幕亮度、窗口属性等,并支持获取应用包信息(如版本号、包名等)。该工具类需在 UIAbility 的 `onWindowStageCreate` 方法中初始化,以便缓存全局变量。代码由鸿蒙布道师编写,适用于鸿蒙系统应用开发,帮助开发者更便捷地管理和配置应用界面及系统属性。
PhalApi 2.x:让PHP接口开发从“简单”到“极简”的开源框架
PhalApi 2.x 是一款专为接口开发设计的轻量级PHP框架,性能卓越且易于上手。它支持多协议、自动生成文档、提供多种客户端SDK,并采用现代化技术栈,适合中小型项目及微服务架构。通过清晰的分层架构和丰富的扩展库,开发者可快速构建高可用API。其日均超1000万次调用,广泛应用于移动App、物联网、电商等领域。官网:https://www.phalapi.net/,欢迎体验高效开发之旅!
工会成立100周年纪念,开发职工健身AI运动小程序、APP方案推荐
为庆祝中华全国总工会成立100周年,特推出基于AI技术的智能健身系统,以小程序和APP形式呈现,助力职工健康生活。方案包括:1) 小程序插件,支持多种运动识别,开箱即用;2) APP插件,提供更高精度的运动检测;3) 成熟的「AI乐运动」系统,支持赛事活动管理。这些方案满足不同需求,推动全民健身体验升级,彰显工会对职工健康的关怀。
【一步步开发AI运动APP】六、运动计时计数能调用
本文章介绍了如何通过【一步步开发AI运动APP】系列博文,利用uniAPP插件开发高性能的AI运动应用。文中详细说明了创建运动分析器、进行运动分析、监听计数变化以及停止/重置分析等功能实现步骤。插件内置多种常见运动(如跳绳、俯卧撑等),支持自定义扩展,满足健身、体测等场景需求。示例代码展示了人体检测、运动计时计数及UI更新的完整流程,帮助开发者快速上手并深耕AI运动领域。
【一步步开发AI运动APP】五、人体检测能力调用
本文介绍如何开发性能更强、体验更优的AI运动APP,涵盖人体检测、实例创建、检测识别、骨骼图绘制及完整代码实现。通过API `createHumanDetector`,可灵活配置高性能、高精度或多人检测模式,省去模型部署麻烦。检测结果可通过`yz-pose-grapher`组件高效渲染骨骼图。最后提醒使用完毕需调用`destroy()`释放资源,下篇将聚焦运动检测分析,敬请期待!
【一步步开发AI运动APP】四、使用相机组件抽帧
本文介绍了如何使用`ai-camera`组件开发AI运动APP,助力开发者深耕AI运动领域。`ai-camera`是专为AI运动场景设计的相机组件,支持多平台,提供更强的抽帧处理能力和API。文章详细讲解了获取相机上下文、执行抽帧操作以及将帧保存到相册的功能实现,并附有代码示例。无论是AI运动APP还是其他场景,该组件都能满足预览、拍照、抽帧等需求。下篇将聚焦人体识别检测,敬请期待!
【一步步开发AI运动APP】二、跨平台APP AI运动识别方案介绍
本系列博文旨在帮助开发者从【AI运动小程序】迈向性能更优的【AI运动APP】开发。通过「云智AI运动识别」uni-app版插件,提供本地原生极速识别、精准姿态检测及运动计时计数功能,支持健身系统、线上赛事、学生体测、康复锻炼等多场景应用。插件无需云端依赖,一次付费永久使用,成本低且扩展性强。同时兼容uni-app与uni-app x框架,适合不同技术背景的开发者快速上手,助力抢占AI辅助运动市场。下篇将介绍插件引入,敬请期待!
【一步步开发AI运动APP】一、写在最前
本文介绍新系列【一步步开发AI运动APP】,旨在帮助开发者突破小程序限制,打造性能更强、体验更佳的AI运动APP。相比小程序,APP可充分利用CPU/GPU算力,实现高精度人体检测、多人检测等复杂功能。本系列基于跨平台框架`uni`及扩展插件`uni AI运动识别插件`,适合有小程序开发经验的开发者。内容涵盖抽帧、人体识别、运动分析、姿态交互等,逐步进阶实现完整AI运动APP,并支持多手机平台发布。
布谷一对一直播源码开发:阿里云视频语音通话社交交友App的必备功能
在当今移动社交领域,一对一视频和语音通话功能已成为用户期待的基础配置。从熟人社交到陌生人交友,从专业咨询到情感陪伴,实时音视频互动能力直接决定了社交App的用户留存和市场竞争力。山东布谷科技将深入探讨一对一直播源码开发高质量一对一视频和语音通话功能的关键要素和技术实现方案。
布谷一对一直播源码开发:阿里云视频语音通话社交交友App的必备功能
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等