From 1fe385c9950da0fb5f4c0e6ac74eddad1671f9d0 Mon Sep 17 00:00:00 2001 From: Tommy Chan Date: Tue, 7 Jul 2026 00:05:09 -0400 Subject: [PATCH] feat: integrate stripe payment gateway --- PROVIDERS-DETAILS.md | 27 ++++++- examples/example-stripe.js | 149 +++++++++++++++++++++++++++++++++++++ lib/stripe.js | 118 +++++++++++++++++++++++++++++ package-lock.json | 22 +++++- package.json | 3 +- readme.md | 4 +- test.js | 6 ++ 7 files changed, 322 insertions(+), 7 deletions(-) create mode 100644 examples/example-stripe.js create mode 100644 lib/stripe.js diff --git a/PROVIDERS-DETAILS.md b/PROVIDERS-DETAILS.md index 210a1ce..b2df4c3 100644 --- a/PROVIDERS-DETAILS.md +++ b/PROVIDERS-DETAILS.md @@ -1014,6 +1014,27 @@ Bu dokümantasyon, QuickPos tarafından desteklenen tüm 48 ödeme sağlayıcıs --- +### 🌐 Stripe (Global) +- **Website**: https://stripe.com +- **Desteklenen Ülkeler**: 46+ ülke +- **Para Birimleri**: 135+ para birimi +- **Client Oluşturma**: + ```javascript + { + secretKey: 'YOUR_SECRET_KEY', + webhookSecret: 'YOUR_WEBHOOK_SECRET' // Opsiyonel, webhook doğrulaması için + } + ``` +- **createPayment Gereken Alanlar**: + - `amount`: Ödeme tutarı + - `currency`: Para birimi + - `orderId`: Benzersiz sipariş ID + - `callback_link`: Başarılı URL yönlendirmesi + - `email`: Müşteri email + - `name`: Ürün açıklaması + +--- + ### 🌐 Amazon Pay (Global) - **Website**: https://pay.amazon.com - **Desteklenen Ülkeler**: ABD, AB, Japonya, Hindistan @@ -1465,7 +1486,7 @@ Bu dokümantasyon, QuickPos tarafından desteklenen tüm 48 ödeme sağlayıcıs - **Orta Doğu & Afrika**: 17 provider - **Avrupa**: 5 provider - **Latin Amerika**: 2 provider -- **Global**: 13 provider +- **Global**: 14 provider - **Kripto Para**: 12 provider ### Para Birimi Çeşitliliği @@ -1473,7 +1494,7 @@ Bu dokümantasyon, QuickPos tarafından desteklenen tüm 48 ödeme sağlayıcıs - **En Çok Kullanılan**: USD, EUR, INR, IDR, TRY ### Toplam İstatistikler -- **Toplam Provider**: 48 +- **Toplam Provider**: 49 - **Desteklenen Ülke**: 200+ - **Kripto Para Desteği**: 2000+ coin - **Yerel Ödeme Yöntemi**: 500+ @@ -1541,4 +1562,4 @@ Bu dokümantasyon QuickPos v1.3.0 için hazırlanmıştır. **Son Güncelleme**: 2024 **Versiyon**: 1.3.0 -**Toplam Provider**: 48 +**Toplam Provider**: 49 diff --git a/examples/example-stripe.js b/examples/example-stripe.js new file mode 100644 index 0000000..1549910 --- /dev/null +++ b/examples/example-stripe.js @@ -0,0 +1,149 @@ +const Stripe = require('../lib/stripe'); +const QuickPos = require('../app'); +const express = require('express'); +const bodyParser = require('body-parser'); + +// QuickPos yapılandırması +const quickPos = new QuickPos({ + providers: { + stripe: { + secretKey: 'sk_test_YOUR_TEST_SECRET_KEY' + } + } +}); + +// Stripe sağlayıcısını al +let stripe = quickPos.providers['stripe']; + +// Express uygulaması oluştur +const app = express(); +// Webhook doğrulaması için raw body kaydetmek gerekebilir (Stripe özel durumu) +app.use(express.json({ + verify: (req, res, buf) => { + req.rawBody = buf; + } +})); +app.use(express.urlencoded({ extended: true })); +app.use(express.static('public')); + +// Ana sayfa - Basit form +app.get('/', (req, res) => { + res.send(` + + + + Stripe Ödeme + + + + + +
+

Stripe ile Ödeme Oluştur

+
+ + + + + + + + + + +
+
+ + + `); +}); + +// Ödeme sayfası - Stripe Checkout Session oluşturur +app.post('/checkout', async (req, res) => { + try { + const { amount, currency, description } = req.body; + + const payment = await stripe.createPayment({ + amount: amount || '99.99', + currency: currency || 'USD', + description: description || 'Premium Üyelik', + orderId: 'order-' + Date.now(), + successUrl: \`http://\${req.headers.host}/success\`, + cancelUrl: \`http://\${req.headers.host}/cancel\` + }); + + // Stripe ödeme sayfasına yönlendir + res.redirect(payment.paymentUrl); + } catch (error) { + res.status(500).send(` +
+

Hata

+

\${error.message}

+ Geri Dön +
+ `); + } +}); + +// İptal sayfası - basit HTML +app.get('/cancel', (req, res) => { + res.send(` +
+

Ödeme İptal Edildi

+

İşlem iptal edildi, herhangi bir ücret tahsil edilmedi.

+ Geri Dön +
+ `); +}); + +// Başarılı ödeme sayfası +app.get('/success', (req, res) => { + const { session_id, order_id } = req.query; + + res.send(` +
+

Ödeme Başarılı

+

Sipariş ID: \${order_id || 'N/A'}

+

Stripe Session ID: \${session_id || 'N/A'}

+

Ödeme durumu: Başarılı

+

İşlem tarihi: \${new Date().toLocaleString('tr-TR')}

+ Yeni Ödeme +
+ `); +}); + +// Stripe webhook handler +app.post('/stripe-webhook', async (req, res) => { + try { + // We pass req to handleCallback to allow signature verification + const result = await stripe.handleCallback(req.body, req); + + if (result.success && result.status === 'completed') { + console.log('Ödeme başarıyla tamamlandı:', result.orderId, result.amount, result.currency); + // Veritabanını güncelleyebilir veya email gönderebilirsiniz + } + + res.status(200).send('OK'); + } catch (error) { + console.error('Webhook işleme hatası:', error.message); + res.status(400).send(\`Webhook Error: \${error.message}\`); + } +}); + +// Sunucuyu başlat +const PORT = process.env.PORT || 3000; +app.listen(PORT, () => { + console.log(\`Sunucu çalışıyor: http://localhost:\${PORT}\`); + console.log(\`Örnek ödeme sayfası: http://localhost:\${PORT}\`); +}); diff --git a/lib/stripe.js b/lib/stripe.js new file mode 100644 index 0000000..b819417 --- /dev/null +++ b/lib/stripe.js @@ -0,0 +1,118 @@ +const StripeSDK = require('stripe'); + +class Stripe { + constructor(config) { + this.config = config || {}; + if (!config.secretKey) { + throw new Error('Missing required field: secretKey'); + } + + this.stripe = StripeSDK(config.secretKey); + this.endpointSecret = config.webhookSecret || null; + } + + /** + * Create a new payment (Checkout Session) + * @param {Object} paymentData - Payment data + * @returns {Promise} - Payment result containing payment URL + */ + async createPayment(paymentData) { + try { + if (!paymentData.amount || !paymentData.currency) { + throw new Error('Tutar (amount) ve para birimi (currency) gerekli'); + } + + const orderId = paymentData.orderId || `order-${Date.now()}`; + const successUrl = paymentData.successUrl || paymentData.callback_link || 'http://localhost:3000/success'; + const cancelUrl = paymentData.cancelUrl || 'http://localhost:3000/cancel'; + + // Ensure amount is properly formatted to cents for Stripe + const unitAmount = Math.round(parseFloat(paymentData.amount) * 100); + + const sessionConfig = { + payment_method_types: ['card'], + line_items: [ + { + price_data: { + currency: paymentData.currency.toLowerCase(), + product_data: { + name: paymentData.name || paymentData.description || 'QuickPos Payment', + }, + unit_amount: unitAmount, + }, + quantity: 1, + }, + ], + mode: 'payment', + success_url: `${successUrl}?session_id={CHECKOUT_SESSION_ID}&order_id=${orderId}`, + cancel_url: cancelUrl, + client_reference_id: orderId, + }; + + if (paymentData.email) { + sessionConfig.customer_email = paymentData.email; + } + + const session = await this.stripe.checkout.sessions.create(sessionConfig); + + return { + success: true, + paymentUrl: session.url, + transactionId: session.id, + orderId: orderId, + amount: paymentData.amount, + currency: paymentData.currency + }; + } catch (error) { + throw new Error(`Stripe payment creation failed: ${error.message}`); + } + } + + /** + * Handle callback/webhook from Stripe + * @param {Object} callbackData - Webhook data (req.body) + * @param {Object} req - Request object (used to verify signature if endpointSecret exists) + * @returns {Promise} - Callback result + */ + async handleCallback(callbackData, req) { + try { + let event = callbackData; + + // Verify webhook signature if endpointSecret is provided + if (this.endpointSecret && req && req.headers && req.headers['stripe-signature']) { + const sig = req.headers['stripe-signature']; + try { + // req.rawBody is needed for stripe webhook verification, assume it's available or we fallback to event + event = this.stripe.webhooks.constructEvent(req.rawBody || JSON.stringify(callbackData), sig, this.endpointSecret); + } catch (err) { + throw new Error(`Webhook Error: ${err.message}`); + } + } + + if (event.type === 'checkout.session.completed') { + const session = event.data.object; + + return { + success: true, + status: 'completed', + transactionId: session.payment_intent || session.id, + orderId: session.client_reference_id, + amount: session.amount_total / 100, + currency: (session.currency || '').toUpperCase(), + rawData: event + }; + } + + return { + success: false, + status: 'pending_or_other', + event: event.type, + rawData: event + }; + } catch (error) { + throw new Error(`Stripe callback handling failed: ${error.message}`); + } + } +} + +module.exports = Stripe; diff --git a/package-lock.json b/package-lock.json index f3ae4d9..b754ffa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "quickpos", - "version": "1.1.0", + "version": "1.3.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "quickpos", - "version": "1.1.0", + "version": "1.3.1", "license": "ISC", "dependencies": { "@tokenpayeng/tokenpay": "^0.0.14", @@ -27,6 +27,7 @@ "paymaya-integration": "^1.0.4", "paymentwall": "^2.1.1", "payu-websdk": "^1.2.0", + "stripe": "^22.3.0", "uuid": "^9.0.1" } }, @@ -1638,6 +1639,23 @@ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "license": "MIT" }, + "node_modules/stripe": { + "version": "22.3.0", + "resolved": "https://registry.npmjs.org/stripe/-/stripe-22.3.0.tgz", + "integrity": "sha512-ypO6xjVrMWs9SmIMeHr8naCx3dAQ0clxMdUTxn7Ejd7hmY9meBGfE+N4pVHkf9sUNebAHp6uJo6mV3GxDIc2cA==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, "node_modules/toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", diff --git a/package.json b/package.json index 15cedf6..232e408 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "quickpos", - "version": "1.0.924", + "version": "1.3.1", "main": "app.js", "scripts": { "test": "node test.js" @@ -133,6 +133,7 @@ "paymaya-integration": "^1.0.4", "paymentwall": "^2.1.1", "payu-websdk": "^1.2.0", + "stripe": "^22.3.0", "uuid": "^9.0.1" } } diff --git a/readme.md b/readme.md index c974eb8..9364a2f 100644 --- a/readme.md +++ b/readme.md @@ -84,6 +84,7 @@ | **EsnekPos** | 🇹🇷 Turkey | ✅ Active | | **Paydisini** | 🇹🇷 Turkey | ✅ Active | | **PayNetTR** | 🇹🇷 Turkey | ✅ Active | +| **Stripe** | 🌐 Global | ✅ Active | | **PayPal** | 🌐 Global | ✅ Active | | **Amazon Pay** | 🌐 Global | ✅ Active | | **Paddle** | 🌐 Global SaaS | ✅ Active | @@ -321,6 +322,7 @@ Middleware for handling payment callbacks. | Midtrans | ✅ | v1.2.9 | | Plisio | ✅ | v1.3.0 | | Tripay | ✅ | v1.3.0 | +| Stripe | ✅ | v1.3.1 | | And 30+ more... | ✅ | Ongoing | @@ -418,4 +420,4 @@ const odeme = await quickPos.paytr.createPayment({ }); ``` -Daha fazla detay için yukarıdaki İngilizce dokümantasyonu inceleyin. \ No newline at end of file +Daha fazla detay için yukarıdaki İngilizce dokümantasyonu inceleyin. diff --git a/test.js b/test.js index 323feac..5263ef6 100644 --- a/test.js +++ b/test.js @@ -148,6 +148,11 @@ const testConfig = { sandbox: true }, + // Stripe Test + stripe: { + secretKey: 'sk_test_YOUR_TEST_SECRET_KEY' + }, + // Cashfree Test cashfree: { appId: 'YOUR_APP_ID', @@ -410,6 +415,7 @@ function getTestDataForProvider(providerName) { '2checkout': { currency: 'USD', amount: '100', customerName: 'Test User' }, coingate: { currency: 'USD', amount: '10', receiveCurrency: 'EUR' }, shurjopay: { currency: 'BDT', amount: '1000', city: 'Dhaka' }, + stripe: { currency: 'USD', amount: '100' }, cashfree: { currency: 'INR', amount: '1000', customerId: 'customer_test' }, payspace: { currency: 'ZAR', amount: '100' }, payulatam: { currency: 'COP', amount: '10000', country: 'CO', paymentMethod: 'VISA' },