btcpay 0.2.0

Rust BTCPayServer client
Documentation
use std::collections::HashMap;

use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Invoice {
    pub url: String,
    pub pos_data: Option<String>,
    pub btc_price: Option<String>,
    pub btc_due: Option<String>,
    #[serde(default)]
    pub crypto_info: Vec<CryptoInfo>,
    pub price: f32,
    pub currency: String,
    #[serde(default)]
    pub ex_rates: Option<HashMap<String, f32>>,
    pub buyer_total_btc_amount: Option<String>,
    pub item_desc: Option<String>,
    pub item_code: Option<String>,
    pub order_id: Option<String>,
    pub guid: Option<String>,
    pub id: String,
    pub invoice_time: u64,
    pub expiration_time: u64,
    pub current_time: u64,
    pub low_fee_detected: Option<bool>,
    pub btc_paid: Option<String>,
    pub rate: f32,
    pub exception_status: bool,
    pub payment_urls: Option<PaymentUrl>,
    pub refund_address_request_pending: Option<bool>,
    pub buyer_paid_btc_miner_fee: Option<String>,
    pub bitcoin_address: Option<String>,
    pub flags: Option<InvoiceFlags>,
    #[serde(default)]
    pub payment_subtotals: HashMap<String, f32>,
    #[serde(default)]
    pub payment_totals: HashMap<String, f32>,
    // FIXME: in create_invoice it's an f32, in the webhook it's a string :/
    pub amount_paid: serde_json::Value,
    #[serde(default)]
    pub miner_fees: HashMap<String, MinerFees>,
    #[serde(default)]
    pub exchange_rates: HashMap<String, HashMap<String, f32>>,
    #[serde(default)]
    pub supported_transaction_currencies: HashMap<String, SupportedCurrency>,
    #[serde(default)]
    pub addresses: HashMap<String, String>,
    #[serde(default)]
    pub payment_codes: HashMap<String, PaymentUrl>,
    pub buyer: Option<Buyer>,
    pub status: InvoiceStatus,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum InvoiceStatus {
    New,
    Expired,
    Paid,
    Confirmed,
    Completed,
    Invalid,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CryptoInfo {
    pub crypto_code: String,
    pub payment_type: String,
    pub rate: f32,
    pub ex_rates: HashMap<String, f32>,
    pub paid: String,
    pub price: String,
    pub due: String,
    pub payment_urls: PaymentUrl,
    pub address: String,
    pub url: String,
    pub total_due: String,
    pub network_fee: String,
    pub tx_count: usize,
    pub crypto_paid: String,
    // pub payments: Vec<> ??
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "UPPERCASE")]
pub struct PaymentUrl {
    pub bip21: Option<String>,
    pub bip72: Option<String>,
    #[serde(rename = "BIP72b")]
    pub bip72b: Option<String>,
    pub bip73: Option<String>,
    pub bolt11: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct InvoiceFlags {
    pub refundable: bool,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct MinerFees {
    pub satoshis_per_byte: f32,
    pub total_fee: f32,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Buyer {
    pub name: Option<String>,
    pub address1: Option<String>,
    pub address2: Option<String>,
    pub locality: Option<String>,
    pub region: Option<String>,
    pub postal_code: Option<String>,
    pub country: Option<String>,
    pub phone: Option<String>,
    pub email: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SupportedCurrency {
    pub enabled: bool,
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test_parse_invoice() {
        let inv = r#"{"url":"https://testnet.demo.btcpayserver.org/invoice?id=3Wy4cKQEvmsBru5yccGbNn","posData":null,"status":"new","btcPrice":"0.00260930","btcDue":"0.00260930","cryptoInfo":[{"cryptoCode":"BTC","paymentType":"BTCLike","rate":7664.908,"exRates":{"USD":0},"paid":"0.00000000","price":"0.00260930","due":"0.00260930","paymentUrls":{"BIP21":"bitcoin:tb1qxzv66jwlm5pphum53alctrsdr7stfahc6hnxeg?amount=0.0026093","BIP72":null,"BIP72b":null,"BIP73":null,"BOLT11":null},"address":"tb1qxzv66jwlm5pphum53alctrsdr7stfahc6hnxeg","url":"https://testnet.demo.btcpayserver.org/i/BTC/3Wy4cKQEvmsBru5yccGbNn","totalDue":"0.00260930","networkFee":"0.00000000","txCount":0,"cryptoPaid":"0.00000000","payments":[]},{"cryptoCode":"LTC","paymentType":"BTCLike","rate":45.07576229821518,"exRates":{"USD":0},"paid":"0.00000000","price":"0.44369744","due":"0.44369744","paymentUrls":{"BIP21":"litecoin:tltc1qxzv66jwlm5pphum53alctrsdr7stfahcrl3cfp?amount=0.44369744","BIP72":null,"BIP72b":null,"BIP73":null,"BOLT11":null},"address":"tltc1qxzv66jwlm5pphum53alctrsdr7stfahcrl3cfp","url":"https://testnet.demo.btcpayserver.org/i/LTC/3Wy4cKQEvmsBru5yccGbNn","totalDue":"0.44369744","networkFee":"0.00000000","txCount":0,"cryptoPaid":"0.00000000","payments":[]}],"price":20,"currency":"USD","exRates":{"USD":0},"buyerTotalBtcAmount":null,"itemDesc":null,"itemCode":null,"orderId":null,"guid":"52ccf34a-f69f-4ccf-8027-46bec72a57c7","id":"3Wy4cKQEvmsBru5yccGbNn","invoiceTime":1587907315000,"expirationTime":1587908215000,"currentTime":1587907528578,"lowFeeDetected":false,"btcPaid":"0.00000000","rate":7664.908,"exceptionStatus":false,"paymentUrls":{"BIP21":"bitcoin:tb1qxzv66jwlm5pphum53alctrsdr7stfahc6hnxeg?amount=0.0026093","BIP72":null,"BIP72b":null,"BIP73":null,"BOLT11":null},"refundAddressRequestPending":false,"buyerPaidBtcMinerFee":null,"bitcoinAddress":"tb1qxzv66jwlm5pphum53alctrsdr7stfahc6hnxeg","token":"8byQ85L8bX4Urq68ne3Wg7","flags":{"refundable":false},"paymentSubtotals":{"BTC":260930,"LTC":44369744},"paymentTotals":{"BTC":260930,"LTC":44369744},"amountPaid":0,"minerFees":{"BTC":{"satoshisPerByte":1,"totalFee":0},"LTC":{"satoshisPerByte":25,"totalFee":0}},"exchangeRates":{"BTC":{"USD":0},"LTC":{"USD":0}},"supportedTransactionCurrencies":{"BTC":{"enabled":true,"reason":null},"LTC":{"enabled":true,"reason":null}},"addresses":{"BTC":"tb1qxzv66jwlm5pphum53alctrsdr7stfahc6hnxeg","LTC":"tltc1qxzv66jwlm5pphum53alctrsdr7stfahcrl3cfp"},"paymentCodes":{"BTC":{"BIP21":"bitcoin:tb1qxzv66jwlm5pphum53alctrsdr7stfahc6hnxeg?amount=0.0026093","BIP72":null,"BIP72b":null,"BIP73":null,"BOLT11":null},"LTC":{"BIP21":"litecoin:tltc1qxzv66jwlm5pphum53alctrsdr7stfahcrl3cfp?amount=0.44369744","BIP72":null,"BIP72b":null,"BIP73":null,"BOLT11":null}},"buyer":{"name":null,"address1":null,"address2":null,"locality":null,"region":null,"postalCode":null,"country":null,"phone":null,"email":null}}"#;

        let parsed: Invoice = serde_json::from_str(inv).unwrap();
        println!("{:#?}", parsed);
    }
}