ledger account describibale support
This commit is contained in:
@@ -373,6 +373,20 @@ func managedWalletFromAccount(account *connectorv1.Account) *chainv1.ManagedWall
|
||||
if asset.GetTokenSymbol() == "" {
|
||||
asset.TokenSymbol = strings.TrimSpace(tokenFromAssetString(account.GetAsset()))
|
||||
}
|
||||
describable := account.GetDescribable()
|
||||
label := strings.TrimSpace(account.GetLabel())
|
||||
if describable == nil {
|
||||
if label != "" {
|
||||
describable = &describablev1.Describable{Name: label}
|
||||
}
|
||||
} else if strings.TrimSpace(describable.GetName()) == "" && label != "" {
|
||||
desc := strings.TrimSpace(describable.GetDescription())
|
||||
if desc == "" {
|
||||
describable = &describablev1.Describable{Name: label}
|
||||
} else {
|
||||
describable = &describablev1.Describable{Name: label, Description: &desc}
|
||||
}
|
||||
}
|
||||
return &chainv1.ManagedWallet{
|
||||
WalletRef: walletRef,
|
||||
OrganizationRef: organizationRef,
|
||||
@@ -382,9 +396,7 @@ func managedWalletFromAccount(account *connectorv1.Account) *chainv1.ManagedWall
|
||||
Status: managedWalletStatusFromAccount(account.GetState()),
|
||||
CreatedAt: account.GetCreatedAt(),
|
||||
UpdatedAt: account.GetUpdatedAt(),
|
||||
Describable: &describablev1.Describable{
|
||||
Name: strings.TrimSpace(account.GetLabel()),
|
||||
},
|
||||
Describable: describable,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -376,6 +376,7 @@ func chainWalletToAccount(wallet *chainv1.ManagedWallet) *connectorv1.Account {
|
||||
ProviderDetails: details,
|
||||
CreatedAt: wallet.GetCreatedAt(),
|
||||
UpdatedAt: wallet.GetUpdatedAt(),
|
||||
Describable: wallet.GetDescribable(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -394,7 +395,7 @@ func chainWalletState(status chainv1.ManagedWalletStatus) connectorv1.AccountSta
|
||||
|
||||
func transferDestinationFromOperation(op *connectorv1.Operation) (*chainv1.TransferDestination, error) {
|
||||
if op == nil {
|
||||
return nil, fmt.Errorf("transfer: operation is required")
|
||||
return nil, merrors.InvalidArgument("transfer: operation is required")
|
||||
}
|
||||
if to := op.GetTo(); to != nil {
|
||||
if account := to.GetAccount(); account != nil {
|
||||
@@ -404,7 +405,7 @@ func transferDestinationFromOperation(op *connectorv1.Operation) (*chainv1.Trans
|
||||
return &chainv1.TransferDestination{Destination: &chainv1.TransferDestination_ExternalAddress{ExternalAddress: strings.TrimSpace(ext.GetExternalRef())}}, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("transfer: to.account or to.external is required")
|
||||
return nil, merrors.InvalidArgument("transfer: to.account or to.external is required")
|
||||
}
|
||||
|
||||
func normalizeMoneyForChain(m *moneyv1.Money) *moneyv1.Money {
|
||||
@@ -451,12 +452,12 @@ func parseChainFees(reader params.Reader) []*chainv1.ServiceFeeBreakdown {
|
||||
|
||||
func parseMoneyFromMap(raw map[string]interface{}) (*moneyv1.Money, error) {
|
||||
if raw == nil {
|
||||
return nil, fmt.Errorf("money is required")
|
||||
return nil, merrors.InvalidArgument("money is required")
|
||||
}
|
||||
amount := strings.TrimSpace(fmt.Sprint(raw["amount"]))
|
||||
currency := strings.TrimSpace(fmt.Sprint(raw["currency"]))
|
||||
if amount == "" || currency == "" {
|
||||
return nil, fmt.Errorf("money is required")
|
||||
return nil, merrors.InvalidArgument("money is required")
|
||||
}
|
||||
return &moneyv1.Money{
|
||||
Amount: amount,
|
||||
@@ -575,11 +576,11 @@ func parseChainAsset(assetString string, reader params.Reader) (*chainv1.Asset,
|
||||
network = networkFromAssetString(assetString)
|
||||
}
|
||||
if token == "" {
|
||||
return nil, fmt.Errorf("asset: token_symbol is required")
|
||||
return nil, merrors.InvalidArgument("asset: token_symbol is required")
|
||||
}
|
||||
chain := shared.ChainEnumFromName(network)
|
||||
if chain == chainv1.ChainNetwork_CHAIN_NETWORK_UNSPECIFIED {
|
||||
return nil, fmt.Errorf("asset: network is required")
|
||||
return nil, merrors.InvalidArgument("asset: network is required")
|
||||
}
|
||||
return &chainv1.Asset{
|
||||
Chain: chain,
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
package shared
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math/big"
|
||||
"strings"
|
||||
|
||||
"github.com/tech/sendico/pkg/merrors"
|
||||
)
|
||||
|
||||
var (
|
||||
errHexEmpty = errors.New("hex value is empty")
|
||||
errHexInvalid = errors.New("invalid hex number")
|
||||
errHexOutOfRange = errors.New("hex number out of range")
|
||||
errHexEmpty = merrors.InvalidArgument("hex value is empty")
|
||||
errHexInvalid = merrors.InvalidArgument("invalid hex number")
|
||||
errHexOutOfRange = merrors.InvalidArgument("hex number out of range")
|
||||
)
|
||||
|
||||
// DecodeHexBig parses a hex string that may include leading zero digits.
|
||||
|
||||
@@ -458,7 +458,7 @@ func (i *Imp) resolveCallbackConfig(cfg callbackConfig) (callbackRuntimeConfig,
|
||||
|
||||
func (i *Imp) startHTTPCallbackServer(svc *mntxservice.Service, cfg callbackRuntimeConfig) error {
|
||||
if svc == nil {
|
||||
return errors.New("nil service provided for callback server")
|
||||
return merrors.InvalidArgument("nil service provided for callback server")
|
||||
}
|
||||
if strings.TrimSpace(cfg.Address) == "" {
|
||||
i.logger.Info("Monetix callback server disabled: address is empty")
|
||||
|
||||
@@ -3,7 +3,6 @@ package gateway
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/shopspring/decimal"
|
||||
@@ -124,26 +123,26 @@ func mntxOperationParams() []*connectorv1.OperationParamSpec {
|
||||
|
||||
func payoutAmount(op *connectorv1.Operation, reader params.Reader) (int64, string, error) {
|
||||
if op == nil {
|
||||
return 0, "", fmt.Errorf("payout: operation is required")
|
||||
return 0, "", merrors.InvalidArgument("payout: operation is required")
|
||||
}
|
||||
currency := currencyFromOperation(op)
|
||||
if currency == "" {
|
||||
return 0, "", fmt.Errorf("payout: currency is required")
|
||||
return 0, "", merrors.InvalidArgument("payout: currency is required")
|
||||
}
|
||||
if minor, ok := reader.Int64("amount_minor"); ok && minor > 0 {
|
||||
return minor, currency, nil
|
||||
}
|
||||
money := op.GetMoney()
|
||||
if money == nil {
|
||||
return 0, "", fmt.Errorf("payout: money is required")
|
||||
return 0, "", merrors.InvalidArgument("payout: money is required")
|
||||
}
|
||||
amount := strings.TrimSpace(money.GetAmount())
|
||||
if amount == "" {
|
||||
return 0, "", fmt.Errorf("payout: amount is required")
|
||||
return 0, "", merrors.InvalidArgument("payout: amount is required")
|
||||
}
|
||||
dec, err := decimal.NewFromString(amount)
|
||||
if err != nil {
|
||||
return 0, "", fmt.Errorf("payout: invalid amount")
|
||||
return 0, "", merrors.InvalidArgument("payout: invalid amount")
|
||||
}
|
||||
minor := dec.Mul(decimal.NewFromInt(100)).IntPart()
|
||||
return minor, currency, nil
|
||||
|
||||
@@ -3,7 +3,6 @@ package gateway
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/tech/sendico/pkg/connector/params"
|
||||
@@ -139,7 +138,7 @@ func tgsettleOperationParams() []*connectorv1.OperationParamSpec {
|
||||
|
||||
func transferDestinationFromOperation(op *connectorv1.Operation) (*chainv1.TransferDestination, error) {
|
||||
if op == nil {
|
||||
return nil, fmt.Errorf("transfer: operation is required")
|
||||
return nil, merrors.InvalidArgument("transfer: operation is required")
|
||||
}
|
||||
if to := op.GetTo(); to != nil {
|
||||
if account := to.GetAccount(); account != nil {
|
||||
@@ -149,7 +148,7 @@ func transferDestinationFromOperation(op *connectorv1.Operation) (*chainv1.Trans
|
||||
return &chainv1.TransferDestination{Destination: &chainv1.TransferDestination_ExternalAddress{ExternalAddress: strings.TrimSpace(ext.GetExternalRef())}}, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("transfer: to.account or to.external is required")
|
||||
return nil, merrors.InvalidArgument("transfer: to.account or to.external is required")
|
||||
}
|
||||
|
||||
func normalizeMoneyForTransfer(m *moneyv1.Money) *moneyv1.Money {
|
||||
|
||||
@@ -2,12 +2,12 @@ package storage
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"github.com/tech/sendico/gateway/tgsettle/storage/model"
|
||||
"github.com/tech/sendico/pkg/merrors"
|
||||
)
|
||||
|
||||
var ErrDuplicate = errors.New("payment gateway storage: duplicate record")
|
||||
var ErrDuplicate = merrors.DataConflict("payment gateway storage: duplicate record")
|
||||
|
||||
type Repository interface {
|
||||
Payments() PaymentsStore
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
|
||||
"github.com/tech/sendico/pkg/merrors"
|
||||
"github.com/tech/sendico/pkg/payments/rail"
|
||||
describablev1 "github.com/tech/sendico/pkg/proto/common/describable/v1"
|
||||
connectorv1 "github.com/tech/sendico/pkg/proto/connector/v1"
|
||||
moneyv1 "github.com/tech/sendico/pkg/proto/common/money/v1"
|
||||
paginationv1 "github.com/tech/sendico/pkg/proto/common/pagination/v1"
|
||||
@@ -196,12 +197,23 @@ func (c *ledgerClient) CreateAccount(ctx context.Context, req *ledgerv1.CreateAc
|
||||
"allow_negative": req.GetAllowNegative(),
|
||||
"is_settlement": req.GetIsSettlement(),
|
||||
}
|
||||
label := ""
|
||||
if desc := req.GetDescribable(); desc != nil {
|
||||
label = strings.TrimSpace(desc.GetName())
|
||||
if desc.Description != nil {
|
||||
trimmed := strings.TrimSpace(desc.GetDescription())
|
||||
if trimmed != "" {
|
||||
params["description"] = trimmed
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(req.GetMetadata()) > 0 {
|
||||
params["metadata"] = mapStringToInterface(req.GetMetadata())
|
||||
}
|
||||
resp, err := c.client.OpenAccount(ctx, &connectorv1.OpenAccountRequest{
|
||||
Kind: connectorv1.AccountKind_LEDGER_ACCOUNT,
|
||||
Asset: strings.TrimSpace(req.GetCurrency()),
|
||||
Label: label,
|
||||
Params: structFromMap(params),
|
||||
})
|
||||
if err != nil {
|
||||
@@ -469,6 +481,18 @@ func ledgerAccountFromConnector(account *connectorv1.Account) *ledgerv1.LedgerAc
|
||||
if ref := account.GetRef(); ref != nil {
|
||||
accountID = strings.TrimSpace(ref.GetAccountId())
|
||||
}
|
||||
describable := account.GetDescribable()
|
||||
label := strings.TrimSpace(account.GetLabel())
|
||||
if describable == nil && label != "" {
|
||||
describable = &describablev1.Describable{Name: label}
|
||||
} else if describable != nil && strings.TrimSpace(describable.GetName()) == "" && label != "" {
|
||||
desc := strings.TrimSpace(describable.GetDescription())
|
||||
if desc == "" {
|
||||
describable = &describablev1.Describable{Name: label}
|
||||
} else {
|
||||
describable = &describablev1.Describable{Name: label, Description: &desc}
|
||||
}
|
||||
}
|
||||
return &ledgerv1.LedgerAccount{
|
||||
LedgerAccountRef: accountID,
|
||||
OrganizationRef: strings.TrimSpace(account.GetOwnerRef()),
|
||||
@@ -480,6 +504,7 @@ func ledgerAccountFromConnector(account *connectorv1.Account) *ledgerv1.LedgerAc
|
||||
IsSettlement: isSettlement,
|
||||
CreatedAt: account.GetCreatedAt(),
|
||||
UpdatedAt: account.GetUpdatedAt(),
|
||||
Describable: describable,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"github.com/tech/sendico/ledger/storage/model"
|
||||
"github.com/tech/sendico/pkg/api/routers/gsresponse"
|
||||
"github.com/tech/sendico/pkg/merrors"
|
||||
describablev1 "github.com/tech/sendico/pkg/proto/common/describable/v1"
|
||||
ledgerv1 "github.com/tech/sendico/pkg/proto/ledger/v1"
|
||||
"go.uber.org/zap"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
@@ -62,6 +63,8 @@ func (s *Service) createAccountResponder(_ context.Context, req *ledgerv1.Create
|
||||
metadata = nil
|
||||
}
|
||||
|
||||
describable := describableFromProto(req.GetDescribable())
|
||||
|
||||
account := &model.Account{
|
||||
AccountCode: accountCode,
|
||||
Currency: currency,
|
||||
@@ -71,6 +74,9 @@ func (s *Service) createAccountResponder(_ context.Context, req *ledgerv1.Create
|
||||
IsSettlement: req.GetIsSettlement(),
|
||||
Metadata: metadata,
|
||||
}
|
||||
if describable != nil {
|
||||
account.Describable = *describable
|
||||
}
|
||||
account.OrganizationRef = orgRef
|
||||
|
||||
err = s.storage.Accounts().Create(ctx, account)
|
||||
@@ -204,5 +210,45 @@ func toProtoAccount(account *model.Account) *ledgerv1.LedgerAccount {
|
||||
Metadata: metadata,
|
||||
CreatedAt: createdAt,
|
||||
UpdatedAt: updatedAt,
|
||||
Describable: describableToProto(account.Describable),
|
||||
}
|
||||
}
|
||||
|
||||
func describableFromProto(desc *describablev1.Describable) *model.Describable {
|
||||
if desc == nil {
|
||||
return nil
|
||||
}
|
||||
name := strings.TrimSpace(desc.GetName())
|
||||
var description *string
|
||||
if desc.Description != nil {
|
||||
trimmed := strings.TrimSpace(desc.GetDescription())
|
||||
if trimmed != "" {
|
||||
description = &trimmed
|
||||
}
|
||||
}
|
||||
if name == "" && description == nil {
|
||||
return nil
|
||||
}
|
||||
return &model.Describable{
|
||||
Name: name,
|
||||
Description: description,
|
||||
}
|
||||
}
|
||||
|
||||
func describableToProto(desc model.Describable) *describablev1.Describable {
|
||||
name := strings.TrimSpace(desc.Name)
|
||||
var description *string
|
||||
if desc.Description != nil {
|
||||
trimmed := strings.TrimSpace(*desc.Description)
|
||||
if trimmed != "" {
|
||||
description = &trimmed
|
||||
}
|
||||
}
|
||||
if name == "" && description == nil {
|
||||
return nil
|
||||
}
|
||||
return &describablev1.Describable{
|
||||
Name: name,
|
||||
Description: description,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"github.com/tech/sendico/ledger/internal/appversion"
|
||||
"github.com/tech/sendico/pkg/connector/params"
|
||||
"github.com/tech/sendico/pkg/merrors"
|
||||
describablev1 "github.com/tech/sendico/pkg/proto/common/describable/v1"
|
||||
moneyv1 "github.com/tech/sendico/pkg/proto/common/money/v1"
|
||||
connectorv1 "github.com/tech/sendico/pkg/proto/connector/v1"
|
||||
ledgerv1 "github.com/tech/sendico/pkg/proto/ledger/v1"
|
||||
@@ -76,6 +77,7 @@ func (c *connectorAdapter) OpenAccount(ctx context.Context, req *connectorv1.Ope
|
||||
|
||||
status := parseLedgerAccountStatus(reader, "status")
|
||||
metadata := mergeMetadata(reader.StringMap("metadata"), req.GetLabel(), req.GetOwnerRef(), req.GetCorrelationId(), req.GetParentIntentId())
|
||||
describable := describableFromLabel(req.GetLabel(), reader.String("description"))
|
||||
|
||||
resp, err := c.svc.CreateAccount(ctx, &ledgerv1.CreateAccountRequest{
|
||||
OrganizationRef: orgRef,
|
||||
@@ -86,6 +88,7 @@ func (c *connectorAdapter) OpenAccount(ctx context.Context, req *connectorv1.Ope
|
||||
AllowNegative: reader.Bool("allow_negative"),
|
||||
IsSettlement: reader.Bool("is_settlement"),
|
||||
Metadata: metadata,
|
||||
Describable: describable,
|
||||
})
|
||||
if err != nil {
|
||||
return &connectorv1.OpenAccountResponse{Error: connectorError(mapErrorCode(err), err.Error(), nil, "")}, nil
|
||||
@@ -340,6 +343,7 @@ func ledgerAccountToConnector(account *ledgerv1.LedgerAccount) *connectorv1.Acco
|
||||
"allow_negative": account.GetAllowNegative(),
|
||||
"is_settlement": account.GetIsSettlement(),
|
||||
})
|
||||
describable := ledgerAccountDescribable(account)
|
||||
return &connectorv1.Account{
|
||||
Ref: &connectorv1.AccountRef{
|
||||
ConnectorId: ledgerConnectorID,
|
||||
@@ -353,6 +357,7 @@ func ledgerAccountToConnector(account *ledgerv1.LedgerAccount) *connectorv1.Acco
|
||||
ProviderDetails: details,
|
||||
CreatedAt: account.GetCreatedAt(),
|
||||
UpdatedAt: account.GetUpdatedAt(),
|
||||
Describable: describable,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -367,6 +372,71 @@ func ledgerAccountState(status ledgerv1.AccountStatus) connectorv1.AccountState
|
||||
}
|
||||
}
|
||||
|
||||
func ledgerAccountDescribable(account *ledgerv1.LedgerAccount) *describablev1.Describable {
|
||||
if account == nil {
|
||||
return nil
|
||||
}
|
||||
if desc := cleanedDescribable(account.GetDescribable()); desc != nil {
|
||||
return desc
|
||||
}
|
||||
metadata := account.GetMetadata()
|
||||
name := ""
|
||||
if metadata != nil {
|
||||
if v := strings.TrimSpace(metadata["name"]); v != "" {
|
||||
name = v
|
||||
} else if v := strings.TrimSpace(metadata["label"]); v != "" {
|
||||
name = v
|
||||
}
|
||||
}
|
||||
if name == "" {
|
||||
name = strings.TrimSpace(account.GetAccountCode())
|
||||
}
|
||||
desc := ""
|
||||
if metadata != nil {
|
||||
desc = strings.TrimSpace(metadata["description"])
|
||||
}
|
||||
if name == "" && desc == "" {
|
||||
return nil
|
||||
}
|
||||
if desc == "" {
|
||||
return &describablev1.Describable{Name: name}
|
||||
}
|
||||
return &describablev1.Describable{Name: name, Description: &desc}
|
||||
}
|
||||
|
||||
func describableFromLabel(label, description string) *describablev1.Describable {
|
||||
label = strings.TrimSpace(label)
|
||||
description = strings.TrimSpace(description)
|
||||
if label == "" && description == "" {
|
||||
return nil
|
||||
}
|
||||
if description == "" {
|
||||
return &describablev1.Describable{Name: label}
|
||||
}
|
||||
return &describablev1.Describable{Name: label, Description: &description}
|
||||
}
|
||||
|
||||
func cleanedDescribable(desc *describablev1.Describable) *describablev1.Describable {
|
||||
if desc == nil {
|
||||
return nil
|
||||
}
|
||||
name := strings.TrimSpace(desc.GetName())
|
||||
var description *string
|
||||
if desc.Description != nil {
|
||||
trimmed := strings.TrimSpace(desc.GetDescription())
|
||||
if trimmed != "" {
|
||||
description = &trimmed
|
||||
}
|
||||
}
|
||||
if name == "" && description == nil {
|
||||
return nil
|
||||
}
|
||||
return &describablev1.Describable{
|
||||
Name: name,
|
||||
Description: description,
|
||||
}
|
||||
}
|
||||
|
||||
func ledgerReceipt(ref string, status connectorv1.OperationStatus) *connectorv1.OperationReceipt {
|
||||
return &connectorv1.OperationReceipt{
|
||||
OperationId: strings.TrimSpace(ref),
|
||||
@@ -454,7 +524,7 @@ func operationAccountID(party *connectorv1.OperationParty) string {
|
||||
func parseLedgerAccountType(reader params.Reader, key string) (ledgerv1.AccountType, error) {
|
||||
value, ok := reader.Value(key)
|
||||
if !ok {
|
||||
return ledgerv1.AccountType_ACCOUNT_TYPE_UNSPECIFIED, fmt.Errorf("open_account: account_type is required")
|
||||
return ledgerv1.AccountType_ACCOUNT_TYPE_UNSPECIFIED, merrors.InvalidArgument("open_account: account_type is required")
|
||||
}
|
||||
switch v := value.(type) {
|
||||
case string:
|
||||
@@ -466,7 +536,7 @@ func parseLedgerAccountType(reader params.Reader, key string) (ledgerv1.AccountT
|
||||
case int64:
|
||||
return ledgerv1.AccountType(v), nil
|
||||
default:
|
||||
return ledgerv1.AccountType_ACCOUNT_TYPE_UNSPECIFIED, fmt.Errorf("open_account: account_type is required")
|
||||
return ledgerv1.AccountType_ACCOUNT_TYPE_UNSPECIFIED, merrors.InvalidArgument("open_account: account_type is required")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -481,7 +551,7 @@ func parseLedgerAccountTypeString(value string) (ledgerv1.AccountType, error) {
|
||||
case "ACCOUNT_TYPE_EXPENSE", "EXPENSE":
|
||||
return ledgerv1.AccountType_ACCOUNT_TYPE_EXPENSE, nil
|
||||
default:
|
||||
return ledgerv1.AccountType_ACCOUNT_TYPE_UNSPECIFIED, fmt.Errorf("open_account: invalid account_type")
|
||||
return ledgerv1.AccountType_ACCOUNT_TYPE_UNSPECIFIED, merrors.InvalidArgument("open_account: invalid account_type")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -518,15 +588,15 @@ func parseLedgerCharges(reader params.Reader) ([]*ledgerv1.PostingLine, error) {
|
||||
for i, item := range items {
|
||||
raw, ok := item.(map[string]interface{})
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("charges[%d]: invalid charge entry", i)
|
||||
return nil, merrors.InvalidArgument(fmt.Sprintf("charges[%d]: invalid charge entry", i))
|
||||
}
|
||||
accountRef := strings.TrimSpace(fmt.Sprint(raw["ledger_account_ref"]))
|
||||
if accountRef == "" {
|
||||
return nil, fmt.Errorf("charges[%d]: ledger_account_ref is required", i)
|
||||
return nil, merrors.InvalidArgument(fmt.Sprintf("charges[%d]: ledger_account_ref is required", i))
|
||||
}
|
||||
money, err := parseMoneyFromMap(raw)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("charges[%d]: %w", i, err)
|
||||
return nil, merrors.InvalidArgumentWrap(err, fmt.Sprintf("charges[%d]: invalid money", i))
|
||||
}
|
||||
lineType := parseLedgerLineType(fmt.Sprint(raw["line_type"]))
|
||||
result = append(result, &ledgerv1.PostingLine{
|
||||
@@ -553,12 +623,12 @@ func parseLedgerLineType(value string) ledgerv1.LineType {
|
||||
|
||||
func parseMoneyFromMap(raw map[string]interface{}) (*moneyv1.Money, error) {
|
||||
if raw == nil {
|
||||
return nil, fmt.Errorf("money is required")
|
||||
return nil, merrors.InvalidArgument("money is required")
|
||||
}
|
||||
amount := strings.TrimSpace(fmt.Sprint(raw["amount"]))
|
||||
currency := strings.TrimSpace(fmt.Sprint(raw["currency"]))
|
||||
if amount == "" || currency == "" {
|
||||
return nil, fmt.Errorf("money is required")
|
||||
return nil, merrors.InvalidArgument("money is required")
|
||||
}
|
||||
return &moneyv1.Money{
|
||||
Amount: amount,
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
type Account struct {
|
||||
storable.Base `bson:",inline" json:",inline"`
|
||||
model.PermissionBound `bson:",inline" json:",inline"`
|
||||
model.Describable `bson:",inline" json:",inline"`
|
||||
|
||||
AccountCode string `bson:"accountCode" json:"accountCode"` // e.g., "asset:cash:usd"
|
||||
Currency string `bson:"currency" json:"currency"` // ISO 4217 currency code
|
||||
|
||||
@@ -2,7 +2,6 @@ package notificationimp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -75,7 +74,7 @@ func (m *confirmationManager) Stop() {
|
||||
|
||||
func (m *confirmationManager) HandleRequest(ctx context.Context, request *model.ConfirmationRequest) error {
|
||||
if m == nil {
|
||||
return errors.New("confirmation manager is nil")
|
||||
return merrors.Internal("confirmation manager is nil")
|
||||
}
|
||||
if request == nil {
|
||||
return merrors.InvalidArgument("confirmation request is nil", "request")
|
||||
@@ -338,25 +337,25 @@ var currencyPattern = regexp.MustCompile(`^[A-Za-z]{3,10}$`)
|
||||
func parseConfirmationReply(text string) (*paymenttypes.Money, string, error) {
|
||||
text = strings.TrimSpace(text)
|
||||
if text == "" {
|
||||
return nil, "empty", errors.New("empty reply")
|
||||
return nil, "empty", merrors.InvalidArgument("empty reply")
|
||||
}
|
||||
parts := strings.Fields(text)
|
||||
if len(parts) < 2 {
|
||||
if len(parts) == 1 && amountPattern.MatchString(parts[0]) {
|
||||
return nil, "missing_currency", errors.New("currency is required")
|
||||
return nil, "missing_currency", merrors.InvalidArgument("currency is required")
|
||||
}
|
||||
return nil, "missing_amount", errors.New("amount is required")
|
||||
return nil, "missing_amount", merrors.InvalidArgument("amount is required")
|
||||
}
|
||||
if len(parts) > 2 {
|
||||
return nil, "format", errors.New("reply format is invalid")
|
||||
return nil, "format", merrors.InvalidArgument("reply format is invalid")
|
||||
}
|
||||
amount := parts[0]
|
||||
currency := parts[1]
|
||||
if !amountPattern.MatchString(amount) {
|
||||
return nil, "invalid_amount", errors.New("amount format is invalid")
|
||||
return nil, "invalid_amount", merrors.InvalidArgument("amount format is invalid")
|
||||
}
|
||||
if !currencyPattern.MatchString(currency) {
|
||||
return nil, "invalid_currency", errors.New("currency format is invalid")
|
||||
return nil, "invalid_currency", merrors.InvalidArgument("currency format is invalid")
|
||||
}
|
||||
return &paymenttypes.Money{
|
||||
Amount: amount,
|
||||
|
||||
@@ -3,7 +3,6 @@ package discovery
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
@@ -13,6 +12,7 @@ import (
|
||||
cons "github.com/tech/sendico/pkg/messaging/consumer"
|
||||
me "github.com/tech/sendico/pkg/messaging/envelope"
|
||||
msgproducer "github.com/tech/sendico/pkg/messaging/producer"
|
||||
"github.com/tech/sendico/pkg/merrors"
|
||||
"github.com/tech/sendico/pkg/mlogger"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
@@ -29,7 +29,7 @@ type Client struct {
|
||||
|
||||
func NewClient(logger mlogger.Logger, msgBroker mb.Broker, producer msg.Producer, sender string) (*Client, error) {
|
||||
if msgBroker == nil {
|
||||
return nil, errors.New("discovery client: broker is nil")
|
||||
return nil, merrors.InvalidArgument("discovery client: broker is nil")
|
||||
}
|
||||
if logger == nil {
|
||||
logger = zap.NewNop()
|
||||
@@ -82,7 +82,7 @@ func (c *Client) Close() {
|
||||
|
||||
func (c *Client) Lookup(ctx context.Context) (LookupResponse, error) {
|
||||
if c == nil || c.producer == nil {
|
||||
return LookupResponse{}, errors.New("discovery client: producer not configured")
|
||||
return LookupResponse{}, merrors.Internal("discovery client: producer not configured")
|
||||
}
|
||||
requestID := uuid.NewString()
|
||||
ch := make(chan LookupResponse, 1)
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/nats-io/nats.go"
|
||||
"github.com/tech/sendico/pkg/merrors"
|
||||
"github.com/tech/sendico/pkg/mlogger"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
@@ -48,7 +49,7 @@ type KVStore struct {
|
||||
|
||||
func NewKVStore(logger mlogger.Logger, js nats.JetStreamContext, bucket string, opts ...KVStoreOption) (*KVStore, error) {
|
||||
if js == nil {
|
||||
return nil, errors.New("discovery kv: jetstream is nil")
|
||||
return nil, merrors.InvalidArgument("discovery kv: jetstream is nil")
|
||||
}
|
||||
if logger == nil {
|
||||
logger = zap.NewNop()
|
||||
@@ -120,11 +121,11 @@ func ensureKVTTL(logger mlogger.Logger, js nats.JetStreamContext, kv nats.KeyVal
|
||||
|
||||
func (s *KVStore) Put(entry RegistryEntry) error {
|
||||
if s == nil || s.kv == nil {
|
||||
return errors.New("discovery kv: not configured")
|
||||
return merrors.Internal("discovery kv: not configured")
|
||||
}
|
||||
key := registryEntryKey(normalizeEntry(entry))
|
||||
if key == "" {
|
||||
return errors.New("discovery kv: entry key is empty")
|
||||
return merrors.InvalidArgument("discovery kv: entry key is empty")
|
||||
}
|
||||
payload, err := json.Marshal(entry)
|
||||
if err != nil {
|
||||
@@ -140,7 +141,7 @@ func (s *KVStore) Put(entry RegistryEntry) error {
|
||||
|
||||
func (s *KVStore) Delete(id string) error {
|
||||
if s == nil || s.kv == nil {
|
||||
return errors.New("discovery kv: not configured")
|
||||
return merrors.Internal("discovery kv: not configured")
|
||||
}
|
||||
key := kvKeyFromRegistryKey(id)
|
||||
if key == "" {
|
||||
@@ -155,7 +156,7 @@ func (s *KVStore) Delete(id string) error {
|
||||
|
||||
func (s *KVStore) WatchAll() (nats.KeyWatcher, error) {
|
||||
if s == nil || s.kv == nil {
|
||||
return nil, errors.New("discovery kv: not configured")
|
||||
return nil, merrors.Internal("discovery kv: not configured")
|
||||
}
|
||||
return s.kv.WatchAll()
|
||||
}
|
||||
|
||||
@@ -2,9 +2,9 @@ package discovery
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
||||
messaging "github.com/tech/sendico/pkg/messaging/envelope"
|
||||
"github.com/tech/sendico/pkg/merrors"
|
||||
"github.com/tech/sendico/pkg/model"
|
||||
)
|
||||
|
||||
@@ -15,7 +15,7 @@ type jsonEnvelope struct {
|
||||
|
||||
func (e *jsonEnvelope) Serialize() ([]byte, error) {
|
||||
if e.payload == nil {
|
||||
return nil, errors.New("discovery envelope payload is nil")
|
||||
return nil, merrors.InvalidArgument("discovery envelope payload is nil")
|
||||
}
|
||||
data, err := json.Marshal(e.payload)
|
||||
if err != nil {
|
||||
|
||||
@@ -3,12 +3,12 @@ package discovery
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/nats-io/nats.go"
|
||||
"github.com/tech/sendico/pkg/merrors"
|
||||
msg "github.com/tech/sendico/pkg/messaging"
|
||||
mb "github.com/tech/sendico/pkg/messaging/broker"
|
||||
cons "github.com/tech/sendico/pkg/messaging/consumer"
|
||||
@@ -50,13 +50,13 @@ type consumerHandler struct {
|
||||
|
||||
func NewRegistryService(logger mlogger.Logger, msgBroker mb.Broker, producer msg.Producer, registry *Registry, sender string, opts ...RegistryOption) (*RegistryService, error) {
|
||||
if msgBroker == nil {
|
||||
return nil, errors.New("discovery registry: broker is nil")
|
||||
return nil, merrors.InvalidArgument("discovery registry: broker is nil", "broker")
|
||||
}
|
||||
if registry == nil {
|
||||
registry = NewRegistry()
|
||||
}
|
||||
if logger == nil {
|
||||
logger = zap.NewNop()
|
||||
return nil, merrors.InvalidArgument("discovery registry: no logger provided", "logger")
|
||||
}
|
||||
logger = logger.Named("discovery_registry")
|
||||
sender = strings.TrimSpace(sender)
|
||||
|
||||
@@ -2,12 +2,12 @@ package discovery
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/nats-io/nats.go"
|
||||
mb "github.com/tech/sendico/pkg/messaging/broker"
|
||||
"github.com/tech/sendico/pkg/merrors"
|
||||
"github.com/tech/sendico/pkg/mlogger"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
@@ -23,22 +23,22 @@ type RegistryWatcher struct {
|
||||
|
||||
func NewRegistryWatcher(logger mlogger.Logger, msgBroker mb.Broker, registry *Registry) (*RegistryWatcher, error) {
|
||||
if msgBroker == nil {
|
||||
return nil, errors.New("discovery watcher: broker is nil")
|
||||
return nil, merrors.InvalidArgument("discovery watcher: broker is nil")
|
||||
}
|
||||
if registry == nil {
|
||||
registry = NewRegistry()
|
||||
}
|
||||
if logger == nil {
|
||||
return nil, errors.New("discovery logger: logger must be provided")
|
||||
return nil, merrors.InvalidArgument("discovery logger: logger must be provided")
|
||||
}
|
||||
logger = logger.Named("discovery_watcher")
|
||||
provider, ok := msgBroker.(jetStreamProvider)
|
||||
if !ok {
|
||||
return nil, errors.New("discovery watcher: jetstream not available")
|
||||
return nil, merrors.Internal("discovery watcher: jetstream not available")
|
||||
}
|
||||
js := provider.JetStream()
|
||||
if js == nil {
|
||||
return nil, errors.New("discovery watcher: jetstream not configured")
|
||||
return nil, merrors.Internal("discovery watcher: jetstream not configured")
|
||||
}
|
||||
store, err := NewKVStore(logger, js, "")
|
||||
if err != nil {
|
||||
@@ -54,7 +54,7 @@ func NewRegistryWatcher(logger mlogger.Logger, msgBroker mb.Broker, registry *Re
|
||||
|
||||
func (w *RegistryWatcher) Start() error {
|
||||
if w == nil || w.kv == nil {
|
||||
return errors.New("discovery watcher: not configured")
|
||||
return merrors.Internal("discovery watcher: not configured")
|
||||
}
|
||||
watcher, err := w.kv.WatchAll()
|
||||
if err != nil {
|
||||
|
||||
@@ -6,6 +6,7 @@ option go_package = "github.com/tech/sendico/pkg/proto/connector/v1;connectorv1"
|
||||
|
||||
import "google/protobuf/struct.proto";
|
||||
import "google/protobuf/timestamp.proto";
|
||||
import "common/describable/v1/describable.proto";
|
||||
import "common/money/v1/money.proto";
|
||||
import "common/pagination/v1/cursor.proto";
|
||||
|
||||
@@ -131,6 +132,7 @@ message Account {
|
||||
google.protobuf.Struct provider_details = 7;
|
||||
google.protobuf.Timestamp created_at = 8;
|
||||
google.protobuf.Timestamp updated_at = 9;
|
||||
common.describable.v1.Describable describable = 10;
|
||||
}
|
||||
|
||||
message Balance {
|
||||
|
||||
@@ -5,6 +5,7 @@ package ledger.v1;
|
||||
option go_package = "github.com/tech/sendico/pkg/proto/ledger/v1;ledgerv1";
|
||||
|
||||
import "google/protobuf/timestamp.proto";
|
||||
import "common/describable/v1/describable.proto";
|
||||
import "common/money/v1/money.proto";
|
||||
|
||||
// ===== Enums =====
|
||||
@@ -55,13 +56,14 @@ message LedgerAccount {
|
||||
map<string, string> metadata = 9;
|
||||
google.protobuf.Timestamp created_at = 10;
|
||||
google.protobuf.Timestamp updated_at = 11;
|
||||
common.describable.v1.Describable describable = 12;
|
||||
}
|
||||
|
||||
// A single posting line (mirrors your PostingLine model)
|
||||
message PostingLine {
|
||||
string ledger_account_ref = 1;
|
||||
common.money.v1.Money money = 2;
|
||||
LineType line_type = 3; // MAIN, FEE, SPREAD, ...
|
||||
string ledger_account_ref = 1;
|
||||
common.money.v1.Money money = 2;
|
||||
LineType line_type = 3; // MAIN, FEE, SPREAD, ...
|
||||
}
|
||||
|
||||
// ===== Requests/Responses =====
|
||||
@@ -75,6 +77,7 @@ message CreateAccountRequest {
|
||||
bool allow_negative = 6;
|
||||
bool is_settlement = 7;
|
||||
map<string, string> metadata = 8;
|
||||
common.describable.v1.Describable describable = 9;
|
||||
}
|
||||
|
||||
message CreateAccountResponse {
|
||||
@@ -124,12 +127,12 @@ message FXRequest {
|
||||
string from_ledger_account_ref = 3;
|
||||
string to_ledger_account_ref = 4;
|
||||
|
||||
common.money.v1.Money from_money = 5; // debited
|
||||
common.money.v1.Money to_money = 6; // credited
|
||||
string rate = 7; // quoted rate as string (snapshot for audit)
|
||||
common.money.v1.Money from_money = 5; // debited
|
||||
common.money.v1.Money to_money = 6; // credited
|
||||
string rate = 7; // quoted rate as string (snapshot for audit)
|
||||
|
||||
string description = 8;
|
||||
repeated PostingLine charges = 9; // FEE/SPREAD lines
|
||||
repeated PostingLine charges = 9; // FEE/SPREAD lines
|
||||
map<string, string> metadata = 10;
|
||||
google.protobuf.Timestamp event_time = 11;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/tech/sendico/pkg/merrors"
|
||||
"github.com/tech/sendico/pkg/model"
|
||||
)
|
||||
|
||||
type LedgerAccountType string
|
||||
@@ -32,6 +33,7 @@ type CreateLedgerAccount struct {
|
||||
AllowNegative bool `json:"allowNegative,omitempty"`
|
||||
IsSettlement bool `json:"isSettlement,omitempty"`
|
||||
Metadata map[string]string `json:"metadata,omitempty"`
|
||||
Describable *model.Describable `json:"describable,omitempty"`
|
||||
}
|
||||
|
||||
func (r *CreateLedgerAccount) Validate() error {
|
||||
|
||||
@@ -12,6 +12,8 @@ type Signup struct {
|
||||
Organization model.Describable `json:"organization"`
|
||||
OrganizationTimeZone string `json:"organizationTimeZone"`
|
||||
OwnerRole model.Describable `json:"ownerRole"`
|
||||
CryptoWallet model.Describable `json:"cryptoWallet"`
|
||||
LedgerWallet model.Describable `json:"ledgerWallet"`
|
||||
}
|
||||
|
||||
// UnmarshalJSON enforces strict parsing to catch malformed or unexpected fields.
|
||||
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
"github.com/tech/sendico/pkg/model"
|
||||
"github.com/tech/sendico/pkg/mservice"
|
||||
"github.com/tech/sendico/pkg/mutil/mzap"
|
||||
describablev1 "github.com/tech/sendico/pkg/proto/common/describable/v1"
|
||||
chainv1 "github.com/tech/sendico/pkg/proto/gateway/chain/v1"
|
||||
"github.com/tech/sendico/server/interface/api/srequest"
|
||||
"github.com/tech/sendico/server/interface/api/sresponse"
|
||||
@@ -252,6 +253,10 @@ func (a *AccountAPI) openOrgWallet(ctx context.Context, org *model.Organization,
|
||||
IdempotencyKey: uuid.NewString(),
|
||||
OrganizationRef: org.ID.Hex(),
|
||||
OwnerRef: org.ID.Hex(),
|
||||
Describable: &describablev1.Describable{
|
||||
Name: sr.CryptoWallet.Name,
|
||||
Description: sr.CryptoWallet.Description,
|
||||
},
|
||||
Asset: a.chainAsset,
|
||||
Metadata: map[string]string{
|
||||
"source": "signup",
|
||||
|
||||
@@ -15,14 +15,20 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
errConfirmationNotFound = errors.New("confirmation not found or expired")
|
||||
errConfirmationUsed = errors.New("confirmation already used")
|
||||
errConfirmationMismatch = errors.New("confirmation code mismatch")
|
||||
errConfirmationAttemptsExceeded = errors.New("confirmation attempts exceeded")
|
||||
errConfirmationCooldown = errors.New("confirmation cooldown active")
|
||||
errConfirmationResendLimit = errors.New("confirmation resend limit reached")
|
||||
errConfirmationNotFound confirmationError = "confirmation not found or expired"
|
||||
errConfirmationUsed confirmationError = "confirmation already used"
|
||||
errConfirmationMismatch confirmationError = "confirmation code mismatch"
|
||||
errConfirmationAttemptsExceeded confirmationError = "confirmation attempts exceeded"
|
||||
errConfirmationCooldown confirmationError = "confirmation cooldown active"
|
||||
errConfirmationResendLimit confirmationError = "confirmation resend limit reached"
|
||||
)
|
||||
|
||||
type confirmationError string
|
||||
|
||||
func (e confirmationError) Error() string {
|
||||
return string(e)
|
||||
}
|
||||
|
||||
type ConfirmationStore struct {
|
||||
db confirmation.DB
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package ledgerapiimp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/tech/sendico/pkg/api/http/response"
|
||||
"github.com/tech/sendico/pkg/merrors"
|
||||
"github.com/tech/sendico/pkg/model"
|
||||
"github.com/tech/sendico/pkg/mservice"
|
||||
ledgerv1 "github.com/tech/sendico/pkg/proto/ledger/v1"
|
||||
@@ -24,7 +24,7 @@ func (a *LedgerAPI) getBalance(r *http.Request, account *model.Account, token *s
|
||||
|
||||
accountRef := strings.TrimSpace(a.aph.GetID(r))
|
||||
if accountRef == "" {
|
||||
return response.BadReference(a.logger, a.Name(), a.aph.Name(), a.aph.GetID(r), errors.New("ledger account reference is required"))
|
||||
return response.BadReference(a.logger, a.Name(), a.aph.Name(), a.aph.GetID(r), merrors.InvalidArgument("ledger account reference is required"))
|
||||
}
|
||||
|
||||
ctx := r.Context()
|
||||
@@ -38,7 +38,7 @@ func (a *LedgerAPI) getBalance(r *http.Request, account *model.Account, token *s
|
||||
return response.AccessDenied(a.logger, a.Name(), "ledger balance read permission denied")
|
||||
}
|
||||
if a.client == nil {
|
||||
return response.Internal(a.logger, mservice.Ledger, errors.New("ledger client is not configured"))
|
||||
return response.Internal(a.logger, mservice.Ledger, merrors.Internal("ledger client is not configured"))
|
||||
}
|
||||
|
||||
resp, err := a.client.GetBalance(ctx, &ledgerv1.GetBalanceRequest{
|
||||
|
||||
@@ -2,7 +2,6 @@ package ledgerapiimp
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
@@ -10,6 +9,7 @@ import (
|
||||
"github.com/tech/sendico/pkg/merrors"
|
||||
"github.com/tech/sendico/pkg/model"
|
||||
"github.com/tech/sendico/pkg/mservice"
|
||||
describablev1 "github.com/tech/sendico/pkg/proto/common/describable/v1"
|
||||
ledgerv1 "github.com/tech/sendico/pkg/proto/ledger/v1"
|
||||
"github.com/tech/sendico/server/interface/api/srequest"
|
||||
"github.com/tech/sendico/server/interface/api/sresponse"
|
||||
@@ -52,7 +52,25 @@ func (a *LedgerAPI) createAccount(r *http.Request, account *model.Account, token
|
||||
}
|
||||
|
||||
if a.client == nil {
|
||||
return response.Internal(a.logger, mservice.Ledger, errors.New("ledger client is not configured"))
|
||||
return response.Internal(a.logger, mservice.Ledger, merrors.Internal("ledger client is not configured"))
|
||||
}
|
||||
|
||||
var describable *describablev1.Describable
|
||||
if payload.Describable != nil {
|
||||
name := strings.TrimSpace(payload.Describable.Name)
|
||||
var description *string
|
||||
if payload.Describable.Description != nil {
|
||||
trimmed := strings.TrimSpace(*payload.Describable.Description)
|
||||
if trimmed != "" {
|
||||
description = &trimmed
|
||||
}
|
||||
}
|
||||
if name != "" || description != nil {
|
||||
describable = &describablev1.Describable{
|
||||
Name: name,
|
||||
Description: description,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resp, err := a.client.CreateAccount(ctx, &ledgerv1.CreateAccountRequest{
|
||||
@@ -64,6 +82,7 @@ func (a *LedgerAPI) createAccount(r *http.Request, account *model.Account, token
|
||||
AllowNegative: payload.AllowNegative,
|
||||
IsSettlement: payload.IsSettlement,
|
||||
Metadata: payload.Metadata,
|
||||
Describable: describable,
|
||||
})
|
||||
if err != nil {
|
||||
a.logger.Warn("Failed to create ledger account", zap.Error(err), zap.String("organization_ref", orgRef.Hex()))
|
||||
@@ -82,6 +101,20 @@ func decodeLedgerAccountCreatePayload(r *http.Request) (*srequest.CreateLedgerAc
|
||||
}
|
||||
payload.AccountCode = strings.TrimSpace(payload.AccountCode)
|
||||
payload.Currency = strings.ToUpper(strings.TrimSpace(payload.Currency))
|
||||
if payload.Describable != nil {
|
||||
payload.Describable.Name = strings.TrimSpace(payload.Describable.Name)
|
||||
if payload.Describable.Description != nil {
|
||||
trimmed := strings.TrimSpace(*payload.Describable.Description)
|
||||
if trimmed == "" {
|
||||
payload.Describable.Description = nil
|
||||
} else {
|
||||
payload.Describable.Description = &trimmed
|
||||
}
|
||||
}
|
||||
if payload.Describable.Name == "" && payload.Describable.Description == nil {
|
||||
payload.Describable = nil
|
||||
}
|
||||
}
|
||||
if len(payload.Metadata) == 0 {
|
||||
payload.Metadata = nil
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package ledgerapiimp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/tech/sendico/pkg/api/http/response"
|
||||
"github.com/tech/sendico/pkg/merrors"
|
||||
"github.com/tech/sendico/pkg/model"
|
||||
"github.com/tech/sendico/pkg/mservice"
|
||||
ledgerv1 "github.com/tech/sendico/pkg/proto/ledger/v1"
|
||||
@@ -32,7 +32,7 @@ func (a *LedgerAPI) listAccounts(r *http.Request, account *model.Account, token
|
||||
return response.AccessDenied(a.logger, a.Name(), "ledger accounts read permission denied")
|
||||
}
|
||||
if a.client == nil {
|
||||
return response.Internal(a.logger, mservice.Ledger, errors.New("ledger client is not configured"))
|
||||
return response.Internal(a.logger, mservice.Ledger, merrors.Internal("ledger client is not configured"))
|
||||
}
|
||||
|
||||
resp, err := a.client.ListAccounts(ctx, &ledgerv1.ListAccountsRequest{
|
||||
|
||||
@@ -3,13 +3,13 @@ package paymentapiimp
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/tech/sendico/pkg/api/http/response"
|
||||
"github.com/tech/sendico/pkg/discovery"
|
||||
me "github.com/tech/sendico/pkg/messaging/envelope"
|
||||
"github.com/tech/sendico/pkg/merrors"
|
||||
"github.com/tech/sendico/pkg/model"
|
||||
"github.com/tech/sendico/server/interface/api/sresponse"
|
||||
mutil "github.com/tech/sendico/server/internal/mutil/param"
|
||||
@@ -21,7 +21,7 @@ const discoveryLookupTimeout = 3 * time.Second
|
||||
|
||||
func (a *PaymentAPI) listDiscoveryRegistry(r *http.Request, account *model.Account, _ *sresponse.TokenData) http.HandlerFunc {
|
||||
if a.discovery == nil {
|
||||
return response.Internal(a.logger, a.Name(), errors.New("discovery client is not configured"))
|
||||
return response.Internal(a.logger, a.Name(), merrors.Internal("discovery client is not configured"))
|
||||
}
|
||||
|
||||
orgRef, err := a.oph.GetRef(r)
|
||||
@@ -55,7 +55,7 @@ func (a *PaymentAPI) listDiscoveryRegistry(r *http.Request, account *model.Accou
|
||||
|
||||
func (a *PaymentAPI) getDiscoveryRefresh(r *http.Request, account *model.Account, _ *sresponse.TokenData) http.HandlerFunc {
|
||||
if a.refreshConsumer == nil {
|
||||
return response.Internal(a.logger, a.Name(), errors.New("discovery refresh consumer is not configured"))
|
||||
return response.Internal(a.logger, a.Name(), merrors.Internal("discovery refresh consumer is not configured"))
|
||||
}
|
||||
|
||||
orgRef, err := a.oph.GetRef(r)
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package walletapiimp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/tech/sendico/pkg/api/http/response"
|
||||
"github.com/tech/sendico/pkg/merrors"
|
||||
"github.com/tech/sendico/pkg/model"
|
||||
"github.com/tech/sendico/pkg/mservice"
|
||||
chainv1 "github.com/tech/sendico/pkg/proto/gateway/chain/v1"
|
||||
@@ -23,7 +23,7 @@ func (a *WalletAPI) getWalletBalance(r *http.Request, account *model.Account, to
|
||||
}
|
||||
walletRef := strings.TrimSpace(a.wph.GetID(r))
|
||||
if walletRef == "" {
|
||||
return response.BadReference(a.logger, a.Name(), a.wph.Name(), a.wph.GetID(r), errors.New("wallet reference is required"))
|
||||
return response.BadReference(a.logger, a.Name(), a.wph.Name(), a.wph.GetID(r), merrors.InvalidArgument("wallet reference is required"))
|
||||
}
|
||||
|
||||
ctx := r.Context()
|
||||
@@ -37,7 +37,7 @@ func (a *WalletAPI) getWalletBalance(r *http.Request, account *model.Account, to
|
||||
return response.AccessDenied(a.logger, a.Name(), "wallet balance read permission denied")
|
||||
}
|
||||
if a.chainGateway == nil {
|
||||
return response.Internal(a.logger, mservice.ChainGateway, errors.New("chain gateway client is not configured"))
|
||||
return response.Internal(a.logger, mservice.ChainGateway, merrors.Internal("chain gateway client is not configured"))
|
||||
}
|
||||
|
||||
resp, err := a.chainGateway.GetWalletBalance(ctx, &chainv1.GetWalletBalanceRequest{WalletRef: walletRef})
|
||||
@@ -49,7 +49,7 @@ func (a *WalletAPI) getWalletBalance(r *http.Request, account *model.Account, to
|
||||
bal := resp.GetBalance()
|
||||
if bal == nil {
|
||||
a.logger.Warn("Wallet balance missing in response", zap.String("wallet_ref", walletRef))
|
||||
return response.Auto(a.logger, mservice.ChainGateway, errors.New("wallet balance not available"))
|
||||
return response.Auto(a.logger, mservice.ChainGateway, merrors.Internal("wallet balance not available"))
|
||||
}
|
||||
|
||||
return sresponse.WalletBalance(a.logger, bal, token)
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package walletapiimp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/tech/sendico/pkg/api/http/response"
|
||||
"github.com/tech/sendico/pkg/merrors"
|
||||
"github.com/tech/sendico/pkg/model"
|
||||
"github.com/tech/sendico/pkg/mservice"
|
||||
chainv1 "github.com/tech/sendico/pkg/proto/gateway/chain/v1"
|
||||
@@ -33,7 +33,7 @@ func (a *WalletAPI) listWallets(r *http.Request, account *model.Account, token *
|
||||
return response.AccessDenied(a.logger, a.Name(), "wallets read permission denied")
|
||||
}
|
||||
if a.chainGateway == nil {
|
||||
return response.Internal(a.logger, mservice.ChainGateway, errors.New("chain gateway client is not configured"))
|
||||
return response.Internal(a.logger, mservice.ChainGateway, merrors.Internal("chain gateway client is not configured"))
|
||||
}
|
||||
|
||||
req := &chainv1.ListManagedWalletsRequest{
|
||||
|
||||
@@ -12,14 +12,18 @@ part 'signup.g.dart';
|
||||
class SignupRequest {
|
||||
final AccountData account;
|
||||
final DescribableDTO organization;
|
||||
final String organizationTimeZone;
|
||||
final DescribableDTO ownerRole;
|
||||
final DescribableDTO cryptoWallet;
|
||||
final DescribableDTO ledgerWallet;
|
||||
final String organizationTimeZone;
|
||||
|
||||
const SignupRequest({
|
||||
required this.account,
|
||||
required this.organization,
|
||||
required this.organizationTimeZone,
|
||||
required this.ownerRole,
|
||||
required this.cryptoWallet,
|
||||
required this.ledgerWallet,
|
||||
});
|
||||
|
||||
factory SignupRequest.build({
|
||||
@@ -27,11 +31,15 @@ class SignupRequest {
|
||||
required Describable organization,
|
||||
required String organizationTimeZone,
|
||||
required Describable ownerRole,
|
||||
required Describable cryptoWallet,
|
||||
required Describable ledgerWallet,
|
||||
}) => SignupRequest(
|
||||
account: account,
|
||||
organization: organization.toDTO(),
|
||||
organizationTimeZone: organizationTimeZone,
|
||||
ownerRole: ownerRole.toDTO(),
|
||||
cryptoWallet: cryptoWallet.toDTO(),
|
||||
ledgerWallet: ledgerWallet.toDTO(),
|
||||
);
|
||||
|
||||
factory SignupRequest.fromJson(Map<String, dynamic> json) => _$SignupRequestFromJson(json);
|
||||
|
||||
@@ -165,8 +165,10 @@ class AccountProvider extends ChangeNotifier {
|
||||
Future<void> signup({
|
||||
required AccountData account,
|
||||
required Describable organization,
|
||||
required String timezone,
|
||||
required Describable ownerRole,
|
||||
required Describable cryptoWallet,
|
||||
required Describable ledgerWallet,
|
||||
required String timezone,
|
||||
}) async {
|
||||
_setResource(_resource.copyWith(isLoading: true, error: null));
|
||||
try {
|
||||
@@ -176,6 +178,8 @@ class AccountProvider extends ChangeNotifier {
|
||||
organization: organization,
|
||||
organizationTimeZone: timezone,
|
||||
ownerRole: ownerRole,
|
||||
cryptoWallet: cryptoWallet,
|
||||
ledgerWallet: ledgerWallet,
|
||||
),
|
||||
);
|
||||
// Signup might not automatically log in the user,
|
||||
|
||||
@@ -474,6 +474,10 @@
|
||||
"optional": "optional",
|
||||
"ownerRole": "Organization Owner",
|
||||
"ownerRoleDescription": "This role is granted to the organization’s creator, providing full administrative privileges",
|
||||
"cryptoWallet": "Crypto",
|
||||
"cryptoWalletDesc": "TRC-20 USDT",
|
||||
"ledgerWallet": "Internal",
|
||||
"ledgerWalletDesc": "RUB wallet for settlements",
|
||||
"accountVerificationFailed": "Oops! We failed to verify your account. Please, contact support",
|
||||
"verifyAccount": "Account Verification",
|
||||
"verificationFailed": "Verification Failed",
|
||||
|
||||
@@ -475,6 +475,10 @@
|
||||
|
||||
"ownerRole": "Владелец организации",
|
||||
"ownerRoleDescription": "Эта роль предоставляется создателю организации и даёт ему полные административные права",
|
||||
"cryptoWallet": "Крипто",
|
||||
"cryptoWalletDesc": "TRC-20 USDT",
|
||||
"ledgerWallet": "Внутренний",
|
||||
"ledgerWalletDesc": "RUB кошелек для расчетов",
|
||||
"accountVerificationFailed": "Упс! Не удалось подтвердить ваш аккаунт. Пожалуйста, свяжитесь с поддержкой.",
|
||||
"verifyAccount": "Подтвердить аккаунт",
|
||||
"verificationFailed": "Ошибка подтверждения",
|
||||
|
||||
@@ -71,6 +71,14 @@ class SignUpFormState extends State<SignUpForm> {
|
||||
name: locs.ownerRole,
|
||||
description: locs.ownerRoleDescription,
|
||||
),
|
||||
cryptoWallet: newDescribable(
|
||||
name: locs.cryptoWallet,
|
||||
description: locs.cryptoWalletDesc,
|
||||
),
|
||||
ledgerWallet: newDescribable(
|
||||
name: locs.ledgerWallet,
|
||||
description: locs.ledgerWalletDesc,
|
||||
),
|
||||
);
|
||||
onSignUp();
|
||||
return 'ok';
|
||||
|
||||
Reference in New Issue
Block a user