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>
174 lines (149 loc) • 6.01 kB
JavaScript
const axios = require('axios');
const crypto = require('crypto');
class DokuClient {
constructor(config) {
const requiredFields = ['clientId', 'secretKey'];
for (let field of requiredFields) {
if (!config[field]) throw new Error(`Missing required field: ${field}`);
}
this.clientId = config.clientId;
this.secretKey = config.secretKey;
this.sharedKey = config.sharedKey || this.secretKey;
this.baseURL = config.sandbox
? 'https://sandbox.doku.com'
: 'https://api.doku.com';
this.client = axios.create({
baseURL: this.baseURL,
headers: {
'Content-Type': 'application/json',
'Client-Id': this.clientId
}
});
}
generateSignature(data, timestamp) {
const digest = crypto.createHash('sha256')
.update(JSON.stringify(data))
.digest('hex');
const signatureComponents = `${this.clientId}:${timestamp}:${digest}`;
return crypto
.createHmac('sha256', this.secretKey)
.update(signatureComponents)
.digest('base64');
}
async createPayment(options) {
try {
const orderId = options.orderId || `ORDER-${Date.now()}`;
const timestamp = new Date().toISOString();
const paymentData = {
order: {
invoice_number: orderId,
amount: parseFloat(options.amount)
},
payment: {
payment_due_date: options.dueDate || new Date(Date.now() + 24*60*60*1000).toISOString().split('T')[0]
},
customer: {
name: options.name || options.customerName || '',
email: options.email || '',
phone: options.phone || ''
},
callback_url: options.callbackUrl || options.callback_link,
return_url: options.successUrl || options.callback_link
};
const signature = this.generateSignature(paymentData, timestamp);
const response = await this.client.post('/v1/payment-code', paymentData, {
headers: {
'Request-Timestamp': timestamp,
'Signature': signature
}
});
return {
status: 'success',
data: {
orderId: orderId,
paymentCode: response.data.payment_code,
amount: paymentData.order.amount,
expiredDate: response.data.expired_date,
virtualAccount: response.data.virtual_account_info
}
};
} catch (error) {
throw new Error(`Payment creation error: ${error.response?.data?.error?.message || error.message}`);
}
}
async handleCallback(callbackData) {
try {
const verification = await this.verifyCallback(callbackData);
if (!verification.status) {
throw new Error(verification.error.message);
}
// Status mapping
const statusMapping = {
'SUCCESS': 'success',
'PAID': 'success',
'PENDING': 'pending',
'FAILED': 'failed',
'EXPIRED': 'failed'
};
return {
status: statusMapping[callbackData.transaction.status] || 'unknown',
orderId: callbackData.order.invoice_number,
transactionId: callbackData.transaction.id,
amount: parseFloat(callbackData.order.amount),
currency: 'IDR',
paymentStatus: callbackData.transaction.status,
paymentChannel: callbackData.payment_channel
};
} catch (error) {
throw new Error(`Error in Doku callback handling: ${error.message}`);
}
}
async verifyCallback(data) {
try {
const receivedSignature = data.signature;
const timestamp = data.timestamp;
const callbackData = { ...data };
delete callbackData.signature;
delete callbackData.timestamp;
const expectedSignature = this.generateSignature(callbackData, timestamp);
if (receivedSignature !== expectedSignature) {
return {
status: false,
error: {
code: 401,
message: 'Invalid signature'
}
};
}
return {
status: true,
data: data
};
} catch (error) {
return {
status: false,
error: {
code: 500,
message: error.message
}
};
}
}
async checkPaymentStatus(invoiceNumber) {
try {
const timestamp = new Date().toISOString();
const requestData = { invoice_number: invoiceNumber };
const signature = this.generateSignature(requestData, timestamp);
const response = await this.client.get(`/v1/payment-code/${invoiceNumber}`, {
headers: {
'Request-Timestamp': timestamp,
'Signature': signature
}
});
return response.data;
} catch (error) {
throw new Error(`Error checking payment status: ${error.response?.data?.error?.message || error.message}`);
}
}
}
module.exports = DokuClient;