121 lines
3.6 KiB
Go
121 lines
3.6 KiB
Go
package gateway
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"github.com/tech/sendico/gateway/chain/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/payments/rail"
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
func isFinalStatus(t *model.Transfer) bool {
|
|
if t == nil {
|
|
return false
|
|
}
|
|
switch t.Status {
|
|
case model.TransferStatusFailed, model.TransferStatusSuccess, model.TransferStatusCancelled:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
func isFinalTransferStatus(status model.TransferStatus) bool {
|
|
switch status {
|
|
case model.TransferStatusFailed, model.TransferStatusSuccess, model.TransferStatusCancelled:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
func toOpStatus(t *model.Transfer) (rail.OperationResult, error) {
|
|
switch t.Status {
|
|
case model.TransferStatusFailed:
|
|
return rail.OperationResultFailed, nil
|
|
case model.TransferStatusSuccess:
|
|
return rail.OperationResultSuccess, nil
|
|
case model.TransferStatusCancelled:
|
|
return rail.OperationResultCancelled, nil
|
|
default:
|
|
return rail.OperationResultFailed, merrors.InvalidArgument(fmt.Sprintf("unexpected transfer status: %s", t.Status), "transfer.status")
|
|
}
|
|
}
|
|
|
|
func toError(t *model.Transfer) string {
|
|
if t == nil {
|
|
return ""
|
|
}
|
|
if t.Status == model.TransferStatusSuccess {
|
|
return ""
|
|
}
|
|
return t.FailureReason
|
|
}
|
|
|
|
func (s *Service) updateTransferStatus(ctx context.Context, transferRef string, status model.TransferStatus, failureReason, txHash string) (*model.Transfer, error) {
|
|
if !isFinalTransferStatus(status) {
|
|
transfer, err := s.storage.Transfers().UpdateStatus(ctx, transferRef, status, failureReason, txHash)
|
|
if err != nil {
|
|
s.logger.Warn("Failed to update transfer status", zap.String("transfer_ref", transferRef), zap.String("status", string(status)), zap.Error(err))
|
|
}
|
|
return transfer, err
|
|
}
|
|
|
|
res, err := s.executeTransaction(ctx, func(txCtx context.Context) (any, error) {
|
|
transfer, statusErr := s.storage.Transfers().UpdateStatus(txCtx, transferRef, status, failureReason, txHash)
|
|
if statusErr != nil {
|
|
return nil, statusErr
|
|
}
|
|
if isFinalStatus(transfer) {
|
|
if emitErr := s.emitTransferStatusEvent(txCtx, transfer); emitErr != nil {
|
|
return nil, emitErr
|
|
}
|
|
}
|
|
return transfer, nil
|
|
})
|
|
if err != nil {
|
|
s.logger.Warn("Failed to update transfer status", zap.String("transfer_ref", transferRef), zap.String("status", string(status)), zap.Error(err))
|
|
return nil, err
|
|
}
|
|
|
|
transfer, _ := res.(*model.Transfer)
|
|
return transfer, nil
|
|
}
|
|
|
|
func (s *Service) emitTransferStatusEvent(ctx context.Context, transfer *model.Transfer) error {
|
|
if s == nil || transfer == nil {
|
|
return nil
|
|
}
|
|
if s.producer == nil || s.outboxStore() == nil {
|
|
return nil
|
|
}
|
|
|
|
status, err := toOpStatus(transfer)
|
|
if err != nil {
|
|
s.logger.Warn("Failed to map transfer status for transfer status event", zap.Error(err), zap.String("transfer_ref", transfer.TransferRef))
|
|
return err
|
|
}
|
|
|
|
exec := pmodel.PaymentGatewayExecution{
|
|
PaymentIntentID: transfer.IntentRef,
|
|
IdempotencyKey: transfer.IdempotencyKey,
|
|
ExecutedMoney: transfer.NetAmount,
|
|
PaymentRef: transfer.PaymentRef,
|
|
Status: status,
|
|
OperationRef: transfer.OperationRef,
|
|
Error: toError(transfer),
|
|
TransferRef: transfer.TransferRef,
|
|
}
|
|
env := paymentgateway.PaymentGatewayExecution(mservice.ChainGateway, &exec)
|
|
if err := s.sendWithOutbox(ctx, env); err != nil {
|
|
s.logger.Warn("Failed to publish transfer status event", zap.Error(err), zap.String("transfer_ref", transfer.TransferRef))
|
|
return err
|
|
}
|
|
return nil
|
|
}
|