package handlers import ( "Wine-Server/utils" "Wine-Server/utils/tables" "context" "database/sql" "github.com/gin-contrib/cors" "github.com/gin-gonic/gin" "github.com/redis/go-redis/v9" "github.com/wechatpay-apiv3/wechatpay-go/core" "github.com/wechatpay-apiv3/wechatpay-go/core/auth/verifiers" "github.com/wechatpay-apiv3/wechatpay-go/core/downloader" "github.com/wechatpay-apiv3/wechatpay-go/core/notify" "github.com/wechatpay-apiv3/wechatpay-go/core/option" "github.com/wechatpay-apiv3/wechatpay-go/services/payments/native" wx "github.com/wechatpay-apiv3/wechatpay-go/utils" "log" "os" ) type routeFn func(*gin.Engine) type createTableFn func() error type App struct { router *gin.Engine address string } func lostHandler(ctx *gin.Context) { ctx.JSON(utils.HttpNotFound, utils.Fail("api/resource not found")) } func loggerFormat(timeFmt string) gin.HandlerFunc { return gin.LoggerWithFormatter(func(param gin.LogFormatterParams) string { return utils.Format("[wine] %s - %s [%s %s %s] %s, %s %d %s: %s\n", param.ClientIP, param.TimeStamp.Format(timeFmt), param.MethodColor(), param.Method, param.ResetColor(), param.Path, param.StatusCodeColor(), param.StatusCode, param.ResetColor(), param.ErrorMessage, ) }) } func CreateApp(config *utils.Config) *App { if config.Release { gin.SetMode(gin.ReleaseMode) } else { gin.SetMode(gin.DebugMode) } utils.ServerPrefix = config.ServerPrefix utils.Logger = log.New(os.Stderr, "[wine] ", log.Ldate|log.Ltime|log.Lshortfile|log.Lmsgprefix) initWxPay(config) initDatabase(config) router := createRouter(config) return &App{router: router, address: config.ServerAddr} } func (app *App) RouteRegister(routeFns ...routeFn) { for _, fn := range routeFns { fn(app.router) } } func (app *App) Start() { err := app.router.Run(app.address) if err != nil { utils.Logger.Printf("server can't run on address[%s] for: %s", app.address, err) } } func initWxPay(config *utils.Config) { utils.WxTitle, utils.WxV3Key = config.WxPayTitle, config.WxApiV3Key utils.WxAppId, utils.WxMchId = config.WxAppId, config.WxMerchantAcc utils.WxCertSeq, utils.WxCertPath = config.WxApiCertSeq, config.WxApiCertPath var err error utils.WxPrivateKey, err = wx.LoadPrivateKeyWithPath(config.WxApiCertPath) if err != nil { utils.Logger.Fatalf("load merchant private key error: %s\n", err) } opts := []core.ClientOption{ option.WithWechatPayAutoAuthCipher(config.WxMerchantAcc, config.WxApiCertSeq, utils.WxPrivateKey, config.WxApiV3Key), } utils.WxPayCli = context.Background() client, err := core.NewClient(utils.WxPayCli, opts...) if err != nil { utils.Logger.Fatalf("new wechat pay client error: %s\n", err) } utils.WxPaySrv = native.NativeApiService{Client: client} err = downloader.MgrInstance().RegisterDownloaderWithPrivateKey( utils.WxPayCli, utils.WxPrivateKey, config.WxApiCertSeq, config.WxMerchantAcc, config.WxApiV3Key, ) visitor := downloader.MgrInstance().GetCertificateVisitor(config.WxMerchantAcc) utils.WxCrtHdr, err = notify.NewRSANotifyHandler(config.WxApiV3Key, verifiers.NewSHA256WithRSAVerifier(visitor)) if err != nil { utils.Logger.Fatalf("wxpay notice handler init error: %s\n", err) } } func initDatabase(config *utils.Config) { // init utils.Redis = redis.NewClient(&redis.Options{ Addr: utils.Format("%s:%d", config.RedisHost, config.RedisPort), Password: config.RedisPass, DB: config.RedisDatabase, }) db, err := sql.Open( "mysql", utils.Format( "%s:%s@tcp(%s:%d)/%s?loc=Local&charset=utf8&parseTime=true", config.MysqlUser, config.MysqlPass, config.MysqlHost, config.MysqlPort, config.MysqlDatabase, ), ) if err != nil { utils.Logger.Fatalf("mysql init failed: %s\n", err) } err = db.Ping() if err != nil { utils.Logger.Fatalf("mysql connection failed: %s\n", err) } utils.Mysql = db // create toCreate := []createTableFn{ tables.CreateAdvertiseTable, tables.CreateDeviceTable, tables.CreateManagerTable, tables.CreateParamsTable, tables.CreateVersionTable, tables.CreateWineTable, tables.CreateTradeTable, tables.CreateWorkerTable, tables.CreateUserTable, tables.CreateRefundTable, tables.CreateOperationTable, tables.CreateWarnTable, tables.CreateChangeTable, } for _, Fn := range toCreate { err = Fn() if err != nil { utils.Logger.Fatalf("create mysql tables failed: %s\n", err) } } } func createRouter(config *utils.Config) *gin.Engine { router := gin.New() err := router.SetTrustedProxies([]string{"127.0.0.1"}) if err != nil { utils.Logger.Fatalf("can't trust '127.0.0.1', for: %s", err) } if err = utils.LoadRsaKeyPairs(config); err != nil { utils.Logger.Fatalf("can't generate private and public key.") } conf := cors.DefaultConfig() allow := append(conf.AllowHeaders, "Token", "Device") conf.AllowAllOrigins, conf.AllowHeaders = true, allow router.Use(loggerFormat(config.TimeFormat), gin.Recovery(), utils.ErrorHandler, cors.New(conf)) router.NoRoute(lostHandler) router.Static("/static", "./static") return router }