nakapay-react
Version:
React components for NakaPay Bitcoin Lightning payments
292 lines (229 loc) • 8.57 kB
Markdown
# NakaPay React Components
React components for integrating Bitcoin Lightning payments with NakaPay.
⚠️ **SECURITY WARNING**: The NakaPay API key must ONLY be used on your backend server. Never expose API keys in client-side code, environment variables with `NEXT_PUBLIC_` prefix, or frontend applications.
## Installation
```bash
npm install nakapay-react
# or
yarn add nakapay-react
```
## Quick Start
### 1. Set up your backend (REQUIRED)
**CRITICAL**: API keys must only be used server-side. Set up backend endpoints to handle payments securely:
```javascript
// Backend example (Express.js) - SERVER SIDE ONLY
const express = require('express');
const { NakaPay } = require('nakapay-sdk');
const app = express();
// SECURE: API key from server environment variable only
const nakaPay = new NakaPay(process.env.NAKAPAY_API_KEY); // NOT NEXT_PUBLIC_
app.post('/api/create-payment', async (req, res) => {
try {
// Add input validation
const { amount, description, metadata } = req.body;
if (!amount || amount <= 0) {
return res.status(400).json({ error: 'Invalid amount' });
}
if (!description || description.trim().length === 0) {
return res.status(400).json({ error: 'Description required' });
}
const payment = await nakaPay.createPaymentRequest(req.body);
res.json(payment);
} catch (error) {
res.status(500).json({ error: error.message });
}
});
app.get('/api/payment-status/:id', async (req, res) => {
try {
const { id } = req.params;
if (!id || id.trim().length === 0) {
return res.status(400).json({ error: 'Payment ID required' });
}
const status = await nakaPay.getPaymentStatus(id);
res.json(status);
} catch (error) {
res.status(500).json({ error: error.message });
}
});
```
### 2. Use React components
```tsx
import React from 'react';
import { NakaPayButton } from 'nakapay-react';
function CheckoutPage() {
return (
<div>
<h2>Complete Your Purchase</h2>
<NakaPayButton
amount={50000} // Amount in satoshis
description="Product purchase"
onPaymentSuccess={(payment) => {
console.log('Payment successful!', payment);
// Redirect to success page or update UI
}}
onPaymentError={(error) => {
console.error('Payment failed:', error);
// Handle error
}}
/>
</div>
);
}
```
## Components
### NakaPayButton
A complete payment button that handles the entire payment flow.
**Props:**
- `amount` (number, required): Payment amount in satoshis
- `description` (string, required): Payment description
- `metadata?` (object): Additional metadata for the payment
- `text?` (string): Custom button text (defaults to "Pay {amount} sats")
- `className?` (string): Additional CSS classes
- `style?` (CSSProperties): Custom styles
- `disabled?` (boolean): Disable the button
- `apiEndpoint?` (string): Backend endpoint for creating payments (default: '/api/create-payment')
- `onPaymentCreated?` (function): Called when payment is created
- `onPaymentSuccess?` (function): Called when payment is successful
- `onPaymentError?` (function): Called when payment fails
**Real-time Payment Notifications:**
- `useAbly?` (boolean): Use Ably for real-time updates (recommended)
- `ablyApiKey?` (string): Ably API key for real-time connection
- `useWebhooks?` (boolean): Use WebSocket connection for real-time updates
- `webhookUrl?` (string): WebSocket server URL for webhook-based updates
- `useSSE?` (boolean): Use Server-Sent Events for real-time updates
- `pollInterval?` (number): Polling interval in ms when real-time methods unavailable (default: 2000)
- `statusEndpoint?` (string): Backend endpoint for status polling (default: '/api/payment-status')
### NakaPayModal
A payment modal component for custom implementations.
**Props:**
- `payment` (Payment, required): Payment object from your backend
- `onClose` (function, required): Called when modal is closed
- `onPaymentSuccess?` (function): Called when payment is successful
- `onPaymentError?` (function): Called when payment fails
- `useAbly?` (boolean): Use Ably for real-time updates
- `ablyApiKey?` (string): Ably API key for real-time connection
- `useWebhooks?` (boolean): Use WebSocket connection for updates
- `webhookUrl?` (string): WebSocket server URL
- `useSSE?` (boolean): Use Server-Sent Events for updates
- `pollInterval?` (number): Status polling interval in ms (default: 2000)
- `statusEndpoint?` (string): Backend endpoint for checking status (default: '/api/payment-status')
## Real-time Payment Updates
NakaPay React components support multiple methods for real-time payment status updates, with automatic fallbacks:
### 1. Ably (Recommended)
Ably provides the most reliable real-time updates using cloud infrastructure:
```tsx
<NakaPayButton
amount={50000}
description="Product purchase"
useAbly={true}
ablyApiKey="your-ably-api-key"
onPaymentSuccess={(payment) => console.log('Success!', payment)}
/>
```
**Setup:**
1. Sign up for [Ably](https://ably.com)
2. Get your API key
3. Configure webhooks in your NakaPay dashboard to publish to Ably
### 2. WebSocket Connection
Direct WebSocket connection to your webhook server:
```tsx
<NakaPayButton
amount={50000}
description="Product purchase"
useWebhooks={true}
webhookUrl="ws://localhost:3002"
onPaymentSuccess={(payment) => console.log('Success!', payment)}
/>
```
**Setup:**
1. Run a WebSocket server (e.g., using Socket.IO)
2. Configure NakaPay webhooks to notify your server
3. Have your server emit WebSocket events to connected clients
### 3. Server-Sent Events (SSE)
HTTP streaming for real-time updates:
```tsx
<NakaPayButton
amount={50000}
description="Product purchase"
useSSE={true}
onPaymentSuccess={(payment) => console.log('Success!', payment)}
/>
```
**Setup:**
1. Implement `/api/payments/stream` endpoint that streams SSE
2. Configure NakaPay webhooks to trigger SSE updates
### 4. Polling (Automatic Fallback)
If no real-time method is configured, components automatically fall back to polling:
```tsx
<NakaPayButton
amount={50000}
description="Product purchase"
pollInterval={3000} // Check every 3 seconds
statusEndpoint="/api/payment-status"
onPaymentSuccess={(payment) => console.log('Success!', payment)}
/>
```
### Priority Order
The components try real-time methods in this order:
1. **Ably** (if `useAbly={true}`)
2. **WebSocket** (if `useWebhooks={true}`)
3. **Server-Sent Events** (if `useSSE={true}`)
4. **Polling** (automatic fallback)
## Advanced Usage
### Custom Payment Flow
```tsx
import React, { useState } from 'react';
import { NakaPayModal, Payment } from 'nakapay-react';
function CustomCheckout() {
const [showModal, setShowModal] = useState(false);
const [payment, setPayment] = useState<Payment | null>(null);
const handleCustomPayment = async () => {
try {
const response = await fetch('/api/create-payment', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
amount: 25000,
description: 'Custom payment',
metadata: { orderId: '12345' }
})
});
const paymentData = await response.json();
setPayment(paymentData);
setShowModal(true);
} catch (error) {
console.error('Failed to create payment:', error);
}
};
return (
<div>
<button onClick={handleCustomPayment}>
Custom Pay Button
</button>
{showModal && payment && (
<NakaPayModal
payment={payment}
onClose={() => setShowModal(false)}
onPaymentSuccess={(payment) => {
console.log('Success!', payment);
setShowModal(false);
}}
/>
)}
</div>
);
}
```
## Styling
Import the default styles:
```tsx
import 'nakapay-react/dist/styles.css';
```
Or provide your own custom styles using the provided CSS classes:
- `.nakapay-button` - Button component
- `.nakapay-modal-overlay` - Modal overlay
- `.nakapay-modal` - Modal content
## TypeScript Support
This package includes TypeScript definitions. All components are fully typed.
## License
MIT