微信支付PHP SDK ―― 公众号支付代码详解


Posted in PHP onSeptember 13, 2016

在微信支付 开发者文档页面 下载最新的 php SDK

http://mch.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=11_1

这里假设你已经申请完微信支付

1. 微信后台配置  如图

微信支付PHP SDK ―― 公众号支付代码详解

我们先进行测试,所以先把测试授权目录和 测试白名单添加上。测试授权目录是你要发起微信请求的哪个文件所在的目录。

例如jsapi 发起请求一般是jsapi.php所在目录 为测试目录,测试白名单即开发人员的微信号。

正式的支付授权目录不能和测试的一样否则会报错。不填写或者填错授权目录以及测试白名单都会报错。

报错样例:

NaNsystem:access_denied

微信支付PHP SDK ―― 公众号支付代码详解

不在测试白名单

微信支付PHP SDK ―― 公众号支付代码详解

2. 配置 lib/WxPay.Config.php文件

最主要配置一下四项:

const APPID = '';
const MCHID = '';
const KEY = '';
const APPSECRET = '';

APPID 和 APPSECRET都可以在微信后台中找到。
MCHID 在申请微信支付后发来的邮件中可以找到,KEY 则根据邮件提示

微信支付PHP SDK ―― 公众号支付代码详解

去商户平台配置即可。

3. 访问起始 index.php

首先访问 index.php 你可以看到界面

微信支付PHP SDK ―― 公众号支付代码详解

我们首先需要的是 JSAPI支付。但是看代码 index.php 最下面的链接。他默认是个demo的链接,改为我们自定义的即可

<ul>
    <li style="background-color:#FF7F24"><a href="<?php echo 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'].'example/jsapi.php';?>">JSAPI支付</a></li>
    <li style="background-color:#698B22"><a href="<?php echo 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'].'example/micropay.php';?>">刷卡支付</a></li>
    <li style="background-color:#8B6914"><a href="<?php echo 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'].'example/native.php';?>">扫码支付</a></li>
    <li style="background-color:#CDCD00"><a href="<?php echo 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'].'example/orderquery.php';?>">订单查询</a></li>
    <li style="background-color:#CD3278"><a href="<?php echo 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'].'example/refund.php';?>">订单退款</a></li>
    <li style="background-color:#848484"><a href="<?php echo 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'].'example/refundquery.php';?>">退款查询</a></li>
    <li style="background-color:#8EE5EE"><a href="<?php echo 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'].'example/download.php';?>">下载订单</a></li>
</ul>

当然你也可以直接写死为自己的访问链接。

4. JSAPI 支付

必要代码解析:

$logHandler= new CLogFileHandler("../logs/".date('Y-m-d').'.log');
$log = Log::Init($logHandler, 15);

调用日志类 可以通过 $log->DEBUG(‘test‘); 打印调试信息。其实也可以直接使用 $Log::DEBUG(‘test‘); 来调试

$tools = new JsApiPay();
$openId = $tools->GetOpenid();

主要是为了获取 openid 其中GetOpenid() 函数定义在 文件 WxPay.JsApiPay.php 文件中

public function GetOpenid()
 {
 //通过code获得openid
 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;
 }
 }

$baseUrl 其实就是为了在跳转回来这个页面。  可以继续跟踪函数__CreateOauthUrlForCode()  其实就是通过微信的Auth2.0 来获取Openid
参考链接:http://mp.weixin.qq.com/wiki/17/c0f37d5704f0b64713d5d2c37b468d75.html

这就需要你把微信的 网页授权接口也设置好。

获取到 Openid 就可以调用微信支付的统一下单接口了。回到 文件 jsapi.php 如下代码
$input = new WxPayUnifiedOrder();
$input->SetBody("test");
$input->SetAttach("test");
$input->SetOut_trade_no(WxPayConfig::MCHID.date("YmdHis"));
$input->SetTotal_fee("1");
$input->SetTime_start(date("YmdHis"));
$input->SetTime_expire(date("YmdHis", time() + 600));
$input->SetGoods_tag("test");
$input->SetNotify_url("http://paysdk.weixin.qq.com/example/notify.php");
$input->SetTrade_type("JSAPI");
$input->SetOpenid($openId);
$order = WxPayApi::unifiedOrder($input);
echo '<font color="#f00"><b>统一下单支付单信息</b></font><br/>';
printf_info($order);
$jsApiParameters = $tools->GetJsApiParameters($order);

这里面的代码:

$input->SetAttach("test");

如果 把值改为 $input->SetAttach("test this is attach");就会存在bug 后面再说,其实这个参数不是必须的干脆可以去掉。
代码:

$input->SetNotify_url("http://paysdk.weixin.qq.com/example/notify.php");

是设置接收支付结果通知的Url 这里是默认的demo 链接我们可以设置成我们的:

$input->SetNotify_url(dirname('http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI']).'/notify.php');

当然你也可以选择直接写死。

其中的函数 unifiedOrder($input) 可以到WxPay.Api.php 中文件跟踪,其实就是调用统一下单接口。

在 WxPay.Api.php 中需要更改的一处代码是:

//异步通知url未设置,则使用配置文件中的url
    if(!$inputObj->IsNotify_urlSet()){
      $inputObj->SetNotify_url(WxPayConfig::NOTIFY_URL);//异步通知url
    }

就是当没设置 notifyUrl 的时候回去配置文件中找,但是配置文件中根本没有设置。

所以你可以选择在 配置文件WxPay.Config.php 中加上这个配置,也可以直接写一个默认的notify链接。

函数 GetJsApiParameters() 是获取jsApi支付的参数给变量 $jsApiParameters 方便在下面的Js中调用

jsapi.php 中js的代码:

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

 function callpay()
 {
 if (typeof WeixinJSBridge == "undefined"){
   if( document.addEventListener ){
     document.addEventListener('WeixinJSBridgeReady', jsApiCall, false);
   }else if (document.attachEvent){
     document.attachEvent('WeixinJSBridgeReady', jsApiCall); 
     document.attachEvent('onWeixinJSBridgeReady', jsApiCall);
   }
 }else{
   jsApiCall();
 }
 }

其中点击立即支付按钮调用的就是 callpay() 函数,他有会调用jsApiCall() 函数打开支付程序。
此后输入密码完成支付。

在完成支付页面点击完成会回到这个支付页面,并弹出 支付成功的提示框

微信支付PHP SDK ―― 公众号支付代码详解

这个其实就是 js函数 jsApiCall 里面的alter 弹出的对话框

其中 res.err_msg 为get_brand_wcpay_request:ok 表明前端判断的支付成功,我们可以根据这个将支付跳转到成功页面。

但是这个并不可信。确认是否支付成功还是应当 通过notify.php 处理业务逻辑。

5. 支付结果通知 notify.php

其实这个页面最主要的代码就两行

$notify = new PayNotifyCallBack();
$notify->Handle(false);

其中大部分逻辑在 Handle 函数中处理 文件 WxPay.Notify.php

final public function Handle($needSign = true)
 {
 $msg = "OK";
 //当返回false的时候,表示notify中调用NotifyCallBack回调失败获取签名校验失败,此时直接回复失败
 $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);
 }

主要代码:

$result = WxpayApi::notify(array($this, 'NotifyCallBack'), $msg);
//跟踪函数 notify 文件WxPay.Api.php
public static function notify($callback, &$msg)
 {
 //获取通知的数据
 $xml = $GLOBALS['HTTP_RAW_POST_DATA'];
 //如果返回成功则验证签名
 try {
  $result = WxPayResults::Init($xml);
 } catch (WxPayException $e){
  $msg = $e->errorMessage();
  return false;
 }
 
 return call_user_func($callback, $result);
 }

通过 $GLOBALS[‘HTTP_RAW_POST_DATA‘]; 获取同志数据 然后 Init 函数验证签名等。验签成功运行代码
return call_user_func($callback, $result);

即调用了一个回调函数,NotifyCallBack() 函数并传递参数 $result 在NotifyCallBack函数中会调用我们重写的NotifyProcess()函数(此函数在notify.php 中被重写)

NotifyProcess() 判断也没有问题就会 设置返回 success的xml信息

$this->SetReturn_code("SUCCESS");
$this->SetReturn_msg("OK");

并最终调用函数 $this->ReplyNotify($needSign);  echo success的结果

函数ReplyNotify 需要修改一处代码:

final private function ReplyNotify($needSign = true)
 {
 //如果需要签名
 if($needSign == true && 
  $this->GetReturn_code($return_code) == "SUCCESS")
 {
  $this->SetSign();
 }
 WxpayApi::replyNotify($this->ToXml());
 }

$this->GetReturn_code($return_code) == "SUCCESS")

改为

$this->GetReturn_code() == "SUCCESS")

即可。
这样整个流程就结束了。上面提到了 传递订单参数

$input->SetAttach("test");

如果我设置 值为 test this is attach (其实只要有空格就会存在bug)

如图 传递的订单信息

微信支付PHP SDK ―― 公众号支付代码详解

可以看到 attach 信息正常,当然支付也是正常的没有任何问题。

但是发现总是会收到notify 通知,即意味着没有返回给微信服务器正确的结果通知。
打印服务器发来的通知数据

微信支付PHP SDK ―― 公众号支付代码详解

可以看到 attach 是 test+this+is+attach 即空格被转化为加号

打印接收到的签名和程序算出来的签名发现 签名不同,即认为接收结果异常。

所以我们要是想使用attach 这个值就不能有空格,要么干脆不使用这个参数

(等待微信修复这个bug, 也可能是我这边有哪个地方不会? - -#)

这样 微信支付的 JsApi支付就大致分析完成了。

以上就是对微信支付PHP SDK 支付代码的详解,后续继续补充相关资料,谢谢大家对本站的支持!

PHP 相关文章推荐
PHP5 字符串处理函数大全
Mar 23 PHP
PHP操作文件类的函数代码(文件和文件夹创建,复制,移动和删除)
Nov 10 PHP
PHP 冒泡排序 二分查找 顺序查找 二维数组排序算法函数的详解
Jun 25 PHP
php生成随机颜色的方法
Nov 13 PHP
如何使用php实现评委评分器
Jul 31 PHP
理解PHP中的Session及对Session有效期的控制
Jan 08 PHP
Django 中 cookie的使用
Aug 17 PHP
PHP中使用mpdf 导出PDF文件的实现方法
Oct 22 PHP
用Laravel Sms实现laravel短信验证码的发送的实现
Nov 29 PHP
php + WebUploader实现图片批量上传功能
May 06 PHP
基于Laravel-admin 后台的自定义页面用法详解
Sep 30 PHP
PHP引擎php.ini参数优化深入讲解
Mar 24 PHP
PHP基于单例模式编写PDO类的方法
Sep 13 #PHP
利用PHP将图片转换成base64编码的实现方法
Sep 13 #PHP
PHP将URL转换成短网址的算法分享
Sep 13 #PHP
PHP使用FFmpeg获取视频播放总时长与码率等信息
Sep 13 #PHP
php微信公众平台开发之微信群发信息
Sep 13 #PHP
PHP入门教程之使用Mysqli操作数据库的方法(连接,查询,事务回滚等)
Sep 11 #PHP
PHP入门教程之面向对象的特性分析(继承,多态,接口,抽象类,抽象方法等)
Sep 11 #PHP
You might like
解决PHP上传非标准格式的图片pjpeg失败的方法
2017/03/12 PHP
PHP PDOStatement::execute讲解
2019/01/31 PHP
JS父页面与子页面相互传值方法
2014/03/05 Javascript
js 动态为textbox添加下拉框数据源的方法
2014/04/24 Javascript
深入理解JavaScript系列(26):设计模式之构造函数模式详解
2015/03/03 Javascript
JavaScript实现简单的数字倒计时
2015/05/15 Javascript
JavaScript无阻塞加载和defer、async详解
2017/02/26 Javascript
原生js实现放大镜特效
2017/03/08 Javascript
JavaScript ES6中export、import与export default的用法和区别
2017/03/14 Javascript
Angular 表单控件示例代码
2017/06/26 Javascript
nodejs模块学习之connect解析
2017/07/05 NodeJs
JavaScript定义及输出螺旋矩阵的方法详解
2017/12/01 Javascript
微信小程序wx:for和wx:for-item的用法详解
2018/04/01 Javascript
Vue-component全局注册实例
2018/09/06 Javascript
element-ui多文件上传的实现示例
2019/04/10 Javascript
Vue2.x通用条件搜索组件的封装及应用详解
2019/05/28 Javascript
layui 实现表单和文件上传一起传到后台的例子
2019/09/16 Javascript
Vue 中 template 有且只能一个 root的原因解析(源码分析)
2020/04/11 Javascript
Vue实现计算器计算效果
2020/08/17 Javascript
[55:25]VGJ.T vs Optic Supermajor小组赛D组 BO3 第三场 6.3
2018/06/04 DOTA
python实现简单购物商城
2016/05/21 Python
Ubuntu安装Jupyter Notebook教程
2017/10/18 Python
Python with语句上下文管理器两种实现方法分析
2018/02/09 Python
Python使用progressbar模块实现的显示进度条功能
2018/05/31 Python
详解安装mitmproxy以及遇到的坑和简单用法
2019/01/21 Python
对python以16进制打印字节数组的方法详解
2019/01/24 Python
利用python和百度地图API实现数据地图标注的方法
2019/05/13 Python
Python使用matplotlib 画矩形的三种方式分析
2019/10/31 Python
Python Gluon参数和模块命名操作教程
2019/12/18 Python
Python+Kepler.gl轻松制作酷炫路径动画的实现示例
2020/06/02 Python
canvas如何实现多张图片编辑的图片编辑器
2020/03/10 HTML / CSS
给水排水工程专业毕业生推荐信
2013/10/28 职场文书
酒店办公室文员岗位职责
2013/12/18 职场文书
党员干部三严三实心得体会
2014/10/13 职场文书
关于群众路线的心得体会
2014/11/05 职场文书
员工福利申请报告
2015/05/15 职场文书