This commit is contained in:
孟帅
2023-05-10 23:54:50 +08:00
parent bbe655a4d8
commit 49a96750bf
314 changed files with 15138 additions and 6244 deletions

View File

@@ -0,0 +1,141 @@
package pay
// 订单创建
import (
"context"
"fmt"
"github.com/gogf/gf/v2/encoding/gjson"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
"github.com/gogf/gf/v2/os/gctx"
"github.com/gogf/gf/v2/util/gmeta"
"hotgo/api/api/pay"
"hotgo/internal/consts"
"hotgo/internal/library/contexts"
"hotgo/internal/library/location"
"hotgo/internal/library/payment"
"hotgo/internal/model/entity"
"hotgo/internal/model/input/payin"
"hotgo/internal/service"
"hotgo/utility/validate"
)
// Create 创建支付订单和日志
func (s *sPay) Create(ctx context.Context, in payin.PayCreateInp) (res *payin.PayCreateModel, err error) {
request := ghttp.RequestFromCtx(ctx)
if in.TradeType == "" {
in.TradeType = payment.AutoTradeType(in.PayType, request.UserAgent())
}
if in.Openid == "" {
if in.Openid, err = service.CommonWechat().GetOpenId(ctx); err != nil {
return
}
}
if in.TradeType == consts.TradeTypeWxMP {
if in.Openid == "" {
err = gerror.New("微信公众号支付必须设置openid")
return
}
if in.ReturnUrl == "" {
err = gerror.New("微信公众号支付必须设置同步通知地址")
return
}
}
notifyURL, err := s.GenNotifyURL(ctx, in)
if err != nil {
return
}
config := payment.GetConfig()
mchId := ""
switch in.PayType {
case consts.PayTypeAliPay:
mchId = config.AliPayAppId
case consts.PayTypeWxPay:
mchId = config.WxPayMchId
case consts.PayTypeQQPay:
mchId = config.QQPayMchId
}
data := &entity.PayLog{
MemberId: contexts.GetUserId(ctx),
AppId: contexts.GetModule(ctx),
AddonsName: contexts.GetAddonName(ctx),
OrderSn: in.OrderSn,
OrderGroup: in.OrderGroup,
Openid: in.Openid,
MchId: mchId,
Subject: in.Subject,
Detail: in.Detail,
OutTradeNo: payment.GenOutTradeNo(),
TransactionId: "",
PayType: in.PayType,
PayAmount: in.PayAmount,
PayStatus: consts.PayStatusWait,
TradeType: in.TradeType,
IsRefund: consts.RefundStatusNo,
CreateIp: location.GetClientIp(request),
NotifyUrl: notifyURL,
ReturnUrl: in.ReturnUrl,
TraceIds: gjson.New([]string{gctx.CtxId(ctx)}),
Status: consts.StatusEnabled,
}
// 创建支付记录
if _, err = s.Model(ctx).Data(data).Insert(); err != nil {
return
}
// 创建第三方平台支付订单
order, err := payment.New(in.PayType).CreateOrder(ctx, payin.CreateOrderInp{Pay: data})
if err != nil {
return
}
res = new(payin.PayCreateModel)
res.Order = order
return
}
// GenNotifyURL 生成支付通知地址
func (s *sPay) GenNotifyURL(ctx context.Context, in payin.PayCreateInp) (notifyURL string, err error) {
basic, err := service.SysConfig().GetBasic(ctx)
if err != nil {
return
}
if basic.Domain == "" {
err = gerror.New("请先到后台【系统设置】-【配置管理】中设置网站域名!")
return
}
if !validate.IsURL(basic.Domain) {
err = gerror.New("网站域名格式有误,请检查!")
return
}
var object interface{}
switch in.PayType {
case consts.PayTypeAliPay:
object = pay.NotifyAliPayReq{}
case consts.PayTypeWxPay:
object = pay.NotifyWxPayReq{}
case consts.PayTypeQQPay:
object = pay.NotifyQQPayReq{}
default:
err = gerror.Newf("未被支持的支付方式:%v", in.PayType)
return
}
notifyURL = fmt.Sprintf("%s%s%s",
basic.Domain,
g.Cfg().MustGet(ctx, "router.api.prefix", "/api").String(),
gmeta.Get(object, "path").String(),
)
return
}

View File

@@ -0,0 +1,85 @@
package pay
// 异步通知
import (
"context"
"github.com/gogf/gf/v2/encoding/gjson"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
"github.com/gogf/gf/v2/os/gctx"
"hotgo/internal/consts"
"hotgo/internal/dao"
"hotgo/internal/library/location"
"hotgo/internal/library/payment"
"hotgo/internal/model/entity"
"hotgo/internal/model/input/payin"
)
// Notify 异步通知
func (s *sPay) Notify(ctx context.Context, in payin.PayNotifyInp) (res *payin.PayNotifyModel, err error) {
data, err := payment.New(in.PayType).Notify(ctx, payin.NotifyInp{})
if err != nil {
return
}
var models *entity.PayLog
if err = s.Model(ctx).Where(dao.PayLog.Columns().OutTradeNo, data.OutTradeNo).Scan(&models); err != nil {
return
}
if models == nil {
err = gerror.Newf("商户订单号[%v]不存在支付记录,请检查", data.OutTradeNo)
return
}
if models.PayStatus != consts.PayStatusWait {
err = gerror.Newf("商户订单号[%v]已被处理,请勿重复操作", data.OutTradeNo)
return
}
var traceIds []string
if err = models.TraceIds.Scan(&traceIds); err != nil {
return
}
traceIds = append(traceIds, gctx.CtxId(ctx))
models.TransactionId = data.TransactionId
models.PayStatus = consts.PayStatusOk
models.PayAt = data.PayAt
models.ActualAmount = data.ActualAmount
models.PayIp = location.GetClientIp(ghttp.RequestFromCtx(ctx))
models.TraceIds = gjson.New(traceIds)
result, err := s.Model(ctx).
Fields(
dao.PayLog.Columns().TransactionId,
dao.PayLog.Columns().PayStatus,
dao.PayLog.Columns().PayAt,
dao.PayLog.Columns().PayIp,
dao.PayLog.Columns().TraceIds,
dao.PayLog.Columns().ActualAmount,
).
Where(dao.PayLog.Columns().Id, models.Id).
Where(dao.PayLog.Columns().PayStatus, consts.PayStatusWait).
OmitEmpty().
Data(models).Update()
if err != nil {
return
}
ret, err := result.RowsAffected()
if err != nil {
return
}
if ret == 0 {
g.Log().Warningf(ctx, "没有被更新的数据行")
return
}
// 回调业务
payment.NotifyCall(ctx, payin.NotifyCallFuncInp{Pay: models})
return
}

View File

@@ -0,0 +1,155 @@
// Package pay
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2023 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package pay
// 支付日志相关
import (
"context"
"fmt"
"hotgo/internal/consts"
"hotgo/internal/dao"
"hotgo/internal/library/hgorm/handler"
"hotgo/internal/model/input/form"
"hotgo/internal/model/input/payin"
"hotgo/internal/service"
"hotgo/utility/convert"
"hotgo/utility/excel"
"hotgo/utility/validate"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gctx"
"github.com/gogf/gf/v2/util/gconv"
)
type sPay struct{}
func NewPay() *sPay {
return &sPay{}
}
func init() {
service.RegisterPay(NewPay())
}
// Model 支付日志ORM模型
func (s *sPay) Model(ctx context.Context, option ...*handler.Option) *gdb.Model {
return handler.Model(dao.PayLog.Ctx(ctx), option...)
}
// List 获取支付日志列表
func (s *sPay) List(ctx context.Context, in payin.PayListInp) (list []*payin.PayListModel, totalCount int, err error) {
mod := s.Model(ctx)
// 查询ID
if in.Id > 0 {
mod = mod.Where(dao.PayLog.Columns().Id, in.Id)
}
// 查询状态
if in.Status > 0 {
mod = mod.Where(dao.PayLog.Columns().Status, in.Status)
}
// 查询创建时间
if len(in.CreatedAt) == 2 {
mod = mod.WhereBetween(dao.PayLog.Columns().CreatedAt, in.CreatedAt[0], in.CreatedAt[1])
}
// 查询分类名称
if in.TestCategoryName != "" {
mod = mod.WhereLike(dao.TestCategory.Columns().Name, in.TestCategoryName)
}
totalCount, err = mod.Clone().Count()
if err != nil {
return
}
if totalCount == 0 {
return
}
err = mod.Page(in.Page, in.PerPage).OrderDesc(dao.PayLog.Columns().Id).Scan(&list)
return
}
// Export 导出支付日志
func (s *sPay) Export(ctx context.Context, in payin.PayListInp) (err error) {
list, totalCount, err := s.List(ctx, in)
if err != nil {
return
}
// 字段的排序是依据tags的字段顺序如果你不想使用默认的排序方式可以直接定义 tags = []string{"字段名称", "字段名称2", ...}
tags, err := convert.GetEntityDescTags(payin.PayExportModel{})
if err != nil {
return
}
var (
fileName = "导出支付日志-" + gctx.CtxId(ctx) + ".xlsx"
sheetName = fmt.Sprintf("索引条件共%v行,共%v页,当前导出是第%v页,本页共%v行", totalCount, form.CalPageCount(totalCount, in.PerPage), in.Page, len(list))
exports []payin.PayExportModel
)
if err = gconv.Scan(list, &exports); err != nil {
return
}
err = excel.ExportByStructs(ctx, tags, exports, fileName, sheetName)
return
}
// Edit 修改/新增支付日志
func (s *sPay) Edit(ctx context.Context, in payin.PayEditInp) (err error) {
// 修改
if in.Id > 0 {
_, err = s.Model(ctx).Where(dao.PayLog.Columns().Id, in.Id).Data(in).Update()
return
}
// 新增
_, err = s.Model(ctx, &handler.Option{FilterAuth: false}).Data(in).Insert()
return
}
// Delete 删除支付日志
func (s *sPay) Delete(ctx context.Context, in payin.PayDeleteInp) (err error) {
_, err = s.Model(ctx).Where(dao.PayLog.Columns().Id, in.Id).Delete()
return
}
// View 获取支付日志指定信息
func (s *sPay) View(ctx context.Context, in payin.PayViewInp) (res *payin.PayViewModel, err error) {
err = s.Model(ctx).Where(dao.PayLog.Columns().Id, in.Id).Scan(&res)
return
}
// Status 更新支付日志状态
func (s *sPay) Status(ctx context.Context, in payin.PayStatusInp) (err error) {
if in.Id <= 0 {
err = gerror.New("ID不能为空")
return
}
if in.Status <= 0 {
err = gerror.New("状态不能为空")
return
}
if !validate.InSliceInt(consts.StatusMap, in.Status) {
err = gerror.New("状态不正确")
return
}
_, err = s.Model(ctx).Where(dao.PayLog.Columns().Id, in.Id).Data(g.Map{
dao.PayLog.Columns().Status: in.Status,
}).Update()
return
}

View File

@@ -0,0 +1,225 @@
// Package pay
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2023 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
// @AutoGenerate Version 2.5.3
// @AutoGenerate Date 2023-04-15 15:59:58
package pay
import (
"context"
"fmt"
"github.com/gogf/gf/v2/encoding/gjson"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
"hotgo/internal/consts"
"hotgo/internal/dao"
"hotgo/internal/library/hgorm"
"hotgo/internal/library/hgorm/handler"
"hotgo/internal/library/location"
"hotgo/internal/library/payment"
"hotgo/internal/model/entity"
"hotgo/internal/model/input/form"
"hotgo/internal/model/input/payin"
"hotgo/internal/service"
"hotgo/utility/convert"
"hotgo/utility/excel"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/os/gctx"
"github.com/gogf/gf/v2/util/gconv"
)
// 订单退款.
type sPayRefund struct{}
func NewPayRefund() *sPayRefund {
return &sPayRefund{}
}
func init() {
service.RegisterPayRefund(NewPayRefund())
}
// Model 交易退款ORM模型
func (s *sPayRefund) Model(ctx context.Context, option ...*handler.Option) *gdb.Model {
return handler.Model(dao.PayRefund.Ctx(ctx), option...)
}
// Refund 订单退款
func (s *sPayRefund) Refund(ctx context.Context, in payin.PayRefundInp) (res *payin.PayRefundModel, err error) {
var models *entity.PayLog
if err = service.Pay().Model(ctx).Where(dao.PayLog.Columns().OrderSn, in.OrderSn).Scan(&models); err != nil {
return
}
if models == nil {
err = gerror.Newf("业务订单号[%v]不存在支付记录,请检查", in.OrderSn)
return
}
if models.PayStatus != consts.PayStatusOk {
err = gerror.Newf("业务订单号[%v]未支付,无需退款", in.OrderSn)
return
}
if models.IsRefund != consts.RefundStatusNo {
err = gerror.Newf("业务订单号[%v]退款已被处理,请勿重复操作", in.OrderSn)
return
}
var traceIds []string
if err = models.TraceIds.Scan(&traceIds); err != nil {
return
}
traceIds = append(traceIds, gctx.CtxId(ctx))
refundSn := payment.GenRefundSn()
// 创建第三方平台退款
req := payin.RefundInp{
Pay: models,
RefundMoney: in.RefundMoney,
Reason: in.Reason,
Remark: in.Remark,
RefundSn: refundSn,
}
if _, err = payment.New(models.PayType).Refund(ctx, req); err != nil {
return
}
models.RefundSn = refundSn
models.IsRefund = consts.RefundStatusAgree
models.TraceIds = gjson.New(traceIds)
result, err := s.Model(ctx).
Fields(
dao.PayLog.Columns().RefundSn,
dao.PayLog.Columns().IsRefund,
dao.PayLog.Columns().TraceIds,
).
Where(dao.PayLog.Columns().Id, models.Id).
OmitEmpty().
Data(models).Update()
if err != nil {
return
}
ret, err := result.RowsAffected()
if err != nil {
return
}
if ret == 0 {
g.Log().Warningf(ctx, "Refund 没有被更新的数据行")
}
data := &entity.PayRefund{
Id: 0,
MemberId: models.MemberId,
AppId: models.AppId,
OrderSn: models.OrderSn,
RefundTradeNo: "",
RefundMoney: in.RefundMoney,
RefundWay: 1,
Ip: location.GetClientIp(ghttp.RequestFromCtx(ctx)),
Reason: in.Reason,
Remark: in.Remark,
Status: consts.RefundStatusAgree,
}
// 创建退款记录
if _, err = s.Model(ctx).Data(data).Insert(); err != nil {
return
}
return
}
// List 获取交易退款列表
func (s *sPayRefund) List(ctx context.Context, in payin.PayRefundListInp) (list []*payin.PayRefundListModel, totalCount int, err error) {
mod := s.Model(ctx)
// 查询变动ID
if in.Id > 0 {
mod = mod.Where(dao.PayRefund.Columns().Id, in.Id)
}
// 查询管理员ID
if in.MemberId > 0 {
mod = mod.Where(dao.PayRefund.Columns().MemberId, in.MemberId)
}
// 查询应用id
if in.AppId != "" {
mod = mod.WhereLike(dao.PayRefund.Columns().AppId, in.AppId)
}
// 查询备注
if in.Remark != "" {
mod = mod.WhereLike(dao.PayRefund.Columns().Remark, in.Remark)
}
// 查询操作人IP
if in.Ip != "" {
mod = mod.WhereLike(dao.PayRefund.Columns().Ip, in.Ip)
}
// 查询状态
if in.Status > 0 {
mod = mod.Where(dao.PayRefund.Columns().Status, in.Status)
}
// 查询创建时间
if len(in.CreatedAt) == 2 {
mod = mod.WhereBetween(dao.PayRefund.Columns().CreatedAt, in.CreatedAt[0], in.CreatedAt[1])
}
totalCount, err = mod.Clone().Count(1)
if err != nil {
return
}
if totalCount == 0 {
return
}
fields, err := hgorm.GenSelect(ctx, payin.PayRefundListModel{}, dao.PayRefund)
if err != nil {
return
}
err = mod.Fields(fields).Page(in.Page, in.PerPage).OrderDesc(dao.PayRefund.Columns().Id).Scan(&list)
return
}
// Export 导出交易退款
func (s *sPayRefund) Export(ctx context.Context, in payin.PayRefundListInp) (err error) {
list, totalCount, err := s.List(ctx, in)
if err != nil {
return
}
// 字段的排序是依据tags的字段顺序如果你不想使用默认的排序方式可以直接定义 tags = []string{"字段名称", "字段名称2", ...}
tags, err := convert.GetEntityDescTags(payin.PayRefundExportModel{})
if err != nil {
return
}
var (
fileName = "导出交易退款-" + gctx.CtxId(ctx) + ".xlsx"
sheetName = fmt.Sprintf("索引条件共%v行,共%v页,当前导出是第%v页,本页共%v行", totalCount, form.CalPageCount(totalCount, in.PerPage), in.Page, len(list))
exports []payin.PayRefundExportModel
)
if err = gconv.Scan(list, &exports); err != nil {
return
}
err = excel.ExportByStructs(ctx, tags, exports, fileName, sheetName)
return
}