本文目录导读:

在PHP项目中处理小程序支付,后端主要承担统一下单、签名生成、回调处理三大核心职责,下面是一个标准流程的详细实现说明,包含关键代码示例。
支付流程总览
小程序前端 PHP后端 微信支付服务器
| | |
|-- 1.请求下单 ----->| |
| |-- 2.统一下单 --------->|
| |<-- 3.返回prepay_id ---|
|<-- 4.返回支付参数--| |
|-- 5.调起支付 ----->| |
| | |
|-- 6.支付结果通知 ->| |
| |-- 7.回调验证签名 ----->|
| |<-- 8.查询订单状态 ----|
| | |
核心处理步骤
步骤1:统一下单(后端接收前端请求)
小程序前端将 openid、商品信息等发给后端,后端向微信支付服务器发起统一下单请求。
// 统一下单接口
public function unifiedOrder($openid, $total_fee, $out_trade_no, $body)
{
$appid = 'your_appid';
$mch_id = 'your_mch_id';
$key = 'your_api_key';
$params = [
'appid' => $appid,
'mch_id' => $mch_id,
'nonce_str' => md5(uniqid(mt_rand(), true)),
'body' => $body,
'out_trade_no' => $out_trade_no,
'total_fee' => intval($total_fee * 100), // 单位:分
'spbill_create_ip' => $_SERVER['REMOTE_ADDR'],
'notify_url' => 'https://yourdomain.com/api/wxpay/notify',
'trade_type' => 'JSAPI',
'openid' => $openid,
];
// 生成签名
$params['sign'] = $this->generateSign($params, $key);
// 发送请求到微信支付
$xml = $this->arrayToXml($params);
$response = $this->postXmlCurl('https://api.mch.weixin.qq.com/pay/unifiedorder', $xml);
$result = $this->xmlToArray($response);
if ($result['return_code'] == 'SUCCESS' && $result['result_code'] == 'SUCCESS') {
// 获取prepay_id
$prepay_id = $result['prepay_id'];
// 生成小程序所需支付参数
$payParams = $this->getPayParams($appid, $prepay_id, $key);
return [
'code' => 0,
'msg' => 'success',
'data' => $payParams
];
} else {
// 错误处理
return [
'code' => -1,
'msg' => $result['err_code_des'] ?? '下单失败'
];
}
}
步骤2:生成小程序调起支付参数
private function getPayParams($appid, $prepay_id, $key)
{
$params = [
'appId' => $appid,
'timeStamp' => (string)time(),
'nonceStr' => md5(uniqid(mt_rand(), true)),
'package' => 'prepay_id=' . $prepay_id,
'signType' => 'MD5',
];
// 计算paySign
$params['paySign'] = $this->generateSign($params, $key);
return $params;
}
步骤3:签名生成方法
private function generateSign($params, $key)
{
// 按键名ASCII排序
ksort($params);
$string = '';
foreach ($params as $k => $v) {
if ($k != 'sign' && $v != '' && !is_array($v)) {
$string .= $k . '=' . $v . '&';
}
}
$string .= 'key=' . $key;
return strtoupper(md5($string));
}
步骤4:支付结果回调处理
微信支付成功后会异步通知 notify_url,后端需验证签名并更新订单状态。
// 回调处理
public function notify()
{
// 获取微信通知的XML数据
$xml = file_get_contents('php://input');
$data = $this->xmlToArray($xml);
// 验证签名
$sign = $data['sign'];
unset($data['sign']);
$mySign = $this->generateSign($data, $this->key);
if ($sign !== $mySign) {
// 签名验证失败
return $this->responseXml('FAIL', '签名失败');
}
// 验证返回码
if ($data['return_code'] !== 'SUCCESS' || $data['result_code'] !== 'SUCCESS') {
return $this->responseXml('FAIL', '支付失败');
}
// 验证订单
$out_trade_no = $data['out_trade_no'];
$transaction_id = $data['transaction_id'];
$total_fee = $data['total_fee']; // 单位:分
// 查询本地订单并验证金额
$order = $this->getOrder($out_trade_no);
if (!$order) {
return $this->responseXml('FAIL', '订单不存在');
}
if ($order['total_fee'] != $total_fee) {
return $this->responseXml('FAIL', '金额不匹配');
}
if ($order['status'] != 'unpaid') {
// 防止重复处理
return $this->responseXml('SUCCESS', 'OK');
}
// 更新订单状态
$this->updateOrderStatus($out_trade_no, 'paid', $transaction_id);
// 执行后续业务逻辑(比如加积分、发货等)
// 返回成功给微信
return $this->responseXml('SUCCESS', 'OK');
}
// 返回给微信的XML格式
private function responseXml($return_code, $return_msg)
{
return "<xml>
<return_code><![CDATA[{$return_code}]]></return_code>
<return_msg><![CDATA[{$return_msg}]]></return_msg>
</xml>";
}
步骤5:订单查询(可选 - 用于兜底)
public function queryOrder($out_trade_no)
{
$params = [
'appid' => $this->appid,
'mch_id' => $this->mch_id,
'out_trade_no' => $out_trade_no,
'nonce_str' => md5(uniqid(mt_rand(), true)),
];
$params['sign'] = $this->generateSign($params, $this->key);
$xml = $this->arrayToXml($params);
$response = $this->postXmlCurl('https://api.mch.weixin.qq.com/pay/orderquery', $xml);
$result = $this->xmlToArray($response);
if ($result['trade_state'] === 'SUCCESS') {
// 支付成功
}
return $result;
}
辅助工具方法
// XML转数组
private function xmlToArray($xml)
{
libxml_disable_entity_loader(true);
return json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
}
// 数组转XML
private function arrayToXml($arr)
{
$xml = "<xml>";
foreach ($arr as $key => $val) {
if (is_numeric($val)) {
$xml .= "<{$key}>{$val}</{$key}>";
} else {
$xml .= "<{$key}><![CDATA[{$val}]]></{$key}>";
}
}
$xml .= "</xml>";
return $xml;
}
// POST XML数据
private function postXmlCurl($url, $xml)
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_HEADER, false);
$response = curl_exec($ch);
curl_close($ch);
return $response;
}
安全与注意事项
1 签名验证
- 所有与微信交互的数据都必须进行签名验证(包括请求和响应)
- 签名算法必须正确,密钥需严格保密
2 回调幂等性
- 微信可能重复发送回调通知
- 必须通过订单状态(如
status = 'unpaid')确保只处理一次
3 金额校验
- 回调中必须验证
total_fee与数据库订单金额一致 - 防止中间人篡改或伪造通知
4 密钥安全
- API密钥、商户证书等敏感信息应存储在环境变量或配置文件中
- 切勿硬编码在代码中或上传到公开仓库
5 日志记录
- 记录完整的请求/响应日志(XML格式),用于排查问题
- 记录关键时间点:下单请求、回调接收、订单处理完成
完整调用示例(简化版)
// 小程序端 - 用户点击支付
// 1. 后端接收前端请求,获得 openid 和订单信息
$openid = $_POST['openid'];
$order = [
'out_trade_no' => '20231001' . rand(1000, 9999),
'total_fee' => 0.01, // 测试金额
'body' => '测试商品',
];
// 2. 统一下单
$result = $this->unifiedOrder($openid, $order['total_fee'], $order['out_trade_no'], $order['body']);
if ($result['code'] == 0) {
// 3. 返回支付参数给小程序端
echo json_encode($result['data']);
} else {
echo json_encode(['error' => $result['msg']]);
}
// 小程序端收到后,调用 wx.requestPayment 发起支付
PHP后端处理小程序支付的核心流程:
- 统一下单:向微信服务器获取
prepay_id - 生成支付参数:
appId, timeStamp, nonceStr, package, signType, paySign - 异步回调处理:验证签名、金额、防止重复处理、更新订单状态
注意:一定要严格验证签名和金额,处理好回调的幂等性,以上代码为便于理解做了简化,实际生产环境建议使用微信官方 SDK(如 wechatpay-php)或成熟的第三方库。
版权声明: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直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播