diff --git a/ymt_v3_generate/example_test.go b/ymt_v3_generate/example_test.go new file mode 100644 index 0000000..4eb684a --- /dev/null +++ b/ymt_v3_generate/example_test.go @@ -0,0 +1,272 @@ +package ymt_v3_generate_test + +import ( + "context" + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "encoding/base64" + "encoding/json" + "encoding/pem" + "fmt" + "net/http" + "net/http/httptest" + "strings" + "testing" + "time" + + sdk "ymt_v3_generate" +) + +// 生成测试用的 RSA 密钥对和 AES key +func generateTestKeys() (privateKeyPEM, publicKeyPEM string, aesKey []byte, err error) { + // RSA 密钥 + priv, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + return "", "", nil, err + } + privBytes := x509.MarshalPKCS1PrivateKey(priv) + privPEM := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: privBytes}) + publicKey := &priv.PublicKey + pubBytes, err := x509.MarshalPKIXPublicKey(publicKey) + if err != nil { + return "", "", nil, err + } + pubPEM := pem.EncodeToMemory(&pem.Block{Type: "PUBLIC KEY", Bytes: pubBytes}) + + // AES key (16字节) + aesKey = make([]byte, 16) + _, err = rand.Read(aesKey) + if err != nil { + return "", "", nil, err + } + return string(privPEM), string(pubPEM), aesKey, nil +} + +// 模拟服务器,返回加密的响应 +func mockServer(t *testing.T, aesKey []byte, responseBody interface{}) *httptest.Server { + return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // 验证请求头 + appID := r.Header.Get("Appid") + timestamp := r.Header.Get("Timestamp") + sign := r.Header.Get("Sign") + if appID == "" || timestamp == "" || sign == "" { + http.Error(w, "missing headers", http.StatusBadRequest) + return + } + // 读取请求体 + var reqBody map[string]string + if err := json.NewDecoder(r.Body).Decode(&reqBody); err != nil { + http.Error(w, "invalid body", http.StatusBadRequest) + return + } + ciphertext := reqBody["ciphertext"] + if ciphertext == "" { + http.Error(w, "missing ciphertext", http.StatusBadRequest) + return + } + // 这里不验证签名,仅测试流程 + + // 构造响应 + respPlaintext, _ := json.Marshal(responseBody) + // 加密 + encrypted, err := sdk.EncryptPlaintext(string(respPlaintext), aesKey) + if err != nil { + http.Error(w, "encrypt error", http.StatusInternalServerError) + return + } + apiResp := map[string]interface{}{ + "code": 200, + "message": "成功", + "data": map[string]string{ + "ciphertext": encrypted, + }, + } + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(apiResp) + })) +} + +func TestCreateKeyOrder(t *testing.T) { + privPEM, pubPEM, aesKey, err := generateTestKeys() + if err != nil { + t.Fatal(err) + } + server := mockServer(t, aesKey, sdk.KeyOrderResponse{ + OutBizNo: "order_001", + TradeNo: "7251449503000383488", + Key: "aZKdU9BymzR6qGRzJM", + ValidBeginTime: "2026-06-22 15:30:00", + ValidEndTime: "2026-12-31 23:59:59", + UsableNum: 1, + UsageNum: 0, + Status: 1, + SettlementPrice: 9.9, + Account: "18666666666", + }) + defer server.Close() + + client := sdk.NewClient( + sdk.WithAppID("test_app"), + sdk.WithPrivateKey(privPEM), + sdk.WithPublicKey(pubPEM), + sdk.WithKey(base64.StdEncoding.EncodeToString(aesKey)), + sdk.WithBaseURL(server.URL), + ) + + req := &sdk.KeyOrderRequest{ + OutBizNo: "order_001", + ActivityNo: "ACT20260622001", + Account: "18666666666", + NotifyURL: "https://notify.example.com/openapi", + } + resp, err := client.CreateKeyOrder(context.Background(), req) + if err != nil { + t.Fatal(err) + } + if resp.TradeNo != "7251449503000383488" { + t.Errorf("expected trade_no 7251449503000383488, got %s", resp.TradeNo) + } +} + +func TestQueryKey(t *testing.T) { + privPEM, pubPEM, aesKey, err := generateTestKeys() + if err != nil { + t.Fatal(err) + } + server := mockServer(t, aesKey, sdk.KeyQueryResponse{ + OutBizNo: "order_001", + TradeNo: "7251449503000383488", + Key: "aZKdU9BymzR6qGRzJM", + ValidBeginTime: "2026-06-22 15:30:00", + ValidEndTime: "2026-12-31 23:59:59", + UsableNum: 1, + UsageNum: 0, + Status: 1, + SettlementPrice: 9.9, + Account: "18666666666", + }) + defer server.Close() + + client := sdk.NewClient( + sdk.WithAppID("test_app"), + sdk.WithPrivateKey(privPEM), + sdk.WithPublicKey(pubPEM), + sdk.WithKey(base64.StdEncoding.EncodeToString(aesKey)), + sdk.WithBaseURL(server.URL), + ) + + req := &sdk.KeyQueryRequest{ + OutBizNo: "order_001", + } + resp, err := client.QueryKey(context.Background(), req) + if err != nil { + t.Fatal(err) + } + if resp.TradeNo != "7251449503000383488" { + t.Errorf("expected trade_no 7251449503000383488, got %s", resp.TradeNo) + } +} + +func TestDiscardKey(t *testing.T) { + privPEM, pubPEM, aesKey, err := generateTestKeys() + if err != nil { + t.Fatal(err) + } + server := mockServer(t, aesKey, sdk.KeyDiscardResponse{ + OutBizNo: "order_001", + TradeNo: "7251449503000383488", + Status: 3, + }) + defer server.Close() + + client := sdk.NewClient( + sdk.WithAppID("test_app"), + sdk.WithPrivateKey(privPEM), + sdk.WithPublicKey(pubPEM), + sdk.WithKey(base64.StdEncoding.EncodeToString(aesKey)), + sdk.WithBaseURL(server.URL), + ) + + req := &sdk.KeyDiscardRequest{ + TradeNo: "7251449503000383488", + } + resp, err := client.DiscardKey(context.Background(), req) + if err != nil { + t.Fatal(err) + } + if resp.Status != 3 { + t.Errorf("expected status 3, got %d", resp.Status) + } +} + +func TestBatchOrder(t *testing.T) { + privPEM, pubPEM, aesKey, err := generateTestKeys() + if err != nil { + t.Fatal(err) + } + server := mockServer(t, aesKey, sdk.BatchOrderResponse{ + OutBizNo: "batch_001", + TradeNo: "7251449503000383499", + Status: "processing", + }) + defer server.Close() + + client := sdk.NewClient( + sdk.WithAppID("test_app"), + sdk.WithPrivateKey(privPEM), + sdk.WithPublicKey(pubPEM), + sdk.WithKey(base64.StdEncoding.EncodeToString(aesKey)), + sdk.WithBaseURL(server.URL), + ) + + req := &sdk.BatchOrderRequest{ + OutBizNo: "batch_001", + ActivityNo: "ACT20260622001", + Number: 100, + } + resp, err := client.BatchOrder(context.Background(), req) + if err != nil { + t.Fatal(err) + } + if resp.Status != "processing" { + t.Errorf("expected status processing, got %s", resp.Status) + } +} + +func TestBatchQuery(t *testing.T) { + privPEM, pubPEM, aesKey, err := generateTestKeys() + if err != nil { + t.Fatal(err) + } + server := mockServer(t, aesKey, sdk.BatchQueryResponse{ + OutBizNo: "batch_001", + TradeNo: "7251449503000383499", + Status: "success", + DownloadURL: "https://oss.example.com/openapi_7251449503000383499.zip", + ZipPassword: "123456", + }) + defer server.Close() + + client := sdk.NewClient( + sdk.WithAppID("test_app"), + sdk.WithPrivateKey(privPEM), + sdk.WithPublicKey(pubPEM), + sdk.WithKey(base64.StdEncoding.EncodeToString(aesKey)), + sdk.WithBaseURL(server.URL), + ) + + req := &sdk.BatchQueryRequest{ + TradeNo: "7251449503000383499", + } + resp, err := client.BatchQuery(context.Background(), req) + if err != nil { + t.Fatal(err) + } + if resp.Status != "success" { + t.Errorf("expected status success, got %s", resp.Status) + } + if resp.DownloadURL == "" { + t.Error("expected download_url not empty") + } +} \ No newline at end of file