#
Payment
A payment can be either a Bolt11 invoice, Bolt12 offer, LNURL string, Lightning Address, Zap, or npub. Unless a fixed user-requested amount has already been set, the flow should be as follows:
flowchart TD Input[Payment Request to, amount?, needReceipt=true] subgraph Resolve Info A{Must resolve?} B[LNURL, Lightning Address, Bolt12, Zap, npub] C[Bolt11] A ---|Yes| B A ---|No| C E[fetch] D[commentMax, commentMin, minAmount, maxAmount, callbackUrl] B --> E E --> D C --> D end Input --> A D --> FetchFinal[Fetch bolt11 and verify hash/amount] FetchFinal --> PayMethod{NWC enabled?} PayMethod ---|No| PayBolt11WithDeeplinkQR PayBolt11WithDeeplinkQR --> ManuallyCloseAwaitZapEscrowEvent PayMethod ---|Yes| NWCPayInvoice NWCPayInvoice --> NWCResponse{notification received?} NWCResponse --> CloseUI
Payments requests, triggered by components in the app, should queue until they are next to be processed. Edge case.
#
Payments for swaps
When we swap funds from lightning into EVM balance, the preimage from the swap invoice we paid is used to claw back the funds if something goes wrong. We also generate this preimage, so we must store this safely.
When we swap from EVM to lightning, we don't need receipt from lightning that payment was made. As we can just reclaim our EVM balance after a timeout if it has not been claimed. Movement of the EVM funds indicate success or failure.
#
Payments Manager / Ephemeral
class PaymentManager {
List<PaymentCubit> payments = [];
void initiatePayment(double amount) {
var paymentCubit = PaymentCubit(amount);
payments.add(paymentCubit);
emit(paymentCubit)
paymentCubit.stream.listen((paymentStatus) {
if (paymentStatus is TerminalPaymentState) {
payments.remove(paymentCubit);
}
});
}
}
class PaymentCubit extends Cubit<String> {
final double amount;
final String to;
PaymentCubit(this.amount) : super('Initialized');
void resolve()
void confirm()
void listenForResponse()
}
Consume payment requests visually
// Listen to PaymentManager for new payments
BlocListener<PaymentManager, PaymentCubit?>(
listener: (context, paymentCubit) {
if (paymentCubit != null) {
/// Trigger resolution of this payments details
paymentCubit.resolve();
/// Open payment modal
openModal(paymentCubit);
}
}
)