This commit is contained in:
admin 2024-01-19 10:32:11 +08:00
commit de4d0558dd
8 changed files with 585 additions and 7 deletions

199
components/TongLianPay.php Normal file
View File

@ -0,0 +1,199 @@
<?php
namespace app\components;
use app\models\Signing;
use app\models\UserOauth;
class TongLianPay
{
private $privateKey = 'MIIEpAIBAAKCAQEAnxhCKTNtYHHSd7mBvTVy2p5M4RtnZzQNYtQMhYcVtgrYKq4lNtw68q3j1y2ZjXprcNtJ9XnsqXkyynXcVGPPLcj/p21kXuakGVUH92OVyaq7f8PjSvKu/n1hytVnLG98S5RW1uECTj4kfyxd9TPSh6PUymhFdMiZqQCGGmuKXQNz5qJW3wqnHedLOQS2ZgAfy0lY4r7fwNNkY67TnOBpLJ8TKrSdYfUonRp1RGVPq1Z6cZFRcaNFZRofrDVqY08nl0cKGOI47vh/Jj+RoEgMKH82+h7IiXWgUp3VciSMovIkiODDAN5fdO1tJSzVzNc83Pnm6E5BxmAFpWsnlCrvJQIDAQABAoIBACARabSYUyGvj7bmQ4p57Y63fdOaDHu2+EPFbkiz8+JfyTbBJ2spdRBZkKVc4ais1l9HNF8wlk2KynwYybKQ87/M1gtPd33Ri543j4WEIzslGOol9/ixdiiB6WZTIZrJVgp9+gsSC77ts7mWndHBAlyo30l1PxrNHHv+SQmmR5t7twPVn6oYkYuRTTvgaujdOjhNzgFSrFfPJC+wko6VEGtvGkIRCGmtleC0mqC8knUT8TpMQ6eksyBCcLuOTRr2sL0p5hc2Tioo9l4QW8HHOJa8w5rQpVZHdsZx6UHUz79YPI9BS/lp9JsKvbX+SKv/cv7AHg7+l1fQqJF9BjzNFHkCgYEA3DnWutEZiidVcbbryblQfbXpVjsuXJTPalTv3dJw4wh3Q13r9DQ9MtkS9pEnTuzgj545rCDSjpeVf8BSui9VnlJfYHfaIiULC4DLdNK/XnNeg4PCbUNENFWcXETgW0s1xMBmUS2ad4M9QssANTpFr5HKIpmCFx5Qu0Ol1D+64VMCgYEAuPBC9O7kSoB0w+WY49AR51qi0vWwQxhCORoCjNldcW89ced2o3ehkPL+yYRSNjLiKfVh1xVaVKXMipDIb5tZARMWf1Jr8WqjJDXQoe+LgDSxzmhdNM7NIhPcJ5ha1fxh3/emc1K0YgObdCxIyrKUrBBoOOkfEIZ+rNNhQZ7eBqcCgYB3BduEBFblT/TiDJbK45kZGCQQMtQPvW2MbnNlU8MnMQAkLLLFvSYGQUP6duDjyypi9IT+/o5N+qcV8H/FmKeRdbuOi7gdFCAwC1/qt6wuA/Rk5+VA4EYQcuSbCa6oKLHasJGb9iWxygBmBQkLu37GMOkqYgTpMb04OIt7dyYteQKBgQC2ln+E01cRN0IRJt2MDiGfCR04qtkbZRf8yHE8Hl7jX7CKhLvdKH+bXE2xJ7MDT7l7M4klkS9d41POeqNpjTaSmQXdLsLj1yS622bgemCAc4YZA3ECciqVOoZhkUodetAnD5qGwWDWZDlWuWIkvauLNaewiHjjF+Z5OzkKUI9kgQKBgQDT/ccCYjTGRAQoewyrSzyYfxjnt/EPE83poeug6v923MmPkihNOajpnIpnXe+aybNNbIcqeu4YVCYBSGTDqL4Dc28AmLUOrnjFfA9DydP4G3HT3Y5RtChDZ44XOp+IJywJ2dufOLbjlLXD7KGIkzpIATpxC7bDhRoZRPmBDnX5uw==';
private $publicKey = 'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCm9OV6zH5DYH/ZnAVYHscEELdCNfNTHGuBv1nYYEY9FrOzE0/4kLl9f7Y9dkWHlc2ocDwbrFSm0Vqz0q2rJPxXUYBCQl5yW3jzuKSXif7q1yOwkFVtJXvuhf5WRy+1X5FOFoMvS7538No0RpnLzmNi3ktmiqmhpcY/1pmt20FHQQIDAQAB';
private $cusid = '56339308999C8UH';
private $appid = '00298533';
public function pay($orderNom, $frontUrl, $userId)
{
$userOauth = UserOauth::find()->where(['user_id' => $userId])->one();
$signing = Signing::find()->where(['order_no' => $orderNom])->one();
$data = [
'cusid' => $this->cusid,
'appid' => $this->appid,
'version' => '11',
'trxamt' => 1,
'reqsn' => $signing->order_no,
'paytype' => 'W06',
'randomstr' => date('dHis') . rand(1000000, 9999999),
'signtype' => 'RSA',
'front_url' => $frontUrl,
'notify_url' => 'http://app.cxgj.dev.1nww.com/api/signing/signing-pay-notify',
'acct' => $userOauth->openid,
'sub_appid' => 'wxbcdac64cf147ee22',
];
$data['sign'] = urlencode($this->sign($data));
$request = $this->ToUrlParams($data);
$res = $this->request('https://vsp.allinpay.com/apiweb/unitorder/pay', $request);
$result = json_decode($res, true);
if ($result['retcode'] !== 'SUCCESS') {
return $this->apiReturnError('支付错误', $result);
}
return $this->apiReturnSuccess('success', $result);
}
public function notify($data)
{
if (!$this->ValidSign($data)) {
return false;
}
$signing = Signing::find()->where(['order_no' => $data['outtrxid'], 'status' => 0])->one();
if (!$signing) {
return true;
}
$signing->status = 1;
$signing->pay_time = time();
return $signing->save();
}
public function refund($signingId)
{
$signing = Signing::findOne($signingId);
if ($signing && $signing->status == 1) {
$data = [
'cusid' => $this->cusid,
'appid' => $this->appid,
'trxamt' => 1,
'reqsn' => date('YmdH') . rand(10000, 99999),
'oldreqsn' => $signing->order_no,
'randomstr' => date('YmdH') . rand(10000, 99999),
'signtype' => 'RSA',
];
$data['sign'] = urlencode($this->sign($data));
$request = $this->ToUrlParams($data);
$res = $this->request('https://vsp.allinpay.com/apiweb/tranx/refund', $request);
$result = json_decode($res, true);
if ($result['trxstatus'] == '0000') {
$signing->status = 2;
$signing->save();
return $this->apiReturnSuccess('操作成功');
}
return $this->apiReturnError($result['errmsg']);
}
return $this->apiReturnError('订单异常');
}
//RSA签名
public function sign(array $array)
{
ksort($array);
$bufSignSrc = $this->ToUrlParams($array);
$private_key = $this->privateKey;
$private_key = chunk_split($private_key, 64, "\n");
$key = "-----BEGIN RSA PRIVATE KEY-----\n" . wordwrap($private_key) . "-----END RSA PRIVATE KEY-----";
// echo $key;
if (openssl_sign($bufSignSrc, $signature, $key)) {
// echo 'sign success';
} else {
echo 'sign fail';
}
$sign = base64_encode($signature);//加密后的内容通常含有特殊字符需要编码转换下在网络间通过url传输时要注意base64编码是否是url安全的
return $sign;
}
public function ToUrlParams(array $array)
{
$buff = "";
foreach ($array as $k => $v) {
if ($v != "" && !is_array($v)) {
$buff .= $k . "=" . $v . "&";
}
}
$buff = trim($buff, "&");
return $buff;
}
/**
* 校验签名
* @param array 参数
* @param unknown_type appkey
*/
public function ValidSign(array $array)
{
$sign = $array['sign'];
unset($array['sign']);
ksort($array);
$bufSignSrc = $this->ToUrlParams($array);
$public_key = $this->publicKey;
$public_key = chunk_split($public_key, 64, "\n");
$key = "-----BEGIN PUBLIC KEY-----\n$public_key-----END PUBLIC KEY-----\n";
$result = openssl_verify($bufSignSrc, base64_decode($sign), $key);
return $result;
}
//发送请求操作仅供参考,不为最佳实践
public function request($url, $params)
{
$ch = curl_init();
$this_header = array("content-type: application/x-www-form-urlencoded;charset=UTF-8");
curl_setopt($ch, CURLOPT_HTTPHEADER, $this_header);
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (compatible; MSIE 5.01; Windows NT 5.0)');
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $params);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);//如果不加验证,就设false,商户自行处理
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
$output = curl_exec($ch);
curl_close($ch);
return $output;
}
public function apiReturnSuccess($msg = "ok", $data = [], $code = 0)
{
return [
'code' => $code,
'msg' => $msg,
'data' => $data
];
}
public function apiReturnError($msg = "failed", $data = [], $code = 1)
{
return [
'code' => $code,
'msg' => $msg,
'data' => $data
];
}
}

View File

@ -0,0 +1,54 @@
<?php
namespace app\modules\admin\controllers;
use app\components\TongLianPay;
use app\modules\admin\behaviors\LoginBehavior;
use app\modules\admin\models\SigningForm;
class SigningController extends Controller
{
public function behaviors()
{
return array_merge(parent::behaviors(), [
'login' => [
'class' => LoginBehavior::className(),
],
]);
}
public function actionIndex()
{
if (\Yii::$app->request->isAjax) {
$signingForm = new SigningForm();
$signingForm->attributes = \Yii::$app->request->get();
$data = $signingForm->getList();
return $this->responseHandler($data);
}
return $this->render('index');
}
public function actionRefund()
{
if (\Yii::$app->request->isAjax) {
$tongLianPay = new TongLianPay();
return $this->responseHandler($tongLianPay->refund(\Yii::$app->request->post('id')));
}
}
}

View File

@ -26,6 +26,15 @@ class Menu
'children' => [
],
],
[
'name' => '签约列表',
'is_menu' => true,
'is_show' => true,
'route' => "admin/signing/index",
'icon' => 'layui-icon-form',
'children' => [
],
],
// [
// 'name' => '布告管理',
// 'is_menu' => true,

View File

@ -0,0 +1,60 @@
<?php
namespace app\modules\admin\models;
use app\models\Goods;
use app\models\GoodsHub;
use app\models\Signing;
use app\models\User;
use yii\data\Pagination;
class SigningForm extends AdminModel
{
public $page;
public $limit;
public $order_no;
public $status;
public function rules()
{
return [
[['order_no'], 'string'],
[['page', 'limit', 'status'], 'integer'],
[['page'], 'default', 'value' => 1],
[['limit'], 'default', 'value' => 20],
[['status'], 'default', 'value' => null],
];
}
public function getList()
{
$query = Signing::find()->alias('s')
->leftJoin(['u' => User::tableName()], 's.user_id=u.id')
->leftJoin(['g' => Goods::tableName()], 's.goods_id=g.id')
->leftJoin(['gh' => GoodsHub::tableName()], 'g.goods_hub_id=gh.id')
->select('u.username,gh.name as goods_name,s.*');
if ($this->status != null) {
$query->where(['s.status' => $this->status]);
}
if ($this->order_no) {
$query->where(['s.order_no' => $this->order_no]);
}
$pagination = new Pagination(['totalCount' => $query->count(), 'defaultPageSize' => $this->limit]);
$list = $query->offset($pagination->offset)->orderBy(['s.create_time' => SORT_DESC])->asArray()->limit($pagination->limit)->all();
return [
'code' => 0,
'msg' => 'ok',
'data' => $list,
'count' => $query->count()
];
}
}

View File

@ -0,0 +1,210 @@
<?php
/**
* @author Any
* @description KISS
* @date 2020-11-5
* @version 1.0.0
*
* _____LOG_____
*
*/
use yii\widgets\LinkPager;
use app\models\User;
$this->title = '签约列表';
$this->params['breadcrumbs'][] = $this->title;
$status = \Yii::$app->request->get('status');
?>
<div class="layui-fluid">
<div class="layui-card">
<div class="layui-card-header">
<?= $this->title ?>
</div>
<div class="layui-card-header layuiadmin-card-header-auto">
<div class="layui-form-item">
<div class="layui-inline">
</div>
<div class="layui-inline layui-float-right layui-form">
<div class="layui-inline layui-form-item">
<div class="layui-inline">
<div class="layui-input-inline">
<input type="text" name="order_no" placeholder="订单号" autocomplete="off"
class="layui-input" value="<?= \Yii::$app->request->get("order_no") ?>">
</div>
</div>
</div>
<div class="layui-inline layui-form-item">
<div class="layui-input-inline">
<select class="layui-select" name="status">
<option value="" <?= $status === null ? 'selected' : '' ?>>全部</option>
<option value="0" <?= $status === 0 ? 'selected' : '' ?>>待支付</option>
<option value="1" <?= $status === 1 ? 'selected' : '' ?>>已签约</option>
<option value="2" <?= $status === -1 ? 'selected' : '' ?>>已取消</option>
</select>
</div>
</div>
<div class="layui-inline">
<button class="layui-btn layuiadmin-btn-useradmin" lay-submit lay-filter="dtable-search">
<i class="layui-icon layui-icon-search layuiadmin-button-btn"></i>
</button>
<button type="button" class="layui-btn" lay-submit lay-filter="dtable-export">导出</button>
</div>
</div>
</div>
</div>
<div class="layui-card-body">
<table id="dtable" lay-filter="dtable"></table>
</div>
</div>
</div>
<script type="text/html" id="status">
{{# if(d.status == 0){ }}
<span class="layui-badge-rim">待支付</span>
{{# } else if(d.status == 1){ }}
<span class="layui-badge-rim">已签约</span>
{{# } else if(d.status == -1){ }}
<span class="layui-badge-rim">已取消</span>
{{# } }}
</script>
<!--行操作列模板-->
<script type="text/html" id="rowBarTpl">
{{# if(d.status == 1){ }}
<a href="javascript:;" class="layui-btn layui-btn-normal layui-btn-xs" lay-event="refund">退还</a>
{{# } }}
</script>
<script>
<?php $this->beginBlock('js_script_wrap') ?>
layui.config({
base: '/statics/layuiadmin/' //静态资源所在路径
}).extend({
index: 'lib/index' //主入口模块
}).use(['index', 'user', 'table', 'util'], function () {
var $ = layui.$
, form = layui.form
, table = layui.table
, admin = layui.admin;
form.render();
//search
form.on('submit(dtable-search)', function (obj) {
var field = obj.field;
reload_table_data(field);
});
//重新加载表格数据
function reload_table_data(conf) {
table.reload('dtable', {
url: '<?=\Yii::$app->urlManager->createUrl(['admin/signing/index'])?>'
, page: true
, where: conf
});
};
//执行渲染
table.render({
//指定原始表格元素选择器推荐id选择器
elem: '#dtable'
//容器高度
//,height: 315
//容器宽度
//,width: 720
//设置表头
, cols: [[
{checkbox: true}
, {field: 'id', title: 'ID', width: 80}
, {field: 'order_no', title: '订单号'}
, {field: 'username', title: '用户名'}
, {field: 'goods_name', title: '商品名称'}
, {field: 'moy', title: '订单金额'}
, {field: 'company_name', title: '公司名称'}
, {field: 'brand_name', title: '品牌名称'}
, {field: 'product', title: '主营产品'}
, {field: 'type', title: '业务类型'}
, {field: 'company_name', title: '公司名称'}
, {field: 'number', title: '签约年限/数量'}
, {field: 'remark', title: '备注'}
, {field: 'status', title: '订单状态', templet: '#status'}
, {
field: 'create_time', title: '创建时间', templet: function (d) {
return layui.util.toDateString(d.create_time * 1000, 'yyyy-MM-dd HH:mm:ss');
}
},
{title: '操作', fixed: 'right', toolbar: '#rowBarTpl'}
]
]
, url: '<?=\Yii::$app->urlManager->createUrl(['admin/signing/index'])?>'
, page: true
//指向自定义工具栏模板选择器
, toolbar: '#toolBarTpl'
//头部工具栏右侧的图标按钮
, defaultToolbar: []
, limit: 20
, limits: [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
, title: '<?=$this->title?>'
, data: []
});
//监听工具栏
table.on('tool(dtable)', function (obj) {
var lay_event = obj.event;
if(lay_event == 'refund'){
confirm_tip = '确定退款?';
confirm_url = '<?=\Yii::$app->urlManager->createUrl(["/admin/signing/refund"])?>';
}
if(confirm_url != null){
layer.confirm(confirm_tip, {
btn: ['确定','取消']
}, function(){
$.ajax(confirm_url,{
type:"POST",
dataType:"json",
data:{
id:obj.data.id,
_csrf:_csrf
},
success:function(res){
if(res.code == 0){
layer.msg(res.msg, {
offset: '15px'
,icon: 1
,time: 1000
}, function(){
location.reload();
});
} else {
layer.msg(res.msg, {
offset: '15px'
,icon: 2
,time: 1000
}, function(){
});
}
},
error:function(xhr,type,err){
layer.msg(xhr.responseText, {
offset: '15px'
,icon: 2
,time: 1000
}, function(){
});
}
})
}, function(){
});
}
})
});
<?php $this->endBlock(); ?>
</script>
<?php $this->registerJs($this->blocks['js_script_wrap'], \yii\web\View::POS_END); ?>

View File

@ -3,11 +3,14 @@
namespace app\modules\api\controllers;
use app\components\SiteHelper;
use app\components\TongLianPay;
use app\modules\api\models\SigningForm;
use app\modules\api\behaviors\LoginBehavior;
class SigningController extends Controller
{
public $enableCsrfValidation = false;
public function behaviors()
{
@ -15,7 +18,7 @@ class SigningController extends Controller
'login' => [
'class' => LoginBehavior::className(),
'ignore' => [
'api/signing/signing-list'
'api/signing/signing-pay-notify'
]
]
]);
@ -124,11 +127,53 @@ class SigningController extends Controller
$signingForm = new SigningForm();
$signingForm->user_id = \Yii::$app->user->identity->id;
$status = \Yii::$app->request->get('status');
$limit = \Yii::$app->request->get('limit',10);
$page = \Yii::$app->request->get('page',1);
$status = \Yii::$app->request->get('status',null);
$limit = \Yii::$app->request->get('limit', 10);
$page = \Yii::$app->request->get('page', 1);
return $this->responseHandler($signingForm->orderList($status,$limit,$page));
return $this->responseHandler($signingForm->orderList($status, $limit, $page));
}
/**
* showdoc
* @catalog 签约订单
* @title 支付
* @description 本接口用于签约订单支付
* @method post
* @url /api/signing/signing-pay
* @param order_no 必填 string 订单号
* @param front_url 必填 string 支付成功回调地址
* @return {"code":0,"msg":"ok","data":[]}javascript:;
* @remark
*/
public function actionSigningPay()
{
if (!\Yii::$app->request->isPost) {
$data = $this->invaildRequest();
return $this->responseHandler($data);
}
$tongLianPayForm = new TongLianPay();
return $this->responseHandler($tongLianPayForm->pay(
\Yii::$app->request->post('order_no'),
\Yii::$app->request->post('front_url'),
\Yii::$app->user->identity->id)
);
}
public function actionSigningPayNotify()
{
$tongLianPayForm = new TongLianPay();
$data = \Yii::$app->request->post();
file_put_contents('test.text', json_encode($data));
return $tongLianPayForm->notify($data) ? 'success' : 'error';
}
}

View File

@ -94,9 +94,9 @@ class SigningForm extends ApiModel
public function orderList($status, $limit, $page)
{
$query = Signing::find()->with(['goods.goodsHub'])->where(['user_id' =>$this->user_id]);
$query = Signing::find()->with(['goods.goodsHub'])->where(['user_id' => $this->user_id]);
if ($status) {
if ($status != null) {
$query->where(['status' => $status]);
}

1
web/test.text Normal file
View File

@ -0,0 +1 @@
{"acct":"onUx66yTrjgu6yR3SJhyEpZ5REO8","accttype":"99","appid":"00298533","bankcode":"OTHERS","chnlid":"205299480","chnltrxid":"4200002097202401188634172740","cmid":"609121428","cusid":"56339308999C8UH","cusorderid":"7080950266767","fee":"0","initamt":"1","outtrxid":"7080950266767","paytime":"20240118104218","sign":"B2p4IC5FOXB9my6uqqzy4VynBKUEQXLXGIP9SjZV6b1MeopchWDbC13q4C2EfJ90ZSeQxWuGmH\/dXZ8FFY9PghaGp5lD8FVZeqrKRGUdTK5WqKc41IP4JWvlqrXt6YP8UZ28S7R\/azpa49YbJ+6EzHfOCSLX2iSgErGnx8NBccw=","signtype":"RSA","termauthno":"OTHERS","termrefnum":"4200002097202401188634172740","termtraceno":"0","trxamt":"1","trxcode":"VSP501","trxdate":"20240118","trxid":"240118113690756943","trxstatus":"0000"}