本文目录导读:

在 PHP 项目中集成支付功能,通常不是直接操作支付接口的底层协议(如 TCP),而是通过接入支付服务商提供的开放 API(如支付宝、微信支付、PayPal 等)来实现。
核心流程可以简化为:用户发起支付 -> 你后端生成订单并请求支付平台 -> 用户跳转支付或扫码 -> 支付平台异步通知你 -> 你更新订单状态。
下面是具体、实用的集成步骤,分阶段讲解:
准备工作(必做)
-
选择支付渠道:
- 国内:支付宝、微信支付(最常见)。
- 国外:PayPal、Stripe。
- 聚合支付:选择如 Laravel Cashier(团队维护)、Omnipay、Pay 等第三方包,可以一套代码对接多种支付。
-
获取商户资质:
- 注册成为开发者/商户。
- 获得 商户号(MCH ID)。
- 下载并配置 密钥(API Key / App Secret) 和 证书文件(如需,如微信支付退款需要)。
- 设置 回调地址(Notify URL):这是你服务器接收支付结果的接口。
-
搭建开发环境:
- 确保服务器支持 HTTPS(支付宝、微信强制要求在生产环境回调时使用 HTTPS)。
- 安装 PHP 扩展(如
curl、openssl、json)。
核心代码实现(以支付宝当面付和微信支付的JSAPI为例)
推荐方案:使用 Composer 包管理。 不要自己从头编写签名、XML解析等代码。
方案 A:使用支付宝官方 PHP SDK(推荐)
composer require alipay/alipay-sdk-php
-
配置:创建配置文件
alipay_config.php,包含app_id、merchant_private_key(应用私钥)、alipay_public_key(支付宝公钥)、notify_url、return_url。 -
发起支付(以手机网站支付为例):
<?php require_once 'vendor/autoload.php'; require_once 'config/alipay_config.php'; // 引入配置 use Alipay\EasySDK\Kernel\Factory; // 1. 初始化SDK Factory::setOptions($config); // $config 从 alipay_config.php 获取 // 2. 构建请求参数 $order = [ 'out_trade_no' => time() . rand(1000,9999), // 商户订单号 'total_amount' => '0.01', // 金额,单位元 'subject' => '测试商品', 'product_code' => 'QUICK_WAP_WAY', ]; // 3. 调用SDK发起支付 $result = Factory::payment()->wap()->pay( $order['subject'], $order['out_trade_no'], $order['total_amount'], $order['product_code'] ); // 4. 返回HTML页面(自动跳转到支付宝收银台) echo $result->body; // 实际上是一个自动提交的表单 -
处理异步通知(Notifiy URL):
<?php require_once 'vendor/autoload.php'; use Alipay\EasySDK\Kernel\Factory; // 1. 验证签名 $result = Factory::payment()->common()->verifyNotify($_POST); // $_POST 是支付宝POST过来的数据 if ($result === true) { // 签名通过 // 2. 获取订单号,检查金额,防止篡改 $out_trade_no = $_POST['out_trade_no']; $trade_no = $_POST['trade_no']; // 支付宝交易号 $total_amount = $_POST['total_amount']; // 3. 业务逻辑:查询本地订单,核对金额,更新状态为“已支付” // if (signature valid && amount matches) { // $order->status = 'paid'; // } // 4. 必须返回 "success" (全小写)给支付宝,否则会重复通知 echo 'success'; } else { echo 'fail'; }
方案 B:使用微信支付 PHP SDK(推荐 wechatpay/wechatpay)
composer require wechatpay/wechatpay
-
配置:获取
mchid(商户号)、apiclient_key.pem(商户私钥文件)、wechatpay-cert.pem(平台证书公钥文件)。 -
支付(以JSAPI为例, 需获取用户的openid):
<?php use WeChatPay\Builder; use WeChatPay\Crypto\Rsa; use WeChatPay\Formatter; $instance = Builder::factory([ 'mchid' => '你的商户号', 'serial' => '商户证书序列号', 'privateKey' => file_get_contents('/path/to/apiclient_key.pem'), 'certs' => ['平台证书序列号' => file_get_contents('/path/to/wechatpay-cert.pem')], ]); // 构建 prepay_id 请求 $resp = $instance->chain('v3/pay/transactions/jsapi')->post([ 'json' => [ 'appid' => '你的小程序/公众号APPID', 'mchid' => '你的商户号', 'description' => '测试商品', 'out_trade_no' => time() . rand(1000,9999), 'notify_url' => 'https://你的域名/wechat_notify.php', 'amount' => ['total' => 1, 'currency' => 'CNY'], // 单位:分 'payer' => ['openid' => '用户openid'], ], ]); $prepay_id = $resp['prepay_id']; // 将 prepay_id 传递给前端,由前端调用微信JS-SDK唤起支付 -
处理异步通知:
- 需要验证签名(使用微信支付平台证书公钥)。
- 解密回调数据中的
resource字段(AES-256-GCM 加密)。 - 同样,最终需返回 XML 格式的
SUCCESS或FAIL。
安全性关键点(必读)
即使完成了接口对接,以下安全措施也必须实施:
- 验证签名(Sign):所有回调都必须验证签名,支付宝使用 RSA2(推荐),微信使用平台证书公钥验证。绝对不要信任未经签名的数据。
- 金额校验:在异步通知的处理逻辑中,必须将接收到的支付结果中的
actual_paid_amount(实际支付金额) 与本地数据库中的订单金额进行精确比对,防止坏人伪造成功回调,把1元订单改成0.01元。 - 订单号防重:确保你的
out_trade_no是唯一的,支付平台允许幂等(同一通知可能多次发送),你的业务逻辑必须处理重复通知(通过数据库主键或唯一索引防止重复更新)。 - 使用 HTTPS加密,防止中间人攻击。
- 日志记录:记录所有支付请求、成功/失败的回调数据(脱敏后),方便排查问题和进行对账。
实战建议(减少踩坑)
- 推荐使用框架:Laravel 有官方推荐的 Laravel Cashier(支持 Stripe、Paddle、Mollie 等国际支付)或 Laravel Alipay 等第三方包,ThinkPHP 也有相应的集成包,它们封装了签名、路由、模型等,能极大降低工作量。
- 沙箱环境测试:所有支付服务商都提供沙箱(Sandbox)环境,用沙箱账号和测试金额先跑通流程,确认回调能正常接收。
- 杜绝硬编码:
app_id、secret、mch_id等敏感信息放在.env文件或环境变量中,不要直接写在代码里。
| 步骤 | 操作 | 说明 |
|---|---|---|
| 1 | 选择支付渠道+获取商户资质 | 商户号、密钥、回调地址 |
| 2 | 安装官方SDK(Composer) | 省去签名、HTTP请求、XML/JSON编解码的麻烦 |
| 3 | 实现“前端/用户点击 -> 后端生成预支付单” | 生成订单号,调用SDK的创建支付接口,获取支付链接/参数 |
| 4 | 实现“后端接收异步通知” | 验证签名、核对金额、更新订单状态、返回success |
| 5 | 实现“查询 & 对账” | 提供订单查询、退款等能力 |
一句话总结:不要重复造轮子,用官方SDK;注重签名验证和金额校验;异步通知里一定要幂等处理。