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>
193 lines (166 loc) • 7.3 kB
JavaScript
const crypto = require('crypto');
const axios = require('axios');
class Paynkolay {
/**
* @param {Object} config
* @param {string} config.sx - SX parametresi (Panelden alınır)
* @param {string} config.merchantSecretKey - Gizli Anahtar
* @param {string} config.merchantNo - Mağaza Numarası (Callback doğrulama için)
* @param {boolean} [config.isTest=true] - Test ortamı mı?
*/
constructor(config) {
this.sx = config.sx;
this.merchantSecretKey = config.merchantSecretKey;
this.merchantNo = config.merchantNo;
// Postman'deki host bilgisine göre
this.baseUrl = config.isTest
? 'https://paynkolaytest.nkolayislem.com.tr'
: 'https://paynkolay.nkolayislem.com.tr';
}
/**
* Link ile Ödeme Oluşturma (/Vpos/by-link-create)
* Postman'deki 'Link Oluştur' script'ine göre hazırlanmıştır.
*/
async createPaymentLink(params) {
try {
const endpoint = '/Vpos/by-link-create';
// Zorunlu alanlar için varsayılan değerler ve kontroller
const rnd = params.rnd || Date.now().toString();
const clientRefCode = params.clientRefCode || ('REF-' + rnd);
const customerKey = params.customerKey || ""; // Kart saklama yoksa boş
const amount = parseFloat(params.amount).toFixed(2); // 1000.00 formatı
// 1. HASH OLUŞTURMA (Postman Script'ine göre)
// Sıralama: sx|clientRefCode|amount|successUrl|failUrl|rnd|customerKey|merchantSecretKey
const hashString = [
this.sx,
clientRefCode,
amount,
params.successUrl,
params.failUrl,
rnd,
customerKey,
this.merchantSecretKey
].join("|");
const hashDatav2 = this.generateHash(hashString);
// 2. FORM DATA HAZIRLAMA
const data = new URLSearchParams();
data.append('sx', this.sx);
data.append('clientRefCode', clientRefCode);
data.append('amount', amount);
data.append('successUrl', params.successUrl);
data.append('failUrl', params.failUrl);
data.append('rnd', rnd);
data.append('hashDatav2', hashDatav2);
data.append('use3D', params.use3D || 'true');
data.append('currencyCode', params.currencyCode || '949'); // 949 = TL
data.append('transactionType', 'SALES');
// Opsiyonel (Link Detayları)
if (params.instalments) data.append('instalments', params.instalments);
if (params.second) data.append('second', params.second); // Link süresi (saniye)
if (params.cardHolderIP) data.append('cardHolderIP', params.cardHolderIP);
// Link Sayfası Görünen Bilgiler
data.append('detail', 'true');
data.append('inputNamesurname', params.inputNamesurname || '');
data.append('inputDescription', params.inputDescription || '');
data.append('inputEmail', params.inputEmail || '');
data.append('inputAddress', params.inputAddress || '');
data.append('inputTckn', params.inputTckn || '');
data.append('inputPhone', params.inputPhone || '');
// 3. İSTEK GÖNDERME
const response = await axios.post(`${this.baseUrl}${endpoint}`, data, {
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
});
return response.data;
} catch (error) {
throw new Error(`Paynkolay Link Oluşturma Hatası: ${error.message}`);
}
}
/**
* Link Silme (/Vpos/by-link-url-remove)
*/
async removePaymentLink(qParam) {
try {
const endpoint = '/Vpos/by-link-url-remove';
// HASH OLUŞTURMA (Link Sil Script'ine göre)
// Sıralama: sx|q|merchantSecretKey
const hashString = [
this.sx,
qParam, // Bu değer genelde referans kodudur
this.merchantSecretKey
].join("|");
const hashDatav2 = this.generateHash(hashString);
const data = new URLSearchParams();
data.append('sx', this.sx);
data.append('q', qParam);
data.append('hashDatav2', hashDatav2);
const response = await axios.post(`${this.baseUrl}${endpoint}`, data, {
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
});
return response.data;
} catch (error) {
throw new Error(`Paynkolay Link Silme Hatası: ${error.message}`);
}
}
/**
* Callback (Bildirim) Doğrulama
* Ödeme sonrası Paynkolay'ın successUrl'e gönderdiği veriyi doğrular.
*/
handleCallback(params) {
const {
RESPONSE_CODE,
MERCHANT_NO,
REFERENCE_CODE,
AUTH_CODE,
USE_3D,
RND,
INSTALLMENT,
AUTHORIZATION_AMOUNT,
CURRENCY_CODE,
hashDataV2
} = params;
// 1. İşlem Başarılı mı kontrolü
if (RESPONSE_CODE !== "2" && RESPONSE_CODE !== 2) {
return { status: false, message: 'İşlem başarısız (RESPONSE_CODE != 2)' };
}
// 2. Hash Hesaplama (Dokümantasyondaki 'Response Hash Testi' sırası)
// MERCHANT_NO | REFERENCE_CODE | AUTH_CODE | RESPONSE_CODE | USE_3D | RND | INSTALLMENT | AUTHORIZATION_AMOUNT | CURRENCY_CODE | MERCHANT_SECRET_KEY
const hashString = [
MERCHANT_NO,
REFERENCE_CODE,
AUTH_CODE,
RESPONSE_CODE,
USE_3D, // Gelen değer string "true" veya "false" olabilir, olduğu gibi kullanılmalı
RND,
INSTALLMENT,
AUTHORIZATION_AMOUNT,
CURRENCY_CODE,
this.merchantSecretKey
].join("|");
const calculatedHash = this.generateHash(hashString);
if (calculatedHash === hashDataV2) {
return {
status: 'success',
message: 'Doğrulama Başarılı',
amount: AUTHORIZATION_AMOUNT,
orderId: params.CLIENT_REFERENCE_CODE || REFERENCE_CODE
};
} else {
console.error("Hash String (Local):", hashString);
console.error("Calculated:", calculatedHash);
console.error("Received:", hashDataV2);
return { status: false, message: 'Hash uyuşmazlığı (Güvenlik Hatası)' };
}
}
/**
* SHA-512 Base64 Helper
*/
generateHash(str) {
// UTF-8 encoding önemlidir (CryptoJS.SHA512 davranışı)
return crypto.createHash('sha512').update(str, 'utf8').digest('base64');
}
}
module.exports = Paynkolay;