277 lines
6.9 KiB
Go
277 lines
6.9 KiB
Go
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"
|
|
)
|