Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add base stripe API #3

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 17 additions & 2 deletions app.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,27 @@
const express = require('express');
const bodyParser = require('body-parser');
const router = require('./routes/index');
const PORT = process.env.PORT || 3000;
const cron = require('node-cron');
const cleanUpPaymentMethods = require('./cronjobs/stripeCardsCleanup');
const { HARD_CODED_CUSTOMER_ID } = require('./constants');

const PORT = process.env.PORT || 3000;
const app = express();

app.use(bodyParser.json());

// Your existing routes
const router = require('./routes/index');
app.use(router);

//Schedule the task to run every hour
cron.schedule('0 * * * *', () => {
console.log('Running scheduled cleanup task...');
cleanUpPaymentMethods(HARD_CODED_CUSTOMER_ID);
}, {
scheduled: true, // Ensures the task starts automatically
timezone: "UTC", // Set timezone if needed
});

app.listen(PORT, () => {
console.log(`App is running on http://localhost:${PORT}`);
});
1 change: 1 addition & 0 deletions constants.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
exports.HARD_CODED_CUSTOMER_ID = 'cus_RPmbpSyKY0cF1N';
33 changes: 33 additions & 0 deletions cronjobs/stripeCardsCleanup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
const Stripe = require("stripe");


async function cleanUpPaymentMethods(customerId) {
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY, {
apiVersion: '2022-11-15'
});
const cutoffDate = new Date('2025-01-01T00:00:00Z'); // Replace with your desired cutoff date
const cutoffTimestamp = Math.floor(cutoffDate.getTime() / 1000); // Convert to Unix timestamp

try {
// Retrieve all card payment methods for the customer
const paymentMethods = await stripe.paymentMethods.list({
customer: customerId,
type: 'card',
limit: 100,
});

// Detach payment methods created after the cutoff date
for (const method of paymentMethods.data) {
if (method.created > cutoffTimestamp) {
await stripe.paymentMethods.detach(method.id);
console.log(`Detached payment method: ${method.id}`);
}
}

console.log('Cleanup complete.');
} catch (error) {
console.error('Error during payment method cleanup:', error);
}
}

module.exports = cleanUpPaymentMethods;
54 changes: 54 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,11 @@
"dependencies": {
"@supabase/supabase-js": "^2.45.4",
"body-parser": "^1.20.3",
"cors": "^2.8.5",
"dotenv": "^16.4.5",
"express": "^4.19.2",
"node-cron": "^3.0.3",
"stripe": "^17.3.1",
"uuid": "^10.0.0"
},
"engines": {
Expand Down
3 changes: 3 additions & 0 deletions routes/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@ const express = require('express');
const router = express.Router();
const userRoutes = require('./users');
const rootRoutes = require('./root');
const stripeApi = require('./stripe');

router.use('/users', userRoutes);
router.use('/', rootRoutes);
router.use('/api', stripeApi);


module.exports = router;
188 changes: 188 additions & 0 deletions routes/stripe.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
const express = require('express');
const Stripe = require('stripe');
const cors = require('cors');

const { HARD_CODED_CUSTOMER_ID } = require('../constants');

const router = express.Router();

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY, {
apiVersion: '2022-11-15'
});

// Enable CORS for all routes in this router
router.use(cors()); // Allow cross-origin requests for all routes in this router

// Route to retrieve all saved payment methods for a customer
router.get('/payment-method', async (req, res) => {
try {
const paymentMethods = await stripe.paymentMethods.list({
customer: HARD_CODED_CUSTOMER_ID,
type: 'card',
});

res.status(200).json({
status: 'success',
message: 'Payment methods retrieved successfully.',
data: paymentMethods.data,
});
} catch (error) {
console.error('Error fetching payment methods:', error.message);
res.status(500).json({
status: 'error',
message: 'Failed to fetch payment methods.',
data: null,
});
}
});

// Route to remove a payment method
router.delete('/payment-method/:id', async (req, res) => {
const {id} = req.params;

try {
await stripe.paymentMethods.detach(id);

res.status(200).json({
status: 'success',
message: 'Payment method removed successfully.',
data: {id},
});
} catch (error) {
console.error('Error removing payment method:', error.message);
res.status(500).json({
status: 'error',
message: 'Failed to remove payment method.',
data: null,
});
}
});

// Route to attach a payment method to a customer
router.put('/payment-method', async (req, res) => {
const {paymentMethodId} = req.body;

if (!paymentMethodId) {
return res.status(400).json({
status: 'error',
message: 'Payment Method ID is required.',
data: null,
});
}

try {
const paymentMethod = await stripe.paymentMethods.attach(paymentMethodId, {
customer: HARD_CODED_CUSTOMER_ID,
});

res.status(200).json({
status: 'success',
message: 'Payment method added successfully.',
data: paymentMethod,
});
} catch (error) {
console.error('Error adding payment method:', error.message);
res.status(500).json({
status: 'error',
message: error.message,
data: null,
});
}
});


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: error.message,
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: error.message,
data: null,
});
}
});

module.exports = router;