feat: add wepay
This commit is contained in:
1
backend/app/http/orders.go
Normal file
1
backend/app/http/orders.go
Normal file
@@ -0,0 +1 @@
|
||||
package http
|
||||
@@ -1,12 +1,16 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"quyun/app/models"
|
||||
"quyun/app/requests"
|
||||
"quyun/database/schemas/public/model"
|
||||
"quyun/providers/wepay"
|
||||
|
||||
"github.com/gofiber/fiber/v3"
|
||||
"github.com/gofiber/fiber/v3/log"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type ListQuery struct {
|
||||
@@ -14,7 +18,9 @@ type ListQuery struct {
|
||||
}
|
||||
|
||||
// @provider
|
||||
type posts struct{}
|
||||
type posts struct {
|
||||
wepay *wepay.Client
|
||||
}
|
||||
|
||||
// List posts
|
||||
// @Router /posts [get]
|
||||
@@ -40,3 +46,40 @@ func (ctl *posts) Mine(ctx fiber.Ctx, pagination *requests.Pagination, query *Li
|
||||
log.Infof("Fetching posts for user with pagination: %+v and keyword: %v", pagination, query.Keyword)
|
||||
return models.Users.PostList(ctx.Context(), 1, pagination, query.Keyword)
|
||||
}
|
||||
|
||||
// Buy
|
||||
// @Router /buy/:id [get]
|
||||
// @Bind id path
|
||||
func (ctl *posts) Buy(ctx fiber.Ctx, id int64) (*string, error) {
|
||||
var userId int64 = 1
|
||||
|
||||
user, err := models.Users.GetByID(ctx.Context(), userId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, " failed to get user: %d", userId)
|
||||
}
|
||||
|
||||
post, err := models.Posts.GetByID(ctx.Context(), id)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, " failed to get post: %d", id)
|
||||
}
|
||||
|
||||
// create order
|
||||
order, err := models.Orders.Create(ctx.Context(), userId, post.ID)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "订单创建失败")
|
||||
}
|
||||
|
||||
body := ctl.wepay.
|
||||
BodyMap().
|
||||
Expire(30 * time.Minute).
|
||||
Description(post.Title).
|
||||
OutTradeNo(order.OrderNo).
|
||||
Payer(user.OpenID)
|
||||
|
||||
prePayResp, err := ctl.wepay.V3TransactionJsapi(ctx.Context(), body)
|
||||
if err != nil {
|
||||
log.Errorf("wepay.V3TransactionJsapi err: %v", err)
|
||||
return nil, errors.Wrap(err, "微信支付失败")
|
||||
}
|
||||
return &prePayResp.Response.PrepayId, nil
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
"quyun/providers/job"
|
||||
"quyun/providers/jwt"
|
||||
"quyun/providers/postgres"
|
||||
"quyun/providers/wepay"
|
||||
|
||||
"go.ipao.vip/atom"
|
||||
"go.ipao.vip/atom/container"
|
||||
@@ -31,6 +32,7 @@ import (
|
||||
func defaultProviders() container.Providers {
|
||||
return service.Default(container.Providers{
|
||||
ali.DefaultProvider(),
|
||||
wepay.DefaultProvider(),
|
||||
http.DefaultProvider(),
|
||||
postgres.DefaultProvider(),
|
||||
jwt.DefaultProvider(),
|
||||
|
||||
@@ -11,6 +11,7 @@ require (
|
||||
github.com/ThreeDotsLabs/watermill-sql/v3 v3.1.0
|
||||
github.com/aliyun/credentials-go v1.4.5
|
||||
github.com/go-jet/jet/v2 v2.13.0
|
||||
github.com/go-pay/gopay v1.5.110
|
||||
github.com/gofiber/fiber/v3 v3.0.0-beta.4
|
||||
github.com/gofiber/utils/v2 v2.0.0-beta.7
|
||||
github.com/golang-jwt/jwt/v4 v4.5.1
|
||||
@@ -82,6 +83,12 @@ require (
|
||||
github.com/go-openapi/jsonreference v0.21.0 // indirect
|
||||
github.com/go-openapi/spec v0.21.0 // indirect
|
||||
github.com/go-openapi/swag v0.23.0 // indirect
|
||||
github.com/go-pay/crypto v0.0.1 // indirect
|
||||
github.com/go-pay/errgroup v0.0.3 // indirect
|
||||
github.com/go-pay/smap v0.0.2 // indirect
|
||||
github.com/go-pay/util v0.0.4 // indirect
|
||||
github.com/go-pay/xlog v0.0.3 // indirect
|
||||
github.com/go-pay/xtime v0.0.2 // indirect
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
|
||||
github.com/gofiber/schema v1.2.0 // indirect
|
||||
|
||||
@@ -82,6 +82,20 @@ github.com/go-openapi/spec v0.21.0 h1:LTVzPc3p/RzRnkQqLRndbAzjY0d0BCL72A6j3CdL9Z
|
||||
github.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk=
|
||||
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
|
||||
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
|
||||
github.com/go-pay/crypto v0.0.1 h1:B6InT8CLfSLc6nGRVx9VMJRBBazFMjr293+jl0lLXUY=
|
||||
github.com/go-pay/crypto v0.0.1/go.mod h1:41oEIvHMKbNcYlWUlRWtsnC6+ASgh7u29z0gJXe5bes=
|
||||
github.com/go-pay/errgroup v0.0.3 h1:DB4s8e8oWYDyETKQ1y1riMJ7y29zE1uIsMCSjEOFSbU=
|
||||
github.com/go-pay/errgroup v0.0.3/go.mod h1:0+4b8mvFMS71MIzsaC+gVvB4x37I93lRb2dqrwuU8x8=
|
||||
github.com/go-pay/gopay v1.5.110 h1:K+S1goJu5235Bi94uxD0VIQXCOiC7taw5QkCBc2J7PE=
|
||||
github.com/go-pay/gopay v1.5.110/go.mod h1:v2VLAEV2NI6SIRS3Qpyi826pBwlWthHvP/ZVVTjqxwU=
|
||||
github.com/go-pay/smap v0.0.2 h1:kKflYor5T5FgZltPFBMTFfjJvqYMHr5VnIFSEyhVTcA=
|
||||
github.com/go-pay/smap v0.0.2/go.mod h1:HW9oAo0okuyDYsbpbj5fJFxnNj/BZorRGFw26SxrNWw=
|
||||
github.com/go-pay/util v0.0.4 h1:TuwSU9o3Qd7m9v1PbzFuIA/8uO9FJnA6P7neG/NwPyk=
|
||||
github.com/go-pay/util v0.0.4/go.mod h1:Tsdhs8Ib9J9b4+NKNO1PHh5hWHhlg98PthsX0ckq6PM=
|
||||
github.com/go-pay/xlog v0.0.3 h1:avyMhCL/JgBHreoGx/am/kHxfs1udDOAeVqbmzP/Yes=
|
||||
github.com/go-pay/xlog v0.0.3/go.mod h1:mH47xbobrdsSHWsmFtSF5agWbMHFP+tK0ZbVCk5OAEw=
|
||||
github.com/go-pay/xtime v0.0.2 h1:7YR4/iuELsEHpJ6LUO0SVK80hQxDO9MLCfuVYIiTCRM=
|
||||
github.com/go-pay/xtime v0.0.2/go.mod h1:W1yRbJaSt4CSBcdAtLBQ8xajiN/Pl5hquGczUcUE9xE=
|
||||
github.com/go-sql-driver/mysql v1.9.0 h1:Y0zIbQXhQKmQgTp44Y1dp3wTXcn804QoTptLZT1vtvo=
|
||||
github.com/go-sql-driver/mysql v1.9.0/go.mod h1:pDetrLJeA3oMujJuvXc8RJoasr589B6A9fwzD3QMrqw=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
||||
|
||||
28
backend/providers/wepay/config.go
Normal file
28
backend/providers/wepay/config.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package wepay
|
||||
|
||||
import (
|
||||
"go.ipao.vip/atom/container"
|
||||
"go.ipao.vip/atom/opt"
|
||||
)
|
||||
|
||||
const DefaultPrefix = "WePay"
|
||||
|
||||
func DefaultProvider() container.ProviderContainer {
|
||||
return container.ProviderContainer{
|
||||
Provider: Provide,
|
||||
Options: []opt.Option{
|
||||
opt.Prefix(DefaultPrefix),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
Debug bool
|
||||
NotifyURL string
|
||||
|
||||
AppId string
|
||||
MchId string
|
||||
SerialNo string
|
||||
APIv3Key string
|
||||
PrivateKey string
|
||||
}
|
||||
127
backend/providers/wepay/pay.go
Normal file
127
backend/providers/wepay/pay.go
Normal file
@@ -0,0 +1,127 @@
|
||||
package wepay
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/go-pay/gopay"
|
||||
"github.com/go-pay/gopay/wechat/v3"
|
||||
"go.ipao.vip/atom/container"
|
||||
"go.ipao.vip/atom/opt"
|
||||
)
|
||||
|
||||
func Provide(opts ...opt.Option) error {
|
||||
o := opt.New(opts...)
|
||||
var config Config
|
||||
if err := o.UnmarshalConfig(&config); err != nil {
|
||||
return err
|
||||
}
|
||||
return container.Container.Provide(func() (*Client, error) {
|
||||
// NewClientV3 初始化微信客户端 v3
|
||||
// mchid:商户ID 或者服务商模式的 sp_mchid
|
||||
// serialNo:商户证书的证书序列号
|
||||
// apiV3Key:apiV3Key,商户平台获取
|
||||
// privateKey:私钥 apiclient_key.pem 读取后的内容
|
||||
client, err := wechat.NewClientV3(config.MchId, config.SerialNo, config.APIv3Key, config.PrivateKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
client.DebugSwitch = gopay.DebugOff
|
||||
if config.Debug {
|
||||
client.DebugSwitch = gopay.DebugOn
|
||||
}
|
||||
|
||||
return &Client{
|
||||
payClient: client,
|
||||
config: &config,
|
||||
}, nil
|
||||
}, o.DiOptions()...)
|
||||
}
|
||||
|
||||
type Client struct {
|
||||
payClient *wechat.ClientV3
|
||||
config *Config
|
||||
}
|
||||
|
||||
func (c *Client) GetClient() *wechat.ClientV3 {
|
||||
return c.payClient
|
||||
}
|
||||
|
||||
func (c *Client) V3TransactionJsapi(ctx context.Context, bm *BodyMap) (*wechat.PrepayRsp, error) {
|
||||
resp, err := c.payClient.V3TransactionJsapi(ctx, bm.bm)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if resp.Code != wechat.Success {
|
||||
return nil, errors.New(resp.Error)
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (c *Client) BodyMap() *BodyMap { return NewBodyMap(c.config) }
|
||||
|
||||
type BodyMap struct {
|
||||
bm gopay.BodyMap
|
||||
}
|
||||
|
||||
func NewBodyMap(c *Config) *BodyMap {
|
||||
bm := make(gopay.BodyMap)
|
||||
bm.Set("sp_appid", c.AppId).
|
||||
Set("sp_mchid", c.MchId).
|
||||
Set("notify_url", c.NotifyURL).
|
||||
Set("amount", func(bm gopay.BodyMap) {
|
||||
bm.Set("total", 1).
|
||||
Set("currency", "CNY")
|
||||
})
|
||||
return &BodyMap{
|
||||
bm: bm,
|
||||
}
|
||||
}
|
||||
|
||||
func (b *BodyMap) Set(key string, value interface{}) *BodyMap {
|
||||
b.bm.Set(key, value)
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *BodyMap) SetBodyMap(key string, f func(bm gopay.BodyMap)) *BodyMap {
|
||||
b.bm.SetBodyMap(key, f)
|
||||
return b
|
||||
}
|
||||
|
||||
// Expire time
|
||||
func (b *BodyMap) Expire(t time.Duration) *BodyMap {
|
||||
return b.Set("time_expire", time.Now().Add(t).Format(time.RFC3339))
|
||||
}
|
||||
|
||||
// Description
|
||||
func (b *BodyMap) Description(desc string) *BodyMap {
|
||||
return b.Set("description", desc)
|
||||
}
|
||||
|
||||
// OutTradeNo
|
||||
func (b *BodyMap) OutTradeNo(outTradeNo string) *BodyMap {
|
||||
return b.Set("out_trade_no", outTradeNo)
|
||||
}
|
||||
|
||||
// Amount
|
||||
func (b *BodyMap) Amount(total int, currency string) *BodyMap {
|
||||
return b.SetBodyMap("amount", func(bm gopay.BodyMap) {
|
||||
bm.Set("total", total).Set("currency", currency)
|
||||
})
|
||||
}
|
||||
|
||||
// Payer
|
||||
func (b *BodyMap) Payer(spOpenId string) *BodyMap {
|
||||
return b.SetBodyMap("payer", func(bm gopay.BodyMap) {
|
||||
bm.Set("sp_openid", spOpenId)
|
||||
})
|
||||
}
|
||||
|
||||
// SubMchId
|
||||
func (b *BodyMap) SubMchId(subMchId string) *BodyMap {
|
||||
return b.Set("sub_mchid", subMchId)
|
||||
}
|
||||
Reference in New Issue
Block a user