package robokassa import ( "net/url" "strings" "testing" "github.com/google/uuid" ) func testCfg() Config { return Config{MerchantLogin: "shop", Password1: "p1", Password2: "p2", IsTest: true} } // resultSig computes the Pass2 Result signature Robokassa would send for a callback carrying only // Shp_order — the fixture the verifier must accept. func resultSig(pass2, outSum, invID string, orderID uuid.UUID) string { return sign(outSum + ":" + invID + ":" + pass2 + ":Shp_order=" + orderID.String()) } func TestPaymentURL(t *testing.T) { cfg := testCfg() id := uuid.New() u, err := url.Parse(cfg.PaymentURL(id, "149.00", "10 chips")) if err != nil { t.Fatalf("parse payment url: %v", err) } q := u.Query() if got := q.Get("OutSum"); got != "149.00" { t.Errorf("OutSum = %q, want 149.00", got) } if got := q.Get("InvId"); got != "0" { t.Errorf("InvId = %q, want 0 (unused)", got) } if got := q.Get("Shp_order"); got != id.String() { t.Errorf("Shp_order = %q, want the order id", got) } if got := q.Get("IsTest"); got != "1" { t.Errorf("IsTest = %q, want 1 (test shop)", got) } want := sign("shop:149.00:0:p1:Shp_order=" + id.String()) if got := q.Get("SignatureValue"); got != want { t.Errorf("SignatureValue = %q, want %q", got, want) } } func TestVerifyResult(t *testing.T) { cfg := testCfg() id := uuid.New() valid := func() url.Values { v := url.Values{} v.Set("OutSum", "149.00") v.Set("InvId", "0") v.Set("Shp_order", id.String()) // Robokassa sends the hash uppercase; the verifier must be case-insensitive. v.Set("SignatureValue", strings.ToUpper(resultSig("p2", "149.00", "0", id))) return v } gotID, gotSum, ok := cfg.VerifyResult(valid()) if !ok || gotID != id || gotSum != "149.00" { t.Fatalf("VerifyResult(valid) = %v/%q/%v, want %v/149.00/true", gotID, gotSum, ok, id) } // A tampered amount breaks the signature. tampered := valid() tampered.Set("OutSum", "1.00") if _, _, ok := cfg.VerifyResult(tampered); ok { t.Error("VerifyResult accepted a tampered amount") } // The wrong Password2 (a forged callback) does not verify. wrongPass := Config{MerchantLogin: "shop", Password1: "p1", Password2: "other"} if _, _, ok := wrongPass.VerifyResult(valid()); ok { t.Error("VerifyResult accepted a signature under the wrong password") } // A missing Shp_order is rejected (no order to credit). noOrder := valid() noOrder.Del("Shp_order") if _, _, ok := cfg.VerifyResult(noOrder); ok { t.Error("VerifyResult accepted a callback with no order") } }