Files
sendico/api/gateway/aurora/internal/service/gateway/transfer_notifications.go
2026-03-05 13:24:41 +01:00

115 lines
3.5 KiB
Go

package gateway
import (
"context"
"fmt"
"github.com/tech/sendico/gateway/aurora/storage/model"
"github.com/tech/sendico/pkg/merrors"
paymentgateway "github.com/tech/sendico/pkg/messaging/notifications/paymentgateway"
pmodel "github.com/tech/sendico/pkg/model"
"github.com/tech/sendico/pkg/mservice"
"github.com/tech/sendico/pkg/mutil/mzap"
"github.com/tech/sendico/pkg/payments/rail"
paytypes "github.com/tech/sendico/pkg/payments/types"
"go.uber.org/zap"
)
func isFinalStatus(t *model.CardPayout) bool {
if t == nil {
return false
}
return isFinalPayoutStatus(t.Status)
}
func isFinalPayoutStatus(status model.PayoutStatus) bool {
switch status {
case model.PayoutStatusFailed, model.PayoutStatusSuccess, model.PayoutStatusCancelled:
return true
default:
return false
}
}
func toOpStatus(t *model.CardPayout) (rail.OperationResult, error) {
switch t.Status {
case model.PayoutStatusFailed:
return rail.OperationResultFailed, nil
case model.PayoutStatusSuccess:
return rail.OperationResultSuccess, nil
case model.PayoutStatusCancelled:
return rail.OperationResultCancelled, nil
default:
return rail.OperationResultFailed, merrors.InvalidArgument(fmt.Sprintf("unexpected transfer status, %s", t.Status), "t.Status")
}
}
func (p *cardPayoutProcessor) updatePayoutStatus(ctx context.Context, state *model.CardPayout) error {
if !isFinalStatus(state) {
if err := p.store.Payouts().Upsert(ctx, state); err != nil {
p.logger.Warn("Failed to update transfer status", zap.Error(err), mzap.ObjRef("payout_ref", state.ID),
zap.String("payment_ref", state.PaymentRef), zap.String("status", string(state.Status)),
)
return err
}
p.observeExecutionState(state)
return nil
}
_, err := p.executeTransaction(ctx, func(txCtx context.Context) (any, error) {
if upsertErr := p.store.Payouts().Upsert(txCtx, state); upsertErr != nil {
return nil, upsertErr
}
if isFinalStatus(state) {
if emitErr := p.emitTransferStatusEvent(txCtx, state); emitErr != nil {
return nil, emitErr
}
}
return nil, nil
})
if err != nil {
p.logger.Warn("Failed to update transfer status", zap.Error(err), mzap.ObjRef("payout_ref", state.ID),
zap.String("payment_ref", state.PaymentRef), zap.String("status", string(state.Status)),
)
return err
}
p.observeExecutionState(state)
return nil
}
func (p *cardPayoutProcessor) emitTransferStatusEvent(ctx context.Context, payout *model.CardPayout) error {
if p == nil || payout == nil {
return nil
}
if p.producer == nil || p.outboxStore() == nil {
return nil
}
status, err := toOpStatus(payout)
if err != nil {
p.logger.Warn("Failed to convert payout status to operation status for transfer status event", zap.Error(err),
mzap.ObjRef("payout_ref", payout.ID), zap.String("payment_ref", payout.PaymentRef), zap.String("status", string(payout.Status)))
return err
}
exec := pmodel.PaymentGatewayExecution{
PaymentIntentID: payout.IntentRef,
IdempotencyKey: payout.IdempotencyKey,
ExecutedMoney: &paytypes.Money{
Amount: fmt.Sprintf("%d", payout.AmountMinor),
Currency: payout.Currency,
},
PaymentRef: payout.PaymentRef,
Status: status,
OperationRef: payout.OperationRef,
Error: payout.FailureReason,
TransferRef: payout.GetID().Hex(),
}
env := paymentgateway.PaymentGatewayExecution(mservice.MntxGateway, &exec)
if err := p.sendWithOutbox(ctx, env); err != nil {
p.logger.Warn("Failed to publish transfer status event", zap.Error(err), mzap.ObjRef("transfer_ref", payout.ID))
return err
}
return nil
}