微信支付问题
准备工作
关于微信支付
1. 生活中的微信支付
目前我们日常生活中接触得比较多的线上电子支付方式主要有两种,一种是支付宝,另一种就是微信支付了,微信支付是集成在微信客户端的支付功能,所谓微信客户端,主要是微信的移动应用,例如安卓(Android)微信APP、IOS微信APP,用户可以通过手机的微信APP完成快速的支付流程,当然,其他的移动应用也可以接入微信支付的接口来达到完成微信支付的所有功能。
2. 微信支付方式
主要的支付方式有:公众号支付或称网页支付,扫描二维码支付,APP支付,除此之外还有刷卡支付等,还有一点就是,微信支付以绑定银行卡的快捷支付为基础的。
3. 为什么要掌握
对于我们开发者而言,熟练地掌握微信支付的接口功能是必不可少的,因为以后我的要做的互联网产品中,可能是网页,亦可能是移动应用APP,都可能会集成微信支付,那么接下来让我们来学习如果使用PHP版的微信支付接口吧。
4. 关于本节课
本门课程主要针对已经开通了微信商户号且开通微信公众平台微信支付功能系统的学员,那我没有开通这些东西怎么学习?如果没有开通,也没关系,可以直接使用本课程的代码,因为本节课程使用的是所有支付配置信息使用的都是测试账号提供的,在你学习了调起支付后,如果使用支付功能,那么会产生每次一分钱的费用,支付到微信PHP开发包代码中提供的测试商户号中,测试账号归属于微信系统。
下载SDK
1. 复制下面的链接在浏览器中打开
https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=11_1
2. 在所打开的页面中选择PHP版本的SDK下载
目前的压缩包名为:WxpayAPI_php_v3,下载完毕后,解压到桌面,里面一共含有5个文件夹,和一个 index.php 文件
cert、doc、example、lib、logs、index.php
为了方便大家学习,我已经在右边的文件管理处上传了对应的一份SDK,大家可以参考学习,不过最好还是在自己的电脑下载一份,首先大家先在右边的编辑框中按照顺序,输入命令ls,然后看下输出的是不是下图,如果输出的是wxPay,那么就输入命令cd wxPay
然后输入命令:sudo php -S 0.0.0.0:80,看输出的是不是下图的内容,是的话,那么就可以往下面看了,如果不是的话,再重复上面的步骤,刷新页面即可。
上面的操作完成后,大家点击右边的访问测试就可以进入到支付样例主页面了,如下图所示:
页面显示出了三种支付方式,第一种jsapi是网页支付,第二种刷卡支付,第三种扫码支付,这时候如果你点击jsapi支付的话,网页会提示请在微信客户端打开链接,为什么会这样呢?这个我会在后面的课程说到,除此之外,扫码支付如果你用微信的扫描二维码扫描了的话,是真的会进入到支付页面的哦。
文件说明
1. index.php
这个是官方提供的所有支付功能集成化的入口,不过需要注意的是:如果使用到我们自己的项目中,记得修改对应的链接。
2. cert-证书存放路径
证书是商家在使用微信支付功能的时候,进行身份验证用到的,起到一种安全的作用,但是,目前微信支付仅仅只在使用退款接口或者撤销订单的时候需要可能会用到证书,为什么是可能呢?因为在接口函数中,我们可以选择是否使用证书,不使用也能使用退款或撤销订单接口,在我们下载的SDK中,文件夹cert里面的证书对应的是微信的测试账号的,如果,我们拥有自己的微信商户平台的账号,那么我们就可以登录商户平台,来下载我们自己的证书放到这个目录。
3. doc-官方文档目录
里面含有一些的SDK使用指南信息
4. example-官方接口例子目录
在为完全掌握微信支付接口功能实现之前,我们主要依赖官方的例子代码来进行学习,进而进行修改,含有:
- 文件夹-phpqrode存放了二维码功能相关文件
- jsapi.php"网页调起支付页面;"
- notify.php"网页支付后的回调的页面;"
- native.php"付款二维码信息组装页面;"
- qrcode.php"生成二维码的页面"
- native_notify.php"扫描所生成二维码进入的页面;"
- notify.php"网页支付后的回调页面;"
- orderquery.php"订单查询页面;"
- download.php"查退款单页面;"
- refund.php"退款的页面;"
- refundquery.php"退款单查询的页面;"
- WxPay.JsApiPay.php"网页支付核心类;"
- WxPay.NativePay.php和WxPay.MicroPay.php"是刷卡支付类"
5. lib-核心库
前两个很重要!
- WxPay.Api.php"接口访问类,包含所有微信支付API列表的封装"
- WxPay.Config.php"配置信息所在文件"
- WxPay.Data.php"签名相关类,含签名生成"
- WxPay.Exception.php"异常类"
- WxPay.Notify.php"回调函数的父类"
6. logs-这个主要用来存放在支付过程中生成的各种日志文件
接下来我们进行文件配置
lib文件夹下的WxPay.Config.php
对应文件管理中lib文件夹下的WxPay.Config.php文件
1,公众号与商户平台
首先,我们简单了解一下微信公众号和微信商户平台在微信支付中扮演的角色,公众号是我们在网页要进行支付的场所,而用户支付了的钱,我们怎样对它进行操作呢,这就需要商户平台了,我们在商户平台里能够进行支付账单查询、款数提现等操作,现在我们采用软件打开lib文件下的WxPay.Config.php,可以采用 PhpStorm,sublime,记事本,或者参照右边的文件管理,由于cert文件夹和doc文件夹里面的内容目前不支持上传,所以右边文件管理处,没有上传这2个文件夹里面的文件。
2,AppId和AppSecret
微信公众号的AppId和公众号秘钥,AppId是用来唯一标识公众号用户,AppSecret可以理解为密码,如果没有公众号,那么我们就使用微信的测试AppId,如果我们有的话,那么我们可以先登录微信公众,在这里,还要求我们的公众号必须已经开通微信支付的接口权限,否则我们的AppId是不具备支付权限的。然后在左边的菜单栏滑到最低,找到并点击开发->基本配置->找到AppId 和 AppSecret
- const APPID = 'wx426b3015555a46be';//这个是微信提供的测试AppId,我们学习用它足以
- const APPSECRET = '01c6d59a3f9024db6336662ac95c8e74'; //也是微信提供的测试AppSecret
lib文件夹下的WxPay.Config.php
1,商户号MCHID
商户号MCHID,它用来唯一标识微信商户用户,它在我们注册成功成为商户的时候,官方会发来一封开户邮件,在这封邮件中可查看,如果没有商户号,我们一样可以使用微信的测试商户号,如果有的话,那么我们可以登陆微信商户平台。
- const MCHID = '1225312702'; //这个也是微信提供的测试商户号
2,商户支付密钥KEY
商户支付密钥KEY,必须由商户自己设置,如果我们有自己的商户,那么可以在商户平台的:账户设置->API安全中可以设置,没有的话也可以使用微信提供的来测试和学习。
- const KEY = 'e10adc3949ba59abbe56e057f20f883e'; //微信提供的测试支付密钥
1,证书文件apiclient_cert与apiclient_key
下面的是支付所用的存储私钥文件[apiclient_cert]、存储公钥文件[apiclient_key],在前面文件说明这节课中有说到,证书是商家在使用微信支付功能的时候,进行身份验证用到的,起到一种安全的作用,但是,目前微信支付仅仅只在使用退款接口或者撤销订单的时候需要可能会用到证书,为什么是可能呢?因为在接口函数中,我们可以选择是否使用证书,不使用也能使用退款或撤销订单接口。
证书下载:证书不是在公众平台下载的,是在商户平台的账户设置->API安全里面下载。下面的是微信提供的测试证书,我们测试和学习可以使用它们。
- const SSLCERT_PATH = '../cert/apiclient_cert.pem'; //测试提供的私钥
- const SSLKEY_PATH = '../cert/apiclient_key.pem'; //测试提供的公钥
2,代理服务器和错误上报
下面的为代理服务器和程序错误信息上报等级相关设置,如无特殊情况,默认即可。
- const CURL_PROXY_HOST = "0.0.0.0"; // 代理的IP,不需要代理,请设置为0.0.0.0和0
- const CURL_PROXY_PORT = 0; //代理的端口,此时不开启代理(如有需要才设置)
- const REPORT_LEVENL = 1; //错误信息上报等级,0.关闭上报; 1.仅错误出错上报; 2.全量上报
网页设置
1,OpenId简介
OpenId是微信用来唯一标识用户的一串字符,通俗来说就是微信用户的id。在前面的课程中我们知道了在微信支付中调起支付需要在代码设置参数,而且要设置的还不止一个,其中还要设置的就有用户的OpenId,哪个用户呢?我们知道每一次的交易,都有一个付款者,那么在这次交易中,我们要设置的OpenId就是他的,上面谈到,OpenId是微信唯一标识用户的,我们不可能随便就获取到,因此微信提供了一个专门用来获取用户OpenId的接口给我们。
2,获取OpenId的准备
如果我们有自己的公众号,而且公众号开通了微信支付权限的话,那么为了获取OpenId,我们需要登录公众平台,然后在左边的菜单栏滑到最低,找到并点击开发->接口权限
再到右边的功能列表中找到网页授权获取用户基本信息,然后点击修改字体
然后在输入框中输入我们要获取用户OpenId的网页页面的所在路径的域名即可,记住,输入域名就行了,前面不用再添加协议字符,即http://或https://的字符,还有不要填域名的ip哦
1,支付相关代码路径授权
现在我们已经下载好了微信支付 PHP 的 SDK,默认所有配置信息都是微信提供的,如果我们有自己的公众号且开通了支付权限,除了上面的获取OpenId的准备工作之外,我们还要把我们写好了的微信支付的相关代码,放到我们自己的服务器里面,然后需要到公众平台授权该路径。
首先,在公众号的左边菜单栏找到微信支付并点击,然后在右边点击开发配置
这里有两个目录,一个是支付授权目录,另一个是支付测试目录,支付授权目录和支付测试目录的区别仅仅在于:支付测试目录中可以设置测试微信号,也就是说,在这个目录里只有我设置了的账号才可以支付,而授权目录是所有人都可以支付的。在测试白名单里设置微信号。
点击支付授权目录的修改,我们就可以设置授权目录了,需要注意的是:发起支付请求的链接地址,都必须在支付授权目录之下,例如:我们把PHP版的SDK全部放在一个叫做 WeChatPay 的文件夹下,那么我们的授权目录路径就是:www.xxxx.com:xx/WeChatPay,为了保证路径没错,拿SDK的文件夹 example 为例,因为发起支付的文件 jsapi.php 在example里面,所以,我们还可以添加多一个目录为:www.xxxx.com:xx/WeChatPay/example。
测试支付目录的修改参照授权目录,如果你设置了测试授权目录,记得到测试白名单中添加测试账号
下面的课程我们讲从代码的角度去学习微信支付
example文件夹下的 WxPay.JsApiPay.php
对应文件管理中example文件夹下的 WxPay.JsApiPay.php文件
1,静态函数 unifiedOrder
它负责统一下单,除刷卡支付外的支付都是由它进行,如果我们是在网页中进行支付的话,这个函数是先行的,所传入的参数是 lib文件夹下WxPay.Data.php的WxPayUnifiedOrder 类,它主要负责设置一些订单的信息,例如设置商户订单号:
- $input->SetOut_trade_no("32个字符内、可包含字母的商户订单号");
返回值是一个数组,包含的状态码和支付信息,但是不能由这个来判断是否支付成功!官方文档
还有一个要注意的是:要设置的订单信息项,不止一个,下面是一个例子,在下面的课程我会逐个解释。
- $input = new WxPayUnifiedOrder();
- $input->SetOut_trade_no("32个字符内、可包含字母的商户订单号");
- //其他的设置
- $order = WxPayApi::unifiedOrder($input);
2,静态函数 refund
它负责退款,所有支付方式的退款都是由它进行,它所传入的参数是 lib文件夹下WxPay.Data.php的WxPayRefund 类,它主要负责设置一些要退款的订单的信息*,例如设置要退款的商户订单号。
注意:每次退款的商户订单号或微信订单号,都是和下单时候的一样,它们是配对的。
- $input->SetOut_trade_no("对应下单时的订单号");
返回值是一个数组,款数数目、时间、退款结果等,下面是一个例子。
- $input = new WxPayRefund();
- $input->SetOut_trade_no("下单时的商户订单号");
- //其他的设置
- $order = WxPayApi::refund($input);
对应文件管理中example文件夹下的WxPay.MicroPay.php文件
3,刷卡支付 pay
它负责刷卡支付下订单,所传入的参数是 lib文件夹下WxPay.Data.php的WxPayMicroPay 类,它主要负责设置要刷卡支付的订单信息,例子如下。
- $input->SetOut_trade_no("下单时的商户订单号");
返回值是一个数组,含有订单下单时间、支付的状态结果等,下面是一个例子。
- $input = new WxPayMicroPay();
- $input->SetOut_trade_no("下单时的订单号");
- //其他的设置
- $microPay = new MicroPay();
- $order = $microPay->pay($input);
1,静态函数 orderQuery
它负责查询订单,所有支付方式的订单都可以调用它来查询,它所传入的参数是 lib文件夹下WxPay.Data.php的WxPayOrderQuery 类,它主要负责设置要查询的订单的信息,除此之外,我们还可以利用它来判断商户号、AppId等信息是否存在,根据商户订单号查询,设置如下。
注意:每次查询的订单,它所设置的商户订单号或微信订单号,都是和下单时候的一样,它们是配对的。
- $input->SetOut_trade_no("对应下单时的订单号");
返回值是一个数组,含有订单下单时间、支付的状态结果、订单是否存在等,下面是一个例子。
- $input = new WxPayOrderQuery();
- $input->SetOut_trade_no("下单时的商户订单号");
- //其他的设置
- $order = WxPayApi::orderQuery($input);
2,静态函数 refundQuery
它负责查询退款订单,相比于orderQuery,我们可以理解为是相比于orderQuery的一部分,它所传入的参数是 lib文件夹下WxPay.Data.php的WxPayRefundQuery 类,它主要负责设置要查询的退款订单的信息,除此之外,我们还可以利用它来判断商户号、AppId等信息是否存在,这部分和WxPayOrderQuery几乎一样,根据商户订单号查询,设置如下。
注意:每次查询的退款订单,它所设置的商户订单号或微信订单号,都是和下单时候的一样,它们是配对的。
- $input->SetOut_trade_no("下单时的商户订单号");
返回值是一个数组,含有订单下单时间、支付的状态结果、订单是否存在等,下面是一个例子。
- $input = new WxPayRefundQuery();
- $input->SetOut_trade_no("下单时的订单号");
- //其他的设置
- $order = WxPayApi::refundQuery($input);
注意事项
1,参数设置个数
无论是下订单还是发起退款等操作,其设置的信息都限制了不能只有一个。
2,参数设置要求
1)商户订单号是由用户设置的,对应每条订单必须唯一;
2)下单中的款数设置,是int类型,微信规定了其单位是分,也就是说如果我们设置了100,事实付款1元,退款结果中的款数也是分作单位,切记;
3)下单中的货币类型,默认是CNY即人民币,若设置了其他的币种,要注意换算!
3,订单唯一性
每条订单可以由商户订单号,这个我们在每次下单一次生成一次的号码确定之外,还可以由微信订单号唯一确定,但是,微信订单号由微信支付自己生成,我们可以通过查询订单获得或者登陆商户平台查看。
在JavaScript处理支付结果
在发起支付请求后,我们需要如果可以通过下面两种方式来判断是否支付成功。第一种是在JavaScript回调函数中处理。
在jsapi.php文件的下面有几个JavaScript函数,他们分别是jsApiCall(),callpay(),editAddress()
editAddress()的功能是获取用户的位置信息,具体表现是,当用户进入支付页面,如果调用这个函数,那么就会调起选择地址的界面,等用户选择完成后,才返回进入下一步,对于它,我们默认即可
- //获取用户地址
- function editAddress()
- {
- WeixinJSBridge.invoke(
- 'editAddress',
- <?php echo $editAddress; ?>,
- function(res)
- {
- var value1 = res.proviceFirstStageName;
- var value2 = res.addressCitySecondStageName;
- var value3 = res.addressCountiesThirdStageName;
- var value4 = res.addressDetailInfo;
- var tel = res.telNumber;
- alert(value1 + value2 + value3 + value4 + ":" + tel);
- }
- );
- }
callpay()的功能是在确保WeixinJSBridge对象存在的情况下再进入jsApiCall()函数,此时把支付接口返回的数据进行解析并回调支付结果,在这里我们就能大致判断下用户支付是否成功,为什么是大致判断呢,因为微信团队告知了我们,在这里处理支付结果不可靠!切记。
- //调用微信JS api 支付
- function jsApiCall()
- {
- WeixinJSBridge.invoke(
- 'getBrandWCPayRequest',
- <?php echo $jsApiParameters; ?>,
- function(res){
- WeixinJSBridge.log(res.err_msg);
- alert(res.err_code+res.err_desc+res.err_msg);
- // 使用以上方式判断前端返回,微信团队郑重提示:
- // res.err_msg将在用户支付成功后返回ok,但并不保证它绝对可靠。
- if(res.err_msg == "get_brand_wcpay_request:ok" ) {
- // 支付成功
- }else if((res.err_msg =="get_brand_wcpay_request:fail"){
- // 支付失败
- }else if((res.err_msg =="get_brand_wcpay_request:cancel"){
- // 支付过程中用户取消
- }
- }
- );
- }
在回调链接中处理结果的入口
在发起订单组合订单参数的时候,我们有下面的一个设置,它就是用来在支持成功回调通知中处理成功之后事宜的,也就是说,我们可以在这个代码文件里面可靠地处理支付结果,下面的链接设置是默认的测试路径。
- $input->SetNotify_url("http://paysdk.weixin.qq.com/example/notify.php");
notify.php的例子文件在example文件夹下面,对应文件管理中example文件夹下的notify.php文件。
必须引入的头文件,以SDK的路径为例子
- require_once "../lib/WxPay.Api.php";
- require_once '../lib/WxPay.Notify.php';
回调信息处理的入口
- $notify = new PayNotifyCallBack();
- $notify->Handle(false);
现在打开lib文件夹下的WxPay.Notify.php文件,找到WxPayNotify类,入口函数Handler如下。
- /**
- * 回调入口
- * @param bool $needSign 是否需要签名输出
- */
- final public function Handle($needSign = true)
- {
- $msg = "OK";
- //当返回false的时候,表示notify中调用NotifyCallBack回调失败
- //若传入需要签名即传入true,获取签名校验失败,此时直接返回失败
- //notify函数里面传入了NotifyCallBack回调函数名,这时候它会被调用
- //$msg作为变量也传入了NotifyCallBack回调函数里面,$msg包含有支付信息
- $result = WxpayApi::notify(array($this, 'NotifyCallBack'), $msg);
- if($result == false)
- {
- $this->SetReturn_code("FAIL");
- $this->SetReturn_msg($msg);
- $this->ReplyNotify(false);
- return;
- }
- else
- {
- //该分支在成功回调到NotifyCallBack方法,处理完成之后流程
- $this->SetReturn_code("SUCCESS");
- $this->SetReturn_msg("OK");
- }
- $this->ReplyNotify($needSign);
- }
重写回调函数自定义处理
notify的回调方法在lib文件夹下的WxPay.Notify.php文件中,在NotifyCallBack函数里面,它调用了NotifyProcess,注意,此时就相当于调用了notify.php中的PayNotifyCallBack类里面的NotifyProcess函数。
- /**
- * notify的回调方法,该方法中需要赋值需要输出的参数
- * @param array $data
- * @return true回调出来完成不需要继续回调,false回调处理未完成需要继续回调
- */
- final public function NotifyCallBack($data) // $msg传进来
- {
- $msg = "OK";
- "//NotifyProcess就是我们要重写的函数"
- $result = $this->NotifyProcess($data, $msg);
- if($result == true){
- $this->SetReturn_code("SUCCESS");
- $this->SetReturn_msg("OK");
- } else {
- $this->SetReturn_code("FAIL");
- $this->SetReturn_msg($msg);
- }
- return $result;
- }
在notify.php中重写,NotifyProcess函数。
- class PayNotifyCallBack extends WxPayNotify
- {
- //查询订单
- public function Queryorder($transaction_id)
- {
- $input = new WxPayOrderQuery();
- $input->SetTransaction_id($transaction_id);
- $result = WxPayApi::orderQuery($input);
- Log::DEBUG("query:" . json_encode($result));
- if(array_key_exists("return_code", $result)
- && array_key_exists("result_code", $result)
- && $result["return_code"] == "SUCCESS"
- && $result["result_code"] == "SUCCESS")
- {
- return true;
- }
- return false;
- }
- //重写回调处理函数
- public function NotifyProcess($data,&$msg)
- {
- //"$data" 是NotifyCallBack函数传进来的含有支付信息的参数
- $notfiyOutput = array();
- // 下面这句判断支付参数中是否含有微信订单号transaction_id
- if(!array_key_exists("transaction_id", $data)){
- $msg = "输入参数不正确";
- return false;
- }
- //查询订单,判断订单真实性,二重判断
- if(!$this->Queryorder($data["transaction_id"])){
- $msg = "订单查询失败";
- return false;
- }
- // "这里返回真,证明支付成功了"
- // "我们也可以直接在这里做支付成功后的操作"
- return true;
- }
- }
订单查询
对应文件管理中example文件夹下的orderquery.php文件
1,头文件引入
必须引入的头文件,以SDK的路径为例子
- require_once "../lib/WxPay.Api.php";
可选的头文件引入,该头文件只是微信支付用于记录操作日志
- require_once 'log.php';
2,查询条件
每条订单可以由商户订单号,这个我们在每次下单一次生成一次的号码确定之外,还可以由微信订单号唯一确定,那么查询订单的时候也需要这两个参数之中的一个,由于微信订单的获取比较麻烦,所以一般我们采用商户订单号来进行查询,微信订单号和商户订单号最少填一个,微信订单号优先。
3,采用商户订单号查询
首先我们要这这个页面里面获取到要查询的商户订单号,例如通过get的形式来获取。
- $tradeId = $_GET["out_trade_no"];
然后就能调用接口函数来进行查询了。
- if(isset($tradeId) && $tradeId != "")
- {
- $input = new WxPayOrderQuery();
- $input->SetOut_trade_no($tradeId); // 设置好要查询的订单
- $order = WxPayApi::orderQuery($input)); // 进行查询
- var_dump($order); // 打印出订单信息
- }
常用的订单信息:
- if($order['err_code_des'] =="order not exist"){
- // 订单不存在
- }else{
- $money = $order['total_fee']; //所付款数,单位分
- if($order['trade_state'] =="SUCCESS"){
- //支付成功
- }else if($order['trade_state'] =="REFUND"){
- //已退款
- }else if($order['trade_state'] =="NOTPAY"){
- //用户还没支付
- }else if($order['trade_state'] =="CLOSED"){
- //订单关闭
- }else if($order['trade_state'] =="REVOKED"){
- //已撤销(刷卡支付)
- }else if($order['trade_state'] =="USERPAYING"){
- //用户支付中
- }else if($order['trade_state'] =="PAYERROR"){
- //支付失败(其他原因,例如银行返回失败)
- }
- }
4,采用微信订单号查询
- $wxId = $_GET["transaction_id"];
- if(isset($wxId) && $wxId != "")
- {
- $input = new WxPayOrderQuery();
- $input->SetOut_trade_no($wxId); // 设置好要查询的订单
- $order = WxPayApi::orderQuery($input)); // 进行查询
- var_dump($order); // 打印出订单信息
- }
申请退款
对应文件管理中example文件夹下的refund.php文件
1,头文件引入
必须引入的头文件,以SDK的路径为例子
- require_once "../lib/WxPay.Api.php";
可选的头文件引入,该头文件只是微信支付用于记录操作日志
- require_once 'log.php';
2,退款参数
除了要输入要退款的订单号之外,退款还需要商户号、退款的款数、商户订单号、为该次退款设置一个退款单号等,在这里要注意退款的款数要化为单位分,例如要退款2元,那么设置时要设置为200。
2,发起退款
这里我们以REQUEST的方式获取传过来的参数为例子,这种方式对发送端是get还是post没要求,无论是get的方式还是post方式,都能接收到,在安全方面不够post形式好。
- if(isset($_REQUEST["out_trade_no"]) && $_REQUEST["out_trade_no"] != ""){
- $out_trade_no = $_REQUEST["out_trade_no"]; // 要退款的订单的商户订单号
- $total_fee = $_REQUEST["total_fee"]; // 该订单的一共支付总额
- $refund_fee = $_REQUEST["refund_fee"]; // 要退款的钱数
- $input = new WxPayRefund();
- /** SetOut_trade_no 设置该订单的商户订单号,凭借这个唯一确定 */
- $input->SetOut_trade_no($out_trade_no);
- /** SetTotal_fee 设置订单的当时支付的总额 */
- $input->SetTotal_fee($total_fee);
- /** SetRefund_fee 设置要退款多少钱 */
- $input->SetRefund_fee($refund_fee);
- /** SetOut_refund_no 设置此次商户退款的单号,它不是商户订单号 */
- $input->SetOut_refund_no(WxPayConfig::MCHID.date("YmdHis"));
- /** SetOp_user_id 设置商户号 */
- $input->SetOp_user_id(WxPayConfig::MCHID);
- /** 发起退款 */
- $order = WxPayApi::refund($input);
- /** 在返回的数组中,我们能够获取键名return_code */
- if($order["return_code"]=="SUCCESS"){
- // 退款申请成功
- }else if($order["return_code"]=="FAIL"){
- // 退款申请失败
- }else{
- // 未知状态
- }
- var_dump($order);
- }
3,退款效果
一旦申请退款成功,那么该订单号对应的用户便会在微信客户端收到退款的凭据,同时在商家的商户平台也会有该退款记录生成。
查询退款
对应文件管理中example文件夹下的refundquery.php文件
1,头文件引入
必须引入的头文件,以SDK的路径为例子
- require_once "../lib/WxPay.Api.php";
可选的头文件引入,该头文件只是微信支付用于记录操作日志
- require_once 'log.php';
2,根据微信订单号查询
微信订单号在上面提到,它是微信支付系统自己帮我们生成的,如果要获知的话,目前我们可以在查询订单处获得,或者直接登录微信商户平台查看获得,由微信订单号查退款的灵活度不高,下面是例子代码。
- if(isset($_REQUEST["transaction_id"]) && $_REQUEST["transaction_id"] != ""){
- $transaction_id = $_REQUEST["transaction_id"];
- $input = new WxPayRefundQuery();
- $input->SetTransaction_id($transaction_id);
- $order = WxPayApi::refundQuery($input);
- var_dump($order)$order;
- };
3,根据商户订单号查询
商户订单号查询的灵活性高,因为商户订单号是我们自己生成的,在下订单生成的时候,我们在处理下单结果确认支付成功后,就可以把它存到数据库,查询的时候再读取出来。
- if(isset($_REQUEST["out_trade_no"]) && $_REQUEST["out_trade_no"] != ""){
- $out_trade_no = $_REQUEST["out_trade_no"];
- $input = new WxPayRefundQuery();
- $input->SetOut_trade_no($out_trade_no);
- $order = WxPayApi::refundQuery($input);
- var_dump($order)$order;
- }
4,根据商户退款单号查询
还记得在退款操作的时候有这么一句设置吗,如下所示,它就是设置商户退款单号的,也是由我们生成,一样要确保它对于每条退款的唯一性。
- /** SetOut_refund_no 设置此次退款的商户单号,它不是商户订单号 */
- $input->SetOut_refund_no(WxPayConfig::MCHID.date("YmdHis"));
- if(isset($_REQUEST["out_refund_no"]) && $_REQUEST["out_refund_no"] != ""){
- $out_refund_no = $_REQUEST["out_refund_no"];
- $input = new WxPayRefundQuery();
- $input->SetOut_refund_no($out_refund_no);
- $order = WxPayApi::refundQuery($input);
- var_dump($order)$order;
- }
5,根据退款单号查询
退款单号的生成和微信订单号一样,也是微信支付系统帮我们生成的。
- if(isset($_REQUEST["refund_id"]) && $_REQUEST["refund_id"] != ""){
- $refund_id = $_REQUEST["refund_id"];
- $input = new WxPayRefundQuery();
- $input->SetRefund_id($refund_id);
- $order = WxPayApi::refundQuery($input);
- var_dump($order)$order;
- }
上面的四种退款查询方式,各有所长,我们可以根据我们的实际情况的选择使用,一般来说,微信支付系统帮我们生成的单号的优先级别是大于我们自己生成的。
使用环境
一定要记住,微信支付的网页版支付方式只能在微信的内置浏览器里面进行,如果你在电脑浏览器打开了链接支付,它是会提示你错误的,那么怎么在电脑使用呢?可以安装手机虚拟机,例如Genymotion或者夜神虚拟机,再到手机虚拟机里面安装一个微信APP,就可以了。
测试的时候,你可以直接通过发送测试链接到你的微信里面,再打开就行了。
支付凭据
在每一次的订单支付成功后,微信都会发一条电子支付凭据给所付款的用户,支付凭据中的重要信息有微信订单号和商户订单号,除此之外还有商品名称,下单时间,支付状态。其中商户订单号由我们代码设置,商品名称也是,商品名称对应的是$input->SetBody("test");,下单时间对应的是$input->SetTime_start(date("YmdHis"));。
支付凭据可以让用户自己恢复订单。想象一下,用户购买了商品且支付成功,但是我们自己的数据库系统却因为其他问题漏单了,这时候我们可以在我们的网页中设置一个订单恢复系统,由用户输入订单号,然后我们后台进行订单查询,若订单的确支付成功了,而数据库没对应记录,那么我们就进而恢复订单。
微信支付严格要求支付的钱数最少是 1 分,在代码里面少于这个数会支付失败。
获取 OpenId
还记得获取OpenId的时候,页面会怎样吗?没错,它会重定向的,为了不丢失我们传过去的数据,最好的方法就是改写。
- lib文件夹下的 WxPay.Api.php 函数
- public function GetOpenid($userName,$userSex)
- {
- //通过code获得openid
- //code在微信服务处理完成之后重定向时带回来的
- if (!isset($_GET['code']))
- {
- // 假设现在我的支付代码文件的链接是:
- // http://hubwiz.com/WeChatPay/example/jsapi.php,那么下面就是
- $baseUrl = urlencode("http://hubwiz.com/WeChatPay/example/jsapi.php?userName=".$userName."&userSex=".$userSex);
- $url = $this->__CreateOauthUrlForCode($baseUrl);
- Header("Location: $url"); // 重定向
- exit();
- }
- else
- {
- //获取code码,以获取openid
- $code = $_GET['code'];
- $openid = $this->getOpenidFromMp($code);
- return $openid;// 返回OpenId
- }
- }
确保已下订单
在处理支付结果回调之后,建议再进行一次订单查询,以确保万无一失,除此之外,设置一个订单恢复系统也是可以的,例如下面的例子。
- if(isset($tradeId) && $tradeId != "")
- {
- $input = new WxPayOrderQuery();
- $input->SetOut_trade_no($tradeId); // 设置好要查询的订单
- $order = WxPayApi::orderQuery($input)); // 进行查询
- if($order['err_code_des'] =="order not exist"){
- // 订单不存在
- }else{
- $money = $order['total_fee']; //所付款数,单位分
- if($order['trade_state'] =="SUCCESS"){
- //支付成功
- //数据库操作
- //两者对比
- //若数据库中没记录,就恢复订单
- }else if($order['trade_state'] =="REFUND"){
- //已退款
- }else if($order['trade_state'] =="NOTPAY"){
- //用户还没支付
- }else if($order['trade_state'] =="CLOSED"){
- //订单关闭
- }else if($order['trade_state'] =="REVOKED"){
- //已撤销(刷卡支付)
- }else if($order['trade_state'] =="USERPAYING"){
- //用户支付中
- }else if($order['trade_state'] =="PAYERROR"){
- //支付失败(其他原因,例如银行返回失败)
- }
- }
- }
基于SDK修改的Demo
用法:先到 index.php 的上面修改链接数据,再把整个工程放到你的服务器,就可以测试使用了。
下载好之后,在index.php 文件里面配置下路径信息,如下面所示,当然,如果你不使用微信自带的测试支付配置,别忘了还要到lib文件夹下修改WxPay.Config.php文件里面的配置信息。
- <?php
- /** 请在下面设置你服务器的路径,默认是测试的,如果您没有自己的服务器,可以使用测试的 */
- $jsApiPay = "http://paysdk.weixin.qq.com/example/jsapi.php";
- /** 刷卡支付 */
- $micropyPay = "http://paysdk.weixin.qq.com/example/micropay.php";
- /** 扫码支付 */
- $nativePay = "http://paysdk.weixin.qq.com/example/native.php";
- /** 订单查询 */
- $orderQuery = "http://paysdk.weixin.qq.com/example/orderquery.php";
- /** 退款 */
- $refundPay = "http://paysdk.weixin.qq.com/example/refund.php";
- /** 退款查询 */
- $refundQueryPay = "http://paysdk.weixin.qq.com/example/refundquery.php";
- ?>