import axios from 'axios'; const WREN_API_BASE_URL = 'https://www.wren.co/api'; /** * Create a carbon offset order via Wren Climate API * @param {Object} orderData - Order data * @param {number} orderData.tons - Number of tons to offset * @param {number} orderData.portfolioId - Portfolio ID * @param {string} orderData.customerEmail - Customer email (included in note) * @param {boolean} orderData.dryRun - Dry run mode (default: false) * @returns {Promise} Wren order response with amountCharged, currency, portfolio, tons */ export async function createWrenOffsetOrder({ tons, portfolioId, customerEmail, dryRun = false }) { const startTime = Date.now(); const apiToken = process.env.WREN_API_TOKEN; console.log('🔵 [WREN API SERVER] ========================================'); console.log('🔵 [WREN API SERVER] POST /offset_orders - Request initiated'); console.log('🔵 [WREN API SERVER] Timestamp:', new Date().toISOString()); console.log('🔵 [WREN API SERVER] API Token:', apiToken ? `${apiToken.substring(0, 8)}...${apiToken.substring(apiToken.length - 4)}` : 'NOT SET'); console.log('🔵 [WREN API SERVER] Request URL:', `${WREN_API_BASE_URL}/offset_orders`); console.log('🔵 [WREN API SERVER] Parameters:', JSON.stringify({ tons: parseFloat(tons), portfolioId, dryRun, note: `Puffin App order - Customer: ${customerEmail}` }, null, 2)); if (!apiToken) { console.error('❌ [WREN API SERVER] WREN_API_TOKEN not configured'); throw new Error('WREN_API_TOKEN environment variable is required'); } try { const response = await axios.post( `${WREN_API_BASE_URL}/offset-orders`, { tons: parseFloat(tons), portfolioId: portfolioId, dryRun: dryRun, note: `Puffin App order - Customer: ${customerEmail}` }, { headers: { 'Authorization': `Bearer ${apiToken}`, 'Content-Type': 'application/json' } } ); const duration = Date.now() - startTime; console.log('✅ [WREN API SERVER] POST /offset_orders - Success'); console.log('✅ [WREN API SERVER] Status:', response.status); console.log('✅ [WREN API SERVER] Duration:', duration + 'ms'); console.log('✅ [WREN API SERVER] Order ID:', response.data.id); console.log('✅ [WREN API SERVER] Response:', JSON.stringify(response.data, null, 2)); console.log('🔵 [WREN API SERVER] ========================================'); return response.data; } catch (error) { const duration = Date.now() - startTime; console.error('❌ [WREN API SERVER] POST /offset_orders - Failed'); console.error('❌ [WREN API SERVER] Status:', error.response?.status || 'No response'); console.error('❌ [WREN API SERVER] Duration:', duration + 'ms'); console.error('❌ [WREN API SERVER] Error message:', error.message); console.error('❌ [WREN API SERVER] Error response:', JSON.stringify(error.response?.data, null, 2)); console.error('❌ [WREN API SERVER] Full error:', error); console.log('🔵 [WREN API SERVER] ========================================'); throw new Error(`Wren API failed: ${error.response?.data?.message || error.message}`); } } /** * Get Wren offset order details * @param {string} orderId - Wren order ID * @returns {Promise} Wren order details */ export async function getWrenOffsetOrder(orderId) { const startTime = Date.now(); const apiToken = process.env.WREN_API_TOKEN; console.log('🔵 [WREN API SERVER] ========================================'); console.log('🔵 [WREN API SERVER] GET /offset_orders/:id - Request initiated'); console.log('🔵 [WREN API SERVER] Timestamp:', new Date().toISOString()); console.log('🔵 [WREN API SERVER] API Token:', apiToken ? `${apiToken.substring(0, 8)}...${apiToken.substring(apiToken.length - 4)}` : 'NOT SET'); console.log('🔵 [WREN API SERVER] Request URL:', `${WREN_API_BASE_URL}/offset_orders/${orderId}`); console.log('🔵 [WREN API SERVER] Order ID:', orderId); if (!apiToken) { console.error('❌ [WREN API SERVER] WREN_API_TOKEN not configured'); throw new Error('WREN_API_TOKEN environment variable is required'); } try { const response = await axios.get( `${WREN_API_BASE_URL}/offset-orders/${orderId}`, { headers: { 'Authorization': `Bearer ${apiToken}`, 'Content-Type': 'application/json' } } ); const duration = Date.now() - startTime; console.log('✅ [WREN API SERVER] GET /offset_orders/:id - Success'); console.log('✅ [WREN API SERVER] Status:', response.status); console.log('✅ [WREN API SERVER] Duration:', duration + 'ms'); console.log('✅ [WREN API SERVER] Response:', JSON.stringify(response.data, null, 2)); console.log('🔵 [WREN API SERVER] ========================================'); return response.data; } catch (error) { const duration = Date.now() - startTime; console.error('❌ [WREN API SERVER] GET /offset_orders/:id - Failed'); console.error('❌ [WREN API SERVER] Status:', error.response?.status || 'No response'); console.error('❌ [WREN API SERVER] Duration:', duration + 'ms'); console.error('❌ [WREN API SERVER] Error message:', error.message); console.error('❌ [WREN API SERVER] Error response:', JSON.stringify(error.response?.data, null, 2)); console.error('❌ [WREN API SERVER] Full error:', error); console.log('🔵 [WREN API SERVER] ========================================'); throw new Error(`Wren API failed: ${error.response?.data?.message || error.message}`); } } /** * Get Wren portfolios * @returns {Promise} Array of portfolio objects with projects */ export async function getWrenPortfolios() { const startTime = Date.now(); const apiToken = process.env.WREN_API_TOKEN; console.log('🔵 [WREN API SERVER] ========================================'); console.log('🔵 [WREN API SERVER] GET /portfolios - Request initiated'); console.log('🔵 [WREN API SERVER] Timestamp:', new Date().toISOString()); console.log('🔵 [WREN API SERVER] API Token:', apiToken ? `${apiToken.substring(0, 8)}...${apiToken.substring(apiToken.length - 4)}` : 'NOT SET'); console.log('🔵 [WREN API SERVER] Request URL:', `${WREN_API_BASE_URL}/portfolios`); if (!apiToken) { console.error('❌ [WREN API SERVER] WREN_API_TOKEN not configured'); throw new Error('WREN_API_TOKEN environment variable is required'); } try { const response = await axios.get( `${WREN_API_BASE_URL}/portfolios`, { headers: { 'Authorization': `Bearer ${apiToken}`, 'Content-Type': 'application/json' } } ); const duration = Date.now() - startTime; console.log('✅ [WREN API SERVER] GET /portfolios - Success'); console.log('✅ [WREN API SERVER] Status:', response.status); console.log('✅ [WREN API SERVER] Duration:', duration + 'ms'); console.log('✅ [WREN API SERVER] Portfolios count:', response.data?.portfolios?.length || 0); // Log detailed portfolio and project information including certification status if (response.data?.portfolios?.length > 0) { console.log('📊 [WREN API SERVER] Portfolio Details:'); response.data.portfolios.forEach((portfolio, idx) => { console.log(` Portfolio ${idx + 1}: "${portfolio.name}" (ID: ${portfolio.id})`); console.log(` ├─ Cost per ton: $${portfolio.cost_per_ton}`); console.log(` └─ Projects (${portfolio.projects?.length || 0}):`); if (portfolio.projects?.length > 0) { portfolio.projects.forEach((project, pIdx) => { console.log(` ${pIdx + 1}. "${project.name}"`); console.log(` ├─ Certification: ${project.certification_status || 'N/A'}`); console.log(` ├─ Cost per ton: $${project.cost_per_ton || 'N/A'}`); console.log(` └─ Percentage: ${project.percentage ? (project.percentage * 100).toFixed(1) + '%' : 'N/A'}`); }); } console.log(''); }); } console.log('🔵 [WREN API SERVER] ========================================'); return response.data?.portfolios || []; } catch (error) { const duration = Date.now() - startTime; console.error('❌ [WREN API SERVER] GET /portfolios - Failed'); console.error('❌ [WREN API SERVER] Status:', error.response?.status || 'No response'); console.error('❌ [WREN API SERVER] Duration:', duration + 'ms'); console.error('❌ [WREN API SERVER] Error message:', error.message); console.error('❌ [WREN API SERVER] Error response:', JSON.stringify(error.response?.data, null, 2)); console.log('🔵 [WREN API SERVER] ========================================'); // Return empty array on error (graceful degradation) return []; } } export default { createWrenOffsetOrder, getWrenOffsetOrder, getWrenPortfolios };