微信小程序支付是小程序商业化场景的核心功能,php作为后端开发的主流语言,接入小程序支付需要遵循微信支付官方规范,完成参数配置、接口调用、结果验证等一系列操作。下面详细介绍完整的集成方案。

前期准备工作
在正式开发前,需要先完成以下基础配置,确保所有必要参数齐全:
- 注册微信小程序并获取小程序的
appid - 开通微信支付商户号,完成商户号与小程序的绑定
- 获取商户号的
mch_id(商户号)和key(API密钥,在商户平台自行设置) - 配置支付回调域名,确保回调接口可以被微信支付服务器访问
核心流程梳理
php集成微信小程序支付的整体流程分为四个核心环节:
- 小程序端调用后端接口发起支付请求
- php后端生成支付参数,调用微信支付统一下单接口
- 后端将支付参数返回给小程序端,小程序调起微信支付组件
- 用户支付完成后,微信服务器向回调接口推送支付结果,后端验证结果并更新业务数据
统一下单接口调用实现
统一下单接口是获取支付参数的核心接口,需要按照微信支付要求拼接参数、生成签名、发送请求。以下是php实现的核心代码:
<?php
class WechatPay {
// 配置参数
private $appid = '你的小程序appid';
private $mch_id = '你的商户号';
private $key = '你的API密钥';
private $notify_url = 'https://ipipp.com/pay/notify'; // 回调地址,替换成自己的域名
/**
* 生成小程序支付参数
* @param string $openid 用户的openid
* @param string $order_sn 商户订单号
* @param int $total_fee 支付金额,单位分
* @param string $body 商品描述
* @return array
*/
public function getPayParams($openid, $order_sn, $total_fee, $body) {
// 1. 拼接统一下单参数
$params = [
'appid' => $this->appid,
'mch_id' => $this->mch_id,
'nonce_str' => $this->generateNonceStr(),
'body' => $body,
'out_trade_no' => $order_sn,
'total_fee' => $total_fee,
'spbill_create_ip' => $_SERVER['REMOTE_ADDR'],
'notify_url' => $this->notify_url,
'trade_type' => 'JSAPI',
'openid' => $openid
];
// 2. 生成签名
$params['sign'] = $this->generateSign($params);
// 3. 转换为XML格式
$xml = $this->arrayToXml($params);
// 4. 调用统一下单接口
$url = 'https://api.mch.weixin.qq.com/pay/unifiedorder';
$result_xml = $this->httpRequest($url, $xml);
$result = $this->xmlToArray($result_xml);
// 5. 生成小程序端需要的支付参数
if ($result['return_code'] == 'SUCCESS' && $result['result_code'] == 'SUCCESS') {
return $this->getJsApiParams($result['prepay_id']);
}
return [];
}
/**
* 生成随机字符串
* @param int $length 字符串长度
* @return string
*/
private function generateNonceStr($length = 32) {
$chars = 'abcdefghijklmnopqrstuvwxyz0123456789';
$str = '';
for ($i = 0; $i < $length; $i++) {
$str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
}
return $str;
}
/**
* 生成签名
* @param array $params 参数数组
* @return string
*/
private function generateSign($params) {
// 1. 参数按照ASCII码从小到大排序
ksort($params);
$string = '';
foreach ($params as $key => $value) {
if ($key != 'sign' && $value != '' && !is_array($value)) {
$string .= $key . '=' . $value . '&';
}
}
// 2. 拼接API密钥
$string = $string . 'key=' . $this->key;
// 3. MD5加密并转大写
return strtoupper(md5($string));
}
/**
* 数组转XML
* @param array $arr 数组
* @return string
*/
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;
}
/**
* XML转数组
* @param string $xml XML字符串
* @return array
*/
private function xmlToArray($xml) {
libxml_disable_entity_loader(true);
$values = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
return $values;
}
/**
* HTTP请求
* @param string $url 请求地址
* @param string $data 请求数据
* @return string
*/
private function httpRequest($url, $data) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
$output = curl_exec($ch);
curl_close($ch);
return $output;
}
/**
* 生成小程序端调起支付需要的参数
* @param string $prepay_id 预支付会话标识
* @return array
*/
private function getJsApiParams($prepay_id) {
$params = [
'appId' => $this->appid,
'timeStamp' => (string)time(),
'nonceStr' => $this->generateNonceStr(),
'package' => 'prepay_id=' . $prepay_id,
'signType' => 'MD5'
];
$params['paySign'] = $this->generateSign($params);
return $params;
}
}
?>
小程序端调起支付
后端返回支付参数后,小程序端需要调用wx.requestPayment方法调起支付组件,示例代码如下:
// 调用后端接口获取支付参数
wx.request({
url: 'https://ipipp.com/api/pay/getParams', // 替换为自己的后端接口地址
method: 'POST',
data: {
order_sn: '20240501001', // 商户订单号
total_fee: 1, // 支付金额,单位分
body: '测试商品'
},
success: (res) => {
if (res.data.code == 0) {
const payParams = res.data.data;
// 调起微信支付
wx.requestPayment({
...payParams,
success: (payRes) => {
console.log('支付成功', payRes);
// 跳转订单结果页
},
fail: (err) => {
console.log('支付失败', err);
}
});
}
}
});
支付回调处理
用户支付完成后,微信服务器会向配置的回调地址发送支付结果通知,后端需要验证签名并处理逻辑,核心代码如下:
<?php
// 支付回调处理
class PayNotify {
private $key = '你的API密钥';
public function handle() {
// 读取回调数据
$xml = file_get_contents('php://input');
$data = $this->xmlToArray($xml);
// 验证签名
$sign = $data['sign'];
unset($data['sign']);
$check_sign = $this->generateSign($data);
if ($check_sign == $sign && $data['return_code'] == 'SUCCESS' && $data['result_code'] == 'SUCCESS') {
// 支付验证通过,处理业务逻辑,比如更新订单状态
$order_sn = $data['out_trade_no'];
$transaction_id = $data['transaction_id'];
// 这里添加订单更新逻辑,注意要做幂等处理,避免重复回调
// 处理完成后返回成功响应给微信服务器
$response = [
'return_code' => 'SUCCESS',
'return_msg' => 'OK'
];
echo $this->arrayToXml($response);
exit;
}
// 验证失败返回错误
$response = [
'return_code' => 'FAIL',
'return_msg' => '签名验证失败'
];
echo $this->arrayToXml($response);
exit;
}
private function generateSign($params) {
ksort($params);
$string = '';
foreach ($params as $key => $value) {
if ($value != '' && !is_array($value)) {
$string .= $key . '=' . $value . '&';
}
}
$string .= 'key=' . $this->key;
return strtoupper(md5($string));
}
private function xmlToArray($xml) {
libxml_disable_entity_loader(true);
return json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
}
private function arrayToXml($arr) {
$xml = '<xml>';
foreach ($arr as $key => $val) {
$xml .= '<' . $key . '>' . $val . '</' . $key . '>';
}
$xml .= '</xml>';
return $xml;
}
}
// 实例化并调用
$notify = new PayNotify();
$notify->handle();
?>
常见问题处理
- 签名错误:检查参数排序是否正确、是否遗漏必传参数、API密钥是否正确、参数值是否有空格或特殊字符
- 回调收不到:检查回调地址是否为https、是否在商户平台配置了支付授权目录、服务器是否有防火墙拦截微信服务器请求
- openid获取失败:小程序端需要先调用
wx.login获取code,再请求后端接口用code换openid,确保openid正确
注意事项
所有涉及金额的参数单位都是分,不要传元为单位的值;回调处理必须做幂等校验,避免同一笔订单重复处理;测试时可以使用微信支付沙箱环境,避免产生实际资金交易。如果是本地开发,回调地址需要配置内网穿透工具才能接收微信的回调通知。