jh-admin/addon/aliapp/model/CloudPay.php

609 lines
22 KiB
PHP

<?php
/**
* SAAS应用系统 --- 十年开发经验汇集巨献!
* ==========================================================
* Copy right 2020-2050 成都众联思索科技有限公司,保留所有权利。
* ----------------------------------------------------------
* 官方网址: https://www.zoomtk.com
* 这不是自由软件!未经允许不得用于商业目或程序代码摘取及修改。
* 任何企业和个人未经允许对程序代码以任何形式任何目的再发布传播。
* 唯一发布渠道www.zoomtk.com;非官方渠道统一视为侵权行为。
* ==========================================================
*/
namespace addon\aliapp\model;
use addon\alipay\data\sdk\AopClient;
use app\exception\ApiException;
use app\model\system\Pay as PayCommon;
use extend\api\HttpClient;
use think\facade\Db;
use think\facade\Log;
class CloudPay
{
public $aop;
public $config_info;
function __construct()
{
try {
$config_info = config('alipay.cloudAlipay');
$this->config_info = $config_info;
// 获取支付宝支付参数(统一支付到平台账户)
$this->aop = new AopClient();
$this->aop->alipayrsaPublicKey = $config_info['public_key'] ?? "";
$this->aop->alipayPublicKey = $config_info['alipay_public_key'] ?? "";
$this->aop->isv_app_id = $config_info["isv_app_id"] ?? "";
$this->aop->rsaPrivateKey = $config_info['private_key'] ?? "";
$this->aop->gatewayUrl = 'https://apigw.alipay-eco.com';
$this->aop->alipaySdkVersion = "alipay-sdk-php-20200415";
$this->aop->apiVersion = '1.0';
$this->aop->signType = 'RSA2';
$this->aop->postCharset = 'UTF-8';
$this->aop->format = 'json';
} catch (\Exception $e) {
throw new ApiException(-1, '支付宝配置错误');
}
}
/***
* 支付回调
* @throws \Exception
*/
public function payNotify()
{
$service = input('service', '-------');
Log::log('info', '芝麻状态通知:' . $service . ':' . json_encode($_POST));
if ($this->rsaCheckV(request()->post())) {
$biz_content = json_decode(input('biz_content'), true);
switch ($_POST['service']) {
case 'spi.fitness.subscription.syncSubscription': //订购状态通知
$res = $this->syncSubscription($biz_content);
break;
case 'spi.fitness.order.syncOrder':
$res = $this->syncOrder($biz_content);
break;
case 'spi.fitness.merchant.syncStatus'://商户状态
$res = $this->syncStatus($biz_content);
break;
case 'spi.fitness.subscription.getActivateInfo'://周期商品回调
$res = $this->getActivateInfo($biz_content);
break;
default:
$fail = [
'success' => false,
'code' => 10000,
"msg" => 'Success',
"sub_code" => 'INVALID_PARAMS',
"sub_msg" => '未找到服务方式',
];
ksort($fail);
$json = [
'response' => $fail,
'sign' => $this->aop->sign(json_encode($fail), "RSA2")
];
echo json_encode($json);
}
} else {
$fail = [
'success' => false,
'code' => 10000,
"msg" => 'Success',
"sub_code" => 'INVALID_PARAMS',
"sub_msg" => '验签失败',
];
ksort($fail);
$json = [
'response' => $fail,
'sign' => $this->aop->sign(json_encode($fail), "RSA2")
];
echo json_encode($json);
}
}
/***
* 获取验证数据
* @param $biz_content
* @return void
*/
public function getActivateInfo($biz_content)
{
if (isset($biz_content['productNo'])) {
$outSubscriptionNo = $biz_content['outSubscriptionNo'];
$res = cache($outSubscriptionNo);
cache($outSubscriptionNo, $res);
unset($res['appSubscriptionUrl']);
} else { //分享
}
$success = [
'code' => "10000",
"msg" => "Success",
];
if ($res) {
$success = array_merge($success, $res);
}
ksort($success);
$json = [
'response' => $success,
'sign' => $this->aop->sign(json_encode($success, JSON_UNESCAPED_UNICODE), "RSA2")
];
echo json_encode($json);
}
/***
* 商户状态
* @param $data
*/
public function syncStatus($data)
{
if (isset($data['merchantPid']) && $data['merchantPid']) {
$merchant_smid = $data['merchantPid'];
$merchantStatus = $data['merchantStatus'];
$recentZhimaReviewStatus = $data['recentZhimaReviewStatus'];
// $merchantStatus=[
// 'NORMAL'=>'正常',
// 'ABNORMAL'=>'异常商户',
// 'PAUSING'=>'暂停中',
// 'PAUSED'=>'已暂停',
// 'CLOSING'=>'关闭中',
// 'CLOSED'=>'已关闭',
// 'RECOVERING'=>'恢复中',
// ];
// $recentZhimaReviewStatus=[
// 'INCOMPLETE'=>'未补全',
// 'UNDER_REVIEW'=>'审核中',
// 'PASSED'=>'已通过',
// 'FAILED'=>'未通过',
// ];
$reviewFailReason = $data['reviewFailReason'] ?? '';
switch ($recentZhimaReviewStatus) {
case 'PASSED':
case 'NORMAL':
$is_zmpay = 1;
break;
case 'UNDER_REVIEW':
$is_zmpay = 0;
break;
case 'FAILED':
$is_zmpay = 2;
break;
}
Db::name('pay_shop')->where('merchant_smid', '=', $merchant_smid)->update([
'is_zmpay' => $is_zmpay,
'merchantStatus' => $merchantStatus,
'ZhimaReviewStatus' => $recentZhimaReviewStatus,
'zmapply_desc' => $reviewFailReason,
]);
}
$success = [
'success' => true,
'code' => 10000,
"msg" => 'Success',
];
ksort($success);
$json = [
'response' => $success,
'sign' => $this->aop->sign(json_encode($success), "RSA2")
];
echo json_encode($json);
}
/***
* 订单回调
* @param $data
* @throws \Exception
*/
public function syncSubscription($data)
{
$out_trade_no = $data['outSubscriptionNo'];
// 支付宝交易号
$trade_no = $data['subscriptionNo'];
// 交易状态
$trade_status = $data['subscriptionStatus'];
switch ($trade_status) {
case 'NORMAL': //正常
$pay_common = new PayCommon();
$retval = $pay_common->onlinePay($out_trade_no, "zmxxpay", $trade_no, "cardservice");
if (empty($retval['data'])) {
$fail = [
'code' => '40000',
"msg" => 'Failed',
"sub_code" => 'INVALID_PARAMS',
"sub_msg" => '参数异常',
];
ksort($fail);
$json = [
'response' => $fail,
'sign' => $this->aop->sign(json_encode($fail, JSON_UNESCAPED_UNICODE), "RSA2")
];
echo json_encode($json);
return;
}
break;
case 'PAUSED': //订单暂停
$where = [
'trade_no' => $trade_no
];
Db::name('member_goods_card')->where($where)->update(['status' => 2, 'jsonValue' => json_encode($data)]);
break;
case "CANCEL": //超时取消
case "END": //订单结束
case "DEFAULT_CANCEL": //违约取消
$where = [
'trade_no' => $trade_no
];
Db::name('member_goods_card')->where($where)->update(['status' => 0, 'jsonValue' => json_encode($data)]);
break;
case "SURRENDER": //解约
$where = [
'trade_no' => $trade_no
];
Db::name('member_goods_card')->where($where)->update(['status' => 4, 'jsonValue' => json_encode($data)]);
break;
default:
$fail = [
'success' => false,
'code' => 10000,
"msg" => 'Success',
"sub_code" => 'INVALID_PARAMS',
"sub_msg" => '未找到业务类型',
];
ksort($fail);
$json = [
'response' => $fail,
'sign' => $this->aop->generateSign($fail, "RSA2")
];
echo json_encode($json);
return;
}
$success = [
'success' => true,
'code' => 10000,
"msg" => 'Success',
];
ksort($success);
$json = [
'response' => $success,
'sign' => $this->aop->sign(json_encode($success), "RSA2")
];
echo json_encode($json);
}
/***
* 订单同步状态
* @param $data
*/
public function syncOrder($data)
{
$out_trade_no = $data['outSubscriptionNo']; //平台交易号
// 支付宝交易号
$trade_no = $data['subscriptionNo'];
$third_order_no = $data['orderNo'];
// 交易状态
$trade_status = $data['orderStatus'];
$where = [
'out_trade_no' => $out_trade_no,
'third_order_no' => $third_order_no
];
$orderInfo = (array)model('order')->getInfo([['out_trade_no', '=', $out_trade_no]], 'site_id,store_id,order_id,member_id,name,mobile,province_id,city_id,district_id,address,full_address,promotion_type,promotion_type_name');
$zmOrder = Db::name('zima_order')->where($where)->find();
if (empty($zmOrder)) {
$insdata = [
'productNo' => $data['productNo'],
'merchantPid' => $data['merchantPid'],
'site_id' => $orderInfo['site_id'],
'store_id' => $orderInfo['store_id'],
'userid' => $data['userId'],
'order_id' => $orderInfo['order_id'] ?? 0,
'uid' => $orderInfo['member_id'] ?? 0,
'trade_no' => $trade_no,
'out_trade_no' => $out_trade_no,
'third_order_no' => $third_order_no,
'verificationCodeStatus' => $data['verificationCodeStatus'] ?? '',
'orderSettleStatus' => $data['orderSettleStatus'],
'create_time' => strtotime($data['orderDate']),
'plan_time' => strtotime($data['planDeductionTime'] ?? ''),
'period' => $data['period'],
'order_status' => $trade_status,
'amount' => $data['deductionAmount']
];
Db::name('zima_order')->insert($insdata);
} else {
$updata = [
'order_status' => $trade_status,
'verificationCodeStatus' => $data['verificationCodeStatus'] ?? '',
'orderSettleStatus' => $data['orderSettleStatus'],
'settleTime' => $data['settleTime'] ?? 0,
'failTimes' => $data['failTimes'] ?? 0,
'failReason' => $data['failReason'] ?? '',
];
if ($trade_status == 'PAID') { //扣款成功
$updata['payChannel'] = $data['payChannel'];
$updata['settleDetails'] = json_encode($data['settleDetails']);
$updata['pay_time'] = strtotime($data['actualDeductionTime']);
event('CloudAliPayOrderPay', array_merge($zmOrder, $updata, $orderInfo));//订单支付成功
}
Db::name('zima_order')->where($where)->update($updata);
//核销支付中
try {
event('CloudAliPaySyncOrder', array_merge($zmOrder, $updata, $orderInfo));//订单同步事件
} catch (\Exception $exception) {
trace($exception, 'CloudAliPaySyncOrder同步事件' . json_encode($data));
}
}
$success = [
'success' => true,
'code' => 10000,
"msg" => 'Success',
];
ksort($success);
$json = [
'response' => $success,
'sign' => $this->aop->sign(json_encode($success), "RSA2")
];
echo json_encode($json);
}
/***
* 退款
*/
public function refund($param)
{
$pay_info = $param['pay_info'];
$bizContent = [
'merchantPid' => $pay_info['merchantPid'],
'subscriptionNo' => $pay_info['trade_no'],
'orderNo' => $pay_info['order_no']
];
//其他参数
$params["charset"] = "UTF-8";
$params["isv_app_id"] = $this->aop->isv_app_id;
$params["utc_timestamp"] = $this->msectime();
//指定需要调用的service接口
$params["service"] = "api.fitness.orderRefund";
$params["request_id"] = $this->uuid();
$params["biz_content"] = json_encode($bizContent);
$params["version"] = "1.0";
$params["sign"] = $this->aop->generateSign($params, "RSA2");
$params["sign_type"] = 'RSA2';
// 调用ECOAPI
$result = $this->aop->call($params);
return $result;
}
/***
* 新增或者修改商户
* @param $param
* @return false|mixed
*/
public function upAlipayUser($param, $settleAccountList = '', $site_id = 0)
{
$bizContent = [
'merchantPid' => $param['smid'],
'smid' => $param['smid'],
'merchantAppId' => $param['merchantAppId'],
'merchantLoginName' => $param['merchantLoginName'],
'merchantName' => $param['merchantName'],
'separateLedgerRate' => $param['separateLedgerRate'],
'phone' => $param['phone'],
'logoUrl' => $param['logoUrl'] ?? ''
];
if ($settleAccountList) {
$bizContent['settleAccountList'] = array_values($settleAccountList);
}
$proxy_url = config('alipay.cloudAlipay.proxy_url');
if ($proxy_url) {
$this->http_post_data($proxy_url, ['userId' => $param['smid'], 'domain' => addon_url('pay/pay/cloudnotify'), 'site_id' => $site_id]);
}
//其他参数
$params["charset"] = "UTF-8";
$params["isv_app_id"] = $this->aop->isv_app_id;
$params["utc_timestamp"] = $this->msectime();
//指定需要调用的service接口
$params["service"] = "api.fitness.newSaveOrUpdate";
$params["request_id"] = $this->uuid();
$params["biz_content"] = json_encode($bizContent);
$params["version"] = "1.0";
$params["sign"] = $this->aop->generateSign($params, "RSA2");
$params["sign_type"] = 'RSA2';
// 调用ECOAPI
$result = $this->aop->call($params);
return $result['response'];
}
public function merchantApplyInfoQuery($merchantPid,$site_id)
{
$bizContent = [
'merchantPid' => $merchantPid
];
//其他参数
$params["charset"] = "UTF-8";
$params["isv_app_id"] = $this->aop->isv_app_id;
$params["utc_timestamp"] = $this->msectime();
//指定需要调用的service接口
$params["service"] = "api.fitness.merchantApplyInfoQuery";
$params["request_id"] = $this->uuid();
$params["biz_content"] = json_encode($bizContent);
$params["version"] = "1.0";
$params["sign"] = $this->aop->generateSign($params, "RSA2");
$params["sign_type"] = 'RSA2';
// 调用ECOAPI
$result = $this->aop->call($params);
return $result['response'];
}
/***
* 上传图片
* @param $base64Img
* @return mixed
*/
public function logoUpload($base64Img)
{
$bizContent = [
'picData' => $base64Img
];
//其他参数
$params["charset"] = "UTF-8";
$params["isv_app_id"] = $this->aop->isv_app_id;
$params["utc_timestamp"] = $this->msectime();
//指定需要调用的service接口
$params["service"] = "api.fitness.logoUpload";
$params["request_id"] = $this->uuid();
$params["biz_content"] = json_encode($bizContent);
$params["version"] = "1.0";
$params["sign"] = $this->aop->generateSign($params, "RSA2");
// 调用ECOAPI
$httpClient = new HttpClient();
$res = $httpClient->post($this->aop->gatewayUrl, $params);
// 将返回结果转换本地文件编码
$result = json_decode($res, true);
return $result['response'];
}
/***
* 商户解约
* @param $param
* @return false|mixed
*/
public function Surrender($subscriptionNo, $type = 'NORMAL')
{
$bizContent = [
'subscriptionNo' => $subscriptionNo,
'subscriptionCancelType' => $type //NORMAL 正常 DEFAULT违约
];
//其他参数
$params["charset"] = "UTF-8";
$params["isv_app_id"] = $this->aop->isv_app_id;
$params["utc_timestamp"] = $this->msectime();
//指定需要调用的service接口
$params["service"] = "api.fitness.subscriptionSurrender";
$params["request_id"] = $this->uuid();
$params["biz_content"] = json_encode($bizContent);
$params["version"] = "1.0";
$params["sign"] = $this->aop->generateSign($params, "RSA2");
$params["sign_type"] = 'RSA2';
// 调用ECOAPI
$result = $this->aop->call($params);
return $result;
}
/***
* 核销
* @param $verificationCode
* @param $orderNo
* @return false|mixed
*/
public function verify($verificationCode, $orderNo = '')
{
$bizContent = [
'verificationCode' => $verificationCode
// 'orderNo'=>$orderNo,
];
//其他参数
$params["charset"] = "UTF-8";
$params["isv_app_id"] = $this->aop->isv_app_id;
$params["utc_timestamp"] = $this->msectime();
//指定需要调用的service接口
$params["service"] = "api.fitness.verification";
$params["request_id"] = $this->uuid();
$params["biz_content"] = json_encode($bizContent);
$params["version"] = "1.0";
$params["sign"] = $this->aop->generateSign($params, "RSA2");
$params["sign_type"] = 'RSA2';
// 调用ECOAPI
$result = $this->aop->call($params);
return $result;
}
/***
* 订单查询
* @param $subscriptionNo
* @param $orderNo
* @return false|mixed
*/
public function orderQuery($subscriptionNo = '', $orderNo = '', $verificationCode = '')
{
$bizContent = [
'subscriptionNo' => $subscriptionNo,
'orderNo' => $orderNo,
'verificationCode' => $verificationCode,
];
//其他参数
$params["charset"] = "UTF-8";
$params["isv_app_id"] = $this->aop->isv_app_id;
$params["utc_timestamp"] = $this->msectime();
//指定需要调用的service接口
$params["service"] = "api.fitness.orderQuery";
$params["request_id"] = $this->uuid();
$params["biz_content"] = json_encode($bizContent);
$params["version"] = "1.0";
$params["sign"] = $this->aop->generateSign($params, "RSA2");
$params["sign_type"] = 'RSA2';
// 调用ECOAPI
$result = $this->aop->call($params);
return $result;
}
public function imageUpload($site_id, $image_url, $image_name = '图片', $isCache = false)
{
$ali = new MinCode($site_id);
$result = $ali->imageUpload($image_name, $image_url, $isCache);
$result = $result['alipay_offline_material_image_upload_response'];
if ($result['code'] == 10000) {
return success(0, '', $result);
} else {
return error(-1, $result['sub_msg']);
}
}
/***
* 验证签名
* @param $data
* @return bool|void
*/
public function rsaCheckV($data)
{
unset($data['sign_type']);
$res = $this->aop->rsaCheckV2($data, $this->aop->alipayrsaPublicKey, 'RSA2');
return $res;
}
/**
* 返回当前的毫秒时间戳
*/
public function msectime()
{
list($s1, $s2) = explode(' ', microtime());
return (float)sprintf('%.0f', (floatval($s1) + floatval($s2)) * 1000);
}
public function http_post_data($url, $params = array())
{
if (is_array($params)) {
$params = http_build_query($params, null, '&');
}
$ch = curl_init();
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POSTFIELDS, $params);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
$response = curl_exec($ch);
curl_close($ch);
return $response;
}
/**
* 生成UUID
*/
public function uuid()
{
$chars = md5(uniqid(mt_rand(), true));
$uuid = substr($chars, 0, 8) . '-'
. substr($chars, 8, 4) . '-'
. substr($chars, 12, 4) . '-'
. substr($chars, 16, 4) . '-'
. substr($chars, 20, 12);
return $uuid;
}
}