200 lines
7.7 KiB
Go
200 lines
7.7 KiB
Go
package hb
|
||
|
||
import (
|
||
"context"
|
||
"fmt"
|
||
"log"
|
||
"testing"
|
||
)
|
||
|
||
// ExampleClient_Refund_MD5 使用MD5签名的退款示例
|
||
func ExampleClient_Refund_MD5() {
|
||
// 创建客户端(使用MD5签名)
|
||
client, err := NewClient(
|
||
"888009941110054",
|
||
WithSignKey("your-64-digit-sign-key-here"),
|
||
WithBaseURL("https://uatipos.10086.cn"), // 测试环境
|
||
)
|
||
if err != nil {
|
||
log.Fatalf("创建客户端失败: %v", err)
|
||
}
|
||
|
||
// 构建退款请求
|
||
req := &RefundRequest{
|
||
RequestId: "20211105053044",
|
||
SignType: SignTypeMD5,
|
||
OrderId: "20211105045315",
|
||
Amount: "1",
|
||
}
|
||
|
||
// 发送退款请求
|
||
resp, err := client.Refund(context.Background(), req)
|
||
if err != nil {
|
||
log.Fatalf("退款请求失败: %v", err)
|
||
}
|
||
|
||
fmt.Printf("返回码: %s\n", resp.ReturnCode)
|
||
fmt.Printf("退款结果: %s\n", resp.Status)
|
||
fmt.Printf("流水号: %s\n", resp.PayNo)
|
||
|
||
// 判断退款请求是否成功
|
||
if IsRefundRequestSuccess(resp.ReturnCode) {
|
||
fmt.Println("退款请求提交成功")
|
||
}
|
||
|
||
// 判断退款最终结果
|
||
if IsRefundSuccess(resp.Status) {
|
||
fmt.Println("退款最终成功")
|
||
}
|
||
|
||
// Output:
|
||
// 返回码: 000000
|
||
// 退款结果: SUCCESS
|
||
// 流水号: 205206151912517707
|
||
// 退款请求提交成功
|
||
// 退款最终成功
|
||
}
|
||
|
||
// ExampleClient_Refund_RSA 使用RSA签名的退款示例
|
||
func ExampleClient_Refund_RSA() {
|
||
// 商户RSA私钥(PEM格式)
|
||
privateKey := `-----BEGIN PRIVATE KEY-----
|
||
MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAJ...(你的私钥)
|
||
-----END PRIVATE KEY-----`
|
||
|
||
// 创建客户端(使用RSA签名)
|
||
client, err := NewClient(
|
||
"888009941110054",
|
||
WithPrivateKey(privateKey),
|
||
WithBaseURL("https://ipos.10086.cn"), // 生产环境
|
||
)
|
||
if err != nil {
|
||
log.Fatalf("创建客户端失败: %v", err)
|
||
}
|
||
|
||
// 构建退款请求
|
||
req := &RefundRequest{
|
||
RequestId: "20211105053044",
|
||
SignType: SignTypeRSA,
|
||
OrderId: "20211105045315",
|
||
Amount: "1",
|
||
}
|
||
|
||
// 发送退款请求
|
||
resp, err := client.Refund(context.Background(), req)
|
||
if err != nil {
|
||
log.Fatalf("退款请求失败: %v", err)
|
||
}
|
||
|
||
fmt.Printf("返回码: %s\n", resp.ReturnCode)
|
||
fmt.Printf("退款结果: %s\n", resp.Status)
|
||
|
||
// Output:
|
||
// 返回码: 000000
|
||
// 退款结果: SUCCESS
|
||
}
|
||
|
||
// TestSignMD5 测试MD5签名
|
||
func TestSignMD5(t *testing.T) {
|
||
signStr := "merchantId=888009941110054&requestId=20211105053044&signType=MD5&type=OrderRefund&version=2.0.0&orderId=20211105045315&amount=1"
|
||
signKey := "test-sign-key-64-chars-length-string-for-testing"
|
||
|
||
hmac := SignMD5(signStr, signKey)
|
||
if hmac == "" {
|
||
t.Error("MD5签名结果为空")
|
||
}
|
||
t.Logf("MD5签名结果: %s", hmac)
|
||
|
||
// 验证签名
|
||
if !VerifyMD5(signStr, signKey, hmac) {
|
||
t.Error("MD5签名验证失败")
|
||
}
|
||
}
|
||
|
||
// TestBuildSignString 测试签名串构建
|
||
func TestBuildSignString(t *testing.T) {
|
||
params := map[string]string{
|
||
"merchantId": "888009941110054",
|
||
"requestId": "20211105053044",
|
||
"signType": "MD5",
|
||
"type": "OrderRefund",
|
||
"version": "2.0.0",
|
||
"orderId": "20211105045315",
|
||
"amount": "1",
|
||
}
|
||
|
||
keys := getRequestSignKeys()
|
||
signStr := buildSignString(params, keys)
|
||
|
||
expected := "merchantId=888009941110054&requestId=20211105053044&signType=MD5&type=OrderRefund&version=2.0.0&orderId=20211105045315&amount=1"
|
||
if signStr != expected {
|
||
t.Errorf("签名串不匹配\n期望: %s\n实际: %s", expected, signStr)
|
||
}
|
||
}
|
||
|
||
// TestParseResponse 测试响应解析
|
||
func TestParseResponse(t *testing.T) {
|
||
rawResponse := `merchantId=888009941110054&payNo=205206151912517707&returnCode=000000&message=SUCCESS&signType=RSA&type=OrderRefund&version=2.0.0&amount=1&orderId=20211105045315&status=SUCCESS&serverCert=3082042030820308A003020102020420B82E72300D06092A864886F70D0101050500302F310B300906035504061302434E3120301E06035504030C17434D434120456E74657270726973652043415F32303438301E170D3230303830363031343834395A170D3232303830363031343834395A3076310B300906035504061302434E310F300D06035504080C06E6B996E58D97310F300D06035504070C06E995BFE6B2993145304306035504030C3CE4B8ADE59BBDE7A7BBE58AA8E9809AE4BFA1E99B86E59BA2E6B996E58D97E69C89E99990E585ACE58FB8E794B5E5AD90E59586E58AA1E4B8ADE5BF8330820122300D06092A864886F70D01010105000382010F003082010A028201010092FCBC26F1107D35B966C0C098B1F31F8313103A29DC5C78A8056D1AFAC1E5BAA89F5AEE6D3E737D3554AE815040AD5A5D9BAF5BB5B4B45AA5622335E3B9F48D9C05DF29248DF944544D414513F37DFE7F618D0C62B24E1E90CFD1790791861E20FDEFC4288EA4BF906EE1A6B576D9FDE6108CF5A1ED01D1931F2C72AED5393AE9CB668BBF629B25A239E09C54D45B565FBA040963B011C7B80F7C93D4695F4568F6358D591FBCB1073C2583630EC858B7C21F5B7CBB8CC05431D93F4AC358F78A06D6E40D1C033083F403697C12A0A99FF6363D792F624B66FCA655E7057686BA5093A8F68597DB8F40865081A3EB43C1C52E91D35F83F9FF3BF40C9901D08B0203010001A381FC3081F930370603551D250101FF042D302B06082B0601050507030206082B0601050507030106096086480186F8420401060A2B0601040182370A0303301F0603551D23041830168014972A89BEE6A2AA3FA666133AE36A3FBA5D86697130710603551D1F046A30683066A02FA02D862B687474703A2F2F7777772E636D63612E6E65742F646F776E6C6F61642F63726C2F43524C3134322E63726CA233A431302F3120301E06035504030C17434D434120456E74657270726973652043415F32303438310B300906035504061302434E300B0603551D0F0404030200B1301D0603551D0E04160414F713DA66CF8A1F30440D83455C2AFC6560B040B6300D06092A864886F70D010105050003820101002AD8B85ABEEAE53C6002381120F7D6B23620464197FBBDB093D4BB98AF256E335F55A67E32BFDF165BB1E2DDA997A0727086D26F8427FE6B77F6F00C6168CE715E8D298D5EB47B7227CDB8094CB1047CBDEA3D911903DCAA57131DEE1CD0069BBF8C7940A3E68438CCCA1949FF045F7349AA865F7FAC7170B7D7339DEBC46930FF9B241200440EAFD280F868C13364D13ECEB04EA702D0479862F9590813AB94BE38DC84E366B884174731464111A1AFEB199E1D7D518651BB5F430926286CA2B5619CD54689F5851A231C1605146D4ACD6CCC54DB79CC49F992083D5DEACAFDDC2B8777B13B7D63FF7E35D1098BEFD6B550EC3328ECE0224F5AAD6C2B83FB85&hmac=00AAA308D1AA66B2E534D9177B408B55A0CC14F8B27ACE59F08150E66E51858F18070A0DD629FCBD47BB1A98A632DB3F492213A92F14885043EC68BD8180E58ED742D748B708B281693226A7D3BF326B812BA45E7E7D13B4D52498B3C6C83380827CD741A9EF0D99B1C96588C2BE56C79BDFC3813CA4E627142BA7717FFFB43BE53F9B3F2A6D3112D83A90C3D3859A8456BC892E6E960FE56F36F829FAFA7CAEF19D01D10C0BD2F2538827CFF4C642BBAA6423AED0CB8E0D980D7D0BDEB4B1A04B9DAC8867B724527423038D953603A07018BEC1980AD31EDD3E88977F72795DCFB0B3B674FA91A6443F9F0DB67B485B3F3AD5E48AA26F3641E44B64FC7B0920`
|
||
|
||
resp, err := parseResponse(rawResponse)
|
||
if err != nil {
|
||
t.Fatalf("解析响应失败: %v", err)
|
||
}
|
||
|
||
if resp.MerchantId != "888009941110054" {
|
||
t.Errorf("MerchantId不匹配: %s", resp.MerchantId)
|
||
}
|
||
if resp.PayNo != "205206151912517707" {
|
||
t.Errorf("PayNo不匹配: %s", resp.PayNo)
|
||
}
|
||
if resp.ReturnCode != "000000" {
|
||
t.Errorf("ReturnCode不匹配: %s", resp.ReturnCode)
|
||
}
|
||
if resp.Status != "SUCCESS" {
|
||
t.Errorf("Status不匹配: %s", resp.Status)
|
||
}
|
||
if resp.SignType != "RSA" {
|
||
t.Errorf("SignType不匹配: %s", resp.SignType)
|
||
}
|
||
}
|
||
|
||
// TestErrorCode 测试错误码映射
|
||
func TestErrorCode(t *testing.T) {
|
||
tests := []struct {
|
||
code string
|
||
expected string
|
||
}{
|
||
{"000000", "未知错误码"},
|
||
{"C01088", "退货金额域非法"},
|
||
{"D22201", "订单不存在"},
|
||
{"IPS0008", "签名不符,一般都是由于中文编码引起"},
|
||
{"UNKNOWN", "未知错误码"},
|
||
}
|
||
|
||
for _, tt := range tests {
|
||
desc := GetErrorDescription(tt.code)
|
||
if desc != tt.expected {
|
||
t.Errorf("错误码 %s: 期望 %s, 实际 %s", tt.code, tt.expected, desc)
|
||
}
|
||
}
|
||
}
|
||
|
||
// TestIsRefundSuccess 测试退款成功判断
|
||
func TestIsRefundSuccess(t *testing.T) {
|
||
if !IsRefundRequestSuccess("000000") {
|
||
t.Error("000000 应该表示退款请求成功")
|
||
}
|
||
if !IsRefundRequestSuccess("MCG00000") {
|
||
t.Error("MCG00000 应该表示退款请求成功")
|
||
}
|
||
if IsRefundRequestSuccess("C01088") {
|
||
t.Error("C01088 不应该表示退款请求成功")
|
||
}
|
||
|
||
if !IsRefundSuccess("SUCCESS") {
|
||
t.Error("SUCCESS 应该表示退款最终成功")
|
||
}
|
||
if IsRefundSuccess("FAILED") {
|
||
t.Error("FAILED 不应该表示退款最终成功")
|
||
}
|
||
} |