🌐 Detecting your location…

কিভাবে 2026 সালে একটি Next.js অ্যাপে স্ট্রাইপ পেমেন্ট যোগ করবেন: সম্পূর্ণ ইন্টিগ্রেশন গাইড

⏱️4 min read  ·  692 words

বেশিরভাগ স্ট্রাইপ ইন্টিগ্রেশন একই জায়গায় ব্যর্থ হয়: তারা ব্রাউজার পুনঃনির্দেশকে অর্থপ্রদানের প্রমাণ হিসাবে বিবেচনা করে। তা নয়। সত্যের একমাত্র নির্ভরযোগ্য উৎস হল ওয়েবহুক স্ট্রাইপ আপনার সার্ভার পাঠায়। এই নির্দেশিকাটি অ্যাপ রাউটারের সাথে Next.js-এ একটি সঠিক ইন্টিগ্রেশন তৈরি করে — চেকআউট, স্বাক্ষর যাচাইকরণ সহ ওয়েবহুক, অদম্য পূর্ণতা, এবং সদস্যতা।

স্থাপত্য প্রথম

প্রবাহ বোঝা দুটি বাগ প্রতিরোধ করে যা বেশিরভাগ ইন্টিগ্রেশন ভঙ্গ করে।

  1. আপনার সার্ভার একটি চেকআউট সেশন তৈরি করে এবং এর URL প্রদান করে।
  2. ব্রাউজার স্ট্রাইপে পুনঃনির্দেশ করে। ব্যবহারকারী স্ট্রাইপের পৃষ্ঠায় অর্থ প্রদান করে — কার্ডের বিবরণ কখনই আপনার সার্ভারকে স্পর্শ করে না।
  3. স্ট্রাইপ ব্রাউজারটিকে আপনার সাফল্যের URL এ পুনঃনির্দেশ করে।
  4. আলাদাভাবে, স্ট্রাইপ পেমেন্ট নিশ্চিত করে আপনার সার্ভারে একটি ওয়েবহুক পাঠায়।
  5. আপনার ওয়েবহুক হ্যান্ডলার অ্যাক্সেস দেয়। সাফল্যের পাতা নয়।

ধাপ 5 পুরো খেলা. ব্যবহারকারী পুনঃনির্দেশের আগে ট্যাবটি বন্ধ করতে পারে, সংযোগ হারাতে পারে, অথবা সফল URL সম্পাদনা করে সরাসরি এটি দেখতে পারে। শুধুমাত্র ওয়েবহুকে অ্যাক্সেস মঞ্জুর করুন।

ধাপ 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: ওয়েবহুক — যেখানে সঠিকতা বাস করে

দুটি জিনিস Next.js-এ ওয়েবহুক ভেঙে দেয়। প্রথমে, আপনাকে অবশ্যইএর বিরুদ্ধে স্বাক্ষর যাচাই করতে হবে কাঁচা অনুরোধ শরীর; যদি কিছু এটিকে প্রথমে 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, তাই CLI এর সাথে ইভেন্ট ফরওয়ার্ড করুন।

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 });
}

লাইভ চেকলিস্ট যাচ্ছে

  • লাইভ কীগুলির জন্য পরীক্ষা কীগুলি অদলবদল করুন এবং একটিতৈরি করুন৷ আলাদা ওয়েবহুক এন্ডপয়েন্ট লাইভ মোডে তার নিজস্ব সাইনিং সিক্রেট
  • নিশ্চিত করুন যে আপনার ওয়েবহুক URL সর্বজনীনভাবে পৌঁছানো যায় এবং 2xx দ্রুত ফেরত দেয় — অ্যাসিঙ্ক্রোনাসভাবে ধীর গতিতে কাজ করুন
  • আপনি যদি এখতিয়ার জুড়ে বিক্রি করেন তবে স্ট্রাইপ ট্যাক্স সক্ষম করুন
  • জালিয়াতি সুরক্ষার জন্য রাডার নিয়ম চালু করুন
  • একটি আসল কার্ড দিয়ে সম্পূর্ণ ফ্লো পরীক্ষা করুন, তারপর এটি ফেরত দিন
  • ইভেন্ট আইডিতে আপনার ইডেমপোটেন্সি টেবিলের একটি অনন্য সূচক আছে যাচাই করুন

সাধারণ ভুল

সাফল্য পৃষ্ঠায় অ্যাক্সেস মঞ্জুর করা. যে ব্যবহারকারীরা এটিতে পৌঁছান না তারা এখনও অর্থপ্রদান করেন এবং যারা সরাসরি এটি দেখেন তারা কখনও করেননি।

স্বাক্ষর যাচাইয়ের আগে শরীর পার্স করা। যাচাইকরণের জন্য কাঁচা বাইট প্রয়োজন। ব্যবহার করুনreq.text() এবং প্রথমে অন্য কিছু না।

ক্লায়েন্টের কাছ থেকে একটি মূল্য বিশ্বাস করা. শনাক্তকারী দ্বারা সার্ভার-সাইড আপ দাম দেখুন, সবসময়.

হ্যান্ডলার ব্যর্থতায় 200 ফেরত দেওয়া হচ্ছে। স্ট্রাইপ 2xx কে সাফল্য হিসাবে বিবেচনা করে এবং পুনরায় চেষ্টা করা বন্ধ করে, তাই ইভেন্টটি স্থায়ীভাবে হারিয়ে যায়। 500 রিটার্ন করুন এবং এটি আবার চেষ্টা করুন।

প্রথম ব্যর্থ অর্থপ্রদানে অ্যাক্সেস প্রত্যাহার করা হচ্ছে। কার্ড সাময়িক কারণে ব্যর্থ হয়. পরিবর্তে স্ট্রাইপের ডনিং প্রক্রিয়া অনুসরণ করুন।

উপসংহার

একটি সঠিক স্ট্রাইপ ইন্টিগ্রেশন কয়েকটি নিয়মের উপর নির্ভর করে:আপনার নিয়ন্ত্রণ করা মূল্যগুলির সাথে সেশন সার্ভার-সাইড তৈরি করুন, ওয়েবহুককে সত্যের একমাত্র উত্স হিসাবে বিবেচনা করুন, কাঁচা অংশের বিরুদ্ধে স্বাক্ষরগুলি যাচাই করুন, ইভেন্ট আইডিতে একটি অনন্য সীমাবদ্ধতার সাথে পূর্ণতাকে ইমপোটেন্ট করুন এবং পরিচালনা ব্যর্থ হলে নন-2xx ফেরত দিন তাই স্ট্রাইপ পুনরায় চেষ্টা করুন. বিলিং পোর্টালকে পুনর্নির্মাণের পরিবর্তে সাবস্ক্রিপশন পরিচালনার হাতে নিন। এই অধিকারগুলি পান এবং অর্থপ্রদানগুলি আপনার আবেদনের সর্বনিম্ন ঘটনাপূর্ণ অংশ হয়ে ওঠে।

MD Rafikul Islam

Written by

MD Rafikul Islam is a software developer and the editor of TechPulse. He writes about developer tooling, hardware, and the practical decisions that come up in day-to-day engineering work — which laptop to buy, which framework to commit to, why a build broke at 2am. He tests the tools he writes about and says plainly when something is not worth the money. Corrections and corrections requests are welcome at rony.yf25@gmail.com.

✍️ Leave a Comment

Your email address will not be published. Required fields are marked *

🌐 Read in:🇬🇧 English🇩🇪 Deutsch🇧🇷 Português🇸🇦 العربية🇮🇳 हिन्दी🇧🇩 বাংলা