ThinkPHP5对接易支付系统 - 完整实现指南
ThinkPHP5对接易支付系统 - 完整实现指南
一、支付系统核心架构设计
1.1 支付流程时序图
sequenceDiagram
用户->>+商户系统: 提交订单
商户系统->>+易支付: 生成支付请求
易支付-->>-商户系统: 返回支付页面
商户系统->>+用户: 跳转支付页面
用户->>+易支付: 完成支付
易支付->>+商户系统: 异步通知(notify_url)
易支付->>+用户: 同步跳转(return_url)
商户系统-->>-用户: 显示支付结果
1.2 数据库设计建议
CREATE TABLE `payment_orders` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`out_trade_no` varchar(32) NOT NULL COMMENT '商户订单号',
`transaction_id` varchar(32) DEFAULT NULL COMMENT '支付平台交易号',
`payment_type` enum('alipay','wechat','unionpay') NOT NULL COMMENT '支付类型',
`amount` decimal(10,2) NOT NULL COMMENT '支付金额',
`subject` varchar(256) NOT NULL COMMENT '订单标题',
`status` tinyint(1) NOT NULL DEFAULT '0' COMMENT '0-未支付 1-支付成功 2-支付失败',
`create_time` int(11) NOT NULL COMMENT '创建时间',
`pay_time` int(11) DEFAULT NULL COMMENT '支付时间',
`notify_data` text COMMENT '通知原始数据',
PRIMARY KEY (`id`),
UNIQUE KEY `out_trade_no` (`out_trade_no`),
KEY `transaction_id` (`transaction_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='支付订单表';
二、支付核心类优化实现
2.1 配置管理类 (PaymentConfig.php)
<?php
namespace app\common\service;
class PaymentConfig
{
// 获取支付配置
public static function getConfig($paymentType = 'alipay')
{
$configs = [
'alipay' => [
'partner' => config('payment.alipay.partner'),
'key' => config('payment.alipay.key'),
'apiurl' => config('payment.alipay.apiurl'),
'sign_type' => 'MD5',
'input_charset' => 'utf-8',
'transport' => 'https'
],
'wechat' => [
// 微信支付配置...
]
];
return $configs[$paymentType] ?? [];
}
// 获取通知URL
public static function getNotifyUrl($type)
{
return url("payment/index/notify", ['type' => $type], true, true);
}
// 获取返回URL
public static function getReturnUrl($type)
{
return url("payment/index/return", ['type' => $type], true, true);
}
}
2.2 支付服务类 (PaymentService.php)
<?php
namespace app\common\service;
use ber_pay\Submit;
use think\Db;
class PaymentService
{
// 创建支付订单
public static function createOrder($params)
{
$data = [
'out_trade_no' => self::generateTradeNo(),
'payment_type' => $params['payment_type'],
'amount' => $params['amount'],
'subject' => $params['subject'],
'create_time' => time(),
'status' => 0
];
Db::name('payment_orders')->insert($data);
return $data['out_trade_no'];
}
// 生成支付请求
public static function buildRequest($paymentType, $orderNo, $amount, $subject)
{
$config = PaymentConfig::getConfig($paymentType);
$parameter = [
"pid" => trim($config['partner']),
"type" => $paymentType,
"notify_url" => PaymentConfig::getNotifyUrl($paymentType),
"return_url" => PaymentConfig::getReturnUrl($paymentType),
"out_trade_no" => $orderNo,
"name" => $subject,
"money" => $amount,
"sitename" => config('site.name')
];
$alipaySubmit = new Submit($config);
return $alipaySubmit->buildRequestForm($parameter);
}
// 生成订单号
private static function generateTradeNo()
{
return date('YmdHis').substr(implode(NULL, array_map('ord', str_split(substr(uniqid(), 7, 13), 1))), 0, 8);
}
}
三、支付控制器优化实现
3.1 支付控制器 (Payment.php)
<?php
namespace app\controller;
use app\common\service\PaymentService;
use think\facade\Request;
class Payment extends BaseController
{
// 发起支付
public function pay()
{
$paymentType = Request::param('type', 'alipay');
$amount = Request::param('amount', 0.01);
$subject = Request::param('subject', '测试商品');
// 创建订单
$orderNo = PaymentService::createOrder([
'payment_type' => $paymentType,
'amount' => $amount,
'subject' => $subject
]);
// 生成支付表单
$html = PaymentService::buildRequest($paymentType, $orderNo, $amount, $subject);
return $html;
}
// 异步通知处理
public function notify()
{
$paymentType = Request::param('type');
$config = PaymentConfig::getConfig($paymentType);
$alipayNotify = new \ber_pay\Notify($config);
$verifyResult = $alipayNotify->verifyNotify();
if ($verifyResult) {
// 获取通知数据
$data = Request::get();
// 处理订单逻辑
$this->updateOrder($data);
// 记录通知日志
$this->logNotify($data);
echo "success";
} else {
echo "fail";
}
}
// 同步跳转处理
public function return()
{
$paymentType = Request::param('type');
$config = PaymentConfig::getConfig($paymentType);
$alipayNotify = new \ber_pay\Notify($config);
$verifyResult = $alipayNotify->verifyReturn();
if ($verifyResult) {
$data = Request::get();
$order = Db::name('payment_orders')
->where('out_trade_no', $data['out_trade_no'])
->find();
if ($order && $order['status'] == 1) {
return $this->success('支付成功');
} else {
return $this->error('支付处理中,请稍后查看');
}
} else {
return $this->error('验证失败');
}
}
// 更新订单状态
private function updateOrder($data)
{
Db::name('payment_orders')
->where('out_trade_no', $data['out_trade_no'])
->update([
'status' => 1,
'transaction_id' => $data['trade_no'],
'pay_time' => time(),
'notify_data' => json_encode($data)
]);
}
// 记录通知日志
private function logNotify($data)
{
Db::name('payment_notify_log')->insert([
'out_trade_no' => $data['out_trade_no'],
'notify_data' => json_encode($data),
'create_time' => time()
]);
}
}
四、安全增强措施
4.1 签名验证优化
// 在Notify类中添加
protected function verifySign($params)
{
// 过滤空值和签名参数
$filteredParams = $this->paraFilter($params);
// 参数排序
$sortedParams = $this->argSort($filteredParams);
// 生成待签名字符串
$signString = $this->createLinkstring($sortedParams);
// 验证签名
return $this->md5Verify($signString, $params['sign'], $this->alipay_config['key']);
}
4.2 防重复通知处理
// 在notify方法中添加
$order = Db::name('payment_orders')
->where('out_trade_no', $data['out_trade_no'])
->find();
// 已处理过的订单直接返回成功
if ($order && $order['status'] == 1) {
echo "success";
return;
}
五、支付结果查询与补单
5.1 订单查询接口
public function queryOrder()
{
$orderNo = Request::param('order_no');
$order = Db::name('payment_orders')
->where('out_trade_no', $orderNo)
->find();
if (!$order) {
return $this->error('订单不存在');
}
return $this->success('查询成功', [
'status' => $order['status'],
'pay_time' => $order['pay_time'],
'amount' => $order['amount']
]);
}
5.2 自动补单机制
public function checkOrders()
{
// 查询超过30分钟未支付的订单
$orders = Db::name('payment_orders')
->where('status', 0)
->where('create_time', '<', time() - 1800)
->select();
foreach ($orders as $order) {
// 调用支付平台接口查询订单状态
$result = $this->queryFromPayment($order);
if ($result['status'] == 'success') {
$this->updateOrder($result['data']);
}
}
return count($orders);
}
六、常见支付问题解决方案
Q1: 支付成功但未收到通知
解决方案:
- 检查服务器是否能访问外网
- 验证notify_url是否可公开访问
- 检查防火墙设置
- 实现主动查询机制补单
Q2: 签名验证失败
排查步骤:
- 检查商户KEY是否正确
- 验证参数编码是否一致
- 检查签名算法实现
- 查看参数是否被修改
Q3: 支付金额不一致
处理方案:
// 在notify处理中添加金额验证
if ($order['amount'] != $data['money']) {
Db::name('payment_orders')
->where('out_trade_no', $data['out_trade_no'])
->update([
'status' => 3, // 标记为异常订单
'notify_data' => json_encode($data)
]);
echo "fail";
return;
}
七、支付界面优化建议
7.1 多支付方式选择
<div class="payment-methods">
<div class="method active" data-type="alipay">
<img src="/static/images/alipay.png" alt="支付宝">
</div>
<div class="method" data-type="wechat">
<img src="/static/images/wechat.png" alt="微信支付">
</div>
</div>
7.2 支付结果轮询
function checkPayment(orderNo) {
let timer = setInterval(() => {
$.get('/payment/query?order_no=' + orderNo, function(res) {
if (res.status == 1) {
clearInterval(timer);
alert('支付成功');
location.href = '/order/detail/' + orderNo;
}
});
}, 3000);
}
八、性能优化与监控
8.1 支付请求日志
Db::name('payment_request_log')->insert([
'out_trade_no' => $orderNo,
'request_data' => json_encode($params),
'ip' => Request::ip(),
'create_time' => time()
]);
8.2 慢查询监控
// 记录执行时间超过1秒的请求
$startTime = microtime(true);
// ...支付处理逻辑...
$endTime = microtime(true);
if ($endTime - $startTime > 1) {
Db::name('slow_payment_log')->insert([
'url' => Request::url(),
'exec_time' => $endTime - $startTime,
'create_time' => time()
]);
}