核心概念与准备工作(必看)
在开始写代码之前,你必须先完成以下准备工作,这是所有教程的基础。

微信支付产品选择
对于网站/App的支付,最常用的是 JSAPI 支付(在微信内置浏览器或微信小程序中使用)和 Native 支付(通过扫描二维码支付),本教程主要围绕 JSAPI 支付 展开,因为它最符合大多数网站集成的需求。
准备工作清单
-
注册微信商户号:
- 访问 微信支付官网,使用你的身份证或企业信息注册一个商户号。
- 注意:个人只能申请“个体工商户”,企业申请“企业”,你需要有对应的营业执照。
-
获取关键参数:
- 登录商户后台,在 “产品中心” -> “开发配置” 中找到你的产品(如“JSAPI支付”)。
- 获取以下核心信息,这些信息需要安全保管,切勿泄露:
- API密钥 (APIv3密钥):用于签名和验签,极其重要。
- 商户号 (mchid):你的商户ID。
- AppID:你的微信公众号或小程序的AppID。
- AppSecret:你的公众号或小程序的密钥。
-
获取 OpenID:
(图片来源网络,侵删)- JSAPI支付必须依赖用户的 OpenID,OpenID 是用户在某个公众号或小程序下的唯一标识。
- 如何获取 OpenID?
- 公众号场景:用户关注你的公众号后,通过网页授权获取(
snsapi_base或snsapi_userinfo)。 - 小程序场景:通过
wx.login获取code,再用code换取openid。
- 公众号场景:用户关注你的公众号后,通过网页授权获取(
- 简单来说:你必须在你的网站/小程序中,先通过微信授权流程,拿到当前登录用户的
openid,才能发起支付。
PHP 实现微信支付(JSAPI)核心步骤
下面是 PHP 实现 JSAPI 支付的完整流程。
步骤 1:安装依赖库(推荐)
强烈建议使用官方或社区维护的 SDK,而不是自己从头实现签名,这样可以避免很多坑。
-
官方推荐 SDK (Composer)
composer require wechatpay/wechatpay
-
老版官方 SDK
(图片来源网络,侵删)composer require wechatpay/wechatpay-v3
-
社区流行 SDK
composer require overtrue/wechat
本教程将以 官方推荐的 wechatpay/wechatpay 为例。
步骤 2:配置商户信息
创建一个配置文件,config.php,将你的商户信息填入。
<?php
// config.php
return [
'mchid' => '你的商户号', // 商户号
'appid' => '你的AppID', // 公众号/小程序AppID
'apikey' => '你的APIv3密钥', // APIv3密钥
'cert_path' => '/path/to/apiclient_cert.pem', // 证书路径(用于退款等操作)
'key_path' => '/path/to/apiclient_key.pem', // 证书私钥路径
'notify_url' => 'https://www.yourdomain.com/notify.php', // 支付结果通知地址
];
注意:证书路径在发起支付时暂时不需要,但在退款、查询等操作时是必需的。
步骤 3:后端生成支付参数
当用户点击“支付”按钮时,你的后端 PHP 代码需要向微信支付服务器请求一个预支付订单,并生成前端 JSAPI 支付所需参数。
创建一个 create_order.php 文件:
<?php
require 'vendor/autoload.php'; // 引入 Composer 自动加载
require 'config.php';
use WeChatPay\Builder;
use WeChatPay\Client;
use WeChatPay\Validator;
// 从配置中读取信息
$config = require 'config.php';
// 创建 Builder 实例
$instance = Builder::factory([
'mchid' => $config['mchid'],
'appid' => $config['appid'],
'apiv3Key' => $config['apikey'],
]);
// 1. 创建预支付订单
// $openid 必须是你通过授权获取到的用户的 openid
$openid = '用户的openid'; // 假设你已经从登录流程中获取到了
try {
// 调用 create 方法创建预支付订单
$result = $instance->chain('v3/pay/transactions/jsapi')->post([
'json' => [
'appid' => $config['appid'],
'mchid' => $config['mchid'],
'description' => '商品描述',
'out_trade_no' => date('YmdHis') . rand(1000, 9999), // 你的内部订单号,必须唯一
'notify_url' => $config['notify_url'],
'amount' => [
'total' => 1, // 金额,单位为分(1元 = 100分)
'currency' => 'CNY',
],
'payer' => [
'openid' => $openid,
],
],
]);
// $result 就是微信返回的预支付订单信息
// 你需要将其中的 prepay_id 传递给前端
$prepayId = $result['prepay_id'];
// 2. 生成前端 JSAPI 支付所需参数
$params = [
'appId' => $config['appid'],
'timeStamp' => strval(time()), // 时间戳
'nonceStr' => md5(uniqid()), // 随机字符串
'package' => 'prepay_id=' . $prepayId,
'signType' => 'RSA', // 签名算法,固定为 RSA
];
// 3. 生成签名
// 使用 SDK 提供的 sign 方法
$paySign = $instance->jssdk()->sign($params);
// 4. 返回给前端
$response = [
'appId' => $params['appId'],
'timeStamp' => $params['timeStamp'],
'nonceStr' => $params['nonceStr'],
'package' => $params['package'],
'paySign' => $paySign,
'success' => true,
];
header('Content-Type: application/json');
echo json_encode($response);
} catch (\Exception $e) {
// 处理异常
echo json_encode(['success' => false, 'message' => $e->getMessage()]);
}
步骤 4:前端发起支付
前端(通常是 H5 页面)接收到后端返回的 JSON 数据后,使用微信 JS-SDK 调起支付。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">支付测试</title>
<script src="https://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
</head>
<body>
<h1>点击支付按钮</h1>
<button id="payButton">立即支付 0.01 元</button>
<script>
document.getElementById('payButton').onclick = function() {
// 1. 调用后端接口获取支付参数
fetch('https://www.yourdomain.com/create_order.php', {
method: 'POST',
// 如果需要传递 openid,可以在这里加 body
// body: JSON.stringify({ openid: 'xxx' })
})
.then(response => response.json())
.then(data => {
if (data.success) {
// 2. 使用微信 JS-SDK 调起支付
wx.chooseWXPay({
appId: data.appId,
timestamp: data.timeStamp,
nonceStr: data.nonceStr,
package: data.package,
signType: data.signType, // 注意,后端生成时是 'RSA',但这里 SDK 可能需要 'MD5',请根据文档调整
paySign: data.paySign,
success: function (res) {
// 支付成功
alert('支付成功!');
console.log('支付成功', res);
},
fail: function (res) {
// 支付失败
alert('支付失败:' + (res.errMsg || '未知错误'));
console.log('支付失败', res);
},
complete: function (res) {
// 无论成功失败都会执行
console.log('支付流程结束', res);
}
});
} else {
alert('获取支付参数失败:' + data.message);
}
})
.catch(error => {
console.error('Error:', error);
alert('请求失败,请检查网络');
});
};
</script>
</body>
</html>
注意:wx.chooseWXPay 中的 signType 有时需要根据实际情况调整为 MD5,请参考你所使用的 JS-SDK 版本文档。
步骤 5:处理支付结果通知
微信支付成功后,会主动向你配置的 notify_url 发送一个 POST 请求,告诉你支付结果,你的服务器必须处理这个通知,并验证其真伪。
创建 notify.php 文件:
<?php
require 'vendor/autoload.php';
require 'config.php';
use WeChatPay\Builder;
use WeChatPay\Crypto\Rsa;
$config = require 'config.php';
$instance = Builder::factory([
'mchid' => $config['mchid'],
'appid' => $config['appid'],
'apiv3Key' => $config['apikey'],
]);
// 获取微信发送的数据
$notification = file_get_contents('php://input');
$notificationData = json_decode($notification, true);
// 1. 验证签名
$serialNo = $notificationData['resource']['ciphertext']['serial_no'];
$certs = $instance->chain('v3/certificates')->get(['query' => ['algorithm' => 'RSA']]); // 获取平台证书
$publicKey = null;
foreach ($certs['data'] as $cert) {
if ($cert['serial_no'] === $serialNo) {
$publicKey = Rsa::fromPem($cert['certificate']); // 将证书内容转换为公钥对象
break;
}
}
if (!$publicKey) {
// 证书不存在,验签失败
http_response_code(500);
exit('fail');
}
$verified = Rsa::verify(
$notificationData['resource']['ciphertext'], // 加密后的报文
$notificationData['resource']['sign'], // 签名
$publicKey, // 公钥
'RSA-SHA256' // 签名算法
);
if (!$verified) {
// 签名验证失败
http_response_code(500);
exit('fail');
}
// 2. 解密报文
$decryptedResource = json_decode(Rsa::decrypt(
$notificationData['resource']['ciphertext'],
Rsa::fromPem(file_get_contents($config['key_path']), true), // 使用商户的私钥解密
'RSA-OAEP'
), true);
// 3. 处理业务逻辑
$outTradeNo = $decryptedResource['out_trade_no']; // 你的内部订单号
$transactionId = $decryptedResource['transaction_id']; // 微信支付订单号
$amount = $decryptedResource['amount']['total']; // 支付金额
// 检查订单状态是否为 "SUCCESS"
if ($decryptedResource['trade_state'] === 'SUCCESS') {
// TODO: 在这里更新你的数据库订单状态为“已支付”
// update_orders_status($outTradeNo, 'paid');
echo 'success'; // 告诉微信服务器,我已成功处理通知
} else {
// 支付失败或其他状态
// TODO: 根据业务逻辑处理,例如记录日志
http_response_code(500);
echo 'fail';
}
重要:
- 必须验证签名:这是保证通知是微信发送给你的,而不是伪造的。
- 必须解密报文:微信返回的敏感数据(如订单号)是加密的。
- 必须回复
success:如果处理成功,必须回复success,否则微信会反复通知你。
视频教程推荐
视频教程能更直观地展示操作过程,特别是环境配置和代码讲解。
官方资源
- 微信支付开发者社区:官方社区有大量的技术文档、最佳实践和视频教程,是第一手资料来源。
- 官方社区地址:https://developers.weixin.qq.com/community/pay
- 在社区搜索 “PHP JSAPI 支付” 可以找到相关教程。
Bilibili (B站) 优质教程
B站是学习编程的宝库,有很多UP主分享实战经验。
- 搜索关键词:
php 微信支付 教程、php wechatpay 教程、php 微信支付V3 - 推荐UP主(请自行甄别视频时效性,优先选择较新的):
- 黑马程序员/传智播客:他们的课程体系完整,通常有详细的步骤讲解。
- 尚硅谷:同样以系统化、深入浅出著称。
- 各种个人技术博主:搜索“小旋风”、“代码小金”等关键词,可以找到很多实战项目型视频。
如何选择视频教程?
- 看发布日期:优先选择 1-2年内 发布的视频,因为微信支付接口更新较快,旧教程可能已经过时。
- 大纲:选择包含“环境配置”、“获取OpenID”、“后端生成订单”、“前端调起支付”、“异步通知处理”等完整流程的视频。
- 看评论区:评论区可以反映视频的质量和是否存在常见问题。
常见问题与注意事项
- 签名错误:这是最常见的问题,99% 的签名错误都是因为 APIv3密钥 不对,或者签名时缺少/多某个参数,仔细核对 SDK 的使用方法和官方文档。
openid为空:JSAPI 支付强制要求openid,请确保你的用户已经通过微信授权,并且正确获取到了openid。appId与mchid不匹配:确保你使用的appId和mchid属于同一个商户主体。- 支付金额单位错误:微信支付 API 的金额单位是 “分”,不是“元”,传入
100代表 1 元。 - 支付通知未处理:必须正确实现
notify_url的逻辑,并验证签名,很多支付失败都是因为通知处理不当,导致订单状态不一致。 - HTTPS 环境:微信支付的所有接口(包括你的
notify_url)都必须运行在 HTTPS 协议下。
希望这份详细的教程能帮助你成功集成微信支付!祝你编码顺利!
