+source +destination in payments
This commit is contained in:
@@ -14,8 +14,11 @@ import (
|
||||
gatewayv1 "github.com/tech/sendico/pkg/proto/common/gateway/v1"
|
||||
paginationv1 "github.com/tech/sendico/pkg/proto/common/pagination/v1"
|
||||
oraclev1 "github.com/tech/sendico/pkg/proto/oracle/v1"
|
||||
endpointv1 "github.com/tech/sendico/pkg/proto/payments/endpoint/v1"
|
||||
orchestrationv2 "github.com/tech/sendico/pkg/proto/payments/orchestration/v2"
|
||||
quotationv2 "github.com/tech/sendico/pkg/proto/payments/quotation/v2"
|
||||
"github.com/tech/sendico/server/interface/api/srequest"
|
||||
"go.mongodb.org/mongo-driver/v2/bson"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
)
|
||||
|
||||
@@ -67,16 +70,24 @@ type PaymentQuotes struct {
|
||||
}
|
||||
|
||||
type Payment struct {
|
||||
PaymentRef string `json:"paymentRef,omitempty"`
|
||||
IdempotencyKey string `json:"idempotencyKey,omitempty"`
|
||||
State string `json:"state,omitempty"`
|
||||
Comment string `json:"comment,omitempty"`
|
||||
FailureCode string `json:"failureCode,omitempty"`
|
||||
FailureReason string `json:"failureReason,omitempty"`
|
||||
Operations []PaymentOperation `json:"operations,omitempty"`
|
||||
LastQuote *PaymentQuote `json:"lastQuote,omitempty"`
|
||||
CreatedAt time.Time `json:"createdAt,omitempty"`
|
||||
Meta map[string]string `json:"meta,omitempty"`
|
||||
PaymentRef string `json:"paymentRef,omitempty"`
|
||||
State string `json:"state,omitempty"`
|
||||
Comment string `json:"comment,omitempty"`
|
||||
Source *PaymentEndpoint `json:"source"`
|
||||
Destination *PaymentEndpoint `json:"destination"`
|
||||
FailureCode string `json:"failureCode,omitempty"`
|
||||
FailureReason string `json:"failureReason,omitempty"`
|
||||
Operations []PaymentOperation `json:"operations,omitempty"`
|
||||
LastQuote *PaymentQuote `json:"lastQuote,omitempty"`
|
||||
CreatedAt time.Time `json:"createdAt,omitempty"`
|
||||
Meta map[string]string `json:"meta,omitempty"`
|
||||
}
|
||||
|
||||
type PaymentEndpoint struct {
|
||||
Type string `json:"type,omitempty"`
|
||||
Data any `json:"data,omitempty"`
|
||||
PaymentMethodRef string `json:"paymentMethodRef,omitempty"`
|
||||
PayeeRef string `json:"payeeRef,omitempty"`
|
||||
}
|
||||
|
||||
type PaymentOperation struct {
|
||||
@@ -290,22 +301,257 @@ func toPayment(p *orchestrationv2.Payment) *Payment {
|
||||
if p == nil {
|
||||
return nil
|
||||
}
|
||||
intent := p.GetIntentSnapshot()
|
||||
operations := toUserVisibleOperations(p.GetStepExecutions(), p.GetQuoteSnapshot())
|
||||
failureCode, failureReason := firstFailure(operations)
|
||||
return &Payment{
|
||||
PaymentRef: p.GetPaymentRef(),
|
||||
State: enumJSONName(p.GetState().String()),
|
||||
Comment: strings.TrimSpace(p.GetIntentSnapshot().GetComment()),
|
||||
FailureCode: failureCode,
|
||||
FailureReason: failureReason,
|
||||
Operations: operations,
|
||||
LastQuote: toPaymentQuote(p.GetQuoteSnapshot()),
|
||||
CreatedAt: timestampAsTime(p.GetCreatedAt()),
|
||||
Meta: paymentMeta(p),
|
||||
IdempotencyKey: "",
|
||||
PaymentRef: p.GetPaymentRef(),
|
||||
State: enumJSONName(p.GetState().String()),
|
||||
Comment: strings.TrimSpace(intent.GetComment()),
|
||||
Source: toPaymentEndpoint(intent.GetSource()),
|
||||
Destination: toPaymentEndpoint(intent.GetDestination()),
|
||||
FailureCode: failureCode,
|
||||
FailureReason: failureReason,
|
||||
Operations: operations,
|
||||
LastQuote: toPaymentQuote(p.GetQuoteSnapshot()),
|
||||
CreatedAt: timestampAsTime(p.GetCreatedAt()),
|
||||
Meta: paymentMeta(p),
|
||||
}
|
||||
}
|
||||
|
||||
func toPaymentEndpoint(endpoint *endpointv1.PaymentEndpoint) *PaymentEndpoint {
|
||||
if endpoint == nil {
|
||||
return nil
|
||||
}
|
||||
if paymentMethodRef := strings.TrimSpace(endpoint.GetPaymentMethodRef()); paymentMethodRef != "" {
|
||||
return &PaymentEndpoint{PaymentMethodRef: paymentMethodRef}
|
||||
}
|
||||
if payeeRef := strings.TrimSpace(endpoint.GetPayeeRef()); payeeRef != "" {
|
||||
return &PaymentEndpoint{PayeeRef: payeeRef}
|
||||
}
|
||||
method := endpoint.GetPaymentMethod()
|
||||
if method == nil {
|
||||
return nil
|
||||
}
|
||||
return &PaymentEndpoint{
|
||||
Type: paymentEndpointType(method.GetType()),
|
||||
Data: paymentEndpointData(method),
|
||||
}
|
||||
}
|
||||
|
||||
func paymentEndpointType(methodType endpointv1.PaymentMethodType) string {
|
||||
switch methodType {
|
||||
case endpointv1.PaymentMethodType_PAYMENT_METHOD_TYPE_IBAN:
|
||||
return string(srequest.EndpointTypeIBAN)
|
||||
case endpointv1.PaymentMethodType_PAYMENT_METHOD_TYPE_CARD:
|
||||
return string(srequest.EndpointTypeCard)
|
||||
case endpointv1.PaymentMethodType_PAYMENT_METHOD_TYPE_CARD_TOKEN:
|
||||
return string(srequest.EndpointTypeCardToken)
|
||||
case endpointv1.PaymentMethodType_PAYMENT_METHOD_TYPE_BANK_ACCOUNT:
|
||||
return string(srequest.EndpointTypeBankAccount)
|
||||
case endpointv1.PaymentMethodType_PAYMENT_METHOD_TYPE_WALLET:
|
||||
return string(srequest.EndpointTypeWallet)
|
||||
case endpointv1.PaymentMethodType_PAYMENT_METHOD_TYPE_CRYPTO_ADDRESS:
|
||||
return string(srequest.EndpointTypeExternalChain)
|
||||
case endpointv1.PaymentMethodType_PAYMENT_METHOD_TYPE_LEDGER:
|
||||
return string(srequest.EndpointTypeLedger)
|
||||
case endpointv1.PaymentMethodType_PAYMENT_METHOD_TYPE_ACCOUNT:
|
||||
return "account"
|
||||
default:
|
||||
return "unspecified"
|
||||
}
|
||||
}
|
||||
|
||||
func paymentEndpointData(method *endpointv1.PaymentMethod) any {
|
||||
if method == nil {
|
||||
return nil
|
||||
}
|
||||
switch method.GetType() {
|
||||
case endpointv1.PaymentMethodType_PAYMENT_METHOD_TYPE_LEDGER:
|
||||
type ledgerMethodData struct {
|
||||
LedgerAccountRef string `bson:"ledgerAccountRef"`
|
||||
ContraLedgerAccountRef string `bson:"contraLedgerAccountRef,omitempty"`
|
||||
}
|
||||
var payload ledgerMethodData
|
||||
if err := bson.Unmarshal(method.GetData(), &payload); err != nil {
|
||||
return toRawBSON(method.GetData())
|
||||
}
|
||||
return srequest.LedgerEndpoint{
|
||||
LedgerAccountRef: strings.TrimSpace(payload.LedgerAccountRef),
|
||||
ContraLedgerAccountRef: strings.TrimSpace(payload.ContraLedgerAccountRef),
|
||||
}
|
||||
|
||||
case endpointv1.PaymentMethodType_PAYMENT_METHOD_TYPE_WALLET:
|
||||
type walletMethodData struct {
|
||||
WalletID string `bson:"walletId"`
|
||||
}
|
||||
var payload walletMethodData
|
||||
if err := bson.Unmarshal(method.GetData(), &payload); err != nil {
|
||||
return toRawBSON(method.GetData())
|
||||
}
|
||||
return srequest.WalletEndpoint{
|
||||
WalletID: strings.TrimSpace(payload.WalletID),
|
||||
}
|
||||
|
||||
case endpointv1.PaymentMethodType_PAYMENT_METHOD_TYPE_CRYPTO_ADDRESS:
|
||||
type cryptoMethodData struct {
|
||||
Currency string `bson:"currency"`
|
||||
Address string `bson:"address"`
|
||||
Network string `bson:"network"`
|
||||
DestinationTag *string `bson:"destinationTag,omitempty"`
|
||||
}
|
||||
var payload cryptoMethodData
|
||||
if err := bson.Unmarshal(method.GetData(), &payload); err != nil {
|
||||
return toRawBSON(method.GetData())
|
||||
}
|
||||
endpoint := srequest.ExternalChainEndpoint{
|
||||
Asset: &srequest.Asset{
|
||||
Chain: parseChainNetwork(payload.Network),
|
||||
TokenSymbol: strings.ToUpper(strings.TrimSpace(payload.Currency)),
|
||||
},
|
||||
Address: strings.TrimSpace(payload.Address),
|
||||
}
|
||||
if memo := strings.TrimSpace(strPtr(payload.DestinationTag)); memo != "" {
|
||||
endpoint.Memo = memo
|
||||
}
|
||||
if endpoint.Asset.Chain == srequest.ChainNetworkUnspecified && endpoint.Asset.TokenSymbol == "" {
|
||||
endpoint.Asset = nil
|
||||
}
|
||||
return endpoint
|
||||
|
||||
case endpointv1.PaymentMethodType_PAYMENT_METHOD_TYPE_CARD:
|
||||
type cardMethodData struct {
|
||||
Pan string `bson:"pan"`
|
||||
FirstName string `bson:"firstName"`
|
||||
LastName string `bson:"lastName"`
|
||||
ExpMonth string `bson:"expMonth"`
|
||||
ExpYear string `bson:"expYear"`
|
||||
Country string `bson:"country,omitempty"`
|
||||
}
|
||||
var payload cardMethodData
|
||||
if err := bson.Unmarshal(method.GetData(), &payload); err != nil {
|
||||
return toRawBSON(method.GetData())
|
||||
}
|
||||
return srequest.CardEndpoint{
|
||||
Pan: strings.TrimSpace(payload.Pan),
|
||||
FirstName: strings.TrimSpace(payload.FirstName),
|
||||
LastName: strings.TrimSpace(payload.LastName),
|
||||
ExpMonth: parseUint32(payload.ExpMonth),
|
||||
ExpYear: parseUint32(payload.ExpYear),
|
||||
Country: strings.TrimSpace(payload.Country),
|
||||
}
|
||||
|
||||
case endpointv1.PaymentMethodType_PAYMENT_METHOD_TYPE_CARD_TOKEN:
|
||||
type cardTokenMethodData struct {
|
||||
Token string `bson:"token"`
|
||||
Last4 string `bson:"last4,omitempty"`
|
||||
}
|
||||
var payload cardTokenMethodData
|
||||
if err := bson.Unmarshal(method.GetData(), &payload); err != nil {
|
||||
return toRawBSON(method.GetData())
|
||||
}
|
||||
return srequest.CardTokenEndpoint{
|
||||
Token: strings.TrimSpace(payload.Token),
|
||||
MaskedPan: strings.TrimSpace(payload.Last4),
|
||||
}
|
||||
|
||||
case endpointv1.PaymentMethodType_PAYMENT_METHOD_TYPE_BANK_ACCOUNT:
|
||||
type bankAccountMethodData struct {
|
||||
RecipientName string `bson:"recipientName"`
|
||||
Inn string `bson:"inn"`
|
||||
Kpp string `bson:"kpp"`
|
||||
BankName string `bson:"bankName"`
|
||||
Bik string `bson:"bik"`
|
||||
AccountNumber string `bson:"accountNumber"`
|
||||
CorrespondentAccount string `bson:"correspondentAccount"`
|
||||
}
|
||||
var payload bankAccountMethodData
|
||||
if err := bson.Unmarshal(method.GetData(), &payload); err != nil {
|
||||
return toRawBSON(method.GetData())
|
||||
}
|
||||
return srequest.BankAccountEndpoint{
|
||||
RecipientName: strings.TrimSpace(payload.RecipientName),
|
||||
Inn: strings.TrimSpace(payload.Inn),
|
||||
Kpp: strings.TrimSpace(payload.Kpp),
|
||||
BankName: strings.TrimSpace(payload.BankName),
|
||||
Bik: strings.TrimSpace(payload.Bik),
|
||||
AccountNumber: strings.TrimSpace(payload.AccountNumber),
|
||||
CorrespondentAccount: strings.TrimSpace(payload.CorrespondentAccount),
|
||||
}
|
||||
|
||||
case endpointv1.PaymentMethodType_PAYMENT_METHOD_TYPE_IBAN:
|
||||
type ibanMethodData struct {
|
||||
IBAN string `bson:"iban"`
|
||||
AccountHolder string `bson:"accountHolder"`
|
||||
BIC *string `bson:"bic,omitempty"`
|
||||
BankName *string `bson:"bankName,omitempty"`
|
||||
}
|
||||
var payload ibanMethodData
|
||||
if err := bson.Unmarshal(method.GetData(), &payload); err != nil {
|
||||
return toRawBSON(method.GetData())
|
||||
}
|
||||
return srequest.IBANEndpoint{
|
||||
IBAN: strings.TrimSpace(payload.IBAN),
|
||||
AccountHolder: strings.TrimSpace(payload.AccountHolder),
|
||||
BIC: strings.TrimSpace(strPtr(payload.BIC)),
|
||||
BankName: strings.TrimSpace(strPtr(payload.BankName)),
|
||||
}
|
||||
|
||||
default:
|
||||
return toRawBSON(method.GetData())
|
||||
}
|
||||
}
|
||||
|
||||
func toRawBSON(raw []byte) map[string]any {
|
||||
if len(raw) == 0 {
|
||||
return nil
|
||||
}
|
||||
var data map[string]any
|
||||
if err := bson.Unmarshal(raw, &data); err != nil {
|
||||
return nil
|
||||
}
|
||||
if len(data) == 0 {
|
||||
return nil
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
func parseChainNetwork(value string) srequest.ChainNetwork {
|
||||
switch strings.ToUpper(strings.TrimSpace(value)) {
|
||||
case "ETHEREUM_MAINNET":
|
||||
return srequest.ChainNetworkEthereumMainnet
|
||||
case "ARBITRUM_ONE":
|
||||
return srequest.ChainNetworkArbitrumOne
|
||||
case "TRON_MAINNET":
|
||||
return srequest.ChainNetworkTronMainnet
|
||||
case "TRON_NILE":
|
||||
return srequest.ChainNetworkTronNile
|
||||
case "", "UNSPECIFIED":
|
||||
return srequest.ChainNetworkUnspecified
|
||||
default:
|
||||
return srequest.ChainNetwork(strings.ToLower(strings.TrimSpace(value)))
|
||||
}
|
||||
}
|
||||
|
||||
func parseUint32(value string) uint32 {
|
||||
clean := strings.TrimSpace(value)
|
||||
if clean == "" {
|
||||
return 0
|
||||
}
|
||||
parsed, err := strconv.ParseUint(clean, 10, 32)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
return uint32(parsed)
|
||||
}
|
||||
|
||||
func strPtr(v *string) string {
|
||||
if v == nil {
|
||||
return ""
|
||||
}
|
||||
return *v
|
||||
}
|
||||
|
||||
func firstFailure(operations []PaymentOperation) (string, string) {
|
||||
for _, op := range operations {
|
||||
if strings.TrimSpace(op.FailureCode) == "" && strings.TrimSpace(op.FailureReason) == "" {
|
||||
|
||||
Reference in New Issue
Block a user