kra-new/internal/integration/payment/native_test.go

181 lines
6.6 KiB
Go

package payment
import (
"crypto/aes"
"crypto/cipher"
"encoding/base64"
bizpayment "kra/internal/biz/payment"
"net/url"
"sort"
"strings"
"testing"
gopayWechat "github.com/go-pay/gopay/wechat"
gopayWechatV3 "github.com/go-pay/gopay/wechat/v3"
)
func wechatV2Sign(values map[string]string, key, signType string) string {
body := toGoPayBodyMap(values)
body.Remove("sign")
return gopayWechat.GetReleaseSign(key, wechatV2SignType(signType), body)
}
func decryptWechatV3(ciphertext, nonce, associatedData, key string) ([]byte, error) {
return gopayWechatV3.V3DecryptNotifyCipherTextToBytes(ciphertext, nonce, associatedData, key)
}
func alipaySignContent(values url.Values) string {
keys := make([]string, 0, len(values))
for key := range values {
if key != "sign" && values.Get(key) != "" {
keys = append(keys, key)
}
}
sort.Strings(keys)
parts := make([]string, 0, len(keys))
for _, key := range keys {
parts = append(parts, key+"="+values.Get(key))
}
return strings.Join(parts, "&")
}
func TestWechatV2SignOfficialStyle(t *testing.T) {
values := map[string]string{
"appid": "wxd930ea5d5a258f4f", "mch_id": "10000100", "device_info": "1000",
"body": "test", "nonce_str": "ibuaiVcKdpRxkhJA",
}
got := wechatV2Sign(values, "192006250b4c09247ec02edce69f6a2d", "MD5")
if got != strings.ToUpper(got) || len(got) != 32 {
t.Fatalf("invalid WeChat v2 MD5 signature %q", got)
}
values["sign"] = got
if err := verifyWechatV2(values, "192006250b4c09247ec02edce69f6a2d"); err != nil {
t.Fatal(err)
}
}
func TestApplePaymentResultSeparatesMerchantAndProviderTradeNumbers(t *testing.T) {
result, err := applePaymentResult("1000000000001", map[string]any{
"transactionId": "1000000000001",
"appAccountToken": "123e4567-e89b-12d3-a456-426614174000",
"currency": "CNY",
"price": "1000",
}, nil, map[string]any{"price_divisor": "10"})
if err != nil {
t.Fatal(err)
}
if result.TradeNo != "123e4567-e89b-12d3-a456-426614174000" || result.ProviderTradeNo != "1000000000001" || result.QueryID != "1000000000001" {
t.Fatalf("unexpected Apple order identifiers: %+v", result)
}
}
func TestApplePaymentResultTreatsZeroRevocationDateAsNotRevoked(t *testing.T) {
result, err := applePaymentResult("1000000000001", map[string]any{
"transactionId": "1000000000001",
"appAccountToken": "123e4567-e89b-12d3-a456-426614174000",
"currency": "CNY",
"price": "1000",
"revocationDate": float64(0),
}, nil, map[string]any{"price_divisor": "10"})
if err != nil || result.Status != "success" || result.Amount != 100 {
t.Fatalf("zero revocationDate result = %+v, err = %v", result, err)
}
result, err = applePaymentResult("1000000000001", map[string]any{
"transactionId": "1000000000001",
"appAccountToken": "123e4567-e89b-12d3-a456-426614174000",
"currency": "CNY",
"price": "1000",
"revocationDate": float64(1700000000000),
}, nil, map[string]any{"price_divisor": "10"})
if err != nil || result.Status != "failed" {
t.Fatalf("positive revocationDate result = %+v, err = %v", result, err)
}
}
func TestWechatV3Decrypt(t *testing.T) {
key := "01234567890123456789012345678901"
nonce := "0123456789ab"
aad := "transaction"
plain := []byte(`{"out_trade_no":"T1","trade_state":"SUCCESS"}`)
block, err := aes.NewCipher([]byte(key))
if err != nil {
t.Fatal(err)
}
gcm, err := cipher.NewGCM(block)
if err != nil {
t.Fatal(err)
}
ciphertext := base64.StdEncoding.EncodeToString(gcm.Seal(nil, []byte(nonce), plain, []byte(aad)))
got, err := decryptWechatV3(ciphertext, nonce, aad, key)
if err != nil {
t.Fatal(err)
}
if string(got) != string(plain) {
t.Fatalf("plaintext = %q", got)
}
}
func TestAlipaySignContentSortedAndExcludesSign(t *testing.T) {
values := url.Values{"method": {"alipay.trade.query"}, "app_id": {"123"}, "sign": {"ignored"}, "empty": {""}}
if got := alipaySignContent(values); got != "app_id=123&method=alipay.trade.query" {
t.Fatalf("sign content = %q", got)
}
}
func TestAlipayBreakdownWithPointPayment(t *testing.T) {
result := &bizpayment.PaymentResult{Status: "success", Amount: 1000}
err := populateAlipayBreakdown(result, map[string]any{
"total_amount": "10.00", "buyer_pay_amount": "8.00", "point_amount": "2.00", "receipt_amount": "8.00",
})
if err != nil {
t.Fatal(err)
}
if !result.AmountBreakdownKnown || result.PayerPaidAmount != 800 || result.CashPaidAmount != 600 || result.PointPaidAmount != 200 || result.DiscountAmount != 200 || result.SettlementAmount != 800 {
t.Fatalf("unexpected Alipay breakdown: %+v", result)
}
}
func TestWechatV2BreakdownWithCoupon(t *testing.T) {
result := &bizpayment.PaymentResult{Status: "success", Amount: 1000}
err := populateWechatV2Breakdown(result, map[string]string{"cash_fee": "900", "coupon_fee": "100", "settlement_total_fee": "950"})
if err != nil {
t.Fatal(err)
}
if !result.AmountBreakdownKnown || result.PayerPaidAmount != 900 || result.DiscountAmount != 100 || result.SettlementAmount != 950 {
t.Fatalf("unexpected WeChat v2 breakdown: %+v", result)
}
}
func TestWechatV3BreakdownWithPromotion(t *testing.T) {
result := &bizpayment.PaymentResult{Status: "success", Amount: 1000}
err := populateWechatV3Breakdown(result, map[string]any{
"amount": map[string]any{"payer_total": float64(800), "settlement_amount": float64(780)},
"promotion_detail": []any{map[string]any{"amount": float64(200)}},
})
if err != nil {
t.Fatal(err)
}
if !result.AmountBreakdownKnown || result.PayerPaidAmount != 800 || result.CashPaidAmount != 800 || result.DiscountAmount != 200 || result.SettlementAmount != 780 {
t.Fatalf("unexpected WeChat v3 breakdown: %+v", result)
}
}
func TestVendorBreakdownUsesConfiguredUnits(t *testing.T) {
result := &bizpayment.PaymentResult{Status: "success", Amount: 1000, Currency: "CNY"}
object := map[string]any{"data": map[string]any{
"payer": "8.00", "cash": "6.00", "points": "2.00", "discount": "2.00", "settlement": "7.80",
}}
config := map[string]any{
"query_amount_scale": "100", "query_payer_paid_amount_field": "data.payer", "query_cash_paid_amount_field": "data.cash",
"query_point_paid_amount_field": "data.points", "query_discount_amount_field": "data.discount",
"query_settlement_amount_field": "data.settlement", "query_payer_currency_field": "data.currency",
}
err := populateVendorBreakdown(result, object, config)
if err != nil {
t.Fatal(err)
}
if !result.AmountBreakdownKnown || result.PayerPaidAmount != 800 || result.CashPaidAmount != 600 || result.PointPaidAmount != 200 || result.DiscountAmount != 200 || result.SettlementAmount != 780 {
t.Fatalf("unexpected vendor breakdown: %+v", result)
}
}