一、参照文档引入 composer 包 overtrue/laravel-wechat
二、商户开发配置自行到微信官网获取 商户后台
三、具体代码如下
<?php
namespace App\Http\Controllers\H5\Verification;
use EasyWeChat\Factory;
use App\Model\Agent\Agent;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Model\Advert\PlatformConfig;
use App\Model\OfflineClassroom\ClassroomAppiesModel;
use App\Model\OfflineClassroom\ClassroomPayHistoryModel;
use App\Model\OfflineClassroom\ClassroomRefundHistoryModel;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use function EasyWeChat\Kernel\Support\generate_sign;
class PayController extends Controller
{
protected $request;
protected $agent;
protected $appiesModel;
protected $platformConfig;
protected $historyModel;
protected $refundHistoryModel;
public function __construct(Request $request, Agent $agent, ClassroomAppiesModel $appiesModel, PlatformConfig $platformConfig, ClassroomPayHistoryModel $historyModel, ClassroomRefundHistoryModel $refundHistoryModel)
{
parent::__construct($request);
$this->request = $request;
$this->agent = $agent;
$this->appiesModel = $appiesModel;
$this->platformConfig = $platformConfig;
$this->historyModel = $historyModel;
$this->refundHistoryModel = $refundHistoryModel;
}
/**
* 支付配置
* @return string[]
*/
public function config()
{
$server = $this->request->server();
return [
// 必要配置
'app_id' => env('WECHAT_OFFICIAL_ACCOUNT_APPID', 'wx0b019484714*****'),
'mch_id' => env('WECHAT_MCH_ID', '******'),//商户id
'key' => env('WECHAT_API_SECRET_KEY', 'p32u23u4jk5l******8sdf378sjdf'), //API 密钥
// 如需使用敏感接口(如退款、发送红包等)需要配置 API 证书路径(登录商户平台下载 API 证书)
'cert_path' => $server['DOCUMENT_ROOT'] . '/wxpay/apiclient_cert.pem', // XXX: 绝对路径!!!!
'key_path' => $server['DOCUMENT_ROOT'] . '/wxpay/apiclient_key.pem', // XXX: 绝对路径!!!!
'notify_url' => env('APP_URL', 'https://lhdev.com') . '/mapi/wechat_pay_notify', //支付回调地址(必须为https)
//'sandbox' => true, // 设置为 false 或注释则关闭沙箱模式
];
}
/**
* 统一下单 唤起微信支付
* @return mixed
* @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
* @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function wechat_pay()
{
$order_no = request('order_no');//订单编号
if (empty($order_no)) {
return restful(['message' => '订单编号不能为空', 'code' => 201]);
}
$data = $this->get_data();
if ($data['code'] == 201) {
return restful($data);
}
$app = Factory::payment($this->config());
$result = $app->order->unify([
'body' => '课程押金',
'out_trade_no' => $order_no,
'total_fee' => $data['total_fee'],
'trade_type' => 'JSAPI', // 请对应换成你的支付方式对应的值类型
'openid' => $data['openid'],
]);
//成功生成统一下单的订单,那么进行二次签名
if ($result['return_code'] === 'SUCCESS' && !empty($result['prepay_id'])) {
// 二次签名的参数必须与下面相同
$params = [
'appId' => $result['appid'],
'timeStamp' => time(),
'nonceStr' => $result['nonce_str'],
'package' => 'prepay_id=' . $result['prepay_id'],
'signType' => 'MD5',
];
$params['paySign'] = generate_sign($params, $this->config()['key']);
return restful(['message' => 'OK', 'data' => $params]);
} else {
return restful(['message' => '错误', 'data' => $result, 'code' => 201]);
}
}
/**
* 支付回调
* @throws \EasyWeChat\Kernel\Exceptions\Exception
*/
public function wechat_pay_notify()
{
try {
DB::beginTransaction();
$app = Factory::payment($this->config());
$response = $app->handlePaidNotify(function ($message, $fail) {
// 使用通知里的 "微信支付订单号" 或者 "商户订单号" 去自己的数据库找到订单
$order = $this->appiesModel->where(['order_no' => $message['out_trade_no']])->first();
// 如果订单不存在 或者 订单已经支付过了
if (!$order || $order->pay_at) {
return true; // 告诉微信,我已经处理完了,订单没找到,别再通知我了
}
//建议在这里调用微信的【订单查询】接口查一下该笔订单的情况,确认是已经支付
if ($message['return_code'] === 'SUCCESS') {
// 用户是否支付成功
if ($message['result_code'] === 'SUCCESS') {
$order->pay_status = 1;
$order->amount = $message['total_fee'];
$order->pay_at = date('Y-m-d H:i:s'); // 更新支付时间为当前时间
// 用户支付失败
} elseif ($message['result_code'] === 'FAIL') {
$order->pay_status = 4;
}
} else {
return true;
// return $fail('通信失败,请稍后再通知我');
}
$order->save(); // 保存订单
$this->payment_history($message, $order->agent_id);//记录支付数据
DB::commit();
return true; //返回处理完成
});
$response->send(); //return $response;
} catch (\Exception $e) {
DB::rollBack();
return false;
}
}
/**
* 用户端回调查询支付结果
* @return mixed
* @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
* @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
*/
public function check_pay_results()
{
$order_no = request('order_no');//订单编号
if (empty($order_no)) {
return restful(['message' => '订单编号不能为空', 'code' => 201]);
}
$app = Factory::payment($this->config());
$result = $app->order->queryByOutTradeNumber($order_no);//系统内的订单编号
//$result = $app->order->queryByTransactionId("4200001133202108190483451692");//微信返回的订单编号。
if ($result['result_code'] === 'SUCCESS') {
return restful(['message' => '支付成功', 'data' => $result]);
}
return restful(['message' => '支付失败', 'data' => $result, 'code' => 201]);
}
/**
* 获取用户open_id和配置的报名金额
* @return array
*/
public function get_data()
{
$agent_id = resolve('agent')['id'];
$agent = $this->agent->where('id', $agent_id)->first(['id', 'weixin_openid']);
if (empty($agent->weixin_openid)) {
return ['message' => '微信未授权无法支付', 'code' => 201];
}
$data = $this->platformConfig->first(['id', 'deposit']);
$total_fee = $data ? bcmul($data->deposit, 100) : 100;
return [
'code' => 200,
'openid' => $agent->weixin_openid,
'total_fee' => (int)$total_fee //报名金额(分)
];
}
/**
* 记录支付数据
* @param array $data
* @param $agent_id
*/
public function payment_history($data = [], $agent_id)
{
$time = time();
$payment = [
'agent_id' => $agent_id,
'trade_type' => $data['trade_type'],
'transaction_id' => $data['transaction_id'],
'order_no' => $data['out_trade_no'],
'total_fee' => $data['total_fee'],
'info' => json_encode($data),
'created_at' => $time,
'updated_at' => $time,
];
$this->historyModel->create($payment);
}
/**
* 退款操作
* @return array
* @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
*/
public function refund()
{
$id = request('id');//报名id
if (empty($id)) {
return ['message' => '参数错误', 'code' => 201];
}
$order = $this->appiesModel->with(['history'])->find($id);
if (empty($order)) {
return ['message' => '报名记录不存在', 'code' => 201];
}
if ($order->pay_status != 1) {
return ['message' => '支付状态错误不可退款(未支付或已退款)', 'code' => 201];
}
if (empty($order->history)) {
return ['message' => '没有支付记录退款失败', 'code' => 201];
}
if (empty($order->amount)) {
return ['message' => '金额有误退款失败', 'code' => 201];
}
$data = $order->toArray()['history'];
$refund_no = 'TK' . time() . mt_rand(10000, 99999);//退单编号
$other['refund_desc'] = '押金退还';
$other['notify_url'] = env('APP_URL', 'https://lhdev.com') . '/mapi/wechat_funded_notify'; //退款回调地址
$app = Factory::payment($this->config());
$result = $app->refund->byTransactionId($data['transaction_id'], $refund_no, $data['total_fee'], $data['total_fee'], $other);
if ($result['return_code'] === 'SUCCESS' && !empty($result['out_trade_no'])) {
//修改订单为退款中
$this->appiesModel->where(['order_no' => $result['out_trade_no']])->update(['pay_status' => 3]);
return ['message' => '操作成功', 'data' => $result];
}
return ['message' => $result['err_code_des'], 'data' => $result, 'code' => 201];
}
/**
* 退款通知
* @throws \EasyWeChat\Kernel\Exceptions\Exception
*/
public function wechat_funded_notify()
{
$app = Factory::payment($this->config());
$response = $app->handleRefundedNotify(function ($message, $reqInfo, $fail) {
// $reqInfo 为 message['req_info'] 解密后的信息
//Log::info($reqInfo);
return $this->refund_history($reqInfo);
//return true; // 返回 true 告诉微信“我已处理完成”
// 或返回错误原因 $fail('参数格式校验错误');
});
$response->send();
}
/**
* 记录退款日志
* @param array $data
* @return bool
*/
public function refund_history($data = [])
{
try {
DB::beginTransaction();
if (!empty($data) && $data['refund_status'] === 'SUCCESS') {
//修改订单状态
$up_data = ['pay_status' => 2, 'refund_at' => date('Y-m-d H:i:s')];
$this->appiesModel->where(['order_no' => $data['out_trade_no']])->update($up_data);
$time = time();
$refund = [
'refund_id' => $data['refund_id'],
'refund_no' => $data['out_refund_no'],
'transaction_id' => $data['transaction_id'],
'order_no' => $data['out_trade_no'],
'total_fee' => $data['total_fee'],
'refund_fee' => $data['refund_fee'],
'info' => json_encode($data),
'created_at' => $time,
'updated_at' => $time,
];
//记录退款日志
$this->refundHistoryModel->create($refund);
DB::commit();
return true;
}
} catch (\Exception $e) {
DB::rollBack();
return false;
}
}
}
四、更多内容请参考EasyWeChat官方文档