efps-version
elf 1 year ago
parent c1eed6b9c2
commit ee5a53e7bc

@ -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);
}
}

@ -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];
}
}

@ -0,0 +1,10 @@
<?php
declare(strict_types=1);
namespace App\Model;
class PrePayLog extends Model
{
protected $table = 'pre_pay_logs';
}

@ -0,0 +1,13 @@
<?php
namespace App\Request;
class PaymentRequest extends ApiRequest
{
public function rules(): array
{
return [
];
}
}

@ -9,14 +9,17 @@ use App\Helper\Efps\Api;
use App\Helper\Efps\Result;
use App\Helper\Redis;
use App\Helper\RedisKey;
use App\Helper\StringHelper;
use App\Model\App;
use App\Model\BankCard;
use App\Model\Order;
use App\Model\PrePayLog;
use App\Model\RefundOrder;
use App\Model\User;
use App\Request\BindCardConfirmRequest;
use App\Request\BindCardRequest;
use App\Request\PaymentQueryRequest;
use App\Request\PaymentRequest;
use App\Request\ProtocolPayConfirmRequest;
use App\Request\ProtocolPayPreRequest;
use App\Request\RefundQueryRequest;
@ -29,9 +32,12 @@ class PaymentService extends AbstractService
return md5($appKey . '-' . $cardNo);
}
public function createOrder(App $app, array $params) {
public function createOrder(App $app, array $params, $user) {
$order = new Order();
$order->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];
}
}

@ -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='退款订单表';
) 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;

@ -0,0 +1,188 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<!-- import CSS -->
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
<title>充值</title>
</head>
<body>
<div id="app">
<el-container>
<el-header style="text-align: center;">充值</el-header>
<el-main>
<el-row>
<el-col :span="24">
<el-form ref="form" :model="form" label-width="80px">
<el-form-item label="姓名">
<el-input v-model="form.name"></el-input>
</el-form-item>
<el-form-item label="手机号">
<el-input v-model="form.mobile"></el-input>
</el-form-item>
<el-form-item label="身份证号">
<el-input v-model="form.cardNo"></el-input>
</el-form-item>
<el-form-item label="银行卡号">
<el-input v-model="form.bankCardNo"></el-input>
</el-form-item>
<el-form-item label="充值金额">
<el-input-number v-model="form.amount" :min="1" :max="100000" label="充值金额"></el-input-number>
</el-form-item>
<el-form-item label="备注">
<el-input type="textarea" v-model="form.remark"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="recharge">立即充值</el-button>
</el-form-item>
</el-form>
</el-col>
</el-row>
</el-main>
</el-container>
<el-dialog title="绑卡验证码" :visible.sync="bindConfirmVisible" width="30%">
<el-form :model="bindConfirmForm" label-width="80px">
<el-form-item label="验证码">
<el-input v-model="bindConfirmForm.smsCode" autocomplete="off"></el-input>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="bindConfirmVisible = false">取 消</el-button>
<el-button type="primary" @click="bindConfirm">确 定</el-button>
</div>
</el-dialog>
<el-dialog title="支付验证码" :visible.sync="payConfirmVisible" width="30%">
<el-form :model="payConfirmForm" label-width="80px">
<el-form-item label="验证码">
<el-input v-model="payConfirmForm.smsCode" autocomplete="off"></el-input>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="payConfirmVisible = false">取 消</el-button>
<el-button type="primary" @click="payConfirm">确 定</el-button>
</div>
</el-dialog>
</div>
</body>
<!-- import Vue before Element -->
<script src="https://unpkg.com/vue@2/dist/vue.js"></script>
<!-- import JavaScript -->
<script src="https://unpkg.com/element-ui/lib/index.js"></script>
<script src="https://unpkg.com/axios@1.1.2/dist/axios.min.js"></script>
<script>
new Vue({
el: '#app',
data() {
return {
token: undefined,
bindConfirmVisible: false,
bindConfirmForm: {
smsCode: '',
smsNo: '',
memberId: ''
},
payConfirmVisible: false,
payConfirmForm: {
smsCode: '',
memberId: '',
token: '',
protocol: ''
},
form: {
name: '',
mobile: '',
cardNo: '',
bankCardNo: '',
amount: 1,
remark: ''
}
}
},
created() {
this.token = this.getQueryParam('token');
if (!this.token) {
return this.$message.error('参数异常');
}
},
methods: {
recharge() {
axios.post('/recharge/recharge', this.form)
.then( (response) => {
console.log(response);
let result = response.data
if (result.code != 1000) {
return this.$message.error(response.data.message);
}
if (result.data.nextStep == 'confirm-bind') {
this.bindConfirmVisible = true;
this.bindConfirmForm.memberId = result.data.memberId;
this.bindConfirmForm.smsNo = result.data.bizData.smsNo;
console.log(this.bindConfirmForm)
} else if (result.data.nextStep == 'confirm-pay') {
this.payConfirmVisible = true;
this.payConfirmForm.memberId = result.data.memberId;
this.payConfirmForm.protocol = result.data.bizData.protocol;
this.payConfirmForm.token = result.data.bizData.token;
console.log(this.payConfirmForm)
}
})
.catch((error) => {
this.$message.error('请求错误');
console.log(error);
});
console.log('submit!');
},
bindConfirm() {
axios.post('/recharge/confirm-bind-card', this.bindConfirmForm)
.then( (response) => {
console.log(response);
let result = response.data
if (result.code != 1000) {
return this.$message.error(response.data.message);
}
this.bindConfirmVisible = false;
this.recharge();
})
.catch((error) => {
this.$message.error('请求错误');
console.log(error);
});
console.log('submit!');
},
payConfirm() {
axios.post('/recharge/confirm-pay', this.payConfirmForm)
.then( (response) => {
console.log(response);
let result = response.data
if (result.code != 1000) {
return this.$message.error(response.data.message);
}
this.payConfirmVisible = false;
this.$message.success('支付成功');
this.$alert('请记住您的订单号:' + result.data.bizData.outTradeNo , '支付成功', {
confirmButtonText: '确定',
callback: action => {
}
});
})
.catch((error) => {
this.$message.error('请求错误');
console.log(error);
});
console.log('submit!');
},
getQueryParam($name) {
var query = window.location.search.substring(1);
var vars = query.split("&");
for (var i = 0; i < vars.length; i++) {
var pair = vars[i].split("=");
if(pair[0] == name) {
return pair[1];
}
}
return undefined;
}
}
})
</script>
</html>
Loading…
Cancel
Save