This commit is contained in:
parent
76198aac02
commit
75cc72ffc4
11
config.yaml
11
config.yaml
|
@ -17,4 +17,13 @@ database:
|
|||
link: "pgsql:root:root@tcp(101.200.127.15:5431)/yuledui"
|
||||
debug: true
|
||||
weixin:
|
||||
appId:"aaabbcc"
|
||||
appId: "wx5078c9d7b9eca030" #"wx231bd3f08954da9e" #微信小程序appid
|
||||
ysepay:
|
||||
notifyUrl: "http://xxxxxxxxxxxxx/ysePay/notifyWxPay"
|
||||
CERTID: "826452972730006"
|
||||
MQTT:
|
||||
topic: "pay"
|
||||
ip: "101.200.88.58"
|
||||
port: 1883
|
||||
user: "yuledui"
|
||||
passwd: "yuledui"
|
|
@ -11,6 +11,7 @@ type Pay struct {
|
|||
payService service.Pay //服务类
|
||||
}
|
||||
|
||||
// 微信小程序,支付接口
|
||||
func (t *Pay) RouterGroup(group *ghttp.RouterGroup) {
|
||||
group.POST("/payQrCode", t.payService.PayQrCode)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
package controller
|
||||
|
||||
import (
|
||||
"yuleduiPay/service"
|
||||
|
||||
"github.com/gogf/gf/v2/net/ghttp"
|
||||
)
|
||||
|
||||
// 银盛路由控制类
|
||||
type YsePay struct {
|
||||
ysePayService service.YsePay //服务类
|
||||
}
|
||||
|
||||
// 银盛异步通知余乐兑支付结果接口
|
||||
func (t *YsePay) RouterGroup(group *ghttp.RouterGroup) {
|
||||
group.POST("/notifyWxPay", t.ysePayService.NotifyWxPay)
|
||||
}
|
2
go.mod
2
go.mod
|
@ -10,6 +10,7 @@ require (
|
|||
github.com/clbanning/mxj v1.8.5-0.20200714211355-ff02cfb8ea28 // indirect
|
||||
github.com/clbanning/mxj/v2 v2.7.0 // indirect
|
||||
github.com/denisenkom/go-mssqldb v0.12.3 // indirect
|
||||
github.com/eclipse/paho.mqtt.golang v1.5.0 // indirect
|
||||
github.com/emirpasic/gods v1.18.1 // indirect
|
||||
github.com/fatih/color v1.18.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.8.0 // indirect
|
||||
|
@ -39,6 +40,7 @@ require (
|
|||
go.opentelemetry.io/otel/trace v1.31.0 // indirect
|
||||
golang.org/x/crypto v0.28.0 // indirect
|
||||
golang.org/x/net v0.30.0 // indirect
|
||||
golang.org/x/sync v0.8.0 // indirect
|
||||
golang.org/x/sys v0.26.0 // indirect
|
||||
golang.org/x/text v0.19.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
|
|
4
go.sum
4
go.sum
|
@ -19,6 +19,8 @@ github.com/denisenkom/go-mssqldb v0.0.0-20200206145737-bbfc9a55622e/go.mod h1:xb
|
|||
github.com/denisenkom/go-mssqldb v0.12.3 h1:pBSGx9Tq67pBOTLmxNuirNTeB8Vjmf886Kx+8Y+8shw=
|
||||
github.com/denisenkom/go-mssqldb v0.12.3/go.mod h1:k0mtMFOnU+AihqFxPMiF05rtiDrorD1Vrm1KEz5hxDo=
|
||||
github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ=
|
||||
github.com/eclipse/paho.mqtt.golang v1.5.0 h1:EH+bUVJNgttidWFkLLVKaQPGmkTUfQQqjOsyvMGvD6o=
|
||||
github.com/eclipse/paho.mqtt.golang v1.5.0/go.mod h1:du/2qNQVqJf/Sqs4MEL77kR8QTqANF7XU7Fk0aOTAgk=
|
||||
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
|
||||
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
|
||||
github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
|
||||
|
@ -152,6 +154,8 @@ golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
|
|||
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
|
||||
golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
|
||||
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
|
||||
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
||||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
|
|
|
@ -2,3 +2,11 @@
|
|||
2024-11-05 08:52:00.651 {d8d007fb40ec04185998a93c02952ba0} main.go:31: 初始化成功
|
||||
2024-11-05 08:52:29.385 main.go:18: 初始化成功
|
||||
2024-11-05 09:42:18.488 main.go:18: 初始化成功
|
||||
2024-11-05 15:21:03.977 main.go:18: 初始化成功
|
||||
2024-11-05 16:09:55.574 main.go:18: 初始化成功
|
||||
2024-11-05 16:55:56.689 main.go:20: 初始化成功
|
||||
2024-11-05 17:01:39.141 main.go:20: 初始化成功
|
||||
2024-11-05 17:26:42.774 mqtt.go:49: no servers defined to connect to
|
||||
2024-11-05 17:26:42.774 main.go:26: 初始化成功
|
||||
2024-11-05 17:34:28.482 mqtt.go:49: no servers defined to connect to
|
||||
2024-11-05 17:34:28.482 main.go:26: 初始化成功
|
||||
|
|
10
main.go
10
main.go
|
@ -3,6 +3,7 @@ package main
|
|||
import (
|
||||
"yuleduiPay/controller"
|
||||
"yuleduiPay/middle"
|
||||
"yuleduiPay/service"
|
||||
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
|
||||
|
@ -10,11 +11,18 @@ import (
|
|||
)
|
||||
|
||||
func main() {
|
||||
|
||||
s := g.Server()
|
||||
s.Use(middle.MiddlewareCORS, middle.MiddlewareHandlerResponse)
|
||||
payRouter := controller.Pay{}
|
||||
s.Group("/wxApi", payRouter.RouterGroup)
|
||||
|
||||
yesPayRouter := controller.YsePay{}
|
||||
s.Group("/ysePay", yesPayRouter.RouterGroup)
|
||||
mqtt := service.MQTT{}
|
||||
err := mqtt.InitMQTT()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
g.Log().Line().Print(nil, "初始化成功")
|
||||
s.Run()
|
||||
|
||||
|
|
|
@ -13,3 +13,13 @@ func (t *PayOrder) CreatePayOrder(payOrder *po.PayOrder) error {
|
|||
_, err := g.Model("pay_order").Data(payOrder).Insert()
|
||||
return err
|
||||
}
|
||||
|
||||
func (t *PayOrder) UpdatePayOrderByOrderId(updateMap g.Map, orderId string) error {
|
||||
_, err := g.Model("pay_order").Data(updateMap).Where("orderId = ?", orderId).Update()
|
||||
return err
|
||||
}
|
||||
func (t *PayOrder) GetPayOrderByOrderId(orderId string) (*po.PayOrder, error) {
|
||||
payOrder := &po.PayOrder{}
|
||||
err := g.Model("pay_order").Where("orderId = ?", orderId).Scan(payOrder)
|
||||
return payOrder, err
|
||||
}
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
package service
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
mqtt "github.com/eclipse/paho.mqtt.golang"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/util/guid"
|
||||
)
|
||||
|
||||
type MQTT struct {
|
||||
mc mqtt.Client
|
||||
}
|
||||
|
||||
func (t *MQTT) InitMQTT() error {
|
||||
ip, err := g.Cfg().Get(nil, "MQTT.ip")
|
||||
if err != nil {
|
||||
g.Log().Line().Print(nil, err)
|
||||
return err
|
||||
}
|
||||
port, err := g.Cfg().Get(nil, "MQTT.port")
|
||||
if err != nil {
|
||||
g.Log().Line().Print(nil, err)
|
||||
return err
|
||||
}
|
||||
user, err := g.Cfg().Get(nil, "MQTT.user")
|
||||
if err != nil {
|
||||
g.Log().Line().Print(nil, err)
|
||||
return err
|
||||
}
|
||||
passwd, err := g.Cfg().Get(nil, "MQTT.passwd")
|
||||
if err != nil {
|
||||
g.Log().Line().Print(nil, err)
|
||||
return err
|
||||
}
|
||||
opts := mqtt.NewClientOptions()
|
||||
opts.AddBroker(fmt.Sprintf("mqtt://%s:%d", ip, port))
|
||||
opts.SetKeepAlive(time.Hour * 24)
|
||||
opts.SetPingTimeout(time.Second * 30)
|
||||
opts.SetClientID(guid.S())
|
||||
opts.SetUsername(user.String())
|
||||
opts.SetPassword(passwd.String())
|
||||
//opts.SetDefaultPublishHandler(messagePubHandler)
|
||||
//opts.OnConnect = connectHandler
|
||||
opts.OnConnectionLost = t.connectLostHandler
|
||||
t.mc = mqtt.NewClient(opts)
|
||||
if token := t.mc.Connect(); token.Wait() && token.Error() != nil {
|
||||
g.Log().Line().Print(nil, token.Error())
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MQTT 推送订单结果消息
|
||||
//func (t *MQTT) Publish(client mqtt.Client,msg *vo.MQTTOrder) string {
|
||||
|
||||
// 返回支付结果消息
|
||||
/*
|
||||
func publishResult(client mqtt.Client, msg *vo.MQTTOrder) string {
|
||||
//command.Msg_Id = guid.S()
|
||||
command.SendTime = time.Now()
|
||||
j, err := json.Marshal(msg)
|
||||
if err == nil {
|
||||
token := client.Publish(command.Topic, 0, false, string(j))
|
||||
token.Wait()
|
||||
return ""
|
||||
} else {
|
||||
return err.Error()
|
||||
}
|
||||
}
|
||||
*/
|
||||
func (t *MQTT) connectLostHandler(client mqtt.Client, err error) {
|
||||
token := t.mc.Connect()
|
||||
g.Log().Line().Print(nil, "MQTT 重新连接成功")
|
||||
token.Wait()
|
||||
}
|
124
service/pay.go
124
service/pay.go
|
@ -1,8 +1,14 @@
|
|||
package service
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
"yuleduiPay/repo"
|
||||
"yuleduiPay/service/po"
|
||||
"yuleduiPay/service/vo"
|
||||
|
@ -18,7 +24,11 @@ type Pay struct {
|
|||
shopRepo repo.Shop
|
||||
}
|
||||
|
||||
var YSEPAYURL = "https://qrcode.ysepay.com/gateway.do" //银盛的微信小程序接口地址
|
||||
|
||||
// 微信小程序,扫码支付
|
||||
func (t *Pay) PayQrCode(r *ghttp.Request) {
|
||||
g.Log().Line().Print(nil, r.Request) //todo
|
||||
req := vo.PayQrCodeReq{}
|
||||
err := r.Parse(&req)
|
||||
if err != nil {
|
||||
|
@ -61,32 +71,100 @@ func (t *Pay) PayQrCode(r *ghttp.Request) {
|
|||
r.SetError(err)
|
||||
return
|
||||
}
|
||||
//调用银盛的微信小程序接口(银盛接口)
|
||||
mMap := g.Map{}
|
||||
mMap["orderId"] = payOrder.OpenId //订单编号
|
||||
mMap["shopdate"] = payOrder.Created.Format("20060102") //商户系统的交易发生日期格式
|
||||
mMap["subject"] = "余乐兑小程序订单" //订单备注
|
||||
mMap["total_amount"] = payOrder.Amount //该笔订单的资金总额????????
|
||||
mMap["currency"] = "CNY" //默认人民币 //订单备注
|
||||
mMap["seller_id"] = shop.AccountNumber //商户ID
|
||||
mMap["seller_name"] = shop.ShopName //店铺名称
|
||||
mMap["timeout_express"] = "1h" //设置未付款交易的超时时间,一个小时
|
||||
mMap["sub_openid"] = payOrder.OpenId //微信OpenId
|
||||
mMap["is_minipg"] = "1" //微信小程序支付:1
|
||||
appId, err := g.Cfg().Get(nil, "weixin.appId")
|
||||
|
||||
jsonBytes, err := t.ysePayRequestJson(shop, &payOrder) //组织银盛请求json
|
||||
if err != nil {
|
||||
r.SetError(err)
|
||||
return
|
||||
}
|
||||
mMap["appid"] = appId
|
||||
|
||||
err = errors.New("cuowu!!!!!")
|
||||
r.SetError(err)
|
||||
//java 项目处理sign字段
|
||||
jsonResp, err := t.ysePayPost(jsonBytes) //Post请求银盛接口
|
||||
if err != nil {
|
||||
r.SetError(err)
|
||||
return
|
||||
}
|
||||
yseResp, err := t.yseRespHandler(jsonResp) //解析银盛应答报文
|
||||
if err != nil {
|
||||
r.SetError(err)
|
||||
return
|
||||
}
|
||||
if yseResp.OutTradeNo != payOrder.OpenId {
|
||||
errStr := fmt.Sprintf("银盛返回订单号错误,银盛订单号:%s,余乐兑订单号:%s", yseResp.OutTradeNo, payOrder.OpenId)
|
||||
r.SetError(errors.New(errStr))
|
||||
return
|
||||
}
|
||||
//更新支付订单表字段
|
||||
updates := g.Map{"ysePayStatus": yseResp.TradeStatus, "updated": time.Now()}
|
||||
err = t.payOrderRepo.UpdatePayOrderByOrderId(updates, payOrder.OpenId)
|
||||
if err != nil {
|
||||
r.SetError(err)
|
||||
}
|
||||
return
|
||||
/*
|
||||
resp := vo.PayQrCodeResp{}
|
||||
resp.Code = 0
|
||||
resp.Message = "success"
|
||||
r.Response.WriteJson(resp)
|
||||
*/
|
||||
}
|
||||
func (t *Pay) yseRespHandler(jsonResp []byte) (*vo.WeixinPayResp, error) {
|
||||
ysePayResp := &vo.PayResp{}
|
||||
err := json.Unmarshal(jsonResp, ysePayResp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
yseWeiXinPayResp := &vo.WeixinPayResp{}
|
||||
err = json.Unmarshal([]byte(ysePayResp.YsepayOnlineWeixinPayResponse), yseWeiXinPayResp)
|
||||
if yseWeiXinPayResp.Code != "10000" {
|
||||
return nil, errors.New("银盛应答返回错误:" + yseWeiXinPayResp.Msg)
|
||||
}
|
||||
return yseWeiXinPayResp, err
|
||||
}
|
||||
func (t *Pay) ysePayPost(jsonBytes []byte) ([]byte, error) {
|
||||
resp, err := http.Post(YSEPAYURL, "application/json", bytes.NewReader(jsonBytes))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
return io.ReadAll(resp.Body)
|
||||
}
|
||||
|
||||
func (t *Pay) ysePayRequestJson(shop *po.Shop, payOrder *po.PayOrder) ([]byte, error) {
|
||||
jsonMap := g.Map{}
|
||||
//公共请求参数
|
||||
jsonMap["method"] = "ysepay.online.weixin.pay" //接口名称,固定值
|
||||
certId, err := g.Cfg().Get(nil, "ysepay.CERTID")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
jsonMap["partner_id"] = certId //在银盛支付开设的服务商商户号
|
||||
jsonMap["timestamp"] = time.Now().Format("2006-01-02 15:04:05") //发送请求的时间
|
||||
jsonMap["charset"] = "UTF-8" //商户网站使用的编码格式
|
||||
jsonMap["sign_type"] = "SM" //报文签名算法
|
||||
//sign 需要java项目处理 todo
|
||||
notifyUrl, err := g.Cfg().Get(nil, "ysepay.notifyUrl")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
jsonMap["notify_url"] = notifyUrl //交易成功异步通知到商户的后台地址
|
||||
jsonMap["version"] = "3.0" //接口版本
|
||||
|
||||
//业务请求参数
|
||||
businessMap := g.Map{}
|
||||
businessMap["orderId"] = payOrder.OpenId //订单编号
|
||||
businessMap["shopdate"] = payOrder.Created.Format("20060102") //商户系统的交易发生日期格式
|
||||
businessMap["subject"] = "余乐兑小程序订单" //订单备注
|
||||
businessMap["total_amount"] = payOrder.Price //该笔订单的资金总额
|
||||
businessMap["currency"] = "CNY" //默认人民币 //订单备注
|
||||
businessMap["seller_id"] = shop.AccountNumber //商户ID
|
||||
businessMap["seller_name"] = shop.ShopName //店铺名称
|
||||
businessMap["timeout_express"] = "1h" //设置未付款交易的超时时间,一个小时
|
||||
businessMap["sub_openid"] = payOrder.OpenId //微信OpenId
|
||||
businessMap["is_minipg"] = "1" //微信小程序支付:1
|
||||
appId, err := g.Cfg().Get(nil, "weixin.appId")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
businessMap["appid"] = appId //微信小程序APPID
|
||||
b, err := json.Marshal(businessMap)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
jsonMap["biz_content"] = string(b)
|
||||
return json.Marshal(jsonMap)
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
package vo
|
||||
|
||||
type MQTTOrder struct {
|
||||
OrderId string `json:"orderId"` //订单号
|
||||
OrderEndTime int64 `json:"orderEndTime"` //支付完成时间,unix时间戳
|
||||
Price string `json:"price"` //订单原价(小数点后6位)
|
||||
Amount string `json:"amount"` //支付金额(小数点后6位)
|
||||
Bean string `json:"bean"` //抵扣金豆
|
||||
ShopId int64 `json:"shopId"` //店铺ID
|
||||
OpenId string `json:"openId"` //微信用户openId
|
||||
Mobile string `json:"mobile"` //付款用户手机号
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
package vo
|
||||
|
||||
type PayResp struct {
|
||||
Sign string `json:"sign"` //签名字符串 Base64编码
|
||||
YsepayOnlineWeixinPayResponse string `json:"ysepay_online_weixin_pay_response"` //业务响应参数的集合,最大长度不限
|
||||
}
|
||||
|
||||
type WeixinPayResp struct {
|
||||
Code string `json:"code"` //响应代码
|
||||
Msg string `json:"msg"` //响应代码描述
|
||||
OutTradeNo string `json:"out_trade_no"` //商户系统生成的订单号
|
||||
TradeNo string `json:"trade_no"` //银盛支付交易流水号
|
||||
TradeStatus string `json:"trade_status"` //交易状态,成功状态的值: TRADE_SUCCESS参考附录8.1
|
||||
TotalAmount int `json:"total_amount"` //该笔订单的资金总额
|
||||
Currency string `json:"currency"` //交易币种 默认CNY
|
||||
ExtraCommonParam string `json:"extra_common_param"` //公用回传参数 商户自定义数据域,原样返回
|
||||
JsapiPayInfo string `json:"jsapi_pay_info"` //Json格式字符串,作用于原生态的js支付时的参数
|
||||
IsDiscount string `json:"is_discount"` //是否参与优惠,Y表示参与,N表示不参与
|
||||
TotalDisCount float64 `json:"total_discount"` //参考总优惠金额 100.00
|
||||
ChannelSendSN string `json:"channel_send_sn"` //发往渠道流水号
|
||||
}
|
||||
|
||||
// 银盛异步通知
|
||||
type NotifyPayReq struct {
|
||||
SignType string `json:"sign_type"` //签名类型
|
||||
Sign string `json:"sign"` //签名字符串
|
||||
NotifyType string `json:"notify_type"` //通知类型
|
||||
NotifyTime string `json:"notify_time"` //发送请求的时间
|
||||
OutTradeNo string `json:"out_trade_no"` //商户生成的订单号
|
||||
TradeStatus string `json:"trade_status"` //交易目前所处的状态 成功状态的值: TRADE_SUCCESS
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
package service
|
||||
|
||||
import (
|
||||
"time"
|
||||
"yuleduiPay/repo"
|
||||
"yuleduiPay/service/vo"
|
||||
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/net/ghttp"
|
||||
)
|
||||
|
||||
type YsePay struct {
|
||||
payOrderRepo repo.PayOrder
|
||||
shopRepo repo.Shop
|
||||
}
|
||||
|
||||
// 银盛 异步通知余乐兑
|
||||
func (t *YsePay) NotifyWxPay(r *ghttp.Request) {
|
||||
//r.Request
|
||||
req := vo.NotifyPayReq{}
|
||||
err := r.Parse(&req)
|
||||
if err != nil {
|
||||
r.SetError(err)
|
||||
return
|
||||
}
|
||||
/*
|
||||
payOrder,err := t.payOrderRepo.GetPayOrderByOrderId(req.OutTradeNo) //获取订单
|
||||
if err != nil {
|
||||
r.SetError(err)
|
||||
return
|
||||
}
|
||||
*/
|
||||
updates := g.Map{"ysePayStatus": req.TradeStatus, "updated": time.Now()}
|
||||
err = t.payOrderRepo.UpdatePayOrderByOrderId(updates, req.OutTradeNo)
|
||||
if err != nil {
|
||||
r.SetError(err)
|
||||
return
|
||||
}
|
||||
//将支付结果写入MQTT中
|
||||
return
|
||||
}
|
Loading…
Reference in New Issue