diff --git a/go.mod b/go.mod index e4d37d4..d52069d 100644 --- a/go.mod +++ b/go.mod @@ -25,6 +25,7 @@ require ( github.com/mojocn/base64Captcha v0.0.0-20190801020520-752b1cd608b2 github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 github.com/pkg/errors v0.8.0 + github.com/qingwg/payjs v0.0.0-20190928033402-c53dbe16b371 github.com/qiniu/api.v7/v7 v7.4.0 github.com/rafaeljusto/redigomock v0.0.0-20191117212112-00b2509252a1 github.com/robfig/cron/v3 v3.0.1 diff --git a/go.sum b/go.sum index 473cc3a..8d56499 100644 --- a/go.sum +++ b/go.sum @@ -160,6 +160,8 @@ github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1: github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/qingwg/payjs v0.0.0-20190928033402-c53dbe16b371 h1:8VWtyY2IwjEQZSNT4Kyyct9zv9hoegD5GQhFr+TMdCI= +github.com/qingwg/payjs v0.0.0-20190928033402-c53dbe16b371/go.mod h1:9UFrQveqNm3ELF6HSvMtDR3KYpJ7Ib9s0WVmYhaUBlU= github.com/qiniu/api.v7/v7 v7.4.0 h1:9dZMVQifh31QGFLVaHls6akCaS2rlj3du8MnEFd7XjQ= github.com/qiniu/api.v7/v7 v7.4.0/go.mod h1:VE5oC5rkE1xul0u1S2N0b2Uxq9/6hZzhyqjgK25XDcM= github.com/quasoft/memstore v0.0.0-20180925164028-84a050167438 h1:jnz/4VenymvySjE+Ez511s0pqVzkUOmr1fwCVytNNWk= diff --git a/models/migration.go b/models/migration.go index 391d78a..55b358b 100644 --- a/models/migration.go +++ b/models/migration.go @@ -140,6 +140,8 @@ Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; verti {Name: "database_version", Value: `6`, Type: "version"}, {Name: "alipay_enabled", Value: `0`, Type: "payment"}, {Name: "payjs_enabled", Value: `0`, Type: "payment"}, + {Name: "payjs_id", Value: ``, Type: "payment"}, + {Name: "payjs_secret", Value: ``, Type: "payment"}, {Name: "appid", Value: ``, Type: "payment"}, {Name: "appkey", Value: ``, Type: "payment"}, {Name: "shopid", Value: ``, Type: "payment"}, diff --git a/pkg/payment/order.go b/pkg/payment/order.go index 426e9ba..37daff2 100644 --- a/pkg/payment/order.go +++ b/pkg/payment/order.go @@ -4,8 +4,10 @@ import ( "fmt" model "github.com/HFO4/cloudreve/models" "github.com/HFO4/cloudreve/pkg/serializer" + "github.com/qingwg/payjs" "github.com/smartwalle/alipay/v3" "math/rand" + "net/url" "time" ) @@ -41,10 +43,9 @@ type Pay interface { // OrderCreateRes 订单创建结果 type OrderCreateRes struct { - Payment bool `json:"payment"` // 是否需要支付 - ID string `json:"id,omitempty"` // 订单号 - QRCode string `json:"qr_code,omitempty"` // 支付二维码指向的地址 - Redirect string `json:"redirect,omitempty"` // 支付跳转连接,和二维码二选一,不需要的留空 + Payment bool `json:"payment"` // 是否需要支付 + ID string `json:"id,omitempty"` // 订单号 + QRCode string `json:"qr_code,omitempty"` // 支付二维码指向的地址 } // NewPaymentInstance 获取新的支付实例 @@ -71,6 +72,21 @@ func NewPaymentInstance(method string) (Pay, error) { } return &Alipay{Client: client}, nil + case "payjs": + options := model.GetSettingByNames("payjs_enabled", "payjs_secret", "payjs_id") + if options["payjs_enabled"] != "1" { + return nil, ErrUnknownPaymentMethod + } + + callback, _ := url.Parse("/api/v3/callback/payjs") + payjsConfig := &payjs.Config{ + Key: options["payjs_secret"], + MchID: options["payjs_id"], + NotifyUrl: model.GetSiteURL().ResolveReference(callback).String(), + } + + return &PayJSClient{Client: payjs.New(payjsConfig)}, nil + default: return nil, ErrUnknownPaymentMethod } @@ -124,8 +140,8 @@ func NewOrder(pack *serializer.PackProduct, group *serializer.GroupProducts, num } func orderID() string { - return fmt.Sprintf("%s%d%d", + return fmt.Sprintf("%s%d", time.Now().Format("20060102150405"), - 10000+rand.Intn(90000), - time.Now().UnixNano()) + 100000+rand.Intn(900000), + ) } diff --git a/pkg/payment/payjs.go b/pkg/payment/payjs.go new file mode 100644 index 0000000..ae77764 --- /dev/null +++ b/pkg/payment/payjs.go @@ -0,0 +1,31 @@ +package payment + +import ( + model "github.com/HFO4/cloudreve/models" + "github.com/HFO4/cloudreve/pkg/serializer" + "github.com/qingwg/payjs" +) + +// PayJSClient PayJS支付处理 +type PayJSClient struct { + Client *payjs.PayJS +} + +// Create 创建订单 +func (pay *PayJSClient) Create(order *model.Order, pack *serializer.PackProduct, group *serializer.GroupProducts, user *model.User) (*OrderCreateRes, error) { + if _, err := order.Create(); err != nil { + return nil, ErrInsertOrder.WithError(err) + } + + PayNative := pay.Client.GetNative() + res, err := PayNative.Create(int64(order.Price*order.Num), order.Name, order.OrderNo, "", "") + if err != nil { + return nil, ErrIssueOrder.WithError(err) + } + + return &OrderCreateRes{ + Payment: true, + QRCode: res.CodeUrl, + ID: order.OrderNo, + }, nil +} diff --git a/routers/controllers/vas.go b/routers/controllers/vas.go index 3bb3c6a..7f85374 100644 --- a/routers/controllers/vas.go +++ b/routers/controllers/vas.go @@ -5,6 +5,7 @@ import ( "github.com/HFO4/cloudreve/pkg/util" "github.com/HFO4/cloudreve/service/vas" "github.com/gin-gonic/gin" + "github.com/qingwg/payjs/notify" "github.com/smartwalle/alipay/v3" ) @@ -100,3 +101,33 @@ func AlipayCallback(c *gin.Context) { // 确认收到通知消息 alipay.AckNotification(c.Writer) } + +// PayJSCallback PayJS回调 +func PayJSCallback(c *gin.Context) { + pay, err := payment.NewPaymentInstance("payjs") + if err != nil { + util.Log().Debug("[PayJS回调] 无法创建支付宝客户端, %s", err) + c.Status(400) + return + } + + payNotify := pay.(*payment.PayJSClient).Client.GetNotify(c.Request, c.Writer) + + //设置接收消息的处理方法 + payNotify.SetMessageHandler(func(msg notify.Message) { + if err := payment.OrderPaid(msg.OutTradeNo); err != nil { + util.Log().Debug("[PayJS回调] 支付处理失败, %s", err) + } + }) + + //处理消息接收以及回复 + err = payNotify.Serve() + if err != nil { + util.Log().Debug("[PayJS回调] 回调处理失败, %s", err) + return + } + + //发送回复的消息 + payNotify.SendResponseMsg() + +} diff --git a/routers/router.go b/routers/router.go index 0d78337..59817f6 100644 --- a/routers/router.go +++ b/routers/router.go @@ -127,6 +127,11 @@ func InitMasterRouter() *gin.Engine { // 回调接口 callback := v3.Group("callback") { + // PAYJS回调 + callback.POST( + "payjs", + controllers.PayJSCallback, + ) // 支付宝回调 callback.POST( "alipay",