这两天做了下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>';