首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 綜合知識

PHP 微信支付开发全流程详解:从下单到回调的完整指南

     編輯:PHP 綜合知識

微信支付核心流程概览

  1. 获取订单信息:商品描述、订单号、金额(分)
  2. 生成签名 & 支付参数:按规范排序、拼接、MD5、大写
  3. 统一下单 API 请求:POST XML 至微信服务器
  4. 生成支付凭证:JSAPI 支付生成 paySign,Native 生成二维码
  5. 前端调起支付:WeixinJSBridge.invoke 或扫码
  6. 异步回调处理:验签、更新订单状态、应答微信

步骤 1:必填订单参数

参数说明示例
body商品/订单描述iPhone 15 Pro 256GB
out_trade_no商户订单号 (唯一)20240115001
total_fee金额 (分,整数)899900 (即 8999.00 元)

注意:金额单位为分,避免浮点精度问题。

步骤 2:完整支付参数构建

参数来源/说明
appid微信公众号/开放平台 AppID
mch_id商户号
device_info固定 WEB
trade_typeJSAPI (公众号) / NATIVE (扫码) / APP (App)
nonce_str32 位随机字符串
spbill_create_ip服务器公网 IP
notify_url支付回调地址 (需外网可达)
openidJSAPI 必填,用户标识
sign签名 (下文详述)

步骤 3:签名算法 (核心难点)

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 不参与签名
  • 参数值不 URL 编码,http_build_query 会编码,故用 urldecode 还原
  • 微信商户平台设置的 API 密钥 (key) 必须一致

步骤 4:统一下单请求

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 转数组
}

步骤 5:响应处理与支付凭证生成

成功响应关键字段








JSAPI 支付二次签名 (生成 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);
        }
    });
}

Native 扫码支付

直接将 code_url 生成二维码 (如 phpqrcode 库),用户扫码完成支付。

步骤 6:异步回调处理 (notify_url)

// 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 "";
}

安全与工程化建议

  1. 证书验证:CURLOPT_SSL_VERIFYPEER => true + CA 证书,防中间人攻击
  2. 幂等性:回调可能重复推送,订单状态机防重复处理
  3. 金额核对:回调中 total_fee 与订单金额比对
  4. 日志审计:全链路请求/响应/签名/错误入库,便于对账排查
  5. 配置隔离:AppID、MchID、Key、证书路径环境变量化
  6. 异常重试:网络超时指数退避重试,记录失败入队列人工干预

常见坑位速查

  • total_fee 单位分,易漏乘 100
  • trade_type 与场景不匹配 (JSAPI 需 openid,Native 不需)
  • 签名参数大小写、排序、编码不一致
  • 回调地址未外网可达、端口未开放、HTTPS 证书不受信
  • 微信支付 SDK 版本过旧,建议用官方 wechatpay-php 库
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved