支付相关
This commit is contained in:
@@ -63,6 +63,7 @@ func NewGinEngine(runtime *conf.Runtime, access *service.AccessControlService, h
|
||||
serverrouter.RegisterMedia(private, handlers.Media)
|
||||
serverrouter.RegisterAnnouncement(private, public, handlers.Announcement)
|
||||
serverrouter.RegisterEmail(private, handlers.Email)
|
||||
serverrouter.RegisterPayment(private, public, handlers.Payment)
|
||||
registerSwagger(engine, prefix, version, logger)
|
||||
registerLocalStorage(engine, runtime)
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ func emptyHandlers() *handler.Set {
|
||||
return &handler.Set{
|
||||
Authority: &handler.Authority{}, Menu: &handler.Menu{}, API: &handler.API{},
|
||||
Permission: &handler.Permission{}, Organization: &handler.Organization{},
|
||||
Announcement: &handler.Announcement{}, Email: &handler.Email{}, Task: &handler.Task{},
|
||||
Announcement: &handler.Announcement{}, Email: &handler.Email{}, Payment: &handler.Payment{}, Task: &handler.Task{},
|
||||
Media: &handler.Media{}, Audit: &handler.Audit{}, Export: &handler.Export{},
|
||||
Version: &handler.Version{}, Dictionary: &handler.Dictionary{}, Parameter: &handler.Parameter{},
|
||||
APIToken: &handler.APIToken{}, SystemConfig: &handler.SystemConfig{}, Public: &handler.Public{},
|
||||
@@ -56,7 +56,7 @@ func TestGinStartupLogsEveryRegisteredRoute(t *testing.T) {
|
||||
if got, want := strings.Count(text, `"msg":"router registered"`), len(engine.Routes()); got != want {
|
||||
t.Fatalf("registered route log count = %d, want %d", got, want)
|
||||
}
|
||||
if !strings.Contains(text, `"msg":"router register success"`) || !strings.Contains(text, `"route_count":178`) {
|
||||
if !strings.Contains(text, `"msg":"router register success"`) || !strings.Contains(text, `"route_count":185`) {
|
||||
t.Fatalf("startup route summary is missing: %s", text)
|
||||
}
|
||||
}
|
||||
@@ -256,6 +256,7 @@ GET /logViewer/content
|
||||
GET /logViewer/dates
|
||||
GET /logViewer/files
|
||||
GET /menu/getMenuRoles
|
||||
GET /payment/configs
|
||||
GET /position/findPosition
|
||||
GET /position/getPositionUsers
|
||||
GET /securityConfig/getSecurityConfig
|
||||
@@ -348,6 +349,12 @@ POST /menu/getMenuAuthority
|
||||
POST /menu/getMenuList
|
||||
POST /menu/setMenuRoles
|
||||
POST /menu/updateBaseMenu
|
||||
POST /payment/callback/:provider
|
||||
POST /payment/config
|
||||
POST /payment/create
|
||||
POST /payment/order
|
||||
POST /payment/query
|
||||
POST /payment/refund
|
||||
POST /position/createPosition
|
||||
POST /position/getPositionList
|
||||
POST /position/setPositionUsers
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"io"
|
||||
"kra/internal/biz"
|
||||
"kra/internal/server/httpx"
|
||||
"kra/internal/service"
|
||||
"kra/internal/service/dto"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type Payment struct{ service *service.PaymentService }
|
||||
|
||||
func NewPayment(service *service.PaymentService) *Payment { return &Payment{service: service} }
|
||||
func (h *Payment) Configs(c *gin.Context) {
|
||||
v, err := h.service.Configs(c.Request.Context())
|
||||
if err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
httpx.OKWithData(c, v)
|
||||
}
|
||||
func (h *Payment) SaveConfig(c *gin.Context) {
|
||||
var req dto.PaymentConfigRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if err := h.service.SaveConfig(c.Request.Context(), &biz.PaymentConfig{Provider: req.Provider, Enabled: req.Enabled, Values: req.Config}); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
httpx.OK(c)
|
||||
}
|
||||
func (h *Payment) Order(c *gin.Context) {
|
||||
var req dto.PaymentQueryRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
order, err := h.service.Order(c.Request.Context(), req.Provider, req.TradeNo)
|
||||
if err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
httpx.OKWithData(c, order)
|
||||
}
|
||||
func (h *Payment) Create(c *gin.Context) {
|
||||
var req dto.PaymentRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
req.ClientIP = c.ClientIP()
|
||||
result, err := h.service.Create(c.Request.Context(), &biz.PaymentRequest{
|
||||
Provider: req.Provider, TradeNo: req.TradeNo, Subject: req.Subject,
|
||||
Amount: req.Amount, OriginalAmount: req.OriginalAmount, Currency: req.Currency, NotifyURL: req.NotifyURL,
|
||||
ReturnURL: req.ReturnURL, ClientIP: req.ClientIP, Extra: req.Extra,
|
||||
BusinessType: req.BusinessType, BusinessID: req.BusinessID, PaymentMode: req.PaymentMode,
|
||||
})
|
||||
if err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
httpx.OKWithData(c, result)
|
||||
}
|
||||
func (h *Payment) Query(c *gin.Context) {
|
||||
var req dto.PaymentQueryRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
result, err := h.service.Query(c.Request.Context(), req.Provider, req.TradeNo)
|
||||
if err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
httpx.OKWithData(c, result)
|
||||
}
|
||||
func (h *Payment) Refund(c *gin.Context) {
|
||||
var req dto.PaymentRefundRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
result, err := h.service.Refund(c.Request.Context(), req.Provider, req.TradeNo, req.Amount)
|
||||
if err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
httpx.OKWithData(c, result)
|
||||
}
|
||||
func (h *Payment) Callback(c *gin.Context) {
|
||||
c.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, 8<<20)
|
||||
raw, err := io.ReadAll(c.Request.Body)
|
||||
if err != nil {
|
||||
writePaymentCallbackAck(c, biz.DefaultPaymentCallbackAck(c.Param("provider"), false))
|
||||
return
|
||||
}
|
||||
headers := map[string]string{}
|
||||
for k, v := range c.Request.Header {
|
||||
if len(v) > 0 {
|
||||
headers[k] = v[0]
|
||||
}
|
||||
}
|
||||
query := map[string]string{}
|
||||
for k, v := range c.Request.URL.Query() {
|
||||
if len(v) > 0 {
|
||||
query[k] = v[0]
|
||||
}
|
||||
}
|
||||
result, err := h.service.Callback(c.Request.Context(), &biz.PaymentCallback{Provider: c.Param("provider"), Headers: headers, Body: raw, Query: query})
|
||||
if err != nil {
|
||||
writePaymentCallbackAck(c, biz.CallbackFailure(err, biz.DefaultPaymentCallbackAck(c.Param("provider"), false)))
|
||||
return
|
||||
}
|
||||
ack := biz.DefaultPaymentCallbackAck(c.Param("provider"), true)
|
||||
if result != nil && result.SuccessAck.StatusCode != 0 {
|
||||
ack = result.SuccessAck
|
||||
}
|
||||
writePaymentCallbackAck(c, ack)
|
||||
}
|
||||
|
||||
func writePaymentCallbackAck(c *gin.Context, ack biz.PaymentCallbackAck) {
|
||||
if ack.StatusCode == 0 {
|
||||
ack.StatusCode = http.StatusInternalServerError
|
||||
}
|
||||
if ack.ContentType != "" {
|
||||
c.Header("Content-Type", ack.ContentType)
|
||||
}
|
||||
c.Status(ack.StatusCode)
|
||||
if len(ack.Body) > 0 {
|
||||
_, _ = c.Writer.Write(ack.Body)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"kra/internal/biz"
|
||||
)
|
||||
|
||||
func TestWritePaymentCallbackAckDoesNotExposeError(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
recorder := httptest.NewRecorder()
|
||||
context, _ := gin.CreateTestContext(recorder)
|
||||
writePaymentCallbackAck(context, biz.PaymentCallbackAck{
|
||||
StatusCode: 500,
|
||||
ContentType: "text/plain; charset=utf-8",
|
||||
Body: []byte("failure"),
|
||||
})
|
||||
if recorder.Code != 500 {
|
||||
t.Fatalf("status = %d, want 500", recorder.Code)
|
||||
}
|
||||
if recorder.Body.String() != "failure" {
|
||||
t.Fatalf("body = %q, want fixed failure ack", recorder.Body.String())
|
||||
}
|
||||
if recorder.Body.String() == "internal order resolver details" {
|
||||
t.Fatal("internal error was exposed in callback response")
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ type Set struct {
|
||||
Organization *Organization
|
||||
Announcement *Announcement
|
||||
Email *Email
|
||||
Payment *Payment
|
||||
Task *Task
|
||||
Media *Media
|
||||
Audit *Audit
|
||||
@@ -23,6 +24,6 @@ type Set struct {
|
||||
Session *Session
|
||||
}
|
||||
|
||||
func NewSet(authority *Authority, menu *Menu, api *API, permission *Permission, organization *Organization, announcement *Announcement, email *Email, task *Task, media *Media, audit *Audit, export *Export, version *Version, dictionary *Dictionary, parameter *Parameter, apiToken *APIToken, systemConfig *SystemConfig, public *Public, user *User, navigation *Navigation, session *Session) *Set {
|
||||
return &Set{Authority: authority, Menu: menu, API: api, Permission: permission, Organization: organization, Announcement: announcement, Email: email, Task: task, Media: media, Audit: audit, Export: export, Version: version, Dictionary: dictionary, Parameter: parameter, APIToken: apiToken, SystemConfig: systemConfig, Public: public, User: user, Navigation: navigation, Session: session}
|
||||
func NewSet(authority *Authority, menu *Menu, api *API, permission *Permission, organization *Organization, announcement *Announcement, email *Email, payment *Payment, task *Task, media *Media, audit *Audit, export *Export, version *Version, dictionary *Dictionary, parameter *Parameter, apiToken *APIToken, systemConfig *SystemConfig, public *Public, user *User, navigation *Navigation, session *Session) *Set {
|
||||
return &Set{Authority: authority, Menu: menu, API: api, Permission: permission, Organization: organization, Announcement: announcement, Email: email, Payment: payment, Task: task, Media: media, Audit: audit, Export: export, Version: version, Dictionary: dictionary, Parameter: parameter, APIToken: apiToken, SystemConfig: systemConfig, Public: public, User: user, Navigation: navigation, Session: session}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"kra/internal/server/handler"
|
||||
)
|
||||
|
||||
func RegisterPayment(group, public *gin.RouterGroup, h *handler.Payment) {
|
||||
payment := group.Group("/payment")
|
||||
payment.GET("/configs", h.Configs)
|
||||
payment.POST("/config", h.SaveConfig)
|
||||
payment.POST("/order", h.Order)
|
||||
payment.POST("/create", h.Create)
|
||||
payment.POST("/query", h.Query)
|
||||
payment.POST("/refund", h.Refund)
|
||||
public.POST("/payment/callback/:provider", h.Callback)
|
||||
}
|
||||
@@ -7,4 +7,4 @@ import (
|
||||
)
|
||||
|
||||
// ProviderSet is server providers.
|
||||
var ProviderSet = wire.NewSet(NewGinEngine, NewGinServer, handler.NewAuthority, handler.NewMenu, handler.NewAPI, handler.NewPermission, handler.NewOrganization, handler.NewAnnouncement, handler.NewEmail, handler.NewTask, handler.NewMedia, handler.NewAudit, handler.NewExport, handler.NewVersion, handler.NewDictionary, handler.NewParameter, handler.NewAPIToken, handler.NewSystemConfig, handler.NewPublic, handler.NewUser, handler.NewNavigation, handler.NewSession, handler.NewSet)
|
||||
var ProviderSet = wire.NewSet(NewGinEngine, NewGinServer, handler.NewAuthority, handler.NewMenu, handler.NewAPI, handler.NewPermission, handler.NewOrganization, handler.NewAnnouncement, handler.NewEmail, handler.NewPayment, handler.NewTask, handler.NewMedia, handler.NewAudit, handler.NewExport, handler.NewVersion, handler.NewDictionary, handler.NewParameter, handler.NewAPIToken, handler.NewSystemConfig, handler.NewPublic, handler.NewUser, handler.NewNavigation, handler.NewSession, handler.NewSet)
|
||||
|
||||
Reference in New Issue
Block a user