-
Notifications
You must be signed in to change notification settings - Fork 1
/
invoice.rs
165 lines (147 loc) · 5.26 KB
/
invoice.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
use crate::db::models::Post;
use chrono::{Duration, NaiveDateTime, Utc};
use tonic::codegen::InterceptedService;
use tonic::transport::Channel;
use tonic::{Code, Status};
use tonic_lnd::MacaroonInterceptor;
use tonic_lnd::rpc::lightning_client::LightningClient;
use tonic_lnd::rpc::{Invoice, PaymentHash};
use lightning_invoice::*;
pub struct InvoiceParams {
pub value: i64,
pub memo: String,
pub expiry: i64,
}
impl InvoiceParams {
pub fn new(value: Option<i64>, memo: Option<String>, expiry: Option<i64>) -> Self {
Self {
value: value.unwrap_or_else(|| 10 as i64),
memo: memo.unwrap_or_else(|| "".to_string()),
expiry: expiry.unwrap_or_else(|| 60 as i64),
}
}
}
/*
Represents a simplified invoice object.
This allow us to keep only the critical data we need from an
invoice object
*/
#[derive(Clone)]
pub struct LndInvoice {
pub memo: String,
pub payment_request: String,
pub value: i64,
pub r_hash: String,
pub expires_at: NaiveDateTime,
}
impl LndInvoice {
/*
Creates an LndInvoice from an Invoice retrieved through RPC + its rHash
*/
pub fn new(invoice: tonic_lnd::rpc::Invoice, r_hash: String) -> Self {
let expires_at = Utc::now()
.checked_add_signed(Duration::seconds(invoice.expiry))
.unwrap();
Self {
payment_request: invoice.payment_request,
memo: invoice.memo,
value: invoice.value as i64,
r_hash: r_hash,
expires_at: expires_at.naive_utc(),
}
}
}
/**
* Provides with the utilities method required to build the Lightning Network
* Paywall on top of the Juniper GraphQL API.
*/
pub struct InvoiceUtils {}
impl InvoiceUtils {
/**
Generates an invoice for a post
This shall be called whenever the user
requests a resource without providing a payment request value
or when the related invoice is expired/canceled.
*/
pub async fn generate_post_invoice(
lnd_client: LightningClient<InterceptedService<tonic::transport::Channel, MacaroonInterceptor>>,
post: Post,
) -> LndInvoice {
let params = InvoiceParams::new(
Some(post.price as i64),
// Memo content should be handle with an env var pattern
Some(format!("buy {} : {}", post.uuid, post.title).to_string()),
Some(60 as i64),
);
// Request invoice generation to the LN Server
InvoiceUtils::generate_invoice(lnd_client, params).await
}
/**
Generate an invoice through lnd
*/
pub async fn generate_invoice(
mut lnd_client: LightningClient<InterceptedService<tonic::transport::Channel, MacaroonInterceptor>>,
params: InvoiceParams,
) -> LndInvoice {
let add_invoice_response = lnd_client.add_invoice(tonic_lnd::rpc::Invoice {
memo: params.memo,
value: params.value,
expiry: params.expiry,
..tonic_lnd::rpc::Invoice::default()
});
let result = add_invoice_response.await.unwrap().into_inner();
// Retrieve the payment hash based on r_hash returned from the AddInvoiceResponse
let payment_hash = tonic_lnd::rpc::PaymentHash {
r_hash: result.r_hash.clone(),
r_hash_str: hex::encode(result.r_hash.clone()), // provided as request by the Struct but not used and deprecated
};
// // Get the Invoice detail so we can return the payment_request
let invoice = lnd_client
.lookup_invoice(payment_hash)
.await
.unwrap()
.into_inner();
LndInvoice::new(invoice, hex::encode(result.r_hash))
}
/*
Gets the invoice state from a payment request string.
It consists as a two steps method.
First it registers an invoice
*/
pub async fn get_invoice_state_from_payment_request<'a>(
lnd_client: &LightningClient<InterceptedService<tonic::transport::Channel, MacaroonInterceptor>>,
payment_request: String,
) -> Result<Option<Invoice>, Status> {
let mut client = lnd_client.clone();
// Parse the payment request
let invoice = payment_request
.as_str()
.parse::<SignedRawInvoice>()
.unwrap();
// Get the payment hash
let p_hash = invoice.payment_hash().unwrap();
/*
The below instruction might seems a bit odd.
the expected r_hash here is not the Invoice r_hash
but rather the r_hash of the payment request which is
denominated in the SignedRawInvoice as the payment_hash.
*/
let request = tonic::Request::new(PaymentHash {
r_hash: p_hash.0.to_vec(),
..PaymentHash::default()
});
match client.lookup_invoice(request).await {
Ok(response) => Ok(Some(response.into_inner())),
Err(status) => {
if status.code() == Code::Unknown
&& (status.message() == "there are no existing invoices"
|| status.message() == "unable to locate invoice")
{
Ok(None)
} else {
Err(status)
}
}
}
}
}