<?php
/**
* Desc 注意生成的私钥和公钥是2048位,PKCS1(PHP使用,如果是java,使用PKCS8),编码GBK,然后用支付宝的秘钥生成公钥来生成。这个网关是为了用来支付宝做notify_url的,所以支付宝必须确保验证签名正确,你的接口安全,才能让你使用开发者模式。
* Author: xiexingqiao
* Date: 2017/5/22
* Time: 10:34
*/
class AliNotifyController
{
private $config = array (
//应用ID,您的APPID。
'app_id' => "****",
//商户私钥,您的原始格式私钥,一行字符串
'merchant_private_key' => '-----BEGIN PRIVATE KEY-----
****
-----END PRIVATE KEY-----
',
//商户应用公钥,一行字符串
'merchant_public_key' => "***",
//支付宝公钥,查看地址:https://openhome.alipay.com/platform/keyManage.htm 对应APPID下的支付宝公钥。
'alipay_public_key' => '****',//没有-----BEGIN PUBLIC KEY-----和-----END PUBLIC KEY-----
//编码格式只支持GBK。
'charset' => "GBK",
//支付宝网关
'gatewayUrl' => "https://openapi.alipay.com/gateway.do",
//签名方式
'sign_type'=>"RSA2"
);
/**
* ali服务窗应用网关验证
*/
public function actionGateway()
{
// $_POST['service']='alipay.service.check';
// $_POST['sign']='ntjOmXFGJMdfdMnrTL5rEp9QG8d0lDEoGg3ZHvqemHeI8BlQoEsFbhEn0IfQT+pvfJz5RCuE+3Qh1X7I4z5iTIiGjDBstc0xeuiAmtP9TrJZuw2jUAODFB9qOwBJLNcWlKHUGTU/db/qRsJQCj8EjoJvSi9MRM/xKv/XmduS/C4=';
// $_POST['sign_type']='RSA2';
// $_POST['charset']='GBK';
/* $_POST['biz_content']='<?xml version="1.0" encoding="gbk"?><XML><AppId><![CDATA['.$this->config['app_id'].']]></AppId><FromUserId></FromUserId><CreateTime><![CDATA[1406083506817]]></CreateTime><MsgType><![CDATA[event]]></MsgType><EventType><![CDATA[verifygw]]></EventType><ActionParam></ActionParam><AgreementId></AgreementId><AccountNo></AccountNo></XML>';
*/
$sign = Yii::$app->request->post( "sign",$_POST['sign'] );
$sign_type = Yii::$app->request->post ( "sign_type" ,$_POST['sign_type']);
$biz_content = Yii::$app->request->post ( "biz_content",$_POST['biz_content'] );
$service = Yii::$app->request->post( "service" ,$_POST['service']);
$charset = Yii::$app->request->post( "charset" ,$_POST['charset']);
if (empty ( $sign ) || empty ( $sign_type ) || empty ( $biz_content ) || empty ( $service ) || empty ( $charset )) {
Yii::info("some parameter is empty.");
exit ();
}
$sign_verify = $this->verify($_POST);//,$this->config['alipay_public_key']
if($service == 'alipay.service.check')
{
// 验证签名,开通开发者模式
if(!$sign_verify)
{
Yii::info('sign qianming verfiy fail.');
$this->verifygw(false);
}else{
Yii::info('sign qianming verfiy success.');
$this->verifygw(true);
}
return true;
}elseif($service == 'alipay.mobile.public.message.notify'){
// 处理收到的消息
}
Yii::info('exception happen');
}
/* 使用支付宝的公钥对支付宝来的消息进行验签 */
private function verify($params) {
Yii::info(json_encode($params));
$sign = $params['sign'];
unset($params['sign']);
ksort($params);
$data = http_build_query($params);
$AliRsaPublicKeyFilePath=Yii::getAlias('@app').'/config/alipay_public_key_sha256.pem';
if(file_exists($AliRsaPublicKeyFilePath))
{
/* 读取公钥文件,PEM格式 */
$pubKey = file_get_contents($AliRsaPublicKeyFilePath);
}else{
Yii::info('alipay_public_key_sha256.pem not exist,verify fail');exit;
}
/* 转换为openssl格式密钥 */
$res = openssl_get_publickey($pubKey);
if(!$res)
{
Yii::info('转换为openssl格式密钥失败');exit;
}
/* 调用openssl内置方法验签 */
$result = (bool) openssl_verify($data, base64_decode($sign), $res);
/* 释放资源 */
openssl_free_key($res);
/* 返回验签结果 */
return $result;
}
private function verifygw($is_sign_success) {
if ($is_sign_success) {
$response_xml = "<success>true</success><biz_content>" . $this->config ['merchant_public_key'] . "</biz_content>";
} else { // echo $response_xml;
$response_xml = "<success>false</success><error_code>VERIFY_FAILED</error_code><biz_content>" . $$this->config ['merchant_public_key'] . "</biz_content>";
}
$mysign=$this->alonersaSign($response_xml,$this->config['merchant_private_key'],$this->config['sign_type']);
$return_xml = "<?xml version=\"1.0\" encoding=\"".$this->config['charset']."\"?><alipay><response>".$response_xml."</response><sign>".$mysign."</sign><sign_type>".$this->config['sign_type']."</sign_type></alipay>";
Yii::info($return_xml);
echo $return_xml;
}
/**
* RSA单独签名方法,未做字符串处理,字符串处理见getSignContent()
* @param $data 待签名字符串
* @param $privatekey 商户私钥,根据keyfromfile来判断是读取字符串还是读取文件,false:填写私钥字符串去回车和空格 true:填写私钥文件路径
* @param $signType 签名方式,RSA:SHA1 RSA2:SHA256
* @param $keyfromfile 私钥获取方式,读取字符串还是读文件
* @return string
* @author mengyu.wh
*/
public function alonersaSign($data,$privatekey,$signType = "RSA") {
$res = openssl_get_privatekey($privatekey);
if(!$res)
{
Yii::info('您使用的私钥格式错误,请检查RSA私钥配置');exit;
}
if ("RSA2" == $signType) {
openssl_sign($data, $sign, $res, OPENSSL_ALGO_SHA256);
} else {
openssl_sign($data, $sign, $res);
}
$sign = base64_encode($sign);
return $sign;
}
}