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>
188 lines (164 loc) • 7.2 kB
JavaScript
const crypto = require('crypto');
class Shopier {
/**
* @param {Object} config
* @param {string} config.apiKey - Shopier API Key
* @param {string} config.apiSecret - Shopier API Secret
*/
constructor(config) {
this.config = config || {};
const requiredFields = ['apiKey', 'apiSecret', 'website'];
for (let field of requiredFields) {
if (!config[field]) throw new Error(`Shopier: Missing required config field: ${field}`);
}
this.apiKey = config.apiKey;
this.websiteIndex = Number(config.website);
this.apiSecret = config.apiSecret;
this.paymentUrl = 'https://www.shopier.com/ShowProduct/api_pay4.php';
this.moduleVersion = '1.0.4';
}
/**
* Ödeme Formunu Oluşturur
* @param {Object} paymentDetails
* @param {number} paymentDetails.amount - Ödenecek tutar (Örn: 15.50)
* @param {string} paymentDetails.currency - Para birimi (TRY, USD, EUR) varsayılan: TRY
* @param {string} paymentDetails.orderId - Sipariş numarası (Benzersiz olmalı)
* @param {Object} paymentDetails.buyer - Alıcı bilgileri { first_name, last_name, email, phone, id_nr }
* @param {Object} paymentDetails.billingAddress - Fatura adresi { address, city, country, postcode }
* @param {Object} paymentDetails.shippingAddress - Teslimat adresi { address, city, country, postcode }
*/
createPayment(paymentDetails) {
try {
// 1. Temel Doğrulamalar
if (!paymentDetails.amount) throw new Error('Shopier: Amount is required');
if (!paymentDetails.buyer) throw new Error('Shopier: Buyer information is required');
if (!paymentDetails.billingAddress) throw new Error('Shopier: Billing address is required');
if (!paymentDetails.shippingAddress) throw new Error('Shopier: Shipping address is required');
const currency = paymentDetails.currency || 'TRY';
const currentLang = 0; // 0: TR, 1: EN (Otomatik TR ayarlandı, isteğe göre parametreye bağlanabilir)
const randomNr = Math.floor(Math.random() * (999999 - 100000 + 1)) + 100000;
// Sipariş ID yoksa rastgele oluştur
const platformOrderId = paymentDetails.orderId || `Ord-${randomNr}`;
// 2. Shopier İçin Argümanları Hazırla
const args = {
API_key: this.apiKey,
website_index: this.websiteIndex || 0,
platform_order_id: platformOrderId,
product_name: paymentDetails.product_name || 'Balance Payment',
product_type: 0, // 0: Reel nesne, 1: Sanal
buyer_name: paymentDetails.buyer.first_name,
buyer_surname: paymentDetails.buyer.last_name,
buyer_email: paymentDetails.buyer.email,
buyer_account_age: 0,
buyer_id_nr: paymentDetails.buyer.id_nr || '0', // Opsiyonel ID
buyer_phone: paymentDetails.buyer.phone,
// Fatura
billing_address: paymentDetails.billingAddress.address,
billing_city: paymentDetails.billingAddress.city,
billing_country: paymentDetails.billingAddress.country,
billing_postcode: paymentDetails.billingAddress.postcode,
// Teslimat
shipping_address: paymentDetails.shippingAddress.address,
shipping_city: paymentDetails.shippingAddress.city,
shipping_country: paymentDetails.shippingAddress.country,
shipping_postcode: paymentDetails.shippingAddress.postcode,
total_order_value: paymentDetails.amount,
currency: this._mapCurrency(currency),
platform: 0,
is_in_frame: 0,
current_language: currentLang,
modul_version: this.moduleVersion,
random_nr: randomNr
};
// 3. Eksik Alan Kontrolü
Object.keys(args).forEach(key => {
if (args[key] === undefined || args[key] === null) {
// Opsiyonel alanlar için esneklik sağlanabilir ama kritik alanlar için hata fırlatıyoruz
// throw new Error(`Shopier: Parameter ${key} is missing`);
}
});
// 4. İmza Oluşturma (Native Crypto)
const dataToSign = args.random_nr + args.platform_order_id + args.total_order_value + args.currency;
const signature = crypto.createHmac('sha256', this.apiSecret)
.update(dataToSign)
.digest('base64');
args.signature = signature;
// 5. HTML Formunu Oluştur
const formHtml = this._generateHtml(args);
return {
status: 'success',
data: {
orderId: platformOrderId,
amount: paymentDetails.amount,
html: formHtml // Frontend'de bu HTML'i ekrana basıp formu submit edeceksin
}
};
} catch (error) {
throw new Error(`Error in Shopier payment creation: ${error.message}`);
}
}
/**
* Callback (Geri Dönüş) İşleme
* @param {Object} body - POST request body
*/
handleCallback(body) {
try {
const data = body.random_nr + body.platform_order_id;
// Gelen imza base64, bunu normal stringe çevirip karşılaştırmalıyız ya da direkt ürettiğimizi karşılaştırmalıyız.
// Shopier dökümantasyonuna göre: HmacSHA256(random_nr + platform_order_id, secret) == signature (base64)
const expectedSignature = crypto.createHmac('sha256', this.apiSecret)
.update(data)
.digest('base64');
if (body.signature === expectedSignature) {
if (body.status === 'success') {
return {
success: true,
order_id: body.platform_order_id,
payment_id: body.payment_id,
installment: body.installment,
random_nr: body.random_nr
};
} else {
return { success: false, message: "Shopier: Payment status is not success." };
}
} else {
throw new Error('Shopier: Signature mismatch. Invalid callback.');
}
} catch (error) {
throw new Error(`Error in Shopier callback handling: ${error.message}`);
}
}
// --- Private Helpers ---
_generateHtml(args) {
let inputs = '';
Object.keys(args).forEach(key => {
inputs += `<input type="hidden" name="${key}" value="${args[key]}">`;
});
return `<!doctype html>
<html lang="tr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<title>Ödeme Yapılıyor...</title>
</head>
<body>
<form id="shopier_payment_form" method="post" action="${this.paymentUrl}">
${inputs}
</form>
<script type="text/javascript">
document.getElementById("shopier_payment_form").submit();
</script>
</body>
</html>`;
}
_mapCurrency(currency) {
switch(currency) {
case 'TL':
case 'TRY': return 0;
case 'USD': return 1;
case 'EUR': return 2;
default: return 0;
}
}
}
module.exports = Shopier;