UNPKG

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>

542 lines (497 loc) 18.1 kB
const braintree = require('braintree'); class PayPal { constructor(config) { this.config = config || {}; const requiredFields = ['merchantId', 'publicKey', 'privateKey']; for (let field of requiredFields) { if (!config[field]) throw new Error(`Missing required field: ${field}`); } const environment = config.environment === 'production' ? braintree.Environment.Production : braintree.Environment.Sandbox; this.gateway = new braintree.BraintreeGateway({ environment: environment, merchantId: config.merchantId, publicKey: config.publicKey, privateKey: config.privateKey }); } async generateClientToken(options = {}) { try { const response = await this.gateway.clientToken.generate(options); return response.clientToken; } catch (error) { throw new Error(`Client token oluşturma hatası: ${error.message}`); } } async processPayment(saleData) { try { if (!saleData.amount || !saleData.paymentMethodNonce) { throw new Error('Tutar ve payment method nonce gerekli'); } const saleRequest = { amount: saleData.amount, paymentMethodNonce: saleData.paymentMethodNonce, orderId: saleData.orderId, customer: saleData.customer, options: { submitForSettlement: true, ...saleData.options } }; const result = await this.gateway.transaction.sale(saleRequest); if (result.success) { return { success: true, transactionId: result.transaction.id, status: result.transaction.status, amount: result.transaction.amount, currency: result.transaction.currencyIsoCode, orderId: result.transaction.orderId, paymentType: 'paypal', createdAt: result.transaction.createdAt }; } else { throw new Error(result.message || 'İşlem başarısız'); } } catch (error) { throw new Error(`Ödeme işlemi hatası: ${error.message}`); } } async getTransaction(transactionId) { try { const result = await this.gateway.transaction.find(transactionId); return result; } catch (error) { throw new Error(`İşlem bulunamadı: ${error.message}`); } } async searchTransactions(searchParams = {}) { try { const stream = await this.gateway.transaction.search((search) => { if (searchParams.orderId) { search.orderId().is(searchParams.orderId); } if (searchParams.status) { search.status().is(searchParams.status); } if (searchParams.customerId) { search.customerId().is(searchParams.customerId); } if (searchParams.startDate && searchParams.endDate) { search.createdAt().between(searchParams.startDate, searchParams.endDate); } }); return new Promise((resolve, reject) => { const transactions = []; stream.on('data', (transaction) => { transactions.push(transaction); }); stream.on('end', () => { resolve(transactions); }); stream.on('error', (err) => { reject(new Error(`İşlem arama hatası: ${err.message}`)); }); }); } catch (error) { throw new Error(`İşlem arama hatası: ${error.message}`); } } async refundTransaction(transactionId, amount = null) { try { let result; if (amount) { result = await this.gateway.transaction.refund(transactionId, amount); } else { result = await this.gateway.transaction.refund(transactionId); } if (result.success) { return { success: true, refundId: result.transaction.id, originalTransactionId: transactionId, amount: result.transaction.amount, status: result.transaction.status }; } else { throw new Error(result.message || 'İade işlemi başarısız'); } } catch (error) { throw new Error(`İade işlemi hatası: ${error.message}`); } } async handleWebhook(signature, payload) { try { const webhookNotification = await this.gateway.webhookNotification.parse(signature, payload); const kind = webhookNotification.kind; const timestamp = webhookNotification.timestamp; switch (kind) { case 'subscription_went_active': case 'subscription_charged_successfully': return { event: 'subscription_payment', status: 'success', subscriptionId: webhookNotification.subscription.id, transactionId: webhookNotification.subscription.transactions[0]?.id, planId: webhookNotification.subscription.planId, amount: webhookNotification.subscription.transactions[0]?.amount, timestamp: timestamp }; case 'subscription_charged_unsuccessfully': return { event: 'subscription_payment', status: 'failed', subscriptionId: webhookNotification.subscription.id, planId: webhookNotification.subscription.planId, timestamp: timestamp }; case 'subscription_canceled': return { event: 'subscription_canceled', subscriptionId: webhookNotification.subscription.id, planId: webhookNotification.subscription.planId, timestamp: timestamp }; case 'transaction_settled': return { event: 'payment_settled', status: 'success', transactionId: webhookNotification.transaction.id, amount: webhookNotification.transaction.amount, orderId: webhookNotification.transaction.orderId, timestamp: timestamp }; case 'transaction_settlement_declined': return { event: 'payment_settlement_declined', status: 'failed', transactionId: webhookNotification.transaction.id, amount: webhookNotification.transaction.amount, orderId: webhookNotification.transaction.orderId, timestamp: timestamp }; default: return { event: kind, raw: webhookNotification }; } } catch (error) { throw new Error(`Webhook işleme hatası: ${error.message}`); } } async testWebhook(kind, id) { try { const result = await this.gateway.webhookTesting.sampleNotification(kind, id); return { btSignature: result.bt_signature, btPayload: result.bt_payload }; } catch (error) { throw new Error(`Test webhook oluşturma hatası: ${error.message}`); } } async createPaymentLink(linkData) { try { if (!linkData.amount || !linkData.currency) { throw new Error('Tutar ve para birimi gerekli'); } const orderId = linkData.orderId || `order-${Date.now()}`; const returnUrl = linkData.successUrl || 'http://localhost:3000/success'; const cancelUrl = linkData.cancelUrl || 'http://localhost:3000/cancel'; const clientToken = await this.generateClientToken(); return { success: true, clientToken: clientToken, amount: linkData.amount, currency: linkData.currency, orderId: orderId, description: linkData.description || `Payment: ${linkData.amount} ${linkData.currency}`, successUrl: returnUrl, cancelUrl: cancelUrl, paypalCheckoutDetails: { clientToken: clientToken, amount: linkData.amount, currency: linkData.currency, orderId: orderId, description: linkData.description || `Payment: ${linkData.amount} ${linkData.currency}`, successUrl: returnUrl, cancelUrl: cancelUrl } }; } catch (error) { throw new Error(`Ödeme bağlantısı oluşturma hatası: ${error.message}`); } } async createCheckoutOptions(checkoutData) { try { if (!checkoutData.amount) { throw new Error('Tutar bilgisi gerekli'); } const clientToken = await this.generateClientToken(); const orderId = checkoutData.orderId || `order-${Date.now()}`; const setupCode = Buffer.from(JSON.stringify({ amount: checkoutData.amount, currency: checkoutData.currency || 'USD', orderId: orderId, intent: 'capture', successUrl: checkoutData.successUrl || '', cancelUrl: checkoutData.cancelUrl || '' })).toString('base64'); return { success: true, clientToken: clientToken, setupCode: setupCode, orderId: orderId, amount: checkoutData.amount, currency: checkoutData.currency || 'USD', successUrl: checkoutData.successUrl, cancelUrl: checkoutData.cancelUrl, paymentUrl: `https://your-app-domain.com/paypal-checkout?setup=${setupCode}` }; } catch (error) { throw new Error(`Checkout seçenekleri oluşturma hatası: ${error.message}`); } } async completePayment(paymentNonce, setupCode) { try { const setupData = JSON.parse(Buffer.from(setupCode, 'base64').toString()); const paymentResult = await this.processPayment({ amount: setupData.amount, paymentMethodNonce: paymentNonce, orderId: setupData.orderId, options: { submitForSettlement: true } }); return { success: true, transactionId: paymentResult.transactionId, status: paymentResult.status, amount: paymentResult.amount, orderId: paymentResult.orderId, redirectUrl: setupData.successUrl || null }; } catch (error) { throw new Error(`Ödeme tamamlama hatası: ${error.message}`); } } async renderHtmlCheckout(data) { try { if (!data.amount) { throw new Error('Amount is required'); } const amount = data.amount; const currency = data.currency || 'USD'; const orderId = data.orderId || `order-${Date.now()}`; const description = data.description || `Payment: ${amount} ${currency}`; const successUrl = data.successUrl || 'http://localhost:3000/success'; const cancelUrl = data.cancelUrl || 'http://localhost:3000/cancel'; const clientToken = await this.generateClientToken(); return ` <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Secure Payment | QuickPos</title> <script src="https://www.paypal.com/sdk/js?client-id=test&currency=${currency}"></script> <style> :root { --primary-color: #0070ba; --secondary-color: #003087; --accent-color: #009cde; --background-color: #f7f9fa; --success-color: #26c281; --text-color: #2c3e50; --border-color: #e5e8ec; } * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif; background: var(--background-color); color: var(--text-color); line-height: 1.6; padding: 0; margin: 0; } .header { background: #fff; box-shadow: 0 1px 3px rgba(0,0,0,0.1); padding: 15px 0; position: relative; } .header-content { display: flex; justify-content: space-between; align-items: center; max-width: 1200px; margin: 0 auto; padding: 0 20px; } .logo { font-weight: 700; font-size: 1.5rem; color: var(--primary-color); } .secure-badge { display: flex; align-items: center; font-size: 14px; color: #718096; } .secure-badge svg { margin-right: 6px; fill: #718096; } .container { max-width: 500px; margin: 30px auto; background: white; border-radius: 8px; box-shadow: 0 4px 6px rgba(0,0,0,0.05); overflow: hidden; } .payment-header { padding: 20px 25px; border-bottom: 1px solid var(--border-color); } .payment-title { font-size: 18px; font-weight: 600; color: var(--text-color); } .payment-details { padding: 20px 25px; border-bottom: 1px solid var(--border-color); } .detail-row { display: flex; justify-content: space-between; padding: 10px 0; border-bottom: 1px solid var(--border-color); } .detail-row:last-child { border-bottom: none; } .detail-label { color: #718096; font-size: 14px; } .detail-value { font-weight: 600; font-size: 14px; } .payment-methods { padding: 20px 25px; } .method-title { font-size: 16px; font-weight: 600; margin-bottom: 15px; color: var(--text-color); } #paypal-button-container { margin: 15px 0; } .cancel-btn { display: block; width: 100%; padding: 10px; text-align: center; background: #f1f5f9; color: #475569; border: none; border-radius: 4px; cursor: pointer; font-weight: 500; text-decoration: none; margin-top: 15px; transition: background 0.2s ease; } .cancel-btn:hover { background: #e2e8f0; } .footer { text-align: center; padding: 20px; color: #94a3b8; font-size: 12px; background: #fff; border-top: 1px solid var(--border-color); margin-top: 30px; } .footer a { color: var(--primary-color); text-decoration: none; } .footer a:hover { text-decoration: underline; } </style> </head> <body> <div class="container"> <div class="payment-header"> <h1 class="payment-title">${description}</h1> </div> <div class="payment-details"> <div class="detail-row"> <span class="detail-label">Amount</span> <span class="detail-value">${currency} ${amount}</span> </div> <div class="detail-row"> <span class="detail-label">Order ID</span> <span class="detail-value">${orderId}</span> </div> </div> <div class="payment-methods"> <h2 class="method-title">Select Payment Method</h2> <div id="paypal-button-container"></div> <a href="/" class="cancel-btn">Cancel Payment</a> </div> </div> <script> paypal.Buttons({ style: { layout: 'vertical', color: 'blue', shape: 'rect', label: 'pay' }, createOrder: function(data, actions) { return actions.order.create({ purchase_units: [{ amount: { value: "${amount}", currency_code: "${currency}" }, description: "${description}", invoice_id: "${orderId}" }] }); }, onApprove: function(data, actions) { return actions.order.capture().then(function(details) { window.location.href = "${successUrl}?order_id=${orderId}&paymentId=" + data.orderID + "&PayerID=" + details.payer.payer_id; }); }, onCancel: function(data) { window.location.href = "${cancelUrl}"; }, onError: function(err) { console.error('PayPal error:', err); alert('Payment error occurred. Please try again.'); } }).render('#paypal-button-container'); </script> </body> </html> `; } catch (error) { throw new Error(`Checkout rendering error: ${error.message}`); } } } module.exports = PayPal;