From d07e9d3f5365c926738c2a19681055dec92bb851 Mon Sep 17 00:00:00 2001 From: Nikolas De Giorgis Date: Mon, 27 Jan 2025 11:10:41 +0000 Subject: [PATCH] backend: provide getBtcDirectInfo API endpoint Provide an endpoint to be called from the frontend. This endpoints returns the APIKey, the URL address and, if the action is "buy", the address. --- backend/arguments/arguments.go | 5 +-- backend/exchanges/btcdirect.go | 35 +++++++++++++++++++ backend/handlers/handlers.go | 28 +++++++++++++++ frontends/web/src/api/exchanges.ts | 11 ++---- .../web/src/routes/exchange/btcdirect.tsx | 14 +++----- 5 files changed, 73 insertions(+), 20 deletions(-) diff --git a/backend/arguments/arguments.go b/backend/arguments/arguments.go index a6bdafae78..58332c8bd1 100644 --- a/backend/arguments/arguments.go +++ b/backend/arguments/arguments.go @@ -49,8 +49,9 @@ type Arguments struct { regtest bool // devservers stores wether the app should connect to the dev servers. - // This also applies to the Pocket widget environment: if devserver is true, the widget - // will be loaded from the staging environment, otherwise from production. + // This also applies to the Pocket and BTCDirect widget environments: + // if devserver is true, the widgets will be loaded from the staging environment, + // otherwise from production. // The devservers configuration is not persisted when switching back to production. devservers bool diff --git a/backend/exchanges/btcdirect.go b/backend/exchanges/btcdirect.go index 21be7209f7..cb10253669 100644 --- a/backend/exchanges/btcdirect.go +++ b/backend/exchanges/btcdirect.go @@ -17,14 +17,29 @@ package exchanges import ( "slices" + "github.com/BitBoxSwiss/bitbox-wallet-app/backend/accounts" "github.com/BitBoxSwiss/bitbox-wallet-app/backend/coins/coin" ) const ( // BTCDirectName is the name of the exchange, it is unique among all the supported exchanges. BTCDirectName = "btcdirect" + + btcDirectTestApiKey = "6ed4d42bd02eeac1776a6bb54fa3126f779c04d5c228fe5128bb74e89ef61f83" + + btcDirectProdAPiKey = "7d71f633626901d5c4d06d91f7d0db2c15cdf524ddd0ebcd36f4d9c4e04694cd" + + btcDirectTestUrl = "/btcdirect/fiat-to-coin.html" + + btcDirectProdUrl = "https://bitboxapp.shiftcrypto.io/widgets/btcdirect/v1/fiat-to-coin.html" ) +type btcDirectInfo struct { + Url string + ApiKey string + Address *string +} + var regions = []string{ "AT", "BE", "BG", "CH", "CY", "CZ", "DE", "DK", "EE", "ES", "FI", "FR", "GR", "HR", "HU", "IE", "IS", "IT", "LI", "LT", "LU", "LV", "MC", "MT", "NL", "NO", @@ -76,3 +91,23 @@ func BtcDirectDeals() *ExchangeDealsList { }, } } + +// BtcDirectInfo returns the information needed to interact with BtcDirect. +// If `devServers` is true, it returns testing URL and ApiKey. +func BtcDirectInfo(action ExchangeAction, acct accounts.Interface, devServers bool) btcDirectInfo { + res := btcDirectInfo{ + Url: btcDirectProdUrl, + ApiKey: btcDirectProdAPiKey, + } + + if devServers { + res.Url = btcDirectTestUrl + res.ApiKey = btcDirectTestApiKey + } + + if action == BuyAction { + addr := acct.GetUnusedReceiveAddresses()[0].Addresses[0].EncodeForHumans() + res.Address = &addr + } + return res +} diff --git a/backend/handlers/handlers.go b/backend/handlers/handlers.go index 286b1dc838..b6884eb13a 100644 --- a/backend/handlers/handlers.go +++ b/backend/handlers/handlers.go @@ -243,6 +243,7 @@ func NewHandlers( getAPIRouterNoError(apiRouter)("/exchange/deals/{action}/{code}", handlers.getExchangeDeals).Methods("GET") getAPIRouterNoError(apiRouter)("/exchange/supported/{code}", handlers.getExchangeSupported).Methods("GET") getAPIRouterNoError(apiRouter)("/exchange/btcdirect-otc/supported/{code}", handlers.getExchangeBtcDirectOTCSupported).Methods("GET") + getAPIRouterNoError(apiRouter)("/exchange/btcdirect/info/{action}/{code}", handlers.getExchangeBtcDirectInfo).Methods("GET") getAPIRouter(apiRouter)("/exchange/moonpay/buy-info/{code}", handlers.getExchangeMoonpayBuyInfo).Methods("GET") getAPIRouterNoError(apiRouter)("/exchange/pocket/api-url/{action}", handlers.getExchangePocketURL).Methods("GET") getAPIRouterNoError(apiRouter)("/exchange/pocket/verify-address", handlers.postPocketWidgetVerifyAddress).Methods("POST") @@ -1438,6 +1439,33 @@ func (handlers *Handlers) getExchangeMoonpayBuyInfo(r *http.Request) (interface{ return resp, nil } +func (handlers *Handlers) getExchangeBtcDirectInfo(r *http.Request) interface{} { + type result struct { + Success bool `json:"success"` + ErrorMessage string `json:"errorMessage"` + Url string `json:"url"` + ApiKey string `json:"apiKey"` + Address *string `json:"address"` + } + + code := accountsTypes.Code(mux.Vars(r)["code"]) + acct, err := handlers.backend.GetAccountFromCode(code) + accountValid := acct != nil && acct.Offline() == nil && !acct.FatalError() + if err != nil || !accountValid { + return result{Success: false, ErrorMessage: "Account is not valid."} + } + + action := exchanges.ExchangeAction(mux.Vars(r)["action"]) + btcInfo := exchanges.BtcDirectInfo(action, acct, handlers.backend.DevServers()) + + return result{ + Success: true, + Url: btcInfo.Url, + ApiKey: btcInfo.ApiKey, + Address: btcInfo.Address, + } +} + func (handlers *Handlers) getExchangePocketURL(r *http.Request) interface{} { lang := handlers.backend.Config().AppConfig().Backend.UserLanguage if len(lang) == 0 { diff --git a/frontends/web/src/api/exchanges.ts b/frontends/web/src/api/exchanges.ts index e610e2a244..54196ba823 100644 --- a/frontends/web/src/api/exchanges.ts +++ b/frontends/web/src/api/exchanges.ts @@ -103,6 +103,7 @@ export type TBTCDirectInfoResponse = { success: true; url: string; apiKey: string; + address?: string; } | { success: false; errorMessage: string; @@ -110,15 +111,9 @@ export type TBTCDirectInfoResponse = { export const getBTCDirectInfo = ( action: TExchangeAction, + code: string, ): Promise => { - console.log(action); - // TODO: change to return apiGet(`exchange/btc-direct/info/${action}`); or similar - return Promise.resolve({ - success: true, - url: '/btcdirect/fiat-to-coin.html', // local static file for testing - apiKey: '6ed4d42bd02eeac1776a6bb54fa3126f779c04d5c228fe5128bb74e89ef61f83', // debug - // apiKey: '7d71f633626901d5c4d06d91f7d0db2c15cdf524ddd0ebcd36f4d9c4e04694cd', // prod - }); + return apiGet(`exchange/btcdirect/info/${action}/${code}`); }; export type SupportedExchanges= { diff --git a/frontends/web/src/routes/exchange/btcdirect.tsx b/frontends/web/src/routes/exchange/btcdirect.tsx index 950296e5d2..2343e421d5 100644 --- a/frontends/web/src/routes/exchange/btcdirect.tsx +++ b/frontends/web/src/routes/exchange/btcdirect.tsx @@ -18,7 +18,7 @@ import { useState, useEffect, createRef, useContext, useRef } from 'react'; import { useTranslation } from 'react-i18next'; import { getBTCDirectInfo } from '@/api/exchanges'; import { AppContext } from '@/contexts/AppContext'; -import { AccountCode, getReceiveAddressList, IAccount } from '@/api/account'; +import { AccountCode, IAccount } from '@/api/account'; import { useLoad } from '@/hooks/api'; import { useDarkmode } from '@/hooks/darkmode'; import { UseDisableBackButton } from '@/hooks/backbutton'; @@ -51,13 +51,7 @@ export const BTCDirect = ({ accounts, code }: TProps) => { const { isDarkMode } = useDarkmode(); const iframeRef = useRef(null); - const btcdirectInfo = useLoad(() => getBTCDirectInfo('buy')); - - const receiveAddresses = useLoad(getReceiveAddressList(code)); - - // TODO: address should probably come from the backend, i.e. ETH address - const p2wpkhAddresses = receiveAddresses?.find(({ scriptType }) => scriptType === 'p2wpkh')?.addresses || []; - const address = p2wpkhAddresses[0]?.address || ''; + const btcdirectInfo = useLoad(() => getBTCDirectInfo('buy', code)); const [agreedTerms, setAgreedTerms] = useState(false); const [iframeLoaded, setIframeLoaded] = useState(false); @@ -124,7 +118,7 @@ export const BTCDirect = ({ accounts, code }: TProps) => {
{!iframeLoaded && } - { address && btcdirectInfo?.success && ( + { btcdirectInfo?.success && (