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
						
					
					
				
			
		
		
		
			
			
			
				
					
				
				
					
				
			
		
		
	
	
							241 lines
						
					
					
						
							7.2 KiB
						
					
					
				
								<?php
							 | 
						|
								
							 | 
						|
								namespace App\Http\Controllers\Api;
							 | 
						|
								use App\Common\PayType;
							 | 
						|
								use App\Models\Agent;
							 | 
						|
								use App\Models\AgentProduct;
							 | 
						|
								use App\Models\Order;
							 | 
						|
								use App\Models\Product;
							 | 
						|
								use App\Models\UserMoneyLog;
							 | 
						|
								use EasyWeChat\Factory;
							 | 
						|
								use EasyWeChat\Kernel\Exceptions\Exception;
							 | 
						|
								use EasyWeChat\Payment\Kernel\Exceptions\InvalidSignException;
							 | 
						|
								use Illuminate\Support\Facades\DB;
							 | 
						|
								use App\Common\OrderStatus;
							 | 
						|
								
							 | 
						|
								class WxpayController
							 | 
						|
								{
							 | 
						|
									//微信支付 支付结果通知网址
							 | 
						|
									public function notify()
							 | 
						|
									{
							 | 
						|
										$agent_id = request()->route('agent_id');
							 | 
						|
										$agent = Agent::find($agent_id);
							 | 
						|
								
							 | 
						|
										$config = config('wechat.payment.default');
							 | 
						|
										$config = array_merge($config, [
							 | 
						|
											'app_id' => $agent->appid,
							 | 
						|
											'mch_id' => $agent->mchid,
							 | 
						|
											'key' => $agent->mchkey,
							 | 
						|
										]);
							 | 
						|
										$app = Factory::payment($config);
							 | 
						|
										try {
							 | 
						|
											$response = $app->handlePaidNotify(function ($message, $fail) {
							 | 
						|
												//TODO 仅测试用
							 | 
						|
												DB::table('pay_debugs')->insert(['content' => json_encode($message)]);
							 | 
						|
								
							 | 
						|
												$this->log($message);
							 | 
						|
												// 请求成功
							 | 
						|
												if ($message['return_code'] === 'SUCCESS') {
							 | 
						|
													//主要是为了区分定金支付和尾款支付,订单号带有-status后缀,分割后前面才是真正的订单号
							 | 
						|
													list($order_no, $status) = explode('-', $message['out_trade_no']);
							 | 
						|
													$order = Order::query()
							 | 
						|
														->where(['order_no' => $order_no])
							 | 
						|
														->first();
							 | 
						|
								
							 | 
						|
													//已经处理过的订单直接返回true
							 | 
						|
													if ($order && in_array($order->status, [OrderStatus::PAID, OrderStatus::PAID_RETAINAGE, OrderStatus::SUCCESS])) {
							 | 
						|
														return true;
							 | 
						|
													}
							 | 
						|
								
							 | 
						|
													//判断该微信支付订单号有没有处理过
							 | 
						|
													$exist_log = UserMoneyLog::where([
							 | 
						|
														'user_id' => $order->user_id,
							 | 
						|
														'order_id' => $order->id,
							 | 
						|
														'type' => 1,
							 | 
						|
														'transaction_id' => $message['transaction_id'],
							 | 
						|
													])->first();
							 | 
						|
													if ($exist_log) {
							 | 
						|
														return true;
							 | 
						|
													}
							 | 
						|
								
							 | 
						|
													// 支付成功
							 | 
						|
													if ($message['result_code'] === 'SUCCESS') {
							 | 
						|
														DB::beginTransaction();
							 | 
						|
														try {
							 | 
						|
															$status = $order->status;
							 | 
						|
															$pay_type = $order->pay_type;
							 | 
						|
															$money = $message['total_fee'] / 100;
							 | 
						|
															//定金支付和首付款支付
							 | 
						|
															if (in_array($pay_type, [PayType::SUBSCRIPTION, PayType::DEPOSIT, PayType::DOWN_PAYMENT])) {
							 | 
						|
																if ($status == OrderStatus::UNPAID) {
							 | 
						|
																	$order->status = OrderStatus::PAY_EARNEST;
							 | 
						|
																} else if ($status == OrderStatus::PAY_EARNEST) {
							 | 
						|
																	//只有支付的金额大于等于订单总价,才将订单状态修改为PAID_RETAINAGE
							 | 
						|
																	if ($order->paid_money + $money >= $order->price) {
							 | 
						|
																		$order->status = OrderStatus::PAID_RETAINAGE;
							 | 
						|
																	}
							 | 
						|
																}
							 | 
						|
															} else if ($pay_type == 0) {
							 | 
						|
																$order->status = OrderStatus::PAID;
							 | 
						|
															}
							 | 
						|
								
							 | 
						|
															//支付金额>=订单金额之后生成核销码
							 | 
						|
															if ($order->paid_money + $money >= $order->price) {
							 | 
						|
																$order->verify_code = uniqid(); //生成核销码
							 | 
						|
															}
							 | 
						|
								
							 | 
						|
															$order->paid_at = now();
							 | 
						|
															$order->paid_money = DB::raw('`paid_money` + ' . $money);
							 | 
						|
															$order->save();
							 | 
						|
								
							 | 
						|
															//增加销量,库存在拍下时已经减了
							 | 
						|
															AgentProduct::query()
							 | 
						|
																->where('id', $order->agent_product_id)
							 | 
						|
																->increment('sale', $order->num);
							 | 
						|
															Product::query()
							 | 
						|
																->where('id', $order->product_id)
							 | 
						|
																->increment('sale', $order->num);
							 | 
						|
								
							 | 
						|
															//资金流水
							 | 
						|
															UserMoneyLog::query()->create([
							 | 
						|
																'user_id' => $order->user_id,
							 | 
						|
																'agent_id' => $order->agent_id,
							 | 
						|
																'money' => -$money,
							 | 
						|
																'order_id' => $order->id,
							 | 
						|
																'type' => 1,
							 | 
						|
																'desc' => DB::raw("LEFT('购买产品:{$order->title}', 250)"),
							 | 
						|
																'transaction_id' => $message['transaction_id'], //微信支付订单号
							 | 
						|
																'created_at' => now(), //模型没有updated_at,无法自动写入时间
							 | 
						|
															]);
							 | 
						|
								
							 | 
						|
															DB::commit();
							 | 
						|
															return true;
							 | 
						|
														} catch (Exception $e) {
							 | 
						|
															DB::rollBack();
							 | 
						|
															$fail('Unknown error');
							 | 
						|
														}
							 | 
						|
													} // 支付失败
							 | 
						|
													else if ($message['result_code'] === 'FAIL') {
							 | 
						|
														return true;
							 | 
						|
													}
							 | 
						|
												}
							 | 
						|
								
							 | 
						|
												// 希望微信重试
							 | 
						|
												$fail('Unknown error 2');
							 | 
						|
											});
							 | 
						|
										} catch (InvalidSignException | Exception $e) {
							 | 
						|
											$this->log($e->getMessage() . $e->getFile() . $e->getLine());
							 | 
						|
											return 'error';
							 | 
						|
										}
							 | 
						|
								
							 | 
						|
										return $response;
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									//退款通知
							 | 
						|
									public function refund()
							 | 
						|
									{
							 | 
						|
										$agent_id = request()->route('agent_id');
							 | 
						|
										$agent = Agent::find($agent_id);
							 | 
						|
								
							 | 
						|
										$config = config('wechat.payment.default');
							 | 
						|
										$config = array_merge($config, [
							 | 
						|
											'app_id' => $agent->appid,
							 | 
						|
											'mch_id' => $agent->mchid,
							 | 
						|
											'key' => $agent->mchkey,
							 | 
						|
										]);
							 | 
						|
										$app = Factory::payment($config);
							 | 
						|
										try {
							 | 
						|
											$response = $app->handleRefundedNotify(function ($message, $reqInfo, $fail) {
							 | 
						|
												// 记录一下本地调试
							 | 
						|
												$this->log(['message' => $message, 'reqInfo' => $reqInfo], 'refund');
							 | 
						|
												// 请求成功
							 | 
						|
												if ($message['return_code'] === 'SUCCESS') {
							 | 
						|
													//主要是为了区分定金支付和尾款支付,订单号带有-status后缀,分割后前面才是真正的订单号
							 | 
						|
													list($order_no, $status) = explode('-', $message['out_trade_no']);
							 | 
						|
													$order = Order::query()
							 | 
						|
														->where(['order_no' => $order_no])
							 | 
						|
														->first();
							 | 
						|
								
							 | 
						|
													//如果已经处理过则不再处理
							 | 
						|
													if ($order->status == OrderStatus::REFUNDED) {
							 | 
						|
														return true;
							 | 
						|
													}
							 | 
						|
								
							 | 
						|
													//如果已经存在相关的退款记录,也不再处理
							 | 
						|
													$exist_log = UserMoneyLog::where([
							 | 
						|
														'user_id' => $order->user_id,
							 | 
						|
														'order_id' => $order->id,
							 | 
						|
														'type' => 2,
							 | 
						|
														'transaction_id' => $reqInfo['transaction_id'],
							 | 
						|
													])->first();
							 | 
						|
													if ($exist_log) {
							 | 
						|
														return true;
							 | 
						|
													}
							 | 
						|
								
							 | 
						|
													$log = UserMoneyLog::query()
							 | 
						|
														->where([
							 | 
						|
															'user_id' => $order->user_id,
							 | 
						|
															'order_id' => $order->id,
							 | 
						|
															'type' => 1,
							 | 
						|
															'transaction_id' => $reqInfo['transaction_id'],
							 | 
						|
														])->first();
							 | 
						|
								
							 | 
						|
													if (!$log) {
							 | 
						|
														$fail('找不到交易信息');
							 | 
						|
													}
							 | 
						|
								
							 | 
						|
													// 退款成功
							 | 
						|
													if ($reqInfo['refund_status'] === 'SUCCESS') {
							 | 
						|
														DB::beginTransaction();
							 | 
						|
														try {
							 | 
						|
															//更新订单状态为退款成功
							 | 
						|
															$order->status = OrderStatus::REFUNDED;
							 | 
						|
															$order->save();
							 | 
						|
															//记录日志
							 | 
						|
															UserMoneyLog::create([
							 | 
						|
																'user_id' => $order->user_id,
							 | 
						|
																'agent_id' => $order->agent_id,
							 | 
						|
																'money' => $reqInfo['settlement_refund_fee'] / 100,
							 | 
						|
																'order_id' => $order->id,
							 | 
						|
																'type' => 2,
							 | 
						|
																'desc' => DB::raw("LEFT('退款:{$order->title}', 250)"),
							 | 
						|
																'transaction_id' => $reqInfo['transaction_id'],
							 | 
						|
																'created_at' => now(), //模型没有updated_at,无法自动写入时间
							 | 
						|
															]);
							 | 
						|
								
							 | 
						|
															// 退库存
							 | 
						|
															Product::query()
							 | 
						|
																->where('id', $order->product_id)
							 | 
						|
																->increment('stock', $order->num);
							 | 
						|
								
							 | 
						|
															DB::commit();
							 | 
						|
															return true;
							 | 
						|
														} catch (\Exception $e) {
							 | 
						|
															DB::rollBack();
							 | 
						|
															$fail('Unknown error');
							 | 
						|
														}
							 | 
						|
													}
							 | 
						|
												}
							 | 
						|
								
							 | 
						|
												$fail('Unknown error 2');
							 | 
						|
											});
							 | 
						|
										} catch (Exception $e) {
							 | 
						|
											return 'error';
							 | 
						|
										}
							 | 
						|
								
							 | 
						|
										return $response;
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									//保存消息,用于调试
							 | 
						|
									private function log($write_data, $type = 'notify')
							 | 
						|
									{
							 | 
						|
										$dir = storage_path('wxpay');
							 | 
						|
										if (!is_dir($dir)) {
							 | 
						|
											mkdir($dir);
							 | 
						|
										}
							 | 
						|
										$filename = $dir . '/' . $type . '_' . date('Y-m-d-H') . '.log';
							 | 
						|
										$data = '[' . date('Y-m-d H:i:s') . ']' . PHP_EOL;
							 | 
						|
										$data .= '[message]: ' . (is_array($write_data) ? json_encode($write_data) : $write_data) . PHP_EOL . PHP_EOL;
							 | 
						|
										file_put_contents($filename, $data, FILE_APPEND);
							 | 
						|
									}
							 | 
						|
								}
							 |