Receiving payments

With the Breez SDK you aren't required to open a channel and set up your inbound liquidity.

Once the SDK is initialized, you can directly begin receiving payments. The receive process takes two steps:

  1. Preparing the Payment
  2. Receiving the Payment

Developer note

Consider implementing the Notification Plugin when using the Breez SDK in a mobile application. By registering a webhook the application can receive notifications to process the payment in the background.

Preparing Payments API docs

During the prepare step, the SDK ensures that the inputs are valid with respect to the specified payment method, and also returns the relative fees related to the payment so they can be confirmed.

The SDK currently supports three methods of receiving: Lightning, Bitcoin and Liquid:

Lightning

When receiving via Lightning, we generate an invoice to be paid. Note that the payment may fallback to a direct Liquid payment (if the payer's client supports this).

Note: The amount field is currently mandatory when paying via Lightning.

Rust
// Fetch the Receive lightning limits
let current_limits = sdk.fetch_lightning_limits().await?;
info!("Minimum amount: {} sats", current_limits.receive.min_sat);
info!("Maximum amount: {} sats", current_limits.receive.max_sat);

// Set the invoice amount you wish the payer to send, which should be within the above limits
let optional_amount = Some(ReceiveAmount::Bitcoin {
    payer_amount_sat: 5_000,
});
let prepare_response = sdk
    .prepare_receive_payment(&PrepareReceiveRequest {
        payment_method: PaymentMethod::Lightning,
        amount: optional_amount,
    })
    .await?;

// If the fees are acceptable, continue to create the Receive Payment
let receive_fees_sat = prepare_response.fees_sat;
info!("Fees: {} sats", receive_fees_sat);
Swift
// Fetch the Receive lightning limits
let currentLimits = try? sdk.fetchLightningLimits()
print("Minimum amount: {} sats", currentLimits?.receive.minSat ?? 0);
print("Maximum amount: {} sats", currentLimits?.receive.maxSat ?? 0);

// Set the invoice amount you wish the payer to send, which should be within the above limits
let optionalAmount = ReceiveAmount.bitcoin(payerAmountSat: 5_000)
let prepareResponse = try? sdk
    .prepareReceivePayment(req: PrepareReceiveRequest(
        paymentMethod: PaymentMethod.lightning,
        amount: optionalAmount
    ));

// If the fees are acceptable, continue to create the Receive Payment
let receiveFeesSat = prepareResponse!.feesSat;
print("Fees: {} sats", receiveFeesSat);
Kotlin
try {
    // Fetch the lightning Receive limits
    val currentLimits = sdk.fetchLightningLimits()
    // Log.v("Breez", "Minimum amount allowed to deposit in sats: ${currentLimits.receive.minSat}")
    // Log.v("Breez", "Maximum amount allowed to deposit in sats: ${currentLimits.receive.maxSat}")

    // Set the invoice amount you wish the payer to send, which should be within the above limits
    val optionalAmount = ReceiveAmount.Bitcoin(5_000.toULong())
    val prepareRequest = PrepareReceiveRequest(PaymentMethod.LIGHTNING, optionalAmount)
    val prepareResponse = sdk.prepareReceivePayment(prepareRequest)

    // If the fees are acceptable, continue to create the Receive Payment
    val receiveFeesSat = prepareResponse.feesSat;
    // Log.v("Breez", "Fees: ${receiveFeesSat} sats")
} catch (e: Exception) {
    // handle error
}
React Native
// Fetch the Receive lightning limits
const currentLimits = await fetchLightningLimits()
console.log(`Minimum amount, in sats: ${currentLimits.receive.minSat}`)
console.log(`Maximum amount, in sats: ${currentLimits.receive.maxSat}`)

// Set the amount you wish the payer to send via lightning, which should be within the above limits
const optionalAmount: ReceiveAmount = {
  type: ReceiveAmountVariant.BITCOIN,
  payerAmountSat: 5_000
}

const prepareResponse = await prepareReceivePayment({
  paymentMethod: PaymentMethod.LIGHTNING,
  amount: optionalAmount
})

// If the fees are acceptable, continue to create the Receive Payment
const receiveFeesSat = prepareResponse.feesSat
console.log(`Fees: ${receiveFeesSat} sats`)
Dart
// Fetch the Receive lightning limits
LightningPaymentLimitsResponse currentLightningLimits =
    await breezSDKLiquid.instance!.fetchLightningLimits();
print("Minimum amount: ${currentLightningLimits.receive.minSat} sats");
print("Maximum amount: ${currentLightningLimits.receive.maxSat} sats");

// Create an invoice and set the amount you wish the payer to send
ReceiveAmount_Bitcoin optionalAmount = ReceiveAmount_Bitcoin(payerAmountSat: 5000 as BigInt);
PrepareReceiveResponse prepareResponse = await breezSDKLiquid.instance!.prepareReceivePayment(
  req: PrepareReceiveRequest(
    paymentMethod: PaymentMethod.lightning,
    amount: optionalAmount,
  ),
);

// If the fees are acceptable, continue to create the Receive Payment
BigInt receiveFeesSat = prepareResponse.feesSat;
print("Fees: $receiveFeesSat sats");
Python
try:
    # Fetch the lightning Receive limits
    current_limits = sdk.fetch_lightning_limits()
    logging.debug("Minimum amount allowed to deposit in sats ", current_limits.receive.min_sat)
    logging.debug("Maximum amount allowed to deposit in sats ", current_limits.receive.max_sat)

    # Set the invoice amount you wish the payer to send, which should be within the above limits
    optional_amount = ReceiveAmount.BITCOIN(5_000)
    prepare_request = PrepareReceiveRequest(PaymentMethod.LIGHTNING, optional_amount)
    prepare_response = sdk.prepare_receive_payment(prepare_request)

    # If the fees are acceptable, continue to create the Receive Payment
    receive_fees_sat = prepare_response.fees_sat
    logging.debug("Fees: ", receive_fees_sat, " sats")
    return prepare_response
except Exception as error:
    logging.error(error)
    raise
Go
// Fetch the lightning Receive limits
if currentLimits, err := sdk.FetchLightningLimits(); err == nil {
    log.Printf("Minimum amount, in sats: %v", currentLimits.Receive.MinSat)
    log.Printf("Maximum amount, in sats: %v", currentLimits.Receive.MaxSat)
}

// Set the invoice amount you wish the payer to send, which should be within the above limits
var optionalAmount breez_sdk_liquid.ReceiveAmount = breez_sdk_liquid.ReceiveAmountBitcoin{
    PayerAmountSat: uint64(5_000),
}
prepareRequest := breez_sdk_liquid.PrepareReceiveRequest{
    PaymentMethod: breez_sdk_liquid.PaymentMethodLightning,
    Amount:        &optionalAmount,
}
if prepareResponse, err := sdk.PrepareReceivePayment(prepareRequest); err == nil {
    // If the fees are acceptable, continue to create the Receive Payment
    receiveFeesSat := prepareResponse.FeesSat
    log.Printf("Fees: %v sats", receiveFeesSat)
}
C#
try
{
    // Fetch the lightning Receive limits
    var currentLimits = sdk.FetchLightningLimits();
    Console.WriteLine($"Minimum amount allowed to deposit in sats: {currentLimits.receive.minSat}");
    Console.WriteLine($"Maximum amount allowed to deposit in sats: {currentLimits.receive.maxSat}");

    // Set the invoice amount you wish the payer to send, which should be within the above limits
    var optionalAmount = new ReceiveAmount.Bitcoin(5000);
    var prepareRequest = new PrepareReceiveRequest(PaymentMethod.Lightning, optionalAmount);
    var prepareResponse = sdk.PrepareReceivePayment(prepareRequest);

    // If the fees are acceptable, continue to create the Receive Payment
    var receiveFeesSat = prepareResponse.feesSat;
    Console.WriteLine($"Fees: {receiveFeesSat} sats");
}
catch (Exception)
{
    // Handle error
}

Bitcoin

When receiving via Bitcoin, we generate a Bitcoin BIP21 URI to be paid.

The amount field is optional when preparing a Bitcoin payment. However, if no amount is provided, the returned fees will only be an estimation. This is because:

  1. The fees have an amount-dependent component that can only be determined once the sender initiates the payment
  2. The fees also depend on current onchain fee conditions, which may change between the time of preparation and actual payment

If the onchain fee rate increases between preparation and payment time, the payment will be put on hold until the user explicitly confirms the new fees. To learn more about this, see the Amountless Bitcoin Payments section below.

Rust
// Fetch the Receive onchain limits
let current_limits = sdk.fetch_onchain_limits().await?;
info!("Minimum amount: {} sats", current_limits.receive.min_sat);
info!("Maximum amount: {} sats", current_limits.receive.max_sat);

// Set the onchain amount you wish the payer to send, which should be within the above limits
let optional_amount = Some(ReceiveAmount::Bitcoin {
    payer_amount_sat: 5_000,
});
let prepare_response = sdk
    .prepare_receive_payment(&PrepareReceiveRequest {
        payment_method: PaymentMethod::BitcoinAddress,
        amount: optional_amount,
    })
    .await?;

// If the fees are acceptable, continue to create the Receive Payment
let receive_fees_sat = prepare_response.fees_sat;
info!("Fees: {} sats", receive_fees_sat);
Swift
// Fetch the Receive onchain limits
let currentLimits = try? sdk.fetchOnchainLimits()
print("Minimum amount: {} sats", currentLimits?.receive.minSat ?? 0);
print("Maximum amount: {} sats", currentLimits?.receive.maxSat ?? 0);

// Set the onchain amount you wish the payer to send, which should be within the above limits
let optionalAmount = ReceiveAmount.bitcoin(payerAmountSat: 5_000)
let prepareResponse = try? sdk
    .prepareReceivePayment(req: PrepareReceiveRequest(
        paymentMethod: PaymentMethod.bitcoinAddress,
        amount: optionalAmount
    ));

// If the fees are acceptable, continue to create the Receive Payment
let receiveFeesSat = prepareResponse!.feesSat;
print("Fees: {} sats", receiveFeesSat);
Kotlin
try {
    // Fetch the onchain Receive limits
    val currentLimits = sdk.fetchOnchainLimits()
    // Log.v("Breez", "Minimum amount allowed to deposit in sats: ${currentLimits.receive.minSat}")
    // Log.v("Breez", "Maximum amount allowed to deposit in sats: ${currentLimits.receive.maxSat}")

    // Set the onchain amount you wish the payer to send, which should be within the above limits
    val optionalAmount = ReceiveAmount.Bitcoin(5_000.toULong())
    val prepareRequest = PrepareReceiveRequest(PaymentMethod.BITCOIN_ADDRESS, optionalAmount)
    val prepareResponse = sdk.prepareReceivePayment(prepareRequest)

    // If the fees are acceptable, continue to create the Receive Payment
    val receiveFeesSat = prepareResponse.feesSat;
    // Log.v("Breez", "Fees: ${receiveFeesSat} sats")
} catch (e: Exception) {
    // handle error
}
React Native
// Fetch the Onchain lightning limits
const currentLimits = await fetchOnchainLimits()
console.log(`Minimum amount, in sats: ${currentLimits.receive.minSat}`)
console.log(`Maximum amount, in sats: ${currentLimits.receive.maxSat}`)

// Set the onchain amount you wish the payer to send, which should be within the above limits
const optionalAmount: ReceiveAmount = {
  type: ReceiveAmountVariant.BITCOIN,
  payerAmountSat: 5_000
}

const prepareResponse = await prepareReceivePayment({
  paymentMethod: PaymentMethod.BITCOIN_ADDRESS,
  amount: optionalAmount
})

// If the fees are acceptable, continue to create the Receive Payment
const receiveFeesSat = prepareResponse.feesSat
console.log(`Fees: ${receiveFeesSat} sats`)
Dart
// Fetch the Receive onchain limits
OnchainPaymentLimitsResponse currentOnchainLimits = await breezSDKLiquid.instance!.fetchOnchainLimits();
print("Minimum amount: ${currentOnchainLimits.receive.minSat} sats");
print("Maximum amount: ${currentOnchainLimits.receive.maxSat} sats");

// Or create a cross-chain transfer (Liquid to Bitcoin) via chain swap
ReceiveAmount_Bitcoin optionalAmount = ReceiveAmount_Bitcoin(payerAmountSat: 5000 as BigInt);
PrepareReceiveResponse prepareResponse = await breezSDKLiquid.instance!.prepareReceivePayment(
  req: PrepareReceiveRequest(
    paymentMethod: PaymentMethod.bitcoinAddress,
    amount: optionalAmount,
  ),
);

// If the fees are acceptable, continue to create the Receive Payment
BigInt receiveFeesSat = prepareResponse.feesSat;
print("Fees: $receiveFeesSat sats");
Python
try:
    # Fetch the onchain Receive limits
    current_limits = sdk.fetch_onchain_limits()
    logging.debug("Minimum amount allowed to deposit in sats ", current_limits.receive.min_sat)
    logging.debug("Maximum amount allowed to deposit in sats ", current_limits.receive.max_sat)

    # Set the onchain amount you wish the payer to send, which should be within the above limits
    optional_amount = ReceiveAmount.BITCOIN(5_000)
    prepare_request = PrepareReceiveRequest(PaymentMethod.BITCOIN_ADDRESS, optional_amount)
    prepare_response = sdk.prepare_receive_payment(prepare_request)

    # If the fees are acceptable, continue to create the Receive Payment
    receive_fees_sat = prepare_response.fees_sat
    logging.debug("Fees: ", receive_fees_sat, " sats")
    return prepare_response
except Exception as error:
    logging.error(error)
    raise
Go
// Fetch the onchain Receive limits
if currentLimits, err := sdk.FetchOnchainLimits(); err == nil {
    log.Printf("Minimum amount, in sats: %v", currentLimits.Receive.MinSat)
    log.Printf("Maximum amount, in sats: %v", currentLimits.Receive.MaxSat)
}

// Set the onchain amount you wish the payer to send, which should be within the above limits
var optionalAmount breez_sdk_liquid.ReceiveAmount = breez_sdk_liquid.ReceiveAmountBitcoin{
    PayerAmountSat: uint64(5_000),
}
prepareRequest := breez_sdk_liquid.PrepareReceiveRequest{
    PaymentMethod: breez_sdk_liquid.PaymentMethodBitcoinAddress,
    Amount:        &optionalAmount,
}
if prepareResponse, err := sdk.PrepareReceivePayment(prepareRequest); err == nil {
    // If the fees are acceptable, continue to create the Receive Payment
    receiveFeesSat := prepareResponse.FeesSat
    log.Printf("Fees: %v sats", receiveFeesSat)
}
C#
try
{
    // Fetch the onchain Receive limits
    var currentLimits = sdk.FetchOnchainLimits();
    Console.WriteLine($"Minimum amount allowed to deposit in sats: {currentLimits.receive.minSat}");
    Console.WriteLine($"Maximum amount allowed to deposit in sats: {currentLimits.receive.maxSat}");

    // Set the onchain amount you wish the payer to send, which should be within the above limits
    var optionalAmount = new ReceiveAmount.Bitcoin(5000);
    var prepareRequest = new PrepareReceiveRequest(PaymentMethod.BitcoinAddress, optionalAmount);
    var prepareResponse = sdk.PrepareReceivePayment(prepareRequest);

    // If the fees are acceptable, continue to create the Receive Payment
    var receiveFeesSat = prepareResponse.feesSat;
    Console.WriteLine($"Fees: {receiveFeesSat} sats");
}
catch (Exception)
{
    // Handle error
}

Developer note

The above checks include validating against maximum and minimum limits. Even when no specific amount is provided, the amount transferred to the swap address must still fall within these limits. Your application's users must be informed of these limits because if the amount transferred falls outside this valid range, the funds will not be successfully received via the normal swap flow. In such cases, a manual refund will be necessary. For further instructions on how to execute a manual refund, see the section on refunding payments.

Liquid

When receiving via Liquid, we can either generate an address to receive to, or a BIP21 URI with information regarding the payment (currently only the amount and message).

To generate a BIP21 address, all you have to do is specify a payer amount.

Developer note

To receive non-Bitcoin assets, see Handling multiple assets.
Rust
// Create a Liquid BIP21 URI/address to receive a payment to.
// There are no limits, but the payer amount should be greater than broadcast fees when specified
// Note: Not setting the amount will generate a plain Liquid address
let optional_amount = Some(ReceiveAmount::Bitcoin {
    payer_amount_sat: 5_000,
});
let prepare_response = sdk
    .prepare_receive_payment(&PrepareReceiveRequest {
        payment_method: PaymentMethod::LiquidAddress,
        amount: optional_amount,
    })
    .await?;

// If the fees are acceptable, continue to create the Receive Payment
let receive_fees_sat = prepare_response.fees_sat;
info!("Fees: {} sats", receive_fees_sat);
Swift
// Create a Liquid BIP21 URI/address to receive a payment to.
// There are no limits, but the payer amount should be greater than broadcast fees when specified
// Note: Not setting the amount will generate a plain Liquid address
let optionalAmount = ReceiveAmount.bitcoin(payerAmountSat: 5_000)
let prepareResponse = try? sdk
    .prepareReceivePayment(req: PrepareReceiveRequest(
        paymentMethod: PaymentMethod.liquidAddress,
        amount: optionalAmount
    ))

// If the fees are acceptable, continue to create the Receive Payment
let receiveFeesSat = prepareResponse!.feesSat;
print("Fees: {} sats", receiveFeesSat);
Kotlin
try {
    // Create a Liquid BIP21 URI/address to receive a payment to.
    // There are no limits, but the payer amount should be greater than broadcast fees when specified
    // Note: Not setting the amount will generate a plain Liquid address
    val optionalAmount = ReceiveAmount.Bitcoin(5_000.toULong())
    val prepareRequest = PrepareReceiveRequest(PaymentMethod.LIQUID_ADDRESS, optionalAmount)
    val prepareResponse = sdk.prepareReceivePayment(prepareRequest)

    // If the fees are acceptable, continue to create the Receive Payment
    val receiveFeesSat = prepareResponse.feesSat;
    // Log.v("Breez", "Fees: ${receiveFeesSat} sats")
} catch (e: Exception) {
    // handle error
}
React Native

// Create a Liquid BIP21 URI/address to receive a payment to.
// There are no limits, but the payer amount should be greater than broadcast fees when specified
// Note: Not setting the amount will generate a plain Liquid address
const optionalAmount: ReceiveAmount = {
  type: ReceiveAmountVariant.BITCOIN,
  payerAmountSat: 5_000
}

const prepareResponse = await prepareReceivePayment({
  paymentMethod: PaymentMethod.LIQUID_ADDRESS,
  amount: optionalAmount
})

// If the fees are acceptable, continue to create the Receive Payment
const receiveFeesSat = prepareResponse.feesSat
console.log(`Fees: ${receiveFeesSat} sats`)
Dart
// Create a Liquid BIP21 URI/address to receive a payment to.
// There are no limits, but the payer amount should be greater than broadcast fees when specified
// Note: Not setting the amount will generate a plain Liquid address
ReceiveAmount_Bitcoin optionalAmount = ReceiveAmount_Bitcoin(payerAmountSat: 5000 as BigInt);
PrepareReceiveResponse prepareResponse = await breezSDKLiquid.instance!.prepareReceivePayment(
  req: PrepareReceiveRequest(
    paymentMethod: PaymentMethod.liquidAddress,
    amount: optionalAmount,
  ),
);

// If the fees are acceptable, continue to create the Receive Payment
BigInt receiveFeesSat = prepareResponse.feesSat;
print("Fees: $receiveFeesSat sats");
Python
try:
    # Create a Liquid BIP21 URI/address to receive a payment to.
    # There are no limits, but the payer amount should be greater than broadcast fees when specified
    # Note: Not setting the amount will generate a plain Liquid address
    optional_amount = ReceiveAmount.BITCOIN(5_000)
    prepare_request = PrepareReceiveRequest(PaymentMethod.LIQUID_ADDRESS, optional_amount)
    prepare_response = sdk.prepare_receive_payment(prepare_request)

    # If the fees are acceptable, continue to create the Receive Payment
    receive_fees_sat = prepare_response.fees_sat
    logging.debug("Fees: ", receive_fees_sat, " sats")
    return prepare_response
except Exception as error:
    logging.error(error)
    raise
Go
// Create a Liquid BIP21 URI/address to receive a payment to.
// There are no limits, but the payer amount should be greater than broadcast fees when specified
// Note: Not setting the amount will generate a plain Liquid address
var optionalAmount breez_sdk_liquid.ReceiveAmount = breez_sdk_liquid.ReceiveAmountBitcoin{
    PayerAmountSat: uint64(5_000),
}
prepareRequest := breez_sdk_liquid.PrepareReceiveRequest{
    PaymentMethod: breez_sdk_liquid.PaymentMethodLiquidAddress,
    Amount:        &optionalAmount,
}
if prepareResponse, err := sdk.PrepareReceivePayment(prepareRequest); err == nil {
    // If the fees are acceptable, continue to create the Receive Payment
    receiveFeesSat := prepareResponse.FeesSat
    log.Printf("Fees: %v sats", receiveFeesSat)
}
C#
try
{
    // Create a Liquid BIP21 URI/address to receive a payment to.
    // There are no limits, but the payer amount should be greater than broadcast fees when specified
    // Note: Not setting the amount will generate a plain Liquid address
    var optionalAmount = new ReceiveAmount.Bitcoin(5000);
    var prepareRequest = new PrepareReceiveRequest(PaymentMethod.LiquidAddress, optionalAmount);
    var prepareResponse = sdk.PrepareReceivePayment(prepareRequest);

    // If the fees are acceptable, continue to create the Receive Payment
    var receiveFeesSat = prepareResponse.feesSat;
    Console.WriteLine($"Fees: {receiveFeesSat} sats");
}
catch (Exception)
{
    // Handle error
}

Receiving Payments API docs

Once the payment has been prepared, all you have to do is pass the prepare response as an argument to the receive method, optionally specifying a description.

Note: The description field will be used differently, depending on the payment method:

  • For Lightning payments, it will be encoded in the invoice
  • For Bitcoin/Liquid BIP21 payments, it will be encoded in the URI as the message field.
  • For plain Liquid payments, the description has no effect.
Rust
let optional_description = Some("<description>".to_string());
let res = sdk
    .receive_payment(&ReceivePaymentRequest {
        prepare_response,
        description: optional_description,
        use_description_hash: None,
    })
    .await?;

let destination = res.destination;
Swift
let optionalDescription = "<description>"
let res = try? sdk.receivePayment(req: ReceivePaymentRequest(
        prepareResponse: prepareResponse,
        description: optionalDescription
    ))

let destination: String = res!.destination;
print("Destination: {}", destination);
Kotlin
try {
    val optionalDescription = "<description>";
    val req = ReceivePaymentRequest(prepareResponse, optionalDescription)
    val res = sdk.receivePayment(req)
    val destination = res.destination;
} catch (e: Exception) {
    // handle error
}
React Native
const optionalDescription = '<description>'
const res = await receivePayment({
  prepareResponse,
  description: optionalDescription
})

const destination = res.destination
Dart
String optionalDescription = "<description>";
ReceivePaymentResponse res = await breezSDKLiquid.instance!.receivePayment(
  req: ReceivePaymentRequest(
    description: optionalDescription,
    prepareResponse: prepareResponse,
  ),
);

String destination = res.destination;
Python
try:
    optional_description = "<description>"
    req = ReceivePaymentRequest(prepare_response, optional_description)
    res = sdk.receive_payment(req)
    destination = res.destination
except Exception as error:
    logging.error(error)
    raise
Go
optionalDescription := "<description>"
req := breez_sdk_liquid.ReceivePaymentRequest{
    PrepareResponse: prepareResponse,
    Description:     &optionalDescription,
}
if res, err := sdk.ReceivePayment(req); err == nil {
    // If the fees are acceptable, continue to create the Receive Payment
    destination := res.Destination
    log.Printf("Destination: %v", destination)
}
C#
try
{
    var optionalDescription = "<description>";
    var req = new ReceivePaymentRequest(prepareResponse, optionalDescription);
    var res = sdk.ReceivePayment(req);
    var destination = res.destination;
}
catch (Exception)
{
    // Handle error
}

Amountless Bitcoin Payments

To receive a Bitcoin payment that does not specify an amount, it may be necessary to explicitly accept the associated fees. This will be the case when the onchain fee rate increases between preparation and payment time.

Alternatively, if the fees are considered too high, the user can either choose to wait for them to come down or outright refund the payment. To learn more about refunds, see the Refunding payments section.

To reduce the likelihood of this extra fee review step being necessary, you can configure a fee rate leeway in the SDK's configuration that will automatically accept slightly higher fees within the specified tolerance.

Rust
// Payments on hold waiting for fee acceptance have the state WaitingFeeAcceptance
let payments_waiting_fee_acceptance = sdk
    .list_payments(&ListPaymentsRequest {
        states: Some(vec![WaitingFeeAcceptance]),
        ..Default::default()
    })
    .await?;

for payment in payments_waiting_fee_acceptance {
    let PaymentDetails::Bitcoin { swap_id, .. } = payment.details else {
        // Only Bitcoin payments can be `WaitingFeeAcceptance`
        continue;
    };

    let fetch_fees_response = sdk
        .fetch_payment_proposed_fees(&FetchPaymentProposedFeesRequest { swap_id })
        .await?;

    info!(
        "Payer sent {} and currently proposed fees are {}",
        fetch_fees_response.payer_amount_sat, fetch_fees_response.fees_sat
    );

    // If the user is ok with the fees, accept them, allowing the payment to proceed
    sdk.accept_payment_proposed_fees(&AcceptPaymentProposedFeesRequest {
        response: fetch_fees_response,
    })
    .await?;
}
Swift
// Payments on hold waiting for fee acceptance have the state WaitingFeeAcceptance
guard
    let paymentsWaitingFeeAcceptance = try? sdk.listPayments(
        req: ListPaymentsRequest(states: [.waitingFeeAcceptance]))
else { return }

for payment in paymentsWaitingFeeAcceptance {
    guard case .bitcoin(let swapId, _, _, _, _, _, _, _) = payment.details else { continue }

    // Only Bitcoin payments can be `WaitingFeeAcceptance`
    guard
        let fetchFeesResponse = try? sdk.fetchPaymentProposedFees(
            req: FetchPaymentProposedFeesRequest(
                swapId: swapId))
    else { continue }

    print(
        "Payer sent \(fetchFeesResponse.payerAmountSat) and currently proposed fees are \(fetchFeesResponse.feesSat)"
    )

    // If the user is ok with the fees, accept them, allowing the payment to proceed
    try? sdk.acceptPaymentProposedFees(
        req: AcceptPaymentProposedFeesRequest(
            response: fetchFeesResponse))
}
Kotlin
try {
    // Payments on hold waiting for fee acceptance have the state WaitingFeeAcceptance
    val paymentsWaitingFeeAcceptance = sdk.listPayments(ListPaymentsRequest(
        states = listOf(PaymentState.WAITING_FEE_ACCEPTANCE)
    ))

    for (payment in paymentsWaitingFeeAcceptance) {
        when (val details = payment.details) {
            is PaymentDetails.Bitcoin -> {
                val fetchFeesResponse = sdk.fetchPaymentProposedFees(
                    FetchPaymentProposedFeesRequest(details.swapId)
                )

                println("Payer sent ${fetchFeesResponse.payerAmountSat} and currently proposed fees are ${fetchFeesResponse.feesSat}")

                // If the user is ok with the fees, accept them, allowing the payment to proceed
                sdk.acceptPaymentProposedFees(AcceptPaymentProposedFeesRequest(fetchFeesResponse))
            }
            else -> {
                // Only Bitcoin payments can be `WaitingFeeAcceptance`
                continue
            }
        }
    }
} catch (e: Exception) {
    // handle error
}
React Native
// Payments on hold waiting for fee acceptance have the state WAITING_FEE_ACCEPTANCE
const paymentsWaitingFeeAcceptance = await listPayments({
  states: [PaymentState.WAITING_FEE_ACCEPTANCE]
})

for (const payment of paymentsWaitingFeeAcceptance) {
  if (payment.details.type !== PaymentDetailsVariant.BITCOIN) {
    // Only Bitcoin payments can be `WAITING_FEE_ACCEPTANCE`
    continue
  }

  const fetchFeesResponse = await fetchPaymentProposedFees({
    swapId: payment.details.swapId
  })

  console.info(
    `Payer sent ${fetchFeesResponse.payerAmountSat} and currently proposed fees are ${fetchFeesResponse.feesSat}`
  )

  // If the user is ok with the fees, accept them, allowing the payment to proceed
  await acceptPaymentProposedFees({
    response: fetchFeesResponse
  })
}
Dart
// Payments on hold waiting for fee acceptance have the state WaitingFeeAcceptance
List<Payment> paymentsWaitingFeeAcceptance = await breezSDKLiquid.instance!.listPayments(
  req: ListPaymentsRequest(
    states: [PaymentState.waitingFeeAcceptance],
  ),
);

for (Payment payment in paymentsWaitingFeeAcceptance) {
  if (payment.details is! PaymentDetails_Bitcoin) {
    // Only Bitcoin payments can be `WaitingFeeAcceptance`
    continue;
  }

  PaymentDetails_Bitcoin details = payment.details as PaymentDetails_Bitcoin;
  FetchPaymentProposedFeesResponse fetchFeesResponse =
      await breezSDKLiquid.instance!.fetchPaymentProposedFees(
    req: FetchPaymentProposedFeesRequest(
      swapId: details.swapId,
    ),
  );

  print(
    "Payer sent ${fetchFeesResponse.payerAmountSat} and currently proposed fees are ${fetchFeesResponse.feesSat}",
  );

  // If the user is ok with the fees, accept them, allowing the payment to proceed
  await breezSDKLiquid.instance!.acceptPaymentProposedFees(
    req: AcceptPaymentProposedFeesRequest(
      response: fetchFeesResponse,
    ),
  );
}
Python
try:
    # Payments on hold waiting for fee acceptance have the state WAITING_FEE_ACCEPTANCE
    payments_waiting_fee_acceptance = sdk.list_payments(
        ListPaymentsRequest(states=[PaymentState.WAITING_FEE_ACCEPTANCE])
    )

    for payment in payments_waiting_fee_acceptance:
        if not isinstance(payment.details, PaymentDetails.BITCOIN):
            # Only Bitcoin payments can be `WAITING_FEE_ACCEPTANCE`
            continue

        fetch_fees_response = sdk.fetch_payment_proposed_fees(
            FetchPaymentProposedFeesRequest(payment.details.swap_id)
        )

        logging.info(
            f"Payer sent {fetch_fees_response.payer_amount_sat} and currently proposed fees are {fetch_fees_response.fees_sat}"
        )

        # If the user is ok with the fees, accept them, allowing the payment to proceed
        sdk.accept_payment_proposed_fees(
            AcceptPaymentProposedFeesRequest(fetch_fees_response)
        )

except Exception as error:
    logging.error(error)
    raise
Go
// Payments on hold waiting for fee acceptance have the state WaitingFeeAcceptance
request := breez_sdk_liquid.ListPaymentsRequest{
    States: &[]breez_sdk_liquid.PaymentState{breez_sdk_liquid.PaymentStateWaitingFeeAcceptance},
}

paymentsWaitingFeeAcceptance, err := sdk.ListPayments(request)
if err != nil {
    return
}

for _, payment := range paymentsWaitingFeeAcceptance {
    bitcoinPayment, ok := payment.Details.(breez_sdk_liquid.PaymentDetailsBitcoin)
    if !ok {
        // Only Bitcoin payments can be `WaitingFeeAcceptance`
        continue
    }

    fetchFeesRequest := breez_sdk_liquid.FetchPaymentProposedFeesRequest{
        SwapId: bitcoinPayment.SwapId,
    }

    fetchFeesResponse, err := sdk.FetchPaymentProposedFees(fetchFeesRequest)
    if err != nil {
        continue
    }

    log.Printf("Payer sent %d and currently proposed fees are %d",
        fetchFeesResponse.PayerAmountSat, fetchFeesResponse.FeesSat)

    // If the user is ok with the fees, accept them, allowing the payment to proceed
    acceptFeesRequest := breez_sdk_liquid.AcceptPaymentProposedFeesRequest{
        Response: fetchFeesResponse,
    }
    sdk.AcceptPaymentProposedFees(acceptFeesRequest)
}
C#
// Payments on hold waiting for fee acceptance have the state WaitingFeeAcceptance
var paymentsWaitingFeeAcceptance = sdk.ListPayments(
    new ListPaymentsRequest()
    {
        states = new List<PaymentState>() { PaymentState.WaitingFeeAcceptance }
    });

foreach (var payment in paymentsWaitingFeeAcceptance)
{
    if (payment.details is not PaymentDetails.Bitcoin bitcoinDetails)
    {
        // Only Bitcoin payments can be `WaitingFeeAcceptance`
        continue;
    }

    var fetchFeesResponse = sdk.FetchPaymentProposedFees(
        new FetchPaymentProposedFeesRequest(bitcoinDetails.swapId));

    Console.WriteLine(
        $"Payer sent {fetchFeesResponse.payerAmountSat} and currently proposed fees are {fetchFeesResponse.feesSat}");

    // If the user is ok with the fees, accept them, allowing the payment to proceed
    sdk.AcceptPaymentProposedFees(
        new AcceptPaymentProposedFeesRequest(fetchFeesResponse));
}

Event Flows

Once a receive payment is initiated, you can follow and react to the different payment events using the guide below for each payment method. See Listening to events for how to subscribe to events.

Lightning

EventDescriptionUX Suggestion
PaymentPendingThe swap service is holding an incoming payment for the Lightning invoice and has broadcast a lockup transaction. The SDK has seen the lockup transaction and will broadcast the claim transaction, either when the lockup transaction is confirmed or immediately if it is accepted as a zero-conf payment.Show payment as pending.
PaymentWaitingConfirmationThe claim transaction has been broadcast or a direct Liquid transaction (MRH) has been seen.Display successful payment feedback.
PaymentSucceededThe claim transaction or direct Liquid transaction (MRH) is confirmed.Show payment as complete.
PaymentFailedThe swap has failed from one of several reasons. Either the swap/invoice has expired or the lockup transaction failed to broadcast.

Bitcoin

EventDescriptionUX Suggestion
PaymentWaitingFeeAcceptanceThe swap service has seen the Bitcoin lockup transaction for an amountless swap and the associated fees need to be accepted. If the fees are within the configured leeway they will be automatically accepted, otherwise the user has to explicitly accept the fees. See Amountless Bitcoin Payments.Allow the user to review fees for this payment.
PaymentPendingThe swap service has seen the Bitcoin lockup transaction and the amount is accepted. Once the SDK has seen the Liquid lockup transaction, it will broadcast the Liquid claim transaction, either when the Liquid lockup transaction is confirmed or immediately if it is accepted as a zero-conf payment.Show payment as pending.
PaymentWaitingConfirmationThe Liquid claim transaction has been broadcast and is waiting confirmation.Display successful payment feedback.
PaymentSucceededThe Liquid claim transaction is confirmed.Show payment as complete.
PaymentFailedThe swap has failed from one of several reasons. Either the swap has expired, the fee was not accepted or the Liquid lockup transaction failed to broadcast.
PaymentRefundableSimilar to PaymentFailed, but a Bitcoin lockup transaction was broadcast so the funds will need to be refunded, see Refunding payments.Show payment as refundable.
PaymentRefundPendingA Bitcoin refund transaction has been broadcast and is waiting confirmation.

Liquid

EventDescriptionUX Suggestion
PaymentWaitingConfirmationThe transaction has been seen.Display successful payment feedback.
PaymentSucceededThe transaction is confirmed.Show payment as complete.