目录
-
第一部分:核心概念
(图片来源网络,侵删)- 什么是支付接口?
- 支付流程简介 (同步/异步通知)
- 理解关键参数 (订单号, 金额, 回调地址等)
-
第二部分:准备工作
- 开通支付宝商家服务
- 开通微信支付商家服务
- 获取必要的密钥和配置参数
-
第三部分:PHP 环境准备
- 安装 Composer
- 引入官方 SDK
-
第四部分:实战 - 支付宝网页支付 (电脑端)
- 步骤 1: 创建订单页面
- 步骤 2: 调用 SDK 创建支付请求
- 步骤 3: 处理支付结果 (同步跳转)
- 步骤 4: 接收和处理异步通知 (最重要!)
- 步骤 5: 查询订单状态
-
第五部分:实战 - 微信扫码支付
(图片来源网络,侵删)- 步骤 1: 创建订单并生成支付二维码
- 步骤 2: 接收和处理异步通知
- 步骤 3: 查询订单状态
-
第六部分:安全最佳实践
- 验证异步通知的签名
- 防止重复通知
- 重要操作使用数据库事务
-
第七部分:总结与进阶
第一部分:核心概念
什么是支付接口?
支付接口是第三方支付平台(如支付宝、微信支付)提供给开发者的一套 API,开发者通过调用这些 API,在自己的网站或 App 中集成支付功能,用户无需跳转到支付平台 App 或网站即可完成支付。
支付流程简介
一个标准的支付流程通常如下:
- 创建订单: 用户在你的网站点击“支付”,你的服务器生成一个唯一的订单号,并调用支付接口的“创建订单”API。
- 用户支付: 支付接口返回一个支付链接或二维码,你的网站将此链接或二维码展示给用户,用户使用支付宝/微信 App 扫描或点击链接完成支付。
- 同步跳转: 用户支付成功后,支付平台会根据你配置的
return_url将用户浏览器同步跳转回你的网站。注意: 这个跳转只告诉用户“支付成功”,绝对不能作为订单最终成功的依据,因为用户可能中途取消,或者网络问题导致跳转失败。 - 异步通知: 这是最关键的一步! 支付平台会在用户支付成功后,主动向你在配置的
notify_url服务器地址发送一个 HTTP POST 请求,这个请求包含了支付结果的详细信息(如订单号、支付金额、交易状态等),你的服务器必须接收并处理这个通知,并验证其签名以确保请求的真实性,验证通过后,更新你数据库中订单的状态为“已支付”。
理解关键参数
- 订单号 (
out_trade_no): 你自己系统里生成的唯一订单 ID,用于后续对账和查询。 - 总金额 (
total_amount): 订单金额,单位通常是元,注意精度。 - 产品码 (
product_code): 指定支付方式,如FAST_INSTANT_TRADE_PAY(网页即时到账)。 - 异步通知地址 (
notify_url): 支付平台用来通知你支付结果的 URL。必须是一个公网可以访问的地址,不能是本地开发地址。 - 同步跳转地址 (
return_url): 支付成功后,用户浏览器跳转回来的 URL。
第二部分:准备工作
支付宝
- 注册账号: 访问 支付宝开放平台,注册并完成实名认证。
- 创建应用: 在控制台 -> 我的应用 -> 网页应用 & 移动应用 -> 创建应用。
- 获取配置信息:
- APPID: 应用的唯一标识。
- 应用私钥: 在应用详情的“接口加签方式”处生成。请务必妥善保管,不要泄露!
- 支付宝公钥: 上传你的应用私钥后,支付宝会生成对应的公钥,你需要下载这个公钥,用于验证异步通知的签名。
- 签约产品: 在“产品服务”中,找到并签约“网页支付 (alipay.trade.page.pay)”产品。
微信支付
- 注册账号: 访问 微信支付商户平台,注册并完成实名认证。
- 获取配置信息:
- 商户号 (
mch_id): 商户的唯一标识。 - API密钥 (
32位密钥): 在账户中心 -> API安全 -> API密钥(32位)处设置和获取。请务必妥善保管! - AppID: 公众平台或开放平台的 AppID。
- 证书: 微信支付部分接口需要使用证书,请下载并妥善保管。
- 签约产品: 在“产品中心”中,找到并开通“Native支付”产品。
- 商户号 (
第三部分:PHP 环境准备
强烈建议使用 Composer 来管理依赖,这是 PHP 项目的标准做法。
-
安装 Composer: 如果你还没有安装,请访问 getcomposer.org 下载并安装。
-
引入官方 SDK:
- 支付宝 SDK:
composer require alipaysdk/alipay-php-sdk
- 微信支付 SDK:
composer require wechatpay/wechatpay
注意:微信支付官方提供了两个 SDK,一个是 v3 版本(推荐),一个是 v2 版本,这里以较新的 v3 版本为例。
- 支付宝 SDK:
第四部分:实战 - 支付宝网页支付
步骤 1: 创建订单页面 (create_order.php)
这个页面用于展示一个“立即支付”按钮,点击后调用后端接口创建支付请求。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">支付宝支付测试</title>
</head>
<body>
<h1>订单信息</h1>
<p>订单号: <?php echo 'ORDER_'.time(); ?></p>
<p>订单金额: 0.01 元</p>
<form action="alipay/pay.php" method="post">
<input type="hidden" name="order_no" value="ORDER_<?php echo time(); ?>">
<input type="hidden" name="amount" value="0.01">
<button type="submit">立即使用支付宝支付</button>
</form>
</body>
</html>
步骤 2: 调用 SDK 创建支付请求 (alipay/pay.php)
<?php
// 引入Composer的自动加载文件
require_once __DIR__ . '/../vendor/autoload.php';
// 引入支付宝配置文件
require_once __DIR__ . '/config.php';
use Alipay\EasySDK\Kernel\Factory;
use Alipay\EasySDK\Kernel\Util\AES;
// 1. 实例化应用
$alipay = Factory::setOptions([
'protocol' => 'https',
'gatewayUrl' => 'https://openapi.alipay.com/gateway.do',
'appId' => $config['app_id'],
'signType' => 'RSA2',
'alipayPublicKey' => $config['alipay_public_key'],
'merchantPrivateKey' => $config['merchant_private_key']
])->payment();
// 2. 获取前端传来的订单信息
$orderNo = $_POST['order_no'];
$amount = $_POST['amount'];
// 3. 调用SDK创建支付请求
try {
$result = $alipay->page()->pay($amount, $orderNo, '商品描述');
// 4. 直接输出HTML表单,用户提交后会跳转到支付宝支付页面
echo $result;
} catch (Exception $e) {
// 处理异常
echo "创建支付订单失败: " . $e->getMessage();
}
步骤 3: 处理同步跳转 (alipay/return.php)
用户支付成功后,支付宝会跳转到这里。仅用于展示成功页面,不更新订单状态!
<?php
require_once __DIR__ . '/../vendor/autoload.php';
require_once __DIR__ . '/config.php';
use Alipay\EasySDK\Kernel\Factory;
$alipay = Factory::setOptions([
'protocol' => 'https',
'gatewayUrl' => 'https://openapi.alipay.com/gateway.do',
'appId' => $config['app_id'],
'signType' => 'RSA2',
'alipayPublicKey' => $config['alipay_public_key'],
'merchantPrivateKey' => $config['merchant_private_key']
])->payment();
// 1. 获取支付宝GET方式传过来的参数
$bizContent = $_GET['biz_content']; // 这里面包含了 out_trade_no
$data = json_decode($bizContent, true);
$outTradeNo = $data['out_trade_no'];
// 2. 验证签名 (非常重要!)
$verifyResult = $alipay->common()->verifyNotify($_GET);
if ($verifyResult) {
// 验证成功
// 这里可以显示一个“支付成功,感谢您的购买!”的页面
// 但不要在这里更新数据库!
echo "支付成功!订单号: " . htmlspecialchars($outTradeNo);
} else {
// 验证失败
echo '签名验证失败!';
}
步骤 4: 接收和处理异步通知 (alipay/notify.php)
这是支付逻辑的核心!
<?php
require_once __DIR__ . '/../vendor/autoload.php';
require_once __DIR__ . '/config.php';
use Alipay\EasySDK\Kernel\Factory;
$alipay = Factory::setOptions([
'protocol' => 'https',
'gatewayUrl' => 'https://openapi.alipay.com/gateway.do',
'appId' => $config['app_id'],
'signType' => 'RSA2',
'alipayPublicKey' => $config['alipay_public_key'],
'merchantPrivateKey' => $config['merchant_private_key']
])->payment();
// 1. 获取POST过来的通知数据
$notifyData = $_POST;
// 2. 验证签名 (这是保障资金安全的关键!)
$verifyResult = $alipay->common()->verifyNotify($notifyData);
if ($verifyResult) {
// 3. 验证成功,处理业务逻辑
$outTradeNo = $notifyData['out_trade_no']; // 商户订单号
$tradeNo = $notifyData['trade_no']; // 支付宝交易号
$tradeStatus = $notifyData['trade_status']; // 交易状态
// 只处理交易成功的通知
if ($tradeStatus == 'TRADE_SUCCESS' || $tradeStatus == 'TRADE_FINISHED') {
// --- 在这里编写你的核心业务逻辑 ---
// 1. 检查订单是否已经处理过,防止重复通知
// 2. 使用数据库事务,确保数据一致性
// 3. 更新你数据库中的订单状态为“已支付”
// 4. 给用户发送购买成功邮件或短信通知
// 5. 执行发货等后续操作
// 示例伪代码:
if (isOrderPaid($outTradeNo)) {
// 订单已处理,直接返回成功
echo 'success';
exit;
}
// updateOrderStatus($outTradeNo, 'paid');
// sendThankYouEmail($outTradeNo);
echo 'success'; // 告诉支付宝服务器,我已成功处理通知
} else {
// 其他状态,如等待付款、交易关闭等
echo 'success'; // 同样返回成功,避免支付宝重复通知
}
} else {
// 4. 签名验证失败
// 记录日志,用于排查问题
file_put_contents('alipay_notify_error.log', date('Y-m-d H:i:s') . " - " . json_encode($notifyData) . "\n", FILE_APPEND);
echo 'fail';
}
步骤 5: 查询订单状态 (alipay/query.php)
当用户或管理员不确定支付状态时,可以主动调用此接口查询。
<?php
require_once __DIR__ . '/../vendor/autoload.php';
require_once __DIR__ . '/config.php';
use Alipay\EasySDK\Kernel\Factory;
$alipay = Factory::setOptions($config)->payment();
// 假设我们从某个地方获取了订单号
$outTradeNo = 'ORDER_1234567890'; // 替换为实际的订单号
try {
$result = $alipay->query()->queryByOutTradeNo($outTradeNo);
if ($result['code'] == '10000' && $result['trade_status'] == 'TRADE_SUCCESS') {
echo "订单支付成功!支付宝交易号: " . $result['trade_no'];
} else {
echo "订单未支付或查询失败。";
print_r($result);
}
} catch (Exception $e) {
echo "查询失败: " . $e->getMessage();
}
第五部分:实战 - 微信扫码支付
微信扫码支付流程与支付宝类似,主要区别在于创建订单时生成的是二维码图片。
步骤 1: 创建订单并生成支付二维码 (wechat/create_qr.php)
<?php
require_once __DIR__ . '/../vendor/autoload.php';
require_once __DIR__ . '/wechat_config.php';
use WeChatPay\Builder;
use WeChatPay\Crypto\AesUtil;
use WeChatPay\Util\RandomStringGenerator;
// 1. 构建微信支付客户端
$merchantId = $config['mch_id']; // 商户号
$serialNo = $config['serial_no']; // 证书序列号
privateKey = $config['private_key']; // 商户私钥
apiV3Key = $config['api_v3_key']; // API v3密钥
$client = Builder::factory([
'mchid' => $merchantId,
'serial' => $serialNo,
'privateKey' => $privateKey,
'cert' => $config['cert_path'], // 证书路径
'apiv3Key' => $apiV3Key,
]);
// 2. 准备订单数据
$outTradeNo = 'WX_ORDER_' . time();
$amount = 1; // 金额,单位:分
$description = '测试商品';
$payload = [
'appid' => $config['app_id'],
'mchid' => $merchantId,
'out_trade_no' => $outTradeNo,
'notify_url' => 'https://your-domain.com/wechat/notify.php', // 你的异步通知地址
'amount' => [
'total' => $amount,
'currency' => 'CNY'
],
'description' => $description,
];
// 3. 发起请求,创建订单
try {
$result = $client->chain('v3/pay/transactions/native')->post($payload);
$body = json_decode($result['body'], true);
// 4. 解析响应,获取二维码码串
$codeUrl = $body['code_url'];
// 5. 生成二维码并展示
// 这里可以使用任何二维码生成库,如 endroid/qr-code
// require_once __DIR__ . '/../vendor/autoload.php';
// use Endroid\QrCode\QrCode;
// use Endroid\QrCode\Writer\PngWriter;
// $qrCode = QrCode::create($codeUrl)
// ->setSize(300)
// ->setMargin(10);
// $writer = new PngWriter();
// $result = $writer->write($qrCode);
// header('Content-Type: '.$result->getMimeType());
// echo $result->getString();
// 为了简化,这里直接输出码串,前端用JS生成二维码
echo "<h1>请使用微信扫码支付</h1>";
echo "<p>订单号: " . $outTradeNo . "</p>";
echo "<div id='qrcode'></div>";
echo "<script src='https://cdn.jsdelivr.net/npm/qrcodejs@1.0.0/qrcode.min.js'></script>";
echo "<script>new QRCode(document.getElementById('qrcode'), { text: '" . htmlspecialchars($codeUrl) . "', width: 256, height: 256 });</script>";
} catch (Exception $e) {
echo "创建订单失败: " . $e->getMessage();
}
步骤 2: 接收和处理异步通知 (wechat/notify.php)
微信支付的异步通知处理逻辑与支付宝类似,但验证方式不同。
<?php
require_once __DIR__ . '/../vendor/autoload.php';
require_once __DIR__ . '/wechat_config.php';
use WeChatPay\Builder;
use WeChatPay\Crypto\AesUtil;
use WeChatPay\Validator\AccessToken;
// 1. 构建客户端
$client = Builder::factory($config);
// 2. 获取HTTP请求体
$notification = file_get_contents('php://input');
$notificationData = json_decode($notification, true);
// 3. 验证签名
// 从请求头中获取平台证书序列号和签名
$serialNo = $_SERVER['HTTP_WECHATPAY_SERIAL'];
$signature = $_SERVER['HTTP_WECHATPAY_SIGNATURE'];
$timestamp = $_SERVER['HTTP_WECHATPAY_TIMESTAMP'];
$nonce = $_SERVER['HTTP_WECHATPAY_NONCE'];
$verify = $client->verify(
$signature,
$timestamp,
$nonce,
$notification
);
if ($verify) {
// 4. 签名验证成功,处理业务逻辑
$outTradeNo = $notificationData['out_trade_no'];
$transactionId = $notificationData['transaction_id'];
$tradeState = $notificationData['trade_state']; // SUCCESS, CLOSED, REFUND 等
if ($tradeState == 'SUCCESS') {
// --- 在这里编写你的核心业务逻辑 ---
// 1. 检查订单是否已处理
// 2. 更新数据库订单状态
// 3. 执行后续操作 (发货等)
// 示例伪代码:
// if (isOrderPaid($outTradeNo)) { echo 'SUCCESS'; exit; }
// updateOrderStatus($outTradeNo, 'paid');
echo 'SUCCESS'; // 告诉微信服务器,通知处理成功
} else {
echo 'SUCCESS'; // 对于非成功状态,也返回成功,避免重复通知
}
} else {
// 5. 签名验证失败
file_put_contents('wechat_notify_error.log', date('Y-m-d H:i:s') . " - " . $notification . "\n", FILE_APPEND);
http_response_code(500); // 返回服务器错误状态码
echo 'FAIL';
}
第六部分:安全最佳实践
- 验证异步通知的签名: 这是防止伪造通知、确保资金安全的第一道防线,无论支付宝还是微信,都必须验证签名。
- 防止重复通知: 支付平台可能会因为网络问题多次发送通知,在你的业务逻辑中,必须根据
out_trade_no检查订单是否已经处理过,如果已处理,直接返回success,不要重复执行业务逻辑。 - 使用数据库事务: 在更新订单状态、减少库存、创建支付记录等多个操作时,务必使用数据库事务,确保所有操作要么全部成功,要么全部失败,避免数据不一致。
- 敏感信息保密: 私钥、API密钥等绝不能硬编码在代码里或提交到代码仓库,建议使用环境变量(如
getenv())或专门的配置文件,并设置好文件权限。 - HTTPS: 整个支付流程,特别是
notify_url和return_url,必须使用HTTPS协议,保证数据传输过程中的加密。
第七部分:总结与进阶
- 本教程带你走通了从零开始集成支付宝和微信支付的全过程,核心在于理解同步跳转和异步通知的区别,并牢牢把握验证异步通知签名这一安全关键点。
- 进阶:
- 退款功能: 实现调用退款接口,处理用户的退款请求。
- 账单下载: 定期从支付平台下载交易账单,用于对账。
- 移动端支付: 学习 App 支付(支付宝 App 支付、微信 App 支付)和 JSAPI 支付(在微信内置浏览器中调起支付)。
- 使用设计模式: 将支付逻辑封装成独立的 Service 类,使代码更清晰、易于维护和扩展。
- 多支付渠道: 设计一个统一的支付接口,可以动态选择使用支付宝还是微信支付。
希望这份详尽的教程能帮助你顺利完成 PHP 支付接口的开发!
