Learn how to create and apply discounts, coupon codes, and promotion codes in Stripe Checkout, including code examples and best practices for dynamic discounts.
Book a call with an Expert
Starting a new venture? Need to upgrade your web app? RapidDev builds application with your growth in mind.
How to Apply Discounts in Stripe Checkout
Step 1: Set Up Your Stripe Account
Before implementing discounts in Stripe Checkout, ensure you have:
For Node.js, install the Stripe library:
npm install stripe
Step 2: Create Coupon Codes in Stripe Dashboard
You can create coupon codes through the Stripe Dashboard:
Alternatively, create coupons programmatically:
const stripe = require('stripe')('sk_test_your_secret_key');
async function createCoupon() {
const coupon = await stripe.coupons.create({
percent\_off: 20,
duration: 'once',
id: 'SUMMER20', // Optional custom coupon code
max\_redemptions: 100, // Optional limit on number of redemptions
redeem\_by: Math.floor(Date.now() / 1000) + (30 _ 24 _ 60 \* 60), // Optional expiration (30 days from now)
});
console.log('Coupon created:', coupon);
return coupon;
}
createCoupon();
Step 3: Create a Promotion Code (Optional)
Promotion codes allow you to create multiple codes for the same coupon:
async function createPromotionCode() {
const promotionCode = await stripe.promotionCodes.create({
coupon: 'SUMMER20',
code: 'SUMMER20PROMO', // Custom promotion code
max\_redemptions: 50, // Optional
expires\_at: Math.floor(Date.now() / 1000) + (30 _ 24 _ 60 \* 60), // Optional
});
console.log('Promotion code created:', promotionCode);
return promotionCode;
}
createPromotionCode();
Step 4: Apply Discounts to Checkout Session
To apply a discount to a Checkout session, use the discounts
parameter when creating a session:
const stripe = require('stripe')('sk_test_your_secret_key');
async function createCheckoutSession() {
const session = await stripe.checkout.sessions.create({
payment_method_types: ['card'],
line\_items: [
{
price: 'price\_1234567890', // Your price ID
quantity: 1,
},
],
mode: 'payment',
success_url: 'https://yourwebsite.com/success?session_id={CHECKOUT_SESSION_ID}',
cancel\_url: 'https://yourwebsite.com/cancel',
discounts: [
{
coupon: 'SUMMER20', // ID of the coupon to apply
},
],
});
return session;
}
If you're using promotion codes instead of coupons:
discounts: [
{
promotion_code: 'promo_1234567890', // ID of the promotion code
},
],
Step 5: Let Customers Add Their Own Coupon Codes
Enable a coupon input field in your Checkout session by setting allow_promotion_codes
to true
:
const session = await stripe.checkout.sessions.create({
payment_method_types: ['card'],
line\_items: [
{
price: 'price\_1234567890',
quantity: 1,
},
],
mode: 'payment',
success_url: 'https://yourwebsite.com/success?session_id={CHECKOUT_SESSION_ID}',
cancel\_url: 'https://yourwebsite.com/cancel',
allow_promotion_codes: true, // Show a coupon field on checkout
});
Step 6: Implement a Custom Coupon Form (Optional)
If you want to validate coupon codes before redirecting to Checkout:
// Frontend HTML (simplified)
// Frontend JavaScript
document.getElementById('apply-coupon').addEventListener('click', async () => {
const couponCode = document.getElementById('coupon-code').value;
try {
const response = await fetch('/validate-coupon', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ code: couponCode }),
});
const result = await response.json();
if (result.valid) {
document.getElementById('coupon-status').textContent = `Coupon applied: ${result.discount}% off`;
sessionStorage.setItem('couponCode', couponCode);
} else {
document.getElementById('coupon-status').textContent = 'Invalid coupon code';
}
} catch (error) {
console.error('Error validating coupon:', error);
document.getElementById('coupon-status').textContent = 'Error validating coupon';
}
});
Server-side coupon validation:
// Node.js/Express server route
app.post('/validate-coupon', async (req, res) => {
const { code } = req.body;
try {
// Retrieve the promotion code from Stripe
const promotionCodes = await stripe.promotionCodes.list({
code,
active: true,
});
if (promotionCodes.data.length > 0) {
const promo = promotionCodes.data[0];
// Check if coupon is valid (not expired, has redemptions left, etc.)
if (promo.active) {
// Retrieve the associated coupon details
const coupon = await stripe.coupons.retrieve(promo.coupon.id);
res.json({
valid: true,
discount: coupon.percent_off || (coupon.amount_off / 100),
id: promo.id,
});
return;
}
}
res.json({ valid: false });
} catch (error) {
console.error('Error validating coupon:', error);
res.status(500).json({ error: 'Error validating coupon' });
}
});
Step 7: Create Checkout Session with Validated Coupon
app.post('/create-checkout-session', async (req, res) => {
const { couponId } = req.body;
try {
const session = await stripe.checkout.sessions.create({
payment_method_types: ['card'],
line\_items: [
{
price: 'price\_1234567890',
quantity: 1,
},
],
mode: 'payment',
success_url: 'https://yourwebsite.com/success?session_id={CHECKOUT_SESSION_ID}',
cancel\_url: 'https://yourwebsite.com/cancel',
discounts: couponId ? [
{
promotion\_code: couponId,
},
] : [],
});
res.json({ id: session.id });
} catch (error) {
console.error('Error creating checkout session:', error);
res.status(500).json({ error: 'Error creating checkout session' });
}
});
Step 8: Verify Discount Application
To verify the discount was applied after payment, retrieve the Checkout session:
app.get('/checkout-session', async (req, res) => {
const { sessionId } = req.query;
try {
const session = await stripe.checkout.sessions.retrieve(sessionId, {
expand: ['total\_details.breakdown'],
});
const discountAmount = session.total\_details.breakdown.discounts.reduce(
(sum, discount) => sum + discount.amount,
0
);
res.json({
success: true,
amountSubtotal: session.amount\_subtotal,
amountTotal: session.amount\_total,
discountAmount: discountAmount,
currency: session.currency,
});
} catch (error) {
console.error('Error retrieving session:', error);
res.status(500).json({ error: 'Error retrieving session' });
}
});
Step 9: Handle Dynamic/Calculated Discounts
For more complex discount scenarios that can't be handled by Stripe coupons (e.g., "buy one get one free"), use the line_items
object with custom prices:
const session = await stripe.checkout.sessions.create({
payment_method_types: ['card'],
line\_items: [
{
price\_data: {
currency: 'usd',
product\_data: {
name: 'T-shirt',
},
unit\_amount: 2000, // $20.00
},
quantity: 1,
},
{
price\_data: {
currency: 'usd',
product\_data: {
name: 'T-shirt (Free)',
},
unit\_amount: 0, // $0.00
},
quantity: 1,
},
],
mode: 'payment',
success_url: 'https://yourwebsite.com/success?session_id={CHECKOUT_SESSION_ID}',
cancel\_url: 'https://yourwebsite.com/cancel',
});
Step 10: Implement Automatic Discount Logic with Webhook (Advanced)
Set up a webhook to automatically apply discounts based on your business rules:
// Install body-parser middleware
const bodyParser = require('body-parser');
app.use('/webhook', bodyParser.raw({ type: 'application/json' }));
app.post('/webhook', async (req, res) => {
const sig = req.headers['stripe-signature'];
const endpointSecret = 'whsec_your_webhook\_secret';
let event;
try {
event = stripe.webhooks.constructEvent(req.body, sig, endpointSecret);
} catch (err) {
return res.status(400).send(`Webhook Error: ${err.message}`);
}
// Handle checkout.session.completed event
if (event.type === 'checkout.session.completed') {
const session = event.data.object;
// Apply post-purchase discounts or update customer's discount eligibility
await updateCustomerDiscountEligibility(session.customer);
}
res.status(200).send();
});
async function updateCustomerDiscountEligibility(customerId) {
// Example: Add metadata to the customer for future discounts
await stripe.customers.update(customerId, {
metadata: {
eligible_for_discount: 'true',
discount\_expiry: Math.floor(Date.now() / 1000) + (30 _ 24 _ 60 \* 60), // 30 days from now
},
});
}
Step 11: Track Discount Usage in Your Database (Recommended)
Maintain a record of discount usage for analytics:
// Example using MongoDB/Mongoose
const mongoose = require('mongoose');
const discountUsageSchema = new mongoose.Schema({
customerId: String,
couponId: String,
checkoutSessionId: String,
discountAmount: Number,
originalAmount: Number,
usedAt: { type: Date, default: Date.now },
});
const DiscountUsage = mongoose.model('DiscountUsage', discountUsageSchema);
// In your checkout success handler
app.get('/success', async (req, res) => {
const { session\_id } = req.query;
try {
const session = await stripe.checkout.sessions.retrieve(session\_id, {
expand: ['total\_details.breakdown', 'customer'],
});
// Calculate the total discount amount
const discountAmount = session.total\_details.breakdown.discounts.reduce(
(sum, discount) => sum + discount.amount,
0
);
// Store the discount usage
if (discountAmount > 0) {
await new DiscountUsage({
customerId: session.customer.id,
couponId: session.total\_details.breakdown.discounts[0]?.discount?.coupon?.id,
checkoutSessionId: session.id,
discountAmount: discountAmount,
originalAmount: session.amount\_subtotal,
}).save();
}
res.redirect('/thank-you');
} catch (error) {
console.error('Error handling success:', error);
res.status(500).send('Error processing your payment');
}
});
When it comes to serving you, we sweat the little things. That’s why our work makes a big impact.