795 lines
30 KiB
PHP
795 lines
30 KiB
PHP
<?php
|
||
|
||
namespace app\api\controller;
|
||
|
||
use addons\epay\library\Service;
|
||
use fast\Random;
|
||
use think\addons\Controller;
|
||
use app\common\exception\Exception;
|
||
use app\common\model\order\Order;
|
||
use think\Db;
|
||
use app\common\controller\Api;
|
||
use app\common\model\User;
|
||
use app\common\library\pay\Balance;
|
||
use app\common\library\pay\VirtualPayService;
|
||
|
||
class Pay extends Api
|
||
{
|
||
|
||
protected $noNeedLogin = ['prepay', 'notifyx', 'notifyr', 'alipay','searchOrder','getNotifyParams'];
|
||
protected $noNeedRight = ['*'];
|
||
|
||
|
||
|
||
/**
|
||
* 获取支付渠道
|
||
* @return void
|
||
*/
|
||
public function getPayType(){
|
||
$config = \app\common\model\config\System::getConfig('payment_basic');
|
||
|
||
$payType = $config['pay_type'];
|
||
|
||
$data = [];
|
||
|
||
foreach ($payType as $index=> $item){
|
||
$temp = [
|
||
'name'=>__('pay_'.$item),
|
||
'type'=>$item,
|
||
'index'=>$index
|
||
];
|
||
if($item == 'balance'){
|
||
$temp['subname'] = "可用余额 ¥{$this->auth->money}";
|
||
}
|
||
|
||
if($item == 'wechat'){
|
||
$platform = \app\common\library\Platform::getPlatform();
|
||
$wechatAvailable = false;
|
||
|
||
// 检查支付账户池是否有可用商户号(仅验证商户号有效性,平台可用性统一由系统配置决定)
|
||
if (\app\admin\library\project\App::isInstall('paymentpool')
|
||
&& \app\common\library\app\paymentpool\Service::isEnable()) {
|
||
$activeAccount = \app\common\library\app\paymentpool\Service::getActiveAccount('wechat');
|
||
if ($activeAccount && !empty($activeAccount['config']['app_id'])) {
|
||
$wechatAvailable = true;
|
||
}
|
||
}
|
||
|
||
// 账户池未启用或无可用商户号时,回退到系统配置
|
||
if (!$wechatAvailable) {
|
||
$wechatConfig = \app\common\model\config\System::getConfig('wechat');
|
||
if(!in_array($platform, $wechatConfig['platform'] ?? [])){
|
||
//微信支付未开启当前平台
|
||
continue;
|
||
}
|
||
$wechatAvailable = true;
|
||
}
|
||
|
||
if (!$wechatAvailable) {
|
||
continue;
|
||
}
|
||
}
|
||
|
||
if($item == 'alipay'){
|
||
$platform = \app\common\library\Platform::getPlatform();
|
||
$alipayAvailable = false;
|
||
|
||
// 检查支付账户池是否有可用商户号(仅验证商户号有效性,平台可用性统一由系统配置决定)
|
||
if (\app\admin\library\project\App::isInstall('paymentpool')
|
||
&& \app\common\library\app\paymentpool\Service::isEnable()) {
|
||
$activeAccount = \app\common\library\app\paymentpool\Service::getActiveAccount('alipay');
|
||
if ($activeAccount && !empty($activeAccount['config']['app_id'])) {
|
||
$alipayAvailable = true;
|
||
}
|
||
}
|
||
|
||
// 账户池未启用或无可用商户号时,回退到系统配置
|
||
if (!$alipayAvailable) {
|
||
$alipayConfig = \app\common\model\config\System::getConfig('alipay');
|
||
if(empty($alipayConfig) || empty($alipayConfig['app_id'])){
|
||
continue;
|
||
}
|
||
if(!in_array($platform, $alipayConfig['platform'] ?? ['wxOfficialAccount','H5'])){
|
||
continue;
|
||
}
|
||
$alipayAvailable = true;
|
||
}
|
||
|
||
if (!$alipayAvailable) {
|
||
continue;
|
||
}
|
||
}
|
||
|
||
if($item == 'douyinpay'){
|
||
if(\app\common\library\Platform::getPlatform() != 'dyMiniProgram'){
|
||
continue;
|
||
}
|
||
}
|
||
|
||
if($item == 'virtual_pay'){
|
||
$virtualPayConfig = \app\common\model\config\System::getConfig('virtual_pay');
|
||
if(empty($virtualPayConfig) || ($virtualPayConfig['status'] ?? 'close') !== 'open'){
|
||
continue;
|
||
}
|
||
if(\app\common\library\Platform::getPlatform() != 'wxMiniProgram'){
|
||
continue;
|
||
}
|
||
}
|
||
|
||
|
||
$data[] = $temp;
|
||
}
|
||
|
||
|
||
$this->success('获取成功', $data);
|
||
}
|
||
|
||
/**
|
||
* 支付宝网页支付
|
||
*/
|
||
public function alipay()
|
||
{
|
||
$orderNo = $this->request->get('orderNo');
|
||
$order = Order::where('orderNo', $orderNo)->find();
|
||
|
||
try {
|
||
if (!$order) {
|
||
throw new \Exception("订单不存在");
|
||
}
|
||
if ($order->status > 0) {
|
||
throw new \Exception("订单已支付");
|
||
}
|
||
if ($order->status < 0) {
|
||
throw new \Exception("订单已失效");
|
||
}
|
||
|
||
$notify_url = $this->request->root(true) . '/pay/notifyx/pay_type/alipay/platform/H5/uniacid/'.UNIACID;
|
||
$pay = new \app\common\library\pay\PayService('alipay', 'url', $notify_url);
|
||
|
||
$order_data = [
|
||
'out_trade_no' => $order->orderNo,
|
||
'total_fee' => $order->total_fee,
|
||
'subject' => '商城订单支付',
|
||
];
|
||
|
||
$result = $pay->create($order_data);
|
||
|
||
$result = $result->getContent();
|
||
|
||
echo $result;
|
||
} catch (\Exception $e) {
|
||
echo $e->getMessage();
|
||
}
|
||
|
||
}
|
||
|
||
|
||
/**
|
||
* 拉起支付
|
||
*/
|
||
public function handle()
|
||
{
|
||
$orderNo = $this->request->post('order_no');
|
||
$pay_type = $this->request->post('pay_type');
|
||
$openid = $this->request->post('openid', '');
|
||
$code = $this->request->post('code', '');
|
||
$platform = request()->header('platform');
|
||
|
||
$order = Order::where([
|
||
'order_no'=>$orderNo,
|
||
'status'=>'unpaid'
|
||
])->find();
|
||
if (!$order) {
|
||
throw new Exception("订单不存在");
|
||
}
|
||
|
||
if (!$pay_type || !in_array($pay_type, ['wechat', 'douyinpay', 'alipay', 'balance', 'virtual_pay'])) {
|
||
throw new Exception("支付类型不能为空");
|
||
}
|
||
|
||
if($order['user_id'] != $this->auth->id){
|
||
throw new Exception("无法支付此订单");
|
||
}
|
||
|
||
// 购买课程前表单支付校验(防止绕过前端直接调支付)
|
||
if (\app\admin\library\project\App::isInstall('form')) {
|
||
$orderItems = \app\common\model\order\Item::getItemList($orderNo);
|
||
foreach ($orderItems as $item) {
|
||
// 通过 item_id 匹配,不按 goods_type(课程类商品的 goods_type 是 course,
|
||
// 而 bind 表存储的实际类型如 column/video 等,直接匹配会查不到)
|
||
$bindForms = \app\admin\model\app\form\Bind::alias('bind')
|
||
->join('app_form f', 'bind.form_id = f.id')
|
||
->where('bind.content_id', $item['item_id'])
|
||
->where('f.bind_type', 'before_buy')
|
||
->where('f.status', 1)
|
||
->field('bind.form_id, f.name')
|
||
->select();
|
||
|
||
if (!empty($bindForms)) {
|
||
foreach ($bindForms as $bindForm) {
|
||
$submitted = \app\admin\model\app\form\Log::where([
|
||
'form_id' => $bindForm['form_id'],
|
||
'user_id' => $this->auth->id
|
||
])->count();
|
||
|
||
if (!$submitted) {
|
||
throw new Exception("请先完成购买前表单:{$bindForm['name']}");
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if ($pay_type == 'balance') {
|
||
$this->balancePay($order, $pay_type, $platform);
|
||
}
|
||
|
||
$isVirtualPay = !empty($order['is_virtual_pay']) && $pay_type !== 'balance';
|
||
if ($isVirtualPay) {
|
||
$pay_type = 'virtual_pay';
|
||
}
|
||
|
||
$order_data = [
|
||
'out_trade_no' => $order->order_no,
|
||
'total_fee' => $order->real_price,
|
||
];
|
||
|
||
// 微信公众号,小程序支付,必须有 openid
|
||
|
||
switch ($pay_type){
|
||
case 'wechat':
|
||
if (in_array($platform, ['wxOfficialAccount', 'wxMiniProgram'])) {
|
||
if (isset($openid) && $openid) {
|
||
// 如果传的有 openid
|
||
$order_data['openid'] = $openid;
|
||
} else {
|
||
// 没有 openid 默认拿下单人的 openid
|
||
$oauth = \app\common\model\user\Oauth::where([
|
||
'user_id' => $order->user_id,
|
||
'provider' => 'Wechat',
|
||
'platform' => $platform
|
||
])->find();
|
||
|
||
$order_data['openid'] = $oauth ? $oauth->openid : '';
|
||
}
|
||
|
||
if (empty($order_data['openid'])) {
|
||
// 缺少 openid
|
||
return $this->success('缺少 openid', 'no_openid');
|
||
}
|
||
}
|
||
|
||
$order_data['body'] = '商城订单支付';
|
||
break;
|
||
case 'douyinpay':
|
||
if (!$openid) {
|
||
// 缺少 openid
|
||
return $this->success('缺少 openid', 'no_openid');
|
||
}
|
||
$order_data['openid'] = $openid;
|
||
break;
|
||
case 'virtual_pay':
|
||
if ($openid) {
|
||
$oauth = \app\common\model\user\Oauth::where([
|
||
'openid' => $openid,
|
||
'provider' => 'Wechat',
|
||
'platform' => 'wxMiniProgram'
|
||
])->find();
|
||
} else {
|
||
$oauth = \app\common\model\user\Oauth::where([
|
||
'user_id' => $order->user_id,
|
||
'provider' => 'Wechat',
|
||
'platform' => 'wxMiniProgram'
|
||
])->find();
|
||
}
|
||
|
||
if (!$oauth || empty($oauth->openid)) {
|
||
return $this->success('缺少openid', 'no_openid');
|
||
}
|
||
|
||
$resumeResult = $this->checkExistingVirtualPayOrder($order, $oauth->openid, $isVirtualPay ? 1 : 0);
|
||
if ($resumeResult !== null) {
|
||
return $resumeResult;
|
||
}
|
||
|
||
if (empty($code)) {
|
||
return $this->success('请先登录后再支付', 'no_session_key');
|
||
}
|
||
|
||
$wechat = new \app\common\library\Wechat('wxMiniProgram');
|
||
$session = $wechat->code($code);
|
||
if (empty($session['session_key'])) {
|
||
return $this->success('获取登录信息失败:' . ($session['errmsg'] ?? '未知错误'), 'no_session_key');
|
||
}
|
||
|
||
if (!empty($session['openid']) && $session['openid'] !== $oauth->openid) {
|
||
return $this->success('微信登录态与当前用户不一致,请重新登录', 'no_session_key');
|
||
}
|
||
|
||
$sessionKey = $session['session_key'];
|
||
$oauth->session_key = $sessionKey;
|
||
$oauth->expiretime = time() + 7200;
|
||
$oauth->save();
|
||
|
||
$order_data['openid'] = $oauth->openid;
|
||
$order_data['session_key'] = $sessionKey;
|
||
$order_data['mode'] = $this->request->post('mode', 'short_series_coin');
|
||
$order_data['product_id'] = $this->request->post('product_id', '');
|
||
|
||
\app\common\model\order\VirtualPay::saveRelation($order->order_no, $oauth->openid);
|
||
break;
|
||
default:
|
||
$order_data['subject'] = '商城订单支付';
|
||
}
|
||
|
||
$notify_url = $this->getNotifyUrl($pay_type,$platform,$openid);
|
||
|
||
if ($pay_type == 'alipay') {
|
||
$userAgent = strtolower($this->request->server('HTTP_USER_AGENT', ''));
|
||
$isMobile = preg_match('/(mobile|android|iphone|ipad|ipod|windows phone)/i', $userAgent);
|
||
if (!$isMobile) {
|
||
$platform = 'pc';
|
||
}
|
||
}
|
||
|
||
try{
|
||
$payResult = $this->createPaymentWithPool($pay_type, $platform, $notify_url, $order_data, $order);
|
||
}catch (\Throwable $e){
|
||
// 使用 Throwable 捕获 \Error 和 \Exception(PHP 7+ openssl_sign 等可能抛 Error)
|
||
// 内部错误(如 openssl_sign 私钥错误)不直接对外暴露,统一返回通用提示
|
||
throw new Exception('支付遇到错误,请重试');
|
||
}
|
||
|
||
$result = $payResult['result'];
|
||
|
||
if ($platform == 'App') {
|
||
$result = $result->getContent();
|
||
}
|
||
if ($pay_type == 'alipay' || ($platform == 'H5' && $pay_type == 'wechat')) {
|
||
$result = $result->getContent();
|
||
}
|
||
$data = [
|
||
'is_virtual_pay' => $isVirtualPay ? 1 : 0,
|
||
'pay_data' => $result
|
||
];
|
||
|
||
return $this->success('拉取支付参数成功', $data);
|
||
}
|
||
|
||
/**
|
||
* 创建支付参数(接入支付账户池失败自动切换重试)
|
||
*
|
||
* 仅 wechat / alipay 类型会触发账户池逻辑;
|
||
* 其它支付方式(douyinpay / virtual_pay 等)保持原有行为。
|
||
*
|
||
* 流程:
|
||
* 1. 若账户池未启用,则直接调用一次 PayService::create();
|
||
* 2. 若启用,则循环选取"使用中"/"暂未启用"商户号尝试发起,
|
||
* 返回错误码时标记该商户号为"不可用"并切换到下一个,
|
||
* 成功后绑定订单与商户号关系并累加使用次数;
|
||
* 3. 全部商户号都失败时回退到系统配置(即不传 forceAccountId)。
|
||
*
|
||
* @param string $pay_type
|
||
* @param string $platform
|
||
* @param string $notify_url
|
||
* @param array $order_data
|
||
* @param \app\common\model\order\Order $order
|
||
* @return array ['result' => mixed, 'account_id' => int|null]
|
||
*/
|
||
private function createPaymentWithPool($pay_type, $platform, $notify_url, $order_data, $order)
|
||
{
|
||
// 非微信/支付宝支付直接走系统配置
|
||
if (!in_array($pay_type, ['wechat', 'alipay'], true)) {
|
||
$pay = new \app\common\library\pay\PayService($pay_type, $platform, $notify_url);
|
||
$result = $pay->create($order_data);
|
||
return ['result' => $result, 'account_id' => null];
|
||
}
|
||
|
||
$poolEnable = \app\admin\library\project\App::isInstall('paymentpool')
|
||
&& \app\common\library\app\paymentpool\Service::isEnable();
|
||
if (!$poolEnable) {
|
||
// 账户池未启用,按原流程使用系统配置
|
||
$pay = new \app\common\library\pay\PayService($pay_type, $platform, $notify_url);
|
||
$result = $pay->create($order_data);
|
||
return ['result' => $result, 'account_id' => null];
|
||
}
|
||
|
||
// 账户池启用:循环切换商户号尝试
|
||
$excludeIds = [];
|
||
$lastError = '';
|
||
$maxRetry = 5;
|
||
|
||
for ($i = 0; $i < $maxRetry; $i++) {
|
||
$account = \app\common\library\app\paymentpool\Service::chooseNextAccount($pay_type, $excludeIds);
|
||
if (!$account) {
|
||
break;
|
||
}
|
||
|
||
try {
|
||
$pay = new \app\common\library\pay\PayService($pay_type, $platform, $notify_url, $account['id']);
|
||
$result = $pay->create($order_data);
|
||
|
||
// 校验返回结果中是否包含错误码(微信返回数组 / 支付宝返回 Response)
|
||
$errInfo = $this->detectPayError($pay_type, $result);
|
||
if ($errInfo) {
|
||
// 标记不可用 + 切换
|
||
\app\common\library\app\paymentpool\Service::markInvalid(
|
||
$account['id'],
|
||
$errInfo['code'],
|
||
$errInfo['msg'],
|
||
$order['order_no']
|
||
);
|
||
$excludeIds[] = $account['id'];
|
||
$lastError = $errInfo['msg'];
|
||
continue;
|
||
}
|
||
|
||
// 成功:绑定订单和累加使用次数
|
||
\app\common\library\app\paymentpool\Service::bindOrder($order['order_no'], $account['id'], $pay_type);
|
||
\app\common\library\app\paymentpool\Service::incrUseCount($account['id']);
|
||
|
||
return ['result' => $result, 'account_id' => $account['id']];
|
||
} catch (\Throwable $e) {
|
||
// 调用异常:标记该商户号不可用,继续切换
|
||
// 使用 Throwable 捕获 \Error 和 \Exception(PHP 7+ openssl_sign 等可能抛 Error)
|
||
\app\common\library\app\paymentpool\Service::markInvalid(
|
||
$account['id'],
|
||
'EXCEPTION',
|
||
$e->getMessage(),
|
||
$order['order_no']
|
||
);
|
||
$excludeIds[] = $account['id'];
|
||
$lastError = $e->getMessage();
|
||
}
|
||
}
|
||
|
||
// 账户池全部失败,回退到系统配置兜底
|
||
try {
|
||
$pay = new \app\common\library\pay\PayService($pay_type, $platform, $notify_url);
|
||
$result = $pay->create($order_data);
|
||
return ['result' => $result, 'account_id' => null];
|
||
} catch (\Throwable $e) {
|
||
// 必须抛出 PHP 原生 \Exception,让外层 handle() 的 catch 能拦截到,
|
||
// 避免 app\common\exception\Exception 构造函数直接 send 响应导致原始错误外泄
|
||
throw new \Exception($lastError ?: $e->getMessage());
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 检测 PayService::create() 返回结果中是否包含错误码
|
||
*
|
||
* @param string $pay_type wechat|alipay
|
||
* @param mixed $result
|
||
* @return array|null ['code' => string, 'msg' => string],无错误返回 null
|
||
*/
|
||
private function detectPayError($pay_type, $result)
|
||
{
|
||
if ($pay_type === 'wechat') {
|
||
// Yansongda 微信 mp/miniapp 返回 Collection(数组式),异常时含 return_code=FAIL
|
||
$data = is_object($result) && method_exists($result, 'all') ? $result->all() : (array)$result;
|
||
if (isset($data['return_code']) && $data['return_code'] !== 'SUCCESS') {
|
||
return [
|
||
'code' => $data['err_code'] ?? ($data['return_code'] ?? 'FAIL'),
|
||
'msg' => $data['err_code_des'] ?? ($data['return_msg'] ?? '微信支付下单失败'),
|
||
];
|
||
}
|
||
if (isset($data['result_code']) && $data['result_code'] !== 'SUCCESS') {
|
||
return [
|
||
'code' => $data['err_code'] ?? 'FAIL',
|
||
'msg' => $data['err_code_des'] ?? '微信支付下单失败',
|
||
];
|
||
}
|
||
return null;
|
||
}
|
||
|
||
if ($pay_type === 'alipay') {
|
||
// 支付宝 wap/web 正常返回 Response 对象(HTML 表单),不会带错误码;
|
||
// 异常时 SDK 会抛出 GatewayException,在 catch 中处理。
|
||
// 但部分错误(如 INVALID_PARAMETER)可能以数组形式返回,需兼容处理。
|
||
$data = is_object($result) && method_exists($result, 'toArray')
|
||
? $result->toArray()
|
||
: (is_array($result) ? $result : null);
|
||
if (is_array($data)) {
|
||
// 兼容支付宝错误响应结构
|
||
$response = $data['alipay_trade_wap_pay_response']
|
||
?? $data['alipay_trade_page_pay_response']
|
||
?? $data;
|
||
if (isset($response['code']) && $response['code'] !== '10000') {
|
||
return [
|
||
'code' => $response['code'],
|
||
'msg' => $response['sub_msg'] ?? ($response['msg'] ?? '支付宝支付下单失败'),
|
||
];
|
||
}
|
||
}
|
||
return null;
|
||
}
|
||
|
||
return null;
|
||
}
|
||
|
||
/**
|
||
* 历史订单再次发起虚拟支付前,先检查微信侧是否已存在同订单号的支付单。
|
||
* 命中旧单时不再继续返回 requestVirtualPayment 参数,避免触发 ORDER_NO_DUP。
|
||
* @param Order $order
|
||
* @param string $openid
|
||
* @param int $isVirtualPay
|
||
* @return mixed|null
|
||
*/
|
||
private function checkExistingVirtualPayOrder($order, $openid, $isVirtualPay)
|
||
{
|
||
try {
|
||
$virtualPayService = new VirtualPayService();
|
||
$queryResult = $virtualPayService->queryOrder($openid, $order->order_no);
|
||
} catch (\Exception $e) {
|
||
return null;
|
||
}
|
||
|
||
if (isset($queryResult['errcode']) && $queryResult['errcode'] !== 0) {
|
||
return null;
|
||
}
|
||
|
||
$wxOrder = $queryResult['order'] ?? [];
|
||
$status = intval($wxOrder['status'] ?? -1);
|
||
|
||
if (in_array($status, [2, 3, 4], true)) {
|
||
return $this->error('订单已存在微信支付记录,请刷新订单状态');
|
||
}
|
||
|
||
if ($status >= 0) {
|
||
return $this->error('该订单已存在微信侧支付单,请先查询支付结果;若需重新支付,请重新下单');
|
||
}
|
||
|
||
return null;
|
||
}
|
||
|
||
public function getNotifyUrl($pay_type,$platform,$openId){
|
||
global $_W;
|
||
if(\app\common\library\Platform::getSystemType() == 'single'){
|
||
return MODULE_URL .'/api/pay/notifyx/pay_type/' . $pay_type . '/platform/' . $platform.'/open_id/' . $openId."/uniacid/".UNIACID;
|
||
}else{
|
||
return $this->request->domain() .'/addons/tuzi_v1/payment/notify/wechat.php/pay_type/' . $pay_type.'/open_id/' . $openId . '/platform/' . $platform."/uniacid/".UNIACID;
|
||
}
|
||
|
||
}
|
||
|
||
|
||
// 余额支付
|
||
public function balancePay ($order, $type, $method) {
|
||
$order = Db::transaction(function () use ($order, $type, $method) {
|
||
$balanceLibrary = new Balance();
|
||
|
||
$userInfo = $balanceLibrary->getOrderUser($order['order_no']);
|
||
|
||
if(!$userInfo){
|
||
throw new Exception('获取用户信息失败');
|
||
}
|
||
|
||
//余额不足
|
||
if($userInfo['money'] < $order['real_price']){
|
||
throw new Exception('余额不足');
|
||
}
|
||
|
||
User::money(($order['real_price'] * -1), $userInfo['id'], "余额支付");
|
||
|
||
// 支付后流程
|
||
$notify = [
|
||
'order_no' => $order['order_no'],
|
||
'transaction_id' => '',
|
||
'pay_time'=>time(),
|
||
'real_pay_price' => $order['real_price'],
|
||
'pay_type' => 'balance' // 支付方式
|
||
];
|
||
$notify['payment_json'] = json_encode($notify);
|
||
$order->paySuccess($order, $notify);
|
||
|
||
return $order;
|
||
});
|
||
|
||
$this->success('支付成功', $order);
|
||
}
|
||
|
||
|
||
/**
|
||
* 获取回调中的参数
|
||
* @return array
|
||
*/
|
||
public function getNotifyParams(){
|
||
$paramArr = explode('/', $this->request->pathinfo());
|
||
|
||
|
||
$result = [];
|
||
for ($i = 0; $i < count($paramArr); $i += 2) {
|
||
$key = $paramArr[$i];
|
||
$value = isset($paramArr[$i + 1]) ? $paramArr[$i + 1] : null;
|
||
$result[$key] = $value;
|
||
}
|
||
return $result;
|
||
}
|
||
|
||
/**
|
||
* 支付成功回调
|
||
*/
|
||
public function notifyx()
|
||
{
|
||
$params = $this->request->param(true);
|
||
$pay_type = isset($params['pay_type']) ? $params['pay_type'] : '';
|
||
$platform = isset($params['platform']) ? $params['platform'] : '';
|
||
|
||
$pay = new \app\common\library\pay\PayService($pay_type, $platform);
|
||
|
||
if($pay_type == 'douyinpay'){
|
||
$result = 'false';
|
||
try{
|
||
$openId = isset($params['open_id']) ? $params['open_id'] : '';
|
||
$content = file_get_contents('php://input');
|
||
if(empty($content)) return false;
|
||
|
||
$paymentConfig = \app\common\model\config\System::getConfig($pay_type);
|
||
$paymentConfig['notify_url'] = $this->getNotifyUrl($pay_type,$platform,$openId);
|
||
$content=json_decode($content,true);
|
||
|
||
$sign = \douyin\Byte::init($paymentConfig)->verifySignature($paymentConfig['rsa_public_key']);
|
||
|
||
if($sign){
|
||
$data = json_decode($content['msg'],true);
|
||
$data['open_id'] = $openId;
|
||
$result = $this->successHandle($data, $pay, $pay_type, $platform);
|
||
}
|
||
} catch (\Exception $e) {
|
||
}
|
||
|
||
|
||
}else{
|
||
$result = $pay->notify(function ($data, $pay) use ($pay_type, $platform) {
|
||
$this->successHandle($data, $pay, $pay_type, $platform);
|
||
});
|
||
}
|
||
|
||
return $result;
|
||
}
|
||
|
||
/**
|
||
* 支付成功回调处理
|
||
* @param $data
|
||
* @param $pay
|
||
* @param $pay_type
|
||
* @param $platform
|
||
* @return false|string
|
||
*/
|
||
public function successHandle($data, $pay,$pay_type, $platform){
|
||
try {
|
||
switch ($pay_type){
|
||
case 'wechat':
|
||
$pay_fee = $data['cash_fee'] / 100;
|
||
$orderNo = $data['out_trade_no'];
|
||
$transactionId = $data['transaction_id'];
|
||
break;
|
||
case 'douyinpay':
|
||
$pay_fee = $data['total_amount'] / 100;
|
||
$orderNo = $data['out_order_no'];
|
||
$transactionId = $data['order_id'];
|
||
break;
|
||
case 'alipay':
|
||
$dataArr = is_object($data) ? $data->toArray() : $data;
|
||
$pay_fee = isset($dataArr['total_amount']) ? $dataArr['total_amount'] : 0;
|
||
$orderNo = isset($dataArr['out_trade_no']) ? $dataArr['out_trade_no'] : '';
|
||
$transactionId = isset($dataArr['trade_no']) ? $dataArr['trade_no'] : '';
|
||
break;
|
||
}
|
||
|
||
$order = Order::where('order_no', $orderNo)->find();
|
||
|
||
if (!$order || $order->status != \app\common\constant\order\Status::STATUS_UNPAID) {
|
||
if (!$order && strpos($orderNo, 'TEST') === 0
|
||
&& \app\admin\library\project\App::isInstall('paymentpool')) {
|
||
$testRecord = \app\admin\model\app\paymentpool\Test::where('test_no', $orderNo)->find();
|
||
if ($testRecord && $testRecord->status == \app\admin\model\app\paymentpool\Test::STATUS_PENDING) {
|
||
$testRecord->status = \app\admin\model\app\paymentpool\Test::STATUS_SUCCESS;
|
||
$testRecord->save();
|
||
}
|
||
}
|
||
return $pay->success()->send();
|
||
}
|
||
|
||
Db::transaction(function () use ($order,$orderNo,$transactionId, $data, $pay_type, $platform, $pay_fee) {
|
||
$paymentJson = $pay_type == 'douyinpay' ? json_encode($data) : (is_object($data) ? json_encode($data->toArray()) : json_encode($data));
|
||
$notify = [
|
||
'order_no' => $orderNo,
|
||
'pay_time'=>time(),
|
||
'transaction_id' => $transactionId,
|
||
'payment_json' => $paymentJson,
|
||
'real_pay_price' => $pay_fee,
|
||
'pay_type' => $pay_type
|
||
];
|
||
$order->paySuccess($order, $notify);
|
||
});
|
||
|
||
if($pay_type == 'douyinpay'){
|
||
return '{"err_no": 0,"err_tips": "success"}';
|
||
}
|
||
return $pay->success()->send();
|
||
} catch (\Exception $e) {
|
||
}
|
||
return false;
|
||
}
|
||
|
||
|
||
/**
|
||
* 退款成功回调
|
||
*/
|
||
public function notifyr()
|
||
{
|
||
|
||
$params = $this->request->param(true);
|
||
|
||
$pay_type = $params['pay_type'];
|
||
$platform = $params['platform'];
|
||
|
||
$pay = new \app\common\library\pay\PayService($pay_type, $platform);
|
||
|
||
if($pay_type == 'douyinpay'){
|
||
$result = 'false';
|
||
// try{
|
||
// $content = file_get_contents('php://input');
|
||
// if(empty($content)) return '{"err_no": 0,"err_tips": "success"}';
|
||
|
||
// $paymentConfig = \app\common\model\config\System::getConfig($pay_type);
|
||
// $paymentConfig['notify_url'] = $this->getNotifyUrl($pay_type,$platform,$openId);
|
||
// $content=json_decode($content,true);
|
||
// $sign = \douyin\Byte::init($paymentConfig)->checkSign($content);
|
||
//
|
||
// if($sign == $content['msg_signature']){
|
||
// $data = json_decode($content['msg'],true);
|
||
// $data['open_id'] = $openId;
|
||
// $result = $this->successHandle($data, $pay, $pay_type, $platform);
|
||
// }
|
||
// } catch (\Exception $e) {
|
||
// file_put_contents("./err1.txt",$e->getMessage());
|
||
// }
|
||
|
||
|
||
return '{"err_no": 0,"err_tips": "success"}';
|
||
|
||
}else{
|
||
$result = $pay->notifyRefund(function ($data, $pay) use ($pay_type, $platform) {
|
||
try {
|
||
$out_refund_no = $data['out_refund_no'];
|
||
$out_trade_no = $data['out_trade_no'];
|
||
// 编写退款逻辑
|
||
$order = Order::where([
|
||
'order_no'=> $out_trade_no
|
||
])->find();
|
||
$refundLog = \app\admin\model\order\RefundLog::where([
|
||
'refund_no'=> $out_refund_no
|
||
])->order("id","desc")->find();
|
||
|
||
file_put_contents("./refund_fail.txt",json_encode([!$order , !$refundLog , $refundLog['status']])."\n\n",FILE_APPEND);
|
||
|
||
if (!$order || !$refundLog || $refundLog['status'] != 0) {
|
||
// 订单不存在,或者订单已支付
|
||
return $pay->success()->send();
|
||
}
|
||
|
||
$item = \app\common\model\order\Item::where('id', $refundLog['order_item_id'])->find();
|
||
|
||
Db::transaction(function () use ($order, $item, $refundLog) {
|
||
//退款成功
|
||
\app\admin\model\order\Refund::refundFinish($order, $item, $refundLog);
|
||
});
|
||
|
||
return $pay->success()->send();
|
||
} catch (\Exception $e) {
|
||
file_put_contents("./refund_fail.txt",$e->getMessage()."\n\n",FILE_APPEND);
|
||
}
|
||
});
|
||
|
||
return $result;
|
||
}
|
||
|
||
|
||
|
||
}
|
||
|
||
|
||
}
|