Initial commit
This commit is contained in:
145
backend/services/paypal.js
Normal file
145
backend/services/paypal.js
Normal file
@@ -0,0 +1,145 @@
|
||||
const paypal = require('@paypal/checkout-server-sdk');
|
||||
|
||||
// PayPal environment setup
|
||||
function environment() {
|
||||
const clientId = process.env.PAYPAL_CLIENT_ID;
|
||||
const clientSecret = process.env.PAYPAL_CLIENT_SECRET;
|
||||
const mode = process.env.PAYPAL_MODE || 'sandbox';
|
||||
|
||||
if (!clientId || !clientSecret) {
|
||||
throw new Error('PayPal credentials not found in environment variables');
|
||||
}
|
||||
|
||||
return mode === 'live'
|
||||
? new paypal.core.LiveEnvironment(clientId, clientSecret)
|
||||
: new paypal.core.SandboxEnvironment(clientId, clientSecret);
|
||||
}
|
||||
|
||||
// PayPal client
|
||||
function client() {
|
||||
return new paypal.core.PayPalHttpClient(environment());
|
||||
}
|
||||
|
||||
// Create PayPal order
|
||||
async function createOrder(orderData) {
|
||||
try {
|
||||
const request = new paypal.orders.OrdersCreateRequest();
|
||||
request.prefer("return=representation");
|
||||
request.requestBody({
|
||||
intent: 'CAPTURE',
|
||||
purchase_units: [{
|
||||
reference_id: orderData.reference_id,
|
||||
amount: {
|
||||
currency_code: orderData.currency || 'USD',
|
||||
value: orderData.total
|
||||
},
|
||||
description: orderData.description || 'Payment from WooCommerce',
|
||||
custom_id: orderData.wc_order_id,
|
||||
items: orderData.items || []
|
||||
}],
|
||||
application_context: {
|
||||
brand_name: orderData.brand_name || 'Your Store',
|
||||
landing_page: 'BILLING',
|
||||
user_action: 'PAY_NOW',
|
||||
return_url: `${process.env.FRONTEND_URL}/success`,
|
||||
cancel_url: `${process.env.FRONTEND_URL}/cancel`
|
||||
}
|
||||
});
|
||||
|
||||
const order = await client().execute(request);
|
||||
|
||||
console.log('PayPal Order Created:', {
|
||||
id: order.result.id,
|
||||
status: order.result.status,
|
||||
amount: orderData.total
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
order_id: order.result.id,
|
||||
status: order.result.status,
|
||||
links: order.result.links
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('PayPal Create Order Error:', error);
|
||||
return {
|
||||
success: false,
|
||||
error: error.message,
|
||||
details: error.details || []
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Capture PayPal payment
|
||||
async function captureOrder(orderId) {
|
||||
try {
|
||||
const request = new paypal.orders.OrdersCaptureRequest(orderId);
|
||||
request.requestBody({});
|
||||
|
||||
const capture = await client().execute(request);
|
||||
|
||||
console.log('PayPal Order Captured:', {
|
||||
id: capture.result.id,
|
||||
status: capture.result.status,
|
||||
payer_email: capture.result.payer?.email_address
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
capture_id: capture.result.id,
|
||||
status: capture.result.status,
|
||||
payer: capture.result.payer,
|
||||
purchase_units: capture.result.purchase_units,
|
||||
transaction_id: capture.result.purchase_units[0]?.payments?.captures[0]?.id
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('PayPal Capture Order Error:', error);
|
||||
return {
|
||||
success: false,
|
||||
error: error.message,
|
||||
details: error.details || []
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Get order details
|
||||
async function getOrderDetails(orderId) {
|
||||
try {
|
||||
const request = new paypal.orders.OrdersGetRequest(orderId);
|
||||
const order = await client().execute(request);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
order: order.result
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('PayPal Get Order Error:', error);
|
||||
return {
|
||||
success: false,
|
||||
error: error.message
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Verify webhook signature
|
||||
function verifyWebhookSignature(headers, body, webhookId) {
|
||||
// This is a simplified version - in production, use PayPal's webhook verification
|
||||
// For now, we'll do basic validation
|
||||
const webhookSecret = process.env.WEBHOOK_SECRET;
|
||||
|
||||
if (!webhookSecret) {
|
||||
console.warn('WEBHOOK_SECRET not set - skipping signature verification');
|
||||
return true;
|
||||
}
|
||||
|
||||
// Add proper webhook signature verification here
|
||||
// For development, we'll return true
|
||||
return true;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
createOrder,
|
||||
captureOrder,
|
||||
getOrderDetails,
|
||||
verifyWebhookSignature
|
||||
};
|
||||
204
backend/services/woocommerce.js
Normal file
204
backend/services/woocommerce.js
Normal file
@@ -0,0 +1,204 @@
|
||||
const axios = require('axios');
|
||||
const https = require('https');
|
||||
|
||||
class WooCommerceService {
|
||||
constructor() {
|
||||
this.baseURL = process.env.WOOCOMMERCE_URL;
|
||||
this.consumerKey = process.env.WOOCOMMERCE_CONSUMER_KEY;
|
||||
this.consumerSecret = process.env.WOOCOMMERCE_CONSUMER_SECRET;
|
||||
|
||||
if (!this.baseURL || !this.consumerKey || !this.consumerSecret) {
|
||||
console.warn('WooCommerce credentials not fully configured');
|
||||
}
|
||||
|
||||
// Create HTTPS agent that ignores SSL errors in development
|
||||
const httpsAgent = new https.Agent({
|
||||
rejectUnauthorized: process.env.NODE_ENV === 'production'
|
||||
});
|
||||
|
||||
this.client = axios.create({
|
||||
baseURL: `${this.baseURL}/wp-json/wc/v3`,
|
||||
auth: {
|
||||
username: this.consumerKey,
|
||||
password: this.consumerSecret
|
||||
},
|
||||
timeout: 10000,
|
||||
httpsAgent: httpsAgent,
|
||||
// Additional options for SSL issues
|
||||
headers: {
|
||||
'User-Agent': 'PaymentWebsite/1.0'
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Get order details from WooCommerce
|
||||
async getOrder(orderId) {
|
||||
try {
|
||||
const response = await this.client.get(`/orders/${orderId}`);
|
||||
|
||||
console.log('WooCommerce Order Retrieved:', {
|
||||
id: response.data.id,
|
||||
status: response.data.status,
|
||||
total: response.data.total
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
order: response.data
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('WooCommerce Get Order Error:', {
|
||||
orderId,
|
||||
error: error.response?.data || error.message
|
||||
});
|
||||
|
||||
return {
|
||||
success: false,
|
||||
error: error.response?.data?.message || error.message
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Update order status in WooCommerce
|
||||
async updateOrderStatus(orderId, status, transactionId = null) {
|
||||
try {
|
||||
const updateData = {
|
||||
status: status
|
||||
};
|
||||
|
||||
// Add transaction ID if payment was successful
|
||||
if (transactionId) {
|
||||
updateData.transaction_id = transactionId;
|
||||
updateData.meta_data = [
|
||||
{
|
||||
key: '_paypal_transaction_id',
|
||||
value: transactionId
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
const response = await this.client.put(`/orders/${orderId}`, updateData);
|
||||
|
||||
console.log('WooCommerce Order Updated:', {
|
||||
id: response.data.id,
|
||||
status: response.data.status,
|
||||
transaction_id: transactionId
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
order: response.data
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('WooCommerce Update Order Error:', {
|
||||
orderId,
|
||||
status,
|
||||
error: error.response?.data || error.message
|
||||
});
|
||||
|
||||
return {
|
||||
success: false,
|
||||
error: error.response?.data?.message || error.message
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Add order note
|
||||
async addOrderNote(orderId, note, customerNote = false) {
|
||||
try {
|
||||
const response = await this.client.post(`/orders/${orderId}/notes`, {
|
||||
note: note,
|
||||
customer_note: customerNote
|
||||
});
|
||||
|
||||
console.log('Order note added:', {
|
||||
orderId,
|
||||
noteId: response.data.id
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
note: response.data
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Add Order Note Error:', {
|
||||
orderId,
|
||||
error: error.response?.data || error.message
|
||||
});
|
||||
|
||||
return {
|
||||
success: false,
|
||||
error: error.response?.data?.message || error.message
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Process payment completion
|
||||
async completePayment(orderId, paypalData) {
|
||||
try {
|
||||
// Update order to processing/completed
|
||||
const updateResult = await this.updateOrderStatus(
|
||||
orderId,
|
||||
'processing', // or 'completed' based on your workflow
|
||||
paypalData.transaction_id
|
||||
);
|
||||
|
||||
if (!updateResult.success) {
|
||||
throw new Error(updateResult.error);
|
||||
}
|
||||
|
||||
// Add payment note
|
||||
const noteText = `Payment completed via PayPal. Transaction ID: ${paypalData.transaction_id}. Payer Email: ${paypalData.payer?.email_address || 'N/A'}`;
|
||||
|
||||
await this.addOrderNote(orderId, noteText, false);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: 'Payment completed and order updated'
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Complete Payment Error:', {
|
||||
orderId,
|
||||
error: error.message
|
||||
});
|
||||
|
||||
return {
|
||||
success: false,
|
||||
error: error.message
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Handle payment failure
|
||||
async failPayment(orderId, reason) {
|
||||
try {
|
||||
// Update order to failed
|
||||
const updateResult = await this.updateOrderStatus(orderId, 'failed');
|
||||
|
||||
if (!updateResult.success) {
|
||||
throw new Error(updateResult.error);
|
||||
}
|
||||
|
||||
// Add failure note
|
||||
const noteText = `Payment failed: ${reason}`;
|
||||
await this.addOrderNote(orderId, noteText, false);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: 'Order marked as failed'
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Fail Payment Error:', {
|
||||
orderId,
|
||||
error: error.message
|
||||
});
|
||||
|
||||
return {
|
||||
success: false,
|
||||
error: error.message
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new WooCommerceService();
|
||||
Reference in New Issue
Block a user