cashweb_payments/
lib.rs

1#![warn(
2    missing_debug_implementations,
3    missing_docs,
4    rust_2018_idioms,
5    unreachable_pub
6)]
7
8//! `cashweb-payments` is a library providing structures and utilities related to
9//! the [`BIP70: Payment Protocol`] and a [`Wallet`] structure to allow receiving
10//! payments.
11//!
12//! [`Wallet`]: wallet::Wallet
13//! [`BIP70: Payment Protocol`]: https://github.com/bitcoin/bips/blob/master/bip-0070.mediawiki
14
15pub mod wallet;
16
17use bytes::Buf;
18use http::header::{HeaderMap, HeaderValue, ACCEPT, CONTENT_TYPE};
19use prost::{DecodeError, Message};
20use thiserror::Error;
21
22#[allow(missing_docs)]
23pub mod bip70 {
24    //! This module contains structures related to the [`BIP70: Payment Protocol`]
25    //!
26    //! [`BIP70: Payment Protocol`]: https://github.com/bitcoin/bips/blob/master/bip-0070.mediawiki
27
28    include!(concat!(env!("OUT_DIR"), "/bip70.rs"));
29}
30
31use bip70::Payment;
32
33/// Error associated with payment preprocessing.
34#[derive(Debug, Error)]
35pub enum PreprocessingError {
36    /// Missing the `application/bitcoincash-paymentack` header.
37    #[error("missing accept header")]
38    MissingAcceptHeader,
39    /// Missing the `application/bitcoincash-payment` header.
40    #[error("invalid content-type")]
41    MissingContentTypeHeader,
42    /// Failed to decode the `Payment` protobuf.
43    #[error("payment decoding failure: {0}")]
44    PaymentDecode(DecodeError),
45}
46
47/// Validates and parses the BIP70 payment.
48pub async fn preprocess_payment<B: Buf>(
49    headers: HeaderMap,
50    body: B,
51) -> Result<Payment, PreprocessingError> {
52    // Bitcoin Cash Headers
53    let bch_content_type_value = HeaderValue::from_static("application/bitcoincash-payment");
54    let bch_accept_value = HeaderValue::from_static("application/bitcoincash-paymentack");
55
56    // Check for content-type header
57    if !headers
58        .get_all(CONTENT_TYPE)
59        .iter()
60        .any(|header_val| header_val == bch_content_type_value)
61    {
62        return Err(PreprocessingError::MissingContentTypeHeader);
63    }
64
65    // Check for accept header
66    if !headers
67        .get_all(ACCEPT)
68        .iter()
69        .any(|header_val| header_val == bch_accept_value)
70    {
71        return Err(PreprocessingError::MissingAcceptHeader);
72    }
73
74    // Read and parse payment proto
75    let payment = bip70::Payment::decode(body).map_err(PreprocessingError::PaymentDecode)?;
76
77    Ok(payment)
78}