अधिकांश स्ट्राइप एकीकरण एक ही स्थान पर विफल हो जाते हैं: वे ब्राउज़र रीडायरेक्ट को भुगतान के प्रमाण के रूप में मानते हैं। यह नहीं है। सत्य का एकमात्र विश्वसनीय स्रोत वेबहुक स्ट्राइप आपके सर्वर को भेजता है। यह मार्गदर्शिका नेक्स्ट.जेएस में ऐप राउटर – चेकआउट, हस्ताक्षर सत्यापन के साथ वेबहुक, इडेम्पोटेंट पूर्ति और सब्सक्रिप्शन के साथ एक सही एकीकरण बनाती है।
📋 Table of Contents
- वास्तुकला प्रथम
- चरण 1: स्थापित करें और कॉन्फ़िगर करें
- चरण 2: चेकआउट सत्र बनाएं
- चरण 3: क्लाइंट से रीडायरेक्ट
- चरण 4: वेबहुक – जहां शुद्धता रहती है
- चरण 5: स्थानीय स्तर पर वेबहुक का परीक्षण करें
- चरण 6: सफलता पृष्ठ पुष्टि करता है, यह अनुदान नहीं देता है
- चरण 7: ग्राहकों को अपनी सदस्यता स्वयं प्रबंधित करने दें
- लाइव चेकलिस्ट
- सामान्य गलतियाँ
- निष्कर्ष
वास्तुकला प्रथम
प्रवाह को समझना उन दो बगों को रोकता है जो अधिकांश एकीकरणों को तोड़ते हैं।
- आपका सर्वर एक चेकआउट सत्र बनाता है और उसका URL लौटाता है।
- ब्राउज़र स्ट्राइप पर रीडायरेक्ट करता है। उपयोगकर्ता स्ट्राइप के पेज पर भुगतान करता है – कार्ड विवरण कभी भी आपके सर्वर को नहीं छूता है।
- स्ट्राइप ब्राउज़र को आपके सफलता URL पर वापस रीडायरेक्ट करता है।
- अलग से, स्ट्राइप भुगतान की पुष्टि करते हुए आपके सर्वर पर एक वेबहुक भेजता है।
- आपका वेबहुक हैंडलर पहुंच प्रदान करता है। सफलता पृष्ठ नहीं.
चरण 5 पूरा खेल है. उपयोगकर्ता रीडायरेक्ट से पहले टैब बंद कर सकता है, कनेक्टिविटी खो सकता है, या बस सफलता यूआरएल को संपादित कर सकता है और सीधे उस पर जा सकता है। केवल वेबहुक पर पहुंच प्रदान करें।
चरण 1: स्थापित करें और कॉन्फ़िगर करें
npm install stripe @stripe/stripe-js
पर्यावरण चर में कुंजियाँ संग्रहीत करें। गुप्त कुंजी कभी भी ग्राहक तक नहीं पहुंचनी चाहिए, इसलिए उसे कोईNEXT_PUBLIC_नहीं मिलता है उपसर्ग.
# .env.local
STRIPE_SECRET_KEY=sk_test_...
STRIPE_WEBHOOK_SECRET=whsec_...
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_...
NEXT_PUBLIC_BASE_URL=http://localhost:3000
एक साझा स्ट्राइप क्लाइंट बनाएं ताकि आप प्रति अनुरोध एक का निर्माण न करें।
// lib/stripe.ts
import Stripe from 'stripe';
if (!process.env.STRIPE_SECRET_KEY) {
throw new Error('STRIPE_SECRET_KEY is not set');
}
export const stripe = new Stripe(process.env.STRIPE_SECRET_KEY, {
apiVersion: '2026-06-30',
typescript: true,
});
पिनिंगapiVersion मायने रखता है. इसके बिना आपके खाते का डिफ़ॉल्ट संस्करण आपको विरासत में मिलेगा और वह आपके अंतर्गत बदल सकता है।
चरण 2: चेकआउट सत्र बनाएं
ग्राहक से कभी भी कीमत स्वीकार न करें। एक उत्पाद पहचानकर्ता भेजें और सर्वर पर कीमत देखें, या उपयोगकर्ता बस अनुरोध को संपादित करेगा और एक प्रतिशत का भुगतान करेगा।
// app/api/checkout/route.ts
import { NextResponse } from 'next/server';
import { stripe } from '@/lib/stripe';
import { getCurrentUser } from '@/lib/auth';
const PRICES: Record<string, string> = {
pro_monthly: 'price_1AbCdEfGhIjKlMnO',
pro_yearly: 'price_1XyZaBcDeFgHiJkL',
};
export async function POST(req: Request) {
const user = await getCurrentUser();
if (!user) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
const { plan } = await req.json();
const priceId = PRICES[plan];
if (!priceId) {
return NextResponse.json({ error: 'Unknown plan' }, { status: 400 });
}
const session = await stripe.checkout.sessions.create({
mode: 'subscription',
line_items: [{ price: priceId, quantity: 1 }],
success_url: `${process.env.NEXT_PUBLIC_BASE_URL}/welcome?session_id={CHECKOUT_SESSION_ID}`,
cancel_url: `${process.env.NEXT_PUBLIC_BASE_URL}/pricing`,
customer_email: user.email,
// client_reference_id survives the round trip and arrives in the webhook.
client_reference_id: user.id,
metadata: { userId: user.id, plan },
});
return NextResponse.json({ url: session.url });
}
client_reference_id औरmetadata आप कैसे जानते हैं कि वेबहुक आने पर किस उपयोगकर्ता ने भुगतान किया है। उन्हें हटा दें और आप ईमेल द्वारा खातों से भुगतान का मिलान करेंगे, जो उस क्षण टूट जाता है जब कोई व्यक्ति किसी भिन्न पते से भुगतान करता है।
चरण 3: क्लाइंट से रीडायरेक्ट
'use client';
export function UpgradeButton({ plan }: { plan: string }) {
async function handleClick() {
const res = await fetch('/api/checkout', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ plan }),
});
if (!res.ok) {
alert('Could not start checkout. Please try again.');
return;
}
const { url } = await res.json();
window.location.href = url;
}
return <button onClick={handleClick}>Upgrade</button>;
}
चरण 4: वेबहुक – जहां शुद्धता रहती है
नेक्स्ट.जेएस में दो चीजें वेबहुक तोड़ती हैं। सबसे पहले, आपकोके सामने हस्ताक्षर सत्यापित करना होगा कच्चा अनुरोध निकाय; यदि कोई चीज़ इसे पहले JSON में पार्स करती है, तो सत्यापन विफल हो जाता है। दूसरा, आपको डुप्लिकेट डिलीवरी को संभालना होगा, क्योंकि स्ट्राइप पुनः प्रयास करेगा और एक ही इवेंट को एक से अधिक बार डिलीवर करेगा।
// app/api/webhooks/stripe/route.ts
import { NextResponse } from 'next/server';
import { headers } from 'next/headers';
import { stripe } from '@/lib/stripe';
import { grantAccess, revokeAccess, hasProcessed, markProcessed } from '@/lib/billing';
export async function POST(req: Request) {
// req.text() gives the raw body, which signature verification requires.
const body = await req.text();
const signature = (await headers()).get('stripe-signature');
if (!signature) {
return NextResponse.json({ error: 'Missing signature' }, { status: 400 });
}
let event;
try {
event = stripe.webhooks.constructEvent(
body,
signature,
process.env.STRIPE_WEBHOOK_SECRET!
);
} catch (err) {
console.error('Signature verification failed:', err);
return NextResponse.json({ error: 'Invalid signature' }, { status: 400 });
}
// Stripe retries on any non-2xx, so the same event can arrive repeatedly.
if (await hasProcessed(event.id)) {
return NextResponse.json({ received: true });
}
try {
switch (event.type) {
case 'checkout.session.completed': {
const session = event.data.object;
const userId = session.metadata?.userId ?? session.client_reference_id;
if (userId) {
await grantAccess(userId, {
customerId: session.customer as string,
subscriptionId: session.subscription as string,
});
}
break;
}
case 'customer.subscription.deleted': {
await revokeAccess(event.data.object.customer as string);
break;
}
case 'invoice.payment_failed': {
// Notify the user; do not revoke immediately — cards fail temporarily.
break;
}
}
await markProcessed(event.id);
return NextResponse.json({ received: true });
} catch (err) {
console.error('Webhook handler failed:', err);
// Return 500 so Stripe retries rather than dropping the event.
return NextResponse.json({ error: 'Handler failed' }, { status: 500 });
}
}
hasProcessed औरmarkProcessed एक अद्वितीय बाधा के साथ स्ट्राइप ईवेंट आईडी पर कुंजीबद्ध तालिका में लिखना चाहिए। वह एकमात्र बाधा ही पूर्ति को निष्क्रिय बनाती है – इसके बिना, दोबारा प्रयास करने पर खाते में डबल-क्रेडिट जमा हो जाता है।
चरण 5: स्थानीय स्तर पर वेबहुक का परीक्षण करें
धारी नहीं पहुंच सकतीlocalhost, इसलिए सीएलआई के साथ घटनाओं को आगे बढ़ाएं।
stripe login
stripe listen --forward-to localhost:3000/api/webhooks/stripe
# In another terminal, fire a test event:
stripe trigger checkout.session.completed
stripe listen एक वेबहुक हस्ताक्षरित रहस्य प्रिंट करता है।STRIPE_WEBHOOK_SECRETके लिए उस मान का उपयोग करें विकास में – यह डैशबोर्ड से भिन्न है।
चरण 6: सफलता पृष्ठ पुष्टि करता है, यह अनुदान नहीं देता है
सफलता पृष्ठ पर यह लिखा होना चाहिए कि आपका वेबहुक पहले ही लिख चुका है। यदि वेबहुक अभी तक नहीं उतरा है, तो कुछ भी देने के बजाय लंबित स्थिति दिखाएं।
// app/welcome/page.tsx
import { getCurrentUser } from '@/lib/auth';
import { getSubscription } from '@/lib/billing';
export default async function WelcomePage() {
const user = await getCurrentUser();
const subscription = await getSubscription(user.id);
if (!subscription?.active) {
return (
<p>Payment received. Your account is being activated — this usually takes a few seconds.</p>
);
}
return <h1>Welcome to Pro</h1>;
}
चरण 7: ग्राहकों को अपनी सदस्यता स्वयं प्रबंधित करने दें
बिलिंग पोर्टल योजना में बदलाव, रद्दीकरण, चालान और कार्ड अपडेट को संभालता है। उन प्रवाहों का निर्माण स्वयं करना कई हफ्तों का काम है जिसे आपको करने की आवश्यकता नहीं है।
// app/api/portal/route.ts
import { NextResponse } from 'next/server';
import { stripe } from '@/lib/stripe';
import { getCurrentUser } from '@/lib/auth';
import { getCustomerId } from '@/lib/billing';
export async function POST() {
const user = await getCurrentUser();
if (!user) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
const customerId = await getCustomerId(user.id);
if (!customerId) return NextResponse.json({ error: 'No subscription' }, { status: 400 });
const session = await stripe.billingPortal.sessions.create({
customer: customerId,
return_url: `${process.env.NEXT_PUBLIC_BASE_URL}/account`,
});
return NextResponse.json({ url: session.url });
}
लाइव चेकलिस्ट
- लाइव कुंजियों के लिए परीक्षण कुंजियाँ स्वैप करें, और एकबनाएं अलग अपने स्वयं के हस्ताक्षर रहस्य के साथ लाइव मोड में वेबहुक एंडपॉइंट
- पुष्टि करें कि आपका वेबहुक यूआरएल सार्वजनिक रूप से पहुंच योग्य है और 2xx जल्दी लौटाता है – एसिंक्रोनस रूप से धीमी गति से काम करें
- यदि आप विभिन्न न्यायक्षेत्रों में बेचते हैं तो स्ट्राइप टैक्स सक्षम करें
- धोखाधड़ी से सुरक्षा के लिए रडार नियम चालू करें
- वास्तविक कार्ड से पूर्ण प्रवाह का परीक्षण करें, फिर उसे वापस कर दें
- सत्यापित करें कि आपकी निष्क्रियता तालिका में ईवेंट आईडी पर एक अद्वितीय अनुक्रमणिका है
सामान्य गलतियाँ
सफलता पृष्ठ पर पहुँच प्रदान करना। जो उपयोगकर्ता इस तक कभी नहीं पहुंचे, उन्हें अभी भी भुगतान करना पड़ा, और जो उपयोगकर्ता इस तक सीधे पहुंचे, उन्होंने कभी भुगतान नहीं किया।
हस्ताक्षर सत्यापन से पहले शरीर को पार्स करना। सत्यापन के लिए कच्चे बाइट्स की आवश्यकता होती है।req.text()का प्रयोग करें और पहले कुछ नहीं.
ग्राहक से कीमत पर भरोसा करना। सर्वर-साइड पर हमेशा पहचानकर्ता द्वारा कीमतों में वृद्धि देखें।
हैंडलर की विफलता पर 200 लौटाया जा रहा है। स्ट्राइप 2xx को सफलता मानता है और पुनः प्रयास करना बंद कर देता है, इसलिए ईवेंट स्थायी रूप से खो जाता है। 500 लौटाएँ और इसे पुनः प्रयास करने दें।
पहले असफल भुगतान पर पहुंच रद्द करना। अस्थायी कारणों से कार्ड विफल हो जाते हैं. इसके बजाय स्ट्राइप की धूर्तता प्रक्रिया का पालन करें।
निष्कर्ष
एक सही स्ट्राइप एकीकरण कुछ नियमों पर आधारित है:आपके द्वारा नियंत्रित कीमतों के साथ सत्र सर्वर-साइड बनाएं, वेबहुक को सत्य का एकमात्र स्रोत मानें, कच्चे शरीर के खिलाफ हस्ताक्षर सत्यापित करें, ईवेंट आईडी पर एक अद्वितीय बाधा के साथ पूर्ति को निष्क्रिय बनाएं, और हैंडलिंग विफल होने पर गैर-2xx लौटाएं ताकि स्ट्राइप पुनः प्रयास करे. बिलिंग पोर्टल का पुनर्निर्माण करने के बजाय उसका सदस्यता प्रबंधन हाथ से करें। इन्हें ठीक से प्राप्त करें और भुगतान आपके आवेदन का सबसे कम घटनापूर्ण हिस्सा बन जाएगा।
🔗 Share this article
✍️ Leave a Comment