微信公众平台开发[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
- 微信公众平台开发[5] —— 微信扫码支付
- 微信公众平台开发[3] —— 微信公众号支付功能(PHP)
- 微信公众平台开发[3] —— 微信公众号支付功能(PHP)
- 微信支付(公众号支付)微信公众平台开发教程(5)
- 微信公众平台开发[4] —— ThinkPHP框架下微信公众号支付
- 微信公众平台,微信支付功能开发文档
- 微信公众平台开发[4] —— ThinkPHP 框架下微信支付
- 微信公众平台支付
- 微信公众平台开发—概述
- 微信公众平台开发:JS API支付
- 微信支付-开放平台-公众平台
- Java微信公众平台开发(一)——接入微信公众平台
- 微信支付(扫码支付)微信公众平台开发教程(6)
- 微信支付流程-公众号支付开发-视频教程5
- 微信支付公众平台配置
- 微信公众平台支付全流程
- 使用golang快速开发微信公众平台(五):公众号支付
- Java微信公众平台开发(十一)——开发中微信公众平台/开放平台/商户平台的关联
- 学习笔记:linux 转换指定目录下文件名编码格式
- WebStorm下载及破解
- vuejs从安装到搭建的整个过程
- http协议
- 解决Android中TextView和ExpandableListView和ScrollView滑动冲突问题
- 微信公众平台开发[5] —— 微信扫码支付
- 图像处理与计算机视觉基础,经典以及最近发展
- git上拉取内容会有两个协议,有什么区别呢
- 安卓服务Servise
- 将元组数据写入txt文件中
- 个人学习笔记----基于Spring4.3.1+mybatis+postgresql+maven搭建的个人用调度平台(四)
- adt更新sdk后不能运行
- WebViewClient与WebChromeClient的区别
- 影徒之始