PayRefund.php 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  1. <?php
  2. namespace addons\shopro\service\pay;
  3. use think\Log;
  4. use think\Db;
  5. use addons\shopro\library\pay\PayService;
  6. use app\admin\model\shopro\Pay as PayModel;
  7. use app\admin\model\shopro\user\User;
  8. use app\admin\model\shopro\Refund as RefundModel;
  9. use addons\shopro\service\Wallet as WalletService;
  10. use app\common\model\Wallet;
  11. class PayRefund
  12. {
  13. protected $user = null;
  14. /**
  15. * 实例化
  16. *
  17. * @param mixed $user
  18. */
  19. public function __construct($user = null)
  20. {
  21. // 优先使用传入的用户
  22. $this->user = is_numeric($user) ? User::get($user) : $user;
  23. }
  24. /**
  25. * 之前没有经历过任何的退款(刚支付的订单要退款|拼团失败订单等)
  26. *
  27. * @param \think\Model $pay
  28. * @param string $refund_type
  29. * @return void
  30. */
  31. public function fullRefund($pay, $data = [])
  32. {
  33. $pay->refund_fee = $pay->pay_fee;
  34. $pay->status = PayModel::PAY_STATUS_REFUND; // 直接退款完成
  35. $pay->save();
  36. // 添加退款单
  37. $refund = $this->add($pay, $pay->pay_fee, $data);
  38. // 退款
  39. $refund = $this->return($pay, $refund);
  40. return $refund;
  41. }
  42. /**
  43. * 部分退款,指定退款金额,并且检测 pay 是否已经退款完成
  44. *
  45. * @param \think\Model $pay
  46. * @param float $refund_money
  47. * @param array $data
  48. * @return \think\Model
  49. */
  50. public function refund($pay, $refund_money, $data = [])
  51. {
  52. $pay->refund_fee = Db::raw('refund_fee + ' . $refund_money);
  53. $pay->save();
  54. // 添加退款单
  55. $refund = $this->add($pay, $refund_money, $data);
  56. // 退款
  57. $refund = $this->return($pay, $refund);
  58. // 检查 pay 是否退款完成,
  59. $this->checkPayAndRefunded($pay);
  60. return $refund;
  61. }
  62. /**
  63. * 微信支付宝退款回调方法
  64. *
  65. * @param array $data
  66. * @return void
  67. */
  68. public function notify($data)
  69. {
  70. $out_trade_no = $data['out_trade_no'];
  71. $out_refund_no = $data['out_refund_no'];
  72. $payment_json = $data['payment_json'];
  73. $pay = PayModel::where('pay_sn', $out_trade_no)->find();
  74. if (!$pay) {
  75. Log::write('refund-notify-error:paymodel notfound;pay:' . json_encode($data));
  76. return false;
  77. }
  78. if ($pay->order_type == 'order') {
  79. $refund = RefundModel::where('refund_sn', $out_refund_no)->find();
  80. if (!$refund || $refund->status != RefundModel::STATUS_ING) {
  81. // 退款单不存在,或者已退款
  82. return false;
  83. }
  84. $refund = $this->completed($refund, $payment_json);
  85. return true;
  86. } else {
  87. // 如有其他订单类型如果支持退款,逻辑这里补充
  88. }
  89. }
  90. /**
  91. * 退回
  92. *
  93. * @param \think\Model $pay
  94. * @param \think\Model $refund
  95. * @return \think\Model
  96. */
  97. protected function return($pay, $refund)
  98. {
  99. $method = $refund->refund_method;
  100. if (method_exists($this, $method)) {
  101. $refund = $this->{$method}($pay, $refund);
  102. } else {
  103. error_stop('退款方式不支持');
  104. }
  105. return $refund;
  106. }
  107. /**
  108. * 退余额
  109. *
  110. * @param \think\Model $pay
  111. * @param \think\Model $refund
  112. * @return \think\Model
  113. */
  114. private function money($pay, $refund)
  115. {
  116. // 退回用户余额
  117. /*WalletService::change($pay->user_id, 'money', $refund->refund_fee, 'order_refund', [
  118. 'refund_id' => $refund->id,
  119. 'refund_sn' => $refund->refund_sn,
  120. 'pay_id' => $pay->id,
  121. 'pay_sn' => $pay->pay_sn,
  122. 'order_id' => $pay->order_id,
  123. 'order_type' => $pay->order_type,
  124. ]);*/
  125. $walletService = new Wallet();
  126. if (!$walletService->change($pay->user_id, $refund->refund_fee, 'money', 32, '商城订单退款', 'shopro_pay', $pay->id)) {
  127. error_stop($walletService->getMessage());
  128. }
  129. $refund = $this->completed($refund);//只是改状态
  130. return $refund;
  131. }
  132. /**
  133. * 退善豆
  134. *
  135. * @param \think\Model $pay
  136. * @param \think\Model $refund
  137. * @return \think\Model
  138. */
  139. private function bean($pay, $refund)
  140. {
  141. // 退回用户余额
  142. /*WalletService::change($pay->user_id, 'bean', $refund->refund_fee, 'order_refund', [
  143. 'refund_id' => $refund->id,
  144. 'refund_sn' => $refund->refund_sn,
  145. 'pay_id' => $pay->id,
  146. 'pay_sn' => $pay->pay_sn,
  147. 'order_id' => $pay->order_id,
  148. 'order_type' => $pay->order_type,
  149. ]);*/
  150. $walletService = new Wallet();
  151. if (!$walletService->change($pay->user_id, $refund->refund_fee, 'bean', 132, '商城订单退款', 'shopro_pay', $pay->id)) {
  152. error_stop($walletService->getMessage());
  153. }
  154. $refund = $this->completed($refund);//只是改状态
  155. return $refund;
  156. }
  157. /**
  158. * 退积分
  159. *
  160. * @param \think\Model $pay
  161. * @param \think\Model $refund
  162. * @return \think\Model
  163. */
  164. private function score($pay, $refund)
  165. {
  166. // 退回用户积分
  167. /*WalletService::change($pay->user_id, 'score', $refund->refund_fee, 'order_refund', [
  168. 'refund_id' => $refund->id,
  169. 'refund_sn' => $refund->refund_sn,
  170. 'pay_id' => $pay->id,
  171. 'pay_sn' => $pay->pay_sn,
  172. 'order_id' => $pay->order_id,
  173. 'order_type' => $pay->order_type,
  174. ]);*/
  175. $refund = $this->completed($refund);
  176. return $refund;
  177. }
  178. /**
  179. * 退 offline
  180. *
  181. * @param \think\Model $pay
  182. * @param \think\Model $refund
  183. * @return \think\Model
  184. */
  185. private function offline($pay, $refund)
  186. {
  187. // offline 退款啥也不干,钱还是线下退回,线上不处理
  188. $refund = $this->completed($refund);
  189. return $refund;
  190. }
  191. /**
  192. * 退微信
  193. *
  194. * @param \think\Model $pay
  195. * @param \think\Model $refund
  196. * @return \think\Model
  197. */
  198. private function wechat($pay, $refund)
  199. {
  200. $order_data = [
  201. 'out_trade_no' => $pay->pay_sn,
  202. 'out_refund_no' => $refund->refund_sn,
  203. 'reason' => $refund->remark,
  204. 'amount' => [
  205. 'refund' => $refund->refund_fee,
  206. 'total' => $pay->pay_fee,
  207. 'currency' => 'CNY'
  208. ]
  209. ];
  210. $pay = new PayService('wechat', $refund->platform);
  211. $result = $pay->refund($order_data);
  212. // 微信通知回调 pay->notifyRefund
  213. if (isset($result['status']) && in_array($result['status'], ['SUCCESS', 'PROCESSING'])) {
  214. // 微信返回的状态会是 PROCESSING
  215. return true;
  216. } else {
  217. error_stop('退款失败:' . (isset($result['message']) ? $result['message'] : json_encode($result, JSON_UNESCAPED_UNICODE)));
  218. }
  219. return $refund;
  220. }
  221. /**
  222. * 退支付宝
  223. *
  224. * @param \think\Model $pay
  225. * @param \think\Model $refund
  226. * @return \think\Model
  227. */
  228. private function alipay($pay, $refund)
  229. {
  230. $order_data = [
  231. 'out_trade_no' => $pay->pay_sn,
  232. 'out_request_no' => $refund->refund_sn,
  233. 'refund_amount' => $refund->refund_fee,
  234. 'refund_reason' => $refund->remark
  235. ];
  236. $pay = new PayService('alipay', $refund->platform);
  237. $result = $pay->refund($order_data);
  238. // 支付宝通知回调 pay->notify // 是和支付通知一个地址
  239. if ($result['code'] == "10000") {
  240. return true;
  241. } else {
  242. error_stop('退款失败:' . $result['msg'] . (isset($result["sub_msg"]) && $result['sub_msg'] ? '-' . $result['sub_msg'] : ''));
  243. }
  244. return $refund;
  245. }
  246. /**
  247. * 添加 pay 记录
  248. *
  249. * @param think\Model $pay
  250. * @param float $refund_money
  251. * @param array $data
  252. * @return think\Model
  253. */
  254. private function add($pay, $refund_money, $data = [])
  255. {
  256. $refund_type = $data['refund_type'] ?? 'back';
  257. // 判断退款方式
  258. if ($refund_type == 'back') {
  259. // 原路退回
  260. $refund_method = $pay->pay_type;
  261. } else {
  262. if ($pay->pay_type == 'score') {
  263. // 退积分
  264. $refund_method = 'score';
  265. } else if ($pay->pay_type == 'offline') {
  266. // 退积分
  267. $refund_method = 'offline';
  268. } else if ($pay->pay_type == 'bean') {
  269. // 退善豆
  270. $refund_method = 'bean';
  271. } else {
  272. // 退回到余额
  273. $refund_method = 'money';
  274. }
  275. }
  276. $refund = new RefundModel();
  277. $refund->refund_sn = get_sn($this->user->id, 'R');
  278. $refund->order_id = $pay->order_id;
  279. $refund->pay_id = $pay->id;
  280. $refund->pay_type = $pay->pay_type;
  281. $refund->refund_fee = $refund_money;
  282. $refund->refund_type = $refund_type;
  283. $refund->refund_method = $refund_method;
  284. $refund->status = RefundModel::STATUS_ING;
  285. $refund->platform = $data['platform'] ?? null;
  286. $refund->remark = $data['remark'] ?? null;
  287. $refund->save();
  288. return $refund;
  289. }
  290. /**
  291. * 完成退款单
  292. *
  293. * @param \think\Model $refund
  294. * @return \think\Model
  295. */
  296. private function completed($refund, $payment_json = '')
  297. {
  298. $refund->status = RefundModel::STATUS_COMPLETED;
  299. $refund->payment_json = $payment_json;
  300. $refund->save();
  301. return $refund;
  302. }
  303. /**
  304. * 检查 pay 并且完成退款
  305. *
  306. * @param \think\Model $pay
  307. * @return void
  308. */
  309. private function checkPayAndRefunded($pay)
  310. {
  311. $pay = PayModel::where('id', $pay->id)->find();
  312. if ($pay->refund_fee >= $pay->pay_fee) {
  313. // 退款完成了
  314. $pay->status = PayModel::PAY_STATUS_REFUND;
  315. $pay->save();
  316. }
  317. }
  318. }