初始化项目:添加后端代码、ThinkPHP框架、前端资源
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
namespace app\admin\model\order;
|
||||
|
||||
use think\Model;
|
||||
|
||||
|
||||
class Evaluate extends Model
|
||||
{
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// 表名
|
||||
protected $name = 'order_evaluate';
|
||||
|
||||
// 自动写入时间戳字段
|
||||
protected $autoWriteTimestamp = 'integer';
|
||||
|
||||
// 定义时间戳字段名
|
||||
protected $createTime = 'createtime';
|
||||
protected $updateTime = false;
|
||||
protected $deleteTime = false;
|
||||
|
||||
// 追加属性
|
||||
protected $append = [
|
||||
|
||||
];
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo('app\admin\model\User', 'user_id', 'id', [], 'LEFT')->setEagerlyType(0);
|
||||
}
|
||||
|
||||
public function item()
|
||||
{
|
||||
return $this->belongsTo('app\admin\model\order\Item', 'item_id', 'item_id', [], 'LEFT')->setEagerlyType(0);
|
||||
}
|
||||
|
||||
|
||||
public function course()
|
||||
{
|
||||
return $this->belongsTo('app\admin\model\course\Course', 'item_id', 'id', [], 'LEFT')->setEagerlyType(0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
namespace app\admin\model\order;
|
||||
|
||||
use think\Model;
|
||||
|
||||
|
||||
class Item extends Model
|
||||
{
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// 表名
|
||||
protected $name = 'order_item';
|
||||
|
||||
// 自动写入时间戳字段
|
||||
protected $autoWriteTimestamp = 'integer';
|
||||
|
||||
// 定义时间戳字段名
|
||||
protected $createTime = 'createtime';
|
||||
protected $updateTime = false;
|
||||
protected $deleteTime = false;
|
||||
|
||||
// 追加属性
|
||||
protected $append = [
|
||||
'refund_time_text'
|
||||
];
|
||||
|
||||
|
||||
// 定义订单商品与订单的关联关系
|
||||
public function order()
|
||||
{
|
||||
return $this->belongsTo('\app\admin\model\order\Order', 'order_no');
|
||||
}
|
||||
|
||||
|
||||
public function getGoodsTypeList()
|
||||
{
|
||||
return [
|
||||
'activity' => __('activity'),
|
||||
'composite' => __('composite'),
|
||||
'course' => __('course'),
|
||||
'exercises' => __('exercises'),
|
||||
'vipcard' => __('vipcard')
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
|
||||
public function getRefundTimeTextAttr($value, $data)
|
||||
{
|
||||
$value = $value ? $value : (isset($data['refund_time']) ? $data['refund_time'] : '');
|
||||
return is_numeric($value) ? date("Y-m-d H:i:s", $value) : $value;
|
||||
}
|
||||
|
||||
protected function setRefundTimeAttr($value)
|
||||
{
|
||||
return $value === '' ? null : ($value && !is_numeric($value) ? strtotime($value) : $value);
|
||||
}
|
||||
|
||||
|
||||
public function detail()
|
||||
{
|
||||
return $this->belongsTo('app\admin\model\Course', 'item_id', 'id', [], 'LEFT')->setEagerlyType(0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
<?php
|
||||
|
||||
namespace app\admin\model\order;
|
||||
|
||||
use think\Model;
|
||||
use app\admin\model\order\Item;
|
||||
|
||||
class Order extends Model
|
||||
{
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// 表名
|
||||
protected $name = 'order';
|
||||
|
||||
// 自动写入时间戳字段
|
||||
protected $autoWriteTimestamp = 'integer';
|
||||
|
||||
// 定义时间戳字段名
|
||||
protected $createTime = 'createtime';
|
||||
protected $updateTime = false;
|
||||
protected $deleteTime = false;
|
||||
|
||||
// 追加属性
|
||||
protected $append = [
|
||||
// 'pay_time_text',
|
||||
// 'pay_type_text',
|
||||
// 'source_text',
|
||||
// 'send_type_text',
|
||||
// 'status_text'
|
||||
];
|
||||
|
||||
|
||||
/**
|
||||
* 获取销量
|
||||
* @params $goodsType course\vipcard\activity\exercises\
|
||||
* @param $goodsId 商品ID
|
||||
* @return void
|
||||
*/
|
||||
public function getSales($goodsType,$goodsId){
|
||||
return \app\admin\model\order\Item::alias('item')
|
||||
->join('order','item.order_no=order.order_no','LEFT')
|
||||
->where([
|
||||
'goods_type'=>$goodsType,
|
||||
'item_id'=>$goodsId,
|
||||
'order.status'=>['in',\app\common\constant\order\Status::getSuccessOrderStatus()]
|
||||
])->sum('count');
|
||||
}
|
||||
|
||||
|
||||
|
||||
public function getPayTypeList()
|
||||
{
|
||||
return ['balance' => __('Balance'), 'alipay' => __('Alipay'), 'wxpay' => __('Wxpay')];
|
||||
}
|
||||
|
||||
public function getSourceList()
|
||||
{
|
||||
return ['h5' => __('source.H5'),'channels' => __('source.channels'), 'wxOfficialAccount' => __('source.wxOfficialAccount'), 'wxMiniProgram' => __('source.wxMiniProgram'), 'dyMiniProgram' => __('source.dyMiniProgram')];
|
||||
}
|
||||
|
||||
public function getSendTypeList()
|
||||
{
|
||||
return ['express' => __('Express'), 'noexpress' => __('Noexpress'), 'offline' => __('Offline')];
|
||||
}
|
||||
|
||||
public function getVirtualPayList()
|
||||
{
|
||||
return ['0' => __('否'), '1' => __('是')];
|
||||
}
|
||||
|
||||
public function getTypeList()
|
||||
{
|
||||
$list = ['goods' => __('order_type.goods'), 'score' => __('order_type.score'), 'vipcard' => __('order_type.vipcard'), 'exercises' => __('order_type.exercises'), 'physical' => __('order_type.physical'), 'live_gift' => __('order_type.live_gift')];
|
||||
// 组合商品订单类型仅在 composite 插件已安装时显示
|
||||
if (\app\admin\library\project\App::isInstall('composite')) {
|
||||
$list['composite'] = __('order_type.composite');
|
||||
}
|
||||
return $list;
|
||||
}
|
||||
|
||||
|
||||
public function getPayTimeTextAttr($value, $data)
|
||||
{
|
||||
$value = $value ? $value : (isset($data['pay_time']) ? $data['pay_time'] : '');
|
||||
return is_numeric($value) ? date("Y-m-d H:i:s", $value) : $value;
|
||||
}
|
||||
|
||||
|
||||
public function getPayTypeTextAttr($value, $data)
|
||||
{
|
||||
$value = $value ? $value : (isset($data['pay_type']) ? $data['pay_type'] : '');
|
||||
$list = $this->getPayTypeList();
|
||||
return isset($list[$value]) ? $list[$value] : '';
|
||||
}
|
||||
|
||||
|
||||
public function getSourceTextAttr($value, $data)
|
||||
{
|
||||
$value = $value ? $value : (isset($data['source']) ? $data['source'] : '');
|
||||
$list = $this->getSourceList();
|
||||
return isset($list[$value]) ? $list[$value] : '';
|
||||
}
|
||||
|
||||
|
||||
public function getSendTypeTextAttr($value, $data)
|
||||
{
|
||||
$value = $value ? $value : (isset($data['send_type']) ? $data['send_type'] : '');
|
||||
$list = $this->getSendTypeList();
|
||||
return isset($list[$value]) ? $list[$value] : '';
|
||||
}
|
||||
|
||||
|
||||
public function getStatusList()
|
||||
{
|
||||
return ['close' => __('Close'), 'unpaid' => __('Unpaid'), 'unsend' => __('Unsend'), 'unreceive' => __('Unreceive'), 'success' => __('Success'), 'cancel' => __('Cancel'), 'after_sale' => __('After_sale'), 'refund' => __('Refund')];
|
||||
}
|
||||
|
||||
public function getStatusTextAttr($value, $data)
|
||||
{
|
||||
$value = $value ? $value : (isset($data['status']) ? $data['status'] : '');
|
||||
$list = $this->getStatusList();
|
||||
return isset($list[$value]) ? $list[$value] : '';
|
||||
}
|
||||
|
||||
protected function setPayTimeAttr($value)
|
||||
{
|
||||
return $value === '' ? null : ($value && !is_numeric($value) ? strtotime($value) : $value);
|
||||
}
|
||||
|
||||
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo('app\admin\model\User', 'user_id', 'id', [], 'LEFT')->setEagerlyType(0);
|
||||
}
|
||||
|
||||
|
||||
public function item()
|
||||
{
|
||||
return $this->hasMany('app\admin\model\order\Item', 'order_no', 'order_no');
|
||||
}
|
||||
|
||||
public function log()
|
||||
{
|
||||
return $this->hasMany('app\common\model\order\Log','order_no', 'order_no');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取交易统计
|
||||
* @return void
|
||||
*/
|
||||
public function getTransactionStatistic($userId){
|
||||
$data = self::where([
|
||||
'user_id'=>$userId,
|
||||
'status'=>['in',['success']]
|
||||
])->order('createtime','desc')->field(['sum(real_pay_price) as pay_price','count(*) as pay_count','createtime as last_pay_time'])->find();
|
||||
|
||||
if(!$data){
|
||||
$data = [
|
||||
'pay_price'=>0,
|
||||
'pay_count'=>0,
|
||||
'last_pay_time'=>0
|
||||
];
|
||||
}
|
||||
|
||||
$data = $data->toArray();
|
||||
foreach ($data as &$item){
|
||||
if(!$item){
|
||||
$item = 0;
|
||||
}
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,480 @@
|
||||
<?php
|
||||
namespace app\admin\model\order;
|
||||
use think\Model;
|
||||
use app\admin\model\order\RefundLog;
|
||||
class Refund extends Model
|
||||
{
|
||||
|
||||
// 表名
|
||||
protected $name = 'order_evaluate';
|
||||
|
||||
// 自动写入时间戳字段
|
||||
protected $autoWriteTimestamp = 'integer';
|
||||
|
||||
// 定义时间戳字段名
|
||||
protected $createTime = 'createtime';
|
||||
protected $updateTime = false;
|
||||
protected $deleteTime = false;
|
||||
|
||||
// 追加属性
|
||||
protected $append = [
|
||||
|
||||
];
|
||||
|
||||
|
||||
// 开始退款
|
||||
public static function startRefund($order, $item, $refund_money, $remark = '',$saveEquity=1) {
|
||||
if (is_object($order)) {
|
||||
$order = $order->toArray();
|
||||
}
|
||||
if (is_object($item)) {
|
||||
$item = $item->toArray();
|
||||
}
|
||||
|
||||
\app\admin\model\order\Item::where([
|
||||
'id'=>$item['id']
|
||||
])->update([
|
||||
'refund_status'=>\app\common\constant\order\Status::REFUND_STATUS_OK,
|
||||
'refund_price'=>$refund_money + $item['refund_price'],
|
||||
'refund_time'=>time(),
|
||||
'refund_msg'=>$remark
|
||||
]);
|
||||
|
||||
|
||||
\app\common\model\order\Log::set($order['order_no'],"「{$item['item_name']}」发起退款,退款金额¥" . $refund_money);
|
||||
|
||||
self::refund($order, $item, $refund_money, $remark,$saveEquity);
|
||||
|
||||
// 订单退款后
|
||||
// $data = ['order' => $order, 'item' => $item];
|
||||
// \think\Hook::listen('order_refund_after', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 退款
|
||||
* @param $order 所属订单
|
||||
* @param $item 订单包含商品
|
||||
* @param $refund_money 退款金额
|
||||
* @param $remark 备注
|
||||
* @param $saveEquity 退款后是否保留权益
|
||||
* @return bool|void
|
||||
*/
|
||||
public static function refund($order, $item, $refund_money, $remark = '',$saveEquity=1)
|
||||
{
|
||||
if (is_object($order)) {
|
||||
$order = $order->toArray();
|
||||
}
|
||||
if (is_object($item)) {
|
||||
$item = $item->toArray();
|
||||
}
|
||||
|
||||
// 生成退款单
|
||||
$refundLog = new RefundLog();
|
||||
|
||||
$refundLog->order_no = $order['order_no'];
|
||||
$refundLog->refund_no = RefundLog::getNo($order['user_id']);
|
||||
$refundLog->order_item_id = $item['id'];
|
||||
$refundLog->pay_fee = $order['real_pay_price'];
|
||||
$refundLog->refund_fee = $refund_money;
|
||||
$refundLog->pay_type = $order['pay_type'];
|
||||
$refundLog->save_equity = $saveEquity;
|
||||
|
||||
|
||||
$notify_url = MODULE_URL .'/api/pay/notifyr/pay_type/' . $order['pay_type'] . '/platform/' . $order['source'] . "/uniacid/".UNIACID;
|
||||
|
||||
if($refund_money <= 0){
|
||||
$refundLog->save([
|
||||
'uniacid'=>UNIACID
|
||||
]);
|
||||
self::refundFinish($order, $item, $refundLog);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!empty($order['is_virtual_pay']) || $order['pay_type'] == 'virtual_pay') {
|
||||
return self::virtualPayRefund($order, $item, $refund_money, $remark, $saveEquity, $refundLog);
|
||||
}
|
||||
|
||||
if ($order['pay_type'] == 'wechat' || $order['pay_type'] == 'alipay') {
|
||||
// 微信|支付宝退款
|
||||
|
||||
// 退款数据
|
||||
$order_data = [
|
||||
'out_trade_no' => $order['order_no']
|
||||
];
|
||||
|
||||
if ($order['pay_type'] == 'wechat') {
|
||||
$total_fee = $order['real_pay_price'] * 100;
|
||||
$refund_fee = $refund_money * 100;
|
||||
|
||||
$order_data = array_merge($order_data, [
|
||||
'out_refund_no' => $refundLog->refund_no,
|
||||
'total_fee' => $total_fee,
|
||||
'refund_fee' => $refund_fee,
|
||||
'refund_desc' => $remark,
|
||||
]);
|
||||
} else {
|
||||
$order_data = array_merge($order_data, [
|
||||
'out_request_no' => $refundLog->refund_no,
|
||||
'refund_amount' => $refund_money,
|
||||
]);
|
||||
}
|
||||
|
||||
try{
|
||||
// 接入支付账户池:若订单是通过账户池发起的,使用对应商户号退款
|
||||
$forceAccountId = null;
|
||||
if (in_array($order['pay_type'], ['wechat', 'alipay'], true)
|
||||
&& \app\admin\library\project\App::isInstall('paymentpool')) {
|
||||
$boundAccount = \app\common\library\app\paymentpool\Service::getAccountByOrder($order['order_no']);
|
||||
if ($boundAccount && !empty($boundAccount['id'])) {
|
||||
$forceAccountId = intval($boundAccount['id']);
|
||||
}
|
||||
}
|
||||
$pay = new \app\common\library\pay\PayService($order['pay_type'], $order['source'], $notify_url, $forceAccountId);
|
||||
$result = $pay->refund($order_data);
|
||||
}catch (\Exception $e){
|
||||
throw new \app\common\exception\Exception($e->getMessage());
|
||||
}
|
||||
|
||||
if ($order['pay_type'] == 'wechat') {
|
||||
if ($result['return_code'] == 'SUCCESS' && $result['result_code'] == 'SUCCESS') {
|
||||
$refundLog->save([
|
||||
'uniacid'=>UNIACID
|
||||
]);
|
||||
return true;
|
||||
} else {
|
||||
throw new \app\common\exception\Exception($result['return_msg']);
|
||||
}
|
||||
} else {
|
||||
if (isset($result['code']) && $result['code'] == '10000') {
|
||||
$refundLog->save([
|
||||
'uniacid'=>UNIACID
|
||||
]);
|
||||
self::refundFinish($order, $item, $refundLog);
|
||||
return true;
|
||||
} else {
|
||||
$errMsg = isset($result['sub_msg']) ? $result['sub_msg'] : (isset($result['msg']) ? $result['msg'] : '退款失败');
|
||||
throw new \app\common\exception\Exception($errMsg);
|
||||
}
|
||||
}
|
||||
} else if ($order['pay_type'] == 'balance') {
|
||||
// 余额退款
|
||||
\app\common\model\User::money($refund_money, $order['user_id'], '订单退款,'.$order['order_no']);
|
||||
$refundLog->save([
|
||||
'uniacid'=>UNIACID
|
||||
]);
|
||||
self::refundFinish($order, $item, $refundLog);
|
||||
return true;
|
||||
} else if ($order['pay_type'] == 'douyinpay') {
|
||||
$paymentConfig = \app\common\model\config\System::getConfig('douyinpay');
|
||||
|
||||
|
||||
$douyinOrderData = \douyin\Byte::init($paymentConfig)->orderQuery($order['order_no']);
|
||||
|
||||
if($douyinOrderData['err_no'] != 0){
|
||||
throw new \app\common\exception\Exception("发起退款失败;" . ($douyinOrderData && isset($douyinOrderData['err_msg']) ? $douyinOrderData['err_msg'] : ''));
|
||||
}
|
||||
|
||||
$item_order_id = '';
|
||||
|
||||
if(isset($douyinOrderData['data']) && !empty($douyinOrderData['data'])){
|
||||
foreach ($douyinOrderData['data']['item_order_list'] as $skuItem){
|
||||
if($skuItem['sku_id'] == $item['item_id']){
|
||||
$item_order_id = $skuItem['item_order_id'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!$item_order_id){
|
||||
throw new \app\common\exception\Exception("发起退款失败;未查询到订单信息" );
|
||||
}
|
||||
|
||||
|
||||
|
||||
$params = [];
|
||||
$params['order_id'] = $order['transaction_id'];//交易系统侧订单号,长度 <= 64 byte
|
||||
$params['out_refund_no'] = $refundLog->refund_no;//开发者侧退款单号,长度 <= 64 byte
|
||||
$params['order_entry_schema'] = [
|
||||
'path'=>'pages/order/detail/detail',
|
||||
'params'=>json_encode(['order_no'=>$params['out_refund_no']])
|
||||
];
|
||||
$params['refund_total_amount'] = $refund_money * 100;//退款总金额,单位[分]
|
||||
// $params['notify_url'] = $notify_url;
|
||||
$params['refund_reason'] = [[
|
||||
'code'=>101,
|
||||
'text'=>$remark ? $remark : '不想要了'
|
||||
]];//退款原因,可填多个,不超过10个
|
||||
$params['item_order_detail'] = [
|
||||
[
|
||||
'item_order_id'=>$item_order_id,
|
||||
'refund_amount'=>$refund_money * 100
|
||||
]
|
||||
];
|
||||
|
||||
|
||||
$data = \douyin\Byte::init($paymentConfig)->refund($params);
|
||||
|
||||
|
||||
if(isset($data['err_no']) && $data['err_no'] == 0){
|
||||
$refundLog->refund_no = $data['data']['refund_id'];
|
||||
$refundLog->save([
|
||||
'uniacid'=>UNIACID
|
||||
]);
|
||||
self::douyinRefundQuery($data['data']['refund_id'],$order, $item, $refundLog);
|
||||
return true;
|
||||
}else{
|
||||
throw new \app\common\exception\Exception("发起退款失败;" . ($data && isset($data['err_msg']) ? $data['err_msg'] : ''));
|
||||
}
|
||||
} else if ($order->pay_type == 'score') {
|
||||
// 积分退款,暂不支持积分退款
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 虚拟支付退款处理
|
||||
* 使用微信虚拟支付退款接口(refund_order)发起退款任务
|
||||
* @param array $order 订单信息
|
||||
* @param array $item 订单商品信息
|
||||
* @param float $refund_money 退款金额(元)
|
||||
* @param string $remark 退款原因
|
||||
* @param int $saveEquity 是否保留权益(1-保留 2-关闭)
|
||||
* @param RefundLog $refundLog 退款日志对象
|
||||
* @return bool
|
||||
* @throws \Exception
|
||||
*/
|
||||
private static function virtualPayRefund($order, $item, $refund_money, $remark, $saveEquity, $refundLog)
|
||||
{
|
||||
try {
|
||||
if (is_object($order)) {
|
||||
$order = $order->toArray();
|
||||
}
|
||||
if (!is_array($order)) {
|
||||
throw new \app\common\exception\Exception('订单数据格式异常');
|
||||
}
|
||||
|
||||
if (is_object($item)) {
|
||||
$item = $item->toArray();
|
||||
}
|
||||
|
||||
$virtualPayService = new \app\common\library\pay\VirtualPayService();
|
||||
|
||||
$orderNo = isset($order['order_no']) ? $order['order_no'] : '';
|
||||
$userId = isset($order['user_id']) ? $order['user_id'] : 0;
|
||||
|
||||
$openid = \app\common\model\order\VirtualPay::getOpenidByOrderNo($orderNo);
|
||||
|
||||
\think\Log::info('[VirtualPayRefund] 开始退款 order_no=' . $orderNo . ', user_id=' . $userId . ', openid=' . ($openid ?: '空') . ', refund_money=' . $refund_money);
|
||||
|
||||
if (empty($openid)) {
|
||||
\think\Log::warning('[VirtualPayRefund] 关联表无openid,尝试从oauth表获取 order_no=' . $orderNo . ', user_id=' . $userId);
|
||||
|
||||
$oauth = \app\common\model\user\Oauth::where([
|
||||
'user_id' => $userId,
|
||||
'provider' => 'Wechat',
|
||||
'platform' => 'wxMiniProgram'
|
||||
])->find();
|
||||
|
||||
if (!$oauth || empty($oauth->openid)) {
|
||||
\think\Log::error('[VirtualPayRefund] 无法获取用户openid order_no=' . $orderNo . ', user_id=' . $userId);
|
||||
throw new \app\common\exception\Exception('用户微信授权信息不存在,无法进行虚拟支付退款');
|
||||
}
|
||||
|
||||
$openid = $oauth->openid;
|
||||
\think\Log::info('[VirtualPayRefund] 从oauth表获取到openid order_no=' . $orderNo);
|
||||
}
|
||||
|
||||
$realPayPrice = isset($order['real_pay_price']) ? $order['real_pay_price'] : 0;
|
||||
|
||||
$orderResult = $virtualPayService->queryOrder($openid, $orderNo);
|
||||
|
||||
if (isset($orderResult['errcode']) && $orderResult['errcode'] !== 0) {
|
||||
throw new \app\common\exception\Exception('查询虚拟支付订单失败:' . ($orderResult['errmsg'] ?? '未知错误'));
|
||||
}
|
||||
|
||||
$leftFee = isset($orderResult['order']['left_fee']) ? intval($orderResult['order']['left_fee']) : intval($realPayPrice * 100);
|
||||
$refundFee = intval($refund_money * 100);
|
||||
|
||||
if ($refundFee <= 0 || $refundFee > $leftFee) {
|
||||
throw new \app\common\exception\Exception('退款金额不合法,可退金额:' . ($leftFee / 100) . '元');
|
||||
}
|
||||
|
||||
$reasonMap = [
|
||||
'产品问题' => '1',
|
||||
'售后问题' => '2',
|
||||
'用户主动退款' => '3',
|
||||
'价格问题' => '4',
|
||||
'其他' => '5',
|
||||
];
|
||||
$reasonCode = '0';
|
||||
foreach ($reasonMap as $key => $code) {
|
||||
if (strpos($remark, $key) !== false) {
|
||||
$reasonCode = $code;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$refundId = $refundLog->refund_no ?: ('VPR' . date('YmdHis') . str_pad(mt_rand(1, 99999), 5, '0', STR_PAD_LEFT));
|
||||
|
||||
$result = $virtualPayService->refundOrder(
|
||||
$openid,
|
||||
$orderNo,
|
||||
$refundId,
|
||||
$leftFee,
|
||||
$refundFee,
|
||||
$reasonCode,
|
||||
'2'
|
||||
);
|
||||
|
||||
if (isset($result['errcode']) && $result['errcode'] !== 0) {
|
||||
throw new \app\common\exception\Exception('虚拟支付退款失败:' . ($result['errmsg'] ?? '未知错误'));
|
||||
}
|
||||
|
||||
$refundOrderId = $result['refund_order_id'] ?? $refundId;
|
||||
$refundLog->refund_no = $refundOrderId;
|
||||
$refundLog->save([
|
||||
'uniacid' => UNIACID
|
||||
]);
|
||||
|
||||
$pollResult = $virtualPayService->pollRefundStatus($openid, $refundOrderId);
|
||||
|
||||
\think\Log::info('[VirtualPayRefund] 退款轮询结果 order_no=' . $orderNo . ', refund_order_id=' . $refundOrderId . ', poll_result=' . json_encode($pollResult));
|
||||
|
||||
if (!$pollResult['success']) {
|
||||
$itemName = isset($item['item_name']) ? $item['item_name'] : '商品';
|
||||
\app\common\model\order\Log::set($orderNo, "「{$itemName}」虚拟支付退款已发起,退款金额¥{$refund_money},退款单号:{$refundOrderId},状态:{$pollResult['message']}");
|
||||
|
||||
throw new \app\common\exception\Exception('虚拟支付退款已发起但未完成:' . $pollResult['message']);
|
||||
}
|
||||
|
||||
self::refundFinish($order, $item, $refundLog);
|
||||
|
||||
return true;
|
||||
|
||||
} catch (\app\common\exception\Exception $e) {
|
||||
$refundLog->status = -1;
|
||||
$refundLog->save();
|
||||
throw $e;
|
||||
} catch (\Exception $e) {
|
||||
$refundLog->status = -1;
|
||||
$refundLog->save();
|
||||
throw new \app\common\exception\Exception('虚拟支付退款失败,请联系客服');
|
||||
} catch (\Throwable $e) {
|
||||
$refundLog->status = -1;
|
||||
$refundLog->save();
|
||||
throw new \app\common\exception\Exception('虚拟支付退款失败,请联系客服');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 退款完成
|
||||
* @param $order
|
||||
* @param $item
|
||||
* @param $refundLog
|
||||
* @return void
|
||||
*/
|
||||
public static function refundFinish($order, $item, $refundLog)
|
||||
{
|
||||
// 退款完成
|
||||
$refundLog->status = 1;
|
||||
$refundLog->save();
|
||||
|
||||
//检查是否已全额退款,满足条件变更订单状态为已退款
|
||||
$refundPrice = \app\admin\model\order\Item::where([
|
||||
'order_no'=>$order['order_no']
|
||||
])->sum('refund_price');
|
||||
|
||||
$orderItem = \app\admin\model\order\Item::get($item['id']);
|
||||
|
||||
|
||||
if($refundPrice >= $order['real_pay_price']){
|
||||
\app\admin\model\order\Order::where([
|
||||
'order_no'=>$order['order_no']
|
||||
])->update([
|
||||
'status'=>\app\common\constant\order\Status::STATUS_REFUND
|
||||
]);
|
||||
}
|
||||
|
||||
if($refundLog->save_equity == 2 && $orderItem->refund_can_use != 2){
|
||||
//关闭权益
|
||||
$orderItem->refund_can_use = 2;
|
||||
|
||||
if ($item['goods_type'] == 'composite') {
|
||||
// 组合商品:关闭所有子商品权限
|
||||
if (\app\admin\library\project\App::isInstall('composite')) {
|
||||
$snapshoot = $item['snapshoot'];
|
||||
if (is_string($snapshoot)) {
|
||||
$snapshoot = json_decode($snapshoot, true);
|
||||
}
|
||||
if (!empty($snapshoot['sub_goods_list'])) {
|
||||
foreach ($snapshoot['sub_goods_list'] as $subGoods) {
|
||||
\app\common\model\user\Subscription::cancelUserSubscription(
|
||||
$item['user_id'],
|
||||
$subGoods['sub_goods_id'],
|
||||
$subGoods['sub_goods_type'],
|
||||
''
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
\app\common\model\user\Subscription::cancelUserSubscription($item['user_id'],$item['item_id'],$item['goods_type'],$item['extend_data']);
|
||||
}
|
||||
}
|
||||
|
||||
// 全额退款时,组合商品返还销量
|
||||
if ($item['goods_type'] == 'composite' && $refundPrice >= $order['real_pay_price']) {
|
||||
if (\app\admin\library\project\App::isInstall('composite')) {
|
||||
\app\common\behavior\order\Payed::compositeRefundHandle($item, $order, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$orderItem->refund_status = \app\common\constant\order\Status::REFUND_STATUS_FINISH;
|
||||
$orderItem->save();
|
||||
|
||||
\app\common\model\order\Log::set($order['order_no'],"「{$item['item_name']}」退款成功");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @param $refund_id
|
||||
* @param $order
|
||||
* @param $item
|
||||
* @param $refundLog
|
||||
* @param $num
|
||||
* @return void
|
||||
*/
|
||||
public static function douyinRefundQuery($refund_id,$order, $item, $refundLog,$num=0)
|
||||
{
|
||||
$paymentConfig = \app\common\model\config\System::getConfig('douyinpay');
|
||||
$data = \douyin\Byte::init($paymentConfig)->refundQuery($refund_id);
|
||||
|
||||
if(isset($data['err_no']) && $data['err_no'] == 0 && !empty($data['data']['refund_list'])){
|
||||
switch ($data['data']['refund_list'][0]['refund_status']){
|
||||
case 'SUCCESS':
|
||||
self::refundFinish($order, $item, $refundLog);
|
||||
break;
|
||||
case 'PROCESSING':
|
||||
sleep(2);
|
||||
if($num < 3){
|
||||
self::douyinRefundQuery($refund_id,$order, $item, $refundLog,($num+1));
|
||||
}else{
|
||||
self::refundFinish($order, $item, $refundLog);
|
||||
}
|
||||
break;
|
||||
case 'FAIL':
|
||||
$refundLog->status = -1;
|
||||
$refundLog->save();
|
||||
throw new \app\common\exception\Exception($data['data']['refund_list'][0]['message']);
|
||||
break;
|
||||
}
|
||||
}else{
|
||||
$refundLog->status = -1;
|
||||
$refundLog->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
namespace app\admin\model\order;
|
||||
|
||||
use think\Model;
|
||||
|
||||
|
||||
class RefundLog extends Model
|
||||
{
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// 表名
|
||||
protected $name = 'order_refund_log';
|
||||
|
||||
// 自动写入时间戳字段
|
||||
protected $autoWriteTimestamp = 'integer';
|
||||
|
||||
// 定义时间戳字段名
|
||||
protected $createTime = 'createtime';
|
||||
protected $updateTime = 'updatetime';
|
||||
protected $deleteTime = false;
|
||||
|
||||
// 追加属性
|
||||
protected $append = [
|
||||
'status_text'
|
||||
];
|
||||
|
||||
// 订单状态
|
||||
const STATUS_INVALID = -2;
|
||||
const STATUS_CANCEL = -1;
|
||||
const STATUS_NOPAY = 0;
|
||||
const STATUS_PAYED = 1;
|
||||
const STATUS_FINISH = 2;
|
||||
|
||||
|
||||
public function getStatusList()
|
||||
{
|
||||
return ['0' => __('Status 0'), '1' => __('Status 1'), '-1' => __('Status -1')];
|
||||
}
|
||||
|
||||
public function getStatusTextAttr($value, $data)
|
||||
{
|
||||
$value = $value ? $value : (isset($data['status']) ? $data['status'] : '');
|
||||
$list = $this->getStatusList();
|
||||
return isset($list[$value]) ? $list[$value] : '';
|
||||
}
|
||||
|
||||
public function order()
|
||||
{
|
||||
return $this->belongsTo('app\admin\model\Order', 'order_no', 'order_no', [], 'LEFT')->setEagerlyType(0);
|
||||
}
|
||||
|
||||
// 获取退款单号
|
||||
public static function getNo($user_id)
|
||||
{
|
||||
$rand = $user_id < 9999 ? mt_rand(100000, 99999999) : mt_rand(100, 99999);
|
||||
$order_sn = date('Yhis') . $rand;
|
||||
|
||||
$id = str_pad($user_id, (24 - strlen($order_sn)), '0', STR_PAD_BOTH);
|
||||
|
||||
return 'R' . $order_sn . $id;
|
||||
}
|
||||
|
||||
public function item()
|
||||
{
|
||||
return $this->belongsTo('app\admin\model\order\Item', 'order_item_id', 'id', [], 'LEFT')->setEagerlyType(0);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user