首页 » 收集 » 正文内容
微信H5支付《支付对接流程》
寻梦xunm| 791| 收集
3年前
超过1198天 温馨提示
本文最后更新于2021年06月10日,已超过1198天没有更新,若内容或图片失效,请留言反馈。

1 组装请求参数
文档地址:pay.weixin.qq.com/wiki/doc/api/H5....
由支付文档可知必填的参数有哪些,以下是组装好的请求参数:

/**
 * 组装统一下单请求数据
 */
public function getOrderData()
{
  $postData = [
            'appid'            => '这里是填申请到的appid',               // 应用APPID
            'mch_id'           => '这里是填收款的商户号',                 // 商户号
            'nonce_str'        => (string)time(),                       // 随机字符串
            'body'             => "商品描述",                            // 商品描述
            'out_trade_no'     => time(),                               // 订单号
            'total_fee'        => 100,                                  // 订单总金额,单位为分
            'spbill_create_ip' => $this->getClientRealIp(),             // 终端IP
            'notify_url'       => 'https://your.domain.name/notify',    // 支付回调通知地址
            'trade_type'       => 'MWEB',                               // 交易类型
        ];

  $postData['sign'] = $this->generateWxPaySign($postData, '这里是填商户的密钥'); // 使用商户密钥签名
  return $this->arrayToXml($postData);
}

getClientRealIp:获取客户端 IP,具体代码见 1.1。
generateWxPaySign():对请求参数使用商户密钥进行签名,具体代码见 1.2。
arrayToXml():将 array 转换为 xml 格式,具体代码见 1.3。
1.1 获取客户端 IP

/**
 * 获取客户端IP
 */
public function getClientRealIp()
{
    if (isset($_SERVER['HTTP_CDN_SRC_IP']) && $_SERVER['HTTP_CDN_SRC_IP'] != 'unknown') {
        $realip = $_SERVER["HTTP_CDN_SRC_IP"];
    } elseif (getenv('HTTP_X_FORWARDED_FOR')) {
        $realip = getenv('HTTP_X_FORWARDED_FOR');
    } elseif (getenv('HTTP_CLIENT_IP')) {
        $realip = getenv('HTTP_CLIENT_IP');
    } else {
        $realip = getenv('REMOTE_ADDR');
    }

    $realip = explode(',', $realip);
    if ($realip[0] === '::1') return '127.0.0.1';
    return $realip[0];
}

1.2 对请求参数签名

/**
 * 使用商户平台密钥对数据进行签名
 *
 * @param $data       array  需要计算签名的数据
 * @param $privateKey string 商户平台密钥
 * @return string
 */
public function generateWxPaySign($data, $privateKey)
{
    ksort($data);
    $signStr = http_build_query($data).'&key='.$privateKey;
    return strtoupper(md5(urldecode($signStr)));
}

1.3 array 转为 xml 格式

/**
 * 数组转为XML格式
 *
 * @param array $postData 需要转换的数组
 * @return string
 */
public function arrayToXml($postData)
{
    $xml = "<xml>";
    $xml .= $this->toXml($postData);
    $xml .= "</xml>";
    return $xml;
}


/**
 * 数组拼接为XML字符
 *
 * @param mixed  $data 数据
 * @param string $item 数字索引时的节点名称
 * @param string $id   数字索引key转换为的属性名
 * @return string
 */
public function toXml($data, $item = 'item', $id = 'id')
{
    $xml = $attr = '';
    foreach ($data as $key => $val) {
        if (is_numeric($key)) {
            $id && $attr = " {$id}=\"{$key}\"";
            $key = $item;
        }
        $xml .= "<{$key}{$attr}>";
        $xml .= (is_array($val) || is_object($val)) ? $this->toXml($val, $item, $id) : $val;
        $xml .= "</{$key}>";
    }
    return $xml;
}

2 请求统一下单

/**
 * 请求统一下单
  *
  * @param $xmlPostData array 请求参数
  * @return bool|string
 */
public function curlUnifiedOrder($xmlPostData)
{
  $curl = curl_init('https://api.mch.weixin.qq.com/pay/unifiedorder');
  curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
  curl_setopt($curl, CURLOPT_HEADER, 0);
  curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
  curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
  curl_setopt($curl, CURLOPT_POST, 1);
  curl_setopt($curl, CURLOPT_POSTFIELDS, $xmlPostData);

  $output = curl_exec($curl);
  return $output;
}

如果请求参数没错的话,会返回如下结果:

<xml>
<return_code><![CDATA[SUCCESS]]></return_code>
<return_msg><![CDATA[OK]]></return_msg>
<result_code><![CDATA[SUCCESS]]></result_code>
<mch_id><![CDATA[这里是商户号]]></mch_id>
<appid><![CDATA[这里是appid]]></appid>
<nonce_str><![CDATA[gIm0m19zaHpp6ZcV]]></nonce_str>
<sign><![CDATA[B9CF4566E51866E75A14127B52C75AF0]]></sign>
<prepay_id><![CDATA[wx041706090025501baee1b1de80ee0d1111]]></prepay_id>
<trade_type><![CDATA[MWEB]]></trade_type>
<mweb_url><![CDATA[https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb?prepay_id=wx041706090025501baee1b1de80ee0d1111&package=91434759]]></mweb_url>
</xml>

3 获取支付跳转链接
从步骤 2 返回的结果中获取 mweb_url。

/**
 * 获取mweb_url
 *
 * @param $output string 请求结果,xml格式
 * @return false|mixed
 */
public function getPrepayId($output)
{
    $output     = $this->xmlToArray($output);
    $conditions = is_array($output)
                  && isset($output['return_code'])
                  && $output['return_code'] == 'SUCCESS'
                  && isset($output['result_code'])
                  && $output['result_code'] == 'SUCCESS';

    return $conditions ? $output['mweb_url'] : false;
}

xmlToArray():xml 格式转换为 array,具体代码见 3.1。
3.1 xml 转为 array 格式

/**
 * XML转为Array格式
 *
 * @param $data string Xml格式的数据
 * @return mixed
 */
public function xmlToArray($data)
{
    $loadXml = simplexml_load_string($data, 'SimpleXMLElement', LIBXML_NOCDATA);
    return json_decode(json_encode($loadXml), true)
}

4 处理支付跳转链接
mweb_url 链接如果直接重定向,例如 location.href=mweb_url,则会出现如下所示错误:

所以需要 curl 请求 mweb_url,再获取真正能拉起微信支付的链接:

/**
 * 处理mweb_url链接
 *
 * @param $mweb_url string 支付跳转链接
 * @return array|false|string|string[]
 */
 public function getPayRedirectUrl($mweb_url)
{
    // 获取客户端IP,设置头部
    $clientRealIp = $this->getClientRealIp();
    $hearder = ['CLIENT-IP' => $clientRealIp, 'X-FORWARDED-FOR' => $clientRealIp];

    $ch = curl_init($mweb_url);
    curl_setopt($ch, CURLOPT_HTTPHEADER, $hearder);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
    curl_setopt($ch, CURLOPT_REFERER, "https://your.domain.name");
    $output = curl_exec($ch);
    curl_close($ch);

    // 获取 $output 中的链接
    preg_match('([a-zA-z]+://[^\s]*)', $output, $match);
    if (!$match) return false;

    $redirectUrl = str_replace('"', '', $match[0]);
    return $redirectUrl;
}

这里对 $ouput 使用正则表达式,是为了获取返回结果中的拉起微信客户端的链接,其格式为:
var url="weixin://wap/pay?prepayid%3Dwx26172159107177eb3a7e2ffd8a16130000&package=640887930&noncestr=1622020919&sign=6bf0fb62f1788fd9f106b5321a6d28cd";
最后前端根据这条链接去拉起微信支付。

getClientRealIp:获取客户端 IP,具体代码见 1.1。
5 支付回调
文档地址:pay.weixin.qq.com/wiki/doc/api/H5....
用户支付完成后,微信会通知商户,商户需要在接受数据、处理数据后,返回应答。
回调地址是在步骤 1 中的 notify_url 字段填写的。

public function notify()
{
    $notifyXml   = file_get_contents('php://input');
    $notifyArray = $this->xmlToArray($notifyXml);

    // 检查回调参数是否正确
    $checkParams = isset($notifyArray['sign'])
                   && isset($notifyArray['result_code']) 
                   && $notifyArray['result_code'] === 'SUCCESS'
                   && isset($notifyArray['return_code']) 
                   && $notifyArray['return_code'] === 'SUCCESS';
    if (!$checkParams) return false;

    // 验签
    $sign = $this->generateWxPaySign($notifyArray, '这里是填商户的密钥');
    if ($sign != $notifyArray['sign']) return false;

    // TODO:按照业务更新订单支付状态等等
}

xmlToArray():xml 格式转换为 array,具体代码见 3.1。
generateWxPaySign():对请求参数使用商户密钥进行签名,具体代码见 1.2。
如果本文章有帮到你的话,别忘了点赞噢

文章来源于:https://learnku.com/docs/payment/h5-payment/11088

0 赞 or 打赏
喜欢就打赏一点
微信 支付宝
隐私
Q Q:1340326824
邮箱:vipshiyi@qq.com
QQ群:422720328

我的音乐

微博客-专为自己编写开发的源码