async def get_confirmations(method, xpub): coin = settings.get_coin(method.currency, xpub) invoice_data = await coin.get_request(method.payment_address) return min(constants.MAX_CONFIRMATION_WATCH, invoice_data.get( "confirmations", 0)) # don't store arbitrary number of confirmations
async def test_create_invoice_and_pay(async_client, token: str, mocker): # get an existing wallet wallet = await models.Wallet.query.where(models.Wallet.name == "test5" ).gino.first() # create invoice r = await async_client.post("/invoices", json={ "store_id": 2, "price": 9.9 }, headers={"Authorization": f"Bearer {token}"}) assert r.status_code == 200 invoice_id = r.json()["id"] invoice = await models.Invoice.get(invoice_id) # get payment payment_method = await models.PaymentMethod.query.where( models.PaymentMethod.invoice_id == invoice_id).gino.first() payment_method.coin = settings.get_coin(payment_method.currency, xpub=wallet.xpub) assert not (await models.Invoice.get(invoice_id)).paid_currency # mock transaction complete mocker.patch.object(payment_method.coin, "get_request", return_value=get_future_return_value( {"status": "complete"})) # process invoice await tasks.process_invoice(invoice, {}, [payment_method], notify=False) # validate invoice paid_currency assert (await models.Invoice.get(invoice_id) ).paid_currency == payment_method.currency await async_client.delete(f"/invoices/{invoice_id}", headers={"Authorization": f"Bearer {token}"})
async def get_wallet_history(model, response): coin = settings.get_coin(model.currency, model.xpub) txes = (await coin.history())["transactions"] for i in txes: response.append({ "date": i["date"], "txid": i["txid"], "amount": i["bc_value"] })
async def check_pending(currency): async for method, invoice, xpub in iterate_pending_invoices(currency): with log_errors(): # issues processing one item if invoice.status == InvoiceStatus.EXPIRED: continue coin = settings.get_coin(method.currency, xpub) if method.lightning: invoice_data = await coin.get_invoice(method.rhash) else: invoice_data = await coin.get_request(method.payment_address) await mark_invoice_paid(invoice, method, xpub, invoice_data["status"])
async def sync_wallet(event, event_data): model = await utils.database.get_object(models.Wallet, event_data["id"], raise_exception=False) if not model: return coin = settings.get_coin(model.currency, model.xpub) balance = await coin.balance() logger.info(f"Wallet {model.id} synced, balance: {balance['confirmed']}") await utils.redis.publish_message(f"wallet:{model.id}", { "status": "success", "balance": str(balance["confirmed"]) }) # convert for json serialization
async def get_wallet_balances(user): show_currency = user.settings.balance_currency balances = Decimal() rates = {} async with utils.database.iterate_helper(): async for wallet in models.Wallet.query.where( models.Wallet.user_id == user.id).gino.iterate(): coin = settings.get_coin(wallet.currency, wallet.xpub) crypto_balance = await get_wallet_balance(coin) if wallet.currency in rates: # pragma: no cover rate = rates[wallet.currency] else: rate = rates[wallet.currency] = await get_rate( coin, show_currency) balances += crypto_balance * rate return currency_table.normalize(show_currency, balances)
async def validate(self, **kwargs): await super().validate(**kwargs) if "currency" in kwargs: coin = settings.get_coin(kwargs["currency"]) if not await coin.validate_key(kwargs.get("xpub")): raise HTTPException(422, "Wallet key invalid")
async def add_fields(self): from api import utils self.balance = await utils.wallets.get_wallet_balance( settings.get_coin(self.currency, self.xpub))
async def get_wallet_coin_by_id(model_id: int, user): wallet = await utils.database.get_object(models.Wallet, model_id, user) return settings.get_coin(wallet.currency, wallet.xpub)
async def _create_payment_method(invoice, wallet, product, store, discounts, promocode, lightning=False): coin = settings.get_coin(wallet.currency, wallet.xpub) discount_id = None rate = await coin.rate(invoice.currency) if math.isnan(rate): rate = await coin.rate(store.default_currency) if math.isnan(rate): rate = await coin.rate("USD") if math.isnan(rate): rate = Decimal(1) # no rate available, no conversion price = invoice.price if discounts: try: discount = max( filter( lambda x: (not x.currencies or wallet.currency in CommaSeparatedStrings(x.currencies)) and (promocode == x.promocode or not x.promocode), discounts, ), key=attrgetter("percent"), ) logger.info( f"Payment method {wallet.currency} of invoice {invoice.id}: matched discount {discount.id}" ) discount_id = discount.id price -= price * (Decimal(discount.percent) / Decimal(100)) except ValueError: # no matched discounts pass request_price = price * ( (1 - (Decimal(store.checkout_settings.underpaid_percentage) / 100))) request_price = currency_table.normalize(wallet.currency, request_price / rate) price = currency_table.normalize(wallet.currency, price / rate) method = coin.add_request if lightning: # pragma: no cover try: await coin.node_id # check if works method = coin.add_invoice except errors.LightningUnsupportedError: return recommended_fee = (await coin.server.recommended_fee( store.checkout_settings.recommended_fee_target_blocks) if not lightning else 0) recommended_fee = 0 if recommended_fee is None else recommended_fee # if no rate available, disable it recommended_fee = round_up(Decimal(recommended_fee) / 1024, 2) # convert to sat/byte, two decimal places data_got = await method(request_price, description=product.name if product else "", expire=invoice.expiration) address = data_got["address"] if not lightning else data_got["invoice"] url = data_got["URI"] if not lightning else data_got["invoice"] node_id = await coin.node_id if lightning else None rhash = data_got["rhash"] if lightning else None return await models.PaymentMethod.create( invoice_id=invoice.id, amount=price, rate=rate, discount=discount_id, currency=wallet.currency, payment_address=address, payment_url=url, rhash=rhash, lightning=lightning, node_id=node_id, recommended_fee=recommended_fee, confirmations=0, )
async def validate(self, **kwargs): await super().validate(**kwargs) currency = kwargs.get("currency", self.currency) coin = settings.get_coin(currency) if not await coin.validate_key(kwargs.get("xpub")): raise HTTPException(422, "Wallet key invalid")