PHP如何接入支付宝H5支付及本地测试
明确结论
支付宝 H5 支付使用的是手机网站支付(WAP 支付),对应的核心接口为:
alipay.trade.wap.pay
接入时应查阅支付宝开放平台的“手机网站支付”文档,不要误用 App 支付、电脑网站支付或当面付文档。
测试环境和 HTTPS 需要注意以下几点:
- 联调可以使用支付宝开放平台的沙箱环境。沙箱会提供单独的应用参数和测试账号。
- 本地 PHP 环境可以测试下单、签名和拉起支付宝收银台。
- 支付宝服务器无法访问
localhost或局域网地址,所以异步通知地址notify_url必须能通过公网访问。 - 本地开发生成支付请求时,HTTPS 通常不是绝对前提。生产环境应使用有效的 HTTPS 地址,具体要求以支付宝开放平台当前的产品规则和安全要求为准。
- 同步跳转地址
return_url只能用于展示支付结果。最终支付状态必须通过异步通知或主动查询确认。
应该选择哪份文档
如果要在 H5 页面中通过浏览器唤起支付宝付款,应使用“手机网站支付”产品,对应接口为:
alipay.trade.wap.pay
下面是几种容易混淆的支付产品:
| 使用场景 | 对应产品或接口 |
|---|---|
| 手机浏览器中的 H5 页面 | 手机网站支付:alipay.trade.wap.pay |
| PC 浏览器网页 | 电脑网站支付:alipay.trade.page.pay |
| 原生 Android、iOS App | App 支付:alipay.trade.app.pay |
| 扫码支付、付款码支付 | 当面付相关接口 |
如果业务页面运行在微信、钉钉或其他应用的内置浏览器中,还要确认该浏览器是否允许直接唤起支付宝。部分内置浏览器会限制跳转,这类限制无法通过 PHP 接口解决,通常需要提示用户改用系统浏览器打开页面。
接入前需要准备的参数
正式环境通常需要以下参数:
- 支付宝开放平台应用的
app_id - 商户应用私钥
- 支付宝公钥
- 签名算法,一般使用
RSA2 - 支付完成后的同步跳转地址
return_url - 支付宝异步通知地址
notify_url
沙箱环境必须使用沙箱控制台提供的参数,不能混用正式环境的应用编号、密钥或账号。
常用网关如下:
沙箱:https://openapi.alipaydev.com/gateway.do
正式:https://openapi.alipay.com/gateway.do
支付宝开放平台的界面和沙箱入口可能发生调整,实际参数应以当前控制台显示的内容为准。
PHP 发起 H5 支付
下面的示例使用支付宝 PHP SDK 中常见的 AopClient 和 AlipayTradeWapPayRequest 类。不同版本 SDK 的目录结构和加载方式可能不同,类名、命名空间和自动加载配置请以实际下载的 SDK 为准。
<?php
require_once __DIR__ . '/vendor/autoload.php';
$aop = new AopClient();
// 沙箱环境;上线时改为正式网关
$aop->gatewayUrl = 'https://openapi.alipaydev.com/gateway.do';
$aop->appId = '你的沙箱APP_ID';
$aop->rsaPrivateKey = '你的应用私钥';
$aop->alipayrsaPublicKey = '你的支付宝公钥';
$aop->apiVersion = '1.0';
$aop->signType = 'RSA2';
$aop->postCharset = 'UTF-8';
$aop->format = 'json';
$request = new AlipayTradeWapPayRequest();
$outTradeNo = 'ORDER_' . date('YmdHis') . random_int(1000, 9999);
$bizContent = [
'out_trade_no' => $outTradeNo,
'total_amount' => '0.01',
'subject' => 'H5支付测试订单',
'product_code' => 'QUICK_WAP_WAY',
'quit_url' => 'https://example.com/order/cancel',
];
$request->setBizContent(
json_encode($bizContent, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)
);
$request->setReturnUrl('https://example.com/payment/alipay/return');
$request->setNotifyUrl('https://example.com/payment/alipay/notify');
// SDK通常返回一段自动提交到支付宝网关的HTML表单
$html = $aop->pageExecute($request, 'POST');
header('Content-Type: text/html; charset=UTF-8');
echo $html;
各参数的作用如下:
out_trade_no是商户订单号,在商户系统内必须唯一。total_amount建议使用字符串传递,以免出现浮点数精度问题。subject是订单标题。product_code在手机网站支付中通常填写QUICK_WAP_WAY。quit_url是用户中途退出支付时返回的地址。notify_url用于接收服务器异步通知。return_url是支付后浏览器跳转的地址。
不要直接拼接未经签名的支付宝网关 URL。请求参数必须按支付宝的规则签名。使用官方 SDK 可以减少签名、编码和参数排序方面的错误。
处理异步通知
确认支付结果主要依靠异步通知。通知接口收到请求后,应先验签,再核对订单号、金额、商户身份和交易状态。
<?php
require_once __DIR__ . '/vendor/autoload.php';
$aop = new AopClient();
$aop->alipayrsaPublicKey = '你的支付宝公钥';
$params = $_POST;
$isValid = $aop->rsaCheckV1(
$params,
$aop->alipayrsaPublicKey,
'RSA2'
);
if (!$isValid) {
http_response_code(400);
echo 'failure';
exit;
}
$outTradeNo = $params['out_trade_no'] ?? '';
$tradeNo = $params['trade_no'] ?? '';
$tradeStatus = $params['trade_status'] ?? '';
$totalAmount = $params['total_amount'] ?? '';
$appId = $params['app_id'] ?? '';
// 从数据库读取订单,核对以下信息:
// 1. out_trade_no 是否存在
// 2. total_amount 是否与订单金额一致
// 3. app_id、seller_id 等商户信息是否匹配
// 4. 订单是否已经处理过,避免重复入账
if ($tradeStatus === 'TRADE_SUCCESS' || $tradeStatus === 'TRADE_FINISHED') {
// 使用事务或幂等逻辑更新订单状态
// 保存支付宝交易号 $tradeNo
echo 'success';
exit;
}
echo 'failure';
支付宝可能重复发送异步通知,所以订单更新逻辑必须具备幂等性。已支付订单再次收到相同通知时,不能重复增加余额、发货或发放权益。
处理成功后,应按接口要求输出:
success
如果接口返回其他内容、处理超时或无法访问,支付宝可能继续重试通知。
本地环境如何测试
本地测试可以分为支付请求生成和异步通知联调两部分。
本地完成支付请求生成
PHP 服务即使运行在下面的本地地址,也可以生成支付表单:
http://localhost:8000
用户点击支付后,浏览器会把请求提交到支付宝沙箱网关。因此,本地环境可以测试以下内容:
- 参数组装
- RSA2 签名
- 支付页面跳转
- 沙箱账号登录
- 沙箱支付流程
- 同步返回页面
例如,可以使用 PHP 内置服务器启动项目:
php -S 127.0.0.1:8000 -t public
让支付宝访问异步通知接口
支付宝服务器无法访问以下地址:
http://localhost:8000/payment/alipay/notify
http://127.0.0.1:8000/payment/alipay/notify
http://192.168.1.10/payment/alipay/notify
测试异步通知时,需要提供公网可访问的地址。常见处理方式有:
- 把测试代码部署到公网测试服务器。
- 使用带有 HTTPS 域名的开发或预发布环境。
- 使用内网穿透服务,把临时公网地址转发到本地端口。
获得公网地址后,可以把通知地址设置为:
https://test.example.com/payment/alipay/notify
使用临时穿透地址时,要确保地址在整个支付和通知期间保持有效,同时检查穿透服务是否会修改 POST 请求体或参数编码。
是否必须使用 HTTPS
不同环节的要求并不完全相同:
- 本地页面生成支付请求:开发调试时通常可以使用 HTTP。
- 跳转支付宝收银台:支付宝网关本身使用 HTTPS。
return_url和notify_url:生产环境应配置 HTTPS。- 是否强制使用 HTTPS:可能受到产品规则、应用配置、浏览器策略和开放平台当前审核要求的影响,应以接入时的官方文档和控制台校验结果为准。
即使测试环境暂时接受 HTTP,正式上线时也不建议继续使用。HTTPS 可以防止支付结果、订单编号等数据在传输过程中被窃听或篡改。
测试流程
可以按下面的顺序进行联调:
- 在支付宝开放平台创建应用,确认已开通手机网站支付能力。
- 进入沙箱环境,获取沙箱
app_id、测试账号和相关配置。 - 生成应用 RSA2 密钥,并正确配置支付宝公钥。
- 把 SDK 网关设置为沙箱网关。
- 创建金额较小、订单号唯一的测试订单。
- 在手机浏览器或浏览器的移动设备模式中打开支付页面。
- 使用沙箱买家账号完成支付。
- 检查同步跳转页面。
- 通过公网通知地址检查异步通知、验签和订单更新。
- 必要时调用交易查询接口,核实最终交易状态。
- 上线前替换正式环境的网关、
app_id、密钥和账号,然后再次完成整套联调。
常见问题
页面提示签名错误
一般需要检查以下内容:
- 应用私钥是否属于当前
app_id - 配置的是“支付宝公钥”,还是误用了“应用公钥”
- 是否混用了沙箱和正式环境的密钥
sign_type是否为RSA2- 字符集是否统一为 UTF-8
- 私钥是否因换行、空格或格式转换而损坏
- 请求参数是否在签名后被修改
浏览器跳转成功,但订单没有变成已支付
应重点检查异步通知接口:
notify_url是否能通过公网访问- 接口是否允许支付宝发送的 POST 请求
- Web 服务器或防火墙是否拦截了请求
- 验签时是否使用了正确的支付宝公钥
- 接口处理成功后是否输出纯文本
success - 数据库更新是否发生异常或被回滚
如果暂时没有收到异步通知,不要只根据 return_url 判断支付成功。此时可以调用支付宝交易查询接口进行补偿查询。
同步返回显示支付成功,能否直接发货
不能。return_url 只是浏览器跳转地址,用户可以刷新、重复访问或伪造该地址。业务系统必须先验签并核对交易金额、订单号和商户信息,再根据异步通知或主动查询结果更新订单。
沙箱测试完成后只修改网关即可上线吗
不够。切换到正式环境时,至少要重新确认以下配置:
- 正式应用的
app_id - 正式应用私钥
- 正式支付宝公钥
- 正式网关地址
- 产品签约或应用能力状态
- 正式回调域名
- 订单金额与商户信息校验
- 服务器时间、网络和日志配置
沙箱参数和正式参数必须分开保存,避免配置混用造成签名失败,或者把订单请求发送到错误的环境。
备注:内容仅供参考。
