paySign,Native 生成二维码WeixinJSBridge.invoke 或扫码| 参数 | 说明 | 示例 |
|---|---|---|
| body | 商品/订单描述 | iPhone 15 Pro 256GB |
| out_trade_no | 商户订单号 (唯一) | 20240115001 |
| total_fee | 金额 (分,整数) | 899900 (即 8999.00 元) |
注意:金额单位为分,避免浮点精度问题。
| 参数 | 来源/说明 |
|---|---|
| appid | 微信公众号/开放平台 AppID |
| mch_id | 商户号 |
| device_info | 固定 WEB |
| trade_type | JSAPI (公众号) / NATIVE (扫码) / APP (App) |
| nonce_str | 32 位随机字符串 |
| spbill_create_ip | 服务器公网 IP |
| notify_url | 支付回调地址 (需外网可达) |
| openid | JSAPI 必填,用户标识 |
| sign | 签名 (下文详述) |
function makeSign(array $params, string $key): string {
// 1. 剔除 sign 字段
unset($params['sign']);
// 2. 字典序排序 (A-Z)
ksort($params);
// 3. 拼接 k1=v1&k2=v2...
$string = urldecode(http_build_query($params));
// 4. 追加 key
$string .= "&key={$key}";
// 5. MD5 + 大写
return strtoupper(md5($string));
}
关键点:
sign 不参与签名http_build_query 会编码,故用 urldecode 还原function unifiedOrder(array $params): array {
$xml = arrayToXml($params); // 数组转 XML (CDATA 包裹)
$url = 'https://api.mch.weixin.qq.com/pay/unifiedorder';
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $xml,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Content-Type: application/xml'],
CURLOPT_SSL_VERIFYPEER => true, // 生产环境必须验证证书
CURLOPT_CAINFO => '/path/to/cacert.pem', // CA 证书路径
]);
$response = curl_exec($ch);
curl_close($ch);
return xmlToArray($response); // XML 转数组
}
paySign)function getJsApiParams(string $prepayId, string $appId, string $key): array {
$params = [
'appId' => $appId,
'timeStamp' => (string)time(),
'nonceStr' => generateNonceStr(),
'package' => 'prepay_id=' . $prepayId,
'signType' => 'MD5',
];
$params['paySign'] = makeSign($params, $key); // 复用签名函数
return $params;
}
前端调用:
function callpay() {
if (typeof WeixinJSBridge == 'undefined') {
document.addEventListener('WeixinJSBridgeReady', jsApiCall, false);
} else {
jsApiCall();
}
}
function jsApiCall() {
WeixinJSBridge.invoke('getBrandWCPayRequest', , function(res) {
if (res.err_msg == 'get_brand_wcpay_request:ok') {
alert('支付成功');
// location.href = '/order/success';
} else {
alert('支付失败: ' + res.err_msg);
}
});
}
直接将 code_url 生成二维码 (如 phpqrcode 库),用户扫码完成支付。
// 1. 获取原始 XML
$xml = file_get_contents('php://input');
$params = xmlToArray($xml);
// 2. 验签
$sign = $params['sign'];
unset($params['sign']);
$calcSign = makeSign($params, $apiKey);
if ($sign !== $calcSign) {
exit(xmlResponse('FAIL', '签名验证失败'));
}
// 3. 业务判断
if ($params['return_code'] === 'SUCCESS' && $params['result_code'] === 'SUCCESS') {
$outTradeNo = $params['out_trade_no'];
// 幂等性处理:检查订单状态、金额核对
// updateOrderStatus($outTradeNo, 'paid');
exit(xmlResponse('SUCCESS', 'OK'));
} else {
// 记录失败日志
exit(xmlResponse('SUCCESS', 'OK')); // 仍返回 SUCCESS 避免微信重复推送
}
function xmlResponse(string $code, string $msg): string {
return " ";
}
CURLOPT_SSL_VERIFYPEER => true + CA 证书,防中间人攻击total_fee 与订单金额比对total_fee 单位分,易漏乘 100trade_type 与场景不匹配 (JSAPI 需 openid,Native 不需)wechatpay-php 库