Getting Started with Lightning Payments in Ruby

This article will explore how to use Ruby with the Lighstorm gem to create and verify Lightning payments. We'll cover the steps from both the buyer's and seller's perspectives, including generating invoices, making payments, and verifying transactions using Proof of Payment.

The Lightning Network is a layer two payment protocol built on top of the Bitcoin blockchain. It is designed to enable fast, low-cost, and secure transactions. Lightning payments use smart contracts to facilitate instant payments between parties without requiring confirmation from the Bitcoin network.

Suppose we have a buyer and a seller who want to engage in a financial transaction, such as the buyer purchasing a cup of coffee from the seller. How would this process unfold on the Lightning Network?

Seller's Perspective

First, you generate an Invoice:

require 'lighstorm'

invoice = Lighstorm::Invoice.create(
  description: '1 Cup of Coffee',
  amount: { millisatoshis: 1000 },
  payable: 'once'
).result

Once you have created the Invoice, you can share the Invoice Code with the individual who will be making the purchase:

invoice.code # => 'lnbc10...gfn9yd'
lnbc10n1pjqux8spp5e5vr8d2f50et6y2mgvltyynecxzun22y2urzmxuhvwcp9877u4nsdqcxysyxatsyphkvgzrdanxvet9cqzpgxqyz5vqsp5ku00sl5p5r76eu9aw6n7mzny9d94r03hpr69r9uvh9yc074pepds9qyyssq0q79336f9qpdfztfflmkfyzweucsphw008mhh2nmtz2m27vugpsnjay5q5p5p5d0dl2gvakzplg757xw8efu4734lpgr88z2y9t3rjqqgfn9yd

It's common practice to share the Invoice code via a QR code. There are several methods for generating a QR code, and one option is to use the RQRCode gem to create an SVG image:

require 'rqrcode'

code = RQRCode::QRCode.new("lightning:#{invoice.code}")
code.as_svg(
  color: '000', shape_rendering: 'crispEdges',
  module_size: 11, standalone: true,
  use_path: true, viewbox: true
)

After generating the Invoice, the individual who wants to purchase can use their mobile camera to scan the QR code.

Let's switch to the buyer's perspective now:

Buyer's Perspective

As a buyer, you will receive the Invoice Code through a message or scanning a QR code. To view additional details about the Invoice you have received, you can:

require 'lighstorm'

invoice = Lighstorm::Invoice.decode(
  'lnbc10n1pjqux8spp5e5vr8d2f50et6y2mgvltyynecxzun22y2urzmxuhvwcp9877u4nsdqcxysyxatsyphkvgzrdanxvet9cqzpgxqyz5vqsp5ku00sl5p5r76eu9aw6n7mzny9d94r03hpr69r9uvh9yc074pepds9qyyssq0q79336f9qpdfztfflmkfyzweucsphw008mhh2nmtz2m27vugpsnjay5q5p5p5d0dl2gvakzplg757xw8efu4734lpgr88z2y9t3rjqqgfn9yd'
)

invoice.amount.millisatoshis # => 1000
invoice.description.memo # => '1 Cup of Coffee'

Great! You have reviewed the amount and description and confirmed it's the correct Invoice. The next step is to pay the Invoice:

payment = invoice.pay.result

If you encounter an error when attempting to make a payment, such as a NoRouteFoundError error, one common reason for this issue is that you do not have a direct channel with the seller. You may need to use third-party channels to complete the payment in this case.

While the Lightning Network handles this process automatically, it's worth noting that third-party channels may not be free to use. Fees may apply to utilize these channels, as those facilitating the payment would like to be compensated for helping you. As such, adding extra money to cover possible fees is necessary.

payment = invoice.pay(
  fee: { maximum: { millisatoshis: 10 } }
).result

To verify that the payment has been successfully made and to check the amount paid, including possible fees, you can:

payment.state # => 'succeeded'
payment.amount.millisatoshis #=> 1000
payment.fee.millisatoshis #=> 0

Validating the Transaction

It seems that we're all set. However, how can the seller be sure that the buyer has paid the Invoice? To verify the payment status, the seller can locate the Invoice and check its current state:

invoice = Lighstorm::Invoice.find_by_code(
  'lnbc10n1pjqux8spp5e5vr8d2f50et6y2mgvltyynecxzun22y2urzmxuhvwcp9877u4nsdqcxysyxatsyphkvgzrdanxvet9cqzpgxqyz5vqsp5ku00sl5p5r76eu9aw6n7mzny9d94r03hpr69r9uvh9yc074pepds9qyyssq0q79336f9qpdfztfflmkfyzweucsphw008mhh2nmtz2m27vugpsnjay5q5p5p5d0dl2gvakzplg757xw8efu4734lpgr88z2y9t3rjqqgfn9yd'
)

invoice.state # => 'settled'

Similarly, the buyer can also double-check the payment status:

payment = Lighstorm::Payment.find_by_invoice_code(
  'lnbc10n1pjqux8spp5e5vr8d2f50et6y2mgvltyynecxzun22y2urzmxuhvwcp9877u4nsdqcxysyxatsyphkvgzrdanxvet9cqzpgxqyz5vqsp5ku00sl5p5r76eu9aw6n7mzny9d94r03hpr69r9uvh9yc074pepds9qyyssq0q79336f9qpdfztfflmkfyzweucsphw008mhh2nmtz2m27vugpsnjay5q5p5p5d0dl2gvakzplg757xw8efu4734lpgr88z2y9t3rjqqgfn9yd'
)

payment.state # => 'succeeded'

Although you know that the Invoice has been paid, it's important to ensure that the buyer truly is the one who made the payment. Someone else could have paid the Invoice, or the buyer could be presenting a fake payment. To address this issue, the seller can request Proof of Payment from the buyer that is only accessible to the legitimate payer of the Invoice.

The buyer can access its Proof of Payment to share with the seller:

payment.secret.proof
0c9e04be6034655f19cd1b9a771dcb6600dc28f4adb058ae1885c8c766f81296

Then, the seller can verify if it is legitimate:

invoice.secret.valid_proof?(
	'0c9e04be6034655f19cd1b9a771dcb6600dc28f4adb058ae1885c8c766f81296'
) # => true

There we ago, now we are 100% confident and the seller can deliver the cup of coffee to the buyer.

Although Proof of Payment may not be necessary for in-person transactions, as the seller can verify the payment on the spot, it becomes crucial in remote transactions or e-commerce. It's important to ensure that the person claiming to have paid for the product is the legitimate payer before delivering the product.

Two Stories

Let's explore some short stories that showcase how the concepts we just learned can be applied in real-world scenarios:

A simple cup of coffee

Buyer: Hey, I would like a coffee, please.
Seller: Sure, it's one satoshi. Here's the invoice:

invoice = Lighstorm::Invoice.create(
  description: '1 Cup of Coffee',
  amount: { millisatoshis: 1000 },
  payable: 'once'
).result

invoice.code
lnbc10n1pjqus8jpp5td8cjd7y0za7x22asl7yee585s6vd4776nqnutv4vdu0hq6n54msdqcxysyxatsyphkvgzrdanxvet9cqzpgxqyz5vqsp5a2fhccxzwm0mva8g9nrr3l04rzp4vvycj4xj9dj9zagv2qg5l3ms9qyyssqg426awt2svu2eak5dm4sendryc42lz9czqs7ztlwkxetd43nhkg5eflegad0ll683t08xxfw87369cv48wkm4w9877vzlxfkwjrywjcq7sgdg6

Buyer: Great, let me pay for it:

payment = Lighstorm::Invoice.decode(
  'lnbc10n1pjqus8jpp5td8cjd7y0za7x22asl7yee585s6vd4776nqnutv4vdu0hq6n54msdqcxysyxatsyphkvgzrdanxvet9cqzpgxqyz5vqsp5a2fhccxzwm0mva8g9nrr3l04rzp4vvycj4xj9dj9zagv2qg5l3ms9qyyssqg426awt2svu2eak5dm4sendryc42lz9czqs7ztlwkxetd43nhkg5eflegad0ll683t08xxfw87369cv48wkm4w9877vzlxfkwjrywjcq7sgdg6'
).pay(
  fee: { maximum: { millisatoshis: 1000 } }
).result

payment.state # => 'succeeded'

Buyer: Done!
Seller: Just one second:

 Lighstorm::Invoice.find_by_code(
  'lnbc10n1pjqux8spp5e5vr8d2f50et6y2mgvltyynecxzun22y2urzmxuhvwcp9877u4nsdqcxysyxatsyphkvgzrdanxvet9cqzpgxqyz5vqsp5ku00sl5p5r76eu9aw6n7mzny9d94r03hpr69r9uvh9yc074pepds9qyyssq0q79336f9qpdfztfflmkfyzweucsphw008mhh2nmtz2m27vugpsnjay5q5p5p5d0dl2gvakzplg757xw8efu4734lpgr88z2y9t3rjqqgfn9yd'
).state # => 'settled'

Seller: All good, here's your coffee, have a nice day!
Buyer: Thank you!

The fraudulent buyer

Buyer: Hey, I'm the one who just bought the keyboard you sold. Can I share an address so you can deliver the product?
Seller: Sure. May you please share the proof of payment?
Buyer: Here it is:

66d408901c89d0038e1630b67ac494f7bc35038374e42e5793bc985bce4ceecf

Seller: Great, just a second:

Lighstorm::Invoice.find_by_code(
  'lnbc10n1pjqux8spp5e5vr8d2f50et6y2mgvltyynecxzun22y2urzmxuhvwcp9877u4nsdqcxysyxatsyphkvgzrdanxvet9cqzpgxqyz5vqsp5ku00sl5p5r76eu9aw6n7mzny9d94r03hpr69r9uvh9yc074pepds9qyyssq0q79336f9qpdfztfflmkfyzweucsphw008mhh2nmtz2m27vugpsnjay5q5p5p5d0dl2gvakzplg757xw8efu4734lpgr88z2y9t3rjqqgfn9yd'
).secret.valid_proof?(
	'66d408901c89d0038e1630b67ac494f7bc35038374e42e5793bc985bce4ceecf'
) # => false

Seller: So, I'm afraid there's something wrong. This proof isn't valid...
Buyer: (became offline)

Wrapping Up

Creating and paying invoices is a crucial part of using the Lightning Network, and I hope this article has provided you with a helpful starting point.

There are numerous other ways to exchange money using Lightning beyond what showed here, and I plan to delve into those topics in future articles. So, stay tuned!

Thank you for taking the time to read this article. Happy coding!

Subscribe to icebaker
Receive the latest updates directly to your inbox.
Mint this entry as an NFT to add it to your collection.
Verification
This entry has been permanently stored onchain and signed by its creator.