package gateway import ( "encoding/json" "testing" "time" "github.com/tech/sendico/gateway/mntx/internal/service/monetix" mntxv1 "github.com/tech/sendico/pkg/proto/gateway/mntx/v1" ) type fixedClock struct { now time.Time } func (f fixedClock) Now() time.Time { return f.now } func baseCallback() monetixCallback { cb := monetixCallback{ ProjectID: 42, } cb.Payment.ID = "payout-1" cb.Payment.Status = "success" cb.Payment.Sum.Amount = 5000 cb.Payment.Sum.Currency = "usd" cb.Customer.ID = "cust-1" cb.Operation.Status = "success" cb.Operation.Code = "" cb.Operation.Message = "ok" cb.Operation.RequestID = "req-1" cb.Operation.Provider.PaymentID = "prov-1" return cb } func TestMapCallbackToState_StatusMapping(t *testing.T) { now := time.Date(2024, 1, 2, 3, 4, 5, 0, time.UTC) cfg := monetix.DefaultConfig() cases := []struct { name string paymentStatus string operationStatus string code string expectedStatus mntxv1.PayoutStatus expectedOutcome string }{ { name: "success", paymentStatus: "success", operationStatus: "success", code: "0", expectedStatus: mntxv1.PayoutStatus_PAYOUT_STATUS_PROCESSED, expectedOutcome: monetix.OutcomeSuccess, }, { name: "processing", paymentStatus: "processing", operationStatus: "success", code: "", expectedStatus: mntxv1.PayoutStatus_PAYOUT_STATUS_PENDING, expectedOutcome: monetix.OutcomeProcessing, }, { name: "decline", paymentStatus: "failed", operationStatus: "failed", code: "1", expectedStatus: mntxv1.PayoutStatus_PAYOUT_STATUS_FAILED, expectedOutcome: monetix.OutcomeDecline, }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { cb := baseCallback() cb.Payment.Status = tc.paymentStatus cb.Operation.Status = tc.operationStatus cb.Operation.Code = tc.code state, outcome := mapCallbackToState(fixedClock{now: now}, cfg, cb) if state.Status != tc.expectedStatus { t.Fatalf("expected status %v, got %v", tc.expectedStatus, state.Status) } if outcome != tc.expectedOutcome { t.Fatalf("expected outcome %q, got %q", tc.expectedOutcome, outcome) } if state.Currency != "USD" { t.Fatalf("expected currency USD, got %q", state.Currency) } if !state.UpdatedAt.AsTime().Equal(now) { t.Fatalf("expected updated_at %v, got %v", now, state.UpdatedAt.AsTime()) } }) } } func TestFallbackProviderPaymentID(t *testing.T) { cb := baseCallback() if got := fallbackProviderPaymentID(cb); got != "prov-1" { t.Fatalf("expected provider payment id, got %q", got) } cb.Operation.Provider.PaymentID = "" if got := fallbackProviderPaymentID(cb); got != "req-1" { t.Fatalf("expected request id fallback, got %q", got) } cb.Operation.RequestID = "" if got := fallbackProviderPaymentID(cb); got != "payout-1" { t.Fatalf("expected payment id fallback, got %q", got) } } func TestVerifyCallbackSignature(t *testing.T) { secret := "secret" cb := baseCallback() sig, err := monetix.SignPayload(cb, secret) if err != nil { t.Fatalf("failed to sign payload: %v", err) } cb.Signature = sig payload, err := json.Marshal(cb) if err != nil { t.Fatalf("failed to marshal callback: %v", err) } if _, err := verifyCallbackSignature(payload, secret); err != nil { t.Fatalf("expected valid signature, got %v", err) } cb.Signature = "invalid" payload, err = json.Marshal(cb) if err != nil { t.Fatalf("failed to marshal callback: %v", err) } if _, err := verifyCallbackSignature(payload, secret); err == nil { t.Fatalf("expected signature mismatch error") } }