* support_pay_refund

* corporate

---------

Co-authored-by: wj <3638877@qq.com>
This commit is contained in:
Jiahui
2025-07-31 17:17:44 +08:00
committed by Yun Pan
co-authored by wj
parent a13e10e8fa
commit d5cfcd2c19
9 changed files with 294 additions and 13 deletions
@@ -913,6 +913,15 @@ func (c *Cockroach) updateWithAccount(userUID uuid.UUID, isDeduction, add, isAct
return HandleUpdateResult(result, types.Account{}.TableName())
}
func (c *Cockroach) UpdateWithAccount(
userUID uuid.UUID,
isDeduction, add, isActive bool,
amount int64,
db *gorm.DB,
) error {
return c.updateWithAccount(userUID, isDeduction, add, isActive, amount, db)
}
func HandleUpdateResult(result *gorm.DB, entityName string) error {
if result.Error != nil {
return fmt.Errorf("failed to update %s: %w", entityName, result.Error)
@@ -1735,7 +1744,7 @@ func (c *Cockroach) transferAccount(from, to *types.UserQueryOpts, amount int64,
func (c *Cockroach) InitTables() error {
err := CreateTableIfNotExist(c.DB, types.Account{}, types.AccountTransaction{}, types.Payment{}, types.Transfer{}, types.Region{}, types.Invoice{},
types.InvoicePayment{}, types.Configs{}, types.Credits{}, types.CreditsTransaction{},
types.CardInfo{}, types.PaymentOrder{},
types.CardInfo{}, types.PaymentOrder{}, types.PaymentRefund{}, types.Corporate{},
types.SubscriptionPlan{}, types.Subscription{}, types.SubscriptionTransaction{},
types.AccountRegionUserTask{}, types.UserKYC{}, types.RegionConfig{}, types.Debt{}, types.DebtStatusRecord{}, types.DebtResumeDeductionBalanceTransaction{},
types.UserTimeRangeTraffic{})
@@ -1808,6 +1817,17 @@ func (c *Cockroach) InitTables() error {
}
}
}
// 增加状态列
if !c.DB.Migrator().HasColumn(&types.Payment{}, "status") {
fmt.Println("add column status to payment")
// 注意:IF NOT EXISTS 避免重复报错
sql := `ALTER TABLE "Payment" ADD COLUMN IF NOT EXISTS "status" TEXT NOT NULL DEFAULT 'PAID';`
if err := c.DB.Exec(sql).Error; err != nil {
return fmt.Errorf("failed to add payment.status column: %v", err)
}
}
return nil
}
@@ -2032,3 +2052,49 @@ func (c *Cockroach) GetEnterpriseRealNameInfoByUserID(userID string) (*types.Ent
}
return &enterpriseRealNameInfo, nil
}
func (c *Cockroach) CreateCorporate(account *types.Corporate) error {
return c.DB.Transaction(func(tx *gorm.DB) error {
if account.UserUID == "" {
return fmt.Errorf("corporate uid is empty")
}
if account.ReceiptSerialNumber == "" {
return fmt.Errorf("corporate receiptSerialNumber is empty")
}
if account.PayerName == "" {
return fmt.Errorf("corporate payerName is empty")
}
if account.PaymentAmount < 0 {
return fmt.Errorf("corporate paymentAmount is zero")
}
id, err := gonanoid.New(12)
if err != nil {
return fmt.Errorf("failed to generate payment id: %v", err)
}
account.ID = id
pay := &types.Payment{}
pay.ID = account.ID
pay.UserUID, err = c.getUserUIDByID(account.UserUID)
if err != nil {
return fmt.Errorf("failed to get user uid: %v", err)
}
pay.Method = "corporate"
pay.TradeNO = account.ReceiptSerialNumber
pay.Amount = account.PaymentAmount
pay.Gift = account.GiftAmount
pay.Status = types.PaymentStatusPAID
if err := c.DB.Create(pay).Error; err != nil {
return fmt.Errorf("failed to create payment: %w", err)
}
if err := c.DB.Create(account).Error; err != nil {
return fmt.Errorf("failed to create corporate: %w", err)
}
amount := account.PaymentAmount + account.GiftAmount
if account.PaymentAmount > 0 {
if err := c.UpdateWithAccount(pay.UserUID, false, true, false, amount, tx); err != nil {
return fmt.Errorf("failed to update corporate payment amount: %w", err)
}
}
return nil
})
}
@@ -18,6 +18,7 @@ import (
"os"
"testing"
"github.com/google/uuid"
"github.com/labring/sealos/controllers/pkg/types"
)
@@ -115,3 +116,26 @@ func TestCockroach_InitTables(t *testing.T) {
// t.Fatalf("AddDeductionBalanceWithCredits() error = %v", err)
//}
}
func TestCockroach_CreateCorporate(t *testing.T) {
os.Setenv("LOCAL_REGION", "")
ck, err := NewCockRoach("/", "")
if err != nil {
t.Errorf("NewCockRoach() error = %v", err)
return
}
defer ck.Close()
cor := &types.Corporate{
UserUID: "66EqYNUnLr",
ReceiptSerialNumber: uuid.New().String(),
PayerName: "payerName",
PaymentAmount: 1_000_000,
GiftAmount: 1_000_000,
}
err = ck.CreateCorporate(cor)
if err != nil {
t.Errorf("CreateCorporate() error = %v", err)
}
t.Logf("cor: %+v", cor)
}
+4 -3
View File
@@ -15,9 +15,10 @@
package pay
type RefundOption struct {
OrderID string `json:"order_id"`
TradeNo string `json:"trade_no"`
Amount int64 `json:"amount"`
OrderID string `json:"order_id"`
RefundID string `json:"refund_id"`
TradeNo string `json:"trade_no"`
Amount int64 `json:"amount"`
}
type Interface interface {
+11 -4
View File
@@ -19,6 +19,8 @@ import (
"fmt"
"time"
"github.com/google/uuid"
"github.com/wechatpay-apiv3/wechatpay-go/core"
"github.com/wechatpay-apiv3/wechatpay-go/services/refunddomestic"
)
@@ -74,10 +76,15 @@ func (w WechatPayment) RefundPayment(option RefundOption) (string, string, error
return "", "", fmt.Errorf("order %s has not been paid or the payment time is unknown and cannot be refunded", option.TradeNo)
}
_, paidAmount, err := w.GetPaymentDetails(option.TradeNo)
if err != nil {
return "", "", fmt.Errorf("failed to query the payment order: %v", err)
}
// generate a merchant refund number
refundNo := GetRandomString(32)
if refundNo == "" {
return "", "", fmt.Errorf("generate refundNo failed")
refundNo := uuid.NewString()
if option.RefundID != "" {
refundNo = option.RefundID
}
// Amount Unit Conversion: option. Amount is the "cent", which is the same as CreatePayment (amount/10000)
@@ -94,7 +101,7 @@ func (w WechatPayment) RefundPayment(option RefundOption) (string, string, error
OutRefundNo: core.String(refundNo),
Reason: core.String(fmt.Sprintf("refund for order %s", option.OrderID)),
Amount: &refunddomestic.AmountReq{
Total: core.Int64(refundAmt),
Total: core.Int64(paidAmount / 10000),
Refund: core.Int64(refundAmt),
Currency: core.String("CNY"),
},
+42 -3
View File
@@ -24,9 +24,10 @@ type PaymentRaw struct {
ActivityType ActivityType `gorm:"type:text;column:activityType"`
Message string `gorm:"type:text;not null"`
//TODO 初始化判断 新加字段
CardUID *uuid.UUID `gorm:"type:uuid"`
Type PaymentType `gorm:"type:text"` // 交易类型: AccountRecharge, Subscription,UpgradeSubscription...
ChargeSource ChargeSource `gorm:"type:text"`
CardUID *uuid.UUID `gorm:"type:uuid"`
Type PaymentType `gorm:"type:text"` // 交易类型: AccountRecharge, Subscription,UpgradeSubscription...
ChargeSource ChargeSource `gorm:"type:text"`
Status PaymentStatus `gorm:"type:text;column:status;not null"`
}
type ChargeSource string
@@ -45,12 +46,19 @@ type PaymentOrder struct {
}
type (
PaymentStatus string
PaymentOrderStatus string
CardPaymentStatus string
PaymentType string
)
const (
PaymentStatusPAID PaymentStatus = "PAID"
PaymentStatusRefunded PaymentStatus = "REFUNDED"
)
const (
// PaymentOrderStatusPending TODO will delete
PaymentOrderStatusPending PaymentOrderStatus = "PENDING"
PaymentOrderStatusSuccess PaymentOrderStatus = "SUCCESS"
PaymentOrderStatusFailed PaymentOrderStatus = "FAILED"
@@ -218,3 +226,34 @@ func (c *CaptureResponse) Raw() []byte {
data, _ := json.Marshal(c)
return data
}
type PaymentRefund struct {
TradeNo string `json:"tradeNo" gorm:"type:uuid;not null"`
ID string `json:"Id" gorm:"type:string;not null"` //外键 跟payment关联
Method string `json:"method" gorm:"type:varchar(255);not null"` // 退款方式
//OutTradeNo string `json:"outTradeNo" gorm:"type:uuid"`
RefundNo string `json:"refundNo" gorm:"type:string;not null"`
RefundAmount int64 `json:"refundAmount" gorm:"type:float;not null"`
DeductAmount int64 `json:"deductAmount" gorm:"type:float;not null"` // 从 account的 balance里面扣款
CreatedAt time.Time `json:"createdAt" gorm:"type:timestamp(3) with time zone;default:current_timestamp"`
RefundReason string `json:"refundReason" gorm:"type:text"`
}
func (PaymentRefund) TableName() string {
return "PaymentRefund"
}
type Corporate struct {
UserUID string `json:"userUid" gorm:"type:string;not null"`
ID string `json:"Id" gorm:"type:string;not null"`
ReceiptSerialNumber string `json:"receiptSerialNumber" gorm:"type:uuid;not null"`
PayerName string `json:"payerName" gorm:"type:varchar(255);not null"`
PaymentAmount int64 `json:"paymentAmount" gorm:"type:float;not null"`
GiftAmount int64 `json:"giftAmount" gorm:"type:float;not null"`
PayDate time.Time `json:"payDate" gorm:"type:timestamp(3) with time zone;default:current_timestamp"`
CreationDate time.Time `json:"creationDate" gorm:"type:timestamp(3) with time zone;default:current_timestamp"`
}
func (Corporate) TableName() string {
return "Corporate"
}