All checks were successful
Build and Push Docker Images / docker (push) Successful in 2m28s
Features: - Complete NocoDB schema with 42 fields supporting B2B and B2C customers - Server-side NocoDB client (REST API integration) - Stripe session data mapper with automatic field mapping - Enhanced webhook handler with comprehensive logging - Automatic order creation in NocoDB after payment - Fulfillment data updates with Wren order IDs - Support for business customers (VAT/EIN, business names) - Complete billing address capture - Non-blocking error handling (webhook succeeds even if NocoDB fails) Files Added: - server/utils/nocodbClient.js - NocoDB REST API client - server/utils/nocodbMapper.js - Stripe to NocoDB data mapper - docs/NOCODB_SCHEMA.md - Complete field reference (42 columns) - docs/NOCODB_INTEGRATION_GUIDE.md - Testing and deployment guide - docs/TESTING_STRIPE_WEBHOOK.md - Webhook testing instructions - docs/STRIPE_INTEGRATION_SUMMARY.md - Project overview Files Modified: - server/routes/webhooks.js - Added NocoDB integration and enhanced logging - src/types.ts - Updated OrderRecord interface with new fields - src/api/nocodbClient.ts - Added createOrder() method - .env.example - Added NocoDB configuration template Schema includes: - Payment tracking (Stripe session/intent/customer IDs, amounts, fees) - Carbon offset details (tons, portfolio, Wren order ID) - Customer information (name, email, phone, business name) - Tax ID collection (VAT, EIN, etc.) - Complete billing address - Optional vessel/trip details for yacht calculations 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
102 lines
2.5 KiB
JavaScript
102 lines
2.5 KiB
JavaScript
/**
|
|
* NocoDB Client for Server
|
|
* Simple REST API client for NocoDB operations
|
|
*/
|
|
|
|
class NocoDBClient {
|
|
constructor() {
|
|
this.config = {
|
|
baseUrl: process.env.NOCODB_BASE_URL || '',
|
|
baseId: process.env.NOCODB_BASE_ID || '',
|
|
apiKey: process.env.NOCODB_API_KEY || '',
|
|
ordersTableId: process.env.NOCODB_ORDERS_TABLE_ID || '',
|
|
};
|
|
|
|
if (!this.config.baseUrl || !this.config.baseId || !this.config.apiKey || !this.config.ordersTableId) {
|
|
console.warn('⚠️ NocoDB configuration incomplete. Database integration disabled.');
|
|
}
|
|
|
|
this.baseUrl = `${this.config.baseUrl}/api/v2/tables/${this.config.ordersTableId}/records`;
|
|
}
|
|
|
|
/**
|
|
* Make authenticated request to NocoDB
|
|
*/
|
|
async request(endpoint, options = {}) {
|
|
const url = endpoint.startsWith('http') ? endpoint : `${this.baseUrl}${endpoint}`;
|
|
|
|
const response = await fetch(url, {
|
|
...options,
|
|
headers: {
|
|
'xc-token': this.config.apiKey,
|
|
'Content-Type': 'application/json',
|
|
...options.headers,
|
|
},
|
|
});
|
|
|
|
if (!response.ok) {
|
|
const errorText = await response.text();
|
|
throw new Error(`NocoDB request failed: ${response.status} - ${errorText}`);
|
|
}
|
|
|
|
return response.json();
|
|
}
|
|
|
|
/**
|
|
* Create new order record
|
|
*/
|
|
async createOrder(orderData) {
|
|
return this.request('', {
|
|
method: 'POST',
|
|
body: JSON.stringify(orderData),
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Update order fields
|
|
*/
|
|
async updateOrder(recordId, data) {
|
|
return this.request(`/${recordId}`, {
|
|
method: 'PATCH',
|
|
body: JSON.stringify(data),
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Find order by orderId field
|
|
*/
|
|
async findOrderByOrderId(orderId) {
|
|
const params = new URLSearchParams();
|
|
params.append('where', `(orderId,eq,${orderId})`);
|
|
params.append('limit', '1');
|
|
|
|
const response = await this.request(`?${params.toString()}`);
|
|
return response.list?.[0] || null;
|
|
}
|
|
|
|
/**
|
|
* Update order with Wren fulfillment data
|
|
*/
|
|
async updateOrderFulfillment(orderId, fulfillmentData) {
|
|
// First find the NocoDB record ID
|
|
const order = await this.findOrderByOrderId(orderId);
|
|
|
|
if (!order) {
|
|
throw new Error(`Order not found in NocoDB: ${orderId}`);
|
|
}
|
|
|
|
// Update the record
|
|
return this.updateOrder(order.Id, fulfillmentData);
|
|
}
|
|
|
|
/**
|
|
* Check if NocoDB is configured
|
|
*/
|
|
isConfigured() {
|
|
return !!(this.config.baseUrl && this.config.baseId && this.config.apiKey && this.config.ordersTableId);
|
|
}
|
|
}
|
|
|
|
// Export singleton instance
|
|
export const nocodbClient = new NocoDBClient();
|