package mifapay import ( "context" "crypto/rand" "crypto/rsa" "crypto/x509" "encoding/base64" "encoding/json" "errors" "io" "net/http" "net/http/httptest" "testing" "time" "hyapp/pkg/xerr" "hyapp/services/wallet-service/internal/config" walletservice "hyapp/services/wallet-service/internal/service/wallet" ) func TestCreateOrderMapsGatewayRejectionToUnavailable(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { if request.URL.Path != "/paygateway/mbpay/order/en_v2" { t.Fatalf("unexpected mifapay path: %s", request.URL.Path) } raw, err := io.ReadAll(request.Body) if err != nil { t.Fatalf("read mifapay request body: %v", err) } var outer envelope if err := json.Unmarshal(raw, &outer); err != nil { t.Fatalf("decode mifapay request envelope: %v", err) } var data struct { Email string `json:"email"` FirstName string `json:"firstName"` LastName string `json:"lastName"` Phone string `json:"phone"` } if err := json.Unmarshal([]byte(outer.Data), &data); err != nil { t.Fatalf("decode mifapay request data: %v", err) } if data.Email != "163055@lalu.com" || data.FirstName != "normal55" || data.LastName != "normal55" || data.Phone != "163055" { t.Fatalf("mifapay payer fields mismatch: %+v", data) } // MiFaPay 会用 200 HTTP 承载业务拒单;签名错误属于三方网关拒绝请求,不是钱包状态冲突。 writer.Header().Set("Content-Type", "application/json") _, _ = writer.Write([]byte(`{"code":"100002","msg":"Signature error"}`)) })) defer server.Close() cfg := testMifaPayConfig(t, server.URL) client, err := New(cfg) if err != nil { t.Fatalf("new mifapay client failed: %v", err) } _, err = client.CreateOrder(context.Background(), walletservice.MifaPayCreateOrderRequest{ OrderID: "h5r_test", TargetUserID: 10001, ProductName: "coin", AmountMinor: 100, CurrencyCode: "SAR", CountryCode: "SA", PayWay: "Card", PayType: "MADA", NotifyURL: "https://api.example.com/api/v1/payment/mifapay/notify", ReturnURL: "https://h5.example.com/recharge/index.html", ClientIP: "127.0.0.1", Language: "en", ProviderAmountMinor: 100, PayerName: "normal55", PayerAccount: "163055", PayerEmail: "163055@lalu.com", }) if !xerr.IsCode(err, xerr.Unavailable) { t.Fatalf("expected MiFaPay rejection to map to UNAVAILABLE, got %v", err) } var payloadErr interface{ ProviderPayloadJSON() string } if !errors.As(err, &payloadErr) || payloadErr.ProviderPayloadJSON() != `{"code":"100002","msg":"Signature error"}` { t.Fatalf("mifapay rejection should expose raw provider payload, got %T %v", err, err) } } func TestQueryOrderUsesProviderOrderIDAndParsesSignedStatus(t *testing.T) { var signer *Client server := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { if request.URL.Path != "/paygateway/mbpay/order/query/en_v2" { t.Fatalf("unexpected mifapay query path: %s", request.URL.Path) } raw, err := io.ReadAll(request.Body) if err != nil { t.Fatalf("read mifapay query body: %v", err) } var outer envelope if err := json.Unmarshal(raw, &outer); err != nil { t.Fatalf("decode mifapay query envelope: %v", err) } var data map[string]any if err := json.Unmarshal([]byte(outer.Data), &data); err != nil { t.Fatalf("decode mifapay query data: %v", err) } if data["mbOrderId"] != "mb_10001" || data["orderId"] != nil { t.Fatalf("mifapay query should prefer mbOrderId: %+v", data) } response, err := signer.signedEnvelope(map[string]string{ "amount": "100", "currency": "SAR", "mbOrderId": "mb_10001", "orderId": "d9a2f7de008443c0993718b1d1f8f23c", "payType": "MADA", "status": "1", }) if err != nil { t.Fatalf("sign mifapay query response: %v", err) } writer.Header().Set("Content-Type", "application/json") _, _ = writer.Write(response) })) defer server.Close() cfg := testMifaPayConfig(t, server.URL) client, err := New(cfg) if err != nil { t.Fatalf("new mifapay client failed: %v", err) } signer = client resp, err := client.QueryOrder(context.Background(), walletservice.MifaPayQueryOrderRequest{ProviderOrderID: "mb_10001"}) if err != nil { t.Fatalf("QueryOrder failed: %v", err) } if resp.OrderID != "d9a2f7de008443c0993718b1d1f8f23c" || resp.ProviderOrderID != "mb_10001" || resp.Status != "1" || resp.Amount != "100" || resp.Currency != "SAR" || resp.PayType != "MADA" { t.Fatalf("mifapay query response mismatch: %+v", resp) } } func testMifaPayConfig(t *testing.T, apiBaseURL string) config.MifaPayConfig { t.Helper() privateKey, err := rsa.GenerateKey(rand.Reader, 2048) if err != nil { t.Fatalf("generate rsa key failed: %v", err) } privateDER, err := x509.MarshalPKCS8PrivateKey(privateKey) if err != nil { t.Fatalf("marshal private key failed: %v", err) } publicDER, err := x509.MarshalPKIXPublicKey(&privateKey.PublicKey) if err != nil { t.Fatalf("marshal public key failed: %v", err) } return config.MifaPayConfig{ MerAccount: "merchant-test", MerNo: "10000001", PrivateKey: base64.StdEncoding.EncodeToString(privateDER), PlatformPublicKey: base64.StdEncoding.EncodeToString(publicDER), APIBaseURL: apiBaseURL, HTTPTimeout: time.Second, } }