第一步 支付申请
登录支付宝,进入我的商家服务,签约App支付服务,签约完成可获得支付宝分配的一些id,seller_id,partnerid
第二步 设置公钥
第三步 开放平台创建支付应用
创建支付应用获取appid,在应用详情设置接口加签方式,设置应用公钥,公钥通过支付宝提供的生成key 工具,生成公钥私钥,RSA2加签的选择生成2048位的密钥,公钥上传到支付宝,同时在本地会生成两个.txt
的密钥文件(应用公钥2048.txt,应用私钥2048.txt),上传到网站,也就是下面示例代码的$_privateKeyPath
,$_publicKeyPath
的路径文件.
第四步 接入
接入可以直接使用官网sdk,也可以自己接入
自己接入主要分几个步骤
1. 生成支付参数(服务端生成,客户端不安全)
2. 客户端调用sdk
3. 服务端异步接收支付通知
分享个生成支付参数,代码类
<?php
class Aliapp.
{
private $_appid = '';
private $_method = 'alipay.trade.app.pay';
private $_charset = 'utf-8';
private $_seller_id = '';
private $_privateKeyPath = '';//私钥路径
private $_publicKeyPath = '';//公钥路径
public function pay(array $params)
{
$pay_params = $this->createParams($params);
return $pay_params;
}
protected function createParams($params)
{
$request = array();
$request['app_id'] = $this->_appid;
$request['method'] = $this->_method;
$request['charset'] = $this->_charset;
$request['sign_type'] = 'RSA2';
$request['timestamp'] = date('Y-m-d H:i:s');
$request['version'] = '1.0';
$request['notify_url'] = 'http://example.com/notify.html';
$request['biz_content'] = json_encode(array (
'subject' => $params['subject'],
'out_trade_no' => $params['out_trade_no'],
'total_amount' => $params['total_fee'],
'seller_id' => $this->_seller_id,
'product_code' => 'QUICK_MSECURITY_PAY'
));
$sign_str = $this->fetchSignStr($request);
$sign = $this->generateSign($sign_str);
return $this->urlencodeRequestStr($request,$sign);
}
/**
* 对所有参数进行url编码
* @param [type] $request [description]
* @param [type] $sign [description]
* @return [type] [description]
*/
protected function urlencodeRequestStr($request,$sign)
{
ksort($request);
$result = '';
foreach ($request as $k=>$v) {
if ($v === '') {
continue;
}
$result .= $k.'='.urlencode($v).'&';
}
$result = rtrim($result,'&');
return $result.'&sign='.urlencode($sign);
}
/**
* 拼接签名字符串
* @param [type] $request [description]
* @return [type] [description]
*/
protected function fetchSignStr($request)
{
ksort($request);
$str = '';
foreach ($request as $k=>$v) {
if ($v === '') {
continue;
}
$v = mb_convert_encoding($v,$this->_charset);//转换编码
$str .= $k.'='.$v.'&';
}
return rtrim($str,'&');
}
/**
* 生成签名函数
* @param string $sign_str 签名拼接好的字符串
* @param string $sign_type 签名方式
* @return [type] [description]
*/
protected function generateSign($sign_str,$sign_type = 'RSA2')
{
$priKey = file_get_contents($this->_privateKeyPath);
$res = openssl_get_privatekey($priKey);
if ($sign_type == 'RSA2') {
openssl_sign($sign_str, $sign, $res, OPENSSL_ALGO_SHA256);
} else {
openssl_sign($data, $sign, $res);
}
$sign = base64_encode($sign);
return $sign;
}
/**
* 验签函数
* @param string $sign_str 签名拼接好的字符串
* @param string $sign 支付宝返回的签名
* @param string $sign_type 签名类型
* @return [type] [description]
*/
public function verifySign($params,$sign_type = 'RSA2')
{
$sign_str = $this->getVerifySignStr($params);
$sign = $params['sign'];
$alipay_public_key = file_get_contents($this->_publicKeyPath);
$alipay_public_key=str_replace("-----BEGIN PUBLIC KEY-----","",$alipay_public_key);
$alipay_public_key=str_replace("-----END PUBLIC KEY-----","",$alipay_public_key);
$alipay_public_key=str_replace("\n","",$alipay_public_key);
$alipay_public_key = "-----BEGIN PUBLIC KEY-----\n" .
wordwrap($alipay_public_key, 64, "\n", true) .
"\n-----END PUBLIC KEY-----";
$res = openssl_get_publickey($alipay_public_key);
if ($sign_type == "RSA2") {
$result = (bool)openssl_verify($sign_str, base64_decode($sign), $res, OPENSSL_ALGO_SHA256);
} else {
$result = (bool)openssl_verify($sign_str, base64_decode($sign), $res);
}
openssl_free_key($res);//释放资源
return $result;
}
/**
* 获取异步通知验签字符串
* @param [type] $params [description]
* @return [type] [description]
*/
protected function getVerifySignStr($params)
{
$response = array();
foreach ($params as $k=>$v) {
if ($k == 'sign_type' || $k == 'sign') {
continue;
}
$response[$k] = urldecode(html_entity_decode($v));
}
ksort($response);
$sign_str = '';
foreach ($response as $key=>$val) {
$sign_str .= $key.'='.$val.'&';
}
return rtrim($sign_str,'&');
}
}
收到支付宝异步通知,通过验签函数,以及自己商城业务逻辑处理后续流程
注意
在验签函数,生成签名字符串函数(getVerifySignStr()
)中有一句
$response[$k] = urldecode(html_entity_decode($v));
不懂是支付宝没有说清楚,还是个人环境问题,在支付宝异步通知的参数中,有个json
字符串中的双引号
被转化为html字符实体
,导致了一直验签失败.
'fund_bill_list' => '[{"amount":"0.01","fundChannel":"ALIPAYACCOUNT"}]',
另外,公钥路径是支付宝公钥,不是开放平台app的公钥
。
另外还遇到一个坑
windows下使用支付宝提供的RSA签名验签工具
生成的公钥与私钥,公钥上传到应用,并且配置到代码中之后,在使用函数生成签名的过程中
$priKey = file_get_contents($this->_privateKeyPath);
$res = openssl_get_privatekey($priKey); //此处res返回false
报错:
openssl_sign(): supplied key param cannot be coerced into a private key
工具生成的应用私钥.txt文件中的私钥是保存成一行的,所以需要特殊处理下,函数修改方案
protected function generateSign($sign_str,$sign_type = 'RSA2')
{
$priKey = file_get_contents($this->_privateKeyPath);
$priKey = chunk_split($priKey, 64, "\n");
$priKey = "-----BEGIN RSA PRIVATE KEY-----\n$priKey-----END RSA PRIVATE KEY-----\n";
$res = openssl_get_privatekey($priKey);
if ($sign_type == 'RSA2') {
openssl_sign($sign_str, $sign, $res, OPENSSL_ALGO_SHA256);
} else {
openssl_sign($sign_str, $sign, $res);
}
$sign = base64_encode($sign);
return $sign;
}
如果上面的类不能使用,把这个函数替换下.