Skip to content

Commit 4bd2cde

Browse files
author
Conor Okus
committed
Start sending payments page
1 parent 869cb87 commit 4bd2cde

File tree

2 files changed

+171
-1
lines changed

2 files changed

+171
-1
lines changed

docs/.vuepress/config.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,8 @@ const tutorialSidebar = [
101101
['/tutorials/building-a-node-with-ldk/handling-events', 'Handling Events'],
102102
['/tutorials/building-a-node-with-ldk/setting-up-a-peer-manager', 'Setting up a Peer Manager'],
103103
['/tutorials/building-a-node-with-ldk/connect-to-peers', 'Connect to Peers'],
104-
['/tutorials/building-a-node-with-ldk/opening-a-channel', 'Opening a Channel']
104+
['/tutorials/building-a-node-with-ldk/opening-a-channel', 'Opening a Channel'],
105+
['/tutorials/building-a-node-with-ldk/sending-payments', 'Sending Payments']
105106
]
106107
},
107108
],
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
# Sending Payments
2+
3+
Lightning payments are used to pay invoices, which are typically encoded as a
4+
string in accordance with BOLT 11. After parsing the invoice, you'll need to
5+
find a route from your node to the recipient and then make the payment using
6+
`ChannelManager`.
7+
8+
<CodeSwitcher :languages="{rust:'Rust', java:'Java', kotlin:'Kotlin'}">
9+
<template v-slot:rust>
10+
11+
```rust
12+
// Parse the invoice.
13+
let invoice = Invoice::from_str(encoded_invoice)
14+
.expect("ERROR: failed to parse invoice");
15+
16+
let amt_pico_btc = invoice.amount_pico_btc()
17+
.expect("ERROR: invalid invoice: must contain amount to pay");
18+
let amt_msat = amt_pico_btc / 10;
19+
let payer_pubkey = channel_manager.get_our_node_id();
20+
let network_graph = router.network_graph.read().unwrap();
21+
let payee_pubkey = invoice.recover_payee_pub_key();
22+
let payee_features = invoice.features().cloned();
23+
let first_hops = channel_manager.list_usable_channels();
24+
let last_hops = invoice.route_hints();
25+
let final_cltv = invoice.min_final_cltv_expiry() as u32;
26+
27+
// Find a route and send the payment.
28+
let route = router::get_route(
29+
&payer_pubkey, &network_graph, &payee_pubkey, payee_features,
30+
Some(&first_hops.iter().collect::<Vec<_>>()), &last_hops,
31+
amt_msat, final_cltv, logger.clone(),
32+
).expect("ERROR: failed to find route");
33+
34+
let payment_hash = PaymentHash(invoice.payment_hash().clone().into_inner());
35+
let payment_secret = invoice.payment_secret().cloned();
36+
37+
channel_manager.send_payment(&route, payment_hash, &payment_secret)
38+
.expect("ERROR: failed to send payment");
39+
```
40+
41+
</template>
42+
<template v-slot:java>
43+
44+
```java
45+
String invoice_str = // get an invoice from the payee
46+
Result_InvoiceNoneZ parsed_invoice = Invoice.from_str(invoice_str);
47+
48+
if (parsed_invoice instanceof Result_InvoiceNoneZ.Result_InvoiceNoneZ_OK) {
49+
Invoice invoice = ((Result_InvoiceNoneZ.Result_InvoiceNoneZ_OK) parsed_invoice).res;
50+
long amt_msat = 0;
51+
if (invoice.amount_pico_btc() instanceof Option_u64Z.Some) {
52+
amt_msat = ((Option_u64Z.Some)invoice.amount_pico_btc()).some / 10;
53+
}
54+
if (amt_msat == 0) {
55+
// <Handle a zero-value invoice>
56+
}
57+
58+
Route route;
59+
try (LockedNetworkGraph netgraph = router.read_locked_graph()) {
60+
NetworkGraph graph = netgraph.graph();
61+
Result_RouteLightningErrorZ route_res = UtilMethods.get_route(
62+
channel_manager.get_our_node_id(),
63+
graph, invoice.recover_payee_pub_key(), invoice.features(),
64+
channel_manager.list_usable_channels(), invoice.route_hints(),
65+
amt_msat, invoice.min_final_cltv_expiry(), logger);
66+
assert route_res instanceof Result_RouteLightningErrorZ.Result_RouteLightningErrorZ_OK;
67+
route = ((Result_RouteLightningErrorZ.Result_RouteLightningErrorZ_OK) route_res).res;
68+
}
69+
70+
Result_NonePaymentSendFailureZ payment_res = channel_manager.send_payment(
71+
route, invoice.payment_hash(), invoice.payment_secret());
72+
assert payment_res instanceof Result_NonePaymentSendFailureZ.Result_NonePaymentSendFailureZ_OK;
73+
}
74+
```
75+
76+
</template>
77+
<template v-slot:kotlin>
78+
79+
```java
80+
// Get an invoice from the recipient/payee
81+
val parsedInvoice = Invoice.from_str(recipientInvoice)
82+
if (!parsedInvoice.is_ok) {
83+
// Unable to parse invoice
84+
}
85+
86+
val invoice = (parsedInvoice as Result_InvoiceParseOrSemanticErrorZ.Result_InvoiceParseOrSemanticErrorZ_OK).res
87+
88+
var amountSats: Long = 0
89+
if (invoice.amount_milli_satoshis() is Option_u64Z.Some) {
90+
amountSats = (invoice.amount_milli_satoshis() as Option_u64Z.Some).some * 1000
91+
}
92+
93+
if (amountSats == 0L) {
94+
// Handle a zero-value invoice
95+
}
96+
97+
val res = channelManagerConstructor.payer.pay_invoice(invoice)
98+
99+
100+
101+
if (parsed_invoice instanceof Result_InvoiceNoneZ.Result_InvoiceNoneZ_OK) {
102+
Invoice invoice = ((Result_InvoiceNoneZ.Result_InvoiceNoneZ_OK) parsed_invoice).res;
103+
long amt_msat = 0;
104+
if (invoice.amount_pico_btc() instanceof Option_u64Z.Some) {
105+
amt_msat = ((Option_u64Z.Some)invoice.amount_pico_btc()).some / 10;
106+
}
107+
if (amt_msat == 0) {
108+
// <Handle a zero-value invoice>
109+
}
110+
111+
Route route;
112+
try (LockedNetworkGraph netgraph = router.read_locked_graph()) {
113+
NetworkGraph graph = netgraph.graph();
114+
Result_RouteLightningErrorZ route_res = UtilMethods.get_route(
115+
channel_manager.get_our_node_id(),
116+
graph, invoice.recover_payee_pub_key(), invoice.features(),
117+
channel_manager.list_usable_channels(), invoice.route_hints(),
118+
amt_msat, invoice.min_final_cltv_expiry(), logger);
119+
assert route_res instanceof Result_RouteLightningErrorZ.Result_RouteLightningErrorZ_OK;
120+
route = ((Result_RouteLightningErrorZ.Result_RouteLightningErrorZ_OK) route_res).res;
121+
}
122+
123+
Result_NonePaymentSendFailureZ payment_res = channel_manager.send_payment(
124+
route, invoice.payment_hash(), invoice.payment_secret());
125+
assert payment_res instanceof Result_NonePaymentSendFailureZ.Result_NonePaymentSendFailureZ_OK;
126+
}
127+
```
128+
129+
</template>
130+
</CodeSwitcher>
131+
132+
An event is generated once a payment has completed. Successful payments result
133+
in a `PaymentSent` event with the preimage of the payment hash. Be sure to look
134+
out for a `PaymentFailed` event, if the payment fails for some reason, and act
135+
accordingly.
136+
137+
<CodeSwitcher :languages="{rust:'Rust', java:'Java'}">
138+
<template v-slot:rust>
139+
140+
```rust
141+
// In the event handler passed to BackgroundProcessor::start
142+
match event {
143+
Event::PaymentSent { payment_preimage } => {
144+
// Handle successful payment
145+
}
146+
Event::PaymentFailed { payment_hash, rejected_by_dest } => {
147+
// Handle failed payment
148+
}
149+
// ...
150+
}
151+
```
152+
153+
</template>
154+
<template v-slot:java>
155+
156+
```java
157+
// In the `handle_event` method of ChannelManagerPersister implementation
158+
else if (e instanceof Event.PaymentSent) {
159+
// Handle successful payment
160+
Event.PaymentSent event = ((Event.PaymentSent) e);
161+
}
162+
else if (e instanceof Event.PaymentFailed) {
163+
// Handle failed payment
164+
Event.PaymentFailed event = ((Event.PaymentFailed) e);
165+
}
166+
```
167+
168+
</template>
169+
</CodeSwitcher>

0 commit comments

Comments
 (0)