海南旅游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.

241 lines
7.2 KiB

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