270
README.md
270
README.md
@@ -1,26 +1,28 @@
|
|||||||
# Cashu Redeem API 🪙⚡
|
# Cashu Redeem API
|
||||||
|
|
||||||
API for redeeming Cashu tokens (ecash) to Lightning addresses using the cashu-ts library and LNURLp protocol.
|
A production-grade API for redeeming Cashu tokens (ecash) to Lightning addresses using the [cashu-ts](https://github.com/cashubtc/cashu-ts) library and LNURLp protocol.
|
||||||
|
|
||||||
## 🚀 Features
|
## Features
|
||||||
|
|
||||||
- **Decode Cashu tokens** - Parse and validate token content
|
- **Decode Cashu tokens** - Parse and validate token content, check spendability at the mint
|
||||||
- **Redeem to Lightning addresses** - Convert ecash to Lightning payments via LNURLp
|
- **Redeem to Lightning addresses** - Convert ecash to Lightning payments via LNURLp with exact fee calculation
|
||||||
- **Security features** - Domain restrictions, rate limiting, input validation
|
- **Lightning address validation** - Verify addresses and query LNURLp capabilities
|
||||||
- **Robust error handling** - Comprehensive error messages
|
- **Duplicate detection** - In-memory tracking prevents double-spend attempts
|
||||||
- **Interactive API Documentation** - Complete Swagger/OpenAPI documentation at `/docs`
|
- **Security** - Domain restrictions, rate limiting, input validation, CORS protection
|
||||||
|
- **Interactive API docs** - Complete Swagger/OpenAPI documentation at `/docs`
|
||||||
|
|
||||||
|
## API Documentation
|
||||||
|
|
||||||
## 📖 API Documentation
|
Interactive Swagger documentation is available at `/docs` when the server is running.
|
||||||
|
|
||||||
**Interactive Swagger Documentation**: Visit `/docs` when running the server for a complete, interactive API reference.
|
- Local: `http://localhost:3000/docs`
|
||||||
|
- OpenAPI spec: `http://localhost:3000/openapi.json`
|
||||||
|
|
||||||
Example: `https://cashu-redeem.azzamo.net/docs/`
|
## API Endpoints
|
||||||
|
|
||||||
## 📡 API Endpoints
|
### `POST /api/decode`
|
||||||
|
|
||||||
### 1. `POST /api/decode`
|
Decode a Cashu token and return its content. Supports both v3 (`cashuB`) and v1 (`cashuA`) token formats. Checks spendability against the mint.
|
||||||
Decode a Cashu token and return its content. Supports both v1 and v3 token formats.
|
|
||||||
|
|
||||||
**Request:**
|
**Request:**
|
||||||
```json
|
```json
|
||||||
@@ -34,25 +36,33 @@ Decode a Cashu token and return its content. Supports both v1 and v3 token forma
|
|||||||
{
|
{
|
||||||
"success": true,
|
"success": true,
|
||||||
"decoded": {
|
"decoded": {
|
||||||
"mint": "https://mint.azzamo.net",
|
"mint": "https://kashu.me",
|
||||||
"totalAmount": 21000,
|
"totalAmount": 16,
|
||||||
"numProofs": 3,
|
"numProofs": 5,
|
||||||
"denominations": [1000, 10000, 10000],
|
"denominations": [8, 4, 2, 1, 1],
|
||||||
"format": "cashuA",
|
"format": "cashuB",
|
||||||
"spent": false
|
"spent": false
|
||||||
},
|
},
|
||||||
"mint_url": "https://mint.azzamo.net"
|
"mint_url": "https://kashu.me"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2. `POST /api/redeem`
|
### `POST /api/redeem`
|
||||||
Redeem a Cashu token to a Lightning address. Lightning address is optional - if not provided, uses the default address from configuration.
|
|
||||||
|
Redeem a Cashu token to a Lightning address. The Lightning address is optional if a default is configured.
|
||||||
|
|
||||||
|
The redemption process:
|
||||||
|
1. Token validation and parsing
|
||||||
|
2. Spendability check at the mint
|
||||||
|
3. Melt quote to determine exact fees
|
||||||
|
4. Invoice creation for the net amount (token amount minus fees)
|
||||||
|
5. Token melting and Lightning payment
|
||||||
|
|
||||||
**Request:**
|
**Request:**
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"token": "cashuB...",
|
"token": "cashuB...",
|
||||||
"lightningAddress": "user@ln.tips"
|
"lightningAddress": "user@twentyone.tips"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -68,50 +78,36 @@ Redeem a Cashu token to a Lightning address. Lightning address is optional - if
|
|||||||
{
|
{
|
||||||
"success": true,
|
"success": true,
|
||||||
"paid": true,
|
"paid": true,
|
||||||
"amount": 21000,
|
"amount": 16,
|
||||||
"invoiceAmount": 20580,
|
"invoiceAmount": 14,
|
||||||
"to": "user@ln.tips",
|
"to": "user@twentyone.tips",
|
||||||
"fee": 1000,
|
"fee": 2,
|
||||||
"actualFee": 420,
|
"netAmount": 14,
|
||||||
"netAmount": 20000,
|
"mint_url": "https://kashu.me",
|
||||||
"mint_url": "https://mint.azzamo.net",
|
"format": "cashuB",
|
||||||
"format": "cashuA",
|
"preimage": "d5af74a3..."
|
||||||
"preimage": "abc123..."
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
**Success Response (using default address):**
|
**Response fields:**
|
||||||
```json
|
|
||||||
{
|
|
||||||
"success": true,
|
|
||||||
"paid": true,
|
|
||||||
"amount": 21000,
|
|
||||||
"invoiceAmount": 20580,
|
|
||||||
"to": "admin@your-domain.com",
|
|
||||||
"fee": 1000,
|
|
||||||
"actualFee": 420,
|
|
||||||
"netAmount": 20000,
|
|
||||||
"mint_url": "https://mint.azzamo.net",
|
|
||||||
"format": "cashuA",
|
|
||||||
"usingDefaultAddress": true,
|
|
||||||
"message": "Redeemed to default Lightning address: admin@your-domain.com"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
|
| Field | Description |
|
||||||
|
|-------|-------------|
|
||||||
|
| `amount` | Total token amount in sats |
|
||||||
|
| `invoiceAmount` | Amount sent to the Lightning address (after fees) |
|
||||||
|
| `fee` | Exact fee charged by the mint (from melt quote) |
|
||||||
|
| `netAmount` | Net amount received |
|
||||||
|
| `preimage` | Lightning payment preimage (proof of payment) |
|
||||||
|
| `usingDefaultAddress` | Present and `true` when the default address was used |
|
||||||
|
|
||||||
**Payment Verification**:
|
### `POST /api/validate-address`
|
||||||
The API uses multiple indicators to verify payment success:
|
|
||||||
- `paid` flag from mint response
|
|
||||||
- Presence of payment preimage
|
|
||||||
- Payment state indicators
|
|
||||||
|
|
||||||
### 3. `POST /api/validate-address`
|
Validate a Lightning address without performing a redemption. Tests LNURLp resolution and returns address capabilities.
|
||||||
Validate a Lightning address without redemption.
|
|
||||||
|
|
||||||
**Request:**
|
**Request:**
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"lightningAddress": "user@ln.tips"
|
"lightningAddress": "user@twentyone.tips"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -120,48 +116,54 @@ Validate a Lightning address without redemption.
|
|||||||
{
|
{
|
||||||
"success": true,
|
"success": true,
|
||||||
"valid": true,
|
"valid": true,
|
||||||
"domain": "ln.tips",
|
"domain": "twentyone.tips",
|
||||||
"minSendable": 1,
|
"minSendable": 1,
|
||||||
"maxSendable": 100000000,
|
"maxSendable": 1000000,
|
||||||
"commentAllowed": 144
|
"commentAllowed": 2000
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### 4. `GET /api/health`
|
### `GET /api/health`
|
||||||
Health check endpoint.
|
|
||||||
|
Health check endpoint returning server status, uptime, and memory usage.
|
||||||
|
|
||||||
**Response:**
|
**Response:**
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"status": "ok",
|
"status": "ok",
|
||||||
"timestamp": "2025-01-14T12:00:00Z",
|
"timestamp": "2026-02-17T03:36:40.987Z",
|
||||||
"uptime": 3600,
|
"uptime": 170.23,
|
||||||
"memory": {...},
|
"memory": { "rss": 83873792, "heapTotal": 20520960, "heapUsed": 17170824 },
|
||||||
"version": "1.0.0"
|
"version": "2.0.0"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## 🛠 Setup & Installation
|
### `GET /openapi.json`
|
||||||
|
|
||||||
|
Returns the full OpenAPI 3.0 specification as JSON.
|
||||||
|
|
||||||
|
## Setup & Installation
|
||||||
|
|
||||||
### Prerequisites
|
### Prerequisites
|
||||||
|
|
||||||
- Node.js >= 18.0.0
|
- Node.js >= 18.0.0
|
||||||
- npm or yarn
|
- npm >= 8.0.0
|
||||||
|
|
||||||
### Installation
|
### Installation
|
||||||
|
|
||||||
1. **Clone and install dependencies:**
|
1. **Clone and install dependencies:**
|
||||||
```bash
|
```bash
|
||||||
git clone <your-repo>
|
git clone https://github.com/Michilis/cashu-redeem-api.git
|
||||||
cd cashu-redeem-api
|
cd cashu-redeem-api
|
||||||
npm install
|
npm install
|
||||||
```
|
```
|
||||||
|
|
||||||
2. **Setup environment variables:**
|
2. **Configure environment variables:**
|
||||||
```bash
|
```bash
|
||||||
cp env.example .env
|
cp env.example .env
|
||||||
```
|
```
|
||||||
|
|
||||||
Edit `.env` file:
|
Edit `.env`:
|
||||||
```bash
|
```bash
|
||||||
# Server Configuration
|
# Server Configuration
|
||||||
PORT=3000
|
PORT=3000
|
||||||
@@ -171,108 +173,148 @@ NODE_ENV=development
|
|||||||
API_DOMAIN=localhost:3000
|
API_DOMAIN=localhost:3000
|
||||||
|
|
||||||
# Security Configuration
|
# Security Configuration
|
||||||
ALLOW_REDEEM_DOMAINS=ln.tips,getalby.com,wallet.mutinywallet.com
|
ALLOW_REDEEM_DOMAINS=*
|
||||||
API_SECRET=your-secret-key-here
|
|
||||||
|
|
||||||
# Default Lightning Address (used when no address is provided in redeem requests)
|
# Default Lightning Address (used when no address is provided in redeem requests)
|
||||||
DEFAULT_LIGHTNING_ADDRESS=admin@your-domain.com
|
DEFAULT_LIGHTNING_ADDRESS=admin@your-domain.com
|
||||||
|
|
||||||
# Rate Limiting (requests per minute)
|
# Rate Limiting (requests per minute per IP)
|
||||||
RATE_LIMIT=100
|
RATE_LIMIT=30
|
||||||
|
|
||||||
|
# Logging
|
||||||
|
LOG_LEVEL=info
|
||||||
|
|
||||||
# CORS Configuration
|
# CORS Configuration
|
||||||
ALLOWED_ORIGINS=http://localhost:3000,https://yourdomain.com
|
ALLOWED_ORIGINS=*
|
||||||
```
|
```
|
||||||
|
|
||||||
3. **Start the server:**
|
3. **Start the server:**
|
||||||
```bash
|
```bash
|
||||||
# Development
|
# Development (with auto-reload)
|
||||||
npm run dev
|
npm run dev
|
||||||
|
|
||||||
# Production
|
# Production
|
||||||
npm start
|
npm start
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Configuration
|
||||||
## 🔧 Configuration
|
|
||||||
|
|
||||||
### Environment Variables
|
### Environment Variables
|
||||||
|
|
||||||
| Variable | Description | Default | Required |
|
| Variable | Description | Default | Required |
|
||||||
|----------|-------------|---------|----------|
|
|----------|-------------|---------|----------|
|
||||||
| `PORT` | Server port | `3000` | No |
|
| `PORT` | Server port | `3000` | No |
|
||||||
| `NODE_ENV` | Environment | `development` | No |
|
| `NODE_ENV` | Environment (`development` / `production`) | `development` | No |
|
||||||
| `ALLOW_REDEEM_DOMAINS` | Comma-separated allowed domains | All allowed | No |
|
| `API_DOMAIN` | Domain for Swagger docs and CORS | `localhost:3000` | No |
|
||||||
|
| `ALLOW_REDEEM_DOMAINS` | Comma-separated allowed Lightning address domains (`*` for all) | All allowed | No |
|
||||||
| `DEFAULT_LIGHTNING_ADDRESS` | Default Lightning address for redemptions | None | No |
|
| `DEFAULT_LIGHTNING_ADDRESS` | Default Lightning address for redemptions | None | No |
|
||||||
| `RATE_LIMIT` | Requests per minute per IP | `100` | No |
|
| `RATE_LIMIT` | Requests per minute per IP | `100` | No |
|
||||||
| `ALLOWED_ORIGINS` | CORS allowed origins | `http://localhost:3000` | No |
|
| `LOG_LEVEL` | Logging level | `info` | No |
|
||||||
|
| `ALLOWED_ORIGINS` | CORS allowed origins (`*` for all) | `http://localhost:3000` | No |
|
||||||
|
|
||||||
### Domain Restrictions
|
### Domain Restrictions
|
||||||
|
|
||||||
To restrict redemptions to specific Lightning address domains, set:
|
Restrict redemptions to specific Lightning address domains:
|
||||||
```bash
|
```bash
|
||||||
ALLOW_REDEEM_DOMAINS=ln.tips,getalby.com,wallet.mutinywallet.com
|
ALLOW_REDEEM_DOMAINS=twentyone.tips,getalby.com,walletofsatoshi.com
|
||||||
```
|
```
|
||||||
|
|
||||||
If not set, all domains are allowed.
|
If set to `*` or not set, all domains are allowed.
|
||||||
|
|
||||||
### Default Lightning Address
|
### Default Lightning Address
|
||||||
|
|
||||||
To set a default Lightning address that will be used when no address is provided in redemption requests:
|
Set a fallback address used when no address is provided in redemption requests:
|
||||||
```bash
|
```bash
|
||||||
DEFAULT_LIGHTNING_ADDRESS=admin@your-domain.com
|
DEFAULT_LIGHTNING_ADDRESS=admin@your-domain.com
|
||||||
```
|
```
|
||||||
|
|
||||||
This allows users to redeem tokens without specifying a Lightning address - the tokens will automatically be sent to your configured default address. If no default is set, Lightning address becomes required for all redemption requests.
|
If no default is configured, the `lightningAddress` field becomes required for all redemption requests.
|
||||||
|
|
||||||
|
## Project Structure
|
||||||
|
|
||||||
### Data Flow
|
```
|
||||||
|
cashu-redeem-api/
|
||||||
|
server.js # Express app, middleware, route mounting
|
||||||
|
swagger.config.js # OpenAPI/Swagger configuration and schemas
|
||||||
|
components/
|
||||||
|
cashu.js # Token parsing, spendability checks, melt operations
|
||||||
|
lightning.js # LNURLp resolution, invoice creation, address validation
|
||||||
|
redemption.js # Orchestrates the full redemption flow
|
||||||
|
routes/
|
||||||
|
cashu.js # POST /api/decode
|
||||||
|
redemption.js # POST /api/redeem
|
||||||
|
lightning.js # POST /api/validate-address
|
||||||
|
health.js # GET /api/health
|
||||||
|
```
|
||||||
|
|
||||||
1. **Token Validation** - Parse and validate Cashu token structure
|
## Redemption Flow
|
||||||
2. **Address Resolution** - Resolve Lightning address to LNURLp endpoint
|
|
||||||
3. **Invoice Generation** - Create Lightning invoice for the amount
|
|
||||||
4. **Token Melting** - Use cashu-ts to melt token and pay invoice
|
|
||||||
|
|
||||||
## 🔒 Security Features
|
1. **Token Parsing** - Validate and decode the Cashu token structure
|
||||||
|
2. **Spendability Check** - Verify proofs are unspent at the mint
|
||||||
|
3. **Melt Quote** - Get exact fee from the mint before creating the invoice
|
||||||
|
4. **Address Resolution** - Resolve Lightning address via LNURLp
|
||||||
|
5. **Invoice Generation** - Create a Lightning invoice for the net amount (total minus fee)
|
||||||
|
6. **Token Melting** - Melt the token at the mint to pay the invoice
|
||||||
|
|
||||||
- **Input validation** - All inputs are sanitized and validated
|
## Internal Status Codes
|
||||||
- **Rate limiting** - 100 requests per minute per IP (configurable)
|
|
||||||
- **Domain restrictions** - Limit allowed Lightning address domains
|
|
||||||
- **CORS protection** - Configurable allowed origins
|
|
||||||
- **Error handling** - Comprehensive error messages without data leaks
|
|
||||||
|
|
||||||
## 🚦 Status Codes
|
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
|--------|-------------|
|
||||||
| `processing` | Redemption is in progress |
|
| `processing` | Redemption initiated |
|
||||||
| `parsing_token` | Validating and parsing the token |
|
| `parsing_token` | Validating and parsing the token |
|
||||||
| `resolving_invoice` | Resolving Lightning address to invoice |
|
| `checking_spendability` | Checking proofs at the mint |
|
||||||
|
| `getting_melt_quote` | Requesting fee quote from mint |
|
||||||
|
| `resolving_invoice` | Creating Lightning invoice via LNURLp |
|
||||||
| `melting_token` | Performing the melt operation |
|
| `melting_token` | Performing the melt operation |
|
||||||
| `paid` | Successfully paid and completed |
|
| `paid` | Successfully completed |
|
||||||
| `failed` | Redemption failed (see error details) |
|
| `failed` | Redemption failed (see error details) |
|
||||||
|
|
||||||
|
## Security Features
|
||||||
|
|
||||||
## 🧪 Testing
|
- **Input validation** - All inputs are sanitized and validated
|
||||||
|
- **Rate limiting** - Configurable per-IP request limits
|
||||||
|
- **Domain restrictions** - Limit allowed Lightning address domains
|
||||||
|
- **CORS protection** - Configurable allowed origins
|
||||||
|
- **Duplicate detection** - Prevents resubmission of already-redeemed tokens
|
||||||
|
- **Error handling** - Comprehensive error messages without sensitive data leaks
|
||||||
|
|
||||||
### Interactive Testing with Swagger
|
## Testing
|
||||||
|
|
||||||
The easiest way to test the API is using the interactive Swagger documentation at `/docs`:
|
The easiest way to test the API is using the interactive Swagger documentation:
|
||||||
- Visit `http://localhost:3000/docs`
|
|
||||||
- Click "Try it out" on any endpoint
|
|
||||||
- Fill in the request parameters
|
|
||||||
- Execute the request directly from the browser
|
|
||||||
|
|
||||||
|
1. Start the server: `npm run dev`
|
||||||
|
2. Visit `http://localhost:3000/docs`
|
||||||
|
3. Click "Try it out" on any endpoint
|
||||||
|
4. Fill in the request parameters and execute
|
||||||
|
|
||||||
|
Or use curl:
|
||||||
|
```bash
|
||||||
|
# Health check
|
||||||
|
curl http://localhost:3000/api/health
|
||||||
|
|
||||||
## 🤝 Contributing
|
# Decode a token
|
||||||
|
curl -X POST http://localhost:3000/api/decode \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"token": "cashuB..."}'
|
||||||
|
|
||||||
|
# Validate a Lightning address
|
||||||
|
curl -X POST http://localhost:3000/api/validate-address \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"lightningAddress": "user@twentyone.tips"}'
|
||||||
|
|
||||||
|
# Redeem a token
|
||||||
|
curl -X POST http://localhost:3000/api/redeem \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"token": "cashuB...", "lightningAddress": "user@twentyone.tips"}'
|
||||||
|
```
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
1. Fork the repository
|
1. Fork the repository
|
||||||
2. Create a feature branch
|
2. Create a feature branch
|
||||||
3. Make your changes
|
3. Make your changes
|
||||||
4. Add tests if applicable
|
4. Submit a pull request
|
||||||
5. Submit a pull request
|
|
||||||
|
|
||||||
## 📝 License
|
## License
|
||||||
|
|
||||||
MIT License - see LICENSE file for details.
|
MIT License - see LICENSE file for details.
|
||||||
|
|||||||
Reference in New Issue
Block a user