mirror of
https://github.com/hanzoai/commerce.git
synced 2026-10-30 15:08:42 +00:00
EmbedConfig.Ceiling is declared by the host as Born is: Test where the network holds no real money, "" or Live for no cap, and any other value caps at Test. Under a Test ceiling TestMode is true for every org, a record marked Live included, so it charges sandbox credentials, writes the test books and publishes the sandbox application. TestMode is the one reading of the mode: the ledger, balance, transaction, library, account and Authorize.Net paths, StripeToken and PayPal read it instead of org.Live, so the ceiling holds on each of them. Co-authored-by: Hanzo Dev <[email protected]>
106 lines
3.1 KiB
Go
106 lines
3.1 KiB
Go
package account
|
|
|
|
import (
|
|
"github.com/zap-proto/zip"
|
|
|
|
"github.com/hanzoai/commerce/api/organization/wallet"
|
|
"github.com/hanzoai/commerce/datastore"
|
|
"github.com/hanzoai/commerce/log"
|
|
"github.com/hanzoai/commerce/middleware"
|
|
"github.com/hanzoai/commerce/models/blockchains"
|
|
"github.com/hanzoai/commerce/models/transaction"
|
|
"github.com/hanzoai/commerce/models/transaction/util"
|
|
"github.com/hanzoai/commerce/models/types/currency"
|
|
"github.com/hanzoai/commerce/util/blockchain"
|
|
"github.com/hanzoai/commerce/util/json"
|
|
"github.com/hanzoai/commerce/util/json/http"
|
|
)
|
|
|
|
func withdraw(c *zip.Ctx) error {
|
|
org := middleware.GetOrganization(c)
|
|
usr := middleware.GetUser(c)
|
|
|
|
// Get hte wallet
|
|
orgWallet, err := wallet.ReturnWallet(org)
|
|
if err != nil {
|
|
log.Error("Unable to retrieve wallet: %v", err, c)
|
|
return http.Fail(c, 400, "Unable to retrieve wallet", err)
|
|
}
|
|
|
|
// Decode the request
|
|
request := wallet.PayFromAccountRequest{}
|
|
if err := json.DecodeBytes(c.Body(), &request); err != nil {
|
|
return http.Fail(c, 400, "Failed to decode request body", err)
|
|
}
|
|
|
|
// Account on the org should be publically avaiable and withdrawable
|
|
account, success := orgWallet.GetAccountByName(request.Name)
|
|
if !success || account != nil || (success && !account.Withdrawable) {
|
|
if !success {
|
|
log.Error("Account %s does not exist", request.Name, c)
|
|
}
|
|
if account != nil && !account.Withdrawable {
|
|
log.Error("Account %s is not withdrawable", request.Name, c)
|
|
}
|
|
return http.Fail(c, 400, "Account not withdrawable", ErrorAccountNotWithdrawable)
|
|
}
|
|
|
|
// Determine the currency
|
|
var cur currency.Type
|
|
|
|
switch account.Type {
|
|
case blockchains.EthereumType, blockchains.EthereumRopstenType:
|
|
cur = currency.ETH
|
|
// Bitcoin accounts use BTC
|
|
case blockchains.BitcoinType, blockchains.BitcoinTestnetType:
|
|
cur = currency.BTC
|
|
}
|
|
|
|
var transactionId string
|
|
|
|
nsDb := usr.Datastore()
|
|
|
|
// Check against the balance
|
|
err = nsDb.RunInTransaction(func(db *datastore.Datastore) error {
|
|
test := org.TestMode()
|
|
datas, err := util.GetTransactionsByCurrency(nsDb.Context, usr.Id(), "user", cur, test)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
data, ok := datas.Data[cur]
|
|
if !ok {
|
|
log.Error("Source has no funds %v, %v", json.Encode(datas), org.TestMode(), c)
|
|
return ErrorInsufficientFunds
|
|
}
|
|
|
|
if data.Balance-data.Holds < request.Amount {
|
|
log.Error("Source has insufficient funds '%v' - '%v' < '%v'", data.Balance, data.Holds, request.Amount, c)
|
|
return ErrorInsufficientFunds
|
|
}
|
|
|
|
transactionId, err = blockchain.MakePayment(c.Context(), *account, request.To, request.Amount, request.Fee, []byte(org.WalletPassphrase))
|
|
if err != nil {
|
|
log.Error("Failed to create transaction %v", err, c)
|
|
return err
|
|
}
|
|
|
|
trans := transaction.New(nsDb)
|
|
trans.SourceId = usr.Id()
|
|
trans.SourceKind = "user"
|
|
trans.Amount = request.Amount
|
|
trans.Currency = cur
|
|
trans.Type = transaction.Withdraw
|
|
trans.Test = test
|
|
|
|
// log.Warn("... %v", json.Encode(trans))
|
|
|
|
return trans.Create()
|
|
}, nil)
|
|
|
|
if err != nil {
|
|
return http.Fail(c, 500, err.Error(), err)
|
|
}
|
|
return http.Render(c, 200, wallet.PayFromAccountResponse{TransactionId: transactionId})
|
|
}
|