2025-10-28 16:08:12 +08:00

398 lines
11 KiB
PHP
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace addons\shopro\service\pay;
use think\Log;
use think\Db;
use addons\shopro\library\pay\PayService;
use app\admin\model\shopro\Pay as PayModel;
use app\admin\model\shopro\user\User;
use app\admin\model\shopro\Refund as RefundModel;
use addons\shopro\service\Wallet as WalletService;
use addons\shopro\library\ccblife\CcbOrderService;
class PayRefund
{
protected $user = null;
/**
* 实例化
*
* @param mixed $user
*/
public function __construct($user = null)
{
// 优先使用传入的用户
$this->user = is_numeric($user) ? User::get($user) : $user;
}
/**
* 之前没有经历过任何的退款(刚支付的订单要退款|拼团失败订单等)
*
* @param \think\Model $pay
* @param string $refund_type
* @return void
*/
public function fullRefund($pay, $data = [])
{
$pay->refund_fee = $pay->pay_fee;
$pay->status = PayModel::PAY_STATUS_REFUND; // 直接退款完成
$pay->save();
// 添加退款单
$refund = $this->add($pay, $pay->pay_fee, $data);
// 退款
$refund = $this->return($pay, $refund);
return $refund;
}
/**
* 部分退款,指定退款金额,并且检测 pay 是否已经退款完成
*
* @param \think\Model $pay
* @param float $refund_money
* @param array $data
* @return \think\Model
*/
public function refund($pay, $refund_money, $data = [])
{
$pay->refund_fee = Db::raw('refund_fee + ' . $refund_money);
$pay->save();
// 添加退款单
$refund = $this->add($pay, $refund_money, $data);
// 退款
$refund = $this->return($pay, $refund);
// 检查 pay 是否退款完成,
$this->checkPayAndRefunded($pay);
return $refund;
}
/**
* 微信支付宝退款回调方法
*
* @param array $data
* @return void
*/
public function notify($data)
{
$out_trade_no = $data['out_trade_no'];
$out_refund_no = $data['out_refund_no'];
$payment_json = $data['payment_json'];
$pay = PayModel::where('pay_sn', $out_trade_no)->find();
if (!$pay) {
Log::write('refund-notify-error:paymodel notfound;pay:' . json_encode($data));
return false;
}
if ($pay->order_type == 'order') {
$refund = RefundModel::where('refund_sn', $out_refund_no)->find();
if (!$refund || $refund->status != RefundModel::STATUS_ING) {
// 退款单不存在,或者已退款
return false;
}
$refund = $this->completed($refund, $payment_json);
return true;
} else {
// 如有其他订单类型如果支持退款,逻辑这里补充
}
}
/**
* 退回
*
* @param \think\Model $pay
* @param \think\Model $refund
* @return \think\Model
*/
protected function return($pay, $refund)
{
$method = $refund->refund_method;
if (method_exists($this, $method)) {
$refund = $this->{$method}($pay, $refund);
} else {
error_stop('退款方式不支持');
}
return $refund;
}
/**
* 退余额
*
* @param \think\Model $pay
* @param \think\Model $refund
* @return \think\Model
*/
private function money($pay, $refund)
{
// 退回用户余额
WalletService::change($pay->user_id, 'money', $refund->refund_fee, 'order_refund', [
'refund_id' => $refund->id,
'refund_sn' => $refund->refund_sn,
'pay_id' => $pay->id,
'pay_sn' => $pay->pay_sn,
'order_id' => $pay->order_id,
'order_type' => $pay->order_type,
]);
$refund = $this->completed($refund);
return $refund;
}
/**
* 退积分
*
* @param \think\Model $pay
* @param \think\Model $refund
* @return \think\Model
*/
private function score($pay, $refund)
{
// 退回用户积分
WalletService::change($pay->user_id, 'score', $refund->refund_fee, 'order_refund', [
'refund_id' => $refund->id,
'refund_sn' => $refund->refund_sn,
'pay_id' => $pay->id,
'pay_sn' => $pay->pay_sn,
'order_id' => $pay->order_id,
'order_type' => $pay->order_type,
]);
$refund = $this->completed($refund);
return $refund;
}
/**
* 退 offline
*
* @param \think\Model $pay
* @param \think\Model $refund
* @return \think\Model
*/
private function offline($pay, $refund)
{
// offline 退款啥也不干,钱还是线下退回,线上不处理
$refund = $this->completed($refund);
return $refund;
}
/**
* 退微信
*
* @param \think\Model $pay
* @param \think\Model $refund
* @return \think\Model
*/
private function wechat($pay, $refund)
{
$order_data = [
'out_trade_no' => $pay->pay_sn,
'out_refund_no' => $refund->refund_sn,
'reason' => $refund->remark,
'amount' => [
'refund' => $refund->refund_fee,
'total' => $pay->pay_fee,
'currency' => 'CNY'
]
];
$pay = new PayService('wechat', $refund->platform);
$result = $pay->refund($order_data);
// 微信通知回调 pay->notifyRefund
if (isset($result['status']) && in_array($result['status'], ['SUCCESS', 'PROCESSING'])) {
// 微信返回的状态会是 PROCESSING
return true;
} else {
error_stop('退款失败:' . (isset($result['message']) ? $result['message'] : json_encode($result, JSON_UNESCAPED_UNICODE)));
}
return $refund;
}
/**
* 退支付宝
*
* @param \think\Model $pay
* @param \think\Model $refund
* @return \think\Model
*/
private function alipay($pay, $refund)
{
$order_data = [
'out_trade_no' => $pay->pay_sn,
'out_request_no' => $refund->refund_sn,
'refund_amount' => $refund->refund_fee,
'refund_reason' => $refund->remark
];
$pay = new PayService('alipay', $refund->platform);
$result = $pay->refund($order_data);
// 支付宝通知回调 pay->notify // 是和支付通知一个地址
if ($result['code'] == "10000") {
return true;
} else {
error_stop('退款失败:' . $result['msg'] . (isset($result["sub_msg"]) && $result['sub_msg'] ? '-' . $result['sub_msg'] : ''));
}
return $refund;
}
/**
* 退建行支付
* 调用建行生活退款API
*
* @param \think\Model $pay 支付记录
* @param \think\Model $refund 退款单
* @return \think\Model
*/
private function ccb($pay, $refund)
{
try {
Log::info('[建行退款] 开始处理退款 pay_id:' . $pay->id . ' refund_id:' . $refund->id . ' refund_fee:' . $refund->refund_fee);
// 调用建行订单服务进行退款
$ccbOrderService = new CcbOrderService();
$result = $ccbOrderService->refundOrder(
$pay->order_id, // 订单ID
$refund->refund_fee, // 退款金额
$refund->refund_sn // 退款流水号refund_code
);
if ($result['status']) {
// 建行退款成功,标记退款单完成
Log::info('[建行退款] 退款成功 pay_id:' . $pay->id . ' refund_id:' . $refund->id . ' result:' . json_encode($result, JSON_UNESCAPED_UNICODE));
// 完成退款单
$refund = $this->completed($refund, json_encode($result['data'] ?? [], JSON_UNESCAPED_UNICODE));
return $refund;
} else {
// 建行退款失败
Log::error('[建行退款] 退款失败 pay_id:' . $pay->id . ' refund_id:' . $refund->id . ' error:' . $result['message']);
error_stop('建行退款失败: ' . $result['message']);
}
} catch (\Exception $e) {
Log::error('[建行退款] 退款异常 pay_id:' . $pay->id . ' refund_id:' . $refund->id . ' error:' . $e->getMessage());
error_stop('建行退款异常: ' . $e->getMessage());
}
return $refund;
}
/**
* 添加 pay 记录
*
* @param think\Model $pay
* @param float $refund_money
* @param array $data
* @return think\Model
*/
private function add($pay, $refund_money, $data = [])
{
$refund_type = $data['refund_type'] ?? 'back';
// 判断退款方式
if ($refund_type == 'back') {
// 原路退回
$refund_method = $pay->pay_type;
} else {
if ($pay->pay_type == 'score') {
// 退积分
$refund_method = 'score';
} else if ($pay->pay_type == 'offline') {
// 退积分
$refund_method = 'offline';
} else {
// 退回到余额
$refund_method = 'money';
}
}
$refund = new RefundModel();
$refund->refund_sn = get_sn($this->user->id, 'R');
$refund->order_id = $pay->order_id;
$refund->pay_id = $pay->id;
$refund->pay_type = $pay->pay_type;
$refund->refund_fee = $refund_money;
$refund->refund_type = $refund_type;
$refund->refund_method = $refund_method;
$refund->status = RefundModel::STATUS_ING;
$refund->platform = $data['platform'] ?? null;
$refund->remark = $data['remark'] ?? null;
$refund->save();
return $refund;
}
/**
* 完成退款单
*
* @param \think\Model $refund
* @return \think\Model
*/
private function completed($refund, $payment_json = '')
{
$refund->status = RefundModel::STATUS_COMPLETED;
$refund->payment_json = $payment_json;
$refund->save();
return $refund;
}
/**
* 检查 pay 并且完成退款
*
* @param \think\Model $pay
* @return void
*/
private function checkPayAndRefunded($pay)
{
$pay = PayModel::where('id', $pay->id)->find();
if ($pay->refund_fee >= $pay->pay_fee) {
// 退款完成了
$pay->status = PayModel::PAY_STATUS_REFUND;
$pay->save();
}
}
}