海南旅游SAAS
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

247 lines
7.3 KiB

  1. <?php
  2. namespace App\Http\Controllers\Api;
  3. use App\Models\Agent;
  4. use App\Models\AgentProduct;
  5. use App\Models\Order;
  6. use App\Models\Product;
  7. use App\Models\UserMoneyLog;
  8. use EasyWeChat\Factory;
  9. use EasyWeChat\Kernel\Exceptions\Exception;
  10. use EasyWeChat\Payment\Kernel\Exceptions\InvalidSignException;
  11. use Illuminate\Support\Facades\DB;
  12. use App\Service\OrderStatus;
  13. class WxpayController
  14. {
  15. //微信支付 支付结果通知网址
  16. public function notify()
  17. {
  18. $agent_id = request()->route('agent_id');
  19. $agent = Agent::find($agent_id);
  20. $config = config('wechat.payment.default');
  21. $config = array_merge($config, [
  22. 'app_id' => $agent->appid,
  23. 'mch_id' => $agent->mchid,
  24. 'key' => $agent->mchkey,
  25. ]);
  26. $app = Factory::payment($config);
  27. try {
  28. $response = $app->handlePaidNotify(function ($message, $fail) {
  29. $this->log($message);
  30. // 请求成功
  31. if ($message['return_code'] === 'SUCCESS') {
  32. //订单号带有status后缀,主要是为了区分定金支付和尾款支付,前24位才是真正的订单号
  33. $order_no = substr($message['out_trade_no'], 0, 24);
  34. $order = Order::query()
  35. ->where(['order_no' => $order_no])
  36. ->first();
  37. //已经处理过的订单直接返回true
  38. if ($order && in_array($order->status, [OrderStatus::PAID, OrderStatus::PAID_RETAINAGE, OrderStatus::SUCCESS])) {
  39. return true;
  40. }
  41. //判断该微信支付订单号有没有处理过
  42. $exist_log = UserMoneyLog::where([
  43. 'user_id' => $order->user_id,
  44. 'order_id' => $order->id,
  45. 'type' => 1,
  46. 'transaction_id' => $message['transaction_id'],
  47. ])->first();
  48. if ($exist_log) {
  49. return true;
  50. }
  51. // 支付成功
  52. if ($message['result_code'] === 'SUCCESS') {
  53. DB::beginTransaction();
  54. try {
  55. $status = $order->status;
  56. $pay_type = $order->pay_type;
  57. $money = $message['total_fee'] / 100;
  58. //定金支付和首付款支付
  59. if (in_array($pay_type, [1, 2])) {
  60. if ($status == OrderStatus::UNPAID) {
  61. $order->status = OrderStatus::PAY_EARNEST;
  62. } else if ($status == OrderStatus::PAY_EARNEST) {
  63. //只有支付的金额大于等于订单总价,才将订单状态修改为PAID_RETAINAGE
  64. if ($order->paid_money + $money >= $order->price) {
  65. $order->status = OrderStatus::PAID_RETAINAGE;
  66. }
  67. }
  68. } else if ($pay_type == 0) {
  69. $order->status = OrderStatus::PAID;
  70. }
  71. $order->paid_at = time();
  72. $order->paid_money = DB::raw('`paid_money` + ' . $money);
  73. $order->save();
  74. //增加销量,库存在拍下时已经减了
  75. AgentProduct::query()
  76. ->where('id', $order->agent_product_id)
  77. ->increment('sale', $order->num);
  78. Product::query()
  79. ->where('id', $order->product_id)
  80. ->increment('sale', $order->num);
  81. //资金流水
  82. UserMoneyLog::query()->create([
  83. 'user_id' => $order->user_id,
  84. 'agent_id' => $order->agent_id,
  85. 'money' => -$money,
  86. 'order_id' => $order->id,
  87. 'type' => 1,
  88. 'desc' => DB::raw("LEFT('购买产品:{$order->title}', 250)"),
  89. 'transaction_id' => $message['transaction_id'], //微信支付订单号
  90. 'created_at' => time(), //模型没有updated_at,无法自动写入时间
  91. ]);
  92. DB::commit();
  93. return true;
  94. } catch (Exception $e) {
  95. DB::rollBack();
  96. $fail('Unknown error');
  97. }
  98. } // 支付失败
  99. else if ($message['result_code'] === 'FAIL') {
  100. return true;
  101. }
  102. }
  103. // 希望微信重试
  104. $fail('Unknown error 2');
  105. });
  106. } catch (InvalidSignException | Exception $e) {
  107. $this->log($e->getMessage() . $e->getFile() . $e->getLine());
  108. return 'error';
  109. }
  110. return $response;
  111. }
  112. //退款通知
  113. public function refund()
  114. {
  115. $agent_id = request()->route('agent_id');
  116. $agent = Agent::find($agent_id);
  117. $config = config('wechat.payment.default');
  118. $config = array_merge($config, [
  119. 'app_id' => $agent->appid,
  120. 'mch_id' => $agent->mchid,
  121. 'key' => $agent->mchkey,
  122. ]);
  123. $app = Factory::payment($config);
  124. try {
  125. $response = $app->handleRefundedNotify(function ($message, $reqInfo, $fail) {
  126. // 记录一下本地调试
  127. $this->log(['message' => $message, 'reqInfo' => $reqInfo], 'refund');
  128. /*$reqInfo = [
  129. 'out_refund_no' => '131811191610442717309',
  130. 'out_trade_no' => '20210722164514946742983',
  131. 'refund_account' => 'REFUND_SOURCE_RECHARGE_FUNDS',
  132. 'refund_fee' => 8800,
  133. 'refund_id' => '50000408942018111907145868882',
  134. 'refund_recv_accout' => '支付用户零钱',
  135. 'refund_request_source' => 'API',
  136. 'refund_status' => 'SUCCESS',
  137. 'settlement_refund_fee' => 8800,
  138. 'settlement_total_fee' => 8800,
  139. 'success_time' => date('Y-m-d H:i:s'),
  140. 'total_fee' => 8800,
  141. 'transaction_id' => '1004400740201409030005092168',
  142. ];*/
  143. // 请求成功
  144. if ($message['return_code'] === 'SUCCESS') {
  145. //订单号带有status后缀,主要是为了区分定金支付和尾款支付,前24位才是真正的订单号
  146. $order_no = substr($reqInfo['out_trade_no'], 0, 24);
  147. $order = Order::query()
  148. ->where(['order_no' => $order_no])
  149. ->first();
  150. //如果已经处理过则不再处理
  151. if ($order->status == OrderStatus::REFUNDED) {
  152. return true;
  153. }
  154. //如果已经存在相关的退款记录,也不再处理
  155. $exist_log = UserMoneyLog::where([
  156. 'user_id' => $order->user_id,
  157. 'order_id' => $order->id,
  158. 'type' => 2,
  159. 'transaction_id' => $reqInfo['transaction_id'],
  160. ])->first();
  161. if ($exist_log) {
  162. return true;
  163. }
  164. $log = UserMoneyLog::query()
  165. ->where([
  166. 'user_id' => $order->user_id,
  167. 'order_id' => $order->id,
  168. 'type' => 1,
  169. 'transaction_id' => $reqInfo['transaction_id'],
  170. ])->first();
  171. if (!$log) {
  172. $fail('找不到交易信息');
  173. }
  174. // 退款成功
  175. if ($reqInfo['refund_status'] === 'SUCCESS') {
  176. DB::beginTransaction();
  177. try {
  178. //更新订单状态为退款成功
  179. $order->status = OrderStatus::REFUNDED;
  180. $order->save();
  181. //记录日志
  182. UserMoneyLog::create([
  183. 'user_id' => $order->user_id,
  184. 'agent_id' => $order->agent_id,
  185. 'money' => $reqInfo['settlement_refund_fee'] / 100,
  186. 'order_id' => $order->id,
  187. 'type' => 2,
  188. 'desc' => DB::raw("LEFT('退款:{$order->title}', 250)"),
  189. 'transaction_id' => $reqInfo['transaction_id'],
  190. 'created_at' => time(),
  191. ]);
  192. // 退库存
  193. Product::query()
  194. ->where('id', $order->product_id)
  195. ->increment('stock', $order->num);
  196. DB::commit();
  197. return true;
  198. } catch (\Exception $e) {
  199. DB::rollBack();
  200. $fail('Unknown error');
  201. }
  202. }
  203. }
  204. $fail('Unknown error 2');
  205. });
  206. } catch (Exception $e) {
  207. return 'error';
  208. }
  209. return $response;
  210. }
  211. //保存消息,用于调试
  212. private function log($write_data, $type = 'notify')
  213. {
  214. $dir = storage_path('wxpay');
  215. if (!is_dir($dir)) {
  216. mkdir($dir);
  217. }
  218. $filename = $dir . '/' . $type . '_' . date('Y-m-d-H') . '.log';
  219. $data = '[' . date('Y-m-d H:i:s') . ']' . PHP_EOL;
  220. $data .= '[message]: ' . (is_array($write_data) ? json_encode($write_data) : $write_data) . PHP_EOL . PHP_EOL;
  221. file_put_contents($filename, $data, FILE_APPEND);
  222. }
  223. }