155 lines
6.1 KiB
Go
155 lines
6.1 KiB
Go
package payment
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"testing"
|
|
|
|
"kra/internal/modules/system/biz"
|
|
|
|
"github.com/go-pay/gopay"
|
|
gopayQQ "github.com/go-pay/gopay/qq"
|
|
"github.com/go-pay/gopay/saobei"
|
|
gopayWechat "github.com/go-pay/gopay/wechat"
|
|
)
|
|
|
|
func TestWechatV2RefundRejectsInvalidRequestBeforeClient(t *testing.T) {
|
|
for _, tc := range []struct {
|
|
name string
|
|
req *biz.PaymentRefundRequest
|
|
want string
|
|
}{
|
|
{name: "blank trade number", req: &biz.PaymentRefundRequest{TradeNo: " ", RefundNo: "R1", Amount: 1, TotalAmount: 1}, want: "out_trade_no"},
|
|
{name: "blank refund number", req: &biz.PaymentRefundRequest{TradeNo: "T1", RefundNo: " ", Amount: 1, TotalAmount: 1}, want: "out_refund_no"},
|
|
{name: "invalid amount", req: &biz.PaymentRefundRequest{TradeNo: "T1", RefundNo: "R1", Amount: 2, TotalAmount: 1}, want: "金额"},
|
|
} {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
_, err := (&wechatV2Adapter{}).Refund(context.Background(), tc.req, nil)
|
|
if err == nil || !strings.Contains(err.Error(), tc.want) {
|
|
t.Fatalf("Refund() error = %v, want %q", err, tc.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestWechatV2RefundIdentityValidation(t *testing.T) {
|
|
valid := func() *gopayWechat.RefundResponse {
|
|
return &gopayWechat.RefundResponse{OutTradeNo: "T1", OutRefundNo: "R1", RefundId: "WX-R1"}
|
|
}
|
|
if err := validateWechatV2RefundIdentity("T1", "R1", valid()); err != nil {
|
|
t.Fatalf("valid refund response: %v", err)
|
|
}
|
|
for _, tc := range []struct {
|
|
name string
|
|
edit func(*gopayWechat.RefundResponse)
|
|
}{
|
|
{name: "trade mismatch", edit: func(r *gopayWechat.RefundResponse) { r.OutTradeNo = "OTHER" }},
|
|
{name: "refund missing", edit: func(r *gopayWechat.RefundResponse) { r.OutRefundNo = "" }},
|
|
{name: "refund mismatch", edit: func(r *gopayWechat.RefundResponse) { r.OutRefundNo = "OTHER" }},
|
|
{name: "provider refund missing", edit: func(r *gopayWechat.RefundResponse) { r.RefundId = "" }},
|
|
} {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
rsp := valid()
|
|
tc.edit(rsp)
|
|
if err := validateWechatV2RefundIdentity("T1", "R1", rsp); err == nil {
|
|
t.Fatal("accepted invalid refund identity")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestQQRefundRejectsInvalidRequestBeforeClient(t *testing.T) {
|
|
for _, tc := range []struct {
|
|
name string
|
|
req *biz.PaymentRefundRequest
|
|
want string
|
|
}{
|
|
{name: "blank trade number", req: &biz.PaymentRefundRequest{TradeNo: " ", RefundNo: "R1", Amount: 1}, want: "out_trade_no"},
|
|
{name: "blank refund number", req: &biz.PaymentRefundRequest{TradeNo: "T1", RefundNo: " ", Amount: 1}, want: "out_refund_no"},
|
|
{name: "invalid amount", req: &biz.PaymentRefundRequest{TradeNo: "T1", RefundNo: "R1", Amount: 0}, want: "金额"},
|
|
} {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
_, err := (&qqAdapter{}).Refund(context.Background(), tc.req, nil)
|
|
if err == nil || !strings.Contains(err.Error(), tc.want) {
|
|
t.Fatalf("Refund() error = %v, want %q", err, tc.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestQQRefundIdentityValidation(t *testing.T) {
|
|
valid := func() *gopayQQ.RefundResponse {
|
|
return &gopayQQ.RefundResponse{OutTradeNo: "T1", OutRefundNo: "R1", RefundId: "QQ-R1"}
|
|
}
|
|
if err := validateQQRefundIdentity("T1", "R1", valid()); err != nil {
|
|
t.Fatalf("valid refund response: %v", err)
|
|
}
|
|
for _, tc := range []struct {
|
|
name string
|
|
edit func(*gopayQQ.RefundResponse)
|
|
}{
|
|
{name: "trade mismatch", edit: func(r *gopayQQ.RefundResponse) { r.OutTradeNo = "OTHER" }},
|
|
{name: "refund missing", edit: func(r *gopayQQ.RefundResponse) { r.OutRefundNo = "" }},
|
|
{name: "refund mismatch", edit: func(r *gopayQQ.RefundResponse) { r.OutRefundNo = "OTHER" }},
|
|
{name: "provider refund missing", edit: func(r *gopayQQ.RefundResponse) { r.RefundId = "" }},
|
|
} {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
rsp := valid()
|
|
tc.edit(rsp)
|
|
if err := validateQQRefundIdentity("T1", "R1", rsp); err == nil {
|
|
t.Fatal("accepted invalid refund identity")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestSaobeiRefundRejectsInvalidRequestBeforeClient(t *testing.T) {
|
|
for _, tc := range []struct {
|
|
name string
|
|
req *biz.PaymentRefundRequest
|
|
want string
|
|
}{
|
|
{name: "blank trade number", req: &biz.PaymentRefundRequest{TradeNo: " ", RefundNo: "R1", Amount: 1}, want: "商户订单号"},
|
|
{name: "blank refund number", req: &biz.PaymentRefundRequest{TradeNo: "T1", RefundNo: " ", Amount: 1}, want: "退款单号"},
|
|
{name: "invalid amount", req: &biz.PaymentRefundRequest{TradeNo: "T1", RefundNo: "R1", Amount: 0}, want: "金额"},
|
|
} {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
clientCreated := false
|
|
adapter := &saobeiAdapter{newClient: func(map[string]any) (saobeiClient, error) {
|
|
clientCreated = true
|
|
return &saobeiClientStub{}, nil
|
|
}}
|
|
_, err := adapter.Refund(context.Background(), tc.req, nil)
|
|
if err == nil || !strings.Contains(err.Error(), tc.want) {
|
|
t.Fatalf("Refund() error = %v, want %q", err, tc.want)
|
|
}
|
|
if clientCreated {
|
|
t.Fatal("Refund() initialized client before validating request")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestSaobeiRefundRejectsMismatchedRefundNumber(t *testing.T) {
|
|
adapter := newSaobeiTestAdapter(&saobeiClientStub{refund: func(context.Context, gopay.BodyMap) (*saobei.RefundRsp, error) {
|
|
return &saobei.RefundRsp{RspBase: saobei.RspBase{ReturnCode: "01", ResultCode: "01"}, TerminalTrace: "OTHER", OutRefundNo: "SAOBEI-REFUND-1", RefundFee: "25"}, nil
|
|
}})
|
|
_, err := adapter.Refund(context.Background(), &biz.PaymentRefundRequest{TradeNo: "T1", RefundNo: "R1", Amount: 25}, nil)
|
|
if err == nil || !strings.Contains(err.Error(), "terminal_trace") {
|
|
t.Fatalf("Refund() error = %v", err)
|
|
}
|
|
}
|
|
|
|
func TestSaobeiBarcodeCreateTreatsPayingAsPending(t *testing.T) {
|
|
adapter := newSaobeiTestAdapter(&saobeiClientStub{barcodePay: func(context.Context, gopay.BodyMap) (*saobei.BarcodePayRsp, error) {
|
|
return &saobei.BarcodePayRsp{RspBase: saobei.RspBase{ReturnCode: "01", ResultCode: saobei.ResultCodePaying}, OutTradeNo: "SAOBEI-ORDER-1"}, nil
|
|
}})
|
|
result, err := adapter.Create(context.Background(), &biz.PaymentRequest{TradeNo: "LOCAL-1", Amount: 10, Extra: map[string]any{"method": "barcode", "auth_no": "AUTH-1"}}, nil)
|
|
if err != nil {
|
|
t.Fatalf("Create() error = %v", err)
|
|
}
|
|
if result.Status != "pending" {
|
|
t.Fatalf("Create() status = %q, want pending", result.Status)
|
|
}
|
|
}
|