140 lines
3.6 KiB
Go
140 lines
3.6 KiB
Go
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")
|
|
}
|
|
}
|