CloudrevePlus/pkg/payment/custom.go

94 lines
2.5 KiB
Go
Raw Normal View History

2024-02-25 08:30:34 +08:00
package payment
import (
"encoding/json"
"errors"
"fmt"
model "github.com/cloudreve/Cloudreve/v3/models"
"github.com/cloudreve/Cloudreve/v3/pkg/auth"
"github.com/cloudreve/Cloudreve/v3/pkg/cache"
"github.com/cloudreve/Cloudreve/v3/pkg/request"
"github.com/cloudreve/Cloudreve/v3/pkg/serializer"
"github.com/gofrs/uuid"
"github.com/qiniu/go-sdk/v7/sms/bytes"
"net/http"
"net/url"
)
// Custom payment client
type Custom struct {
client request.Client
endpoint string
authClient auth.Auth
}
const (
paymentTTL = 3600 * 24 // 24h
CallbackSessionPrefix = "custom_payment_callback_"
)
func newCustomClient(endpoint, secret string) *Custom {
authClient := auth.HMACAuth{
SecretKey: []byte(secret),
}
return &Custom{
endpoint: endpoint,
authClient: auth.General,
client: request.NewClient(
request.WithCredential(authClient, paymentTTL),
request.WithMasterMeta(),
),
}
}
// Request body from Cloudreve to create a new payment
type NewCustomOrderRequest struct {
Name string `json:"name"` // Order name
OrderNo string `json:"order_no"` // Order number
NotifyURL string `json:"notify_url"` // Payment callback url
Amount int64 `json:"amount"` // Order total amount
}
// Create a new payment
func (pay *Custom) Create(order *model.Order, pack *serializer.PackProduct, group *serializer.GroupProducts, user *model.User) (*OrderCreateRes, error) {
callbackID := uuid.Must(uuid.NewV4())
gateway, _ := url.Parse(fmt.Sprintf("/api/v3/callback/custom/%s/%s", order.OrderNo, callbackID))
callback, err := auth.SignURI(pay.authClient, model.GetSiteURL().ResolveReference(gateway).String(), paymentTTL)
if err != nil {
return nil, fmt.Errorf("failed to sign callback url: %w", err)
}
cache.Set(CallbackSessionPrefix+callbackID.String(), order.OrderNo, paymentTTL)
body := &NewCustomOrderRequest{
Name: order.Name,
OrderNo: order.OrderNo,
NotifyURL: callback.String(),
Amount: int64(order.Price * order.Num),
}
bodyJson, err := json.Marshal(body)
if err != nil {
return nil, fmt.Errorf("failed to encode body: %w", err)
}
res, err := pay.client.Request("POST", pay.endpoint, bytes.NewReader(bodyJson)).
CheckHTTPResponse(http.StatusOK).DecodeResponse()
if err != nil {
return nil, fmt.Errorf("failed to request payment gateway: %w", err)
}
if res.Code != 0 {
return nil, errors.New(res.Error)
}
if _, err := order.Create(); err != nil {
return nil, ErrInsertOrder.WithError(err)
}
return &OrderCreateRes{
Payment: true,
QRCode: res.Data.(string),
ID: order.OrderNo,
}, nil
}