手机支付宝 PHP RSA 签名验证失败 签名不匹配 The signature is not match

5 篇文章 0 订阅
终于弄好了。支付宝说明语焉不详啊,我们是做自动售货机软件的,足足花了一天时间啊。。。瞎猜猜对的

1.notify 使用的是 支付宝公钥 而不是商户自己的公钥
 2.notify 手机支付 使用的是 appid 对应的 支付宝公钥即开放平台的app对应的支付宝公钥

         而不是mapi 对应的支付宝公钥



1.2018年 支付宝只支持RSA2  

2 原来的应用如果申请了。rsa 密钥的话 就是已经启用了rsa ,

只能用rsa 。不能同时启用。有折腾一天




/*
* @author 绍兴远帆软件 http://www.ewshop.net 主营网店系统和自动售货机软件
  转载请保留版权

// 文档 https://github.com/lokielse/omnipay-alipay
//      https://github.com/lokielse/omnipay-alipay/wiki/Aop-WAP-Gateway
//      https://doc.open.alipay.com/docs/doc.htm?spm=a219a.7629140.0.0.WNXxqz&treeId=193&articleId=105286&docType=1


* notify 调试方法
* 工具:firefox 插件 poster
*       网址 http://19.com/alipay/webnotify
        参数 tal_amount=0.01&buyer_id=2088002088586290&trade_no=2 一段字符串
       为了方便poster 测试
     notfify程序中加上   parse_str(file_get_contents("php://input"), $_POST);
 */
 
//这里是RSA验证

//这里的字符串是支付宝notfiy 提交上来的 用日志程序把保存到一个文件中
parse_str("total_amount=0.01&buyer_id=2088002088586290&trade_no=2XXXXXXXXXXXXXXXXXXXXXXXXXX",$myArray);
//print_r($myArray);




$para_temp = $myArray;

$sign = $para_temp['sign'];


//除去待签名参数数组中的空值和签名参数
$para_filter = paraFilter($para_temp);

//var_dump($para_filter);
//对待签名参数数组排序
$para_sort = argSort($para_filter);

//把数组所有元素,按照“参数=参数值”的模式用“&”字符拼接成字符串
$prestr = createLinkstring($para_sort);

//把数组所有元素,按照“参数=参数值”的模式用“&”字符拼接成字符串
$prestr = createLinkstring($para_sort);

//var_dump($prestr);

if( rsaVerify($prestr, $sign)){
    echo 'ok';
}else{
    echo 'fail';
}
/* *
 * 支付宝接口公用函数
 * 详细:该类是请求、通知返回两个文件所调用的公用函数核心处理文件
 * 版本:3.3
 * 日期:2012-07-19
 * 说明:
 * 以下代码只是为了方便商户测试而提供的样例代码,商户可以根据自己网站的需要,按照技术文档编写,并非一定要使用该代码。
 * 该代码仅供学习和研究支付宝接口使用,只是提供一个参考。
 */

/**
 * 把数组所有元素,按照“参数=参数值”的模式用“&”字符拼接成字符串
 * @param $para 需要拼接的数组
 * return 拼接完成以后的字符串
 */
function createLinkstring($para) {
    $arg  = "";
    while (list ($key, $val) = each ($para)) {
        $arg.=$key."=".$val."&";
    }
    //去掉最后一个&字符
    $arg = substr($arg,0,count($arg)-2);
    
    //如果存在转义字符,那么去掉转义
    if(get_magic_quotes_gpc()){$arg = stripslashes($arg);}
    
    return $arg;
}
/**
 * 把数组所有元素,按照“参数=参数值”的模式用“&”字符拼接成字符串,并对字符串做urlencode编码
 * @param $para 需要拼接的数组
 * return 拼接完成以后的字符串
 */
function createLinkstringUrlencode($para) {
    $arg  = "";
    while (list ($key, $val) = each ($para)) {
        $arg.=$key."=".urlencode($val)."&";
    }
    //去掉最后一个&字符
    $arg = substr($arg,0,count($arg)-2);
    
    //如果存在转义字符,那么去掉转义
    if(get_magic_quotes_gpc()){$arg = stripslashes($arg);}
    
    return $arg;
}
/**
 * 除去数组中的空值和签名参数
 * @param $para 签名参数组
 * return 去掉空值与签名参数后的新签名参数组
 */
function paraFilter($para) {
    $para_filter = array();
    while (list ($key, $val) = each ($para)) {
        if($key == "sign" || $key == "sign_type" || $val == "")continue;
        else    $para_filter[$key] = $para[$key];
    }
    return $para_filter;
}
/**
 * 对数组排序
 * @param $para 排序前的数组
 * return 排序后的数组
 */
function argSort($para) {
    ksort($para);
    reset($para);
    return $para;
}
/**
 * 写日志,方便测试(看网站需求,也可以改成把记录存入数据库)
 * 注意:服务器需要开通fopen配置
 * @param $word 要写入日志里的文本内容 默认值:空值
 */
function logResult($word='') {
    $fp = fopen("log.txt","a");
    flock($fp, LOCK_EX) ;
    fwrite($fp,"执行日期:".strftime("%Y%m%d%H%M%S",time())."\n".$word."\n");
    flock($fp, LOCK_UN);
    fclose($fp);
}

/**
 * 远程获取数据,POST模式
 * 注意:
 * 1.使用Crul需要修改服务器中php.ini文件的设置,找到php_curl.dll去掉前面的";"就行了
 * 2.文件夹中cacert.pem是SSL证书请保证其路径有效,目前默认路径是:getcwd().'\\cacert.pem'
 * @param $url 指定URL完整路径地址
 * @param $cacert_url 指定当前工作目录绝对路径
 * @param $para 请求的数据
 * @param $input_charset 编码格式。默认值:空值
 * return 远程输出的数据
 */
function getHttpResponsePOST($url, $cacert_url, $para, $input_charset = '') {

    if (trim($input_charset) != '') {
        $url = $url."_input_charset=".$input_charset;
    }
    $curl = curl_init($url);
    curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, true);//SSL证书认证
    curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 2);//严格认证
    curl_setopt($curl, CURLOPT_CAINFO,$cacert_url);//证书地址
    curl_setopt($curl, CURLOPT_HEADER, 0 ); // 过滤HTTP头
    curl_setopt($curl,CURLOPT_RETURNTRANSFER, 1);// 显示输出结果
    curl_setopt($curl,CURLOPT_POST,true); // post传输数据
    curl_setopt($curl,CURLOPT_POSTFIELDS,$para);// post传输数据
    $responseText = curl_exec($curl);
    //var_dump( curl_error($curl) );//如果执行curl过程中出现异常,可打开此开关,以便查看异常内容
    curl_close($curl);
    
    return $responseText;
}

/**
 * 远程获取数据,GET模式
 * 注意:
 * 1.使用Crul需要修改服务器中php.ini文件的设置,找到php_curl.dll去掉前面的";"就行了
 * 2.文件夹中cacert.pem是SSL证书请保证其路径有效,目前默认路径是:getcwd().'\\cacert.pem'
 * @param $url 指定URL完整路径地址
 * @param $cacert_url 指定当前工作目录绝对路径
 * return 远程输出的数据
 */
function getHttpResponseGET($url,$cacert_url) {
    $curl = curl_init($url);
    curl_setopt($curl, CURLOPT_HEADER, 0 ); // 过滤HTTP头
    curl_setopt($curl,CURLOPT_RETURNTRANSFER, 1);// 显示输出结果
    curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, true);//SSL证书认证
    curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 2);//严格认证
    curl_setopt($curl, CURLOPT_CAINFO,$cacert_url);//证书地址
    $responseText = curl_exec($curl);
    //var_dump( curl_error($curl) );//如果执行curl过程中出现异常,可打开此开关,以便查看异常内容
    curl_close($curl);
    
    return $responseText;
}

/**
 * 实现多种字符编码方式
 * @param $input 需要编码的字符串
 * @param $_output_charset 输出的编码格式
 * @param $_input_charset 输入的编码格式
 * return 编码后的字符串
 */
function charsetEncode($input,$_output_charset ,$_input_charset) {
    $output = "";
    if(!isset($_output_charset) )$_output_charset  = $_input_charset;
    if($_input_charset == $_output_charset || $input ==null ) {
        $output = $input;
    } elseif (function_exists("mb_convert_encoding")) {
        $output = mb_convert_encoding($input,$_output_charset,$_input_charset);
    } elseif(function_exists("iconv")) {
        $output = iconv($_input_charset,$_output_charset,$input);
    } else die("sorry, you have no libs support for charset change.");
    return $output;
}
/**
 * 实现多种字符解码方式
 * @param $input 需要解码的字符串
 * @param $_output_charset 输出的解码格式
 * @param $_input_charset 输入的解码格式
 * return 解码后的字符串
 */
function charsetDecode($input,$_input_charset ,$_output_charset) {
    $output = "";
    if(!isset($_input_charset) )$_input_charset  = $_input_charset ;
    if($_input_charset == $_output_charset || $input ==null ) {
        $output = $input;
    } elseif (function_exists("mb_convert_encoding")) {
        $output = mb_convert_encoding($input,$_output_charset,$_input_charset);
    } elseif(function_exists("iconv")) {
        $output = iconv($_input_charset,$_output_charset,$input);
    } else die("sorry, you have no libs support for charset changes.");
    return $output;
}

/**
 * 验证签名
 * @param $prestr 需要签名的字符串
 * @param $sign 签名结果
 * return 签名结果
 */
function rsaVerify($prestr, $sign) {
    $sign = base64_decode($sign);
    $public_key= file_get_contents('rsa_public_key.pem');
    $pkeyid = openssl_get_publickey($public_key);
    if ($pkeyid) {
        $verify = openssl_verify($prestr, $sign, $pkeyid);
        openssl_free_key($pkeyid);
    }else{
        echo '公钥格式错误';
    }
    echo $verify;
    if($verify == 1){
        return true;
    }else{
        return false;
    }
}



第二部分

支付宝RSA流程

 /*
         *
         服务器异步通知(notify_url)是支付宝服务器主动向商户发送的通知,只有当订单的交易状态改变时才会触发,可以防止因网络等原因引起的丢单问题。对同步通知和异步通知结果的处理步骤大致相同,只是处理完成后的返回值不同。

            1.      验证签名

            在通知返回参数列表中,除去sign、sign_type两个参数外,凡是通知返回回来的参数皆是要签名的参数,具体的签名步骤与请求时相同。

            如果新得到的签名与支付宝返回的签名相同,则签名验证成功。

            2.      判断是否是支付宝服务器发来的处理结果

            为了防止某些人伪造支付宝发来的处理消息,还要验证消息的合法性,有以下三个方法:
                2.1验证消息是否是支付宝发出
                    paraArray
                    notify_id
                    sign
                2.2 活动是否是支付宝服务器发来的青球员的验证结果
                    notify_id partner
                    https://mapi.alipay.com/gateway.do?partner=合作者身份ID¬ify_id=通知ID的值

                2.3  获得远程服务器的ATN结果
                     strUrl
                     timeout

            第五步:需要严格按照如下描述校验通知数据的正确性。
            1、商户需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号,
            2、判断total_amount是否确实为该订单的实际金额(即商户订单创建时的金额),
            3、校验通知中的seller_id(或者seller_email) 是否为out_trade_no这笔单据的对应的操作方(
            有的时候,一个商户可能有多个seller_id/seller_email),
            4、验证app_id是否为该商户本身。上述1、2、3、4有任何一个验证不通过,则表明本次通知是异常通知,
            务必忽略。在上述验证通过后商户必须根据支付宝不同类型的业务通知,正确的进行不同的业务处理,
            并且过滤重复的通知结果数据。在支付宝的业务通知中,只有交易通知状态为TRADE_SUCCESS或TRADE_FINISHED时,
            支付宝才会认定为买家付款成功。


        *
         * */


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值