PayRefund.php 8.8 KB

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