• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录

阿西八

  • 博客园
  • 联系
  • 订阅
  • 管理

公告

View Post

WxPayRefundQuery

准备工作

1 准备工作

1.1 微信支付简述

关于微信支付

 

1. 生活中的微信支付

  目前我们日常生活中接触得比较多的线上电子支付方式主要有两种,一种是支付宝,另一种就是微信支付了,微信支付是集成在微信客户端的支付功能,所谓微信客户端,主要是微信的移动应用,例如安卓(Android)微信APP、IOS微信APP,用户可以通过手机的微信APP完成快速的支付流程,当然,其他的移动应用也可以接入微信支付的接口来达到完成微信支付的所有功能。

2. 微信支付方式

  主要的支付方式有:公众号支付或称网页支付,扫描二维码支付,APP支付,除此之外还有刷卡支付等,还有一点就是,微信支付以绑定银行卡的快捷支付为基础的。

3. 为什么要掌握

  对于我们开发者而言,熟练地掌握微信支付的接口功能是必不可少的,因为以后我的要做的互联网产品中,可能是网页,亦可能是移动应用APP,都可能会集成微信支付,那么接下来让我们来学习如果使用PHP版的微信支付接口吧。

4. 关于本节课

   本门课程主要针对已经开通了微信商户号且开通微信公众平台微信支付功能系统的学员,那我没有开通这些东西怎么学习?如果没有开通,也没关系,可以直接使用本课程的代码,因为本节课程使用的是所有支付配置信息使用的都是测试账号提供的,在你学习了调起支付后,如果使用支付功能,那么会产生每次一分钱的费用,支付到微信PHP开发包代码中提供的测试商户号中,测试账号归属于微信系统。

 

### 1.2 下载SDK ###下载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.3 文件说明

文件说明

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-这个主要用来存放在支付过程中生成的各种日志文件 接下来我们进行文件配置 ## 2 进行配置 ### 2.1 微信公众平台 ###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

### 2.2 微信商户平台 ###lib文件夹下的WxPay.Config.php 1,商户号MCHID

  商户号MCHID,它用来唯一标识微信商户用户,它在我们注册成功成为商户的时候,官方会发来一封开户邮件,在这封邮件中可查看,如果没有商户号,我们一样可以使用微信的测试商户号,如果有的话,那么我们可以登陆微信商户平台。

const MCHID = '1225312702'; //这个也是微信提供的测试商户号

2,商户支付密钥KEY

  商户支付密钥KEY,必须由商户自己设置,如果我们有自己的商户,那么可以在商户平台的:账户设置->API安全中可以设置,没有的话也可以使用微信提供的来测试和学习。

const KEY = 'e10adc3949ba59abbe56e057f20f883e'; //微信提供的测试支付密钥

### 2.3 证书与代理服务器 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.全量上报
配置文件中,微信支付公众号和商户平台一共需要配置的有多少项?
4项

### 2.4 公众平台网页设置1 ###网页设置 1,OpenId简介

  OpenId是微信用来唯一标识用户的一串字符,通俗来说就是微信用户的id。在前面的课程中我们知道了在微信支付中调起支付需要在代码设置参数,而且要设置的还不止一个,其中还要设置的就有用户的OpenId,哪个用户呢?我们知道每一次的交易,都有一个付款者,那么在这次交易中,我们要设置的OpenId就是他的,上面谈到,OpenId是微信唯一标识用户的,我们不可能随便就获取到,因此微信提供了一个专门用来获取用户OpenId的接口给我们。

2,获取OpenId的准备

  如果我们有自己的公众号,而且公众号开通了微信支付权限的话,那么为了获取OpenId,我们需要登录公众平台,然后在左边的菜单栏滑到最低,找到并点击开发->接口权限

  再到右边的功能列表中找到网页授权获取用户基本信息,然后点击修改字体

  然后在输入框中输入我们要获取用户OpenId的网页页面的所在路径的域名即可,记住,输入域名就行了,前面不用再添加协议字符,即http://或https://的字符,还有不要填域名的ip哦

2.5 公众平台网页设置2

1,支付相关代码路径授权

  现在我们已经下载好了微信支付 PHP 的 SDK,默认所有配置信息都是微信提供的,如果我们有自己的公众号且开通了支付权限,除了上面的获取OpenId的准备工作之外,我们还要把我们写好了的微信支付的相关代码,放到我们自己的服务器里面,然后需要到公众平台授权该路径。

  首先,在公众号的左边菜单栏找到微信支付并点击,然后在右边点击开发配置

  这里有两个目录,一个是支付授权目录,另一个是支付测试目录,支付授权目录和支付测试目录的区别仅仅在于:支付测试目录中可以设置测试微信号,也就是说,在这个目录里只有我设置了的账号才可以支付,而授权目录是所有人都可以支付的。在测试白名单里设置微信号。

  点击支付授权目录的修改,我们就可以设置授权目录了,需要注意的是:发起支付请求的链接地址,都必须在支付授权目录之下,例如:我们把PHP版的SDK全部放在一个叫做 WeChatPay 的文件夹下,那么我们的授权目录路径就是:www.xxxx.com:xx/WeChatPay,为了保证路径没错,拿SDK的文件夹 example 为例,因为发起支付的文件 jsapi.php 在example里面,所以,我们还可以添加多一个目录为:www.xxxx.com:xx/WeChatPay/example。

  测试支付目录的修改参照授权目录,如果你设置了测试授权目录,记得到测试白名单中添加测试账号

  下面的课程我们讲从代码的角度去学习微信支付

 

如果只有公众账号而没有开通微信支付权限,能用自己的公众号使用微信支付功能吗?
不能

 

3 核心API函数

3.1 请求方面

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

 

  对应文件管理中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);

 

无论是下单还是退款,是否只设置一个订单号就够了呢?
不是

 

3.2 查询方面

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);

 

发起订单A时设置的商户订单号,和查询订单A的退款时传入的商户订单号是不是要保持一致?
是

 

3.3 注意事项

注意事项

1,参数设置个数

  无论是下订单还是发起退款等操作,其设置的信息都限制了不能只有一个。

2,参数设置要求

  1)商户订单号是由用户设置的,对应每条订单必须唯一;

  2)下单中的款数设置,是int类型,微信规定了其单位是分,也就是说如果我们设置了100,事实付款1元,退款结果中的款数也是分作单位,切记;

  3)下单中的货币类型,默认是CNY即人民币,若设置了其他的币种,要注意换算!

3,订单唯一性

  每条订单可以由商户订单号,这个我们在每次下单一次生成一次的号码确定之外,还可以由微信订单号唯一确定,但是,微信订单号由微信支付自己生成,我们可以通过查询订单获得或者登陆商户平台查看。

4 调起支付

4.1 H5支付页面

测试例子的调起支付页面

  对应文件管理中example文件夹下的jsapi.php文件,还记得在准备工作的课程中我们有学习过如果在电脑浏览器打开支付样例主页面点击jsapi支付的时候,页面会告诉我们请在微信客户端打开链接的情况吗,这是因为微信支付的网页版支付方式只能在微信的内置浏览器里面进行,这时候如果你在微信点击进入这个页面的话,而且成功调起支付了,那么我们会看到下面的这个页面。

   页面中的下单支付信息我会在下面的课程中介绍到,这里要注意哦,如果你点击了立即支付按钮,那么就会进入到支付页面,这是真实支付,测试支付的钱数是1分钱,钱会打到微信自己的账号,下面让我来进入调起支付的学习!

4.2 初识重定向

页面重定向

1,头文件引入

必须引入的头文件,以SDK的路径为例子

require_once "../lib/WxPay.Api.php";
require_once "WxPay.JsApiPay.php";

可选的头文件引入,该头文件只是微信支付用于记录操作日志

require_once 'log.php';

2,获取OpenId

  在前面的课程中介绍了OpenId的简介和获取的准备,现在我们来通过接口代码获取它,如果不考虑从其他页面传值到这个页面,那么我们可以直接通过下面的代码获取当前用户的OpenId

//获取用户openid
$tools = new JsApiPay();
$openId = $tools->GetOpenid();

  在真实的开发中,很大可能是需要传数据到这个页面的,我们可能通过get的方式,或者post的方式传带参数过来,希望在付完款后做数据库操作。

  这里就要注意下了:在使用微信支付的接口获取OpenId的时候,该接口函数是会进行页面重定向的,什么是页面重定向呢?通俗来说就是会自动再次访问当前这个网页,那么在我们第一次进来这个网页的时候如果带有参数过来,页面重定向后会出现什么问题呢?如果我们不做其他操作,那么我们第一次传过来的数据就会丢失,因为重定向的时候没有再传过来,接下来我们来学习解决如何处理这个问题。

4.3 自定义传参

解决页面重定向问题

1,页面重定向的解决方法

  1)使用SECTION,在第一次进入页面,把传递过来的数据保存到SECTION,重定向后再取出来;

  2)使用Cookie,但不推荐,这个有时候会失效。

  3)改造$tools->GetOpenid()

2,改造获取OpenId函数

  example文件夹下的WxPay.JsApiPay.php文件,找到方法GetOpenid()

public function GetOpenid()
{
    //通过code获得openid
    //code在微信服务处理完成之后重定向时带回来的
    if (!isset($_GET['code']))
    {
        //触发微信返回code码
        $baseUrl = urlencode('http://'.$_SERVER['HTTP_HOST'].$_SERVER['PHP_SELF'].$_SERVER['QUERY_STRING']);
        $url = $this->__CreateOauthUrlForCode($baseUrl);
        Header("Location: $url"); // 重定向
        exit();
    } 
    else 
    {
        //获取code码,以获取openid
        $code = $_GET['code'];
        $openid = $this->getOpenidFromMp($code);
        return $openid; // 返回OpenId
    }
}

  $baseUrl是要进行页面重定向的链接,默认是我们当前发起支付的页面,我们也可以设置为其他链接,需要注意的是这个链接是需要被urlencode编码的,而在__CreateOauthUrlForCode函数里面就加上了其他的信息,例如之前一直有说到的AppId,那么我们修改的话,只需要修改$baseUrl就行了。

  假设现在我需要传入用户的名称:userName,和用户的性别:userSex

 

$userName = $_GET["userName"];
$userSex  = $_GET["userSex"];
$tools = new JsApiPay();
$openId = $tools->GetOpenid($userName,$userSex); // 改造前是不能传参数进去的

 

  对应的改造如下

public function GetOpenid($userName,$userSex)
{
    //通过code获得openid
    //code在微信服务处理完成之后重定向时带回来的
    if (!isset($_GET['code']))
    {
        // 假设现在我的支付代码文件的链接是:
        // http://website/WeChatPay/example/jsapi.php,那么下面就是

        $baseUrl = urlencode("http://website/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
    }
}

  这样子,我们就能在页面重定向后继续获取我们第一次传过去的数据 userName 和 userSex 了。

4.4 发起支付

发起支付第一步

1,发起支付请求

  在获取了OpenId之后,我们就可以组合订单了。下面是一个标准的组合例子

$input = new WxPayUnifiedOrder();
/** SetBody 设置商品或支付单简要描述 */
$input->SetBody("test");  
/** SetAttach 设置附加数据,在查询API和支付通知中原样返回 */
$input->SetAttach("test");
/** SetOut_trade_no 设置商户系统内部的订单号,32个字符内、可包含字母 */
$input->SetOut_trade_no(WxPayConfig::MCHID.date("YmdHis"));
/** SetTotal_fee 设置订单总金额,单位为分,只能为整数 */
$input->SetTotal_fee("1");
/** SetTime_start 设置订单生成时间,格式为yyyyMMddHHmmss */
$input->SetTime_start(date("YmdHis"));
/** SetTime_expire 设置订单失效时间,格式为yyyyMMddHHmmss */
$input->SetTime_expire(date("YmdHis", time() + 600));
/** SetGoods_tag 设置商品标记,代金券或立减优惠功能的参数 */
$input->SetGoods_tag("test");
/** SetNotify_url 设置接收微信支付异步通知回调地址 */
$input->SetNotify_url("http://paysdk.weixin.qq.com/example/notify.php");
/** SetTrade_type 设置支付方式,设置取值如下:JSAPI,NATIVE,APP,分别是网页、扫码、APP,刷卡不在这 */
$input->SetTrade_type("JSAPI");
/** SetOpenid 设置我们获取的用户OpenId */
$input->SetOpenid($openId);
/** 进行支付 */
$order = WxPayApi::unifiedOrder($input);

2,调起支付的效果

  一旦调起了支付请求,此时用户将会进入到微信的支付页面,这个页面会显示出付款信息,含有时间、款数、付钱给谁、还有商品名字,与此同时在商家的商户平台也会有对应的订单生成。

4.5 前端处理支付结果

在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"){
                // 支付过程中用户取消
            }
        }
    );
}

 

结合我们前面讲到过的课程,在支付回调时能否结合 某个 查询来确保订单支付成功呢?
能

 

4.6 PHP回调入口

在回调链接中处理结果的入口

   在发起订单组合订单参数的时候,我们有下面的一个设置,它就是用来在支持成功回调通知中处理成功之后事宜的,也就是说,我们可以在这个代码文件里面可靠地处理支付结果,下面的链接设置是默认的测试路径。

$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);
}

4.7 PHP回调重写

重写回调函数自定义处理

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

 

处理订单支付结果的方式有几种?哪种更可靠呢?
两种,在回调链接中处理结果更可靠。

 

5 查询与退款

5.1 订单查询

订单查询

  对应文件管理中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); // 打印出订单信息
}

 

订单不存在的情况是什么原因?
输入了不存在的微信订单号或者商户订单号。

 

5.2 申请退款

申请退款

  对应文件管理中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,退款效果

  一旦申请退款成功,那么该订单号对应的用户便会在微信客户端收到退款的凭据,同时在商家的商户平台也会有该退款记录生成。

5.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;
}

   上面的四种退款查询方式,各有所长,我们可以根据我们的实际情况的选择使用,一般来说,微信支付系统帮我们生成的单号的优先级别是大于我们自己生成的。

官方的查询返回值文档

6 常见问题及解决方法

6.1 支付环境

使用环境

   一定要记住,微信支付的网页版支付方式只能在微信的内置浏览器里面进行,如果你在电脑浏览器打开了链接支付,它是会提示你错误的,那么怎么在电脑使用呢?可以安装手机虚拟机,例如Genymotion或者夜神虚拟机,再到手机虚拟机里面安装一个微信APP,就可以了。

   测试的时候,你可以直接通过发送测试链接到你的微信里面,再打开就行了。

6.2 支付凭据

支付凭据

   在每一次的订单支付成功后,微信都会发一条电子支付凭据给所付款的用户,支付凭据中的重要信息有微信订单号和商户订单号,除此之外还有商品名称,下单时间,支付状态。其中商户订单号由我们代码设置,商品名称也是,商品名称对应的是$input->SetBody("test");,下单时间对应的是$input->SetTime_start(date("YmdHis"));。

   支付凭据可以让用户自己恢复订单。想象一下,用户购买了商品且支付成功,但是我们自己的数据库系统却因为其他问题漏单了,这时候我们可以在我们的网页中设置一个订单恢复系统,由用户输入订单号,然后我们后台进行订单查询,若订单的确支付成功了,而数据库没对应记录,那么我们就进而恢复订单。

   微信支付严格要求支付的钱数最少是 1 分,在代码里面少于这个数会支付失败。

6.3 获取 OpenId

获取 OpenId

   还记得获取OpenId的时候,页面会怎样吗?没错,它会重定向的,为了不丢失我们传过去的数据,最好的方法就是改写。

lib文件夹下的 WxPay.Api.php 函数
public function GetOpenid($userName,$userSex)
{
    //通过code获得openid
    //code在微信服务处理完成之后重定向时带回来的
    if (!isset($_GET['code']))
    {
        // 假设现在我的支付代码文件的链接是:
        // http://website/WeChatPay/example/jsapi.php,那么下面就是

        $baseUrl = urlencode("http://website/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
    }
}

6.4 确保已下订单

确保已下订单

   在处理支付结果回调之后,建议再进行一次订单查询,以确保万无一失,除此之外,设置一个订单恢复系统也是可以的,例如下面的例子。

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"){
            //支付失败(其他原因,例如银行返回失败)
        }
    }
}

6.5 提供一个基于SDK修改的Demo

基于SDK修改的Demo

   用法:先到 index.php 的上面修改链接数据,再把整个工程放到你的服务器,就可以测试使用了。

PHP版Demo下载

   下载好之后,在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";
?>

posted on 2017-07-07 15:17  阿西八  阅读(1841)  评论(1)    收藏  举报

刷新页面返回顶部
 
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3