diff --git a/app/Controller/Payment/PayController.php b/app/Controller/Payment/PayController.php index 1e8e4ef..597f663 100644 --- a/app/Controller/Payment/PayController.php +++ b/app/Controller/Payment/PayController.php @@ -60,4 +60,9 @@ class PayController extends AbstractController $data = $this->paymentService->paymentQuery($request->all()); return $this->success($data); } + + public function payment(RequestInterface $request) { + $data = $this->paymentService->payment($request->all()); + return $this->success($data); + } } diff --git a/app/Controller/Recharge/RechargeController.php b/app/Controller/Recharge/RechargeController.php index 00f9337..ac8a7cc 100644 --- a/app/Controller/Recharge/RechargeController.php +++ b/app/Controller/Recharge/RechargeController.php @@ -11,6 +11,7 @@ use App\Helper\StringHelper; use App\Model\App; use App\Model\BankCard; use App\Model\Order; +use App\Model\PrePayLog; use App\Model\User; use App\Service\PaymentService; use Hyperf\HttpServer\Contract\RequestInterface; @@ -24,8 +25,23 @@ class RechargeController extends AbstractController $this->paymentService = $paymentService; } + public function getMemberInfo(RequestInterface $request) { + [$app, $prePayLog] = $this->checkToken($request->input('token', '')); + $user = User::where('out_member_id', $prePayLog->out_member_id)->first(); + $bankCard = null; + if ($user) { + $bankCard = BankCard::where('member_id', $user->member_id)->where('status', BankCard::STATUS_ACTIVE)->first(); + } + return $this->success([ + 'name' => $user->real_name ?? '', + 'card_no' => $user->card_no ?? '', + 'mobile' => $user->mobile ?? '', + 'bank_card_no' => $bankCard->bank_card_no ?? '', + ]); + } + public function recharge(RequestInterface $request) { - $app = App::query()->orderBy('id', 'asc')->first(); + [$app, $prePayLog] = $this->checkToken($request->input('token', '')); $name = $request->input('name'); if (empty($name)) { throw new BusinessException('请输入姓名'); @@ -60,47 +76,49 @@ class RechargeController extends AbstractController } if ($bankCard) { - $outTradeNo = StringHelper::generateOrderNo(); + $outTradeNo = $prePayLog->out_order_no; $nextStep = 'confirm-pay'; - $bizData = $this->paymentService->protocolPayPreRequest($this->buildPrepayParams($user->member_id, $outTradeNo, $bankCard->protocol, intval($amount * 100), $app)); + $bizData = $this->paymentService->protocolPayPreRequest($this->buildPrepayParams($prePayLog->out_member_id, $outTradeNo, $bankCard->protocol, intval($amount * 100), $app)); } else { $mchOrderNo = StringHelper::generateBankCardOrderNo(); $nextStep = 'confirm-bind'; - $bizData = $this->paymentService->bindCard($this->buildBindCardParams($mchOrderNo, $memberId, $name, $cardNo, $mobile, $bankCardNo, $app)); + $bizData = $this->paymentService->bindCard($this->buildBindCardParams( + $mchOrderNo, $prePayLog->out_member_id, $name, $cardNo, $mobile, $bankCardNo, $app + )); } return $this->success([ 'nextStep' => $nextStep, - 'memberId' => $memberId, + 'outMemberId' => $memberId, 'bizData' => $bizData ]); } public function confirmBindCard(RequestInterface $request) { - $app = App::query()->orderBy('id', 'asc')->first(); - $memberId = $request->input('memberId'); + [$app, $prePayLog] = $this->checkToken($request->input('token', '')); + $outMemberId = $request->input('outMemberId'); $smsNo = $request->input('smsNo'); $smsCode = $request->input('smsCode'); - $bizData = $this->paymentService->bindCardConfirm($this->buildComfirmBindCardParams($memberId, $smsNo, $smsCode, $app)); + $bizData = $this->paymentService->bindCardConfirm($this->buildComfirmBindCardParams($outMemberId, $smsNo, $smsCode, $app)); return $this->success([ - 'memberId' => $memberId, + 'outMemberId' => $outMemberId, 'bizData' => $bizData ]); } public function confirmPay(RequestInterface $request) { - $app = App::query()->orderBy('id', 'asc')->first(); - $memberId = $request->input('memberId'); + [$app, $prePayLog] = $this->checkToken($request->input('token', '')); + $outMemberId = $request->input('outMemberId'); $token = $request->input('token'); $protocol = $request->input('protocol'); $smsCode = $request->input('smsCode'); $bizData = $this->paymentService->protocolPayConfirm($this->buildConfirmPayParams($token, $protocol, $smsCode, $app)); return $this->success([ - 'memberId' => $memberId, + 'outMemberId' => $outMemberId, 'bizData' => ['outTradeNo' => $bizData['outTradeNo']] ]); } - private function buildBindCardParams($mchOrderNo, $memberId, $name, $cardNo, $mobile, $bankCardNo, $app) + private function buildBindCardParams($mchOrderNo, $outMemberId, $name, $cardNo, $mobile, $bankCardNo, $app) { $params = [ 'app_id' => $app->app_id, @@ -108,7 +126,7 @@ class RechargeController extends AbstractController 'nonce_str' => StringHelper::getRandomString(32), 'data' => json_encode([ 'mchtOrderNo' => $mchOrderNo, - 'memberId' => $memberId, + 'outMemberId' => $outMemberId, 'userName' => $name, 'phoneNum' => $mobile, 'bankCardNo' => $bankCardNo, @@ -121,14 +139,14 @@ class RechargeController extends AbstractController return $params; } - private function buildPrepayParams($memberId, $outTradeNo, $protocol, $amount, $app) + private function buildPrepayParams($outMemberId, $outTradeNo, $protocol, $amount, $app) { $params = [ 'app_id' => $app->app_id, 'timestamp' => time(), 'nonce_str' => StringHelper::getRandomString(32), 'data' => json_encode([ - 'memberId' => $memberId, + 'outMemberId' => $outMemberId, 'outTradeNo' => $outTradeNo, 'protocol' => $protocol, 'payAmount' => $amount, @@ -156,14 +174,14 @@ class RechargeController extends AbstractController return $params; } - private function buildComfirmBindCardParams($memberId, $smsNo, $smsCode, $app) + private function buildComfirmBindCardParams($outMemberId, $smsNo, $smsCode, $app) { $params = [ 'app_id' => $app->app_id, 'timestamp' => time(), 'nonce_str' => StringHelper::getRandomString(32), 'data' => json_encode([ - 'memberId' => $memberId, + 'outMemberId' => $outMemberId, 'smsNo' => $smsNo, 'smsCode' => $smsCode, ]), @@ -240,4 +258,29 @@ class RechargeController extends AbstractController 'ioexlp2' => 'wwU8Ir3Xp0rxXssA9NV' ]; } + + private function checkToken($token) { + $app = App::query()->orderBy('id', 'asc')->first(); + return $app; + + if (empty($token)) { + throw new BusinessException('token异常'); + } + $log = PrePayLog::where('token', $token)->first(); + if (empty($log)) { + throw new BusinessException('预支付记录不存在'); + } + if ($log->created_at->timestamp < time() - 5*60) { + throw new BusinessException('支付超时,请重新请求'); + } + $order = Order::where('app_id', $log->app_id)->where('out_order_no', $log->out_order_no)->first(); + if ($order) { + throw new BusinessException('订单重复,请重新请求'); + } + $app = App::where('app_id', $log->app_id)->first(); + if (empty($app)) { + throw new BusinessException('应用异常'); + } + return [$app, $log]; + } } diff --git a/app/Model/PrePayLog.php b/app/Model/PrePayLog.php new file mode 100644 index 0000000..d4f991c --- /dev/null +++ b/app/Model/PrePayLog.php @@ -0,0 +1,10 @@ +app_id = $app->app_id; + $order->order_no = StringHelper::generateOrderNo(); + $order->member_id = $user->member_id; + $order->out_member_id = $params['outMemberId'] ?? ''; $order->out_order_no = $params['outTradeNo'] ?? ''; $order->amount = $params['payAmount'] ?? 0; $order->notify_url = $params['notifyUrl'] ?? ''; @@ -71,17 +77,6 @@ class PaymentService extends AbstractService } } - private function generateOrderNo() { - $now = time(); - $key = RedisKey::getGenerateOrderNoKey($now); - $incrId = Redis::incr($key); - $incrId = '' . $incrId; - Redis::expire($key, 5*60); - $padLength = 6 - strlen($incrId); - $incrId = str_pad($incrId, $padLength, '0', STR_PAD_LEFT); - return date('YmdHis', $now) . $incrId; - } - public function protocolPayPreRequest($params) { $req = new ProtocolPayPreRequest($params); @@ -89,7 +84,11 @@ class PaymentService extends AbstractService $data = $req->getData(); $result = Api::protocolPayPre($data); - $order = $this->createOrder($app, $data); + $user = User::where('app_id', $app->app_id)->where('out_member_id', $data['outMemberId'])->first(); + if (!$user) { + throw new BusinessException('用户不存在'); + } + $order = $this->createOrder($app, $data, $user); if (!$result->isSuccess()) { $order->status = Order::STATUS_APPLY_FAIL; @@ -156,14 +155,23 @@ class PaymentService extends AbstractService if (!$user) { $user = new User(); $user->member_id = $memberId; + $user->out_member_id = $reqData['outMemberId']; $user->app_id = $app->app_id; $user->real_name = $reqData['userName']; $user->card_no = $reqData['certificatesNo']; $user->mobile = $reqData['phoneNum']; $user->save(); } + if ($user && empty($user->out_member_id)) { + $user->out_member_id = $reqData['outMemberId']; + $user->save(); + } + if ($user && $user->out_member_id && $user->out_member_id != $reqData['outMemberId']) { + throw new BusinessException('身份证号重复!'); + } $bankCard = new BankCard(); $bankCard->member_id = $memberId; + $bankCard->out_member_id = $reqData['outMemberId']; $bankCard->app_id = $app->app_id; $bankCard->real_name = $reqData['userName']; $bankCard->card_no = $reqData['certificatesNo']; @@ -186,7 +194,7 @@ class PaymentService extends AbstractService $reqData = $req->getData(); $bankCard = BankCard::where('app_id', $app->app_id) - ->where('member_id', $reqData['memberId']) + ->where('out_member_id', $reqData['outMemberId']) ->where('sms_no', $reqData['smsNo']) ->where('status', BankCard::STATUS_WAIT_CONFIRM) ->first(); @@ -308,4 +316,40 @@ class PaymentService extends AbstractService return $result->getData(); } + + public function payment($params) + { + $req = new PaymentRequest($params); + $app = $req->getApp(); + $reqData = $req->getData(); + + if (!$reqData['outOrderNo']) { + throw new BusinessException('订单号错误'); + } + if (!$reqData['outMemberId']) { + throw new BusinessException('用户ID不能为空'); + } + if (!$reqData['notifyUrl']) { + throw new BusinessException('通知地址不能为空'); + } + + $log = PrePayLog::where('app_id', $app->app_id)->where('out_order_no', $reqData['outOrderNo'])->first(); + $order = Order::where('app_id', $app->app_id)->where('out_order_no', $reqData['outOrderNo'])->first(); + if ($log || $order) { + throw new BusinessException('订单重复'); + } + + $token = md5(microtime(true) . json_encode($reqData) . $app->app_id . $app->app_key); + + $log = new PrePayLog(); + $log->app_id = $app->app_id; + $log->out_order_no = $reqData['outOrderNo']; + $log->out_member_id = $reqData['outMemberId']; + $log->notify_url = $reqData['notifyUrl']; + $log->redirect_url = $reqData['redirectUrl'] ?? ''; + $log->token = $token; + $log->save(); + + return ['payUrl' => 'http://146.70.113.165:9501/payment.html?token=' . $token]; + } } diff --git a/payment.sql b/payment.sql index 1d26672..c868828 100644 --- a/payment.sql +++ b/payment.sql @@ -108,4 +108,23 @@ CREATE TABLE `refund_orders` ( KEY `idx_outrefundorderno` (`out_refund_order_no`) USING BTREE, KEY `idx_createdat` (`created_at`) USING BTREE, PRIMARY KEY (`id`) USING BTREE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 ROW_FORMAT=DYNAMIC COMMENT='退款订单表'; \ No newline at end of file +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 ROW_FORMAT=DYNAMIC COMMENT='退款订单表'; + +CREATE TABLE `pre_pay_logs` ( + `id` int unsigned NOT NULL AUTO_INCREMENT COMMENT '用户ID', + `app_id` varchar(16) NOT NULL COMMENT '应用ID', + `out_order_no` varchar(32) NOT NULL COMMENT '外部订单号', + `out_member_id` varchar(32) NOT NULL COMMENT '外部用户ID', + `redirect_url` varchar(1024) NOT NULL DEFAULT '' COMMENT '跳转地址', + `notify_url` varchar(1024) NOT NULL DEFAULT '' COMMENT '通知地址', + `token` varchar(32) NOT NULL DEFAULT '', + `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + KEY `idx_outorderno` (`out_order_no`) USING BTREE, + KEY `idx_token` (`token`) USING BTREE, + KEY `idx_createdat` (`created_at`) USING BTREE, + PRIMARY KEY (`id`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 ROW_FORMAT=DYNAMIC COMMENT='预支付表'; + +alter table users add column `out_member_id` varchar(32) not null default '' after member_id; +alter table orders add column `out_member_id` varchar(32) not null default '' after member_id; \ No newline at end of file diff --git a/public/payment.html b/public/payment.html new file mode 100644 index 0000000..b4e1906 --- /dev/null +++ b/public/payment.html @@ -0,0 +1,188 @@ + + + + + + + 充值 + + +
+ + 充值 + + + + + + + + + + + + + + + + + + + + + + + + 立即充值 + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + \ No newline at end of file