init
This commit is contained in:
43
pkg/payment/alipay.go
Normal file
43
pkg/payment/alipay.go
Normal file
@ -0,0 +1,43 @@
|
||||
package payment
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
|
||||
model "github.com/cloudreve/Cloudreve/v3/models"
|
||||
"github.com/cloudreve/Cloudreve/v3/pkg/serializer"
|
||||
alipay "github.com/smartwalle/alipay/v3"
|
||||
)
|
||||
|
||||
// Alipay 支付宝当面付支付处理
|
||||
type Alipay struct {
|
||||
Client *alipay.Client
|
||||
}
|
||||
|
||||
// Create 创建订单
|
||||
func (pay *Alipay) Create(order *model.Order, pack *serializer.PackProduct, group *serializer.GroupProducts, user *model.User) (*OrderCreateRes, error) {
|
||||
gateway, _ := url.Parse("/api/v3/callback/alipay")
|
||||
var p = alipay.TradePreCreate{
|
||||
Trade: alipay.Trade{
|
||||
NotifyURL: model.GetSiteURL().ResolveReference(gateway).String(),
|
||||
Subject: order.Name,
|
||||
OutTradeNo: order.OrderNo,
|
||||
TotalAmount: fmt.Sprintf("%.2f", float64(order.Price*order.Num)/100),
|
||||
},
|
||||
}
|
||||
|
||||
if _, err := order.Create(); err != nil {
|
||||
return nil, ErrInsertOrder.WithError(err)
|
||||
}
|
||||
|
||||
res, err := pay.Client.TradePreCreate(p)
|
||||
if err != nil {
|
||||
return nil, ErrIssueOrder.WithError(err)
|
||||
}
|
||||
|
||||
return &OrderCreateRes{
|
||||
Payment: true,
|
||||
QRCode: res.QRCode,
|
||||
ID: order.OrderNo,
|
||||
}, nil
|
||||
}
|
93
pkg/payment/custom.go
Normal file
93
pkg/payment/custom.go
Normal file
@ -0,0 +1,93 @@
|
||||
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
|
||||
}
|
171
pkg/payment/order.go
Normal file
171
pkg/payment/order.go
Normal file
@ -0,0 +1,171 @@
|
||||
package payment
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
model "github.com/cloudreve/Cloudreve/v3/models"
|
||||
"github.com/cloudreve/Cloudreve/v3/pkg/serializer"
|
||||
"github.com/iGoogle-ink/gopay/wechat/v3"
|
||||
"github.com/qingwg/payjs"
|
||||
"github.com/smartwalle/alipay/v3"
|
||||
"math/rand"
|
||||
"net/url"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrUnknownPaymentMethod 未知支付方式
|
||||
ErrUnknownPaymentMethod = serializer.NewError(serializer.CodeInternalSetting, "Unknown payment method", nil)
|
||||
// ErrUnsupportedPaymentMethod 未知支付方式
|
||||
ErrUnsupportedPaymentMethod = serializer.NewError(serializer.CodeInternalSetting, "This order cannot be paid with this method", nil)
|
||||
// ErrInsertOrder 无法插入订单记录
|
||||
ErrInsertOrder = serializer.NewError(serializer.CodeDBError, "Failed to insert order record", nil)
|
||||
// ErrScoreNotEnough 积分不足
|
||||
ErrScoreNotEnough = serializer.NewError(serializer.CodeInsufficientCredit, "", nil)
|
||||
// ErrCreateStoragePack 无法创建容量包
|
||||
ErrCreateStoragePack = serializer.NewError(serializer.CodeDBError, "Failed to create storage pack record", nil)
|
||||
// ErrGroupConflict 用户组冲突
|
||||
ErrGroupConflict = serializer.NewError(serializer.CodeGroupConflict, "", nil)
|
||||
// ErrGroupInvalid 用户组冲突
|
||||
ErrGroupInvalid = serializer.NewError(serializer.CodeGroupInvalid, "", nil)
|
||||
// ErrAdminFulfillGroup 管理员无法购买用户组
|
||||
ErrAdminFulfillGroup = serializer.NewError(serializer.CodeFulfillAdminGroup, "", nil)
|
||||
// ErrUpgradeGroup 用户组冲突
|
||||
ErrUpgradeGroup = serializer.NewError(serializer.CodeDBError, "Failed to update user's group", nil)
|
||||
// ErrUInitPayment 无法初始化支付实例
|
||||
ErrUInitPayment = serializer.NewError(serializer.CodeInternalSetting, "Failed to initialize payment client", nil)
|
||||
// ErrIssueOrder 订单接口请求失败
|
||||
ErrIssueOrder = serializer.NewError(serializer.CodeInternalSetting, "Failed to create order", nil)
|
||||
// ErrOrderNotFound 订单不存在
|
||||
ErrOrderNotFound = serializer.NewError(serializer.CodeNotFound, "", nil)
|
||||
)
|
||||
|
||||
// Pay 支付处理接口
|
||||
type Pay interface {
|
||||
Create(order *model.Order, pack *serializer.PackProduct, group *serializer.GroupProducts, user *model.User) (*OrderCreateRes, error)
|
||||
}
|
||||
|
||||
// OrderCreateRes 订单创建结果
|
||||
type OrderCreateRes struct {
|
||||
Payment bool `json:"payment"` // 是否需要支付
|
||||
ID string `json:"id,omitempty"` // 订单号
|
||||
QRCode string `json:"qr_code,omitempty"` // 支付二维码指向的地址
|
||||
}
|
||||
|
||||
// NewPaymentInstance 获取新的支付实例
|
||||
func NewPaymentInstance(method string) (Pay, error) {
|
||||
switch method {
|
||||
case "score":
|
||||
return &ScorePayment{}, nil
|
||||
case "alipay":
|
||||
options := model.GetSettingByNames("alipay_enabled", "appid", "appkey", "shopid")
|
||||
if options["alipay_enabled"] != "1" {
|
||||
return nil, ErrUnknownPaymentMethod
|
||||
}
|
||||
|
||||
// 初始化支付宝客户端
|
||||
var client, err = alipay.New(options["appid"], options["appkey"], true)
|
||||
if err != nil {
|
||||
return nil, ErrUInitPayment.WithError(err)
|
||||
}
|
||||
|
||||
// 加载支付宝公钥
|
||||
err = client.LoadAliPayPublicKey(options["shopid"])
|
||||
if err != nil {
|
||||
return nil, ErrUInitPayment.WithError(err)
|
||||
}
|
||||
|
||||
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
|
||||
case "wechat":
|
||||
options := model.GetSettingByNames("wechat_enabled", "wechat_appid", "wechat_mchid", "wechat_serial_no", "wechat_api_key", "wechat_pk_content")
|
||||
if options["wechat_enabled"] != "1" {
|
||||
return nil, ErrUnknownPaymentMethod
|
||||
}
|
||||
client, err := wechat.NewClientV3(options["wechat_appid"], options["wechat_mchid"], options["wechat_serial_no"], options["wechat_api_key"], options["wechat_pk_content"])
|
||||
if err != nil {
|
||||
return nil, ErrUInitPayment.WithError(err)
|
||||
}
|
||||
|
||||
return &Wechat{Client: client, ApiV3Key: options["wechat_api_key"]}, nil
|
||||
case "custom":
|
||||
options := model.GetSettingByNames("custom_payment_enabled", "custom_payment_endpoint", "custom_payment_secret")
|
||||
if !model.IsTrueVal(options["custom_payment_enabled"]) {
|
||||
return nil, ErrUnknownPaymentMethod
|
||||
}
|
||||
|
||||
return newCustomClient(options["custom_payment_endpoint"], options["custom_payment_secret"]), nil
|
||||
default:
|
||||
return nil, ErrUnknownPaymentMethod
|
||||
}
|
||||
}
|
||||
|
||||
// NewOrder 创建新订单
|
||||
func NewOrder(pack *serializer.PackProduct, group *serializer.GroupProducts, num int, method string, user *model.User) (*OrderCreateRes, error) {
|
||||
// 获取支付实例
|
||||
pay, err := NewPaymentInstance(method)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var (
|
||||
orderType int
|
||||
productID int64
|
||||
title string
|
||||
price int
|
||||
)
|
||||
if pack != nil {
|
||||
orderType = model.PackOrderType
|
||||
productID = pack.ID
|
||||
title = pack.Name
|
||||
price = pack.Price
|
||||
} else if group != nil {
|
||||
if err := checkGroupUpgrade(user, group); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
orderType = model.GroupOrderType
|
||||
productID = group.ID
|
||||
title = group.Name
|
||||
price = group.Price
|
||||
} else {
|
||||
orderType = model.ScoreOrderType
|
||||
productID = 0
|
||||
title = fmt.Sprintf("%d 积分", num)
|
||||
price = model.GetIntSetting("score_price", 1)
|
||||
}
|
||||
|
||||
// 创建订单记录
|
||||
order := &model.Order{
|
||||
UserID: user.ID,
|
||||
OrderNo: orderID(),
|
||||
Type: orderType,
|
||||
Method: method,
|
||||
ProductID: productID,
|
||||
Num: num,
|
||||
Name: fmt.Sprintf("%s - %s", model.GetSettingByName("siteName"), title),
|
||||
Price: price,
|
||||
Status: model.OrderUnpaid,
|
||||
}
|
||||
|
||||
return pay.Create(order, pack, group, user)
|
||||
}
|
||||
|
||||
func orderID() string {
|
||||
return fmt.Sprintf("%s%d",
|
||||
time.Now().Format("20060102150405"),
|
||||
100000+rand.Intn(900000),
|
||||
)
|
||||
}
|
31
pkg/payment/payjs.go
Normal file
31
pkg/payment/payjs.go
Normal file
@ -0,0 +1,31 @@
|
||||
package payment
|
||||
|
||||
import (
|
||||
model "github.com/cloudreve/Cloudreve/v3/models"
|
||||
"github.com/cloudreve/Cloudreve/v3/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
|
||||
}
|
137
pkg/payment/purchase.go
Normal file
137
pkg/payment/purchase.go
Normal file
@ -0,0 +1,137 @@
|
||||
package payment
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
model "github.com/cloudreve/Cloudreve/v3/models"
|
||||
"github.com/cloudreve/Cloudreve/v3/pkg/cache"
|
||||
"github.com/cloudreve/Cloudreve/v3/pkg/serializer"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
// GivePack 创建容量包
|
||||
func GivePack(user *model.User, packInfo *serializer.PackProduct, num int) error {
|
||||
timeNow := time.Now()
|
||||
expires := timeNow.Add(time.Duration(packInfo.Time*int64(num)) * time.Second)
|
||||
pack := model.StoragePack{
|
||||
Name: packInfo.Name,
|
||||
UserID: user.ID,
|
||||
ActiveTime: &timeNow,
|
||||
ExpiredTime: &expires,
|
||||
Size: packInfo.Size,
|
||||
}
|
||||
if _, err := pack.Create(); err != nil {
|
||||
return ErrCreateStoragePack.WithError(err)
|
||||
}
|
||||
cache.Deletes([]string{strconv.FormatUint(uint64(user.ID), 10)}, "pack_size_")
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkGroupUpgrade(user *model.User, groupInfo *serializer.GroupProducts) error {
|
||||
if user.Group.ID == 1 {
|
||||
return ErrAdminFulfillGroup
|
||||
}
|
||||
|
||||
// 检查用户是否已有未过期用户
|
||||
if user.PreviousGroupID != 0 && user.GroupID != groupInfo.GroupID {
|
||||
return ErrGroupConflict
|
||||
}
|
||||
|
||||
// 用户组不能相同
|
||||
if user.GroupID == groupInfo.GroupID && user.PreviousGroupID == 0 {
|
||||
return ErrGroupInvalid
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GiveGroup 升级用户组
|
||||
func GiveGroup(user *model.User, groupInfo *serializer.GroupProducts, num int) error {
|
||||
if err := checkGroupUpgrade(user, groupInfo); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
timeNow := time.Now()
|
||||
expires := timeNow.Add(time.Duration(groupInfo.Time*int64(num)) * time.Second)
|
||||
if user.PreviousGroupID != 0 {
|
||||
expires = user.GroupExpires.Add(time.Duration(groupInfo.Time*int64(num)) * time.Second)
|
||||
}
|
||||
|
||||
if err := user.UpgradeGroup(groupInfo.GroupID, &expires); err != nil {
|
||||
return ErrUpgradeGroup.WithError(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GiveScore 积分充值
|
||||
func GiveScore(user *model.User, num int) error {
|
||||
user.AddScore(num)
|
||||
return nil
|
||||
}
|
||||
|
||||
// GiveProduct “发货”
|
||||
func GiveProduct(user *model.User, pack *serializer.PackProduct, group *serializer.GroupProducts, num int) error {
|
||||
if pack != nil {
|
||||
return GivePack(user, pack, num)
|
||||
} else if group != nil {
|
||||
return GiveGroup(user, group, num)
|
||||
} else {
|
||||
return GiveScore(user, num)
|
||||
}
|
||||
}
|
||||
|
||||
// OrderPaid 订单已支付处理
|
||||
func OrderPaid(orderNo string) error {
|
||||
order, err := model.GetOrderByNo(orderNo)
|
||||
if err != nil || order.Status == model.OrderPaid {
|
||||
return ErrOrderNotFound.WithError(err)
|
||||
}
|
||||
|
||||
// 更新订单状态为 已支付
|
||||
order.UpdateStatus(model.OrderPaid)
|
||||
|
||||
user, err := model.GetActiveUserByID(order.UserID)
|
||||
if err != nil {
|
||||
return serializer.NewError(serializer.CodeUserNotFound, "", err)
|
||||
}
|
||||
|
||||
// 查询商品
|
||||
options := model.GetSettingByNames("pack_data", "group_sell_data")
|
||||
|
||||
var (
|
||||
packs []serializer.PackProduct
|
||||
groups []serializer.GroupProducts
|
||||
)
|
||||
if err := json.Unmarshal([]byte(options["pack_data"]), &packs); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := json.Unmarshal([]byte(options["group_sell_data"]), &groups); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 查找要购买的商品
|
||||
var (
|
||||
pack *serializer.PackProduct
|
||||
group *serializer.GroupProducts
|
||||
)
|
||||
if order.Type == model.GroupOrderType {
|
||||
for _, v := range groups {
|
||||
if v.ID == order.ProductID {
|
||||
group = &v
|
||||
break
|
||||
}
|
||||
}
|
||||
} else if order.Type == model.PackOrderType {
|
||||
for _, v := range packs {
|
||||
if v.ID == order.ProductID {
|
||||
pack = &v
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// "发货"
|
||||
return GiveProduct(&user, pack, group, order.Num)
|
||||
|
||||
}
|
45
pkg/payment/score.go
Normal file
45
pkg/payment/score.go
Normal file
@ -0,0 +1,45 @@
|
||||
package payment
|
||||
|
||||
import (
|
||||
model "github.com/cloudreve/Cloudreve/v3/models"
|
||||
"github.com/cloudreve/Cloudreve/v3/pkg/serializer"
|
||||
)
|
||||
|
||||
// ScorePayment 积分支付处理
|
||||
type ScorePayment struct {
|
||||
}
|
||||
|
||||
// Create 创建新订单
|
||||
func (pay *ScorePayment) Create(order *model.Order, pack *serializer.PackProduct, group *serializer.GroupProducts, user *model.User) (*OrderCreateRes, error) {
|
||||
if pack != nil {
|
||||
order.Price = pack.Score
|
||||
} else {
|
||||
order.Price = group.Score
|
||||
}
|
||||
|
||||
// 检查此订单是否可用积分支付
|
||||
if order.Price == 0 {
|
||||
return nil, ErrUnsupportedPaymentMethod
|
||||
}
|
||||
|
||||
// 扣除用户积分
|
||||
if !user.PayScore(order.Price * order.Num) {
|
||||
return nil, ErrScoreNotEnough
|
||||
}
|
||||
|
||||
// 商品“发货”
|
||||
if err := GiveProduct(user, pack, group, order.Num); err != nil {
|
||||
user.AddScore(order.Price * order.Num)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 创建订单记录
|
||||
order.Status = model.OrderPaid
|
||||
if _, err := order.Create(); err != nil {
|
||||
return nil, ErrInsertOrder.WithError(err)
|
||||
}
|
||||
|
||||
return &OrderCreateRes{
|
||||
Payment: false,
|
||||
}, nil
|
||||
}
|
88
pkg/payment/wechat.go
Normal file
88
pkg/payment/wechat.go
Normal file
@ -0,0 +1,88 @@
|
||||
package payment
|
||||
|
||||
import (
|
||||
"errors"
|
||||
model "github.com/cloudreve/Cloudreve/v3/models"
|
||||
"github.com/cloudreve/Cloudreve/v3/pkg/cache"
|
||||
"github.com/cloudreve/Cloudreve/v3/pkg/serializer"
|
||||
"github.com/cloudreve/Cloudreve/v3/pkg/util"
|
||||
"github.com/iGoogle-ink/gopay"
|
||||
"github.com/iGoogle-ink/gopay/wechat/v3"
|
||||
"net/url"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Wechat 微信扫码支付接口
|
||||
type Wechat struct {
|
||||
Client *wechat.ClientV3
|
||||
ApiV3Key string
|
||||
}
|
||||
|
||||
// Create 创建订单
|
||||
func (pay *Wechat) Create(order *model.Order, pack *serializer.PackProduct, group *serializer.GroupProducts, user *model.User) (*OrderCreateRes, error) {
|
||||
gateway, _ := url.Parse("/api/v3/callback/wechat")
|
||||
bm := make(gopay.BodyMap)
|
||||
bm.
|
||||
Set("description", order.Name).
|
||||
Set("out_trade_no", order.OrderNo).
|
||||
Set("notify_url", model.GetSiteURL().ResolveReference(gateway).String()).
|
||||
SetBodyMap("amount", func(bm gopay.BodyMap) {
|
||||
bm.Set("total", int64(order.Price*order.Num)).
|
||||
Set("currency", "CNY")
|
||||
})
|
||||
|
||||
wxRsp, err := pay.Client.V3TransactionNative(bm)
|
||||
if err != nil {
|
||||
return nil, ErrIssueOrder.WithError(err)
|
||||
}
|
||||
|
||||
if wxRsp.Code == wechat.Success {
|
||||
if _, err := order.Create(); err != nil {
|
||||
return nil, ErrInsertOrder.WithError(err)
|
||||
}
|
||||
|
||||
return &OrderCreateRes{
|
||||
Payment: true,
|
||||
QRCode: wxRsp.Response.CodeUrl,
|
||||
ID: order.OrderNo,
|
||||
}, nil
|
||||
}
|
||||
|
||||
return nil, ErrIssueOrder.WithError(errors.New(wxRsp.Error))
|
||||
}
|
||||
|
||||
// GetPlatformCert 获取微信平台证书
|
||||
func (pay *Wechat) GetPlatformCert() string {
|
||||
if cert, ok := cache.Get("wechat_platform_cert"); ok {
|
||||
return cert.(string)
|
||||
}
|
||||
|
||||
res, err := pay.Client.GetPlatformCerts()
|
||||
if err == nil {
|
||||
// 使用反馈证书中启用时间较晚的
|
||||
var (
|
||||
currentLatest *time.Time
|
||||
currentCert string
|
||||
)
|
||||
for _, cert := range res.Certs {
|
||||
effectiveTime, err := time.Parse("2006-01-02T15:04:05-0700", cert.EffectiveTime)
|
||||
if err != nil {
|
||||
if currentLatest == nil {
|
||||
currentLatest = &effectiveTime
|
||||
currentCert = cert.PublicKey
|
||||
continue
|
||||
}
|
||||
if currentLatest.Before(effectiveTime) {
|
||||
currentLatest = &effectiveTime
|
||||
currentCert = cert.PublicKey
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cache.Set("wechat_platform_cert", currentCert, 3600*10)
|
||||
return currentCert
|
||||
}
|
||||
|
||||
util.Log().Debug("Failed to get Wechat Pay platform certificate: %s", err)
|
||||
return ""
|
||||
}
|
Reference in New Issue
Block a user