AfterSaleRefund.php 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | likeshop100%开源免费商用商城系统
  4. // +----------------------------------------------------------------------
  5. // | 欢迎阅读学习系统程序代码,建议反馈是我们前进的动力
  6. // | 开源版本可自由商用,可去除界面版权logo
  7. // | 商业版本务必购买商业授权,以免引起法律纠纷
  8. // | 禁止对系统程序代码以任何目的,任何形式的再发布
  9. // | gitee下载:https://gitee.com/likeshop_gitee
  10. // | github下载:https://github.com/likeshop-github
  11. // | 访问官网:https://www.likeshop.cn
  12. // | 访问社区:https://home.likeshop.cn
  13. // | 访问手册:http://doc.likeshop.cn
  14. // | 微信公众号:likeshop技术社区
  15. // | likeshop团队 版权所有 拥有最终解释权
  16. // +----------------------------------------------------------------------
  17. // | author: likeshopTeam
  18. // +----------------------------------------------------------------------
  19. namespace app\common\command;
  20. use app\common\enum\AccountLogEnum;
  21. use app\common\enum\AfterSaleEnum;
  22. use app\common\enum\AfterSaleLogEnum;
  23. use app\common\enum\BargainEnum;
  24. use app\common\enum\NoticeEnum;
  25. use app\common\enum\OrderEnum;
  26. use app\common\enum\PayEnum;
  27. use app\common\logic\RefundLogic;
  28. use app\common\model\AccountLog;
  29. use app\common\model\AfterSale;
  30. use app\common\model\BargainInitiate;
  31. use app\common\model\Order;
  32. use app\common\model\OrderGoods;
  33. use app\common\model\Refund;
  34. use app\common\service\after_sale\AfterSaleService;
  35. use app\common\service\pay\AliPayService;
  36. use app\common\service\pay\ToutiaoPayService;
  37. use app\common\service\pay\WeChatPayService;
  38. use app\common\service\WeChatConfigService;
  39. use EasyWeChat\Factory;
  40. use think\console\Command;
  41. use think\console\Input;
  42. use think\console\Output;
  43. use think\facade\Log;
  44. /**
  45. * 售后退款查询
  46. * 只查询微信退款、支付宝退款两种退款方式,退回余额方式是马上到账的得到售后结果的,无需查询
  47. * Class DistributionSettlement
  48. * @package app\common\command
  49. */
  50. class AfterSaleRefund extends Command
  51. {
  52. protected function configure()
  53. {
  54. $this->setName('after_sale_refund')
  55. ->setDescription('售后退款查询');
  56. }
  57. protected function execute(Input $input, Output $output)
  58. {
  59. // 查找售后中:售后退款中记录
  60. $afterSaleList = AfterSale::where('sub_status', AfterSaleEnum::SUB_STATUS_SELLER_REFUND_ING)->select()->toArray();
  61. if(empty($afterSaleList)) {
  62. return true;
  63. }
  64. foreach($afterSaleList as $item) {
  65. switch ($item['refund_way']) {
  66. // 原路退回
  67. case AfterSaleEnum::REFUND_WAYS_ORIGINAL:
  68. $result = self::originalRefund($item);
  69. break;
  70. default:
  71. $result = null;
  72. }
  73. // 退款成功
  74. if($result === true) {
  75. self::afterSuccess($item);
  76. continue;
  77. }
  78. // 退款失败
  79. if($result === false) {
  80. self::afterFail($item);
  81. continue;
  82. }
  83. }
  84. }
  85. /**
  86. * @notes 查询微信退款是否成功
  87. * @param $item
  88. * @author Tab
  89. * @date 2021/9/11 11:33
  90. */
  91. public static function checkWechatRefund($item)
  92. {
  93. $order = Order::findOrEmpty($item['order_id'])->toArray();
  94. $pay = new WeChatPayService($order['order_terminal']);
  95. // 获取售后单对应的退款记录
  96. $refund = Refund::where('after_sale_id', $item['id'])->findOrEmpty();
  97. // 根据商户退款单号查询退款
  98. $result = $pay->queryRefund($refund);
  99. if (is_bool($result)) {
  100. return $result;
  101. }
  102. // 其他情况,将查询结果写入到退款记录中
  103. $refund->refund_msg = json_encode($result, JSON_UNESCAPED_UNICODE);
  104. $refund->save();
  105. return null;
  106. }
  107. /**
  108. * @notes 查询支付宝退款是否成功
  109. * @param $item
  110. * @author Tab
  111. * @date 2021/9/13 10:34
  112. */
  113. public static function checkAliRefund($item)
  114. {
  115. $order = Order::findOrEmpty($item['order_id'])->toArray();
  116. // 获取售后单对应的退款记录
  117. $refund = Refund::where('after_sale_id', $item['id'])->findOrEmpty()->toArray();
  118. $result = (new AliPayService())->queryRefund($order['sn'], $refund['sn']);
  119. $result = $result->toMap();
  120. if ($result['code'] == '10000' && $result['msg'] == 'Success' && $result['refund_status'] == 'REFUND_SUCCESS') {
  121. // 退款成功
  122. return true;
  123. }
  124. // 退款查询请求未收到 或 退款失败
  125. return null;
  126. }
  127. /**
  128. * @notes 校验字节退款
  129. * @param $item
  130. * @throws \Exception
  131. * @author Tab
  132. * @date 2021/11/18 14:51
  133. */
  134. public static function checkByteRefund($item)
  135. {
  136. // 获取售后单对应的退款记录
  137. $refund = Refund::where('after_sale_id', $item['id'])->findOrEmpty()->toArray();
  138. return (new ToutiaoPayService())->queryRefund($refund['sn']);
  139. }
  140. /**
  141. * @notes 计算退款状态
  142. * @param $item
  143. * @return int
  144. * @author Tab
  145. * @date 2021/8/18 11:51
  146. */
  147. public static function calcRefundStatus($item)
  148. {
  149. // 整单退款
  150. if($item['refund_type'] == AfterSaleEnum::REFUND_TYPE_ORDER) {
  151. $order = Order::findOrEmpty($item['order_id'])->toArray();
  152. return $item['refund_total_amount'] == $order['order_amount'] ? AfterSaleEnum::FULL_REFUND : AfterSaleEnum::PARTIAL_REFUND;
  153. }
  154. // 商品售后
  155. if($item['refund_type'] == AfterSaleEnum::REFUND_TYPE_GOODS) {
  156. $orderGoods = OrderGoods::findOrEmpty($item['order_goods_id'])->toArray();
  157. return $item['refund_total_amount'] == $orderGoods['total_pay_price'] ? AfterSaleEnum::FULL_REFUND : AfterSaleEnum::PARTIAL_REFUND;
  158. }
  159. }
  160. /**
  161. * @notes 校验原路退款
  162. * @param $item
  163. * @return bool
  164. * @author Tab
  165. * @date 2021/8/18 14:31
  166. */
  167. public static function originalRefund($item)
  168. {
  169. $order = Order::findOrEmpty($item['order_id'])->toArray();
  170. if (empty($order)) {
  171. return null;
  172. }
  173. switch($order['pay_way']) {
  174. case PayEnum::WECHAT_PAY:
  175. return self::checkWechatRefund($item);
  176. case PayEnum::ALI_PAY:
  177. return self::checkAliRefund($item);
  178. case PayEnum::BYTE_PAY:
  179. return self::checkByteRefund($item);
  180. // 线下 直接完成
  181. case PayEnum::OFFLINE_PAY:
  182. return true;
  183. }
  184. }
  185. /**
  186. * @notes 退款成功后操作
  187. * @param $item
  188. * @author Tab
  189. * @date 2021/8/18 14:20
  190. */
  191. public static function afterSuccess($item)
  192. {
  193. $refundStauts = self::calcRefundStatus($item);
  194. AfterSale::update([
  195. 'id' => $item['id'],
  196. 'refund_status' => $refundStauts,
  197. 'status' => AfterSaleEnum::STATUS_SUCCESS,
  198. 'sub_status' => AfterSaleEnum::SUB_STATUS_SELLER_REFUND_SUCCESS
  199. ]);
  200. AfterSaleService::createAfterLog($item['id'], '系统已完成退款', 0, AfterSaleLogEnum::ROLE_SYS);
  201. //更新订单状态
  202. RefundLogic::afterSaleRefundUpdate($item['order_id']);
  203. $order = Order::findOrEmpty($item['order_id'])->toArray();
  204. // 消息通知
  205. event('Notice', [
  206. 'scene_id' => NoticeEnum::REFUND_SUCCESS_NOTICE,
  207. 'params' => [
  208. 'user_id' => $item['user_id'],
  209. 'after_sale_sn' => $item['sn'],
  210. 'order_sn' => $order['sn'],
  211. 'refund_type' => AfterSaleEnum::getRefundTypeDesc($item['refund_way']),
  212. 'refund_total_amount' => $item['refund_total_amount'],
  213. 'refund_time' => date('Y-m-d H:i:s'),
  214. ]
  215. ]);
  216. }
  217. /**
  218. * @notes 退款失败后操作
  219. * @param $item
  220. * @author Tab
  221. * @date 2021/8/18 14:21
  222. */
  223. public static function afterFail($item)
  224. {
  225. AfterSale::update([
  226. 'id' => $item['id'],
  227. 'sub_status' => AfterSaleEnum::SUB_STATUS_SELLER_REFUND_FAIL
  228. ]);
  229. AfterSaleService::createAfterLog($item['id'], '系统退款失败,等待卖家处理', 0, AfterSaleLogEnum::ROLE_SYS);
  230. }
  231. }