php 支付宝 SDK 单笔转账
支付宝单笔转账,记录下使用过程和一些碰到的问题,以便为同样需求的提供一些参考.
官方的一些文档:单笔转账 , SDK,如何生成及配置公钥证书(视频),公钥证书
上面差不多是准备的工作,配置好后把三个证书都下载下来,这里可能有一些用错文件的情况
下载后的文件名称
<?php
//基础配置代码
namespace App\Services\Alipay;
class AlipayBase{
protected $aop;
private $error;
protected $appCertPath;
protected $rootCertPath;
protected $appId;
protected $rsaPrivateKey;
protected $alipayrsaPublicKey;
protected $rsaPrivateKeyPath;
protected $alipayrsaPublicKeyPath;
protected $payer_name = '支付返利';
public function __construct(){
$this->appId = config('alipay.app_id');//TODO 支付宝应用id
$this->rsaPrivateKey = file_get_contents(storage_path('cert'.DIRECTORY_SEPARATOR.'rsaPrivateKey.txt')); //TODO 使用支付宝开放平台开发助手生成的应用私钥
$this->appCertPath = storage_path('cert'.DIRECTORY_SEPARATOR.'appCertPublicKey.crt');//TODO 上传证书后生成的crt文件 证书
$this->rootCertPath = storage_path('cert'.DIRECTORY_SEPARATOR.'alipayRootCert.crt');//TODO 上传证书后生成的crt文件 支付宝根证书
$this->alipayrsaPublicKeyPath = storage_path('cert'.DIRECTORY_SEPARATOR.'alipayCertPublicKey_RSA2.crt');//TODO 上传证书后生成的txt文件 支付宝公钥证书(不能使用支付宝开放平台开发助手生成的应用公钥,这样子会导致支付回调验签失败)
require_once(__DIR__.'/../../Libs/Alipay/aop/AopCertClient.php');//SDK的路径
//1、execute 使用
$aop = new \AopCertClient ();
$aop->gatewayUrl = 'https://openapi.alipay.com/gateway.do';
$aop->apiVersion = '1.0';
$aop->signType = 'RSA2';
$aop->postCharset = 'utf-8';
$aop->format = 'json';
$aop->isCheckAlipayPublicCert = true;//是否校验自动下载的支付宝公钥证书,如果开启校验要保证支付宝根证书在有效期内
$aop->appId = $this->appId;
$aop->rsaPrivateKey = $this->rsaPrivateKey;
$aop->alipayrsaPublicKey = $aop->getPublicKey($this->alipayrsaPublicKeyPath);//调用getPublicKey从支付宝公钥证书中提取公钥
$aop->appCertSN = $aop->getCertSN($this->appCertPath);//调用getCertSN获取证书序列号
$aop->alipayRootCertSN = $aop->getRootCertSN($this->rootCertPath);//调用getRootCertSN获取支付宝根证书序列号
$this->aop = $aop;
}
/**
* 获取错误信息
* @return mixed
*/
public function getError()
{
return $this->error;
}
/**
* 设置错误信息
* @param mixed $error
*/
public function setError($error = '支付宝服务异常,请重试')
{
$this->error = $error;
return false;
}
}
//转账类
<?php
namespace App\Services\Alipay;
use App\Models\Basic\AlipayRecord;
/**
* 转账类
* Class Trans
* @package app\models\alipay
*/
class Trans extends AlipayBase {
/**
* 转账给支付宝
* @param $data
* @param string $order_title
* @return bool|\SimpleXMLElement
*/
public function transfer($uid,$order_number,$pay_no,$pay_name,$amount,$inviteFlowPayIds,$memo=''){
//判断是否是本地坏境
$isLocal = app()->environment('local');
if($isLocal){
return ['msg' => '本地坏境不转账'];
}
try {
require_once(__DIR__.'/../../Libs/Alipay/aop/request/AlipayFundTransUniTransferRequest.php');
$request = new \AlipayFundTransUniTransferRequest ();
$param = [
'biz_scene' => 'DIRECT_TRANSFER',//业务场景,单笔无密转账固定为DIRECT_TRANSFER
'product_code' => 'TRANS_ACCOUNT_NO_PWD',//销售产品码,单笔无密转账固定为TRANS_ACCOUNT_NO_PWD
'order_title' => $memo,//TODO 转账备注
'out_biz_no' => $order_number,//TODO 订单号
'trans_amount' => $amount,//TODO 金额
'payee_info' => [
'identity_type' => 'ALIPAY_LOGON_ID',
'identity' => $pay_no,//TODO 收款方账号
'name' => $pay_name,//TODO 姓名
],
'business_params'=>[
'payer_show_name'=>$this->payer_name //付款方账户
]
];
$bizcontent = json_encode($param);
$request->setBizContent($bizcontent);
$result = $this->aop->execute($request);
$responseNode = str_replace(".", "_", $request->getApiMethodName()) . "_response";
$resultCode = $result->$responseNode->code;
//验证sign
$params=[];
foreach ($result->$responseNode as $key=>$value){
$params[$key]= $value;
}
if(!empty($resultCode)&&$resultCode == 10000){
return true;
} else {
if (isset($params['sub_msg'])) {
$msg = $params['sub_msg'];
} else {
$msg = '未知错误,请查看日志';
}
return ['msg' => $msg];
}
} catch (\Exception $e) {
return $this->setError($e->getMessage());
}
}
/**
* 查询转账订单
* @param $out_biz_no
* @return bool|\SimpleXMLElement
*/
public function query($out_biz_no,$order_id=''){
try {
require_once(__DIR__.'/../../Libs/Alipay/aop/request/AlipayFundTransOrderQueryRequest.php');
$request = new \AlipayFundTransOrderQueryRequest ();
$param = [
'out_biz_no' => $out_biz_no,//TODO 订单号
'order_id'=>$order_id,
'biz_scene' => 'DIRECT_TRANSFER',//业务场景,单笔无密转账固定为DIRECT_TRANSFER
'product_code' => 'TRANS_ACCOUNT_NO_PWD',//销售产品码,单笔无密转账固定为TRANS_ACCOUNT_NO_PWD
];
$bizcontent = json_encode($param);
$request->setBizContent($bizcontent);
$result = $this->aop->execute ( $request);
$responseNode = str_replace(".", "_", $request->getApiMethodName()) . "_response";
$resultCode = $result->$responseNode->code;
$res_arr = [];
if(!empty($resultCode)&&$resultCode == 10000){
$res_arr['code'] = '1';
$res_arr['data'] = $result->$responseNode;
} else {
$res_arr['code'] = '-1';
$res_arr['data'] = $result->$responseNode;
}
return $res_arr;
} catch (\Exception $e) {
return $this->setError($e->getMessage());
}
}
}
SDK里面已经写了一个例子,这里贴出来
这里需要注意两个地方
使用证书模式本地必须开始SSL
如果出现SSL certificate: unable to get local issuer certificate错误信息
解决办法:到 curl.haxx.se/ca/cacert.pem 下载pem文件,并将文件拷贝到如下路径(根据自己的实际情况),并在php.ini 增加
curl.cainfo =”E:/wamp64/bin/php/php7.0.10/extras/ssl/cacert.pem”
另外一个就是这个SDK 的alipaySDK中定义的Encrypt()/Decrypt()函数与Laravel中定义的Encrypt()/Decrypt()函数重名了。这里在文件中查找encrypt/decrypt替换为alipayEncrypt/alipayDecrypt即可。
本作品采用《CC 协议》,转载必须注明作者和本文链接