商城项目-微信支付流程
- 没有企业APPID, 只记录老师视频中的实现过程
1. 微信文档说明
微信支付文档很乱, 新的旧的方式掺杂在一起. 在微信支付的首页, 点击”产品中心”, 在”支付产品”点击自己希望接入微信支付的应用场景(课程实例是PC站接入, 所以选择Native支付).
微信支付根据不同的支付场景, 提供不同的接入方式. 微信支付场景.
- 付款码支付: 生成永久付款二维码供别人扫码支付.
- JSAPI支付: 在公众号中接入支付.
- 小程序支付: 微信小程序中支付.
- Native支付: PC端网站接入支付, 即生成临时付款二维码, 用户扫码支付.
- APP支付: 安卓和IOS端的APP接入支付.
- H5支付: 手机/平板等移动端的浏览器中接入支付.
点击Native支付中的”开发文档”, 在Native支付中的开发文档中, 这里是微信推荐的支付页面样式, 并提供了素材下载. 开发模式一已不被推荐, 开发模式二是老师推荐的方式. API列表新手直接放弃阅读, 难度很大, 坑也多; 作为替代方案, 使用新提供的sdk方式实现模式二接入相对来说更加简单一些.
开始之前, 先按照开发步骤中的流程, 申请到APPID, MCHID(商户号), KEY和APPSECRET.
2. 把Native支付示例嵌入到laravel项目中
2.1 调通微信支付并从微信获取支付二维码
在sdk方式中下载PHP版本的SDK包, 解压到laravel的vendor目录(假设重命名为wxpay). 在
/wxpay/example
中有各种支付场景的接入方式的示例(index.php
文件中列出了各种接入方式的链接). 其中, Native方式接入的示例文件是/wxpay/example/native.php
.在
/wxpay/example/native.php
中把模式二用到的代码拷贝到生成二维码的控制器方法中, 并改对引用到的文件路径(注意, 引入的文件中, 还引入了其他文件, 引入其他文件的也要改), 修改代码中微信支付相关的类为完全限定名称方式的调用(都在全局命名空间中).- 控制器方法中的路径修改和微信支付相关类改为完全限定名称调用
public function pay() {
// __DIR__ = X:/.../laravel/app/Http/Controllers/front;
// 用相对路径导航到wxpay根目录
$wxpayPath = __DIR__ . "/../../../../vendor/wxpay/";
// 微信二维码支付需要用到的文件
require_once $wxpayPath . "lib/WxPay.Api.php";// 改成
require_once $wxpayPath . "/example/WxPay.NativePay.php";
/* 改成完全限定名称 */
$notify = new \NativePay();
//模式二
/**
* 流程:
* 1、调用统一下单,取得code_url,生成二维码
* 2、用户扫描二维码,进行支付
* 3、支付完成之后,微信服务器会通知支付成功
* 4、在支付成功通知中需要查单确认是否真正支付成功(见:notify.php)
*/
/* 改成完全限定名称 */
$input = new \WxPayUnifiedOrder();
// 模式二的其他业务逻辑
$input->SetBody("test");
$input->SetAttach("test");
// 这里要改成站点自己生成的订单号
$input->SetOut_trade_no("sdkphp123456789".date("YmdHis"));
// 设置支付金额(单位: 分)
$input->SetTotal_fee("1");
$input->SetTime_start(date("YmdHis"));
// 二维码有效时长
$input->SetTime_expire(date("YmdHis", time() + 600));
$input->SetGoods_tag("test");
// 处理微信返回的用户支付结果(xml文件格式数据)的请求地址.
$input->SetNotify_url("http://www.myweb.com/notify.php");
// 设置支付场景(Native)
$input->SetTrade_type("NATIVE");
$input->SetProduct_id("123456789");
$result = $notify->GetPayUrl($input);
$data['code_url'] = '';
// 调通的情况下, 把二维码地址发送到视图中渲染;
if($result['return_code'] == 'SUCCESS') $url2 = $result["code_url"];
// 调试时打印输出看看效果
var_dump($result);
return view('/front/shop/pay', $data);
}
- SDK中的文件引入路径修改-
/vendor/wxpay/example/WxPay.NativePay.php
require_once __DIR__ . "/../lib/WxPay.Api.php";
require_once __DIR__ . "/WxPay.Config.php";
require_once __DIR__ . '/log.php';
// ......
- SDK中的文件引入路径修改-
/vendor/wxpay/example/WxPay.Config.php
require_once __DIR__ . "/../lib/WxPay.Config.Interface.php";
- SDK中的日志方法修改-
/vendor/wxpay/example/log.php
// 注释写日志方法
public static function ERROR($msg)
{
// 修改为直接打印错误并返回.
echo $mes;return;
$debugInfo = debug_backtrace();
$stack = "[";
foreach($debugInfo as $key => $val){
if(array_key_exists("file", $val)){
$stack .= ",file:" . $val["file"];
}
if(array_key_exists("line", $val)){
$stack .= ",line:" . $val["line"];
}
if(array_key_exists("function", $val)){
$stack .= ",function:" . $val["function"];
}
}
$stack .= "]";
//self::$instance->write(8, $stack . $msg);
}
做完上述修改后, 调通该做的工作基本完成, 此时要把申请到的4个配置值设置到
WxPay.Config.php
中.- 把申请到的APPID, MCHID(商户号), KEY和APPSECRET值添加到配置文件
WxPay.Config.php
- 把申请到的APPID, MCHID(商户号), KEY和APPSECRET值添加到配置文件
/**
* TODO: 修改这里配置为您自己申请的商户信息
* 微信公众号信息配置
*
* APPID:绑定支付的APPID(必须配置,开户邮件中可查看)
*
* MCHID:商户号(必须配置,开户邮件中可查看)
*
*/
public function GetAppId()
{
// 返回APPID
return '';
}
public function GetMerchantId()
{
// 返回MCHID, 即商户号.
return '';
}
// ......
/*
* KEY:商户支付密钥,参考开户邮件设置(必须配置,登录商户平台自行设置), 请妥善保管, 避免密钥泄露
* 设置地址:https://pay.weixin.qq.com/index.php/account/api_cert
*
* APPSECRET:公众帐号secert(仅JSAPI支付的时候需要配置, 登录公众平台,进入开发者中心可设置), 请妥善保管, 避免密钥泄露
* 获取地址:https://mp.weixin.qq.com/advanced/advanced?action=dev&t=advanced/dev&token=2005451881&lang=zh_CN
* @var string
*/
public function GetKey()
{
// 返回KEY
return '';
}
public function GetAppSecret()
{
// 返回APPSECRET
return '';
}
// ......
- 当前实例没有设置证书, 所以需要修改
WxPay.Api.php
中的配置, 把相关配置设置成不需要证书:
private static function postXmlCurl($config, $xml, $url, $useCert = false, $second = 30) {
// ......
// 原文件中的567-568行
curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE); // 设置为FALSE表示不需要证书
curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,0); // 设置为0表示不执行严格校验, 默认值是2, 严格校验.
// ......
}
经过上面一通操作后,
var_dump($result)
就有值打印出来了, 即设置完成. 创建一个控制器方法, 复制/wxpay/example/qrcode.php
中的逻辑到方法中(注意修改相关文件的引入路径), 供前端<img>
元素获取生成的二维码图片- 生成二维码的控制器方法()
// 获取二维码的控制器方法
public function createQrccode() {
$wxpayPath = __DIR__ . '/../../../../vendor/wxpay/';
require_once $wxpayPath . 'example/phpqrccode/phpqrccode.php';
$url = urldecode($_GET['data']);
if(substr($url, 0, 6) == 'weixin') {
/* 改成完全限定名称 */
\QRcode::png($url);
} else {
header('HTTP/1.1 404 Not Found');
}
}
- 前端
<img>
元素获取二维码图片(记得设置路由)<img src="/shop/qrccode?data=<?php echo urlencode($url2); ?>" alt="" class="qrccode">
- 到这里, 应该能正常显示二维码了.
tips: 在SDK中, 没有移动端浏览器支付的实例, 只要设置该方法传入参数”mweb”即可:
$input->SetTrade_type("mweb");
.
2.2 获取从微信返回的支付结果
- 用户支付完成后, 微信会发送一个post请求, 发送用户支付结果(xml文件格式数据). 接收该post请求的url地址, 是在调通微信支付时设置的:
$input->SetNotify_url("处理微信返回的用户支付结果的请求地址");
. 该请求必须能通过外网访问. 支付结果信息中包含这些内容. 其中:
openid
: 谁付的钱.out_trade_no
: 站点生成的订单号.result_code
: 支付结果, SUCCES(成功)/FEIL().total_fee
: 支付的金额(单位: 分).transaction_id
: 微信内部的交易号.
支付界面(二维码扫码页面)需要设置一个定时器, 定时去查询订单的支付状态, 当支付状态为”已支付”时, 就使用调用
window.parent.location.reload()
的js表达式调用父页面的js方法, 完成父页面的刷新或跳转(到支付成功页面).