参考资料:
https://github.com/overtrue/laravel-wechat
https://www.easywechat.com/docs/
基于框架:laravel 5.7
首先安装easywechat拓展包
1、安装 laravel-wechat( easywehcat 的laravel支持包 )
# Laravel < 5.8
composer require "overtrue/laravel-wechat:~4.0"
# Laravel >= 5.8
composer require "overtrue/laravel-wechat:~5.0"
2、创建配置文件
php artisan vendor:publish --provider="Overtrue\LaravelWeChat\ServiceProvider"
3、修改配置文件参数(config/wechat.php)
微信支付基本配置需要配置如下
WECHAT_PAYMENT_APPID=商户号授权的公共号appid
WECHAT_PAYMENT_MCH_ID=商户号id
WECHAT_PAYMENT_KEY=商户号密钥,在商户号->账户中心->API安全->设置密钥
到这里已经基本配置好了laravel-wechat拓展包,然后根据支付的业务逻辑来创建支付代码
支付代码
我的业务逻辑是:
a)用户点击支付-》进入订单接口-》创建订单+创建微信支付统一订单-》返回到小程序-》小程序请求支付-》支付成功-》微信支付默认回调通知-》更新订单状态
b)待支付订单-》确认支付
普通流程
1、创建小程序订单创建接口并编辑(api/order/store)
// 新增订单
public function store(Request $request){
//请求参数正确性验证
$pay = null;
//创建一笔商品订单
if(count($request->mallsArray)){
// mysql 事务处理订单生成
DB::transaction(function () use($request,$address,&$pay,&$type) {
//存储订单信息
$order = new Order();
$order->form_id = $request->form_id;//存储微信小程序form-id
...
$order->save();
//存储该订单对应产品信息
foreach($request->mallsArray as $good){
$orderItem = new OrderItem();
...
$orderItem->save();
}
/*** 微信支付开始 ***/
// 1、创建微信支付统一订单
$app = app('easywechat.payment');
$unify = $app->order->unify([
'body' => $order->orderItem[0]->good->title . ' ' . $order->orderItem[0]->num . '...',
'out_trade_no' => $order->code,
'total_fee' => ($order->price_express+$order->price_good)*100,
'trade_type' => 'JSAPI',
'openid' => Customer::find($request->customer_id)->open_id, // 用户的openid
]);
//2、储存订单的微信统一支付订单号,防止用户取消支付再付款
$order->prepay_id = $unify['prepay_id'];
$order->save();
//3、如果成功生成统一下单的订单,那么进行二次签名,有2种方法
if ($unify['return_code'] === 'SUCCESS' && !isset($unify['err_code'])) {
/* $pay = [
'appId' => config('wechat.payment.default.app_id'),
'timeStamp' => (string) time(),
'nonceStr' => $unify['nonce_str'],
'package' => 'prepay_id=' . $unify['prepay_id'],
'signType' => 'MD5',
];
$pay['paySign'] = generate_sign($pay, config('wechat.payment.default.key')); */
$jssdk = $app->jssdk;
$pay = $jssdk->bridgeConfig($unify['prepay_id'], false); // 返回数组
} else {
//$unify['return_code'] = 'FAIL';
Log::info($unify);
return response()->json([
'info' => false,
'errmsg'=> '支付订单创建失败',
]);
}
/*** 微信支付结束 ***/
}, 5);
}
return response()->json([
'info' => true,
'pay'=>$pay,
]);
}
2、微信小程序端wxml代码
<form bindsubmit="formSubmit" report-submit="true">
// report-submit设为true获取一个form-id
...
</form>
3、微信小程序端js代码
formSubmit: function (e) {
var that = this
//数据验证
//数据验证通过,提交至服务器接口
wx.request({
url: app.globalData.baseUrl + '/api/order/store',
method: 'post',
data: {
mallsArray: mallsArray,//请求商品数组
...
form_id: e.detail.formId,//小程序表单formid,用于小程序给客户发模板消息
},
success(result) {
console.log(result)
if (result.data.info) {
//如果是购物车商品,删除购物车相关数据
if (wx.getStorageSync('order').type == 'cart') {
....
}
//调用微信支付接口
wx.requestPayment({
timeStamp: result.data.pay.timeStamp, //注意 timeStamp 的格式
nonceStr: result.data.pay.nonceStr,
package: result.data.pay.package,
signType: result.data.pay.signType,
paySign: result.data.pay.paySign, // 支付签名
// 支付成功后的回调函数
success: function (res) {
wx.redirectTo({
url: '/pages/order/order?status=2',
})
},
fail: function (res) {
if (res.errMsg == 'requestPayment:fail cancel') {
return wx.showToast({
icon: 'none',
title: '用户取消支付',
});
}
}
})
} else {
wx.showToast({
title: result.data.errmsg,
image: '/images/error.png'
})
}
}
})
},
4、微信支付默认回调通知控制器代码
public function paySuccess(Request $request){
$app = app('easywechat.payment');
$response = $app->handlePaidNotify(function($message, $fail) use($app) {
//Log::info($message);
// 使用通知里的 "微信支付订单号" 或者 "商户订单号" 去自己的数据库找到订单
$order = Order::where('code',$message['out_trade_no'])->first();
if (!$order || $order->status!=1) { // 如果订单不存在 或者 订单已经支付过了
return true; // 告诉微信,我已经处理完了,订单没找到,别再通知我了
}
//查询订单是否真的支付成功,防止用户模拟post
$order_search = $app->order->queryByOutTradeNumber($message['out_trade_no']);
//Log::info($order_search);
// return_code 表示通信状态,不代表支付状态
if ($message['return_code'] === 'SUCCESS') {
// 用户是否支付成功
if ($message['result_code']=== 'SUCCESS' && $order_search['result_code']=== 'SUCCESS' && $order_search['trade_state']=== 'SUCCESS') {
$order->paid_at = Carbon::now()->toDateTimeString(); // 更新支付时间为当前时间
$order->status = 2;
$order->save(); // 保存订单
// 用户支付失败
} elseif ($message['result_code'] === 'FAIL') {
log::info('用户支付失败,订单号'.$message['out_trade_no']);
}
} else {
return $fail('通信失败,请稍后再通知我');
}
return true; // 返回处理完成
});
return $response;
}
待支付订单流程
1、订单接口代码(api/store)
//立即付款接口
public function pay(Request $request){
$order = Order::find($request->order_id);
if(!$order || $order->customer_id != $request->customer_id || $order->status != 1){
return response()->json([
'info' => false,
'errmsg'=>'参数错误',
]);
}
//通过查询获取订单状态是否可用 且 未支付
$app = app('easywechat.payment');
$order_search = $app->order->queryByOutTradeNumber($order->code);
//Log::info($order_search);
if ($order_search['return_code'] === 'SUCCESS' && $order_search['result_code'] === 'SUCCESS' && $order_search['trade_state'] === 'NOTPAY' && $order->prepay_id) {
$jssdk = $app->jssdk;
$pay = $jssdk->bridgeConfig($order->prepay_id, false); // 返回数组
$type = $order->type;
} else {
//$unify['return_code'] = 'FAIL';
Log::info($order_search);
return response()->json([
'info' => false,
'errmsg'=> '支付订单创建失败',
]);
}
return response()->json([
'info' => true,
'pay'=>$pay,
'type'=>$type,
]);
}
2、微信端代码基本一样都是通过调用同一个接口请求支付
ps:form-id是用于小程序服务器给用户发送模板消息使用(订单支付成功、订单发货等),一个form-id只能使用一次,有效期7天,每次用户提交订单只会得到一个form-id,所以使用起来很受限。
后期会有新的订阅接口代替这个功能