quickpos
Version:
<div align="center"> <h1>💳 QuickPos 🚀</h1> <p><strong>A powerful, multi-gateway payment integration module for Node.js</strong></p> <p>Seamlessly integrate with 50+ payment providers worldwide</p>
216 lines (183 loc) • 6.99 kB
JavaScript
const crypto = require('crypto');
const axios = require('axios');
class BufPay {
constructor(config) {
this.config = config || {};
const requiredFields = ['appId', 'appSecret'];
for (let field of requiredFields) {
if (!config[field]) throw new Error(`Missing required field: ${field}`);
}
this.appId = config.appId;
this.appSecret = config.appSecret;
this.baseUrl = 'https://bufpay.com/api';
}
async createPayment(paymentDetails) {
try {
// console.log('Creating payment with details:', JSON.stringify(paymentDetails, null, 2));
let requiredData = ['name', 'payType', 'price', 'orderId', 'orderUid', 'notifyUrl'];
for (let data of requiredData) {
if (!paymentDetails[data]) throw new Error(`Missing required data: ${data}`);
}
const signature = this.generateToken(
paymentDetails.name,
paymentDetails.payType,
paymentDetails.price,
paymentDetails.orderId,
paymentDetails.orderUid,
paymentDetails.notifyUrl,
paymentDetails.returnUrl || '',
paymentDetails.feedbackUrl || '',
this.appSecret
);
// console.log('Generated signature:', signature);
const formData = {
name: paymentDetails.name,
pay_type: paymentDetails.payType,
price: paymentDetails.price,
order_id: paymentDetails.orderId,
order_uid: paymentDetails.orderUid,
notify_url: paymentDetails.notifyUrl,
return_url: paymentDetails.returnUrl || '',
feedback_url: paymentDetails.feedbackUrl || '',
sign: signature
};
// console.log('Sending request with form data:', JSON.stringify(formData, null, 2));
const response = await axios({
method: 'POST',
url: `${this.baseUrl}/pay/${this.appId}?format=json`,
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
data: new URLSearchParams(formData).toString()
});
// console.log('Received response:', JSON.stringify(response.data, null, 2));
if (!response.data) {
throw new Error('Empty response received from BufPay API');
}
const responseData = response.data;
// Log the full response for debugging
// console.log('Response status:', responseData.status);
// console.log('Full response:', JSON.stringify(responseData, null, 2));
if (responseData.status === 'success' || responseData.status === 'ok') {
return {
status: 'success',
data: {
aoid: responseData.aoid,
pay_type: responseData?.pay_type,
price: responseData?.price,
qr_price: responseData?.qr_price,
qr_img: responseData?.qr_img,
qr: responseData?.qr,
cid: responseData?.cid,
expires_in: responseData?.expires_in,
return_url: responseData?.return_url,
feedback_url: responseData?.feedback_url
}
};
} else {
return {
status: 'fail',
message: responseData?.status
}
}
} catch (error) {
// console.error('Error details:', error);
if (error.response) {
// console.error('API Response Error:', {
// status: error.response.status,
// data: error.response.data,
// headers: error.response.headers
// });
throw new Error(`BufPay API error: ${JSON.stringify(error.response.data)}`);
} else if (error.request) {
// console.error('No Response Error:', error.request);
throw new Error('No response received from BufPay API');
} else {
// console.error('Request Setup Error:', error.message);
throw new Error(`Error in BufPay payment creation: ${error.message}`);
}
}
}
generateToken(...params) {
try {
const concatenated = params
.filter(param => param !== undefined && param !== null)
.map(param => String(param))
.join('');
// console.log('Generating token with params:', concatenated);
const token = crypto
.createHash('md5')
.update(concatenated, 'utf8')
.digest('hex')
.toUpperCase();
// console.log('Generated token:', token);
return token;
} catch (error) {
// console.error('Token generation error:', error);
throw new Error(`Error generating token: ${error.message}`);
}
}
verifyCallback(callbackData) {
try {
console.log('Verifying callback data:', JSON.stringify(callbackData, null, 2));
const { aoid, order_id, order_uid, price, pay_price, sign } = callbackData;
if (!aoid || !order_id || !order_uid || !price || !pay_price || !sign) {
console.log('Missing required callback fields');
return false;
}
const expectedSignature = this.generateToken(
aoid,
order_id,
order_uid,
price,
pay_price,
this.appSecret
);
console.log('Expected signature:', expectedSignature);
console.log('Received signature:', sign);
return sign === expectedSignature;
} catch (error) {
console.error('Callback verification error:', error);
return false;
}
}
async handleCallback(callbackData) {
try {
console.log('Processing callback data:', JSON.stringify(callbackData, null, 2));
const isValid = this.verifyCallback(callbackData);
if (!isValid) {
throw new Error("BufPay notification failed: invalid signature");
}
if (callbackData.status === 'success') {
return {
status: 'success',
orderId: callbackData.order_id,
transactionId: callbackData.aoid,
amount: parseFloat(callbackData.pay_price),
originalAmount: parseFloat(callbackData.price),
orderUid: callbackData.order_uid
};
} else {
throw new Error(`Payment failed: ${JSON.stringify(callbackData)}`);
}
} catch (error) {
console.error('Callback handling error:', error);
throw new Error(`Error in BufPay callback handling: ${error.message}`);
}
}
async queryPayment(aoid) {
try {
console.log('Querying payment with AOID:', aoid);
if (!aoid) {
throw new Error('Transaction ID (AOID) is required');
}
const response = await axios.get(`${this.baseUrl}/query/${aoid}`);
console.log('Query response:', JSON.stringify(response.data, null, 2));
return response.data;
} catch (error) {
console.error('Payment query error:', error);
throw new Error(`Payment query failed: ${error.message}`);
}
}
}
module.exports = BufPay;