这两天接了一个需求,就是对接支付宝的APP支付,看了下支付宝文档,结合之前写的支付宝sign篇,最终决定不用支付宝SDK,在这里跟大家分享下代码,我这里是用thinkphp开发。
一、支付宝类
我们先创建一个支付宝的类,命名为AliPay,代码如下
<?php
/**
* Created by PhpStorm.
* User: zhangw
* Date: 2024-03-01
* Time: 17:22
*/
namespace app\service;
/**
* 支付宝付款
* Class AliPay
* @package app\service
*/
class AliPay
{
/**
* 获取支付宝APP支付参数
* @param string $outOrderSn
* @param int $totalAmount
* @param string $notifyUrl
* @return string
*/
function aliPayOrder($outOrderSn = "", $totalAmount = 0, $notifyUrl = "")
{
#获取配置参数
$appId = Config('appConfig.ali_pay_app_id');
$rsaPrivateKey = Config('appConfig.ali_pay_private_key');
#获取时间
$outTime = time() + 2 *60;
#非公共参数
$requestData = [
'total_amount' => $totalAmount, // 订单总金额(元)
'product_code' => 'QUICK_MSECURITY_PAY', // 销售产品码
'subject' => "商品标题", // 商品标题
'out_trade_no' => $outOrderSn, // 订单号
'time_expire'=> date("Y-m-d H:i:s",$outTime), // 超时时间
];
#公共参数
$commonData = array(
'app_id' => $appId,
'method' => 'alipay.trade.app.pay',
'charset' => 'UTF-8',
'sign_type' => 'RSA2',
'timestamp' => date('Y-m-d H:i:s', time()),
'version' => '1.0',
'notify_url' => $notifyUrl,
'biz_content' => json_encode($requestData)
);
#拼接成字符串
$signString = self::getSignContent($commonData);
#待请求字符串加密获取sign
$sign = self::alonersaSign($signString, $rsaPrivateKey, "RSA2");
#将sign带入数组
$commonData["sign"] = $sign;
#请求链接编码及转换
$url = self::getSignContentUrlencode($commonData);
#返回支付链接给前端
return $url;
}
/////////////////////////////////////发起支付公共方法/////////////////////////////////////
/**
* 链接拼接
* @param $params
* @return string
*/
function getSignContentUrlEncode($params)
{
ksort($params);
$stringToBeSigned = "";
$i = 0;
foreach ($params as $k => $v) {
if (false === self::checkEmpty($v) && "@" != substr($v, 0, 1)) {
// 转换成目标字符集
$v = self::charaCet($v, "UTF-8");
if ($i == 0) {
$stringToBeSigned .= "$k" . "=" . urlencode($v);
} else {
$stringToBeSigned .= "&" . "$k" . "=" . urlencode($v);
}
$i++;
}
}
unset ($k, $v);
return $stringToBeSigned;
}
/**
* 请求参数加密获取sign
* @param $data
* @param $privateKey
* @param string $signType
* @param bool $keyFromFile
* @return string
*/
function aloneRsaSign($data, $privateKey, $signType = "RSA", $keyFromFile = false)
{
if (!$keyFromFile) {
$priKey = $privateKey;
$res = "-----BEGIN RSA PRIVATE KEY-----\n" .
wordwrap($priKey, 64, "\n", true) .
"\n-----END RSA PRIVATE KEY-----";
} else {
$priKey = file_get_contents($privateKey);
$res = openssl_get_privatekey($priKey);
}
($res) or die('您使用的私钥格式错误,请检查RSA私钥配置');
if ("RSA2" == $signType) {
openssl_sign($data, $sign, $res, OPENSSL_ALGO_SHA256);
} else {
openssl_sign($data, $sign, $res);
}
if ($keyFromFile) {
openssl_free_key($res);
}
$sign = base64_encode($sign);
return $sign;
}
/**
* 数组转成字符串
* @param $params
* @return string
*/
function getSignContent($params)
{
ksort($params);
$stringToBeSigned = "";
$i = 0;
foreach ($params as $k => $v) {
if (false === self::checkEmpty($v) && "@" != substr($v, 0, 1)) {
// 转换成目标字符集
$v = self::charaCet($v, "UTF-8");
if ($i == 0) {
$stringToBeSigned .= "$k" . "=" . "$v";
} else {
$stringToBeSigned .= "&" . "$k" . "=" . "$v";
}
$i++;
}
}
unset ($k, $v);
return $stringToBeSigned;
}
/**
* 判断参数值是否为空
* @param $value
* @return bool
*/
function checkEmpty($value)
{
if (!isset($value))
return true;
if ($value === null)
return true;
if (trim($value) === "")
return true;
return false;
}
/**
* 转换成目标字符集
* @param $data
* @param $targetCharset
* @return string
*/
function charaCet($data, $targetCharset)
{
if (!empty($data)) {
$fileType = "UTF-8";
if (strcasecmp($fileType, $targetCharset) != 0) {
$data = mb_convert_encoding($data, $targetCharset, $fileType);
}
}
return $data;
}
/**
* 支付宝回调验签
* @param $params
* @param $ali_pay_public_key
* @param string $sign_type
* @return bool
*/
function verifySign($params,$ali_pay_public_key,$sign_type='RSA2')
{
$ori_sign=$params['sign'];
unset($params['sign']);
unset($params['sign_type']);
ksort($params);
$data='';
foreach($params as $k => $v){
$data.=$k.'='.$v.'&';
}
$data=substr($data,0,-1);
$public_content="-----BEGIN PUBLIC KEY-----\n" . wordwrap($ali_pay_public_key, 64, "\n", true) . "\n-----END PUBLIC KEY-----";
$public_key=openssl_get_publickey($public_content);
if($public_key){
if($sign_type=='RSA2') {
$result = (bool)openssl_verify($data, base64_decode($ori_sign), $public_key, OPENSSL_ALGO_SHA256);
} else {
$result = (bool)openssl_verify($data, base64_decode($ori_sign), $public_key);
}
openssl_free_key($public_key);
return $result;
}else{
return false;
}
}
}
二、新增配置
我这里是放在thinkphp的config下的appConfig.php文件下,分别是
'ali_pay_app_id' => '支付宝appId',
'ali_pay_private_key' => '支付宝私钥'
'ali_pay_public_key'=>'支付宝平台应用公钥【非自己的的支付宝公钥】'
三、获取前端需要支付参数
我们直接在我们的接口直接调用该函数,获取前端所需的支付参数
#初始化支付宝类
$aliPay = new AliPay();
#初始化参数
$order_sn = time();
$total_money = 0.01;
$notify_url = "";
#获取支付参数
$aliPayUrl = $aliPay->aliPayOrder($order_sn,$total_money,$notify_url);
这样我们就可以获得前端所需支付参数。
四、回调处理
我们同样新增一个接口来处理回调,代码如下
#获取用户参数
$signData = request()->post();
#记录请求参数
Log::write("支付宝回调",json_encode($signData));
#检查回调参数是否正确
if (!isset($signData['sign']) && !isset($signData['trade_status'])){
echo "false";
die;
}
#获取配置
$aliPayPublicKey = Config('appConfig.ali_pay_public_key');
#初始化支付宝
$aliPay = new AliPay();
#格式化公钥【支付宝平台公钥】
$verifyRes = $aliPay->verifySign($signData,$aliPayPublicKey);
#验签
if (!$verifyRes){
echo "false";
die;
};
#判断是否是支付成功
if($signData['trade_status']!="TRADE_SUCCESS"){
echo "false";
die;
}
#提取参数
$buyerId = isset($signData["buyer_id"])?$signData["buyer_id"]:"";
$tradeNo = isset($signData["trade_no"])?$signData["trade_no"]:NULL;
$outTradeNo = isset($signData["out_trade_no"])?$signData["out_trade_no"]:"";
$gmtPayment = isset($signData["gmt_payment"])?$signData["gmt_payment"]:time();
以上是部分片段,这部分代码基本可以直接复制,下面的就是各自业务处理逻辑,我这里就不放上去了,仅供大家参考
以上就是thinkphp不通过支付宝SDK,进行支付宝APP支付以及回调处理。