diff --git a/app/Cache/Redis/Api/ApiRedisKey.php b/app/Cache/Redis/Api/ApiRedisKey.php index df00fa2..c65ec74 100644 --- a/app/Cache/Redis/Api/ApiRedisKey.php +++ b/app/Cache/Redis/Api/ApiRedisKey.php @@ -55,4 +55,13 @@ class ApiRedisKey { return 'good:list:stock:cycle_id:'.$cycleId.':kitchen_id:'.$kitchenId; } + + /** + * @param int $userId + * @return string + */ + public static function payLock(int $userId) + { + return 'lock:pay:user_id:'. $userId; + } } \ No newline at end of file diff --git a/app/Constants/Common/PayCode.php b/app/Constants/Common/PayCode.php new file mode 100644 index 0000000..3540a6b --- /dev/null +++ b/app/Constants/Common/PayCode.php @@ -0,0 +1,18 @@ +handle(); } #[RequestMapping(path: 'refund',methods: 'POST')] diff --git a/app/Extend/SystemUtil.php b/app/Extend/SystemUtil.php index 3d6e398..569bed0 100644 --- a/app/Extend/SystemUtil.php +++ b/app/Extend/SystemUtil.php @@ -12,7 +12,7 @@ class SystemUtil * prod 1=生产环境 0=开发环境 * @return bool */ - static function checkProEnv() + static function checkProEnv(): bool { return Env('APP_ENV') == 'prod'; } @@ -21,7 +21,7 @@ class SystemUtil * 获取客户端 ip * @return mixed|string */ - static function getClientIp() + static function getClientIp(): mixed { $request = Context::get(ServerRequestInterface::class); diff --git a/app/Model/PayOrder.php b/app/Model/PayOrder.php new file mode 100644 index 0000000..efce18e --- /dev/null +++ b/app/Model/PayOrder.php @@ -0,0 +1,56 @@ + 'integer', 'user_id' => 'integer', 'order_id' => 'integer', 'order_type' => 'integer', 'recharge_type' => 'integer', 'status' => 'integer']; + + const string CREATED_AT = 'create_time'; + + const null UPDATED_AT = null; + + /** + * @param int $id + * @param int $type + * @return \Hyperf\Database\Model\Model|null + */ + public function getInfoByOrderIdAndType(int $id,int $type): \Hyperf\Database\Model\Model|null + { + return $this->where('order_id',$id)->where('order_type',$type)->first(); + } +} diff --git a/app/Service/Admin/System/SiteService.php b/app/Service/Admin/System/SiteService.php index f36eceb..dfaead7 100644 --- a/app/Service/Admin/System/SiteService.php +++ b/app/Service/Admin/System/SiteService.php @@ -144,7 +144,7 @@ class SiteService extends BaseService /** * @return array */ - public function add() + public function add(): array { $name = $this->request->input('name'); $this->imageId = (int)$this->request->input('image_id'); diff --git a/app/Service/Api/Order/PlaceOrderService.php b/app/Service/Api/Order/PlaceOrderService.php index d8965b0..e7d96c3 100644 --- a/app/Service/Api/Order/PlaceOrderService.php +++ b/app/Service/Api/Order/PlaceOrderService.php @@ -46,6 +46,8 @@ class PlaceOrderService extends BaseOrderService */ public function handle(): array { + //todo 加防抖 + $this->siteId = (int)$this->request->input('site_id'); $this->couponId = (int)$this->request->input('coupon_id',0); diff --git a/app/Service/Api/Pay/OrderTypeFactory.php b/app/Service/Api/Pay/OrderTypeFactory.php new file mode 100644 index 0000000..a24d4f2 --- /dev/null +++ b/app/Service/Api/Pay/OrderTypeFactory.php @@ -0,0 +1,33 @@ + new Order(), + OrderCode::ORDER_TYPE_BALANCE => new Order(), + default => throw new ErrException('order_type不对'), + }; + } +} \ No newline at end of file diff --git a/app/Service/Api/Pay/PlacePayService.php b/app/Service/Api/Pay/PlacePayService.php new file mode 100644 index 0000000..de99d50 --- /dev/null +++ b/app/Service/Api/Pay/PlacePayService.php @@ -0,0 +1,195 @@ +lockKey = ApiRedisKey::payLock($this->userId); + } + + /** + * @return array + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface + */ + public function handle(): array + { + if (0 == ($this->redisCache->addLock($this->lockKey,5))) { + throw new ErrException('请勿重复点击'); + } + + $this->orderId = (int)$this->request->input('order_id'); + $this->orderType = (int)$this->request->input('order_type'); + $this->payType = (int)$this->request->input('pay_type'); + + $this->orderModel = (new OrderTypeFactory)->getPayOrderModel($this->payType); + + $this->checkOrder(); + + $this->setPayInfo(); + + $rechargeService = match ($this->payType) + { + PayCode::ALIPAY => $this->setAliPayOrder(), + PayCode::WECHAT_PAY => $this->setWechatPayOrder(), + }; + + $rechargeService->setConfig(); + $rechargeService->setNotify(); + + $payData = $rechargeService->pay( + (float)$this->orderInfo->actual_price, + $this->request->input('body','订单支付'), + $this->orderInfo->order_no, + $this->userId + ); + + $res = [ + 'orderId' => $this->orderId, + ]; + + //返回支付数组 + match ($this->payType) { + PayCode::WECHAT_PAY => $res['weixin'] = $payData, + PayCode::ALIPAY => throw new ErrException('暂不开放') + }; + + $this->redisCache->delLock($this->lockKey); + + return $this->return->success('success', $res); + } + + private function setAliPayOrder(): null + { + return null; + } + + /** + * @return WxJsRechargeOrderService + */ + private function setWechatPayOrder(): WxJsRechargeOrderService + { + $rechargeService = new WxJsRechargeOrderService(); + + if (empty($rechargeService)) { + throw new ErrException('充值异常'); + } + + $rechargeService->openId = '123'; + + return $rechargeService; + } + + /** + * @var PayOrder + */ + #[Inject] + protected PayOrder $payOrderModel; + + /** + * @return void + */ + private function setPayInfo(): void + { + if (!empty($this->payInfo)) return; + + $this->payInfo = new PayOrder(); + + $this->payInfo->user_id = $this->userId; + $this->payInfo->order_id = $this->orderId; + $this->payInfo->order_type = $this->orderType; + $this->payInfo->order_no = $this->orderInfo->order_no; + $this->payInfo->pay_money = $this->orderInfo->actual_price; + $this->payInfo->recharge_type = $this->payType; + $this->payInfo->status = PayCode::WAIT_PAY; + + $this->payInfo->save(); + } + + /** + * @return void + */ + private function checkOrder(): void + { + if (empty($this->orderModel)) throw new ErrException('order type不对'); + + $this->orderInfo = $this->orderModel->where('id',$this->orderId)->first(); + if (empty($this->orderInfo)) throw new ErrException('该订单为空'); + if ($this->orderInfo->user_id != $this->userId) throw new ErrException('该订单不属于你'); + if ($this->orderInfo->status != OrderCode::WAIT_PAY) throw new ErrException('该订单已支付或已取消,请确认后重试'); + + $this->payInfo = $this->payOrderModel->getInfoByOrderIdAndType($this->orderId,$this->orderType); + if (empty($this->payInfo)) return; + if ($this->payInfo->status == PayCode::FINISH_PAY) throw new ErrException('该订单已支付,请确认后重试'); + if ($this->payInfo->recharge_type != $this->payType) throw new ErrException('该订单调起支付失败'); + if ($this->payInfo->order_type != $this->orderType) throw new ErrException('传值错误'); + } +} \ No newline at end of file diff --git a/app/Service/Common/Pay/ThirdPayInterface.php b/app/Service/Common/Pay/ThirdPayInterface.php index 0f04d58..9e24310 100644 --- a/app/Service/Common/Pay/ThirdPayInterface.php +++ b/app/Service/Common/Pay/ThirdPayInterface.php @@ -12,5 +12,5 @@ namespace App\Service\Common\Pay; interface ThirdPayInterface { - public function pay(float $money,int $orderId,int $orderType,string $outTradeNo,int $userId); + public function pay(float $money,int $orderId,string $outTradeNo,int $userId); } \ No newline at end of file diff --git a/app/Service/Common/Pay/Wx/WxJsRechargeBaseService.php b/app/Service/Common/Pay/Wx/WxJsRechargeBaseService.php index 38c1d87..eaee1cd 100644 --- a/app/Service/Common/Pay/Wx/WxJsRechargeBaseService.php +++ b/app/Service/Common/Pay/Wx/WxJsRechargeBaseService.php @@ -18,6 +18,11 @@ use function Hyperf\Config\config; abstract class WxJsRechargeBaseService implements ThirdPayInterface { + /** + * @var string + */ + public string $openId; + /** * 回调地址 * @var string @@ -110,7 +115,7 @@ abstract class WxJsRechargeBaseService implements ThirdPayInterface /** * @return void */ - private function setConfig(): void + public function setConfig(): void { $this->config = config('ysdPay'); } @@ -118,17 +123,16 @@ abstract class WxJsRechargeBaseService implements ThirdPayInterface /** * @param float $money * @param int $orderId - * @param int $orderType * @param string $outTradeNo * @param int $userId * @return Collection|Rocket * @throws ContainerExceptionInterface * @throws NotFoundExceptionInterface */ - public function pay(float $money, int $orderId, int $orderType, string $outTradeNo, int $userId): Collection|Rocket + public function pay(float $money, int $orderId, string $outTradeNo, int $userId): Collection|Rocket { if (empty($this->config)) throw new ErrException('调起支付失败-微信支付配置项不存在'); - $userOpenId = '123'; + try { $wxOrder = [ 'out_trade_no' => $outTradeNo, @@ -138,7 +142,7 @@ abstract class WxJsRechargeBaseService implements ThirdPayInterface 'currency' => 'CNY' ], 'payer' => [ - 'openid' => $userOpenId, + 'openid' => $this->openId, ] ]; diff --git a/app/Service/Common/Pay/Wx/WxJsRechargeOrderService.php b/app/Service/Common/Pay/Wx/WxJsRechargeOrderService.php new file mode 100644 index 0000000..5b02212 --- /dev/null +++ b/app/Service/Common/Pay/Wx/WxJsRechargeOrderService.php @@ -0,0 +1,20 @@ +config['wechat']['default']['notify_url'] = config('system.api_url').'/common/wxPay/order/js/callBack'; + } +} \ No newline at end of file diff --git a/config/autoload/system.php b/config/autoload/system.php index addb8d6..78916cf 100644 --- a/config/autoload/system.php +++ b/config/autoload/system.php @@ -25,4 +25,6 @@ return [ 'wx_appid' => env('WX_APPID','wxc2bc0e47e212d831'), // 微信小程序的secret 'wx_secret' => env('WX_SECRET','5d89ebae17d836625f0e155216d71f2d'), + //api_url + 'api_url' => env('DEFAULT_API_URL','http://127.0.0.1:9501'), ]; \ No newline at end of file