【原创】 移动支付集成笔记1-微信扫码支付
栏目:ThinkPHP  作者:隆航  阅读:(246)

业务处理逻辑:

扫码支付模式二文档: https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_5

需要用到的API接口

统一下单:

文档地址:https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_1

查询订单:

文档地址:https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_2

关闭订单:

文档地址:https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_3

这里的支付的话可以自己写,也可以拿SDK用。

SDK的写法的这里不做说明,因为我有轻微强迫症。所以这里根据自己的需要写的接口


实际开发步骤

1.生成订单(这里不做描述)

2.过滤订单信息(订单过期时间等逻辑)

3.构造订单主体(生成签名)

4.请求微信服务器返回预支付URL

5.用JS或者PHP生成二维码

6.AJAX轮询请求查询订单接口


这里是基于ThinkPHP3.2的写法,需要单独写的可以参考下。


<?php
/**
 * Created by PhpStorm.
 * User: 隆航
 * Date: 2016/12/11 0011
 * Time: 17:21
 */
namespace Home\Controller;
use Think\Controller;
class WxpayController extends Controller
{
    // 引入微信支付类
    public function _initialize()
    {
        Vendor('Weixinpay.Weixinpay');
    }
    public function indexAction()
    {
        echo '404';
    }
    // 远程请求支付接口的时候ajax返回微信支付的URL地址
    public function dowithpayAction(){
        if(!IS_POST){
            $this->error("非法请求");
        }
        $weixinpay = new \Weixinpay(C('weixinpay'));
        //根据传递的订单号,信息查询 状态为 未支付 微信支付
        M()->startTrans();
        // todo 查询订单信息
        $info = "";
        if(!$info){
            // 订单信息错误
            M()->rollback();
        }
        // todo 判断订单过期
        $order = array(
            // 订单主体
            'body'          =>  "",//支付时候显示的标题
            // 订单金额 单位是分
            'total_fee'     =>  "",//注意单位是分
            // 商品订单号
            'out_trade_no'  =>  "",//商户订单号
            // 商品ID 扫码支付必须的参数!
            'product_id'    =>  "",//商品ID
            // 支付方式 模式2 扫码支付
            'trade_type'    =>  'NATIVE',
            // 订单支付结束时间
            'time_expire'   =>  date("YmdHis",time()+1800)//过期时间
        );
        $result = $weixinpay->unifiedOrder($order);
        // todo 更新第三方订单号 生成预支付的时候会返回微信订单号 存入数据库
        $arr['url'] = urldecode($result['code_url']);   //返回URL地址
        $arr['out_trade_no'] = $order['out_trade_no'];  //返回订单号,查询时用
        // todo 记录日志
        M()->commit();
        $this->ajaxReturn(array('code'=>1,'result'=>$arr));
    }
    //微信异步地址
    public function notifyAction()
    {
        $wxpay=new \Weixinpay(C('weixinpay'));
        $result=$wxpay->notify();
        if ($result) {
            // 验证成功 修改数据库的订单状态等 $result['out_trade_no']为订单号
            // todo 记录日志 ("异步收到订单".$result['out_trade_no']."支付结果,等待验证数据");
            if($result['result_code'] == 'SUCCESS'){
                // todo 记录日志 ("异步收到订单".$result['out_trade_no']."支付完成通知,更新数据库数据");
                M()->startTrans();
                // todo 根据订单号查询订单信息
                $info = '';
                if(!empty($info)){
                    //订单信息正确,修改订单完成支付
                    $A = "";
                    $B = "";
                    if(false !== $A && false !== $B ){
                        M()->commit();
                        // todo 记录日志 ("异步返回订单:".$result['out_trade_no']." 支付完成,且修改数据库完成");
                    }else{
                        M()->rollback();
                        // todo 记录日志 ("异步返回订单:".$result['out_trade_no']." 支付完成,修改数据库时发生错误");
                    }
                }else{
                    M()->rollback();
                    // 订单已经处理过 或者不符合要修改的状态 不做改动
                    // todo 记录日志 ("异步返回订单:".$result['out_trade_no']." 支付完成,订单已经处理过 或者不符合要修改的状态 不做改动");
                }
            }else{
                // todo 记录日志 ("异步收到订单".$result['out_trade_no']."支付失败通知,错误代码:".$result['err_code'].",错误描述:".$result['err_code_des']);
            }
        }else{
            // todo 记录日志 ("异步验证不通过");
        }
    }
    // 查询订单状态
    // 因为微信的异步时间没支付宝的即时
    public function orderqueryAction($out_trade_no=null){
        $out_trade_no  =  $_POST['out_trade_no'];
        //检测必填参数
        if(empty($out_trade_no)) {
            $this->ajaxReturn(array('code'=>0,'msg'=>'查询订单号不能为空'));
        }
        $wxpay=new \Weixinpay(C('weixinpay'));
        $result=$wxpay->orderquery($out_trade_no);
        if($result['return_code']== 'SUCCESS'){
            // todo 记录日志 ("查询订单号".$result['out_trade_no']."请求完成");
            if($result['result_code'] == 'SUCCESS'){
                // todo 记录日志 ("查询订单号".$result['out_trade_no']."请求业务结果完成");
                if($result["trade_state"] == "SUCCESS" ){
                    // todo 记录日志 ("查询订单号".$result['out_trade_no']."完成,订单支付完成.验证数据库数据后修改数据");
                    M()->startTrans();
                    // todo 查询订单信息 订单未支付的时候
                    $info = "";
                    if(!empty($info)){
                        $A = "";
                        $B = "";
                        if(false !== $A && false !== $B){
                            M()->commit();
                            // todo 记录日志 ("查询订单号:".$result['out_trade_no']." 支付完成,且修改数据库完成");
                            $this->ajaxReturn(array('code'=>1,'msg'=>'支付完成,成功付款'.($result['total_fee']/100).'元',"url"=>"/index.php/Home/EQiHai/payOk/ordersn/".$result['out_trade_no']));
                        }else{
                            M()->rollback();
                            // todo 记录日志 ("查询订单号:".$result['out_trade_no']." 支付完成,修改数据库时发生错误");
                            $this->ajaxReturn(array('code'=>0,'msg'=>'支付完成,修改数据库时发生错误'));
                        }
                    }else{
                        // 订单已经处理过 或者不符合要修改的状态 不做改动
                        // todo 记录日志 ("查询订单号:".$result['out_trade_no']." 支付完成,订单已经处理过 或者不符合要修改的状态 不做改动");
                        M()->rollback();
                        $this->ajaxReturn(array('code'=>1,'msg'=>'支付完成,成功付款'.($result['total_fee']/100).'元',"url"=>"/index.php/Home/EQiHai/payOk/ordersn/".$result['out_trade_no']));
                    }
                }elseif ($result['trade_state'] == "REFUND"){
                    // todo 记录日志 ("查询订单号".$result['out_trade_no']."完成,订单转入退款");
                    $this->ajaxReturn(array('code'=>0,'msg'=>'订单转入退款'));
                }elseif ($result['trade_state'] == "NOTPAY"){
                    // todo 记录日志 ("查询订单号".$result['out_trade_no']."完成,订单未支付");
                    $this->ajaxReturn(array('code'=>0,'msg'=>'订单未支付'));
                }elseif ($result['trade_state'] == "CLOSED"){
                    // todo 记录日志 ("查询订单号".$result['out_trade_no']."完成,订单已关闭");
                    $this->ajaxReturn(array('code'=>0,'msg'=>'订单已关闭'));
                }elseif ($result['trade_state'] == "USERPAYING"){
                    // todo 记录日志 ("查询订单号".$result['out_trade_no']."完成,订单正在支付中");
                    $this->ajaxReturn(array('code'=>0,'msg'=>'订单正在支付中'));
                }elseif ($result['trade_state'] == "PAYERROR"){
                    // todo 记录日志 ("查询订单号".$result['out_trade_no']."完成,订单支付失败");
                    $this->ajaxReturn(array('code'=>0,'msg'=>'订单支付失败'));
                }
            }else{
                // todo 记录日志 ("查询订单号".$result['out_trade_no']."业务结果失败,错误代码:".$result['err_code'].",错误描述:".$result['err_code_des']);
                $this->ajaxReturn(array('code'=>0,'msg'=>'错误代码'.$result['err_code'].",错误描述:".$result['err_code_des']));
            }
        }else {
            if ($result['result_code'] == "ORDERCLOSED") {
                // 订单已关闭
                // todo 记录日志 ("查询订单:" . $result['out_trade_no'] . " 支付已过期,且修改数据库");
                $this->ajaxReturn(array('code'=>0,'msg'=>'该订单已过期'));
            }
            // todo 记录日志 ("查询订单号" . $result['out_trade_no'] . "请求失败,错误码:".$result['return_msg']);
        }
    }
}


类库代码:放入ThinkPHP/Library/Vendor/Weixinpay/

<?php
/**
 * Created by PhpStorm.
 * User: 隆航
 * Date: 2016/12/12 0012
 * Time: 10:30
 */
class Weixinpay {
    // 定义配置项
    private $config = array();
    // 设置参数
    public function __construct($config){
        if (empty($config)) {
            $this->config = C('weixinpay');
        }else{
            $this->config = $config;
        }
    }
    // 统一下单
    // 传递订单参数
    // 手册地址:https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_1
    // body(产品描述)、total_fee(订单金额)、out_trade_no(订单号)、product_id(产品id)、trade_type(类型:JSAPI,NATIVE,APP)
    public function unifiedOrder($order){
        $weixinpay_config   = $this->config;
        $config             = array(
            // 公众账号ID
            'appid'             =>  $weixinpay_config['APPID'],
            // 商家号
            'mch_id'            =>  $weixinpay_config['MCHID'],
            // 随机字符串
            'nonce_str'         =>  uniqid(),
            // 终端IP
            'spbill_create_ip'  =>  $_SERVER['REMOTE_ADDR'],
            // 异步地址
            'notify_url'        =>  $weixinpay_config['NOTIFY_URL']
        );
        // 合并配置数据和订单数据
        $data               = array_merge($order,$config);
        // 生成签名
        $sign               = $this->makeSign($data);
        $data['sign']       = $sign;
        $xml                = $this->arrayToXml($data);
        $url                = 'https://api.mch.weixin.qq.com/pay/unifiedorder';
        $result             = $this->httpPost($xml,$url);
        if ($result['return_code'] == 'FAIL') {
            die($result['return_msg']);
        }
        //$result['sign']     = $sign;
        //$result['nonce_str']= $config['nonce_str'];
        return $result;
    }
    // 查询订单状态
    // 传递商家订单号
    // 手册地址 https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_2
    public function orderQuery($out_trade_no)
    {
        $weixinpay_config   = $this->config;
        $data               = array(
            'appid'         => $weixinpay_config['APPID'],
            'mch_id'        => $weixinpay_config['MCHID'],
            'nonce_str'     => uniqid(),
            'out_trade_no'  => $out_trade_no
        );
        $sign               = $this->makeSign($data);
        $data['sign']       = $sign;
        $xml                = $this->arrayToXml($data);
        $url                = "https://api.mch.weixin.qq.com/pay/orderquery";//接收xml数据的文件
        $result             = $this->httpPost($xml,$url);
        return $result;
    }
    public function orderClose($out_trade_no)
    {
        $weixinpay_config   = $this->config;
        $data               = array(
            'appid'         => $weixinpay_config['APPID'],
            'mch_id'        => $weixinpay_config['MCHID'],
            'out_trade_no'  => $out_trade_no,
            'nonce_str'     => uniqid()
        );
        $sign               = $this->makeSign($data);
        $data['sign']       = $sign;
        $xml                = $this->arrayToXml($data);
        $url                = "https://api.mch.weixin.qq.com/pay/closeorder ";//接收xml数据的文件
        $result             = $this->httpPost($xml,$url);
        return $result;
    }
    /**
     * CURL 请求
     * @param $xml
     * @param $url
     * @param bool $useCert
     * @param int $second
     * @return mixed
     */
    function httpPost($xml, $url, $useCert = false, $second = 30)
    {
        $weixinpay_config   = $this->config;
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_TIMEOUT, $second);
        curl_setopt($ch, CURLOPT_URL, $url);
        // todo 兼容本地 正式请改为 true
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER,false);
        // todo 严格校验 兼容本地 正式请不要注释
        //curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,2);
        //设置header
        $header[] = "Content-type: text/xml";
        curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
        curl_setopt($ch, CURLOPT_HEADER, false);
        //post提交方式
        curl_setopt($ch, CURLOPT_POST, TRUE);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
        if($useCert == true){
            curl_setopt($ch, CURLOPT_SSLCERTTYPE,'PEM');
            curl_setopt($ch, CURLOPT_SSLCERT, $weixinpay_config['CERT']);
            curl_setopt($ch, CURLOPT_SSLKEYTYPE,'PEM');
            curl_setopt($ch, CURLOPT_SSLKEY, $weixinpay_config['CERT_KEY']);
        }
        $data = curl_exec($ch);
        if(curl_errno($ch)){
            die(curl_error($ch));
        }
        curl_close($ch);
        $result = $this->XmltoArray($data);
        return $result;
    }
    /**
     * 验证回调
     * @return bool|mixed
     */
    public function notify(){
        $xml        = file_get_contents('php://input', 'r');
        $data       = $this->XmltoArray($xml);
        // 保存原sign
        $data_sign  = $data['sign'];
        // sign不参与签名
        unset($data['sign']);
        $sign       = $this->makeSign($data);
        // 判断签名是否正确  判断支付状态
        if ($sign === $data_sign && $data['return_code']=='SUCCESS' && $data['result_code']=='SUCCESS') {
            $result = $data;
        }else{
            $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;
        return $result;
    }
    /**
     * 数组转xml
     * @param $data
     * @return string
     * @throws Exception
     */
    public function arrayToXml($data){
        if(!is_array($data) || count($data) <= 0){
            throw new Exception("数组数据异常!");
        }
        $xml = "<xml>";
        foreach ($data as $key=>$val){
            if (is_numeric($val)){
                $xml.="<".$key.">".$val."</".$key.">";
            }else{
                $xml.="<".$key."><![CDATA[".$val."]]></".$key.">";
            }
        }
        $xml.="</xml>";
        return $xml;
    }
    /**
     * 生成签名
     * 手册: https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=4_3
     * @param $data
     * @return string
     */
    public function makeSign($data){
        // 去空
        $data=array_filter($data);
        // 字典序排序
        ksort($data);
        // 转换请求参数
        $string = http_build_query($data);
        // 编码URL地址
        $string = urldecode($string);
        $config = $this->config;
        // 加入KEY
        $string_sign = $string."&key=".$config['KEY'];
        // MD5加密
        $sign = md5($string_sign);
        // 转为大写
        $result = strtoupper($sign);
        return $result;
    }
    /**
     * 将xml转为array
     * @param $xml
     * @return mixed
     */
    public function xmlToArray($xml){
        //禁止引用外部xml实体
        libxml_disable_entity_loader(true);
        $result= json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
        return $result;
    }
}

配置文件(这里的配置文件要用 LOAD_EXT_CONFIG 引入,或者直接写在config里面 )

<?php
/**
 * Created by PhpStorm.
 * User: 隆航
 * Date: 2016/12/10 0010
 * Time: 17:44
 */
return array(
    'weixinpay'       => array(
        'APPID'             => '', // 微信支付APPID
        'MCHID'             => '', // 微信支付MCHID 商户收款账号
        'KEY'               => 'v', // 微信支付KEY
        //'APPSECRET'         => '', // 公众帐号secert (公众号支付专用)
        'NOTIFY_URL'        => '', // 接收支付状态的连接
        'CERT'              =>  getcwd().'/Public/wxcacert/apiclient_cert.pem', //证书签名文件
        'CERT_KEY'          =>  getcwd().'/Public/wxcacert/apiclient_key.pem', //证书签名文件
    ),
);



下一篇: 没有了


昵称
邮箱
域名
  记住 通知博主
验证码

  程序相关

站点版本:青春博客-V 2.1.0

开源版本:青春博客-Beta v2.0

 下载  在线支付  在线工具  在线音乐

  随机文章