|
|
@@ -21,6 +21,11 @@ namespace app\common\service\pay;
|
|
|
|
|
|
use app\common\enum\AccountLogEnum;
|
|
|
use app\common\enum\PayEnum;
|
|
|
+use app\common\enum\AfterSaleEnum;
|
|
|
+use app\common\enum\AfterSaleLogEnum;
|
|
|
+use app\common\enum\NoticeEnum;
|
|
|
+use app\common\model\AfterSale;
|
|
|
+use app\common\service\after_sale\AfterSaleService;
|
|
|
use app\common\logic\AccountLogLogic;
|
|
|
use app\common\model\User;
|
|
|
use think\facade\Db;
|
|
|
@@ -191,42 +196,139 @@ class MixedPayService extends BasePayService
|
|
|
*/
|
|
|
public function refund($order, $refundAmount, $afterSaleId)
|
|
|
{
|
|
|
+ Db::startTrans();
|
|
|
try {
|
|
|
+ // 记录退款日志
|
|
|
+ file_put_contents(
|
|
|
+ runtime_path() . 'log' . DIRECTORY_SEPARATOR . 'mixed_refund_debug_' . date('Y-m-d') . '.log',
|
|
|
+ "[" . date('Y-m-d H:i:s') . "] 混合支付退款开始 - 订单号: {$order['sn']}, 退款金额: {$refundAmount}" . PHP_EOL,
|
|
|
+ FILE_APPEND | LOCK_EX
|
|
|
+ );
|
|
|
+
|
|
|
// 获取订单的余额支付金额和微信支付金额
|
|
|
$balanceAmount = $order['balance_amount'] ?? 0;
|
|
|
$wechatAmount = $order['order_amount'] - $balanceAmount;
|
|
|
|
|
|
- // 按比例退款
|
|
|
- if ($refundAmount >= $order['order_amount']) {
|
|
|
- // 全额退款
|
|
|
- $refundBalanceAmount = $balanceAmount;
|
|
|
- $refundWechatAmount = $wechatAmount;
|
|
|
- } else {
|
|
|
- // 部分退款,按比例分配
|
|
|
- $refundRatio = $refundAmount / $order['order_amount'];
|
|
|
- $refundBalanceAmount = $balanceAmount * $refundRatio;
|
|
|
- $refundWechatAmount = $wechatAmount * $refundRatio;
|
|
|
+ // 计算退款比例
|
|
|
+ $refundRatio = $refundAmount / $order['order_amount'];
|
|
|
+ $refundBalanceAmount = round($balanceAmount * $refundRatio, 2);
|
|
|
+ $refundWechatAmount = round($wechatAmount * $refundRatio, 2);
|
|
|
+
|
|
|
+ // 确保退款金额精确
|
|
|
+ $totalCalculated = $refundBalanceAmount + $refundWechatAmount;
|
|
|
+ if ($totalCalculated != $refundAmount) {
|
|
|
+ $diff = $refundAmount - $totalCalculated;
|
|
|
+ if ($refundWechatAmount > 0) {
|
|
|
+ $refundWechatAmount += $diff;
|
|
|
+ } else {
|
|
|
+ $refundBalanceAmount += $diff;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- // 退回余额
|
|
|
+ file_put_contents(
|
|
|
+ runtime_path() . 'log' . DIRECTORY_SEPARATOR . 'mixed_refund_debug_' . date('Y-m-d') . '.log',
|
|
|
+ "[" . date('Y-m-d H:i:s') . "] 退款分配 - 余额退款: {$refundBalanceAmount}, 微信退款: {$refundWechatAmount}" . PHP_EOL,
|
|
|
+ FILE_APPEND | LOCK_EX
|
|
|
+ );
|
|
|
+
|
|
|
+ // 处理余额退款
|
|
|
if ($refundBalanceAmount > 0) {
|
|
|
- $this->balancePayService->refund($order, $refundBalanceAmount, $afterSaleId);
|
|
|
+ // 返回余额
|
|
|
+ User::update([
|
|
|
+ 'user_money' => ['inc', $refundBalanceAmount]
|
|
|
+ ], ['id' => $order['user_id']]);
|
|
|
+
|
|
|
+ // 记录余额流水
|
|
|
+ $afterSale = AfterSale::findOrEmpty($afterSaleId);
|
|
|
+ AccountLogLogic::add(
|
|
|
+ $order['user_id'],
|
|
|
+ AccountLogEnum::BNW_INC_AFTER_SALE,
|
|
|
+ AccountLogEnum::INC,
|
|
|
+ $refundBalanceAmount,
|
|
|
+ $afterSale->sn,
|
|
|
+ '混合支付退款-余额部分'
|
|
|
+ );
|
|
|
+
|
|
|
+ file_put_contents(
|
|
|
+ runtime_path() . 'log' . DIRECTORY_SEPARATOR . 'mixed_refund_debug_' . date('Y-m-d') . '.log',
|
|
|
+ "[" . date('Y-m-d H:i:s') . "] 余额退款完成: {$refundBalanceAmount}" . PHP_EOL,
|
|
|
+ FILE_APPEND | LOCK_EX
|
|
|
+ );
|
|
|
}
|
|
|
|
|
|
- // 微信退款
|
|
|
+ // 处理微信退款
|
|
|
if ($refundWechatAmount > 0) {
|
|
|
- // 这里需要调用微信退款接口
|
|
|
$refundData = [
|
|
|
- 'out_trade_no' => $order['sn'],
|
|
|
- 'out_refund_no' => 'refund_' . $order['sn'] . '_' . time(),
|
|
|
- 'total_fee' => $wechatAmount * 100, // 原订单金额(分)
|
|
|
- 'refund_fee' => $refundWechatAmount * 100, // 退款金额(分)
|
|
|
+ 'transaction_id' => $order['transaction_id'],
|
|
|
+ 'refund_sn' => 'mixed_refund_' . $order['sn'] . '_' . time(),
|
|
|
+ 'total_fee' => $wechatAmount,
|
|
|
+ 'refund_fee' => $refundWechatAmount,
|
|
|
];
|
|
|
- $this->wechatPayService->refund($refundData);
|
|
|
+
|
|
|
+ $result = $this->wechatPayService->refund($refundData);
|
|
|
+ if ($result !== true) {
|
|
|
+ throw new \Exception('微信退款失败:' . $this->wechatPayService->getError());
|
|
|
+ }
|
|
|
+
|
|
|
+ file_put_contents(
|
|
|
+ runtime_path() . 'log' . DIRECTORY_SEPARATOR . 'mixed_refund_debug_' . date('Y-m-d') . '.log',
|
|
|
+ "[" . date('Y-m-d H:i:s') . "] 微信退款完成: {$refundWechatAmount}" . PHP_EOL,
|
|
|
+ FILE_APPEND | LOCK_EX
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ // 更新售后状态
|
|
|
+ $afterSale = AfterSale::findOrEmpty($afterSaleId);
|
|
|
+ if (!$afterSale->isEmpty()) {
|
|
|
+ // 判断退款状态
|
|
|
+ $refundStatus = ($refundAmount >= $order['order_amount']) ?
|
|
|
+ AfterSaleEnum::FULL_REFUND : AfterSaleEnum::PARTIAL_REFUND;
|
|
|
+
|
|
|
+ $afterSale->status = AfterSaleEnum::STATUS_SUCCESS;
|
|
|
+ $afterSale->sub_status = AfterSaleEnum::SUB_STATUS_SELLER_REFUND_SUCCESS;
|
|
|
+ $afterSale->refund_status = $refundStatus;
|
|
|
+ $afterSale->save();
|
|
|
+
|
|
|
+ // 添加售后日志
|
|
|
+ AfterSaleService::createAfterLog(
|
|
|
+ $afterSale->id,
|
|
|
+ '系统已完成混合支付退款',
|
|
|
+ 0,
|
|
|
+ AfterSaleLogEnum::ROLE_SYS
|
|
|
+ );
|
|
|
+
|
|
|
+ // 发送退款成功通知
|
|
|
+ event('Notice', [
|
|
|
+ 'scene_id' => NoticeEnum::REFUND_SUCCESS_NOTICE,
|
|
|
+ 'params' => [
|
|
|
+ 'user_id' => $afterSale->user_id,
|
|
|
+ 'order_sn' => $order['sn'],
|
|
|
+ 'after_sale_sn' => $afterSale->sn,
|
|
|
+ 'refund_type' => AfterSaleEnum::getRefundTypeDesc($afterSale->refund_type),
|
|
|
+ 'refund_total_amount' => $afterSale->refund_total_amount,
|
|
|
+ 'refund_time' => date('Y-m-d H:i:s'),
|
|
|
+ ]
|
|
|
+ ]);
|
|
|
}
|
|
|
|
|
|
+ file_put_contents(
|
|
|
+ runtime_path() . 'log' . DIRECTORY_SEPARATOR . 'mixed_refund_debug_' . date('Y-m-d') . '.log',
|
|
|
+ "[" . date('Y-m-d H:i:s') . "] 混合支付退款成功" . PHP_EOL,
|
|
|
+ FILE_APPEND | LOCK_EX
|
|
|
+ );
|
|
|
+
|
|
|
+ Db::commit();
|
|
|
return true;
|
|
|
+
|
|
|
} catch (\Exception $e) {
|
|
|
+ Db::rollback();
|
|
|
+
|
|
|
+ file_put_contents(
|
|
|
+ runtime_path() . 'log' . DIRECTORY_SEPARATOR . 'mixed_refund_debug_' . date('Y-m-d') . '.log',
|
|
|
+ "[" . date('Y-m-d H:i:s') . "] 混合支付退款失败: " . $e->getMessage() . PHP_EOL,
|
|
|
+ FILE_APPEND | LOCK_EX
|
|
|
+ );
|
|
|
+
|
|
|
$this->setError($e->getMessage());
|
|
|
return false;
|
|
|
}
|