供应链B到B商品

This commit is contained in:
彭桃 2023-03-22 16:51:51 +08:00
parent 9f09cba053
commit 649140cad5
9 changed files with 1126 additions and 101 deletions

View File

@ -1127,101 +1127,6 @@ class Api extends BaseController
return table_assign(0, '', $result);
}
// 接收微信支付状态的通知
public function notify()
{
$app = $this->payment();
// 用 easywechat 封装的方法接收微信的信息, 根据 $message 的内容进行处理, 之后要告知微信服务器处理好了, 否则微信会一直请求这个 url, 发送信息
$response = $app->handlePaidNotify(function($message, $fail){
// 首先查看 order 表, 如果 order 表有记录, 表示已经支付过了
$order = Order::where('order_sn', $message['out_trade_no'])->first();
if ($order) {
return true; // 如果已经生成订单, 表示已经处理完了, 告诉微信不用再通知了
}
// 查看支付日志
$payLog = PayLog::where('out_trade_no', $message['out_trade_no'])->first();
if (!$payLog || $payLog->paid_at) { // 如果订单不存在 或者 订单已经支付过了
return true; // 告诉微信,我已经处理完了,订单没找到,别再通知我了
}
// return_code 表示通信状态,不代表支付状态
if ($message['return_code'] === 'SUCCESS') {
// 用户是否支付成功
if ($message['result_code'] === 'SUCCESS') {
// 更新支付时间为当前时间
$payLog->paid_at = now();
$post_id = $payLog->post_id;
// 联表查询 post 的相关信息
$post_title = $payLog->post->title;
$post_price = $payLog->post->price;
$post_original_price = $payLog->post->original_price;
$post_cover = $payLog->post->post_cover;
$post_description = $payLog->post->description;
$user_id = $payLog->post->user_id;
// 创建订单记录
Order::create([
'order_sn' => $message['out_trade_no'],
'total_fee' => $message['total_fee'],
'pay_log_id' => $payLog->id,
'status' => 1,
'user_id' => $user_id,
'paid_at' => $payLog->paid_at,
'post_id' => $post_id,
'post_title' => $post_title,
'post_price' => $post_price,
'post_original_price' => $post_original_price,
'post_cover' => $post_cover,
'post_description' => $post_description,
]);
// 更新 PayLog, 这里的字段都是根据微信支付结果通知的字段设置的(https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_7&index=8)
PayLog::where('out_trade_no', $message['out_trade_no'])->update([
'appid' => $message['appid'],
'bank_type' => $message['bank_type'],
'total_fee' => $message['total_fee'],
'trade_type' => $message['trade_type'],
'is_subscribe' => $message['is_subscribe'],
'mch_id' => $message['mch_id'],
'nonce_str' => $message['nonce_str'],
'openid' => $message['openid'],
'sign' => $message['sign'],
'cash_fee' => $message['cash_fee'],
'fee_type' => $message['fee_type'],
'transaction_id' => $message['transaction_id'],
'time_end' => $payLog->paid_at,
'result_code' => $message['result_code'],
'return_code' => $message['return_code'],
]);
}
} else {
// 如果支付失败, 也更新 PayLog, 跟上面一样, 就是多了 error 信息
PayLog::where('out_trade_no', $message['out_trade_no'])->update([
'appid' => $message['appid'],
'bank_type' => $message['bank_type'],
'total_fee' => $message['total_fee'],
'trade_type' => $message['trade_type'],
'is_subscribe' => $message['is_subscribe'],
'mch_id' => $message['mch_id'],
'nonce_str' => $message['nonce_str'],
'openid' => $message['openid'],
'sign' => $message['sign'],
'cash_fee' => $message['cash_fee'],
'fee_type' => $message['fee_type'],
'transaction_id' => $message['transaction_id'],
'time_end' => $payLog->paid_at,
'result_code' => $message['result_code'],
'return_code' => $message['return_code'],
'err_code' => $message['err_code'],
'err_code_des' => $message['err_code_des'],
]);
return $fail('通信失败,请稍后再通知我');
}
return true; // 返回处理完成
});
// 这里是必须这样返回的, 会发送给微信服务器处理结果
return $response;
}
}

View File

@ -12,8 +12,10 @@ namespace app\admin\controller;
use app\admin\BaseController;
use app\admin\model\StoreProduct as StoreProductModel;
use app\admin\validate\StoreProductValidate;
use EasyWeChat\Factory;
use think\exception\ValidateException;
use think\facade\Db;
use think\facade\Request;
use think\facade\View;
class StoreProduct extends BaseController
@ -34,11 +36,17 @@ class StoreProduct extends BaseController
{
if (request()->isAjax()) {
$param = get_params();
$where = [];
$where[] = ['admin_id','=',$this->uid];
if (isset($param['keywords']) && !empty($param['keywords'])){
$where[]=['store_name','like','%'.$param['keywords'].'%'];
}
$list = $this->model->getStoreProductList($where,$param);
foreach ($list as $k=>$v){
$www['brand_id'] = $v['brand_id'];
$list[$k]['brand_id'] = Db::connect('shop')->table('eb_store_brand')->where($www)->value('brand_name');
$www2['store_category_id'] = $v['cate_id'];
$list[$k]['cate_id'] = Db::connect('shop')->table('eb_store_category')->where($www2)->value('cate_name');
}
return table_assign(0, '', $list);
}
else{
@ -60,7 +68,7 @@ class StoreProduct extends BaseController
// 验证失败 输出错误信息
return to_assign(1, $e->getError());
}
$param['admin_id'] = $this->uid;
$this->model->addStoreProduct($param);
}else{
@ -144,4 +152,191 @@ class StoreProduct extends BaseController
$this->model->delStoreProductById($product_id,1);
}
/**
* 商品列表
*/
public function index()
{
if (request()->isAjax()) {
$param = get_params();
$where = [];
if (isset($param['keywords']) && !empty($param['keywords'])){
$where[]=['store_name','like','%'.$param['keywords'].'%'];
}
if (isset($param['store_cate']) && !empty($param['store_cate'])){
$where[]=['cate_id','=',$param['store_cate']];
}
$list = $this->model->getStoreProductList($where,$param);
foreach ($list as $k=>$v){
$www['brand_id'] = $v['brand_id'];
$list[$k]['brand_id'] = Db::connect('shop')->table('eb_store_brand')->where($www)->value('brand_name');
$www2['store_category_id'] = $v['cate_id'];
$list[$k]['cate_id'] = Db::connect('shop')->table('eb_store_category')->where($www2)->value('cate_name');
}
return table_assign(0, '', $list);
}
else{
return view();
}
}
/**
* 购买下单页面
*/
public function buy()
{
$param = get_params();
$product_id = isset($param['product_id']) ? $param['product_id'] : 0;
$detail = $this->model->getStoreProductById($product_id);
if (!empty($detail)) {
$detail['content'] = Db::table('cms_store_product_content')->where('product_id',$detail['product_id'])->value('content');
$detail['slider_image_arr'] = explode(',',$detail['slider_image']);
View::assign('detail', $detail);
$store_brand= Db::connect('shop')->table('eb_store_brand')->where(['is_show' => 1])
->select();
View::assign('store_brand', $store_brand);
return view();
}
else{
throw new \think\exception\HttpException(404, '找不到页面');
}
}
// 请求微信接口的公用配置, 所以单独提出来
private function payment()
{
// 配置信息
$config = [
'app_id' => 'wx0b3defb62f0f910b',//注意这个APPID只能是公众号的id没有的话要去申请并且在微信支付平台里绑定
'mch_id' => '1635725673',//商户号
'key' => '95d195Dcf6ec66156dfeeb4E7435faef',//支付秘钥
'secret' => 'c02aa7ad9e4a5c423862e068b6cb4ad4',
'notify_url' => Request::instance()->domain().'/api/PayNotify/notify',//异步回调通知地址
];
// 这个就是 easywechat 封装的了, 一行代码搞定, 照着写就行了
$app = Factory::payment($config);
return $app;
}
// 向微信请求统一下单接口, 创建预支付订单
public function place_order()
{
$post = get_params();
$id = $post['product_id'];
$number = $post['number'];
// 因为没有先创建订单, 所以这里先生成一个随机的订单号, 存在 pay_log 里, 用来标识订单, 支付成功后再把这个订单号存到 order 表里
$order_sn = date('ymd').substr((string)time(),-5).substr(microtime(),2,5);
// 根据 id 查出价格
$where['product_id'] = $id;
Db::startTrans();//开启事务
$store_product = Db::table('cms_store_product')->where($where)->lock(true)->find();
if (empty($store_product)) {
return to_assign(0,'查询的数据不存在');
}
// 判断库存
if($store_product['stock'] < $number){
return to_assign(0,'超过库存数量');
}
$post_price = bcmul(bcmul($store_product['price'],$number),'100');
$admin_id = get_login_admin('id');
// 创建 Paylog 记录
$param =[
'appid' => 'wx0b3defb62f0f910b',
'mch_id' => '1635725673',
'out_trade_no' => $order_sn,
'product_id' => $id,
'number' => $number,
'admin_id' =>$admin_id,
];
Db::table('cms_store_product_paylog')->strict(false)->field(true)->insert($param);
add_log('buy', $id, $param);
$app = $this->payment();
$total_fee = env('APP_DEBUG') ? 1 : $post_price;
// 用 easywechat 封装的方法请求微信的统一下单接口
$result = $app->order->unify([
'trade_type' => 'NATIVE', // 原生支付即扫码支付,商户根据微信支付协议格式生成的二维码,用户通过微信“扫一扫”扫描二维码后即进入付款确认界面,输入密码即完成支付。
'body' => '采购商品-订单支付', // 这个就是会展示在用户手机上巨款界面的一句话, 随便写的
'out_trade_no' => $order_sn,
'total_fee' => $total_fee,
'spbill_create_ip' => request()->ip(), // 可选如不传该参数SDK 将会自动获取相应 IP 地址
]);
if ($result['result_code'] == 'SUCCESS') {
// 如果请求成功, 微信会返回一个 'code_url' 用于生成二维码
$code_url = $result['code_url'];
// 生成二维码
// 引用二维码生成方法
require '../vendor/phpqrcode/phpqrcode.php';
$errorCorrectionLevel = 'L'; //容错级别
$matrixPointSize = 5; //生成图片大小
//生成二维码图片
// 判断是否有这个文件夹 没有的话就创建一个
if(!is_dir("static/qrcode")){
// 创建文件加
mkdir("static/qrcode");
}
//设置二维码文件名
$filename = 'static/qrcode/'.time().rand(10000,9999999).'.png';
//生成二维码
\QRcode::png($code_url,$filename , $errorCorrectionLevel, $matrixPointSize, 2);
// 订单编号, 用于在当前页面向微信服务器发起订单状态查询请求
$data['order_sn'] = $order_sn;
$data['html'] = Request::instance()->domain().'/'.$filename;
Db::commit();
return to_assign(200,'操作成功',$data);
}
return to_assign(0,'操作失败');
}
// 查询订单支付状态
public function paid(Request $request)
{
$out_trade_no = get_params('out_trade_no');
$app = $this->payment();
// 用 easywechat 封装的方法请求微信
$result = $app->order->queryByOutTradeNumber($out_trade_no);
if ($result['trade_state'] === 'SUCCESS'){
return to_assign(200,'支付成功');
}else{
return to_assign(0,'未支付');
}
}
// 采购订单列表
public function order(){
if (request()->isAjax()) {
$param = get_params();
$where[] = ['a.admin_id','=',$this->uid];
if (isset($param['keywords']) && !empty($param['keywords'])){
$where[]=['b.store_name','like','%'.$param['keywords'].'%'];
}
if (isset($param['store_cate']) && !empty($param['store_cate'])){
$where[]=['b.cate_id','=',$param['store_cate']];
}
$rows = empty($param['limit']) ? get_config('app . page_size') : $param['limit'];
$list = Db::table('cms_store_product_order')
->alias('a')
->join('cms_store_product b','a.product_id = b.product_id')
->field('a.*,b.image,b.store_name')
->withAttr('paid_at',function ($value,$data){
return date('Y-m-d H:i:s',(int)$value);
})
->where($where)
->order('a.id desc,a.paid_at desc')
->paginate($rows, false, ['query' => $param]);;
return table_assign(0, '', $list);
}
else{
return view();
}
}
}

View File

@ -0,0 +1,212 @@
{extend name="common/base"/}
{block name="style"}
<style type="text/css">
.editormd-code-toolbar select {
display: inline-block
}
.editormd li {
list-style: inherit;
}
</style>
{/block}
<!-- 主体 -->
{block name="body"}
<form class="layui-form p-4">
<h3 class="pb-3">购买商品</h3>
<table class="layui-table layui-table-form">
<tr>
<td class="layui-td-gray">商品名称</td>
<td colspan="7"><input type="text" name="store_name" lay-verify="required" lay-reqText="请输入商品名称"
autocomplete="off" placeholder="请输入商品名称" class="layui-input" value="{$detail.store_name}" readonly></td>
</tr>
<tr>
<td class="layui-td-gray" style="vertical-align:top;">商品封面图</td>
<td>
<div class="layui-upload">
<!-- <button type="button" class="layui-btn layui-btn-sm" id="upload_btn_thumb">-->
<!-- 上传缩略图(尺寸:428x270)-->
<!-- </button>-->
<div class="layui-upload-list" id="upload_box_thumb"
style="overflow: hidden;">
<img src="{$detail.image}"
onerror="javascript:this.src='{__GOUGU__}/gougu/images/nonepic600x360.jpg';this.onerror=null;"
width="100" style="max-width: 100%; height:66px;"/>
<input type="hidden" name="image" value="{$detail.image}">
</div>
</div>
</td>
</tr>
<tr>
<td class="layui-td-gray">购买数量<font>*</font></td>
<td colspan="7"><input type="text" name="number" lay-verify="required|integer" lay-reqText="请输入购买数量"
autocomplete="off" placeholder="请输入购买数量" class="layui-input" value=""></td>
</tr>
</table>
<div class="pt-3">
<input type="hidden" name="product_id" value="{$detail.product_id}"/>
<button class="layui-btn layui-btn-normal preservation" lay-submit="" lay-filter="webform" >立即提交</button>
<button type="reset" class="layui-btn layui-btn-primary">重置</button>
</div>
</form>
{/block}
<!-- /主体 -->
<!-- 脚本 -->
{block name="script"}
<script src="/static/assets/js/xm-select.js"></script>
<script>
var moduleInit = ['tool', 'tagpicker', 'tinymce'];
var group_access = "{:session('gougu_admin')['group_access']}";
var multiple_images = "{$detail['slider_image']}".split(',');
//单击图片删除图片 【注册全局函数】
function delMultipleImgs(this_img){
//获取下标
var subscript=$("#upload_box_thumb2 img").index(this_img);
//删除图片
this_img.remove();
//删除数组
multiple_images.splice(subscript, 1);
//重新排序
multiple_images.sort();
$('#upload_box_thumb2 input').attr('value', multiple_images);
//返回
return ;
}
function gouguInit() {
var form = layui.form, tool = layui.tool, tagpicker = layui.tagpicker;
form.verify({
integer: [
/^[1-9]\d*$/
, '只能输入正整数'
]
});
//上传缩略图
var upload_thumb = layui.upload.render({
elem: '#upload_btn_thumb',
url: '/admin/api/upload',
done: function (res) {
//如果上传失败
if (res.code == 1) {
layer.msg('上传失败');
return false;
}
//上传成功
$('#upload_box_thumb input').attr('value', res.data.filepath);
$('#upload_box_thumb img').attr('src', res.data.filepath);
}
});
//上传商品轮播图
var upload_thumb = layui.upload.render({
elem: '#upload_btn_thumb2',
url: '/admin/api/upload',
multiple: true,
before: function(obj){
//预读本地文件示例不支持ie8
obj.preview(function(index, file, result){
$('#upload_box_thumb2').append(`
<img src="${result}"
onerror="javascript:this.src='{__GOUGU__}/gougu/images/nonepic600x360.jpg';this.onerror=null;"
width="100" style="max-width: 100%; height:66px;" alt="${file.name}" onclick="delMultipleImgs(this)" title="点击删除"/>
`)
});
},
done: function (res) {
//如果上传失败
if (res.code == 1) {
return layer.msg('上传失败');
}
//上传成功
//追加图片成功追加文件名至图片容器
multiple_images.push(res.data.filepath);
$('#upload_box_thumb2 input').attr('value', multiple_images);
// $('#upload_box_thumb2 img').attr('src', res.data.filepath);
}
});
var editor = layui.tinymce;
var edit = editor.render({
selector: "#container_content",
height: 500
});
//监听提交
form.on('submit(webform)', function (data) {
// 单击之后提交按钮不可选,防止重复提交
var DISABLED = 'layui-btn-disabled';
// 增加样式
$('.preservation').addClass(DISABLED);
// 增加属性
$('.preservation').attr('disabled', 'disabled');
let callback = function (e) {
if (e.code == 200) {
// 成功的时候
/** 把生成的二维码放到页面上 */
var url = e.data.html;
/** 弹出二维码 **/
layer.open({
//type:1 表示页面层
type: 1,
title: '扫码支付',
//是否点击遮罩关闭
shadeClose: false,
//样式类名,可以自定义弹窗样式
skin:'demo_share',
// 去掉关闭按钮
closeBtn :'',
//弹层外区域
shade: 0.3,
maxmin: false, //开启最大化最小化按钮
//宽高
area: ['200px','220px'],
//内容
content: "<img src='"+url+"' alt='' style='margin-left: 20px;'>",
});
/** 设置定时器, 即一弹出二维码就开始不断请求查看支付状态, 直到收到支付成功的返回, 再终止定时器 **/
var timer = setInterval(function () {
/** 在这里请求微信支付状态的接口 **/
//ajax。必填参数'options':请求参数,对象,'callback':成功回调函数
let options = {
url: '/admin/store_product/paid',
type: 'get',
data: {
out_trade_no: e.data.order_sn,
}
};
let callback = function(res) {
if (res.code == 200) {
layer.msg('支付成功',{time:2000,icon:6});
/** 如果支付成功, 就取消定时器, 并重新加载页面 */
window.clearInterval(timer);
// 关闭所有层
tool.sideClose(2000);
// window.location.reload();
}
}
tool.ajax(options, callback);
}, 3000);
}else{
layer.msg(e.msg,{icon:5});
// 失败的时候
// 单击之后提交按钮不可选,防止重复提交
var DISABLED = 'layui-btn-disabled';
$('.preservation').removeClass(DISABLED);
$('.preservation').removeAttr('disabled');
}
}
tool.post("/admin/store_product/place_order", data.field, callback);
return false;
});
}
</script>
{/block}
<!-- /脚本 -->

View File

@ -13,7 +13,7 @@
<!-- 主体 -->
{block name="body"}
<form class="layui-form p-4">
<h3 class="pb-3">编辑文章表</h3>
<h3 class="pb-3">编辑</h3>
<table class="layui-table layui-table-form">
<tr>
<td class="layui-td-gray">商品名称<font>*</font></td>

View File

@ -0,0 +1,317 @@
{extend name="common/base"/}
<!-- 主体 -->
{block name="body"}
<style>
.layui-input-inline{
width:35% !important;
}
.layui-input-inline .layui-form-item{
display:flex;
}
.layui-input-inline .layui-form-item .layui-form-label{
width:30%;
}
.layui-input-block{
margin-left:unset !important;
width:75%;
}
#seleform{
border-color: #eee;
background-color: #fff;
color:#a39f9f;
width:100%;
}
</style>
<div class="p-3">
<form id="filterform" class="layui-form gg-form-bar border-t border-x">
<div class="layui-form-item">
<div class="layui-input-inline">
<div class="layui-form-item">
<label class="layui-form-label">商品分类</label>
<div class="layui-input-block">
<input id="store_cate" type="hidden" readonly="readonly" name="store_cate">
<button id="seleform" lay-filter="seleform" class="layui-btn">请选择</button>
<div id="removeselect"><i class="bi-x-lg" style="position: absolute;right:2px;top:10px;z-index: 9;cursor:pointer;"></i></div>
</div>
</div>
</div>
</div>
<div class="layui-input-inline" style="width:300px;">
<input type="text" name="keywords" placeholder="请输入关键字" class="layui-input" autocomplete="off" />
</div>
<button class="layui-btn layui-btn-normal" lay-submit="" lay-filter="searchform">提交搜索</button>
</form>
<table class="layui-hide" id="store_product" lay-filter="store_product"></table>
</div>
<script type="text/html" id="barDemo">
<div class="layui-btn-group"><a class="layui-btn layui-btn-normal layui-btn-xs" lay-event="read">采购</a></div>
</script>
{/block}
<!-- /主体 -->
<!-- 脚本 -->
{block name="script"}
<script>
const moduleInit = ['tool'];
function gouguInit() {
var table = layui.table,tool = layui.tool, form = layui.form;
layui.pageTable = table.render({
elem: '#store_product',
title: '商品表列表',
toolbar: '#toolbarDemo',
url: '/admin/store_product/index',
page: true,
limit: 20,
cellMinWidth: 300,
cols: [
[
{
fixed: 'left',
field: 'product_id',
title: '编号',
align: 'center',
width: 80
},{
field: 'image',
title: '商品图片',
align: 'center',
width: 100,
templet: '<div><img src="{{ d.image }}" style="width:30px; height:30px;"></div>',
},{
field: 'store_name',
title: '商品名称',
align: 'center',
width: 100
},{
field: 'store_info',
title: '商品简介',
align: 'center',
width: 100
},{
field: 'keyword',
title: '关键字',
align: 'center',
width: 100
},{
field: 'brand_id',
title: '品牌 id',
align: 'center',
width: 100
},{
field: 'cate_id',
title: '分类',
align: 'center',
width: 100
},{
field: 'unit_name',
title: '单位名',
align: 'center',
width: 100
},{
field: 'sales',
title: '销量',
align: 'center',
width: 100
},{
field: 'price',
title: '最低价格',
align: 'center',
width: 100
},{
field: 'cost',
title: '成本价',
align: 'center',
width: 100
},{
field: 'ot_price',
title: '原价',
align: 'center',
width: 100
},{
field: 'stock',
title: '总库存',
align: 'center',
width: 100
}, {
fixed: 'right',
field: 'right',
title: '操作',
toolbar: '#barDemo',
width: 136,
align: 'center'
}
]
]
});
//监听表头工具栏事件
table.on('toolbar(store_product)', function(obj){
if (obj.event === 'add') {
tool.side("/admin/store_product/add");
return false;
}
});
//监听表格行工具事件
table.on('tool(store_product)', function(obj) {
var data = obj.data;
if (obj.event === 'read') {
tool.side('/admin/store_product/buy?product_id='+obj.data.product_id);
}
else if (obj.event === 'edit') {
tool.side('/admin/store_product/edit?product_id='+obj.data.product_id);
}
else if (obj.event === 'del') {
layer.confirm('确定要删除该记录吗?', {
icon: 3,
title: '提示'
}, function(index) {
let callback = function (e) {
layer.msg(e.msg);
if (e.code == 0) {
obj.del();
}
}
tool.delete("/admin/store_product/del", { product_id: data.product_id }, callback);
layer.close(index);
});
}
return false;
});
// 商户商品分类搜索
layui.use(['rate','dropdown', 'util', 'layer', 'table'], function(){
var dropdown = layui.dropdown
,util = layui.util
,layer = layui.layer
,rate = layui.rate
// ,table = layui.table
,$ = layui.jquery;
var $ = layui.$, active = {
reload: function(){
let dataRload = getformdata();
//执行重载
table.reload('store_product', {
page: {
curr: 1 //重新从第 1 页开始
}
,where: {
...dataRload
}
});
},
};
//商户商品分类菜单
$.ajax({
url: '/admin/product.StoreCategory/getAllList',
method: "get",
data: {},
success: function(res) {
if (res.code!==0)return ;
//高级演示 - 各种组合
dropdown.render({
elem: '#seleform'
,isAllowSpread: false //禁止菜单组展开收缩
,style: 'width: 200px' //定义宽度,默认自适应
,id: 'test777' //定义唯一索引
,title: 'title1'
,data: res.data
,click: function(item){
$('#seleform').text(item.title);
$('#seleform').css({color:'rgba(0,0,0,.85)'});
$('#store_cate').val(item.id);
active['reload'] ? active['reload'].call(this) : '';
}
});
},
fail:function(){}
});
//基础效果
rate.render({
elem: '#is_good'
})
//监听搜索提交
form.on('submit(searchform)', function(data) {
layui.pageTable.reload({
where: {
...data.field
},
page: {
curr: 1
}
});
return false;
});
//监听select提交
form.on('select(seleform)', function(data) {
active['reload'] ? active['reload'].call(this) : '';
return false;
});
//监听removeselect点击
$('#removeselect').on('click', function(){
$('#seleform').text('请选择');
$('#seleform').css({color:'#a39f9f'});
$('#store_cate').val('');
active['reload'] ? active['reload'].call(this) : '';
return false;
});
// tab 状态列表切换
$('.site-demo-active').on('click', function(){
var othis = $(this), type = othis.data('type');
$('#protype').val(this.getAttribute('type'));
active[type] ? active[type].call(this, othis) : '';
});
});
// //监听搜索提交
// form.on('submit(searchform)', function(data) {
// layui.pageTable.reload({
// where: {
// keywords: data.field.keywords
// },
// page: {
// curr: 1
// }
// });
// return false;
// });
// 获取表单所有参数
function getformdata() {
var form = $('#filterform').serializeArray();
var data = new Array();
for(let i=0;i<form.length; i++){
data[form[i].name] = form[i].value;
}
return data;
}
}
</script>
{/block}
<!-- /脚本 -->

View File

@ -0,0 +1,290 @@
{extend name="common/base"/}
<!-- 主体 -->
{block name="body"}
<style>
.layui-input-inline{
width:35% !important;
}
.layui-input-inline .layui-form-item{
display:flex;
}
.layui-input-inline .layui-form-item .layui-form-label{
width:30%;
}
.layui-input-block{
margin-left:unset !important;
width:75%;
}
#seleform{
border-color: #eee;
background-color: #fff;
color:#a39f9f;
width:100%;
}
</style>
<div class="p-3">
<form id="filterform" class="layui-form gg-form-bar border-t border-x">
<div class="layui-form-item">
<div class="layui-input-inline">
<div class="layui-form-item">
<label class="layui-form-label">商品分类</label>
<div class="layui-input-block">
<input id="store_cate" type="hidden" readonly="readonly" name="store_cate">
<button id="seleform" lay-filter="seleform" class="layui-btn">请选择</button>
<div id="removeselect"><i class="bi-x-lg" style="position: absolute;right:2px;top:10px;z-index: 9;cursor:pointer;"></i></div>
</div>
</div>
</div>
</div>
<div class="layui-input-inline" style="width:300px;">
<input type="text" name="keywords" placeholder="请输入关键字" class="layui-input" autocomplete="off" />
</div>
<button class="layui-btn layui-btn-normal" lay-submit="" lay-filter="searchform">提交搜索</button>
</form>
<table class="layui-hide" id="store_product" lay-filter="store_product"></table>
</div>
<script type="text/html" id="barDemo">
<div class="layui-btn-group"></div>
</script>
{/block}
<!-- /主体 -->
<!-- 脚本 -->
{block name="script"}
<script>
const moduleInit = ['tool'];
function gouguInit() {
var table = layui.table,tool = layui.tool, form = layui.form;
layui.pageTable = table.render({
elem: '#store_product',
title: '列表',
toolbar: '#toolbarDemo',
url: '/admin/store_product/order',
page: true,
limit: 20,
cellMinWidth: 300,
cols: [
[
{
fixed: 'left',
field: 'id',
title: '编号',
align: 'center',
width: 80
},{
field: 'order_sn',
title: '订单号',
align: 'center',
width: 200,
},{
field: 'number',
title: '购买数量',
align: 'center',
width: 100
},{
field: 'total_fee',
title: '订单金额',
align: 'center',
width: 100
},{
field: 'product',
title: '商品简介',
align: 'center',
width: 300,
templet: function (d) {
var html = '<div><img src="'+d.image+'" style="width:30px; height:30px;"> '+d.store_name+'</div>';
return html;
}
},{
field: 'paid_at',
title: '支付完成时间',
align: 'center',
width: 150
},{
field: 'status',
title: '状态',
align: 'center',
width: 100,
templet: function (d) {
var html = '<span style="color:#fbbc05">未支付</span>';
if (d.status == '1') {
html = '<span style="color:#12bb37">已支付</span>';
}
return html;
}
}
]
]
});
//监听表头工具栏事件
table.on('toolbar(store_product)', function(obj){
if (obj.event === 'add') {
tool.side("/admin/store_product/add");
return false;
}
});
//监听表格行工具事件
table.on('tool(store_product)', function(obj) {
var data = obj.data;
if (obj.event === 'read') {
tool.side('/admin/store_product/buy?product_id='+obj.data.product_id);
}
else if (obj.event === 'edit') {
tool.side('/admin/store_product/edit?product_id='+obj.data.product_id);
}
else if (obj.event === 'del') {
layer.confirm('确定要删除该记录吗?', {
icon: 3,
title: '提示'
}, function(index) {
let callback = function (e) {
layer.msg(e.msg);
if (e.code == 0) {
obj.del();
}
}
tool.delete("/admin/store_product/del", { product_id: data.product_id }, callback);
layer.close(index);
});
}
return false;
});
// 商户商品分类搜索
layui.use(['rate','dropdown', 'util', 'layer', 'table'], function(){
var dropdown = layui.dropdown
,util = layui.util
,layer = layui.layer
,rate = layui.rate
// ,table = layui.table
,$ = layui.jquery;
var $ = layui.$, active = {
reload: function(){
let dataRload = getformdata();
//执行重载
table.reload('store_product', {
page: {
curr: 1 //重新从第 1 页开始
}
,where: {
...dataRload
}
});
},
};
//商户商品分类菜单
$.ajax({
url: '/admin/product.StoreCategory/getAllList',
method: "get",
data: {},
success: function(res) {
if (res.code!==0)return ;
//高级演示 - 各种组合
dropdown.render({
elem: '#seleform'
,isAllowSpread: false //禁止菜单组展开收缩
,style: 'width: 200px' //定义宽度,默认自适应
,id: 'test777' //定义唯一索引
,title: 'title1'
,data: res.data
,click: function(item){
$('#seleform').text(item.title);
$('#seleform').css({color:'rgba(0,0,0,.85)'});
$('#store_cate').val(item.id);
active['reload'] ? active['reload'].call(this) : '';
}
});
},
fail:function(){}
});
//基础效果
rate.render({
elem: '#is_good'
})
//监听搜索提交
form.on('submit(searchform)', function(data) {
layui.pageTable.reload({
where: {
...data.field
},
page: {
curr: 1
}
});
return false;
});
//监听select提交
form.on('select(seleform)', function(data) {
active['reload'] ? active['reload'].call(this) : '';
return false;
});
//监听removeselect点击
$('#removeselect').on('click', function(){
$('#seleform').text('请选择');
$('#seleform').css({color:'#a39f9f'});
$('#store_cate').val('');
active['reload'] ? active['reload'].call(this) : '';
return false;
});
// tab 状态列表切换
$('.site-demo-active').on('click', function(){
var othis = $(this), type = othis.data('type');
$('#protype').val(this.getAttribute('type'));
active[type] ? active[type].call(this, othis) : '';
});
});
// //监听搜索提交
// form.on('submit(searchform)', function(data) {
// layui.pageTable.reload({
// where: {
// keywords: data.field.keywords
// },
// page: {
// curr: 1
// }
// });
// return false;
// });
// 获取表单所有参数
function getformdata() {
var form = $('#filterform').serializeArray();
var data = new Array();
for(let i=0;i<form.length; i++){
data[form[i].name] = form[i].value;
}
return data;
}
}
</script>
{/block}
<!-- /脚本 -->

View File

@ -7,7 +7,7 @@
<!-- 主体 -->
{block name="body"}
<div class="layui-form p-4">
<h3 class="pb-3">文章详情</h3>
<h3 class="pb-3">详情</h3>
<table class="layui-table layui-table-form">
<tr>
<td class="layui-td-gray">商品名称<font>*</font></td>

View File

@ -1,12 +1,16 @@
<?php
namespace app\api\controller;
use app\common\controller\Api;
use app\api\BaseController;
use EasyWeChat\Factory;
use EasyWeChat\Pay\Application;
use EasyWeChat\Pay\Message;
use think\facade\Db;
use think\facade\Log;
use think\facade\Request;
class PayNotify extends Api{
class PayNotify extends BaseController
{
public function index(){
$config = [
'mch_id' => 1635725673,
@ -55,4 +59,106 @@ class PayNotify extends Api{
}
// 接收微信支付状态的通知 商城后台采购商品回调
public function notify()
{
// 配置信息
$config = [
'app_id' => 'wx0b3defb62f0f910b',//注意这个APPID只能是公众号的id没有的话要去申请并且在微信支付平台里绑定
'mch_id' => '1635725673',//商户号
'key' => '95d195Dcf6ec66156dfeeb4E7435faef',//支付秘钥
'secret' => 'c02aa7ad9e4a5c423862e068b6cb4ad4',
'notify_url' => Request::instance()->domain().'/api/PayNotify/notify',//异步回调通知地址
];
// 这个就是 easywechat 封装的了, 一行代码搞定, 照着写就行了
$app = Factory::payment($config);
// 用 easywechat 封装的方法接收微信的信息, 根据 $message 的内容进行处理, 之后要告知微信服务器处理好了, 否则微信会一直请求这个 url, 发送信息
$response = $app->handlePaidNotify(function($message, $fail){
// 首先查看 order 表, 如果 order 表有记录, 表示已经支付过了
$order = Db::table('cms_store_product_order')->where('order_sn', $message['out_trade_no'])->find();
if ($order) {
return true; // 如果已经生成订单, 表示已经处理完了, 告诉微信不用再通知了
}
// 查看支付日志
$payLog = Db::table('cms_store_product_paylog')->where('out_trade_no', $message['out_trade_no'])->find();
if (!$payLog || $payLog['time_end']) { // 如果订单不存在 或者 订单已经支付过了
return true; // 告诉微信,我已经处理完了,订单没找到,别再通知我了
}
// 更新支付时间为当前时间
$paid_at = time();
// return_code 表示通信状态,不代表支付状态
if ($message['return_code'] === 'SUCCESS') {
// 用户是否支付成功
if ($message['result_code'] === 'SUCCESS') {
$post_id = $payLog['product_id'];
// 创建订单记录
$order_arr = [
'order_sn' => $message['out_trade_no'],
'total_fee' => bcdiv($message['total_fee'],'100',2),
'pay_log_id' => $payLog['id'],
'status' => 1,
'admin_id' => $payLog['admin_id'],
'paid_at' => $paid_at,
'product_id' => $post_id,
'number' => $payLog['number'],
];
Db::table('cms_store_product_order')->strict(false)->field(true)->insert($order_arr);
// 减库存
Db::table('cms_store_product')->where('product_id', $post_id)->dec('stock',$payLog['number'])->update();
// 更新 PayLog, 这里的字段都是根据微信支付结果通知的字段设置的(https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_7&index=8)
$pay_log = [
'appid' => $message['appid'],
'bank_type' => $message['bank_type'],
'total_fee' => $message['total_fee'],
'trade_type' => $message['trade_type'],
'is_subscribe' => $message['is_subscribe'],
'mch_id' => $message['mch_id'],
'nonce_str' => $message['nonce_str'],
'openid' => $message['openid'],
'sign' => $message['sign'],
'cash_fee' => $message['cash_fee'],
'fee_type' => $message['fee_type'],
'transaction_id' => $message['transaction_id'],
'time_end' => $paid_at,
'result_code' => $message['result_code'],
'return_code' => $message['return_code'],
];
Db::table('cms_store_product_paylog')->where('out_trade_no', $message['out_trade_no'])->update($pay_log);
}
} else {
// 如果支付失败, 也更新 PayLog, 跟上面一样, 就是多了 error 信息
$pay_log = [
'appid' => $message['appid'],
'bank_type' => $message['bank_type'],
'total_fee' => $message['total_fee'],
'trade_type' => $message['trade_type'],
'is_subscribe' => $message['is_subscribe'],
'mch_id' => $message['mch_id'],
'nonce_str' => $message['nonce_str'],
'openid' => $message['openid'],
'sign' => $message['sign'],
'cash_fee' => $message['cash_fee'],
'fee_type' => $message['fee_type'],
'transaction_id' => $message['transaction_id'],
'time_end' => $paid_at,
'result_code' => $message['result_code'],
'return_code' => $message['return_code'],
'err_code' => $message['err_code'],
'err_code_des' => $message['err_code_des'],
];
Db::table('cms_store_product_paylog')->where('out_trade_no', $message['out_trade_no'])->update($pay_log);
return $fail('通信失败,请稍后再通知我');
}
return true; // 返回处理完成
});
// 这里是必须这样返回的, 会发送给微信服务器处理结果
return $response;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 444 B