| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135 |
- package centralpayment
- import (
- "bytes"
- "encoding/json"
- "fmt"
- "net/http"
- "net/http/httptest"
- "testing"
- "time"
- "github.com/gin-gonic/gin"
- "github.com/glebarez/sqlite"
- "github.com/stretchr/testify/require"
- "gorm.io/gorm"
- "wails-app/internal/dao"
- "wails-app/internal/global"
- ticketService "wails-app/internal/modules/digital-ticket/service"
- paymentService "wails-app/internal/modules/payment/service"
- utils "wails-app/internal/pkg"
- )
- func TestPOFSimulatorHTTPFlow(t *testing.T) {
- dsn := fmt.Sprintf("file:pof-http-%d?mode=memory&cache=shared", time.Now().UnixNano())
- db, err := gorm.Open(sqlite.Open(dsn), &gorm.Config{})
- require.NoError(t, err)
- sqlDB, err := db.DB()
- require.NoError(t, err)
- t.Cleanup(func() { _ = sqlDB.Close() })
- previousDB := global.GVA_DB
- global.GVA_DB = db
- t.Cleanup(func() { global.GVA_DB = previousDB })
- require.NoError(t, db.AutoMigrate(
- &dao.CentralPaymentDevice{}, &dao.CentralPaymentToken{}, &dao.CentralPaymentRequestLog{},
- &dao.Owner{}, &dao.Vehicle{}, &dao.VehicleType{}, &dao.FeeConfig{}, &dao.VehicleRecord{}, &dao.DigitalTicket{},
- &dao.PaymentMethodConfig{}, &dao.PaymentEntryConfig{}, &dao.PaymentEntryMethod{}, &dao.PaymentRecord{},
- &dao.Shortlist{}, &dao.Channel{}, &dao.UHFReader{}, &dao.ParkingLot{}, &dao.MonthlyCard{},
- ))
- device := dao.CentralPaymentDevice{
- DeviceID: "POF-SIM-01", Username: "admin", PasswordHash: utils.BcryptHash("admin"),
- Enabled: true, PaymentEntry: "central_payment_machine",
- }
- require.NoError(t, db.Create(&device).Error)
- require.NoError(t, db.Create(&dao.PaymentMethodConfig{
- Code: paymentService.PaymentCash, Name: "现金", InputMode: paymentService.InputModeManualAmount,
- Enabled: true, IsSystem: true,
- }).Error)
- require.NoError(t, db.Create(&dao.PaymentEntryConfig{
- Code: "central_payment_machine", Name: "中央缴费机", Enabled: true, IsSystem: true,
- }).Error)
- require.NoError(t, db.Create(&dao.PaymentEntryMethod{
- EntryCode: "central_payment_machine", MethodCode: paymentService.PaymentCash,
- }).Error)
- vehicleType := dao.VehicleType{Name: "临时车", IsSystem: true}
- require.NoError(t, db.Create(&vehicleType).Error)
- require.NoError(t, db.Create(&dao.FeeConfig{
- VehicleTypeID: vehicleType.ID, StartTime: 0, StartFee: 0, UnitTime: 60, UnitFee: 1, DailyMaxFee: 100,
- }).Error)
- require.NoError(t, db.Create(&dao.Vehicle{PlateNumber: "POF-TEST", VehicleTypeID: vehicleType.ID}).Error)
- record := dao.VehicleRecord{
- PlateNumber: "POF-TEST", EntryTime: time.Now().Add(-2 * time.Hour), PaymentStatus: "unpaid",
- }
- require.NoError(t, db.Create(&record).Error)
- ticket, err := ticketService.NewTicketService().CreateTx(db, record.PlateNumber, "pof-test", record.ID)
- require.NoError(t, err)
- router := gin.New()
- SetupPublicRouter(router.Group(""))
- server := httptest.NewServer(router)
- t.Cleanup(server.Close)
- login := pofPost(t, server.URL, "/api/login", map[string]string{"UserName": "admin", "Password": "admin"})
- token, ok := login["Token"].(string)
- require.True(t, ok)
- require.NotEmpty(t, token)
- var loginLog dao.CentralPaymentRequestLog
- require.NoError(t, db.Where("endpoint = ?", "/api/login").First(&loginLog).Error)
- require.NotEmpty(t, loginLog.SourceIP)
- require.Contains(t, loginLog.RequestBody, `"password_format":"plain-text_expected"`)
- require.NotContains(t, loginLog.RequestBody, `"password":"admin"`)
- price := pofPost(t, server.URL, "/api/queryprice", map[string]interface{}{
- "Token": token,
- "Data": map[string]string{"CarNo": record.PlateNumber, "InTime": record.EntryTime.Format("2006-01-02 15:04:05")},
- })
- priceData := price["Data"].(map[string]interface{})
- require.Equal(t, ticket.TicketNo, priceData["OrderId"])
- priceValue, ok := priceData["Price"].(string)
- require.True(t, ok)
- payment := map[string]interface{}{
- "Token": token,
- "Data": map[string]interface{}{
- "OrderId": ticket.TicketNo, "CarNo": record.PlateNumber, "Money": priceValue,
- "PayTime": time.Now().Format("2006-01-02 15:04:05"), "PayType": 0, "PayDev": 2,
- // DevId is an audit field; authentication relies on the Token.
- "DevId": "FC068C0D365F", "ReMark": "HTTP simulator",
- },
- }
- require.EqualValues(t, 1, pofPost(t, server.URL, "/api/paysuccess", payment)["Result"])
- status := pofPost(t, server.URL, "/api/paystatus", map[string]interface{}{
- "Token": token, "Data": map[string]string{"OrderId": ticket.TicketNo, "CarNo": record.PlateNumber, "DevId": "FC068C0D365F"},
- })
- statusData := status["Data"].(map[string]interface{})
- require.Equal(t, "paid", statusData["PayStatus"])
- require.Equal(t, ticketService.StatePaid, statusData["State"])
- // A common device retry must be accepted without generating another payment record.
- require.EqualValues(t, 1, pofPost(t, server.URL, "/api/paysuccess", payment)["Result"])
- require.EqualValues(t, 1, pofPost(t, server.URL, "/api/heartbeat", map[string]string{"Token": token})["Result"])
- var paymentCount int64
- require.NoError(t, db.Model(&dao.PaymentRecord{}).Where("record_id = ?", record.ID).Count(&paymentCount).Error)
- require.EqualValues(t, 1, paymentCount)
- var persistedTicket dao.DigitalTicket
- require.NoError(t, db.First(&persistedTicket, ticket.ID).Error)
- require.Equal(t, ticketService.StatePaid, persistedTicket.State)
- }
- func pofPost(t *testing.T, baseURL, path string, payload interface{}) map[string]interface{} {
- t.Helper()
- body, err := json.Marshal(payload)
- require.NoError(t, err)
- response, err := http.Post(baseURL+path, "application/json", bytes.NewReader(body))
- require.NoError(t, err)
- defer response.Body.Close()
- require.Equal(t, http.StatusOK, response.StatusCode)
- var envelope map[string]interface{}
- require.NoError(t, json.NewDecoder(response.Body).Decode(&envelope))
- require.EqualValues(t, 1, envelope["Result"], "POF failure: %v", envelope["ReMark"])
- return envelope
- }
|