From aafa7f99ad63664055fce765e6d83ea1d13a4e95 Mon Sep 17 00:00:00 2001 From: Vasyl Date: Thu, 26 Dec 2024 10:26:14 +0200 Subject: [PATCH] - feat: add invoices API with pagination - feat: add payment API (create invoice and pay) --- routes/checkout-sesssion.js | 44 --------------- routes/stripe.js | 107 ++++++++++++++++++++++++++++++++++-- 2 files changed, 101 insertions(+), 50 deletions(-) delete mode 100644 routes/checkout-sesssion.js diff --git a/routes/checkout-sesssion.js b/routes/checkout-sesssion.js deleted file mode 100644 index 99c9503..0000000 --- a/routes/checkout-sesssion.js +++ /dev/null @@ -1,44 +0,0 @@ -const express = require('express'); -const Stripe = require('stripe'); -const router = express.Router(); - -const stripe = new Stripe('process.env.STRIPE_SECRET_KEY', { - apiVersion: '2020-08-27', -}); - -router.post('/create-session', async (req, res) => { - const { amount = 500, product = 'test', quantity = 1 } = req.body; - - // Validate that amount, product, and quantity are provided in the request - if (!amount || !product || !quantity) { - return res.status(400).json({ error: 'Amount, product, and quantity are required.' }); - } - - try { - const session = await stripe.checkout.sessions.create({ - payment_method_types: ['card'], - line_items: [ - { - price_data: { - currency: 'usd', - product_data: { - name: product, - }, - unit_amount: amount, // Amount should be in cents, e.g., $50 = 5000 - }, - quantity: quantity, - }, - ], - mode: 'payment', - success_url: `${req.headers.origin}/success?session_id={CHECKOUT_SESSION_ID}`, - cancel_url: `${req.headers.origin}/cancel`, - }); - - res.json({ id: session.id }); - } catch (error) { - console.error('Error creating checkout session:', error); - res.status(500).json({ error: 'Internal Server Error' }); - } -}); - -module.exports = router; diff --git a/routes/stripe.js b/routes/stripe.js index 0ee5478..cdbf675 100644 --- a/routes/stripe.js +++ b/routes/stripe.js @@ -1,14 +1,14 @@ const express = require('express'); const Stripe = require('stripe'); -const cors = require('cors'); // Import the CORS middleware +const cors = require('cors'); const router = express.Router(); const stripe = new Stripe(process.env.STRIPE_SECRET_KEY, { - apiVersion: '2020-08-27', + apiVersion: '2022-11-15' }); -const HARD_CODED_CUSTOMER_ID = 'cus_RPmbpSyKY0cF1N'; // Hard-coded customer ID for testing purposes +const HARD_CODED_CUSTOMER_ID = 'cus_RPmbpSyKY0cF1N'; // Enable CORS for all routes in this router router.use(cors()); // Allow cross-origin requests for all routes in this router @@ -38,7 +38,7 @@ router.get('/payment-method', async (req, res) => { // Route to remove a payment method router.delete('/payment-method/:id', async (req, res) => { - const { id } = req.params; + const {id} = req.params; try { await stripe.paymentMethods.detach(id); @@ -46,7 +46,7 @@ router.delete('/payment-method/:id', async (req, res) => { res.status(200).json({ status: 'success', message: 'Payment method removed successfully.', - data: { id }, + data: {id}, }); } catch (error) { console.error('Error removing payment method:', error.message); @@ -60,7 +60,7 @@ router.delete('/payment-method/:id', async (req, res) => { // Route to attach a payment method to a customer router.put('/payment-method', async (req, res) => { - const { paymentMethodId } = req.body; + const {paymentMethodId} = req.body; if (!paymentMethodId) { return res.status(400).json({ @@ -90,4 +90,99 @@ router.put('/payment-method', async (req, res) => { } }); + +router.post('/payment-intent', async (req, res) => { + try { + const {amount, currency, paymentMethod} = req.body + const paymentIntent = await stripe.paymentIntents.create({ + amount, + currency, + customer: HARD_CODED_CUSTOMER_ID, + payment_method: paymentMethod, + confirm: true, + }) + res.json(paymentIntent) + } catch (error) { + res.status(500).json({error: error.message}) + } +}) + +router.post('/payment', async (req, res) => { + const { + customerId = HARD_CODED_CUSTOMER_ID, + paymentMethod, + amount, + currency = 'usd', + description = 'Invoice for custom payment' + } = req.body; + + try { + // 1. Create and finalize the invoice + const invoice = await stripe.invoices.create({ + customer: customerId, + auto_advance: false, // Automatically finalize and charge this invoice + }); + + // 2. Create an invoice item + await stripe.invoiceItems.create({ + customer: customerId, + amount, + currency, + description, + invoice: invoice.id, + }); + + // 3. Pay the invoice using the specified payment method + const paidInvoice = await stripe.invoices.pay(invoice.id, { + payment_method: paymentMethod, + }); + + + // Respond with the invoice details + res.status(200).json({ + status: 'success', + message: 'Payment and invoice processed successfully.', + data: {invoice: paidInvoice}, + }); + } catch (error) { + console.error('Error processing payment and invoice:', error.message); + res.status(500).json({ + status: 'error', + message: 'Failed to process payment and invoice.', + data: null, + }); + } +}); + + +// Route to fetch invoices with pagination (10 per page) +router.get('/invoices', async (req, res) => { + try { + const {starting_after} = req.query; // Get the starting_after parameter from query params + + // Use Stripe's invoices.list API to retrieve the invoices + const invoices = await stripe.invoices.list({ + customer: HARD_CODED_CUSTOMER_ID, + limit: 10, // Get only 10 invoices per request + starting_after: starting_after || undefined, // If starting_after is provided, use it + }); + + res.status(200).json({ + status: 'success', + message: 'Invoices retrieved successfully.', + data: { + invoices: invoices.data, + has_more: invoices.has_more, // Check if there are more invoices to paginate + }, + }); + } catch (error) { + console.error('Error fetching invoices:', error.message); + res.status(500).json({ + status: 'error', + message: 'Failed to fetch invoices.', + data: null, + }); + } +}); + module.exports = router;