2019-10-17 15:04:50 +01:00
|
|
|
// Copyright (C) 2019 Storj Labs, Inc.
|
|
|
|
// See LICENSE for copying information.
|
|
|
|
|
|
|
|
package satellitedb
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"math/big"
|
2019-10-23 13:04:54 +01:00
|
|
|
"time"
|
2019-10-17 15:04:50 +01:00
|
|
|
|
satellite/payments: specialized type for monetary amounts
Why: big.Float is not an ideal type for dealing with monetary amounts,
because no matter how high the precision, some non-integer decimal
values can not be represented exactly in base-2 floating point. Also,
storing gob-encoded big.Float values in the database makes it very hard
to use those values in meaningful queries, making it difficult to do
any sort of analysis on billing.
For better accuracy, then, we can just represent monetary values as
integers (in whatever base units are appropriate for the currency). For
example, STORJ tokens or Bitcoins can not be split into pieces smaller
than 10^-8, so we can store amounts of STORJ or BTC with precision
simply by moving the decimal point 8 digits to the right. For USD values
(assuming we don't want to deal with fractional cents), we can move the
decimal point 2 digits to the right.
To make it easier and less error-prone to deal with the math involved, I
introduce here a new type, monetary.Amount, instances of which have an
associated value _and_ a currency.
Change-Id: I03395d52f0e2473cf301361f6033722b54640265
2021-08-10 23:29:50 +01:00
|
|
|
"github.com/shopspring/decimal"
|
2019-10-17 15:04:50 +01:00
|
|
|
"github.com/zeebo/errs"
|
|
|
|
|
2020-03-30 10:08:50 +01:00
|
|
|
"storj.io/common/uuid"
|
2019-10-17 15:04:50 +01:00
|
|
|
"storj.io/storj/satellite/payments/coinpayments"
|
satellite/payments: specialized type for monetary amounts
Why: big.Float is not an ideal type for dealing with monetary amounts,
because no matter how high the precision, some non-integer decimal
values can not be represented exactly in base-2 floating point. Also,
storing gob-encoded big.Float values in the database makes it very hard
to use those values in meaningful queries, making it difficult to do
any sort of analysis on billing.
For better accuracy, then, we can just represent monetary values as
integers (in whatever base units are appropriate for the currency). For
example, STORJ tokens or Bitcoins can not be split into pieces smaller
than 10^-8, so we can store amounts of STORJ or BTC with precision
simply by moving the decimal point 8 digits to the right. For USD values
(assuming we don't want to deal with fractional cents), we can move the
decimal point 2 digits to the right.
To make it easier and less error-prone to deal with the math involved, I
introduce here a new type, monetary.Amount, instances of which have an
associated value _and_ a currency.
Change-Id: I03395d52f0e2473cf301361f6033722b54640265
2021-08-10 23:29:50 +01:00
|
|
|
"storj.io/storj/satellite/payments/monetary"
|
2019-10-17 15:04:50 +01:00
|
|
|
"storj.io/storj/satellite/payments/stripecoinpayments"
|
2020-01-15 02:29:51 +00:00
|
|
|
"storj.io/storj/satellite/satellitedb/dbx"
|
2019-10-17 15:04:50 +01:00
|
|
|
)
|
|
|
|
|
2019-11-05 13:16:02 +00:00
|
|
|
// ensure that coinpaymentsTransactions implements stripecoinpayments.TransactionsDB.
|
|
|
|
var _ stripecoinpayments.TransactionsDB = (*coinPaymentsTransactions)(nil)
|
2019-10-17 15:04:50 +01:00
|
|
|
|
2019-10-29 16:04:34 +00:00
|
|
|
// applyBalanceIntentState defines states of the apply balance intents.
|
|
|
|
type applyBalanceIntentState int
|
|
|
|
|
|
|
|
const (
|
|
|
|
// apply balance intent waits to be applied.
|
|
|
|
applyBalanceIntentStateUnapplied applyBalanceIntentState = 0
|
|
|
|
// transaction which balance intent points to has been consumed.
|
|
|
|
applyBalanceIntentStateConsumed applyBalanceIntentState = 1
|
|
|
|
)
|
|
|
|
|
|
|
|
// Int returns intent state as int.
|
|
|
|
func (intent applyBalanceIntentState) Int() int {
|
|
|
|
return int(intent)
|
|
|
|
}
|
|
|
|
|
2019-11-05 13:16:02 +00:00
|
|
|
// coinPaymentsTransactions is CoinPayments transactions DB.
|
2019-10-17 15:04:50 +01:00
|
|
|
//
|
|
|
|
// architecture: Database
|
2019-11-05 13:16:02 +00:00
|
|
|
type coinPaymentsTransactions struct {
|
2019-12-14 02:29:54 +00:00
|
|
|
db *satelliteDB
|
2019-10-17 15:04:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Insert inserts new coinpayments transaction into DB.
|
2019-11-15 14:59:39 +00:00
|
|
|
func (db *coinPaymentsTransactions) Insert(ctx context.Context, tx stripecoinpayments.Transaction) (_ *stripecoinpayments.Transaction, err error) {
|
|
|
|
defer mon.Task()(&ctx)(&err)
|
|
|
|
|
satellite/payments: specialized type for monetary amounts
Why: big.Float is not an ideal type for dealing with monetary amounts,
because no matter how high the precision, some non-integer decimal
values can not be represented exactly in base-2 floating point. Also,
storing gob-encoded big.Float values in the database makes it very hard
to use those values in meaningful queries, making it difficult to do
any sort of analysis on billing.
For better accuracy, then, we can just represent monetary values as
integers (in whatever base units are appropriate for the currency). For
example, STORJ tokens or Bitcoins can not be split into pieces smaller
than 10^-8, so we can store amounts of STORJ or BTC with precision
simply by moving the decimal point 8 digits to the right. For USD values
(assuming we don't want to deal with fractional cents), we can move the
decimal point 2 digits to the right.
To make it easier and less error-prone to deal with the math involved, I
introduce here a new type, monetary.Amount, instances of which have an
associated value _and_ a currency.
Change-Id: I03395d52f0e2473cf301361f6033722b54640265
2021-08-10 23:29:50 +01:00
|
|
|
amount, err := tx.Amount.AsBigFloat().GobEncode()
|
2019-10-17 15:04:50 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, errs.Wrap(err)
|
|
|
|
}
|
satellite/payments: specialized type for monetary amounts
Why: big.Float is not an ideal type for dealing with monetary amounts,
because no matter how high the precision, some non-integer decimal
values can not be represented exactly in base-2 floating point. Also,
storing gob-encoded big.Float values in the database makes it very hard
to use those values in meaningful queries, making it difficult to do
any sort of analysis on billing.
For better accuracy, then, we can just represent monetary values as
integers (in whatever base units are appropriate for the currency). For
example, STORJ tokens or Bitcoins can not be split into pieces smaller
than 10^-8, so we can store amounts of STORJ or BTC with precision
simply by moving the decimal point 8 digits to the right. For USD values
(assuming we don't want to deal with fractional cents), we can move the
decimal point 2 digits to the right.
To make it easier and less error-prone to deal with the math involved, I
introduce here a new type, monetary.Amount, instances of which have an
associated value _and_ a currency.
Change-Id: I03395d52f0e2473cf301361f6033722b54640265
2021-08-10 23:29:50 +01:00
|
|
|
received, err := tx.Received.AsBigFloat().GobEncode()
|
2019-10-17 15:04:50 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, errs.Wrap(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
dbxCPTX, err := db.db.Create_CoinpaymentsTransaction(ctx,
|
|
|
|
dbx.CoinpaymentsTransaction_Id(tx.ID.String()),
|
|
|
|
dbx.CoinpaymentsTransaction_UserId(tx.AccountID[:]),
|
|
|
|
dbx.CoinpaymentsTransaction_Address(tx.Address),
|
|
|
|
dbx.CoinpaymentsTransaction_Amount(amount),
|
|
|
|
dbx.CoinpaymentsTransaction_Received(received),
|
|
|
|
dbx.CoinpaymentsTransaction_Status(tx.Status.Int()),
|
|
|
|
dbx.CoinpaymentsTransaction_Key(tx.Key),
|
2019-11-15 14:59:39 +00:00
|
|
|
dbx.CoinpaymentsTransaction_Timeout(int(tx.Timeout.Seconds())),
|
2019-10-17 15:04:50 +01:00
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return fromDBXCoinpaymentsTransaction(dbxCPTX)
|
|
|
|
}
|
|
|
|
|
2019-10-23 13:04:54 +01:00
|
|
|
// Update updates status and received for set of transactions.
|
2019-11-15 14:59:39 +00:00
|
|
|
func (db *coinPaymentsTransactions) Update(ctx context.Context, updates []stripecoinpayments.TransactionUpdate, applies coinpayments.TransactionIDList) (err error) {
|
|
|
|
defer mon.Task()(&ctx)(&err)
|
|
|
|
|
2019-10-23 13:04:54 +01:00
|
|
|
if len(updates) == 0 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return db.db.WithTx(ctx, func(ctx context.Context, tx *dbx.Tx) error {
|
|
|
|
for _, update := range updates {
|
satellite/payments: specialized type for monetary amounts
Why: big.Float is not an ideal type for dealing with monetary amounts,
because no matter how high the precision, some non-integer decimal
values can not be represented exactly in base-2 floating point. Also,
storing gob-encoded big.Float values in the database makes it very hard
to use those values in meaningful queries, making it difficult to do
any sort of analysis on billing.
For better accuracy, then, we can just represent monetary values as
integers (in whatever base units are appropriate for the currency). For
example, STORJ tokens or Bitcoins can not be split into pieces smaller
than 10^-8, so we can store amounts of STORJ or BTC with precision
simply by moving the decimal point 8 digits to the right. For USD values
(assuming we don't want to deal with fractional cents), we can move the
decimal point 2 digits to the right.
To make it easier and less error-prone to deal with the math involved, I
introduce here a new type, monetary.Amount, instances of which have an
associated value _and_ a currency.
Change-Id: I03395d52f0e2473cf301361f6033722b54640265
2021-08-10 23:29:50 +01:00
|
|
|
received, err := update.Received.AsBigFloat().GobEncode()
|
2019-10-23 13:04:54 +01:00
|
|
|
if err != nil {
|
|
|
|
return errs.Wrap(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err = tx.Update_CoinpaymentsTransaction_By_Id(ctx,
|
|
|
|
dbx.CoinpaymentsTransaction_Id(update.TransactionID.String()),
|
|
|
|
dbx.CoinpaymentsTransaction_Update_Fields{
|
|
|
|
Received: dbx.CoinpaymentsTransaction_Received(received),
|
|
|
|
Status: dbx.CoinpaymentsTransaction_Status(update.Status.Int()),
|
|
|
|
},
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-29 16:04:34 +00:00
|
|
|
for _, txID := range applies {
|
2020-07-17 21:13:25 +01:00
|
|
|
query := db.db.Rebind(`INSERT INTO stripecoinpayments_apply_balance_intents ( tx_id, state, created_at )
|
|
|
|
VALUES ( ?, ?, ? ) ON CONFLICT DO NOTHING`)
|
|
|
|
_, err = tx.Tx.ExecContext(ctx, query, txID.String(), applyBalanceIntentStateUnapplied.Int(), db.db.Hooks.Now().UTC())
|
2019-10-29 16:04:34 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-23 13:04:54 +01:00
|
|
|
return nil
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2019-10-29 16:04:34 +00:00
|
|
|
// Consume marks transaction as consumed, so it won't participate in apply account balance loop.
|
2019-11-15 14:59:39 +00:00
|
|
|
func (db *coinPaymentsTransactions) Consume(ctx context.Context, id coinpayments.TransactionID) (err error) {
|
|
|
|
defer mon.Task()(&ctx)(&err)
|
|
|
|
|
2020-02-13 18:08:45 +00:00
|
|
|
query := db.db.Rebind(`
|
|
|
|
WITH intent AS (
|
|
|
|
SELECT tx_id, state FROM stripecoinpayments_apply_balance_intents WHERE tx_id = ?
|
|
|
|
), updated AS (
|
|
|
|
UPDATE stripecoinpayments_apply_balance_intents AS ints
|
|
|
|
SET
|
|
|
|
state = ?
|
|
|
|
FROM intent
|
|
|
|
WHERE intent.tx_id = ints.tx_id AND ints.state = ?
|
|
|
|
RETURNING 1
|
|
|
|
)
|
|
|
|
SELECT EXISTS(SELECT 1 FROM intent) AS intent_exists, EXISTS(SELECT 1 FROM updated) AS intent_consumed;
|
|
|
|
`)
|
|
|
|
|
|
|
|
row := db.db.QueryRowContext(ctx, query, id, applyBalanceIntentStateConsumed, applyBalanceIntentStateUnapplied)
|
|
|
|
|
|
|
|
var exists, consumed bool
|
|
|
|
if err = row.Scan(&exists, &consumed); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if !exists {
|
|
|
|
return errs.New("can not consume transaction without apply balance intent")
|
|
|
|
}
|
|
|
|
if !consumed {
|
|
|
|
return stripecoinpayments.ErrTransactionConsumed
|
|
|
|
}
|
|
|
|
|
2019-10-29 16:04:34 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-11-15 14:59:39 +00:00
|
|
|
// LockRate locks conversion rate for transaction.
|
satellite/payments: specialized type for monetary amounts
Why: big.Float is not an ideal type for dealing with monetary amounts,
because no matter how high the precision, some non-integer decimal
values can not be represented exactly in base-2 floating point. Also,
storing gob-encoded big.Float values in the database makes it very hard
to use those values in meaningful queries, making it difficult to do
any sort of analysis on billing.
For better accuracy, then, we can just represent monetary values as
integers (in whatever base units are appropriate for the currency). For
example, STORJ tokens or Bitcoins can not be split into pieces smaller
than 10^-8, so we can store amounts of STORJ or BTC with precision
simply by moving the decimal point 8 digits to the right. For USD values
(assuming we don't want to deal with fractional cents), we can move the
decimal point 2 digits to the right.
To make it easier and less error-prone to deal with the math involved, I
introduce here a new type, monetary.Amount, instances of which have an
associated value _and_ a currency.
Change-Id: I03395d52f0e2473cf301361f6033722b54640265
2021-08-10 23:29:50 +01:00
|
|
|
func (db *coinPaymentsTransactions) LockRate(ctx context.Context, id coinpayments.TransactionID, rate decimal.Decimal) (err error) {
|
2019-11-15 14:59:39 +00:00
|
|
|
defer mon.Task()(&ctx)(&err)
|
|
|
|
|
satellite/payments: specialized type for monetary amounts
Why: big.Float is not an ideal type for dealing with monetary amounts,
because no matter how high the precision, some non-integer decimal
values can not be represented exactly in base-2 floating point. Also,
storing gob-encoded big.Float values in the database makes it very hard
to use those values in meaningful queries, making it difficult to do
any sort of analysis on billing.
For better accuracy, then, we can just represent monetary values as
integers (in whatever base units are appropriate for the currency). For
example, STORJ tokens or Bitcoins can not be split into pieces smaller
than 10^-8, so we can store amounts of STORJ or BTC with precision
simply by moving the decimal point 8 digits to the right. For USD values
(assuming we don't want to deal with fractional cents), we can move the
decimal point 2 digits to the right.
To make it easier and less error-prone to deal with the math involved, I
introduce here a new type, monetary.Amount, instances of which have an
associated value _and_ a currency.
Change-Id: I03395d52f0e2473cf301361f6033722b54640265
2021-08-10 23:29:50 +01:00
|
|
|
buff, err := rate.BigFloat().GobEncode()
|
2019-11-15 14:59:39 +00:00
|
|
|
if err != nil {
|
|
|
|
return errs.Wrap(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err = db.db.Create_StripecoinpaymentsTxConversionRate(ctx,
|
|
|
|
dbx.StripecoinpaymentsTxConversionRate_TxId(id.String()),
|
|
|
|
dbx.StripecoinpaymentsTxConversionRate_Rate(buff))
|
|
|
|
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetLockedRate returns locked conversion rate for transaction or error if non exists.
|
satellite/payments: specialized type for monetary amounts
Why: big.Float is not an ideal type for dealing with monetary amounts,
because no matter how high the precision, some non-integer decimal
values can not be represented exactly in base-2 floating point. Also,
storing gob-encoded big.Float values in the database makes it very hard
to use those values in meaningful queries, making it difficult to do
any sort of analysis on billing.
For better accuracy, then, we can just represent monetary values as
integers (in whatever base units are appropriate for the currency). For
example, STORJ tokens or Bitcoins can not be split into pieces smaller
than 10^-8, so we can store amounts of STORJ or BTC with precision
simply by moving the decimal point 8 digits to the right. For USD values
(assuming we don't want to deal with fractional cents), we can move the
decimal point 2 digits to the right.
To make it easier and less error-prone to deal with the math involved, I
introduce here a new type, monetary.Amount, instances of which have an
associated value _and_ a currency.
Change-Id: I03395d52f0e2473cf301361f6033722b54640265
2021-08-10 23:29:50 +01:00
|
|
|
func (db *coinPaymentsTransactions) GetLockedRate(ctx context.Context, id coinpayments.TransactionID) (_ decimal.Decimal, err error) {
|
2019-11-15 14:59:39 +00:00
|
|
|
defer mon.Task()(&ctx)(&err)
|
|
|
|
|
|
|
|
dbxRate, err := db.db.Get_StripecoinpaymentsTxConversionRate_By_TxId(ctx,
|
|
|
|
dbx.StripecoinpaymentsTxConversionRate_TxId(id.String()),
|
|
|
|
)
|
|
|
|
if err != nil {
|
satellite/payments: specialized type for monetary amounts
Why: big.Float is not an ideal type for dealing with monetary amounts,
because no matter how high the precision, some non-integer decimal
values can not be represented exactly in base-2 floating point. Also,
storing gob-encoded big.Float values in the database makes it very hard
to use those values in meaningful queries, making it difficult to do
any sort of analysis on billing.
For better accuracy, then, we can just represent monetary values as
integers (in whatever base units are appropriate for the currency). For
example, STORJ tokens or Bitcoins can not be split into pieces smaller
than 10^-8, so we can store amounts of STORJ or BTC with precision
simply by moving the decimal point 8 digits to the right. For USD values
(assuming we don't want to deal with fractional cents), we can move the
decimal point 2 digits to the right.
To make it easier and less error-prone to deal with the math involved, I
introduce here a new type, monetary.Amount, instances of which have an
associated value _and_ a currency.
Change-Id: I03395d52f0e2473cf301361f6033722b54640265
2021-08-10 23:29:50 +01:00
|
|
|
return decimal.Decimal{}, err
|
2019-11-15 14:59:39 +00:00
|
|
|
}
|
|
|
|
|
satellite/payments: specialized type for monetary amounts
Why: big.Float is not an ideal type for dealing with monetary amounts,
because no matter how high the precision, some non-integer decimal
values can not be represented exactly in base-2 floating point. Also,
storing gob-encoded big.Float values in the database makes it very hard
to use those values in meaningful queries, making it difficult to do
any sort of analysis on billing.
For better accuracy, then, we can just represent monetary values as
integers (in whatever base units are appropriate for the currency). For
example, STORJ tokens or Bitcoins can not be split into pieces smaller
than 10^-8, so we can store amounts of STORJ or BTC with precision
simply by moving the decimal point 8 digits to the right. For USD values
(assuming we don't want to deal with fractional cents), we can move the
decimal point 2 digits to the right.
To make it easier and less error-prone to deal with the math involved, I
introduce here a new type, monetary.Amount, instances of which have an
associated value _and_ a currency.
Change-Id: I03395d52f0e2473cf301361f6033722b54640265
2021-08-10 23:29:50 +01:00
|
|
|
var rateF big.Float
|
|
|
|
if err = rateF.GobDecode(dbxRate.Rate); err != nil {
|
|
|
|
return decimal.Decimal{}, errs.Wrap(err)
|
|
|
|
}
|
|
|
|
rate, err := monetary.DecimalFromBigFloat(&rateF)
|
|
|
|
if err != nil {
|
|
|
|
return decimal.Decimal{}, errs.Wrap(err)
|
2019-11-15 14:59:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return rate, nil
|
|
|
|
}
|
|
|
|
|
2019-11-12 11:14:34 +00:00
|
|
|
// ListAccount returns all transaction for specific user.
|
2019-11-15 14:59:39 +00:00
|
|
|
func (db *coinPaymentsTransactions) ListAccount(ctx context.Context, userID uuid.UUID) (_ []stripecoinpayments.Transaction, err error) {
|
|
|
|
defer mon.Task()(&ctx)(&err)
|
|
|
|
|
2019-11-12 11:14:34 +00:00
|
|
|
dbxTXs, err := db.db.All_CoinpaymentsTransaction_By_UserId_OrderBy_Desc_CreatedAt(ctx,
|
|
|
|
dbx.CoinpaymentsTransaction_UserId(userID[:]),
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
var txs []stripecoinpayments.Transaction
|
|
|
|
for _, dbxTX := range dbxTXs {
|
|
|
|
tx, err := fromDBXCoinpaymentsTransaction(dbxTX)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errs.Wrap(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
txs = append(txs, *tx)
|
|
|
|
}
|
|
|
|
|
|
|
|
return txs, nil
|
|
|
|
}
|
|
|
|
|
2019-10-23 13:04:54 +01:00
|
|
|
// ListPending returns paginated list of pending transactions.
|
2019-11-15 14:59:39 +00:00
|
|
|
func (db *coinPaymentsTransactions) ListPending(ctx context.Context, offset int64, limit int, before time.Time) (_ stripecoinpayments.TransactionsPage, err error) {
|
|
|
|
defer mon.Task()(&ctx)(&err)
|
|
|
|
|
2020-01-14 13:38:32 +00:00
|
|
|
query := db.db.Rebind(`SELECT
|
|
|
|
id,
|
|
|
|
user_id,
|
|
|
|
address,
|
|
|
|
amount,
|
|
|
|
received,
|
|
|
|
status,
|
|
|
|
key,
|
|
|
|
created_at
|
|
|
|
FROM coinpayments_transactions
|
|
|
|
WHERE status IN (?,?)
|
|
|
|
AND created_at <= ?
|
|
|
|
ORDER by created_at DESC
|
|
|
|
LIMIT ? OFFSET ?`)
|
2019-10-23 13:04:54 +01:00
|
|
|
|
2020-01-14 13:38:32 +00:00
|
|
|
rows, err := db.db.QueryContext(ctx, query, coinpayments.StatusPending, coinpayments.StatusReceived, before, limit+1, offset)
|
2019-10-23 13:04:54 +01:00
|
|
|
if err != nil {
|
|
|
|
return stripecoinpayments.TransactionsPage{}, err
|
|
|
|
}
|
|
|
|
|
2020-01-14 13:38:32 +00:00
|
|
|
defer func() {
|
|
|
|
err = errs.Combine(err, rows.Close())
|
|
|
|
}()
|
2019-10-23 13:04:54 +01:00
|
|
|
|
2020-01-14 13:38:32 +00:00
|
|
|
var page stripecoinpayments.TransactionsPage
|
2019-10-23 13:04:54 +01:00
|
|
|
|
2020-01-14 13:38:32 +00:00
|
|
|
for rows.Next() {
|
|
|
|
var id, address string
|
2020-03-31 17:49:16 +01:00
|
|
|
var userID uuid.UUID
|
2020-01-14 13:38:32 +00:00
|
|
|
var amountB, receivedB []byte
|
|
|
|
var status int
|
|
|
|
var key string
|
|
|
|
var createdAt time.Time
|
|
|
|
|
2020-03-31 17:49:16 +01:00
|
|
|
err := rows.Scan(&id, &userID, &address, &amountB, &receivedB, &status, &key, &createdAt)
|
2019-10-23 13:04:54 +01:00
|
|
|
if err != nil {
|
|
|
|
return stripecoinpayments.TransactionsPage{}, err
|
|
|
|
}
|
|
|
|
|
satellite/payments: specialized type for monetary amounts
Why: big.Float is not an ideal type for dealing with monetary amounts,
because no matter how high the precision, some non-integer decimal
values can not be represented exactly in base-2 floating point. Also,
storing gob-encoded big.Float values in the database makes it very hard
to use those values in meaningful queries, making it difficult to do
any sort of analysis on billing.
For better accuracy, then, we can just represent monetary values as
integers (in whatever base units are appropriate for the currency). For
example, STORJ tokens or Bitcoins can not be split into pieces smaller
than 10^-8, so we can store amounts of STORJ or BTC with precision
simply by moving the decimal point 8 digits to the right. For USD values
(assuming we don't want to deal with fractional cents), we can move the
decimal point 2 digits to the right.
To make it easier and less error-prone to deal with the math involved, I
introduce here a new type, monetary.Amount, instances of which have an
associated value _and_ a currency.
Change-Id: I03395d52f0e2473cf301361f6033722b54640265
2021-08-10 23:29:50 +01:00
|
|
|
// TODO: the currency here should be passed in to this function or stored
|
|
|
|
// in the database.
|
|
|
|
currency := monetary.StorjToken
|
|
|
|
|
|
|
|
amount, err := monetaryAmountFromGobEncodedBigFloat(amountB, currency)
|
|
|
|
if err != nil {
|
|
|
|
return stripecoinpayments.TransactionsPage{}, err
|
2020-01-14 13:38:32 +00:00
|
|
|
}
|
satellite/payments: specialized type for monetary amounts
Why: big.Float is not an ideal type for dealing with monetary amounts,
because no matter how high the precision, some non-integer decimal
values can not be represented exactly in base-2 floating point. Also,
storing gob-encoded big.Float values in the database makes it very hard
to use those values in meaningful queries, making it difficult to do
any sort of analysis on billing.
For better accuracy, then, we can just represent monetary values as
integers (in whatever base units are appropriate for the currency). For
example, STORJ tokens or Bitcoins can not be split into pieces smaller
than 10^-8, so we can store amounts of STORJ or BTC with precision
simply by moving the decimal point 8 digits to the right. For USD values
(assuming we don't want to deal with fractional cents), we can move the
decimal point 2 digits to the right.
To make it easier and less error-prone to deal with the math involved, I
introduce here a new type, monetary.Amount, instances of which have an
associated value _and_ a currency.
Change-Id: I03395d52f0e2473cf301361f6033722b54640265
2021-08-10 23:29:50 +01:00
|
|
|
received, err := monetaryAmountFromGobEncodedBigFloat(receivedB, currency)
|
|
|
|
if err != nil {
|
|
|
|
return stripecoinpayments.TransactionsPage{}, err
|
2020-01-14 13:38:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
page.Transactions = append(page.Transactions,
|
|
|
|
stripecoinpayments.Transaction{
|
|
|
|
ID: coinpayments.TransactionID(id),
|
|
|
|
AccountID: userID,
|
|
|
|
Address: address,
|
|
|
|
Amount: amount,
|
|
|
|
Received: received,
|
|
|
|
Status: coinpayments.Status(status),
|
|
|
|
Key: key,
|
|
|
|
CreatedAt: createdAt,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err = rows.Err(); err != nil {
|
|
|
|
return stripecoinpayments.TransactionsPage{}, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(page.Transactions) == limit+1 {
|
|
|
|
page.Next = true
|
2020-02-11 13:11:14 +00:00
|
|
|
page.NextOffset = offset + int64(limit)
|
2020-01-14 13:38:32 +00:00
|
|
|
page.Transactions = page.Transactions[:len(page.Transactions)-1]
|
2019-10-23 13:04:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return page, nil
|
|
|
|
}
|
|
|
|
|
satellite/payments: specialized type for monetary amounts
Why: big.Float is not an ideal type for dealing with monetary amounts,
because no matter how high the precision, some non-integer decimal
values can not be represented exactly in base-2 floating point. Also,
storing gob-encoded big.Float values in the database makes it very hard
to use those values in meaningful queries, making it difficult to do
any sort of analysis on billing.
For better accuracy, then, we can just represent monetary values as
integers (in whatever base units are appropriate for the currency). For
example, STORJ tokens or Bitcoins can not be split into pieces smaller
than 10^-8, so we can store amounts of STORJ or BTC with precision
simply by moving the decimal point 8 digits to the right. For USD values
(assuming we don't want to deal with fractional cents), we can move the
decimal point 2 digits to the right.
To make it easier and less error-prone to deal with the math involved, I
introduce here a new type, monetary.Amount, instances of which have an
associated value _and_ a currency.
Change-Id: I03395d52f0e2473cf301361f6033722b54640265
2021-08-10 23:29:50 +01:00
|
|
|
// ListUnapplied returns TransactionsPage with a pending or completed status, that should be applied to account balance.
|
2019-11-05 13:16:02 +00:00
|
|
|
func (db *coinPaymentsTransactions) ListUnapplied(ctx context.Context, offset int64, limit int, before time.Time) (_ stripecoinpayments.TransactionsPage, err error) {
|
2019-11-15 14:59:39 +00:00
|
|
|
defer mon.Task()(&ctx)(&err)
|
|
|
|
|
2019-10-29 16:04:34 +00:00
|
|
|
query := db.db.Rebind(`SELECT
|
|
|
|
txs.id,
|
|
|
|
txs.user_id,
|
|
|
|
txs.address,
|
|
|
|
txs.amount,
|
|
|
|
txs.received,
|
2020-01-14 13:38:32 +00:00
|
|
|
txs.status,
|
2019-10-29 16:04:34 +00:00
|
|
|
txs.key,
|
|
|
|
txs.created_at
|
|
|
|
FROM coinpayments_transactions as txs
|
|
|
|
INNER JOIN stripecoinpayments_apply_balance_intents as ints
|
|
|
|
ON txs.id = ints.tx_id
|
2020-07-01 16:26:23 +01:00
|
|
|
WHERE txs.status >= ?
|
2019-10-29 16:04:34 +00:00
|
|
|
AND txs.created_at <= ?
|
|
|
|
AND ints.state = ?
|
|
|
|
ORDER by txs.created_at DESC
|
|
|
|
LIMIT ? OFFSET ?`)
|
|
|
|
|
2020-07-01 16:26:23 +01:00
|
|
|
rows, err := db.db.QueryContext(ctx, query, coinpayments.StatusReceived, before, applyBalanceIntentStateUnapplied, limit+1, offset)
|
2019-10-29 16:04:34 +00:00
|
|
|
if err != nil {
|
|
|
|
return stripecoinpayments.TransactionsPage{}, err
|
|
|
|
}
|
2020-01-16 14:27:24 +00:00
|
|
|
defer func() { err = errs.Combine(err, rows.Close()) }()
|
2019-10-29 16:04:34 +00:00
|
|
|
|
|
|
|
var page stripecoinpayments.TransactionsPage
|
|
|
|
|
|
|
|
for rows.Next() {
|
|
|
|
var id, address string
|
2020-03-31 17:49:16 +01:00
|
|
|
var userID uuid.UUID
|
2019-10-29 16:04:34 +00:00
|
|
|
var amountB, receivedB []byte
|
2020-01-14 13:38:32 +00:00
|
|
|
var status int
|
2019-10-29 16:04:34 +00:00
|
|
|
var key string
|
|
|
|
var createdAt time.Time
|
|
|
|
|
2020-03-31 17:49:16 +01:00
|
|
|
err := rows.Scan(&id, &userID, &address, &amountB, &receivedB, &status, &key, &createdAt)
|
2019-10-29 16:04:34 +00:00
|
|
|
if err != nil {
|
|
|
|
return stripecoinpayments.TransactionsPage{}, err
|
|
|
|
}
|
|
|
|
|
satellite/payments: specialized type for monetary amounts
Why: big.Float is not an ideal type for dealing with monetary amounts,
because no matter how high the precision, some non-integer decimal
values can not be represented exactly in base-2 floating point. Also,
storing gob-encoded big.Float values in the database makes it very hard
to use those values in meaningful queries, making it difficult to do
any sort of analysis on billing.
For better accuracy, then, we can just represent monetary values as
integers (in whatever base units are appropriate for the currency). For
example, STORJ tokens or Bitcoins can not be split into pieces smaller
than 10^-8, so we can store amounts of STORJ or BTC with precision
simply by moving the decimal point 8 digits to the right. For USD values
(assuming we don't want to deal with fractional cents), we can move the
decimal point 2 digits to the right.
To make it easier and less error-prone to deal with the math involved, I
introduce here a new type, monetary.Amount, instances of which have an
associated value _and_ a currency.
Change-Id: I03395d52f0e2473cf301361f6033722b54640265
2021-08-10 23:29:50 +01:00
|
|
|
// TODO: the currency here should be passed in to this function or stored
|
|
|
|
// in the database.
|
|
|
|
currency := monetary.StorjToken
|
|
|
|
|
|
|
|
amount, err := monetaryAmountFromGobEncodedBigFloat(amountB, currency)
|
|
|
|
if err != nil {
|
2019-10-29 16:04:34 +00:00
|
|
|
return stripecoinpayments.TransactionsPage{}, errs.Wrap(err)
|
|
|
|
}
|
satellite/payments: specialized type for monetary amounts
Why: big.Float is not an ideal type for dealing with monetary amounts,
because no matter how high the precision, some non-integer decimal
values can not be represented exactly in base-2 floating point. Also,
storing gob-encoded big.Float values in the database makes it very hard
to use those values in meaningful queries, making it difficult to do
any sort of analysis on billing.
For better accuracy, then, we can just represent monetary values as
integers (in whatever base units are appropriate for the currency). For
example, STORJ tokens or Bitcoins can not be split into pieces smaller
than 10^-8, so we can store amounts of STORJ or BTC with precision
simply by moving the decimal point 8 digits to the right. For USD values
(assuming we don't want to deal with fractional cents), we can move the
decimal point 2 digits to the right.
To make it easier and less error-prone to deal with the math involved, I
introduce here a new type, monetary.Amount, instances of which have an
associated value _and_ a currency.
Change-Id: I03395d52f0e2473cf301361f6033722b54640265
2021-08-10 23:29:50 +01:00
|
|
|
received, err := monetaryAmountFromGobEncodedBigFloat(receivedB, currency)
|
|
|
|
if err != nil {
|
2019-10-29 16:04:34 +00:00
|
|
|
return stripecoinpayments.TransactionsPage{}, errs.Wrap(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
page.Transactions = append(page.Transactions,
|
|
|
|
stripecoinpayments.Transaction{
|
|
|
|
ID: coinpayments.TransactionID(id),
|
|
|
|
AccountID: userID,
|
|
|
|
Address: address,
|
|
|
|
Amount: amount,
|
|
|
|
Received: received,
|
2020-01-14 13:38:32 +00:00
|
|
|
Status: coinpayments.Status(status),
|
2019-10-29 16:04:34 +00:00
|
|
|
Key: key,
|
|
|
|
CreatedAt: createdAt,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err = rows.Err(); err != nil {
|
|
|
|
return stripecoinpayments.TransactionsPage{}, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(page.Transactions) == limit+1 {
|
|
|
|
page.Next = true
|
2020-02-11 13:11:14 +00:00
|
|
|
page.NextOffset = offset + int64(limit)
|
2019-10-29 16:04:34 +00:00
|
|
|
page.Transactions = page.Transactions[:len(page.Transactions)-1]
|
|
|
|
}
|
|
|
|
|
|
|
|
return page, nil
|
|
|
|
}
|
|
|
|
|
2019-10-17 15:04:50 +01:00
|
|
|
// fromDBXCoinpaymentsTransaction converts *dbx.CoinpaymentsTransaction to *stripecoinpayments.Transaction.
|
|
|
|
func fromDBXCoinpaymentsTransaction(dbxCPTX *dbx.CoinpaymentsTransaction) (*stripecoinpayments.Transaction, error) {
|
2020-03-31 17:49:16 +01:00
|
|
|
userID, err := uuid.FromBytes(dbxCPTX.UserId)
|
2019-10-17 15:04:50 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, errs.Wrap(err)
|
|
|
|
}
|
|
|
|
|
satellite/payments: specialized type for monetary amounts
Why: big.Float is not an ideal type for dealing with monetary amounts,
because no matter how high the precision, some non-integer decimal
values can not be represented exactly in base-2 floating point. Also,
storing gob-encoded big.Float values in the database makes it very hard
to use those values in meaningful queries, making it difficult to do
any sort of analysis on billing.
For better accuracy, then, we can just represent monetary values as
integers (in whatever base units are appropriate for the currency). For
example, STORJ tokens or Bitcoins can not be split into pieces smaller
than 10^-8, so we can store amounts of STORJ or BTC with precision
simply by moving the decimal point 8 digits to the right. For USD values
(assuming we don't want to deal with fractional cents), we can move the
decimal point 2 digits to the right.
To make it easier and less error-prone to deal with the math involved, I
introduce here a new type, monetary.Amount, instances of which have an
associated value _and_ a currency.
Change-Id: I03395d52f0e2473cf301361f6033722b54640265
2021-08-10 23:29:50 +01:00
|
|
|
// TODO: the currency here should be passed in to this function or stored
|
|
|
|
// in the database.
|
|
|
|
currency := monetary.StorjToken
|
|
|
|
|
|
|
|
amount, err := monetaryAmountFromGobEncodedBigFloat(dbxCPTX.Amount, currency)
|
|
|
|
if err != nil {
|
2019-10-17 15:04:50 +01:00
|
|
|
return nil, errs.Wrap(err)
|
|
|
|
}
|
satellite/payments: specialized type for monetary amounts
Why: big.Float is not an ideal type for dealing with monetary amounts,
because no matter how high the precision, some non-integer decimal
values can not be represented exactly in base-2 floating point. Also,
storing gob-encoded big.Float values in the database makes it very hard
to use those values in meaningful queries, making it difficult to do
any sort of analysis on billing.
For better accuracy, then, we can just represent monetary values as
integers (in whatever base units are appropriate for the currency). For
example, STORJ tokens or Bitcoins can not be split into pieces smaller
than 10^-8, so we can store amounts of STORJ or BTC with precision
simply by moving the decimal point 8 digits to the right. For USD values
(assuming we don't want to deal with fractional cents), we can move the
decimal point 2 digits to the right.
To make it easier and less error-prone to deal with the math involved, I
introduce here a new type, monetary.Amount, instances of which have an
associated value _and_ a currency.
Change-Id: I03395d52f0e2473cf301361f6033722b54640265
2021-08-10 23:29:50 +01:00
|
|
|
received, err := monetaryAmountFromGobEncodedBigFloat(dbxCPTX.Received, currency)
|
|
|
|
if err != nil {
|
2019-10-17 15:04:50 +01:00
|
|
|
return nil, errs.Wrap(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return &stripecoinpayments.Transaction{
|
|
|
|
ID: coinpayments.TransactionID(dbxCPTX.Id),
|
|
|
|
AccountID: userID,
|
|
|
|
Address: dbxCPTX.Address,
|
|
|
|
Amount: amount,
|
|
|
|
Received: received,
|
|
|
|
Status: coinpayments.Status(dbxCPTX.Status),
|
|
|
|
Key: dbxCPTX.Key,
|
2019-11-15 14:59:39 +00:00
|
|
|
Timeout: time.Second * time.Duration(dbxCPTX.Timeout),
|
2019-10-17 15:04:50 +01:00
|
|
|
CreatedAt: dbxCPTX.CreatedAt,
|
|
|
|
}, nil
|
|
|
|
}
|
satellite/payments: specialized type for monetary amounts
Why: big.Float is not an ideal type for dealing with monetary amounts,
because no matter how high the precision, some non-integer decimal
values can not be represented exactly in base-2 floating point. Also,
storing gob-encoded big.Float values in the database makes it very hard
to use those values in meaningful queries, making it difficult to do
any sort of analysis on billing.
For better accuracy, then, we can just represent monetary values as
integers (in whatever base units are appropriate for the currency). For
example, STORJ tokens or Bitcoins can not be split into pieces smaller
than 10^-8, so we can store amounts of STORJ or BTC with precision
simply by moving the decimal point 8 digits to the right. For USD values
(assuming we don't want to deal with fractional cents), we can move the
decimal point 2 digits to the right.
To make it easier and less error-prone to deal with the math involved, I
introduce here a new type, monetary.Amount, instances of which have an
associated value _and_ a currency.
Change-Id: I03395d52f0e2473cf301361f6033722b54640265
2021-08-10 23:29:50 +01:00
|
|
|
|
|
|
|
func monetaryAmountFromGobEncodedBigFloat(encoded []byte, currency *monetary.Currency) (_ monetary.Amount, err error) {
|
|
|
|
var bf big.Float
|
|
|
|
if err := bf.GobDecode(encoded); err != nil {
|
|
|
|
return monetary.Amount{}, Error.Wrap(err)
|
|
|
|
}
|
|
|
|
return monetary.AmountFromBigFloat(&bf, currency)
|
|
|
|
}
|