微信公众平台开发[5] —— 微信扫码支付

来源:互联网 发布:access数据库新技术 编辑:程序博客网 时间:2024/04/23 22:12

    背景:因为微信占据众多的用户群,作为程序开发,自然而然也成了研究的重点。毕竟个人能力有限,很难想象设计的复杂性,多数时间接触起来,各种蒙圈,在此笔记自己的操作流程,仅做参考,欢迎指正。 

一.微信扫码支付模式

    1.附带微信公众号“微信开发”中,对微信扫码支付的两种模式流程图以作“膜拜”。

    

    

    2.具体的操作,可详细参考官方开发文档

    文档有强调:
    模式一开发前,商户必须在公众平台后台设置支付回调URL。URL实现的功能:接收用户扫码后微信支付系统回调的productid和openid;URL设置详见回调地址设置。
    模式二与模式一相比,流程更为简单,不依赖设置的回调支付URL。商户后台系统先调用微信支付的统一下单接口,微信后台系统返回链接参数code_url,商户后台系统将code_url值生成二维码图片,用户使用微信客户端扫码后发起支付。注意:code_url有效期为2小时,过期后扫码不能再发起支付。

    3.建议下载微信公众平台提供的示例代码

    个人觉得作为原生开发,着重更改几个配置项还是难度不大的,重要的是了解对方的代码实现思路。下面主要介绍在ThinkPHP框架下的模式二开发参考,声明:该过程部分参考白俊遥博客,尊重原创。

二.ThinkPHP框架下的微信扫码支付模式二开发

    1.导入weixinpay.php文件到“/ThinkPHP/Library/Vendor/Weixinpay/”目录下

    
   

   2.配置项

    在项目的配置文件中,添加类此如下的代码,,其中的值需要自己根据实际公众号的信息进行配置,注意:很多人会搞错MCHID而造成不必要的时间浪费,具体开发参照官方文档。

/Application/Common/Conf/config.PHP

'WEIXINPAY_CONFIG'  =>          array(              'APPID'              => 'wxdaxxxxxxxxx53', // 微信支付APPID              'MCHID'              => '1xxxxxxx2', // 微信支付MCHID 商户收款账号              'KEY'                => '1qaxxxxxxxxxxxxxxxxxxxgf5', // 微信支付KEY              'APPSECRET'          => '79xxxxxxxxxxxxxxxxxxxxxxxxx5544', // 公众帐号secert (公众号支付专用)              'NOTIFY_URL'         => 'http://pay.fetow.com/Home/Order/notify', // 接收支付状态的连接          ), 

    3.在公共函数中添加 weixinpay()方法。

/** * 微信扫码支付 * @param  array $order 订单 必须包含支付所需要的参数 body(产品描述)、total_fee(订单金额)、out_trade_no(订单号)、product_id(产品id) */function weixinpay($order){    $order['trade_type']='NATIVE';    Vendor('Weixinpay.Weixinpay');    $weixinpay=new \Weixinpay();    return $weixinpay->pay($order);}

    4.设计显示微信扫码的页面

    我定的页面为 wechatpaymenter.html,即通过ajax访问Order/wechatpaymenter ,同时调用微信生成二维码,在该页面的<img>标签中显示出来,具体代码见后面的附录。

    

    5.补充wechatpaymenter方法。

    

    6.补充回调方法

    编辑Home\Controller\OrderController.class.php 中的notify().此处用于在支付完成后的订单号状态更改,进行数据库操作。

    注意:该方法的位置要和 配置信息的NOTIFY_URL回调路径是一致的。

    

   7.注意:

    模式二生成的二维码也是有时间限制的,同时,不能有权限拦截,并且,二维码的生成是根据订单号唯一确定的,如果生成一次,那么很可能第二次就不显示,建议可用time()代替测试。

三.附录代码

   1.wechatpaymenter.html

<layout name="Public/layout" /><style>.wxDialog,.wxdia{margin-top: 33px;}</style><div class="wxDialog"><center><h5>请用微信扫描二维码,完成支付..</h5><div class="wxdia"><img src="{:U('Order/wechatpaymenter',array('order_sn'=>$data['order_sn'],'order_amount'=>$data['order_amount']))}" style="width:150px;height:150px;" /></div></center> </div><script type="text/javascript">$(function(){var timer1 =setInterval(function(){check(timer1)},3000);});function check(timer1){var order_sn = $("#order_sn").val();var cart_id = $("#cart_id").val();var homeUrl = "{:U('/index')}";$.ajax({type : "POST",url: "{:U('Order/ajaxcheckorder')}",dataType : "json",data :{order_sn:order_sn,},success : function(data){//当前支付成功的状态if (data.status == 1) {dialog.tipPaySucc(data.message,homeUrl);clearInterval(timer1);}else {//dialog.tipPayErr(data.message);}}});}</script>

2./ThinkPHP/Library/Vendor/Weixinpay/Weixinpay.php

<?phperror_reporting(E_ALL);ini_set('display_errors', '1');// 定义时区ini_set('date.timezone','Asia/Shanghai');class Weixinpay {    // 定义配置项    private $config=array();    // 构造函数    public function __construct($config){        // 如果配置项为空 则直接返回        if (empty($config)) {            $this->config=C('WEIXINPAY_CONFIG');        }else{            $this->config=$config;        }    }    /**     * 统一下单     * @param  array $order 订单 必须包含支付所需要的参数 body(产品描述)、total_fee(订单金额)、out_trade_no(订单号)、product_id(产品id)、trade_type(类型:JSAPI,NATIVE,APP)     */    public function unifiedOrder($order){        // 获取配置项        $weixinpay_config=$this->config;        $config=array(            'appid'=>$weixinpay_config['APPID'],            'mch_id'=>$weixinpay_config['MCHID'],            'nonce_str'=>'test',            'spbill_create_ip'=>'192.168.0.1',            'notify_url'=>$weixinpay_config['NOTIFY_URL']            );        // 合并配置数据和订单数据        $data=array_merge($order,$config);        // 生成签名        $sign=$this->makeSign($data);        $data['sign']=$sign;        $xml=$this->toXml($data);        $url = 'https://api.mch.weixin.qq.com/pay/unifiedorder';//接收xml数据的文件        $header[] = "Content-type: text/xml";//定义content-type为xml,注意是数组        $ch = curl_init ($url);        curl_setopt($ch, CURLOPT_URL, $url);        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 兼容本地没有指定curl.cainfo路径的错误        curl_setopt($ch, CURLOPT_HTTPHEADER, $header);        curl_setopt($ch, CURLOPT_POST, 1);        curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);        $response = curl_exec($ch);        if(curl_errno($ch)){            // 显示报错信息;终止继续执行            die(curl_error($ch));        }        curl_close($ch);        $result=$this->toArray($response);        // 显示错误信息        if ($result['return_code']=='FAIL') {            die($result['return_msg']);        }        $result['sign']=$sign;        $result['nonce_str']='test';        return $result;    }    /**     * 验证     * @return array 返回数组格式的notify数据     */    public function notify(){        // 获取xml        $xml=file_get_contents('php://input', 'r');         // 转成php数组        $data=$this->toArray($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字符     * @throws WxPayException    **/    public function toXml($data){        if(!is_array($data) || count($data) <= 0){            throw new WxPayException("数组数据异常!");        }        $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;     }    /**     * 生成签名     * @return 签名,本函数不覆盖sign成员变量,如要设置签名需要调用SetSign方法赋值     */    public function makeSign($data){        // 去空        $data=array_filter($data);        //签名步骤一:按字典序排序参数        ksort($data);        $string_a=http_build_query($data);        $string_a=urldecode($string_a);        //签名步骤二:在string后加入KEY        $config=$this->config;        $string_sign_temp=$string_a."&key=".$config['KEY'];        //签名步骤三:MD5加密        $sign = md5($string_sign_temp);        // 签名步骤四:所有字符转为大写        $result=strtoupper($sign);        return $result;    }    /**     * 将xml转为array     * @param  string $xml xml字符串     * @return array       转换得到的数组     */    public function toArray($xml){           //禁止引用外部xml实体        libxml_disable_entity_loader(true);        $result= json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);                return $result;    }    /**     * 获取jssdk需要用到的数据     * @return array jssdk需要用到的数据     */    public function getParameters(){        // 获取配置项        $config=$this->config;        // 如果没有get参数没有code;则重定向去获取openid;        if (!isset($_GET['code'])) {            // 获取订单号            $out_trade_no1=I('get.out_trade_no',1,'intval');            $total_fee = I('get.total_fee');            $out_trade_no = $out_trade_no1.'-'.$total_fee;            // 返回的url            $redirect_uri=U('M/Cart/pay','','',true);            $redirect_uri=urlencode($redirect_uri);            $url='https://open.weixin.qq.com/connect/oauth2/authorize?appid='.$config['APPID'].'&redirect_uri='.$redirect_uri.'&response_type=code&scope=snsapi_base&state='.$out_trade_no.'#wechat_redirect';            redirect($url);        }else{            // 如果有code参数;则表示获取到openid            $code=I('get.code');                        $chuancan = I('get.state');            $chuancans = explode('-', $chuancan);            // 取出订单号            $out_trade_no = $chuancans[0];            // 取出订单金额            $total_fee = $chuancans[1];            // 组合获取prepay_id的url            $url='https://api.weixin.qq.com/sns/oauth2/access_token?appid='.$config['APPID'].'&secret='.$config['APPSECRET'].'&code='.$code.'&grant_type=authorization_code';                        // curl获取prepay_id            $result=curl_get_contents($url);            $result=json_decode($result,true);            $openid=$result['openid'];            // 订单数据  请根据订单号out_trade_no 从数据库中查出实际的body、total_fee、out_trade_no、product_id            $order=array(                'body'=>'test',// 商品描述(需要根据自己的业务修改)                'total_fee'=>$total_fee,// 订单金额  以(分)为单位(需要根据自己的业务修改)                'out_trade_no'=>$out_trade_no,// 订单号(需要根据自己的业务修改)                'product_id'=>'1',// 商品id(需要根据自己的业务修改)                'trade_type'=>'JSAPI',// JSAPI公众号支付                'openid'=>$openid// 获取到的openid            );            // 统一下单 获取prepay_id            $unified_order=$this->unifiedOrder($order);            // 获取当前时间戳            $time=time();            // 组合jssdk需要用到的数据            $data=array(                'appId'=>$config['APPID'], //appid                'timeStamp'=>strval($time), //时间戳                'nonceStr'=>$unified_order['nonce_str'],// 随机字符串                'package'=>'prepay_id='.$unified_order['prepay_id'],// 预支付交易会话标识                'signType'=>'MD5'//加密方式            );            // 生成签名            $data['paySign']=$this->makeSign($data);            $data['out_trade_no'] = $out_trade_no;            $data['total_fee'] = $total_fee;            return $data;        }    }    /**     * 生成支付二维码     * @param  array $order 订单 必须包含支付所需要的参数 body(产品描述)、total_fee(订单金额)、out_trade_no(订单号)、product_id(产品id)、trade_type(类型:JSAPI,NATIVE,APP)     */    public function pay($order){        $result=$this->unifiedOrder($order);        $decodeurl=urldecode($result['code_url']);        qrcode($decodeurl);    }}







0 0
原创粉丝点击