418 lines
11 KiB
Markdown
418 lines
11 KiB
Markdown
|
|
# Puffin Offset API Documentation
|
||
|
|
|
||
|
|
## Table of Contents
|
||
|
|
- [QR Code Generation API](#qr-code-generation-api)
|
||
|
|
- [Endpoint](#endpoint)
|
||
|
|
- [Request Format](#request-format)
|
||
|
|
- [Calculation Types](#calculation-types)
|
||
|
|
- [Response Format](#response-format)
|
||
|
|
- [Format Comparison (PNG vs SVG)](#format-comparison-png-vs-svg)
|
||
|
|
- [Usage Examples](#usage-examples)
|
||
|
|
- [Checkout API](#checkout-api)
|
||
|
|
- [Best Practices](#best-practices)
|
||
|
|
- [Error Handling](#error-handling)
|
||
|
|
|
||
|
|
## QR Code Generation API
|
||
|
|
|
||
|
|
### Endpoint
|
||
|
|
```
|
||
|
|
POST /api/qr-code/generate
|
||
|
|
```
|
||
|
|
|
||
|
|
Generate QR codes that encode carbon calculation parameters. The QR code directs users to the Puffin Offset calculator with pre-filled calculation data.
|
||
|
|
|
||
|
|
### Request Format
|
||
|
|
|
||
|
|
The API accepts three types of calculations:
|
||
|
|
|
||
|
|
#### 1. Fuel-Based Calculation
|
||
|
|
Calculate carbon offset based on fuel consumption:
|
||
|
|
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"calculationType": "fuel",
|
||
|
|
"fuelAmount": 1000,
|
||
|
|
"fuelUnit": "liters",
|
||
|
|
"vessel": {
|
||
|
|
"imo": "1234567",
|
||
|
|
"name": "Sample Yacht",
|
||
|
|
"type": "Motor Yacht",
|
||
|
|
"enginePower": 2250
|
||
|
|
},
|
||
|
|
"timestamp": "2025-03-15T10:00:00Z",
|
||
|
|
"source": "marina-api"
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Fields:**
|
||
|
|
- `calculationType`: Must be `"fuel"`
|
||
|
|
- `fuelAmount`: Number of fuel units consumed
|
||
|
|
- `fuelUnit`: Either `"liters"` or `"gallons"`
|
||
|
|
- `vessel`: Optional vessel metadata (for informational purposes only)
|
||
|
|
- `timestamp`: Optional ISO timestamp
|
||
|
|
- `source`: Optional identifier (e.g., "marina-api", "broker-portal")
|
||
|
|
|
||
|
|
#### 2. Distance-Based Calculation
|
||
|
|
Calculate carbon offset based on trip distance and vessel characteristics:
|
||
|
|
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"calculationType": "distance",
|
||
|
|
"distance": 150,
|
||
|
|
"speed": 12,
|
||
|
|
"fuelRate": 85,
|
||
|
|
"vessel": {
|
||
|
|
"imo": "1234567",
|
||
|
|
"name": "Sample Yacht",
|
||
|
|
"type": "Motor Yacht",
|
||
|
|
"enginePower": 2250
|
||
|
|
},
|
||
|
|
"timestamp": "2025-03-15T10:00:00Z",
|
||
|
|
"source": "marina-api"
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Fields:**
|
||
|
|
- `calculationType`: Must be `"distance"`
|
||
|
|
- `distance`: Distance in nautical miles
|
||
|
|
- `speed`: Average speed in knots
|
||
|
|
- `fuelRate`: Fuel consumption rate in liters per hour
|
||
|
|
- `vessel`: Optional vessel metadata
|
||
|
|
- `timestamp`: Optional ISO timestamp
|
||
|
|
- `source`: Optional identifier
|
||
|
|
|
||
|
|
#### 3. Custom Amount
|
||
|
|
Direct monetary amount for carbon offsetting:
|
||
|
|
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"calculationType": "custom",
|
||
|
|
"customAmount": 250.00,
|
||
|
|
"vessel": {
|
||
|
|
"name": "Corporate Event",
|
||
|
|
"type": "Conference"
|
||
|
|
},
|
||
|
|
"timestamp": "2025-03-15T10:00:00Z",
|
||
|
|
"source": "event-organizer"
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Fields:**
|
||
|
|
- `calculationType`: Must be `"custom"`
|
||
|
|
- `customAmount`: Dollar amount (USD) for direct offset purchase
|
||
|
|
- `vessel`: Optional context information
|
||
|
|
- `timestamp`: Optional ISO timestamp
|
||
|
|
- `source`: Optional identifier
|
||
|
|
|
||
|
|
### Response Format
|
||
|
|
|
||
|
|
The API returns both PNG and SVG formats of the QR code:
|
||
|
|
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"success": true,
|
||
|
|
"data": {
|
||
|
|
"qrCodeDataURL": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUg...",
|
||
|
|
"qrCodeSVG": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 256 256\">...</svg>",
|
||
|
|
"url": "https://puffinoffset.com/calculator?qr=eyJjYWxjdWxhdGlvblR5cGUiOi...",
|
||
|
|
"expiresAt": "2025-04-14T10:00:00.000Z"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Response Fields:**
|
||
|
|
- `qrCodeDataURL`: Base64-encoded PNG image as data URL (ready for `<img>` src)
|
||
|
|
- `qrCodeSVG`: SVG markup string (ready for direct HTML injection)
|
||
|
|
- `url`: The full URL that the QR code points to
|
||
|
|
- `expiresAt`: ISO timestamp when the QR code expires (30 days from generation)
|
||
|
|
|
||
|
|
### Format Comparison (PNG vs SVG)
|
||
|
|
|
||
|
|
| Feature | PNG (qrCodeDataURL) | SVG (qrCodeSVG) |
|
||
|
|
|---------|---------------------|-----------------|
|
||
|
|
| **Format** | Base64-encoded PNG as data URL | SVG markup string |
|
||
|
|
| **Use Case** | Email, printing, basic displays | Web, scaling, professional prints |
|
||
|
|
| **File Size** | Larger (~5-10 KB) | Smaller (~2-4 KB) |
|
||
|
|
| **Scalability** | Pixelated when enlarged | Perfect at any size |
|
||
|
|
| **Browser Support** | Universal | Universal (all modern browsers) |
|
||
|
|
| **Best For** | Quick implementation, emails | Websites, responsive design, high-DPI displays |
|
||
|
|
| **Embedding** | `<img src="{qrCodeDataURL}">` | Direct HTML injection or `<img src="data:image/svg+xml,{encoded}">` |
|
||
|
|
|
||
|
|
**When to Use PNG:**
|
||
|
|
- Email campaigns (better compatibility)
|
||
|
|
- Print materials with fixed sizes
|
||
|
|
- Quick prototyping
|
||
|
|
- When exact pixel dimensions are known
|
||
|
|
|
||
|
|
**When to Use SVG:**
|
||
|
|
- Responsive web design
|
||
|
|
- High-resolution displays (Retina, 4K)
|
||
|
|
- Professional print materials (scales to any size)
|
||
|
|
- When minimizing file size is important
|
||
|
|
- Dynamic styling with CSS
|
||
|
|
|
||
|
|
### Usage Examples
|
||
|
|
|
||
|
|
#### cURL
|
||
|
|
```bash
|
||
|
|
curl -X POST https://puffinoffset.com/api/qr-code/generate \
|
||
|
|
-H "Content-Type: application/json" \
|
||
|
|
-d '{
|
||
|
|
"calculationType": "distance",
|
||
|
|
"distance": 150,
|
||
|
|
"speed": 12,
|
||
|
|
"fuelRate": 85,
|
||
|
|
"vessel": {
|
||
|
|
"imo": "1234567",
|
||
|
|
"name": "Sample Yacht"
|
||
|
|
}
|
||
|
|
}'
|
||
|
|
```
|
||
|
|
|
||
|
|
#### JavaScript (Fetch API)
|
||
|
|
```javascript
|
||
|
|
const response = await fetch('https://puffinoffset.com/api/qr-code/generate', {
|
||
|
|
method: 'POST',
|
||
|
|
headers: {
|
||
|
|
'Content-Type': 'application/json',
|
||
|
|
},
|
||
|
|
body: JSON.stringify({
|
||
|
|
calculationType: 'fuel',
|
||
|
|
fuelAmount: 1000,
|
||
|
|
fuelUnit: 'liters',
|
||
|
|
vessel: {
|
||
|
|
name: 'Sample Yacht',
|
||
|
|
imo: '1234567'
|
||
|
|
}
|
||
|
|
})
|
||
|
|
});
|
||
|
|
|
||
|
|
const { data } = await response.json();
|
||
|
|
console.log('QR Code URL:', data.url);
|
||
|
|
|
||
|
|
// Use PNG format
|
||
|
|
document.getElementById('qr-png').src = data.qrCodeDataURL;
|
||
|
|
|
||
|
|
// Use SVG format
|
||
|
|
document.getElementById('qr-svg-container').innerHTML = data.qrCodeSVG;
|
||
|
|
```
|
||
|
|
|
||
|
|
#### Python
|
||
|
|
```python
|
||
|
|
import requests
|
||
|
|
|
||
|
|
response = requests.post(
|
||
|
|
'https://puffinoffset.com/api/qr-code/generate',
|
||
|
|
json={
|
||
|
|
'calculationType': 'custom',
|
||
|
|
'customAmount': 250.00,
|
||
|
|
'source': 'python-client'
|
||
|
|
}
|
||
|
|
)
|
||
|
|
|
||
|
|
data = response.json()['data']
|
||
|
|
print(f"QR Code URL: {data['url']}")
|
||
|
|
print(f"Expires at: {data['expiresAt']}")
|
||
|
|
|
||
|
|
# Save PNG to file
|
||
|
|
import base64
|
||
|
|
png_data = data['qrCodeDataURL'].split(',')[1]
|
||
|
|
with open('qr-code.png', 'wb') as f:
|
||
|
|
f.write(base64.b64decode(png_data))
|
||
|
|
|
||
|
|
# Save SVG to file
|
||
|
|
with open('qr-code.svg', 'w') as f:
|
||
|
|
f.write(data['qrCodeSVG'])
|
||
|
|
```
|
||
|
|
|
||
|
|
#### HTML Embedding
|
||
|
|
|
||
|
|
**PNG Format:**
|
||
|
|
```html
|
||
|
|
<!-- Direct data URL in img tag -->
|
||
|
|
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUg..."
|
||
|
|
alt="Carbon Offset QR Code"
|
||
|
|
style="width: 300px; height: 300px;">
|
||
|
|
```
|
||
|
|
|
||
|
|
**SVG Format:**
|
||
|
|
```html
|
||
|
|
<!-- Direct SVG injection (recommended for web) -->
|
||
|
|
<div id="qr-container">
|
||
|
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256">
|
||
|
|
<!-- SVG content from API -->
|
||
|
|
</svg>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- Or as data URL -->
|
||
|
|
<img src="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'>...</svg>"
|
||
|
|
alt="Carbon Offset QR Code"
|
||
|
|
style="width: 100%; max-width: 400px;">
|
||
|
|
```
|
||
|
|
|
||
|
|
**Dynamic Sizing with SVG:**
|
||
|
|
```css
|
||
|
|
/* SVG automatically scales to container */
|
||
|
|
#qr-container {
|
||
|
|
width: 100%;
|
||
|
|
max-width: 500px;
|
||
|
|
margin: 0 auto;
|
||
|
|
}
|
||
|
|
|
||
|
|
#qr-container svg {
|
||
|
|
width: 100%;
|
||
|
|
height: auto;
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
## Checkout API
|
||
|
|
|
||
|
|
### Create Checkout Session
|
||
|
|
```
|
||
|
|
POST /api/checkout/create-session
|
||
|
|
```
|
||
|
|
|
||
|
|
Create a Stripe checkout session for carbon offset purchase.
|
||
|
|
|
||
|
|
**Request Body:**
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"tons": 2.5,
|
||
|
|
"portfolioId": 123,
|
||
|
|
"pricePerTon": 20.00,
|
||
|
|
"customerEmail": "customer@example.com"
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Response:**
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"sessionId": "cs_test_...",
|
||
|
|
"url": "https://checkout.stripe.com/...",
|
||
|
|
"orderId": "order_abc123"
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Note:** Vessel information is NOT included in checkout sessions or stored in orders. Vessel metadata in QR codes is for informational purposes only.
|
||
|
|
|
||
|
|
### Get Order Details
|
||
|
|
```
|
||
|
|
GET /api/checkout/session/{sessionId}
|
||
|
|
```
|
||
|
|
|
||
|
|
Retrieve order details by Stripe session ID.
|
||
|
|
|
||
|
|
**Response:**
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"order": {
|
||
|
|
"id": "order_abc123",
|
||
|
|
"tons": 2.5,
|
||
|
|
"portfolioId": 123,
|
||
|
|
"baseAmount": 5000,
|
||
|
|
"processingFee": 180,
|
||
|
|
"totalAmount": 5180,
|
||
|
|
"currency": "USD",
|
||
|
|
"status": "paid",
|
||
|
|
"wrenOrderId": "wren_xyz789",
|
||
|
|
"stripeSessionId": "cs_test_...",
|
||
|
|
"createdAt": "2025-03-15T10:00:00.000Z"
|
||
|
|
},
|
||
|
|
"session": {
|
||
|
|
"paymentStatus": "paid",
|
||
|
|
"customerEmail": "customer@example.com"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
## Best Practices
|
||
|
|
|
||
|
|
### QR Code Generation
|
||
|
|
|
||
|
|
1. **Choose the Right Format:**
|
||
|
|
- Use **PNG** for emails, fixed-size prints, and quick prototypes
|
||
|
|
- Use **SVG** for websites, responsive designs, and high-quality prints
|
||
|
|
|
||
|
|
2. **Include Metadata:**
|
||
|
|
- Always include `source` field to track where QR codes originate
|
||
|
|
- Include `timestamp` for auditing and expiration tracking
|
||
|
|
- Use descriptive `vessel` names for context
|
||
|
|
|
||
|
|
3. **Handle Expiration:**
|
||
|
|
- QR codes expire after 30 days
|
||
|
|
- Check `expiresAt` timestamp in response
|
||
|
|
- Regenerate QR codes if needed for long-term use
|
||
|
|
|
||
|
|
4. **Error Handling:**
|
||
|
|
- Validate input data before sending to API
|
||
|
|
- Handle network failures gracefully
|
||
|
|
- Display user-friendly error messages
|
||
|
|
|
||
|
|
### Integration Recommendations
|
||
|
|
|
||
|
|
1. **Marinas & Brokers:**
|
||
|
|
- Generate QR codes after trip logging
|
||
|
|
- Print QR codes on receipts or invoices
|
||
|
|
- Include vessel details for tracking
|
||
|
|
|
||
|
|
2. **Event Organizers:**
|
||
|
|
- Use `custom` calculation type for flat-rate offsets
|
||
|
|
- Generate bulk QR codes for attendee packets
|
||
|
|
- Track source with `source` field
|
||
|
|
|
||
|
|
3. **Mobile Apps:**
|
||
|
|
- Use SVG format for responsive display
|
||
|
|
- Cache QR codes locally to reduce API calls
|
||
|
|
- Implement QR code scanning for peer-to-peer sharing
|
||
|
|
|
||
|
|
## Error Handling
|
||
|
|
|
||
|
|
### Error Response Format
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"success": false,
|
||
|
|
"error": "Invalid calculation type"
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Common Errors
|
||
|
|
|
||
|
|
| Error | Cause | Solution |
|
||
|
|
|-------|-------|----------|
|
||
|
|
| "Invalid calculation type" | `calculationType` not "fuel", "distance", or "custom" | Use valid calculation type |
|
||
|
|
| "Missing required fields" | Required fields for calculation type not provided | Check calculation type requirements |
|
||
|
|
| "Invalid fuel unit" | `fuelUnit` not "liters" or "gallons" | Use valid fuel unit |
|
||
|
|
| "Invalid amount" | Negative or zero values | Provide positive values |
|
||
|
|
| 500 Server Error | Server-side issue | Retry request, contact support if persistent |
|
||
|
|
|
||
|
|
### HTTP Status Codes
|
||
|
|
|
||
|
|
- **200 OK**: QR code generated successfully
|
||
|
|
- **400 Bad Request**: Invalid request data (check error message)
|
||
|
|
- **500 Internal Server Error**: Server-side error (retry or contact support)
|
||
|
|
|
||
|
|
## Future Enhancements
|
||
|
|
|
||
|
|
The following features are planned for future releases:
|
||
|
|
|
||
|
|
1. **Authentication:** API key authentication for rate limiting and analytics
|
||
|
|
2. **Batch Generation:** Generate multiple QR codes in a single request
|
||
|
|
3. **Custom Branding:** Add logos or custom colors to QR codes
|
||
|
|
4. **Analytics Dashboard:** Track QR code scans and conversion rates
|
||
|
|
5. **Webhook Support:** Receive notifications when QR codes are scanned or orders completed
|
||
|
|
|
||
|
|
## Changelog
|
||
|
|
|
||
|
|
### Version 1.0 (Current)
|
||
|
|
- Initial release
|
||
|
|
- Support for fuel-based, distance-based, and custom amount calculations
|
||
|
|
- PNG and SVG format outputs
|
||
|
|
- 30-day expiration
|
||
|
|
- No authentication required
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
**Support:** For questions or issues, contact [support@puffinoffset.com](mailto:support@puffinoffset.com)
|
||
|
|
|
||
|
|
**API Base URL:** `https://puffinoffset.com/api`
|