# 支付宝支付和微信支付的服务端签名

这两天做了下APP支付的服务端签名,记下流水账。

# 一.支付流程

简单归纳下主要流程,详细请看微信和支付宝的开发文档

# 二.资料准备

# 支付宝

1.登录https://open.alipay.com/platform/manageHome.htm,创建一个应用,设置好自己的私钥和公钥

2.将该应用上线,签约其中的APP支付功能。为了便于测试,将手机网站支付,电脑网站支付也签约加上

# 微信

1.登录微信开发者中心https://open.weixin.qq.com,创建一个应用

2.将该应用上线,开通微信支付功能

3.登录微信支付https://pay.weixin.qq.com,记录下appid,mchid

# SDK

开发调试时使用的sdk https://github.com/yansongda/pay

# 三.服务端签名

# 支付宝

1. 签名

使用支付宝的官方sdk,RSA2签名

$Client = new \AopClient();//实例化支付宝sdk里面的AopClient类,下单时需要的操作,都在这个类里面
$param['timestamp'] = date("Y-m-d Hi:i:s");//发送请求的时间
$privateKey = config('ali.private_key'); //使用自己的私钥
$paramStr = $Client->getSignContent($param);//组装请求签名参数
$sign = $Client->alonersaSign($paramStr, $privateKey, 'RSA2');//生成签名

2.回调

支付完成后,支付宝会结果发回给回调地址,之后服务端可以对结果做验证,以及一些业务逻辑处理

//获取返回结果
$inputs = $request->all();
if(!$inputs){
    $data = file_get_contents('php://input');
    parse_str($data,$inputs);
}

$client = new \AopClient();
//验证签名
$client->alipayrsaPublicKey = config('ali.ali_public_key');//注意,用阿里云的公钥
$flag = $client->rsaCheckV1($inputs, NULL, "RSA2");
if(!$flag){
    //验证失败
}
if($inputs['trade_status'] != 'TRADE_SUCCESS'){
    //交易失败
}

//做一些自己业务代码处理

//处理完成之后,告诉支付宝成功结果
return 'success';

# 微信

1. 签名

微信目前可以用md5签名,流程如下

第一步 将必要参数以键值对的格式(即key1=value1&key2=value2…)拼接成字符串stringA
第二步 将stringA最后拼接上key得到stringSignTemp字符串,并对stringSignTemp进行MD5运算,再结果转换为大写,得到sign值
第三步 将必要参数和sign值组合,转成xml格式

必要参数中有个nonce_str值,需要一个随机值

protected function getNonceStr($length = 32) {
    $chars = "abcdefghijklmnopqrstuvwxyz0123456789";
    $str ="";
    for ( $i = 0; $i < $length; $i++ )  {
        $str .= substr($chars, mt_rand(0, strlen($chars)-1), 1);
    }
    return $str;
}

组合数组

public function genContent($params)
{
    $stringToBeSigned = "";
    $i = 0;
    foreach ($params as $k => $v) {
        if($k == 'sign'){
            continue;
        }
        if($k == 'sign_type'){
            continue;
        }
        if (false === $this->checkEmpty($v) && "@" != substr($v, 0, 1)) {
            if ($i == 0) {
                $stringToBeSigned .= "$k" . "=" . "$v";
            } else {
                $stringToBeSigned .= "&" . "$k" . "=" . "$v";
            }
            $i++;
        }
    }
    unset ($k, $v);
    return $stringToBeSigned;
}

MD5签名

public function wxMd5($params)
{
    //获取微信支付秘钥
    $key = config('payment.wx.key');;
    //去空
    $data = array_filter($params);
    //签名步骤一:按字典序排序参数
    ksort($data);
    $string_a = $this->genContent($data); //按要求对数组进行组合
    $string_a = urldecode($string_a);
    //签名步骤二:在string后加入KEY
    $string_sign_temp = $string_a."&key=".$key;
    //签名步骤三:MD5加密
    $sign = md5($string_sign_temp);
    // 签名步骤四:所有字符转为大写
    $result = strtoupper($sign);
    return $result;
}

返回xml结果

$xml = "<xml>";
foreach ($params as $key=>$val){
    if(is_array($val)){
        $xml.="<".$key.">".arrayToXml($val)."</".$key.">";
    }else{
        $xml.="<".$key.">".$val."</".$key.">";
    }
}
$xml.="</xml>";

2.回调 支付完成后,微信也会发送支付结果给回调地址,和支付宝一样,只是获取方式不同,字段有些不同,返回确认的字符不一样。

//获取返回结果
$xmlData = file_get_contents('php://input');
libxml_disable_entity_loader(true);
$inputs = json_decode(json_encode(simplexml_load_string($xmlData, 'SimpleXMLElement', LIBXML_NOCDATA)), true);

//验证签名

//校验md5值
$sign = $this->wxMd5($inputs);
if($sign != $inputs['sign']){
    //验证失败,md5值不相等
}

//验证支付状态
if($inputs['result_code'] != 'SUCCESS'){
   //交易失败
}

//做一些自己业务代码处理

//处理完成之后,告诉微信成功结果
return '<xml>
    <return_code><![CDATA[SUCCESS]]></return_code>
    <return_msg><![CDATA[OK]]></return_msg>
</xml>';