本文目录导读:

在 PHP 项目中对接支付接口,虽然具体 API 会因支付服务商(如微信支付、支付宝、PayPal、Stripe 等)而异,但核心流程和逻辑是通用的。
下面是一个标准的、模块化的对接指南,适合大多数 PHP 项目(无论是原生 PHP 还是 ThinkPHP、Laravel 等框架)。
核心流程(以用户付款为例)
- 用户在前端点击“支付”按钮。
- 前端请求后端 PHP(生成订单,获取支付参数)。
- 后端 PHP 处理逻辑:
- 校验订单(金额、库存、状态)。
- 生成订单记录(状态:未支付)。
- 调用支付服务商 SDK,生成支付参数(如二维码链接、支付令牌、Form 表单)。
- PHP 返回支付参数给前端。
- 前端调起支付组件(跳转支付页面、弹出二维码、调用 App 支付)。
- 用户完成支付。
- 支付服务商异步通知 PHP 后端(最重要的一步)。
- PHP 后端验证签名 -> 更新订单状态为“已支付” -> 返回成功标识给支付服务商。
准备工作:获取商户密钥
在每个支付平台(支付宝/微信开放平台等)都需要完成:
- 注册商户账号。
- 创建应用,获取
AppID。 - 配置密钥:
- 支付宝:配置 RSA2 公钥/私钥(推荐使用沙箱环境测试)。
- 微信支付:获取商户号
MchID、APIv2或APIv3密钥,下载证书文件。
- 设置回调地址(Notify URL),也就是你的服务器接收支付结果通知的 URL。
PHP 支付模块封装示例(模块化设计)
建议创建一个 PaymentService 类,而不是把逻辑散落在各处。
依赖管理(强烈建议使用 Composer)
# 支付宝官方 SDK composer require alipay/alipay-sdk-php # 微信支付官方 SDK (推荐使用 APIv3) composer require wechatpay/wechatpay-guzzle-middleware # 通用支付网关 (如果你的项目对接多家) # composer require hprose/hprose-php # 或其他
支付服务类骨架 (PaymentService.php)
<?php
namespace App\Services;
use Alipay\EasySDK\Kernel\Factory;
use WechatPay\GuzzleMiddleware\WechatPayMiddleware;
use GuzzleHttp\Client;
class PaymentService
{
// 支付宝配置
private $alipayConfig;
// 微信支付配置
private $wechatConfig;
public function __construct()
{
// 通常从 .env 文件读取,不要写死
$this->alipayConfig = [
'app_id' => env('ALIPAY_APP_ID'),
'alipay_public_key' => env('ALIPAY_PUBLIC_KEY'),
'merchant_private_key' => env('ALIPAY_MERCHANT_PRIVATE_KEY'),
'notify_url' => env('APP_URL') . '/api/payment/alipay/notify',
];
$this->wechatConfig = [
'mchid' => env('WECHAT_MCHID'),
'serial' => env('WECHAT_SERIAL'),
'private_key_path' => env('WECHAT_PRIVATE_KEY_PATH'),
'apiv3_key' => env('WECHAT_API_V3_KEY'),
];
}
// 支付宝 - 手机网站/PC网站支付
public function alipayPagePay($orderId, $amount, $subject)
{
// 1. 初始化 SDK
Factory::setOptions($this->alipayConfig);
// 2. 调用 API 生成支付页面 form 或 URL
$result = Factory::payment()->page()->pay(
$subject, // 订单标题
$orderId, // 商户订单号
$amount, // 金额 (单位: 元)
$this->alipayConfig['notify_url'] // 回调地址
);
// 3. 返回给前端的数据(一个自动提交的 Form 表单或者跳转链接)
// 对于 PC 网站,一般是返回 HTML 代码
return response($result->body);
// 对于小程序/APP,会返回 trade_no (交易号)
}
// 微信支付 - JSAPI (H5/小程序) 或 Native (扫码)
public function wechatNativePay($orderId, $amount, $description)
{
// 1. 构建支付参数
$params = [
'mchid' => $this->wechatConfig['mchid'],
'out_trade_no' => $orderId,
'appid' => env('WECHAT_APPID'),
'description' => $description,
'notify_url' => env('APP_URL') . '/api/payment/wechat/notify',
'amount' => [
'total' => (int)($amount * 100), // 微信金额单位为分
'currency' => 'CNY'
],
];
// 2. 发起 HTTP 请求 (使用官方 SDK 或 Guzzle)
// ... 这里省略具体签名和请求代码
// 3. 获取返回的 code_url(二维码链接)
$codeUrl = $response['code_url'];
// 4. 生成二维码,或直接返回链接给前端
return [
'code_url' => $codeUrl,
'out_trade_no' => $orderId,
];
}
// 统一下单入口 (根据前端传递的 channel)
public function unifyOrder($channel, $orderId, $amount, $subject)
{
switch ($channel) {
case 'alipay_wap':
return $this->alipayPagePay($orderId, $amount, $subject);
case 'wechat_native':
return $this->wechatNativePay($orderId, $amount, $subject);
default:
throw new \Exception("不支持的支付方式");
}
}
}
处理异步通知(最关键的环节)
支付成功后的回调必须由服务端接收,因为这是安全的(通过签名验证)。绝对不能只依赖前端跳转。
支付宝异步通知示例 (alipayNotify)
支付宝会 POST 一个表单到你的 notify_url。
// 路由: POST /api/payment/alipay/notify
public function alipayNotify(Request $request)
{
$postData = $request->post(); // 支付宝发来的数据
// 1. 签名验证 (必须做! 防止伪造通知)
Factory::setOptions($this->alipayConfig);
$isVerify = Factory::payment()->common()->verifyNotify($postData);
if (!$isVerify) {
// 签名失败,记录日志
return 'fail'; // 必须返回 fail,以便支付宝重发
}
// 2. 验证业务参数
$outTradeNo = $postData['out_trade_no']; // 我们的订单号
$tradeNo = $postData['trade_no']; // 支付宝交易号
$totalAmount = $postData['total_amount'];
$tradeStatus = $postData['trade_status'];
// 3. 只处理交易成功状态
if ($tradeStatus === 'TRADE_SUCCESS' || $tradeStatus === 'TRADE_FINISHED') {
// 4. 查询本地订单,防止重复回调
$order = OrderModel::where('order_no', $outTradeNo)->first();
// 重要: 检查订单状态,防止重复更新
if (!$order || $order->status === 'paid') {
return 'success'; // 已处理,直接返回成功
}
// 重要: 验证金额是否一致 (防止坏人修改金额)
if (abs(floatval($order->total_amount) - floatval($totalAmount)) > 0.01) {
// 金额不匹配,记录日志并报警
return 'fail';
}
// 5. 更新订单状态 (使用事务或锁)
\DB::transaction(function () use ($order, $tradeNo) {
$order->status = 'paid';
$order->transaction_id = $tradeNo;
$order->paid_at = now();
$order->save();
// 6. 触发后续业务逻辑 ( 给用户加积分, 发短信, 更新库存)
event(new OrderPaid($order));
});
// 7. 返回成功字符串(必须是 success,不能是其他)
return 'success';
}
// 其他状态,暂时忽略,但返回 success 防止重试
return 'success';
}
微信支付异步通知示例 (wechatNotify)
微信的通知是加密的 XML 或 JSON(APIv3 是 JSON)。
// 路由: POST /api/payment/wechat/notify
public function wechatNotify(Request $request)
{
// 微信 APIv3 通知是 JSON 格式, 且 body 是加密的
// 1. 需要先解密 (使用 APIv3 密钥)
// $decrypted = WechatPayDecrypt::decrypt($request->getContent(), $this->wechatConfig['apiv3_key']);
// 2. 验证签名 (微信平台证书签名)
// $isValid = WechatPayVerify::verify(...);
// 3. 解析数据
// $resource = $decrypted['resource'];
// $outTradeNo = $resource['out_trade_no'];
// $transactionId = $resource['transaction_id'];
// $tradeSuccess = $resource['trade_state'] === 'SUCCESS';
// 4. 业务逻辑 (同支付宝,检查订单、更新状态)
if ($tradeSuccess) {
// ... 更新订单逻辑 ...
// 更新库存等
}
// 5. 返回给微信: 必须返回 HTTP 200 和 特定格式的 JSON
return response()->json([
'code' => 'SUCCESS',
'message' => '成功'
]);
// 如果返回错误,微信会重试 3-7 天
}
安全与规范避坑指南
- 绝对不能在前端拼接支付逻辑! 所有请求支付服务商的动作必须在后端完成,前端只负责展示后端返回的结果并调起支付组件。
- 校验金额:在异步通知中,必须拿回调中的
total_amount与你数据库中的order.total_amount做比对,防止被篡改。 - 幂等性处理:支付回调可能会重复收到(网络抖动、重试机制),务必判断订单是否已经是
paid状态,如果已支付则直接return 'success'并跳出,不要重复发积分或发货。 - 签名验证:支付回调是唯一可信的通知方式,必须验证签名,验证通过后才更新数据库。
- 日志记录:记录原始回调数据
$request->all()和签名验证结果,方便以后排查问题。 - 订单号唯一性:使用
uniqid+recorder_id或 UUID 生成订单号,防止重复调用。 - 使用 HTTPS:前后端通信、支付回调地址必须使用 HTTPS,防止中间人攻击。
- 备好退款接口:支付功能上线前,建议先写好退款逻辑(PHP 调用服务商退款 API)。
常见问题与排查
验签失败:大概率是密钥配置不正确,或公钥/私钥搞反了,重新检查支付宝或微信的密钥设置页面。收不到回调:- 检查服务器防火墙,确保端口 80/443 开放。
- 检查
notify_url是否能被外网访问,且不要有 等参数(支付服务商会覆盖)。 - 检查代码是否返回了正确的
success(支付宝)或{"code":"SUCCESS"}(微信),如果是fail会被重试但仍可能失败。
重复通知:正常现象,支付服务商会自动重试,你的代码必须能处理同一个通知多次执行,并且结果一致(幂等)。
如果项目对安全性要求较高,或希望快速接入多种支付方式,推荐使用 Laravel 的 laravel/cashier 或 Yansongda/pay 这类成熟的 Composer 包,它们封装了底层的签名、解密和很多安全细节。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播