Files
hfb_sys/backend/internal/integrations/payment/leshua/client_test.go
T
2026-06-03 21:47:04 +08:00

219 lines
6.6 KiB
Go

package leshua
import (
"strings"
"testing"
"hfb_sys/backend/internal/config"
)
func TestSignUsesASCIISortedNonEmptyParams(t *testing.T) {
params := map[string]string{
"service": "get_tdcode",
"merchant_id": "1234567890",
"third_order_id": "NO1",
"amount": "100",
"nonce_str": "abc",
"empty": "",
"sign": "ignored",
}
got := Sign(params, "secret", SignOptions{})
want := "1E7892034AA77899E8DD609C4FA09E76"
if got != want {
t.Fatalf("Sign() = %s, want %s", got, want)
}
baseString := SignBaseString(params, SignOptions{})
wantBaseString := "amount=100&merchant_id=1234567890&nonce_str=abc&service=get_tdcode&third_order_id=NO1"
if baseString != wantBaseString {
t.Fatalf("SignBaseString() = %s, want %s", baseString, wantBaseString)
}
}
func TestVerifyNotifyIncludesEmptyAndExcludesErrorCode(t *testing.T) {
client := NewClient(config.LeshuaPaymentConfig{NotifyKey: "notify-secret"})
params := map[string]string{
"merchant_id": "1234567890",
"third_order_id": "NO1",
"leshua_order_id": "LS1",
"amount": "100",
"status": "2",
"attach": "",
"leshua": "",
"error_code": "-20001",
}
params["sign"] = Sign(params, "notify-secret", SignOptions{
IncludeEmpty: true,
ExcludeKeys: []string{"error_code", "leshua", "sign"},
})
if !client.VerifyNotify(params) {
t.Fatal("VerifyNotify() = false, want true")
}
params["amount"] = "101"
if client.VerifyNotify(params) {
t.Fatal("VerifyNotify() = true after amount changed, want false")
}
}
func TestVerifyNotifyDoesNotFallBackToSignKey(t *testing.T) {
client := NewClient(config.LeshuaPaymentConfig{
NotifyKey: "wrong-notify-secret",
SignKey: "sign-secret",
})
params := map[string]string{
"merchant_id": "1234567890",
"third_order_id": "NO1",
"leshua_order_id": "LS1",
"amount": "100",
"status": "2",
}
params["sign"] = Sign(params, "sign-secret", SignOptions{
IncludeEmpty: true,
ExcludeKeys: []string{"error_code", "sign"},
})
result := client.VerifyNotifyDetail(params)
if result.OK {
t.Fatal("VerifyNotifyDetail().OK = true, want false")
}
if _, ok := result.Expected["sign_key"]; ok {
t.Fatal("VerifyNotifyDetail() unexpectedly used sign_key fallback")
}
}
func TestVerifyNotifyUsesDocumentedNotifySignature(t *testing.T) {
client := NewClient(config.LeshuaPaymentConfig{NotifyKey: "notify-secret"})
params := map[string]string{
"merchant_id": "1234567890",
"third_order_id": "NO1",
"leshua_order_id": "LS1",
"amount": "100",
"status": "2",
"sign_type": "MD5",
}
params["sign"] = Sign(params, "notify-secret", SignOptions{
IncludeEmpty: true,
ExcludeKeys: []string{"error_code", "leshua", "sign"},
})
result := client.VerifyNotifyDetail(params)
if !result.OK {
t.Fatal("VerifyNotifyDetail().OK = false, want true")
}
if result.MatchedKey != "notify_key" {
t.Fatalf("MatchedKey = %s, want notify_key", result.MatchedKey)
}
}
func TestVerifyNotifyKeepsEmptyXMLFieldsInSignature(t *testing.T) {
client := NewClient(config.LeshuaPaymentConfig{NotifyKey: "notify-secret"})
params, err := ParsePayload([]byte(`<leshua>
<amount>100</amount>
<goods_tag></goods_tag>
<merchant_id>1234567890</merchant_id>
<sign_type>MD5</sign_type>
<status>2</status>
<third_order_id>NO1</third_order_id>
</leshua>`))
if err != nil {
t.Fatalf("ParsePayload(xml) error = %v", err)
}
params["sign"] = Sign(params, "notify-secret", SignOptions{
IncludeEmpty: true,
ExcludeKeys: []string{"error_code", "leshua", "sign"},
})
result := client.VerifyNotifyDetail(params)
if !result.OK {
t.Fatal("VerifyNotifyDetail().OK = false, want true")
}
baseString := result.BaseString[result.MatchedKey]
if !strings.Contains(baseString, "goods_tag=") {
t.Fatalf("baseString = %s, want goods_tag included", baseString)
}
}
func TestParsePayloadSupportsFormAndXML(t *testing.T) {
form, err := ParsePayload([]byte("third_order_id=NO1&status=2&amount=100"))
if err != nil {
t.Fatalf("ParsePayload(form) error = %v", err)
}
if form["third_order_id"] != "NO1" || form["status"] != "2" || form["amount"] != "100" {
t.Fatalf("ParsePayload(form) = %#v", form)
}
xml, err := ParsePayload([]byte("<xml><third_order_id>NO2</third_order_id><status>6</status><goods_tag></goods_tag><coupon/></xml>"))
if err != nil {
t.Fatalf("ParsePayload(xml) error = %v", err)
}
if xml["third_order_id"] != "NO2" || xml["status"] != "6" {
t.Fatalf("ParsePayload(xml) = %#v", xml)
}
if value, ok := xml["goods_tag"]; !ok || value != "" {
t.Fatalf("ParsePayload(xml).goods_tag = %q, exists=%v; want empty value", value, ok)
}
if value, ok := xml["coupon"]; !ok || value != "" {
t.Fatalf("ParsePayload(xml).coupon = %q, exists=%v; want empty value", value, ok)
}
}
func TestSignRefundUsesSameAlgorithm(t *testing.T) {
params := map[string]string{
"service": "unified_refund",
"merchant_id": "1234567890",
"merchant_refund_id": "REF001",
"refund_amount": "100",
"leshua_order_id": "LS1",
"nonce_str": "abc",
"sign": "ignored",
}
got := Sign(params, "secret", SignOptions{})
baseString := SignBaseString(params, SignOptions{})
wantBaseString := "leshua_order_id=LS1&merchant_id=1234567890&merchant_refund_id=REF001&nonce_str=abc&refund_amount=100&service=unified_refund"
if baseString != wantBaseString {
t.Fatalf("SignBaseString() = %s, want %s", baseString, wantBaseString)
}
want := Sign(map[string]string{
"service": "unified_refund",
"merchant_id": "1234567890",
"merchant_refund_id": "REF001",
"refund_amount": "100",
"leshua_order_id": "LS1",
"nonce_str": "abc",
}, "secret", SignOptions{})
if got != want {
t.Fatalf("Sign() = %s, want %s", got, want)
}
}
func TestVerifyRefundNotify(t *testing.T) {
client := NewClient(config.LeshuaPaymentConfig{NotifyKey: "notify-secret"})
params := map[string]string{
"merchant_id": "1234567890",
"third_order_id": "NO1",
"leshua_order_id": "LS1",
"merchant_refund_id": "REF001",
"leshua_refund_id": "LREF001",
"refund_amount": "100",
"total_amount": "200",
"status": "11",
"attach": "",
}
params["sign"] = Sign(params, "notify-secret", SignOptions{
IncludeEmpty: true,
ExcludeKeys: []string{"error_code", "leshua", "sign"},
})
if !client.VerifyNotify(params) {
t.Fatal("VerifyNotify() = false, want true for refund notify")
}
params["status"] = "12"
if client.VerifyNotify(params) {
t.Fatal("VerifyNotify() = true after status changed without re-sign, want false")
}
}