From 089db7e168af60beff58320ad8c11ae064d174ae Mon Sep 17 00:00:00 2001 From: xiaochong0302 Date: Sun, 10 May 2020 21:29:39 +0800 Subject: [PATCH] =?UTF-8?q?=E6=95=B4=E7=90=86=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Console/Tasks/CloseTradeTask.php | 2 + app/Http/Admin/Controllers/TestController.php | 2 +- app/Http/Web/Controllers/AlipayController.php | 29 +++++++++++ app/Http/Web/Controllers/OrderController.php | 51 +++++++++++-------- app/Http/Web/Controllers/TradeController.php | 41 +++++++++++++++ app/Http/Web/Controllers/WxpayController.php | 29 +++++++++++ app/Http/Web/Services/Order.php | 9 ++++ app/Models/User.php | 14 +++++ app/Services/Frontend/Order/ConfirmInfo.php | 32 +++++------- app/Services/Frontend/Order/OrderCancel.php | 2 + app/Services/Frontend/Order/OrderCreate.php | 2 +- app/Services/Frontend/OrderTrait.php | 7 +++ app/Services/Frontend/Pay/Alipay.php | 35 +++++++++++++ app/Services/Frontend/Pay/Wxpay.php | 25 +++++++++ app/Services/Frontend/Trade/TradeCancel.php | 18 +++++++ .../Frontend/{Order => Trade}/TradeCreate.php | 28 ++++++---- app/Services/Frontend/Trade/TradeInfo.php | 34 +++++++++++++ app/Services/Frontend/TradeTrait.php | 24 +++++++++ app/Services/Pay/Alipay.php | 41 +++++++++++++-- app/Services/Pay/Wxpay.php | 11 ++-- app/Validators/Order.php | 5 ++ app/Validators/Refund.php | 26 ++++++++-- app/Validators/Trade.php | 18 +++++++ config/errors.php | 1 + 24 files changed, 422 insertions(+), 64 deletions(-) create mode 100644 app/Http/Web/Controllers/AlipayController.php create mode 100644 app/Http/Web/Controllers/TradeController.php create mode 100644 app/Http/Web/Controllers/WxpayController.php create mode 100644 app/Http/Web/Services/Order.php create mode 100644 app/Services/Frontend/Pay/Alipay.php create mode 100644 app/Services/Frontend/Pay/Wxpay.php create mode 100644 app/Services/Frontend/Trade/TradeCancel.php rename app/Services/Frontend/{Order => Trade}/TradeCreate.php (65%) create mode 100644 app/Services/Frontend/Trade/TradeInfo.php create mode 100644 app/Services/Frontend/TradeTrait.php diff --git a/app/Console/Tasks/CloseTradeTask.php b/app/Console/Tasks/CloseTradeTask.php index 7ec1bef3..a210bb5f 100644 --- a/app/Console/Tasks/CloseTradeTask.php +++ b/app/Console/Tasks/CloseTradeTask.php @@ -57,6 +57,7 @@ class CloseTradeTask extends Task if (!$allowClosed) return; $trade->status = TradeModel::STATUS_CLOSED; + $trade->update(); } @@ -88,6 +89,7 @@ class CloseTradeTask extends Task if (!$allowClosed) return; $trade->status = TradeModel::STATUS_CLOSED; + $trade->update(); } diff --git a/app/Http/Admin/Controllers/TestController.php b/app/Http/Admin/Controllers/TestController.php index 0685c219..8ea8a85a 100644 --- a/app/Http/Admin/Controllers/TestController.php +++ b/app/Http/Admin/Controllers/TestController.php @@ -186,7 +186,7 @@ class TestController extends Controller $codeUrl = null; - if ($code) { + if (!empty($code)) { $codeUrl = $this->url->get( ['for' => 'web.qrcode_img'], ['text' => urlencode($code)] diff --git a/app/Http/Web/Controllers/AlipayController.php b/app/Http/Web/Controllers/AlipayController.php new file mode 100644 index 00000000..3ea96d33 --- /dev/null +++ b/app/Http/Web/Controllers/AlipayController.php @@ -0,0 +1,29 @@ +notify(); + + if (!$response) exit; + + $response->send(); + + exit; + } + +} diff --git a/app/Http/Web/Controllers/OrderController.php b/app/Http/Web/Controllers/OrderController.php index 94726974..ec7637ad 100644 --- a/app/Http/Web/Controllers/OrderController.php +++ b/app/Http/Web/Controllers/OrderController.php @@ -2,6 +2,11 @@ namespace App\Http\Web\Controllers; +use App\Services\Frontend\Order\OrderCancel as OrderCancelService; +use App\Services\Frontend\Order\OrderConfirm as OrderConfirmService; +use App\Services\Frontend\Order\OrderCreate as OrderCreateService; +use App\Services\Frontend\Order\OrderInfo as OrderInfoService; + /** * @RoutePrefix("/order") */ @@ -13,7 +18,11 @@ class OrderController extends Controller */ public function confirmAction() { + $service = new OrderConfirmService(); + $info = $service->handle(); + + $this->view->setVar('info', $info); } /** @@ -21,47 +30,47 @@ class OrderController extends Controller */ public function createAction() { + $service = new OrderCreateService(); + $order = $service->handle(); + + return $this->jsonSuccess(['sn' => $order->sn]); } /** - * @Get("/cashier", name="web.order.cashier") + * @Get("/{sn:[0-9]+}/pay", name="web.order.pay") */ - public function cashierAction() + public function payAction($sn) { + $service = new OrderInfoService(); + $order = $service->handle($sn); + + $this->view->setVar('order', $order); } /** - * @Post("/pay", name="web.order.pay") + * @Get("/{sn:[0-9]+}/info", name="web.order.info") */ - public function payAction() + public function infoAction($sn) { + $service = new OrderInfoService(); + $order = $service->handle($sn); + + return $this->jsonSuccess(['order' => $order]); } /** - * @Post("/notify/{channel}", name="web.order.notify") + * @Post("/{sn:[0-9]+}/cancel", name="web.order.cancel") */ - public function notifyAction($channel) + public function cancelAction($sn) { + $service = new OrderCancelService(); - } - - /** - * @Post("/status", name="web.order.status") - */ - public function statusAction() - { - - } - - /** - * @Post("/cancel", name="web.order.cancel") - */ - public function cancelAction() - { + $order = $service->handle($sn); + return $this->jsonSuccess(['order' => $order]); } } diff --git a/app/Http/Web/Controllers/TradeController.php b/app/Http/Web/Controllers/TradeController.php new file mode 100644 index 00000000..f41f8b0f --- /dev/null +++ b/app/Http/Web/Controllers/TradeController.php @@ -0,0 +1,41 @@ +handle(); + + return $this->jsonSuccess([ + 'trade_sn' => $result['trade_sn'], + 'code_url' => $result['code_url'], + ]); + } + + /** + * @Get("/{sn:[0-9]+}/status", name="web.trade.status") + */ + public function statusAction($sn) + { + $service = new TradeInfoService(); + + $trade = $service->handle($sn); + + return $this->jsonSuccess(['status' => $trade->status]); + } + +} diff --git a/app/Http/Web/Controllers/WxpayController.php b/app/Http/Web/Controllers/WxpayController.php new file mode 100644 index 00000000..bc6274ab --- /dev/null +++ b/app/Http/Web/Controllers/WxpayController.php @@ -0,0 +1,29 @@ +notify(); + + if (!$response) exit; + + $response->send(); + + exit; + } + +} diff --git a/app/Http/Web/Services/Order.php b/app/Http/Web/Services/Order.php new file mode 100644 index 00000000..bbaf6c89 --- /dev/null +++ b/app/Http/Web/Services/Order.php @@ -0,0 +1,9 @@ +checkCourse($itemId); - $courseInfo = $this->handleCourseInfo($course); - $result['item_info']['course'] = $courseInfo; + $result['item_info']['course'] = $this->handleCourse($course); $result['amount'] = $user->vip ? $course->vip_price : $course->market_price; } elseif ($itemType == OrderModel::ITEM_PACKAGE) { $package = $validator->checkPackage($itemId); - $packageInfo = $this->handlePackageInfo($package); - $result['item_info']['package'] = $packageInfo; + $result['item_info']['package'] = $this->handlePackage($package); $result['amount'] = $user->vip ? $package->vip_price : $package->market_price; } elseif ($itemType == OrderModel::ITEM_VIP) { $vip = $validator->checkVip($itemId); - $vipInfo = $this->handleVipInfo($vip); - $result['item_info']['vip'] = $vipInfo; + $result['item_info']['vip'] = $this->handleVip($vip); $result['amount'] = $vip->price; } elseif ($itemType == OrderModel::ITEM_REWARD) { @@ -61,11 +58,8 @@ class ConfirmInfo extends Service $course = $validator->checkCourse($courseId); $reward = $validator->checkReward($rewardId); - $courseInfo = $this->handleCourseInfo($course); - $rewardInfo = $this->handleRewardInfo($reward); - - $result['item_info']['course'] = $courseInfo; - $result['item_info']['reward'] = $rewardInfo; + $result['item_info']['course'] = $this->handleCourse($course); + $result['item_info']['reward'] = $this->handleReward($reward); $result['amount'] = $reward->price; } @@ -74,12 +68,12 @@ class ConfirmInfo extends Service return $result; } - protected function handleCourseInfo(CourseModel $course) + protected function handleCourse(CourseModel $course) { - return $this->formatCourseInfo($course); + return $this->formatCourse($course); } - protected function handlePackageInfo(PackageModel $package) + protected function handlePackage(PackageModel $package) { $result = [ 'id' => $package->id, @@ -93,13 +87,13 @@ class ConfirmInfo extends Service $courses = $packageRepo->findCourses($package->id); foreach ($courses as $course) { - $result['courses'][] = $this->formatCourseInfo($course); + $result['courses'][] = $this->formatCourse($course); } return $result; } - protected function handleVipInfo(VipModel $vip) + protected function handleVip(VipModel $vip) { return [ 'id' => $vip->id, @@ -109,7 +103,7 @@ class ConfirmInfo extends Service ]; } - protected function handleRewardInfo(RewardModel $reward) + protected function handleReward(RewardModel $reward) { return [ 'id' => $reward->id, @@ -118,7 +112,7 @@ class ConfirmInfo extends Service ]; } - protected function formatCourseInfo(CourseModel $course) + protected function formatCourse(CourseModel $course) { $course->cover = kg_ci_img_url($course->cover); diff --git a/app/Services/Frontend/Order/OrderCancel.php b/app/Services/Frontend/Order/OrderCancel.php index ad217268..2fa5e087 100644 --- a/app/Services/Frontend/Order/OrderCancel.php +++ b/app/Services/Frontend/Order/OrderCancel.php @@ -27,6 +27,8 @@ class OrderCancel extends Service $order->status = OrderModel::STATUS_CLOSED; $order->update(); + + return $order; } } diff --git a/app/Services/Frontend/Order/OrderCreate.php b/app/Services/Frontend/Order/OrderCreate.php index 9be78acd..b706ff86 100644 --- a/app/Services/Frontend/Order/OrderCreate.php +++ b/app/Services/Frontend/Order/OrderCreate.php @@ -40,7 +40,7 @@ class OrderCreate extends Service */ if ($order) { $caseA = $order->status == OrderModel::STATUS_PENDING; - $caseB = time() - $order->create_time < 6 * 3600; + $caseB = time() - $order->create_time < 12 * 3600; if ($caseA && $caseB) { return $order; } diff --git a/app/Services/Frontend/OrderTrait.php b/app/Services/Frontend/OrderTrait.php index ce41f3ab..0d37b853 100644 --- a/app/Services/Frontend/OrderTrait.php +++ b/app/Services/Frontend/OrderTrait.php @@ -7,6 +7,13 @@ use App\Validators\Order as OrderValidator; trait OrderTrait { + public function checkOrderById($id) + { + $validator = new OrderValidator(); + + return $validator->checkOrderById($id); + } + public function checkOrderBySn($sn) { $validator = new OrderValidator(); diff --git a/app/Services/Frontend/Pay/Alipay.php b/app/Services/Frontend/Pay/Alipay.php new file mode 100644 index 00000000..13f007c3 --- /dev/null +++ b/app/Services/Frontend/Pay/Alipay.php @@ -0,0 +1,35 @@ +scan($trade); + + if ($text) { + $qrCodeUrl = $this->url->get( + ['for' => 'web.qrcode_img'], + ['text' => urlencode($text)] + ); + } + + return $qrCodeUrl; + } + + public function wap(TradeModel $trade) + { + + } + +} diff --git a/app/Services/Frontend/Pay/Wxpay.php b/app/Services/Frontend/Pay/Wxpay.php new file mode 100644 index 00000000..67c9c18e --- /dev/null +++ b/app/Services/Frontend/Pay/Wxpay.php @@ -0,0 +1,25 @@ +create(); - $qrCode = $this->getQrCode($trade); + $qrCodeUrl = $this->getQrCodeUrl($trade); $this->db->commit(); - return $qrCode; + return [ + 'trade_sn' => $trade->sn, + 'code_url' => $qrCodeUrl, + ]; } catch (\Exception $e) { $this->db->rollback(); - return false; + throw new \RuntimeException('trade.create_failed'); } } - protected function getQrCode(TradeModel $trade) + protected function getQrCodeUrl(TradeModel $trade) { - $qrCode = null; + $qrCodeUrl = null; if ($trade->channel == TradeModel::CHANNEL_ALIPAY) { $alipayService = new AlipayService(); - $qrCode = $alipayService->scan($trade); + $text = $alipayService->scan($trade); + + if ($text) { + $qrCodeUrl = $this->url->get( + ['for' => 'web.qrcode_img'], + ['text' => urlencode($text)] + ); + } } elseif ($trade->channel == TradeModel::CHANNEL_WXPAY) { $wxpayService = new WxPayService(); - $qrCode = $wxpayService->scan($trade); + $qrCodeUrl = $wxpayService->scan($trade); } - return $qrCode; + return $qrCodeUrl; } } diff --git a/app/Services/Frontend/Trade/TradeInfo.php b/app/Services/Frontend/Trade/TradeInfo.php new file mode 100644 index 00000000..67fda454 --- /dev/null +++ b/app/Services/Frontend/Trade/TradeInfo.php @@ -0,0 +1,34 @@ +checkTradeBySn($sn); + + return $this->handleTrade($trade); + } + + protected function handleTrade(TradeModel $trade) + { + return [ + 'id' => $trade->id, + 'sn' => $trade->sn, + 'subject' => $trade->subject, + 'amount' => $trade->amount, + 'channel' => $trade->channel, + 'status' => $trade->status, + 'create_time' => $trade->create_time, + ]; + } + +} diff --git a/app/Services/Frontend/TradeTrait.php b/app/Services/Frontend/TradeTrait.php new file mode 100644 index 00000000..a45d133b --- /dev/null +++ b/app/Services/Frontend/TradeTrait.php @@ -0,0 +1,24 @@ +checkTrade($id); + } + + public function checkTradeBySn($id) + { + $validator = new TradeValidator(); + + return $validator->checkTradeBySn($id); + } + +} diff --git a/app/Services/Pay/Alipay.php b/app/Services/Pay/Alipay.php index 2c228ea1..8c7da3b4 100644 --- a/app/Services/Pay/Alipay.php +++ b/app/Services/Pay/Alipay.php @@ -5,12 +5,13 @@ namespace App\Services\Pay; use App\Models\Refund as RefundModel; use App\Models\Trade as TradeModel; use App\Repos\Trade as TradeRepo; -use App\Services\Pay as AppPay; +use App\Services\Pay as PayService; +use Yansongda\Pay\Gateways\Alipay as AlipayGateway; use Yansongda\Pay\Log; use Yansongda\Pay\Pay; use Yansongda\Supports\Collection; -class Alipay extends AppPay +class Alipay extends PayService { /** @@ -19,13 +20,14 @@ class Alipay extends AppPay protected $settings; /** - * @var \Yansongda\Pay\Gateways\Alipay + * @var AlipayGateway */ protected $gateway; public function __construct() { $this->settings = $this->getSectionSettings('pay.alipay'); + $this->gateway = $this->getGateway(); } @@ -60,6 +62,37 @@ class Alipay extends AppPay return $result; } + /** + * 移动端支付 + * + * @param TradeModel $trade + * @return bool|string + */ + public function wap(TradeModel $trade) + { + try { + + $response = $this->gateway->wap([ + 'out_trade_no' => $trade->sn, + 'total_amount' => $trade->amount, + 'subject' => $trade->subject, + ]); + + $result = $response->qr_code ?? false; + + } catch (\Exception $e) { + + Log::error('Alipay Qrcode Exception', [ + 'code' => $e->getCode(), + 'message' => $e->getMessage(), + ]); + + $result = false; + } + + return $result; + } + /** * 异步通知 */ @@ -236,7 +269,7 @@ class Alipay extends AppPay /** * 获取 Gateway * - * @return \Yansongda\Pay\Gateways\Alipay + * @return AlipayGateway */ public function getGateway() { diff --git a/app/Services/Pay/Wxpay.php b/app/Services/Pay/Wxpay.php index 610f036b..ab412cac 100644 --- a/app/Services/Pay/Wxpay.php +++ b/app/Services/Pay/Wxpay.php @@ -5,13 +5,13 @@ namespace App\Services\Pay; use App\Models\Refund as RefundModel; use App\Models\Trade as TradeModel; use App\Repos\Trade as TradeRepo; -use App\Services\Pay as AppPay; -use Yansongda\Pay\Gateways\Wechat; +use App\Services\Pay as PayService; +use Yansongda\Pay\Gateways\Wechat as WechatGateway; use Yansongda\Pay\Log; use Yansongda\Pay\Pay; use Yansongda\Supports\Collection; -class Wxpay extends AppPay +class Wxpay extends PayService { /** @@ -20,13 +20,14 @@ class Wxpay extends AppPay protected $settings; /** - * @var Wechat + * @var WechatGateway */ protected $gateway; public function __construct() { $this->settings = $this->getSectionSettings('pay.wxpay'); + $this->gateway = $this->getGateway(); } @@ -218,7 +219,7 @@ class Wxpay extends AppPay /** * 获取 Gateway * - * @return Wechat + * @return WechatGateway */ public function getGateway() { diff --git a/app/Validators/Order.php b/app/Validators/Order.php index 2fd8ff92..d14dbe8d 100644 --- a/app/Validators/Order.php +++ b/app/Validators/Order.php @@ -13,6 +13,11 @@ use App\Repos\Vip as VipRepo; class Order extends Validator { + public function checkOrder($id) + { + return $this->checkOrderById($id); + } + public function checkOrderById($id) { $orderRepo = new OrderRepo(); diff --git a/app/Validators/Refund.php b/app/Validators/Refund.php index 87bdee7a..1e4cc917 100644 --- a/app/Validators/Refund.php +++ b/app/Validators/Refund.php @@ -11,15 +11,33 @@ class Refund extends Validator public function checkRefund($id) { - $tradeRepo = new RefundRepo(); + return $this->checkRefundById($id); + } - $trade = $tradeRepo->findById($id); + public function checkRefundById($id) + { + $refundRepo = new RefundRepo(); - if (!$trade) { + $refund = $refundRepo->findById($id); + + if (!$refund) { throw new BadRequestException('refund.not_found'); } - return $trade; + return $refund; + } + + public function checkRefundBySn($sn) + { + $refundRepo = new RefundRepo(); + + $refund = $refundRepo->findById($sn); + + if (!$refund) { + throw new BadRequestException('refund.not_found'); + } + + return $refund; } public function checkReviewStatus($status) diff --git a/app/Validators/Trade.php b/app/Validators/Trade.php index 3e809a05..160c2dd2 100644 --- a/app/Validators/Trade.php +++ b/app/Validators/Trade.php @@ -11,6 +11,11 @@ class Trade extends Validator { public function checkTrade($id) + { + return $this->checkTradeById($id); + } + + public function checkTradeById($id) { $tradeRepo = new TradeRepo(); @@ -23,6 +28,19 @@ class Trade extends Validator return $trade; } + public function checkTradeBySn($sn) + { + $tradeRepo = new TradeRepo(); + + $trade = $tradeRepo->findBySn($sn); + + if (!$trade) { + throw new BadRequestException('trade.not_found'); + } + + return $trade; + } + public function checkChannel($channel) { $list = TradeModel::channelTypes(); diff --git a/config/errors.php b/config/errors.php index eb4373cc..1ccb8a5d 100644 --- a/config/errors.php +++ b/config/errors.php @@ -264,6 +264,7 @@ $error['order.trade_expired'] = '交易已过期'; * 交易相关 */ $error['trade.not_found'] = '交易不存在'; +$error['trade.create_failed'] = '创建交易失败'; $error['trade.invalid_channel'] = '无效的平台类型'; $error['trade.invalid_close_action'] = '当前不允许关闭交易'; $error['trade.invalid_refund_action'] = '当前不允许交易退款';