Research/Bitcoin

Building a Bitcoin Payment App in 2026: Technology Stack Guide

Technical guide to building Bitcoin payment applications: SDKs, APIs, architecture patterns, and deployment.

bcTanjiMar 14, 2026

Building a Bitcoin payment app in 2026 is fundamentally different from even two years ago. The SDK landscape has matured, Layer 2 protocols have reached production stability, and stablecoin support on Bitcoin has gone from experimental to mainstream. Whether you are building a consumer wallet, a merchant checkout flow, or an embedded payments feature inside a fintech product, the decisions you make at the architecture level will determine your development velocity, regulatory posture, and user experience for years to come.

This guide walks through the full decision tree: custodial vs. self-custodial, which payment layer to target, which SDKs to evaluate, how to handle compliance, and how to test before going live. Code examples use real APIs from production SDKs.

The First Decision: Custodial vs. Self-Custodial

Every Bitcoin payment app starts with a custody question. The answer shapes your entire stack: what SDKs you can use, what licenses you need, how you handle key management, and what your liability profile looks like.

Custodial architecture

In a custodial model, your backend holds private keys on behalf of users. Payments are internal ledger entries until settlement. This is how most traditional fintech apps work: users see balances, but the platform controls the underlying funds.

  • Simpler UX: no seed phrases, no backup flows, no transaction signing
  • Lower latency: internal transfers are database writes
  • Full regulatory burden: you are a money transmitter in every jurisdiction you operate
  • Counterparty risk: your users trust you completely with their funds

Self-custodial architecture

In a self-custodial model, users hold their own keys. Your app facilitates transactions but never has unilateral control over funds. This is the native model for Bitcoin and most Layer 2 protocols.

  • Users control their funds: no counterparty risk
  • Reduced regulatory scope: you may not need a money transmitter license depending on jurisdiction
  • More complex UX: seed phrase backup, transaction confirmation, key recovery
  • SDK-dependent: you need a wallet SDK that handles key derivation, signing, and state management
Regulatory nuance: Self-custody does not automatically exempt you from licensing. If your app facilitates transfers between users, state regulators may still classify you as a money transmitter. California's Digital Financial Assets Law (DFAL), which began accepting license applications in March 2026, covers exchanging, transferring, and storing digital assets regardless of custody model. Always consult legal counsel for your specific use case.

Hybrid approaches

Some apps use MPC wallets or threshold signature schemes to split key material between the user and server. This gives users partial custody (neither party can spend alone) while preserving a smoother UX. Privy and Dynamic both offer embedded wallet infrastructure that follows this pattern and both support Spark.

Choosing a Payment Layer

Bitcoin L1, Lightning, and newer Layer 2 protocols like Spark each serve different use cases. The right choice depends on your transaction profile: average amount, frequency, latency requirements, and whether you need stablecoin support.

FeatureBitcoin L1LightningSpark
Confirmation time10-60 min1-3 secondsSub-second
Transaction feesVariable (1-50+ sat/vB)Base + proportional routing feesZero for Spark-to-Spark transfers
Minimum practical amount~$1 (fee-dependent)1 sat1 sat
Finality modelProbabilistic (6 confirmations)Instant (conditional on HTLCs)Instant (1-of-n operator trust)
Stablecoin supportLimited (Omni, RGB)Via Taproot AssetsNative (BTKN standard)
Liquidity managementNot requiredRequired (channels)Not required
Offline receivingYes (address)No (node must be online)Yes (SSP holds conditionally)
SDK maturityMature (BDK)Mature (LDK)Production (Spark SDK, Breez SDK)

For most consumer payment apps in 2026, the practical choice is between Lightning and Spark, with L1 reserved for large-value settlement. If you need dollar-denominated payments, Spark is the most direct path: stablecoins like USDB are native to the protocol, not bridged from another chain.

SDK Landscape: What to Build With

Four SDKs dominate Bitcoin payment app development. Each targets a different layer of the stack and a different developer profile.

BDK: Bitcoin Dev Kit

BDK is the standard library for on-chain Bitcoin wallet functionality. Written in Rust with bindings for Swift, Kotlin, Python, and WASM (via bdk-ffi and bdk-wasm). The current release is bdk_wallet 1.0 with 2.0 in beta.

  • Descriptor-based wallet architecture using miniscript
  • Multiple backends: Esplora, Electrum, bitcoind RPC, compact block filters
  • SQLite persistence for wallet state
  • Full UTXO management, coin selection, and fee estimation
  • Used in production by Bitkey, Peach Bitcoin, Envoy, and Bull Bitcoin

BDK is the right choice when you need on-chain wallet capabilities: generating addresses, tracking balances, constructing transactions, and managing PSBTs. It does not handle Lightning or Layer 2 protocols.

LDK: Lightning Dev Kit

LDK provides modular Lightning Network functionality. The core is Rust (rust-lightning), with language bindings for Swift, Kotlin, Java, JavaScript/WASM, and Python. Current version: v0.1.4.

LDK is the right choice when you need full control over your Lightning implementation. It is powerful but complex: managing channels, capacity, and liquidity is your responsibility.

Breez SDK

Breez SDK abstracts payment complexity behind a unified API. Built in Rust with bindings for Swift, Kotlin, Python, Flutter, Go, C#, React Native, and JavaScript/WASM. Over 75 apps have integrated it in production.

  • Three backend modes: Greenlight (CLN cloud node), Liquid (sidechain), and Spark (Bitcoin L2)
  • Built-in LNURL, Lightning Addresses, BOLT12, BIP 353, and Nostr Wallet Connect
  • Self-custodial across all modes
  • The Spark implementation (beta) uses Spark as the payment layer with Lightning interoperability

Breez SDK is the right choice when you want to support multiple payment protocols behind a single API. Its Spark mode gives you instant Bitcoin and stablecoin transfers with Lightning send/receive, without managing channels yourself.

Spark SDK

The Spark SDK provides direct access to the Spark protocol. Available as a TypeScript package with React Native support. Current version: 0.5.8 on npm.

  • Zero-fee Spark-to-Spark transfers with instant settlement
  • Native stablecoin support via the BTKN token standard
  • Lightning interop: create and pay Lightning invoices
  • L1 deposit and withdrawal with fee quotes
  • Event-driven architecture for real-time payment notifications

SDK comparison

SDKLayerPrimary LanguagesComplexityBest For
BDKL1Rust, Swift, Kotlin, Python, WASMMediumOn-chain wallets, UTXO management
LDKLightningRust, Swift, Kotlin, JS/WASMHighCustom Lightning nodes, full protocol control
Breez SDKLightning / SparkRust, Swift, Kotlin, Flutter, JS, Go, C#LowMulti-protocol apps, unified payment API
Spark SDKSparkTypeScript, React NativeLowInstant payments, stablecoins, simplest integration

Architecture Patterns by Use Case

Different products need different architectures. Here are four common patterns for Bitcoin payment apps, each with a recommended stack.

Consumer wallet

A mobile wallet for sending and receiving Bitcoin and stablecoins. Latency and UX are the top priorities: users expect payments to feel instant.

  • Frontend: React Native or Swift/Kotlin native
  • Payment layer: Spark (via Spark SDK or Breez SDK Spark mode)
  • On-chain fallback: BDK for L1 deposits and withdrawals
  • Key storage: device keychain (iOS Keychain, Android Keystore) with encrypted cloud backup

Spark is a natural fit for consumer wallets because it eliminates channel reserves, inbound liquidity requirements, and the need for users to be online to receive payments. The Spark SDK handles wallet initialization from a BIP-39 mnemonic, transfer execution, and real-time event monitoring in a single package.

Merchant checkout

A payment gateway that lets merchants accept Bitcoin at point of sale or online. The merchant wants to receive a specific fiat-denominated amount and settle quickly.

  • Backend: Node.js or Go service generating payment requests
  • Payment layer: Spark for stablecoin settlement, Lightning for broad wallet compatibility
  • Invoice generation: Spark invoices for dollar amounts via USDB, Lightning invoices as fallback
  • Settlement: hold stablecoins or convert to fiat via off-ramp partner

For merchants, dollar-denominated payments on Spark solve the volatility problem. The merchant creates a USDB invoice for the exact dollar amount. The customer pays from any Spark wallet. No exchange rate risk, no waiting for confirmations.

Embedded payments in a fintech app

A non-crypto fintech app (neobank, remittance service, payroll platform) that wants to add Bitcoin rails for faster, cheaper transfers.

  • Integration: Spark SDK on the backend, abstracted behind your existing payment API
  • Wallet infrastructure: Privy or Dynamic for embedded wallet provisioning
  • User experience: no crypto terminology exposed to users
  • Compliance: full KYC/AML on your platform, Spark handles the transfer layer

Lightning service provider

A business running Lightning infrastructure: routing nodes, channel management, liquidity services.

  • Core: LDK for full protocol control
  • On-chain: BDK for channel funding and settlement
  • Spark bridge: Spark SDK for cross-protocol swaps
  • Monitoring: custom watchtower implementation

Code Walkthrough: Common Payment Flows

These examples use the Spark SDK (TypeScript) to demonstrate the most common operations in a payment app. All code runs against the production API.

Initializing a wallet

import { SparkWallet } from "@buildonspark/spark-sdk";

// Create a new wallet
const { wallet, mnemonic } = await SparkWallet.initialize({
  options: { network: "MAINNET" }
});

// Store the mnemonic securely - this is the user's backup
console.log("Spark address:", await wallet.getSparkAddress());

// Or restore from existing mnemonic
const { wallet: restored } = await SparkWallet.initialize({
  mnemonicOrSeed: existingMnemonic,
  accountNumber: 0,
  options: { network: "MAINNET" }
});

Sending a Bitcoin payment

// Spark-to-Spark transfer (instant, zero fees)
const result = await wallet.transfer({
  receiverSparkAddress: "spark1p...",
  amountSats: 50000,
});

// Pay a Lightning invoice (interoperable with any LN wallet)
const lnResult = await wallet.payLightningInvoice({
  invoice: "lnbc500u1p...",
  maxFeeSats: 100,
});

Receiving payments

// Create a Spark invoice for Bitcoin
const invoice = await wallet.createSatsInvoice({
  amount: 10000,
  memo: "Order #1234",
  expiryTime: new Date(Date.now() + 3600 * 1000)
});

// Create a Lightning invoice (receivable from any LN wallet)
const lnInvoice = await wallet.createLightningInvoice({
  amountSats: 50000,
  memo: "Deposit to wallet"
});

// Listen for incoming payments
wallet.on("transfer:claimed", (transferId, balance) => {
  console.log("Payment received:", transferId);
  console.log("New balance:", balance, "sats");
});

Stablecoin transfers

// Send USDB (or any BTKN token)
const tokenTransfer = await wallet.transferTokens({
  tokenIdentifier: "btkn1p...",  // USDB token ID
  tokenAmount: BigInt(1000),      // Amount in token units
  receiverSparkAddress: "spark1p...",
});

// Create an invoice for token payment
const tokenInvoice = await wallet.createTokensInvoice({
  tokenIdentifier: "btkn1p...",
  amount: BigInt(5000),
  memo: "Invoice #5678",
  expiryTime: new Date(Date.now() + 3600 * 1000)
});

// Check token balances
const { tokenBalances } = await wallet.getBalance();

L1 deposits and withdrawals

// Get a Bitcoin L1 address for depositing into Spark
const depositAddress = await wallet.getSingleUseDepositAddress();

// After sending BTC to this address, claim the deposit
await wallet.claimDeposit();

// Withdraw from Spark to a Bitcoin L1 address
const feeQuote = await wallet.getWithdrawalFeeQuote({
  amountSats: 100000,
  onchainAddress: "bc1q..."
});

const withdrawal = await wallet.withdraw({
  amountSats: 100000,
  onchainAddress: "bc1q...",
  feeQuote: feeQuote,
});
Scaffolding shortcut: Run npx @buildonspark/create-spark-app my-app to generate a project with the Spark SDK pre-configured, wallet initialization, and example payment flows. This is the fastest way to get a working prototype.

Compliance Considerations

Regulatory requirements for Bitcoin payment apps vary by jurisdiction, custody model, and transaction type. In the United States, the compliance landscape in 2026 includes federal and state obligations that developers must account for early in the architecture process.

Federal requirements (US)

If your app facilitates Bitcoin transfers between users, you are likely classified as a Money Services Business (MSB) under FinCEN regulations. This requires:

  • MSB registration (Form 107, filed within 180 days of establishment)
  • AML/KYC program with customer identification and verification
  • OFAC screening on all transactions
  • Suspicious Activity Reports (SARs) and Currency Transaction Reports (CTRs)
  • Funds Travel Rule compliance for transfers over $3,000
  • Five-year recordkeeping on all transactions

State licensing

Most states require a Money Transmitter License (MTL) for businesses that transmit money or monetary value. As of 2026, 31 states have enacted the Model Money Transmitter Modernization Act (MTMA) in full or in part, which standardizes some requirements but does not eliminate per-state licensing.

  • Applications filed through NMLS (Nationwide Multistate Licensing System)
  • Surety bonds typically range from $50K to $500K depending on transaction volume
  • Timeline: 3 months best case, 6-9 months typical, up to 2 years for New York
  • California's DFAL began accepting license applications in March 2026, covering digital asset exchange, transfer, and storage

For a deeper look at the compliance landscape, see our guide on stablecoin regulation frameworks.

Compliance architecture patterns

Build compliance into your architecture from day one. Retrofitting KYC and transaction monitoring is expensive and error-prone.

  • Identity verification: integrate a KYC provider (Jumio, Persona, or similar) at onboarding
  • Transaction monitoring: implement real-time screening against OFAC and sanctions lists
  • Audit trail: log all transactions with timestamps, amounts, and counterparty identifiers
  • Threshold management: flag transactions that approach reporting thresholds

Testing and Testnet Deployment

Never ship a Bitcoin payment app without thorough testnet validation. The Bitcoin testing ecosystem has improved significantly, with several networks available for different stages of development.

Available test networks

NetworkTypeBest ForSDK Support
RegtestLocal, privateUnit tests, CI/CD, rapid iterationAll SDKs
Signet (BIP 325)Centralized, publicIntegration testing, stable environmentBDK, LDK, Spark SDK
Testnet4Decentralized, publicRealistic network conditionsBDK, Bitcoin Core v28+
MutinyNetManaged SignetLightning and L2 testingLDK, Breez SDK

The Spark SDK supports regtest for local development. Initialize with network: "REGTEST" to get a fully functional Spark wallet that operates against a local environment. For integration testing against the Spark network, use the testnet configuration documented at docs.spark.money.

Testing checklist

Before deploying to mainnet, validate these scenarios:

  1. Wallet creation and restoration from mnemonic
  2. Inbound payments: Spark transfers, Lightning invoices, L1 deposits
  3. Outbound payments: Spark transfers, Lightning payments, L1 withdrawals
  4. Stablecoin transfers: send and receive tokens, check balance updates
  5. Edge cases: expired invoices, insufficient balance, network timeouts
  6. Concurrent operations: multiple payments in flight simultaneously
  7. Recovery: restore wallet on a new device and verify balance and history

Production Deployment Considerations

Moving from testnet to mainnet involves more than flipping a network flag. These are the areas that trip up teams most often.

Key management in production

How you store and protect keys defines your security posture. For self-custodial apps, seed phrases must be encrypted at rest on the device, with optional encrypted cloud backup for recovery. For custodial or hybrid models, use an HSM (Hardware Security Module) or a managed signing service that supports threshold signatures.

Connection management

The Spark SDK maintains persistent connections to Spark operators. In production, call wallet.cleanupConnections() when the app backgrounds, and reinitialize when it returns to the foreground. For server-side integrations, use connection pooling and implement reconnection logic with exponential backoff.

Error handling patterns

Payment operations can fail for reasons outside your control: network partitions, fee spikes on L1, operator unavailability. Structure your error handling around these categories:

  • Retryable errors: network timeouts, temporary operator unavailability
  • User-actionable errors: insufficient balance, expired invoice
  • Fatal errors: invalid mnemonic, corrupted wallet state

For Lightning payments, always set a maxFeeSats parameter to prevent overpaying on routing fees. For L1 withdrawals, use the fee quote API to show users the exact cost before confirming.

Choosing Your Stack: A Decision Framework

The right stack depends on what you are building. Use this framework to narrow your choices.

  • Building a consumer wallet with stablecoins: Spark SDK or Breez SDK (Spark mode)
  • Adding Bitcoin payments to an existing fintech app: Spark SDK on your backend, Privy/Dynamic for wallet provisioning
  • Building a merchant payment gateway: Spark SDK for stablecoin invoices, Lightning via Breez SDK for broad compatibility
  • Running Lightning infrastructure: LDK for full protocol control, BDK for on-chain operations
  • Building a Bitcoin savings app with on-chain settlement: BDK for UTXO management, Spark for instant transfers between users

For most teams building payment-focused apps today, Spark offers the shortest path to production. No channel management, no liquidity planning, instant finality, native stablecoins, and Lightning interoperability out of the box. The tradeoff is a 1-of-n trust assumption on operators during transfers: a meaningful consideration, but one that most consumer apps are comfortable with given the UX benefits.

Getting Started

The fastest way to go from zero to a working Bitcoin payment app:

  1. Scaffold a project: npx @buildonspark/create-spark-app my-payment-app
  2. Explore the Spark SDK documentation for API reference and guides
  3. Review the Spark open-source repository (Apache-2.0 licensed)
  4. Compare SDKs using our Bitcoin wallet SDK comparison
  5. Understand the underlying protocol: What is Spark?

To see a production Spark-powered wallet in action, General Bread demonstrates what a polished consumer experience looks like with instant Bitcoin and stablecoin payments built on the Spark SDK.

This article is for educational purposes only. It does not constitute financial or investment advice. Bitcoin and Layer 2 protocols involve technical and financial risk. Always do your own research and understand the tradeoffs before using any protocol.