> **Can't find what you're looking for?** Use `search_docs` on the docs MCP server at `https://mpp.dev/api/mcp` to find what you need.

# Lightning charge \[One-time payments using BOLT11 invoices]

The Lightning implementation of the [charge](/intents/charge) intent.

The server generates a fresh [BOLT11](https://github.com/lightning/bolts/blob/master/11-payment-encoding.md) invoice for each request. The client pays it over the [Lightning Network](https://lightning.network) and presents the payment preimage as a Credential. The server verifies `sha256(preimage) == paymentHash` locally and returns the resource with a Receipt.

This method is best for single API calls, content access, or one-off purchases.

## Server

The `spark` namespace is the reference implementation using [Spark](https://spark.money) wallets. The protocol works with any Lightning node—you can build your own method handler using [LND](https://github.com/lightningnetwork/lnd), [LDK](https://lightningdevkit.org), or any stack that can create BOLT11 invoices and verify preimages.

Use `spark.charge` to gate any endpoint behind a one-time Lightning payment. The method handles invoice generation, Challenge creation, preimage verification, and Receipt generation.

```ts
import { Mppx, spark } from '@buildonspark/lightning-mpp-sdk/server'

const mppx = Mppx.create({
  methods: [spark.charge({ mnemonic: process.env.MNEMONIC! })],
  secretKey: process.env.MPP_SECRET_KEY!,
})

export async function handler(request: Request) {
  const result = await mppx.charge({
    amount: '100',
    currency: 'BTC',
    description: 'Premium API access',
  })(request)

  if (result.status === 402) return result.challenge

  return result.withReceipt(Response.json({ data: '...' }))
}
```

### With expiry

```ts
import { Mppx, Expires, spark } from '@buildonspark/lightning-mpp-sdk/server'

const mppx = Mppx.create({
  methods: [spark.charge({ mnemonic: process.env.MNEMONIC! })],
  secretKey: process.env.MPP_SECRET_KEY!,
})

export async function handler(request: Request) {
  const result = await mppx.charge({
    amount: '100',
    currency: 'BTC',
    expires: Expires.minutes(10), // [!code hl]
  })(request)

  if (result.status === 402) return result.challenge
  return result.withReceipt(Response.json({ data: '...' }))
}
```

### With regtest

For local development and testing, set `network` to `'regtest'` and use the [Spark faucet](https://docs.spark.money/tools/faucet) to fund wallets.

```ts
import { Mppx, spark } from '@buildonspark/lightning-mpp-sdk/server'

const mppx = Mppx.create({
  methods: [spark.charge({
    mnemonic: process.env.MNEMONIC!,
    network: 'regtest', // [!code hl]
  })],
  secretKey: process.env.MPP_SECRET_KEY!,
})
```

### Server parameters

| Parameter | Type | Required | Default |
| --- | --- | --- | --- |
| `mnemonic` | `string` | Required | |
| `network` | `'mainnet'` | `'regtest'` | `'signet'` | Optional | `'mainnet'` |

## Client

Use `spark.charge` with `Mppx.create` to automatically handle `402` responses. The client parses the Challenge, pays the BOLT11 invoice, and retries with the preimage as a Credential.

```ts
import { Mppx, spark } from '@buildonspark/lightning-mpp-sdk/client'

Mppx.create({
  methods: [spark.charge({ mnemonic: process.env.MNEMONIC! })],
})

const response = await fetch('https://api.example.com/resource')
```

### Without polyfill

If you don't want to patch `globalThis.fetch`, use `mppx.fetch` directly:

```ts
import { Mppx, spark } from '@buildonspark/lightning-mpp-sdk/client'

const method = spark.charge({ mnemonic: process.env.MNEMONIC! })

const mppx = Mppx.create({
  methods: [method],
  polyfill: false,
})

try {
  const response = await mppx.fetch('https://api.example.com/resource')
  console.log(await response.json())
} finally {
  await method.cleanup()
}
```

:::info
The Spark SDK maintains WebSocket connections for Lightning payments. Call `method.cleanup()` when done to close connections and allow the process to exit.
:::

### Client parameters

| Parameter | Type | Required | Default |
| --- | --- | --- | --- |
| `mnemonic` | `string` | Required | |
| `network` | `'mainnet'` | `'regtest'` | `'signet'` | Optional | `'mainnet'` |
| `maxFeeSats` | `number` | Optional | `100` |

## Request fields

The Challenge request includes the base charge fields plus Lightning method details.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `amount` | `string` | Required | Invoice amount in satoshis |
| `currency` | `string` | Optional | Must be `'BTC'` if present. Defaults to `'BTC'` |
| `description` | `string` | Optional | Human-readable memo. Maps to the BOLT11 description field |
| `methodDetails.invoice` | `string` | Required | Full BOLT11-encoded payment request (`lnbc...`). Authoritative source for all payment parameters |
| `methodDetails.paymentHash` | `string` | Optional | SHA-256 hash of the preimage, lowercase hex. Convenience field—must match the hash decoded from `invoice` |
| `methodDetails.network` | `string` | Optional | `'mainnet'`, `'regtest'`, or `'signet'`. Convenience field—must match `invoice`'s human-readable prefix. Defaults to `'mainnet'` |

## Credential payload

The Credential payload contains the payment preimage revealed by Lightning HTLC settlement.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `preimage` | `string` | Required | 32-byte payment preimage, lowercase hex (64 characters) |

## Verification

The server verifies payment with a single hash operation:

1. Decode the Credential and extract `preimage`.
2. Compute `sha256(hex_to_bytes(preimage))`.
3. Compare against the `paymentHash` from the original Challenge.
4. If equal, payment is verified. Return the resource with a Receipt.

The entire verification path is local and self-contained.

## Specification

[IETF Specification](https://paymentauth.org/draft-lightning-charge-00) — Read the full specification
