支付宝app
第一步:整理支付宝 参数,appid,密码等。在extra文件夹下建立一个文件ali.php,把参数都放进去。
1,支付宝app支付三大基本参数: APPID; 秘钥,应用公钥;
2,支付宝退款三大基本参数: APPID; 秘钥,支付宝公钥;
3,支付宝需要自己生成唯一订单号,这个和支付宝商户中心商户订单号不一样,但成功后回调也会把我们这个发过去的订单号回调发过来,以此订单号为条件,查询订单表数据,更改订单状态;可以顺便把支付宝交易号回调过来保存数据库,后面退款需要用到.
4,支付宝回调地址notify_url必须公网可以访问
<?php
//支付宝配置文件,放在应用配置文件或者模块配置文件都可以。如果多个模块都需要就放置在应用模块config.php中。
return [
'zhn' => [
//应用ID APPID)
'app_id' =>'25555644',
// ֧异步通知地址,异步是服务器和服务器之间的交互,没有cookieId,没有session值,写业务逻辑。比如接收值,存入数据库
'notify_url' => 'http://localhost/admin/payment/notify',
// ֧//同步通知页面跳转,支付宝支付成功要跳转的地址
'return_url' => '',
//支付宝网关
'gatewayUrl'=>'https://openapi.alipay.com/gateway.do',
//支付宝公钥
'ali_public_key' => '9ulOzQH63idl05qQIDAQAB',
// 商户私钥
'private_key' => '9ulOzQH63idl05qQIDAQAB',
'log' => [ // optional
'file' => '../storage/logs/alipay.log',
'level' => 'info', //
'type' => 'single', //
'max_file' => 30, //
],
'http' => [
'timeout' => 5.0,
'connect_timeout' => 5.0,
],
]
]
?>
第二步:去支付宝开放平台下载 App支付服务端 DEMO & SDK
解压后放入vendor文件夹
第三步:建立一个控制器,支付类。
<?php
namespace app\admin\Controller;
use think\Controller;
use think\Loader;
use think\Request;
class Payment extends Base{
private $appId = '应用id';
private $rsaPrivateKey = "商户秘钥";
private $alipayrsaPublicKey = "商户公钥";
private $aop;
private $notify_url = "公网服务器域名/index/alipay/notify";
private $alipayrsaPublicKey_zhifubao = "支护宝公钥";
//生成唯一订单号退款用
public function build_order_no(){
return date('Ymd').substr(implode(NULL, array_map('ord', str_split(substr(uniqid(), 7, 13), 1))), 0, 8);
}
public function __construct()
{
$config=config('alipay.zhiun');
$this->appId=$config['app_id'];
$this->rsaPrivateKey=$config['private_key'];
$this->alipayrsaPublicKey_zhifubao=$config['ali_public_key'];
$this->notify_url=$config['notify_url'];
Loader::import('alipay.AopSdk');
}
//支付宝app退款 退款用支付宝公钥
public function exitPay(Request $request)
{
//引入sdk
vendor('aop.AopClient');
vendor('aop.request.AlipayTradeRefundRequest');
$aop = new \AopClient ();
$aop->gatewayUrl = 'https://openapi.alipay.com/gateway.do';
$aop->appId = $this->appId;
$aop->rsaPrivateKey = $this->rsaPrivateKey;
$aop->alipayrsaPublicKey = $this->alipayrsaPublicKey_zhifubao;
$aop->apiVersion = '1.0';
$aop->signType = "RSA2";
$aop->postCharset = 'UTF-8';
$aop->format = "json";
$request = new \AlipayTradeRefundRequest ();
//需要传入的参数变量
$trade_no = ""; //支付宝交易号,和商户订单号不能同时为空
$refund_fee = "10";//退款金额
$out_trade_no = "87177359170";//订单支付时传入的商户订单号,不能和 trade_no同时为空。
$request->setBizContent("{" .
"\"trade_no\":\"{$trade_no}\"," . //支付宝交易号,和商户订单号不能同时为空 特殊可选
"\"out_trade_no\":\"{$out_trade_no}\"," . //订单支付时传入的商户订单号,不能和 trade_no同时为空。 特殊可选
"\"refund_amount\":\"{$refund_fee}\"," . //需要退款的金额,该金额不能大于订单金额,单位为元,支持两位小数 必选
"\"refund_reason\":\"退款\"" . //退款的原因说明 正常退款 可选 最后的“,”逗号去掉
"}");
$result = $aop->execute($request);
$responseNode = str_replace(".", "_", $request->getApiMethodName()) . "_response";
$resultCode = $result->$responseNode->code;
if(!empty($resultCode) && $resultCode == 10000){
//退款成功,处理订单状态以及更新退款等信息
return json(['code'=>200,'msg'=>'退款成功']);
} else {
return json(['code'=>400,'msg'=>'退款失败']);
}
}
//支付宝app支付付款 支付用的是应用公钥.,下面这个没测试
public function payOrder(Request $request)
{
//引入sdk
vendor('aop.AopClient');
vendor('aop.request.AlipayTradeAppPayRequest');
$subject="测试公益付款";//商品标题
$body = '公益事业';
$out_trade_no = $this->build_order_no();
$total_amount = 0.01;//付款金额
$aop = new \AopClient ();
$aop->gatewayUrl = "https://openapi.alipay.com/gateway.do";
$aop->appId = $this->appId;
$aop->rsaPrivateKey = $this->rsaPrivateKey;
$aop->format = "json";
$aop->charset = "UTF-8";
$aop->signType = "RSA2";
$request = new \AlipayTradeAppPayRequest();
$bizcontent = "{\"body\":\"".$body."\","
. "\"subject\": \"".$subject."\","
. "\"out_trade_no\": \"".$out_trade_no."\","
. "\"timeout_express\": \"30m\","
. "\"total_amount\": \"".$total_amount."\","
. "\"product_code\":\"QUICK_MSECURITY_PAY\""
. "}";
$request->setNotifyUrl($this->notify_url);
$request->setBizContent($bizcontent);
//这里和普通的接口调用不同,使用的是sdkExecute
$response = $aop->sdkExecute($request);
if($response){
echo json_encode(['status'=>1,'msg'=>'success','data'=>$response]);
}else{
echo json_encode(['status'=>0,'msg'=>'false','orderid'=>$orderid]);
}
}
//支付宝支付成功之后异步回调处理
public function notify()
{
if ($_POST['trade_status'] == 'TRADE_SUCCESS')
{//如果支付成功
//===============修改订单状态===========================//
$orderSn = $_POST['out_trade_no'];//获取订单号
$data1['batch'] = $_POST['trade_no']; //交易流水号
//根据订单号,以此为条件,修改订单状态以及存入交易流水号
echo 'success'; //必须success结尾,否则支付宝会多次回调
exit;
}
}
// --------------------------------微信-----------------------------------------------------------
注意:
1,证书路径,注意应该填写绝对路径(仅退款、撤销订单时需要,可登录商户平台下载,API证书下载地址:https://pay.weixin.qq.com/index.php/account/api_cert
,下载之前需要安装商户操作证书)
/**
* 退款
* @param float $totalFee 订单金额 单位元
* @param float $refundFee 退款金额 单位元
* @param string $refundNo 退款单号
* @param string $wxOrderNo 微信订单号
* @param string $orderNo 商户订单号
* @return string
*/
public function doRefund($totalFee='10', $refundFee='10', $refundNo='', $wxOrderNo = '', $orderNo = '87371826215') {
$config=config('weconfig.zhun');
$unified = array(
'appid' => $config['app_id'],
'mch_id' => $config['mch_id'],
'nonce_str' => self::createNonceStr(),//随机字符串
'total_fee' => intval($totalFee * 100), //订单金额 单位 转为分
'refund_fee' => intval($refundFee * 100), //退款金额 单位 转为分
'sign_type' => 'MD5', //签名类型 支持HMAC-SHA256和MD5,默认为MD5
'transaction_id' => $wxOrderNo, //微信订单号 (商户订单号与微信订单号二选一,至少填一个)
'out_trade_no' => $orderNo, //商户订单号
'out_refund_no' => uniqid(), //商户退款单号
'refund_desc' => '商品已售完', //退款原因(选填)
);
$unified['sign'] = self::getSign($unified, $config['key']);
$responseXml = $this->curlPost('https://api.mch.weixin.qq.com/secapi/pay/refund', self::arrayToXml($unified));
$unifiedOrder = simplexml_load_string($responseXml, 'SimpleXMLElement', LIBXML_NOCDATA);
// { ["return_code"]=> "SUCCESS" ["return_msg"]=> "OK" ["appid"]=> "w90065" ["mch_id"]=> "1001" ["nonce_str"]=> "S4ZqlV36q" ["sign"]=> "CD37B81C02D937335D89" ["result_code"]=> "FAIL" ["err_code"]=> "ERROR" ["err_code_des"]=> string(21) "订单已全额退款" }
if ($unifiedOrder === false) {
die('parse xml error');
}
if ($unifiedOrder->return_code != 'SUCCESS') {
die($unifiedOrder->return_msg);
}
if(!empty($unifiedOrder) && $unifiedOrder->return_code == 'SUCCESS'){
//退款成功,处理订单状态以及更新退款等信息
return json_encode(['code'=>100,'msg'=>'退款成功']);
} else {
return json_encode(['code'=>200,'msg'=>$result->$unifiedOrder->err_code_des]);
}
}
public static function curlGet($url = '', $options = array()) {
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
if (!empty($options)) {
curl_setopt_array($ch, $options);
}
//https请求 不验证证书和host
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
$data = curl_exec($ch);
curl_close($ch);
return $data;
}
public function curlPost($url = '', $postData = '', $options = array()) {
if (is_array($postData)) {
$postData = http_build_query($postData);
}
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
curl_setopt($ch, CURLOPT_TIMEOUT, 30); //设置cURL允许执行的最长秒数
if (!empty($options)) {
curl_setopt_array($ch, $options);
}
//https请求 不验证证书和host
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
//第一种方法,cert 与 key 分别属于两个.pem文件
//默认格式为PEM,可以注释
curl_setopt($ch, CURLOPT_SSLCERTTYPE, 'PEM');
curl_setopt($ch, CURLOPT_SSLCERT, "cert/wechat/apiclient_cert.pem");//绝对路径 必须有证书
//默认格式为PEM,可以注释
curl_setopt($ch, CURLOPT_SSLKEYTYPE, 'PEM');
curl_setopt($ch, CURLOPT_SSLKEY, "cert/wechat/apiclient_key.pem");//绝对路径,必须有证书
//第二种方式,两个文件合成一个.pem文件
//curl_setopt($ch,CURLOPT_SSLCERT,getcwd().'/all.pem');
$data = curl_exec($ch);
if ($data) {
curl_close($ch);
return $data;
} else {
$error = curl_errno($ch);
echo "curl出错,错误码:$error" . "<br>";
curl_close($ch);
return false;
}
}
public static function createNonceStr($length = 16) {
$chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
$str = '';
for ($i = 0; $i < $length; $i++) {
$str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
}
return $str;
}
public function arrayToXml($arr) {
$xml = "<xml>";
foreach ($arr as $key => $val) {
if (is_numeric($val)) {
$xml .= "<" . $key . ">" . $val . "</" . $key . ">";
} else {
$xml .= "<" . $key . "><![CDATA[" . $val . "]]></" . $key . ">";
}
}
$xml .= "</xml>";
return $xml;
}
public static function xmlToArray($xml) {
$array_data = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
return $array_data;
}
public static function getSign($params, $key) {
ksort($params, SORT_STRING);
$unSignParaString = self::formatQueryParaMap($params, false);
$signStr = strtoupper(md5($unSignParaString . "&key=" . $key));
return $signStr;
}
protected static function formatQueryParaMap($paraMap, $urlEncode = false) {
$buff = "";
ksort($paraMap);
foreach ($paraMap as $k => $v) {
if (null != $v && "null" != $v) {
if ($urlEncode) {
$v = urlencode($v);
}
$buff .= $k . "=" . $v . "&";
}
}
$reqPar = '';
if (strlen($buff) > 0) {
$reqPar = substr($buff, 0, strlen($buff) - 1);
}
return $reqPar;
}
}
?>