Bootstrap copied BFF and local accounts with public Sendico contracts (#34)

This commit is contained in:
Stephan D committed 2026-09-12 13:37:48 +02:00
1 parent 67dc5b5b1e
commit 7062672a87
556 files changed
+42291 -777

No files matched your search

+276
View File
@@ -0,0 +1,276 @@
package model
import (
"bytes"
"encoding/json"
"fmt"
"git.sendico.io/tech/sxchnge/bff/pkg/merrors"
"go.mongodb.org/mongo-driver/v2/bson"
)
type PaymentType int
const (
PaymentTypeIban PaymentType = iota
PaymentTypeCard
PaymentTypeCardToken
PaymentTypeBankAccount
PaymentTypeWallet
PaymentTypeCryptoAddress
PaymentTypeLedger
)
var paymentTypeToString = map[PaymentType]string{
PaymentTypeIban: "iban",
PaymentTypeCard: "card",
PaymentTypeCardToken: "cardToken",
PaymentTypeBankAccount: "bankAccount",
PaymentTypeWallet: "wallet",
PaymentTypeCryptoAddress: "cryptoAddress",
PaymentTypeLedger: "ledger",
}
var paymentTypeFromString = map[string]PaymentType{
"iban": PaymentTypeIban,
"card": PaymentTypeCard,
"cardToken": PaymentTypeCardToken,
"bankAccount": PaymentTypeBankAccount,
"wallet": PaymentTypeWallet,
"cryptoAddress": PaymentTypeCryptoAddress,
"ledger": PaymentTypeLedger,
}
func (t PaymentType) String() string {
if v, ok := paymentTypeToString[t]; ok {
return v
}
return "iban"
}
func (t PaymentType) IsValid() bool {
_, ok := paymentTypeToString[t]
return ok
}
func (t PaymentType) MarshalJSON() ([]byte, error) {
return json.Marshal(t.String())
}
func (t *PaymentType) UnmarshalJSON(data []byte) error {
var val string
if err := json.Unmarshal(data, &val); err != nil {
return err
}
v, ok := paymentTypeFromString[val]
if !ok {
return merrors.InvalidArgument(fmt.Sprintf("unknown PaymentType: %q", val))
}
*t = v
return nil
}
type PaymentMethodOwnerKind string
const (
PaymentMethodOwnerKindUnspecified PaymentMethodOwnerKind = ""
PaymentMethodOwnerKindAccount PaymentMethodOwnerKind = "ACCOUNT"
PaymentMethodOwnerKindRecipient PaymentMethodOwnerKind = "RECIPIENT"
)
func (k PaymentMethodOwnerKind) String() string {
return string(k)
}
func (k PaymentMethodOwnerKind) IsValid() bool {
switch k {
case PaymentMethodOwnerKindAccount, PaymentMethodOwnerKindRecipient:
return true
default:
return false
}
}
type PaymentMethod struct {
PermissionBound `bson:",inline" json:",inline"`
Describable `bson:",inline" json:",inline"`
OwnerKind PaymentMethodOwnerKind `bson:"ownerKind" json:"ownerKind"`
OwnerRef bson.ObjectID `bson:"ownerRef" json:"ownerRef"`
Type PaymentType `bson:"type" json:"type"`
Data bson.Raw `bson:"data" json:"data"`
IsMain bool `bson:"isMain" json:"isMain"`
}
type paymentMethodJSON struct {
PermissionBound `json:",inline"`
Describable `json:",inline"`
OwnerKind PaymentMethodOwnerKind `json:"ownerKind"`
OwnerRef bson.ObjectID `json:"ownerRef"`
Type PaymentType `json:"type"`
Data json.RawMessage `json:"data"`
IsMain bool `json:"isMain"`
}
func (m PaymentMethod) MarshalJSON() ([]byte, error) {
marshaledData, err := m.marshalDataJSON()
if err != nil {
return nil, err
}
payload := paymentMethodJSON{
PermissionBound: m.PermissionBound,
Describable: m.Describable,
OwnerKind: m.OwnerKind,
OwnerRef: m.OwnerRef,
Type: m.Type,
Data: marshaledData,
IsMain: m.IsMain,
}
return json.Marshal(payload)
}
func (m *PaymentMethod) UnmarshalJSON(data []byte) error {
var payload paymentMethodJSON
if err := json.Unmarshal(data, &payload); err != nil {
return err
}
m.PermissionBound = payload.PermissionBound
m.Describable = payload.Describable
m.OwnerKind = payload.OwnerKind
m.OwnerRef = payload.OwnerRef
m.Type = payload.Type
m.IsMain = payload.IsMain
if len(payload.Data) == 0 || bytes.Equal(payload.Data, []byte("null")) {
m.Data = nil
return nil
}
if raw, ok, err := m.unmarshalTypedDataJSON(payload.Data); ok || err != nil {
if err != nil {
return err
}
m.Data = raw
return nil
}
var rawData map[string]any
if err := json.Unmarshal(payload.Data, &rawData); err != nil {
return err
}
raw, err := bson.Marshal(rawData)
if err != nil {
return err
}
m.Data = raw
return nil
}
func (m PaymentMethod) marshalDataJSON() (json.RawMessage, error) {
if len(m.Data) == 0 {
return nil, nil
}
var payload any
var err error
switch m.Type {
case PaymentTypeIban:
payload, err = marshalTypedPaymentData[IbanPaymentData](m.Data)
case PaymentTypeCard:
payload, err = marshalTypedPaymentData[CardPaymentData](m.Data)
case PaymentTypeCardToken:
payload, err = marshalTypedPaymentData[TokenPaymentData](m.Data)
case PaymentTypeBankAccount:
payload, err = marshalTypedPaymentData[RussianBankAccountPaymentData](m.Data)
case PaymentTypeWallet:
payload, err = marshalTypedPaymentData[WalletPaymentData](m.Data)
case PaymentTypeCryptoAddress:
payload, err = marshalTypedPaymentData[CryptoAddressPaymentData](m.Data)
case PaymentTypeLedger:
payload, err = marshalTypedPaymentData[LedgerPaymentData](m.Data)
default:
var data bson.M
if err := bson.Unmarshal(m.Data, &data); err != nil {
return nil, err
}
if data == nil {
return nil, nil
}
payload = data
}
if err != nil {
return nil, err
}
data, err := json.Marshal(payload)
if err != nil {
return nil, err
}
return data, nil
}
func (m PaymentMethod) unmarshalTypedDataJSON(data json.RawMessage) (bson.Raw, bool, error) {
switch m.Type {
case PaymentTypeIban:
return unmarshalTypedPaymentData[IbanPaymentData](data)
case PaymentTypeCard:
return unmarshalTypedPaymentData[CardPaymentData](data)
case PaymentTypeCardToken:
return unmarshalTypedPaymentData[TokenPaymentData](data)
case PaymentTypeBankAccount:
return unmarshalTypedPaymentData[RussianBankAccountPaymentData](data)
case PaymentTypeWallet:
return unmarshalTypedPaymentData[WalletPaymentData](data)
case PaymentTypeCryptoAddress:
return unmarshalTypedPaymentData[CryptoAddressPaymentData](data)
case PaymentTypeLedger:
return unmarshalTypedPaymentData[LedgerPaymentData](data)
default:
return nil, false, nil
}
}
func marshalTypedPaymentData[T any](raw bson.Raw) (T, error) {
var payload T
err := bson.Unmarshal(raw, &payload)
return payload, err
}
func unmarshalTypedPaymentData[T any](data json.RawMessage) (bson.Raw, bool, error) {
var payload T
if err := json.Unmarshal(data, &payload); err != nil {
return nil, true, err
}
raw, err := bson.Marshal(payload)
if err != nil {
return nil, true, err
}
return raw, true, nil
}
type PaymentStatus string
const (
PaymentStatusUnspecified PaymentStatus = "unspecified"
// Intent exists, no funds touched yet.
PaymentStatusCreated PaymentStatus = "created"
// Internal Sendico operations: holds, ledger, FX, fee calc.
PaymentStatusAuthorizing PaymentStatus = "authorizing"
// Funds are outside Sendico (rail/gateway/blockchain/provider).
// Observe lives ONLY in this state.
PaymentStatusExecuting PaymentStatus = "executing"
// Final states.
PaymentStatusSucceeded PaymentStatus = "success"
PaymentStatusFailed PaymentStatus = "failed"
PaymentStatusCancelled PaymentStatus = "cancelled"
)