Files
sendico/api/server/internal/server/paymentapiimp/mapper.go
2025-12-26 15:14:31 +01:00

327 lines
9.9 KiB
Go

package paymentapiimp
import (
"strings"
"github.com/tech/sendico/pkg/merrors"
"github.com/tech/sendico/pkg/model"
fxv1 "github.com/tech/sendico/pkg/proto/common/fx/v1"
moneyv1 "github.com/tech/sendico/pkg/proto/common/money/v1"
chainv1 "github.com/tech/sendico/pkg/proto/gateway/chain/v1"
orchestratorv1 "github.com/tech/sendico/pkg/proto/payments/orchestrator/v1"
"github.com/tech/sendico/server/interface/api/srequest"
)
func mapPaymentIntent(intent *srequest.PaymentIntent) (*orchestratorv1.PaymentIntent, error) {
if intent == nil {
return nil, merrors.InvalidArgument("intent is required")
}
kind, err := mapPaymentKind(intent.Kind)
if err != nil {
return nil, err
}
settlementMode, err := mapSettlementMode(intent.SettlementMode)
if err != nil {
return nil, err
}
source, err := mapPaymentEndpoint(intent.Source, "source")
if err != nil {
return nil, err
}
destination, err := mapPaymentEndpoint(intent.Destination, "destination")
if err != nil {
return nil, err
}
fx, err := mapFXIntent(intent.FX)
if err != nil {
return nil, err
}
return &orchestratorv1.PaymentIntent{
Kind: kind,
Source: source,
Destination: destination,
Amount: mapMoney(intent.Amount),
RequiresFx: fx != nil,
Fx: fx,
SettlementMode: settlementMode,
Attributes: copyStringMap(intent.Attributes),
Customer: mapCustomer(intent.Customer),
}, nil
}
func mapPaymentEndpoint(endpoint *srequest.Endpoint, field string) (*orchestratorv1.PaymentEndpoint, error) {
if endpoint == nil {
return nil, nil
}
var result orchestratorv1.PaymentEndpoint
switch endpoint.Type {
case srequest.EndpointTypeLedger:
payload, err := endpoint.DecodeLedger()
if err != nil {
return nil, merrors.InvalidArgument(field + " endpoint: " + err.Error())
}
result.Endpoint = &orchestratorv1.PaymentEndpoint_Ledger{
Ledger: mapLedgerEndpoint(&payload),
}
case srequest.EndpointTypeManagedWallet:
payload, err := endpoint.DecodeManagedWallet()
if err != nil {
return nil, merrors.InvalidArgument(field + " endpoint: " + err.Error())
}
mw, err := mapManagedWalletEndpoint(&payload)
if err != nil {
return nil, merrors.InvalidArgument(field + " endpoint: " + err.Error())
}
result.Endpoint = &orchestratorv1.PaymentEndpoint_ManagedWallet{
ManagedWallet: mw,
}
case srequest.EndpointTypeExternalChain:
payload, err := endpoint.DecodeExternalChain()
if err != nil {
return nil, merrors.InvalidArgument(field + " endpoint: " + err.Error())
}
ext, err := mapExternalChainEndpoint(&payload)
if err != nil {
return nil, merrors.InvalidArgument(field + " endpoint: " + err.Error())
}
result.Endpoint = &orchestratorv1.PaymentEndpoint_ExternalChain{
ExternalChain: ext,
}
case srequest.EndpointTypeCard:
payload, err := endpoint.DecodeCard()
if err != nil {
return nil, merrors.InvalidArgument(field + " endpoint: " + err.Error())
}
result.Endpoint = &orchestratorv1.PaymentEndpoint_Card{
Card: mapCardEndpoint(&payload),
}
case srequest.EndpointTypeCardToken:
payload, err := endpoint.DecodeCardToken()
if err != nil {
return nil, merrors.InvalidArgument(field + " endpoint: " + err.Error())
}
result.Endpoint = &orchestratorv1.PaymentEndpoint_Card{
Card: mapCardTokenEndpoint(&payload),
}
case "":
return nil, merrors.InvalidArgument(field + " endpoint type is required")
default:
return nil, merrors.InvalidArgument(field + " endpoint has unsupported type: " + string(endpoint.Type))
}
result.Metadata = copyStringMap(endpoint.Metadata)
return &result, nil
}
func mapLedgerEndpoint(endpoint *srequest.LedgerEndpoint) *orchestratorv1.LedgerEndpoint {
if endpoint == nil {
return nil
}
return &orchestratorv1.LedgerEndpoint{
LedgerAccountRef: endpoint.LedgerAccountRef,
ContraLedgerAccountRef: endpoint.ContraLedgerAccountRef,
}
}
func mapManagedWalletEndpoint(endpoint *srequest.ManagedWalletEndpoint) (*orchestratorv1.ManagedWalletEndpoint, error) {
if endpoint == nil {
return nil, nil
}
asset, err := mapAsset(endpoint.Asset)
if err != nil {
return nil, err
}
return &orchestratorv1.ManagedWalletEndpoint{
ManagedWalletRef: endpoint.ManagedWalletRef,
Asset: asset,
}, nil
}
func mapExternalChainEndpoint(endpoint *srequest.ExternalChainEndpoint) (*orchestratorv1.ExternalChainEndpoint, error) {
if endpoint == nil {
return nil, nil
}
asset, err := mapAsset(endpoint.Asset)
if err != nil {
return nil, err
}
return &orchestratorv1.ExternalChainEndpoint{
Asset: asset,
Address: endpoint.Address,
Memo: endpoint.Memo,
}, nil
}
func mapAsset(asset *srequest.Asset) (*chainv1.Asset, error) {
if asset == nil {
return nil, nil
}
chain, err := mapChainNetwork(asset.Chain)
if err != nil {
return nil, err
}
return &chainv1.Asset{
Chain: chain,
TokenSymbol: asset.TokenSymbol,
ContractAddress: asset.ContractAddress,
}, nil
}
func mapMoney(m *model.Money) *moneyv1.Money {
if m == nil {
return nil
}
return &moneyv1.Money{
Amount: m.Amount,
Currency: m.Currency,
}
}
func mapFXIntent(fx *srequest.FXIntent) (*orchestratorv1.FXIntent, error) {
if fx == nil {
return nil, nil
}
side, err := mapFXSide(fx.Side)
if err != nil {
return nil, err
}
return &orchestratorv1.FXIntent{
Pair: mapCurrencyPair(fx.Pair),
Side: side,
Firm: fx.Firm,
TtlMs: fx.TTLms,
PreferredProvider: fx.PreferredProvider,
MaxAgeMs: fx.MaxAgeMs,
}, nil
}
func mapCustomer(customer *srequest.Customer) *orchestratorv1.Customer {
if customer == nil {
return nil
}
return &orchestratorv1.Customer{
Id: strings.TrimSpace(customer.ID),
FirstName: strings.TrimSpace(customer.FirstName),
MiddleName: strings.TrimSpace(customer.MiddleName),
LastName: strings.TrimSpace(customer.LastName),
Ip: strings.TrimSpace(customer.IP),
Zip: strings.TrimSpace(customer.Zip),
Country: strings.TrimSpace(customer.Country),
State: strings.TrimSpace(customer.State),
City: strings.TrimSpace(customer.City),
Address: strings.TrimSpace(customer.Address),
}
}
func mapCurrencyPair(pair *srequest.CurrencyPair) *fxv1.CurrencyPair {
if pair == nil {
return nil
}
return &fxv1.CurrencyPair{
Base: pair.Base,
Quote: pair.Quote,
}
}
func mapCardEndpoint(card *srequest.CardEndpoint) *orchestratorv1.CardEndpoint {
if card == nil {
return nil
}
result := &orchestratorv1.CardEndpoint{
CardholderName: strings.TrimSpace(card.FirstName),
CardholderSurname: strings.TrimSpace(card.LastName),
ExpMonth: card.ExpMonth,
ExpYear: card.ExpYear,
Country: strings.TrimSpace(card.Country),
}
if pan := strings.TrimSpace(card.Pan); pan != "" {
result.Card = &orchestratorv1.CardEndpoint_Pan{Pan: pan}
}
return result
}
func mapCardTokenEndpoint(card *srequest.CardTokenEndpoint) *orchestratorv1.CardEndpoint {
if card == nil {
return nil
}
return &orchestratorv1.CardEndpoint{
Card: &orchestratorv1.CardEndpoint_Token{Token: strings.TrimSpace(card.Token)},
MaskedPan: strings.TrimSpace(card.MaskedPan),
}
}
func mapPaymentKind(kind srequest.PaymentKind) (orchestratorv1.PaymentKind, error) {
switch strings.TrimSpace(string(kind)) {
case "", string(srequest.PaymentKindUnspecified):
return orchestratorv1.PaymentKind_PAYMENT_KIND_UNSPECIFIED, nil
case string(srequest.PaymentKindPayout):
return orchestratorv1.PaymentKind_PAYMENT_KIND_PAYOUT, nil
case string(srequest.PaymentKindInternalTransfer):
return orchestratorv1.PaymentKind_PAYMENT_KIND_INTERNAL_TRANSFER, nil
case string(srequest.PaymentKindFxConversion):
return orchestratorv1.PaymentKind_PAYMENT_KIND_FX_CONVERSION, nil
default:
return orchestratorv1.PaymentKind_PAYMENT_KIND_UNSPECIFIED, merrors.InvalidArgument("unsupported payment kind: " + string(kind))
}
}
func mapSettlementMode(mode srequest.SettlementMode) (orchestratorv1.SettlementMode, error) {
switch strings.TrimSpace(string(mode)) {
case "", string(srequest.SettlementModeUnspecified):
return orchestratorv1.SettlementMode_SETTLEMENT_MODE_UNSPECIFIED, nil
case string(srequest.SettlementModeFixSource):
return orchestratorv1.SettlementMode_SETTLEMENT_MODE_FIX_SOURCE, nil
case string(srequest.SettlementModeFixReceived):
return orchestratorv1.SettlementMode_SETTLEMENT_MODE_FIX_RECEIVED, nil
default:
return orchestratorv1.SettlementMode_SETTLEMENT_MODE_UNSPECIFIED, merrors.InvalidArgument("unsupported settlement mode: " + string(mode))
}
}
func mapFXSide(side srequest.FXSide) (fxv1.Side, error) {
switch strings.TrimSpace(string(side)) {
case "", string(srequest.FXSideUnspecified):
return fxv1.Side_SIDE_UNSPECIFIED, nil
case string(srequest.FXSideBuyBaseSellQuote):
return fxv1.Side_BUY_BASE_SELL_QUOTE, nil
case string(srequest.FXSideSellBaseBuyQuote):
return fxv1.Side_SELL_BASE_BUY_QUOTE, nil
default:
return fxv1.Side_SIDE_UNSPECIFIED, merrors.InvalidArgument("unsupported fx side: " + string(side))
}
}
func mapChainNetwork(chain srequest.ChainNetwork) (chainv1.ChainNetwork, error) {
switch strings.TrimSpace(string(chain)) {
case "", string(srequest.ChainNetworkUnspecified):
return chainv1.ChainNetwork_CHAIN_NETWORK_UNSPECIFIED, nil
case string(srequest.ChainNetworkEthereumMainnet):
return chainv1.ChainNetwork_CHAIN_NETWORK_ETHEREUM_MAINNET, nil
case string(srequest.ChainNetworkArbitrumOne):
return chainv1.ChainNetwork_CHAIN_NETWORK_ARBITRUM_ONE, nil
case string(srequest.ChainNetworkTronMainnet):
return chainv1.ChainNetwork_CHAIN_NETWORK_TRON_MAINNET, nil
case string(srequest.ChainNetworkTronNile):
return chainv1.ChainNetwork_CHAIN_NETWORK_TRON_NILE, nil
default:
return chainv1.ChainNetwork_CHAIN_NETWORK_UNSPECIFIED, merrors.InvalidArgument("unsupported chain network: " + string(chain))
}
}
func copyStringMap(src map[string]string) map[string]string {
if len(src) == 0 {
return nil
}
dst := make(map[string]string, len(src))
for k, v := range src {
dst[k] = v
}
return dst
}