Przejdź do treści
Poradniki

Integracja płatności Stripe w aplikacji Next.js

Opublikowano:
·5 min czytania·Autor: MDS Software Solutions Group

Integracja płatności Stripe

poradniki

Integracja płatności Stripe w Next.js

Obsługa płatności online to kluczowy element każdego sklepu internetowego i aplikacji SaaS. Stripe od lat jest jednym z najpopularniejszych procesorów płatności na świecie, oferując rozbudowane API, doskonałą dokumentację i szerokie wsparcie dla nowoczesnych frameworków. W tym przewodniku pokażemy, jak krok po kroku zintegrować Stripe z aplikacją Next.js, wykorzystując TypeScript, API Routes, webhooki, subskrypcje i wiele innych funkcji.

Czym jest Stripe?#

Stripe to platforma do obsługi płatności internetowych, z której korzystają zarówno startupy, jak i globalne korporacje (m.in. Shopify, Amazon, Google). Stripe oferuje:

  • Przetwarzanie płatności kartami kredytowymi i debetowymi w ponad 135 walutach
  • Stripe Elements — gotowe, konfigurowalne komponenty UI do bezpiecznego zbierania danych kart
  • Checkout Sessions — hostowane strony płatności z pełnym UI
  • Payment Intents API — elastyczne API do budowania własnych przepływów płatności
  • Billing — zarządzanie subskrypcjami i fakturami
  • Stripe Dashboard — panel administracyjny do monitorowania transakcji, zwrotów i analityki
  • Stripe CLI — narzędzie do testowania webhooków lokalnie

Stripe jest zgodny ze standardami PCI DSS Level 1, co oznacza, że dane kart płatniczych nigdy nie przechodzą przez Twój serwer, jeśli korzystasz z Elements lub Checkout.

Konfiguracja projektu#

Zacznijmy od zainstalowania wymaganych pakietów w projekcie Next.js:

// Instalacja zależności
// npm install stripe @stripe/stripe-js @stripe/react-stripe-js

// Zmienne środowiskowe (.env.local)
// NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_...
// STRIPE_SECRET_KEY=sk_test_...
// STRIPE_WEBHOOK_SECRET=whsec_...

Następnie skonfigurujmy instancję Stripe po stronie serwera i klienta:

// lib/stripe.ts
import Stripe from 'stripe';

export const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
  apiVersion: '2023-10-16',
  typescript: true,
});
// lib/stripe-client.ts
import { loadStripe, Stripe } from '@stripe/stripe-js';

let stripePromise: Promise<Stripe | null>;

export const getStripe = () => {
  if (!stripePromise) {
    stripePromise = loadStripe(
      process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY!
    );
  }
  return stripePromise;
};

Stripe Checkout Sessions#

Najprostszym sposobem na przyjęcie płatności jest Stripe Checkout — hostowana strona płatności, którą Stripe w pełni zarządza. Nie musisz budować własnego formularza ani martwić się o PCI compliance.

API Route do tworzenia sesji#

// app/api/checkout/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { stripe } from '@/lib/stripe';

interface CheckoutRequestBody {
  priceId: string;
  quantity?: number;
  mode?: 'payment' | 'subscription';
}

export async function POST(request: NextRequest) {
  try {
    const body: CheckoutRequestBody = await request.json();
    const { priceId, quantity = 1, mode = 'payment' } = body;

    if (!priceId) {
      return NextResponse.json(
        { error: 'Brak wymaganego parametru priceId' },
        { status: 400 }
      );
    }

    const session = await stripe.checkout.sessions.create({
      mode,
      payment_method_types: ['card', 'blik', 'p24'],
      line_items: [
        {
          price: priceId,
          quantity,
        },
      ],
      success_url: `${process.env.NEXT_PUBLIC_APP_URL}/checkout/success?session_id={CHECKOUT_SESSION_ID}`,
      cancel_url: `${process.env.NEXT_PUBLIC_APP_URL}/checkout/cancel`,
      metadata: {
        source: 'next-app',
      },
    });

    return NextResponse.json({ sessionId: session.id, url: session.url });
  } catch (error) {
    console.error('Błąd tworzenia sesji Checkout:', error);
    return NextResponse.json(
      { error: 'Nie udało się utworzyć sesji płatności' },
      { status: 500 }
    );
  }
}

Komponent przekierowujący do Checkout#

// components/CheckoutButton.tsx
'use client';

import { useState } from 'react';
import { getStripe } from '@/lib/stripe-client';

interface CheckoutButtonProps {
  priceId: string;
  mode?: 'payment' | 'subscription';
  label?: string;
}

export function CheckoutButton({
  priceId,
  mode = 'payment',
  label = 'Kup teraz',
}: CheckoutButtonProps) {
  const [loading, setLoading] = useState(false);

  const handleCheckout = async () => {
    setLoading(true);

    try {
      const response = await fetch('/api/checkout', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ priceId, mode }),
      });

      const { sessionId } = await response.json();
      const stripe = await getStripe();

      if (stripe) {
        const { error } = await stripe.redirectToCheckout({ sessionId });
        if (error) {
          console.error('Błąd przekierowania:', error.message);
        }
      }
    } catch (error) {
      console.error('Błąd Checkout:', error);
    } finally {
      setLoading(false);
    }
  };

  return (
    <button
      onClick={handleCheckout}
      disabled={loading}
      className="bg-indigo-600 text-white px-6 py-3 rounded-lg
                 hover:bg-indigo-700 disabled:opacity-50 transition-colors"
    >
      {loading ? 'Przetwarzanie...' : label}
    </button>
  );
}

Payment Intents — zaawansowane przepływy#

Jeśli potrzebujesz pełnej kontroli nad wyglądem formularza płatności, użyj Payment Intents API w połączeniu ze Stripe Elements. Dzięki temu budujesz własny UI, a Stripe zajmuje się bezpiecznym przetwarzaniem danych karty.

Tworzenie Payment Intent po stronie serwera#

// app/api/payment-intent/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { stripe } from '@/lib/stripe';

interface PaymentIntentRequest {
  amount: number;
  currency?: string;
  description?: string;
  customerEmail?: string;
}

export async function POST(request: NextRequest) {
  try {
    const body: PaymentIntentRequest = await request.json();
    const { amount, currency = 'pln', description, customerEmail } = body;

    // Walidacja po stronie serwera
    if (!amount || amount < 200) {
      return NextResponse.json(
        { error: 'Minimalna kwota to 2.00 PLN' },
        { status: 400 }
      );
    }

    if (amount > 99999999) {
      return NextResponse.json(
        { error: 'Kwota przekracza dozwolony limit' },
        { status: 400 }
      );
    }

    // Opcjonalnie: utwórz lub pobierz klienta Stripe
    let customerId: string | undefined;
    if (customerEmail) {
      const customers = await stripe.customers.list({
        email: customerEmail,
        limit: 1,
      });

      if (customers.data.length > 0) {
        customerId = customers.data[0].id;
      } else {
        const customer = await stripe.customers.create({
          email: customerEmail,
        });
        customerId = customer.id;
      }
    }

    const paymentIntent = await stripe.paymentIntents.create({
      amount,
      currency,
      description,
      customer: customerId,
      automatic_payment_methods: {
        enabled: true,
      },
      metadata: {
        source: 'custom-form',
      },
    });

    return NextResponse.json({
      clientSecret: paymentIntent.client_secret,
      paymentIntentId: paymentIntent.id,
    });
  } catch (error) {
    console.error('Błąd tworzenia PaymentIntent:', error);

    if (error instanceof Stripe.errors.StripeError) {
      return NextResponse.json(
        { error: error.message },
        { status: error.statusCode || 500 }
      );
    }

    return NextResponse.json(
      { error: 'Wewnętrzny błąd serwera' },
      { status: 500 }
    );
  }
}

Stripe Elements — bezpieczny formularz płatności#

Stripe Elements to zestaw wstępnie zbudowanych komponentów UI, które bezpiecznie zbierają dane karty płatniczej. Dane karty nigdy nie trafiają na Twój serwer — są przesyłane bezpośrednio do Stripe.

// components/PaymentForm.tsx
'use client';

import { useState, FormEvent } from 'react';
import {
  Elements,
  PaymentElement,
  useStripe,
  useElements,
} from '@stripe/react-stripe-js';
import { getStripe } from '@/lib/stripe-client';
import type { StripeElementsOptions } from '@stripe/stripe-js';

function CheckoutForm() {
  const stripe = useStripe();
  const elements = useElements();
  const [error, setError] = useState<string | null>(null);
  const [processing, setProcessing] = useState(false);
  const [succeeded, setSucceeded] = useState(false);

  const handleSubmit = async (e: FormEvent) => {
    e.preventDefault();

    if (!stripe || !elements) return;

    setProcessing(true);
    setError(null);

    const { error: submitError } = await elements.submit();
    if (submitError) {
      setError(submitError.message || 'Wystąpił błąd');
      setProcessing(false);
      return;
    }

    const { error: confirmError } = await stripe.confirmPayment({
      elements,
      confirmParams: {
        return_url: `${window.location.origin}/checkout/success`,
      },
    });

    if (confirmError) {
      setError(confirmError.message || 'Płatność nie powiodła się');
      setProcessing(false);
    } else {
      setSucceeded(true);
    }
  };

  if (succeeded) {
    return (
      <div className="text-center p-8">
        <h2 className="text-2xl font-bold text-green-600">
          Płatność zakończona sukcesem!
        </h2>
      </div>
    );
  }

  return (
    <form onSubmit={handleSubmit} className="max-w-md mx-auto space-y-6">
      <PaymentElement />
      {error && (
        <div className="text-red-500 text-sm bg-red-50 p-3 rounded">
          {error}
        </div>
      )}
      <button
        type="submit"
        disabled={!stripe || processing}
        className="w-full bg-indigo-600 text-white py-3 px-4 rounded-lg
                   hover:bg-indigo-700 disabled:opacity-50 transition-colors"
      >
        {processing ? 'Przetwarzanie...' : 'Zapłać'}
      </button>
    </form>
  );
}

interface PaymentFormProps {
  clientSecret: string;
}

export function PaymentForm({ clientSecret }: PaymentFormProps) {
  const options: StripeElementsOptions = {
    clientSecret,
    appearance: {
      theme: 'stripe',
      variables: {
        colorPrimary: '#4f46e5',
        borderRadius: '8px',
      },
    },
  };

  return (
    <Elements stripe={getStripe()} options={options}>
      <CheckoutForm />
    </Elements>
  );
}

Webhooki Stripe#

Webhooki to mechanizm, dzięki któremu Stripe informuje Twoją aplikację o zdarzeniach (np. udana płatność, nieudana subskrypcja, zwrot). Jest to jedyny niezawodny sposób potwierdzenia płatności — nigdy nie polegaj wyłącznie na przekierowaniu klienta na stronę sukcesu.

// app/api/webhooks/stripe/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { stripe } from '@/lib/stripe';
import Stripe from 'stripe';

// Wyłączenie automatycznego parsowania body
export const runtime = 'nodejs';

async function handleCheckoutComplete(
  session: Stripe.Checkout.Session
) {
  console.log('Checkout zakończony:', session.id);

  // Zaktualizuj zamówienie w bazie danych
  // await db.order.update({
  //   where: { stripeSessionId: session.id },
  //   data: { status: 'paid', paidAt: new Date() },
  // });

  // Wyślij e-mail z potwierdzeniem
  // await sendOrderConfirmation(session.customer_email);
}

async function handlePaymentFailed(
  paymentIntent: Stripe.PaymentIntent
) {
  console.error('Płatność nieudana:', paymentIntent.id);
  // Powiadom użytkownika o nieudanej płatności
}

async function handleSubscriptionUpdated(
  subscription: Stripe.Subscription
) {
  console.log('Subskrypcja zaktualizowana:', subscription.id);
  // Zaktualizuj status subskrypcji w bazie danych
}

async function handleInvoicePaid(invoice: Stripe.Invoice) {
  console.log('Faktura opłacona:', invoice.id);
  // Zarejestruj opłaconą fakturę
}

export async function POST(request: NextRequest) {
  const body = await request.text();
  const signature = request.headers.get('stripe-signature');

  if (!signature) {
    return NextResponse.json(
      { error: 'Brak nagłówka stripe-signature' },
      { status: 400 }
    );
  }

  let event: Stripe.Event;

  try {
    event = stripe.webhooks.constructEvent(
      body,
      signature,
      process.env.STRIPE_WEBHOOK_SECRET!
    );
  } catch (err) {
    const message = err instanceof Error ? err.message : 'Nieznany błąd';
    console.error('Weryfikacja webhooka nieudana:', message);
    return NextResponse.json(
      { error: `Webhook Error: ${message}` },
      { status: 400 }
    );
  }

  try {
    switch (event.type) {
      case 'checkout.session.completed':
        await handleCheckoutComplete(
          event.data.object as Stripe.Checkout.Session
        );
        break;
      case 'payment_intent.payment_failed':
        await handlePaymentFailed(
          event.data.object as Stripe.PaymentIntent
        );
        break;
      case 'customer.subscription.updated':
        await handleSubscriptionUpdated(
          event.data.object as Stripe.Subscription
        );
        break;
      case 'invoice.paid':
        await handleInvoicePaid(event.data.object as Stripe.Invoice);
        break;
      default:
        console.log(`Nieobsługiwane zdarzenie: ${event.type}`);
    }

    return NextResponse.json({ received: true });
  } catch (error) {
    console.error('Błąd obsługi webhooka:', error);
    return NextResponse.json(
      { error: 'Błąd przetwarzania webhooka' },
      { status: 500 }
    );
  }
}

Subskrypcje i rozliczenia cykliczne#

Stripe Billing umożliwia zarządzanie subskrypcjami. Możesz tworzyć plany cenowe (Products i Prices) w Stripe Dashboard, a następnie oferować je klientom.

// app/api/subscriptions/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { stripe } from '@/lib/stripe';

interface SubscriptionRequest {
  email: string;
  priceId: string;
  paymentMethodId: string;
}

export async function POST(request: NextRequest) {
  try {
    const { email, priceId, paymentMethodId }: SubscriptionRequest =
      await request.json();

    // Znajdź lub utwórz klienta
    const customers = await stripe.customers.list({
      email,
      limit: 1,
    });

    let customer = customers.data[0];
    if (!customer) {
      customer = await stripe.customers.create({
        email,
        payment_method: paymentMethodId,
        invoice_settings: {
          default_payment_method: paymentMethodId,
        },
      });
    } else {
      await stripe.paymentMethods.attach(paymentMethodId, {
        customer: customer.id,
      });
      await stripe.customers.update(customer.id, {
        invoice_settings: {
          default_payment_method: paymentMethodId,
        },
      });
    }

    // Utwórz subskrypcję
    const subscription = await stripe.subscriptions.create({
      customer: customer.id,
      items: [{ price: priceId }],
      payment_settings: {
        payment_method_types: ['card'],
        save_default_payment_method: 'on_subscription',
      },
      expand: ['latest_invoice.payment_intent'],
    });

    const invoice = subscription.latest_invoice as Stripe.Invoice;
    const paymentIntent =
      invoice.payment_intent as Stripe.PaymentIntent;

    return NextResponse.json({
      subscriptionId: subscription.id,
      clientSecret: paymentIntent.client_secret,
      status: subscription.status,
    });
  } catch (error) {
    console.error('Błąd tworzenia subskrypcji:', error);
    return NextResponse.json(
      { error: 'Nie udało się utworzyć subskrypcji' },
      { status: 500 }
    );
  }
}

// Zarządzanie subskrypcją — anulowanie
export async function DELETE(request: NextRequest) {
  try {
    const { subscriptionId } = await request.json();

    const subscription = await stripe.subscriptions.update(
      subscriptionId,
      {
        cancel_at_period_end: true,
      }
    );

    return NextResponse.json({
      status: subscription.status,
      cancelAt: subscription.cancel_at,
    });
  } catch (error) {
    console.error('Błąd anulowania subskrypcji:', error);
    return NextResponse.json(
      { error: 'Nie udało się anulować subskrypcji' },
      { status: 500 }
    );
  }
}

SCA i 3D Secure#

Strong Customer Authentication (SCA) to wymóg regulacyjny w Europie (PSD2), który wymusza dodatkowe uwierzytelnianie płatności. Stripe automatycznie obsługuje 3D Secure, gdy jest to wymagane. Wystarczy użyć Payment Intents API z opcją automatic_payment_methods:

// Stripe automatycznie wywoła 3D Secure, gdy bank tego wymaga.
// Po stronie klienta wystarczy obsłużyć stan requires_action:
const { error, paymentIntent } = await stripe.confirmCardPayment(
  clientSecret,
  {
    payment_method: {
      card: elements.getElement('card')!,
      billing_details: {
        name: 'Jan Kowalski',
        email: 'jan@example.com',
      },
    },
  }
);

if (error) {
  // Użytkownik nie przeszedł weryfikacji 3D Secure
  console.error('Błąd 3DS:', error.message);
} else if (paymentIntent?.status === 'succeeded') {
  // Płatność zakończona sukcesem
  console.log('Płatność potwierdzona');
}

Obsługa wielu walut#

Stripe obsługuje ponad 135 walut. Możesz dynamicznie ustawiać walutę w zależności od lokalizacji użytkownika:

// lib/currency.ts
interface CurrencyConfig {
  currency: string;
  locale: string;
  symbol: string;
  minAmount: number;
}

const CURRENCY_MAP: Record<string, CurrencyConfig> = {
  PL: { currency: 'pln', locale: 'pl-PL', symbol: 'zł', minAmount: 200 },
  DE: { currency: 'eur', locale: 'de-DE', symbol: '€', minAmount: 50 },
  US: { currency: 'usd', locale: 'en-US', symbol: '$', minAmount: 50 },
  GB: { currency: 'gbp', locale: 'en-GB', symbol: '£', minAmount: 30 },
};

export function getCurrencyConfig(
  countryCode: string
): CurrencyConfig {
  return CURRENCY_MAP[countryCode] || CURRENCY_MAP['US'];
}

export function formatAmount(
  amount: number,
  currency: string,
  locale: string
): string {
  return new Intl.NumberFormat(locale, {
    style: 'currency',
    currency: currency.toUpperCase(),
  }).format(amount / 100);
}

Testowanie z Stripe CLI#

Stripe CLI pozwala symulować zdarzenia webhookowe lokalnie, bez konieczności wdrażania aplikacji:

// 1. Zainstaluj Stripe CLI
// brew install stripe/stripe-cli/stripe (macOS)
// scoop install stripe (Windows)

// 2. Zaloguj się
// stripe login

// 3. Nasłuchuj webhooków lokalnie
// stripe listen --forward-to localhost:3000/api/webhooks/stripe

// 4. Wyślij testowe zdarzenie
// stripe trigger payment_intent.succeeded

// 5. Testowe numery kart:
// 4242 4242 4242 4242 — płatność udana
// 4000 0025 0000 3155 — wymaga 3D Secure
// 4000 0000 0000 9995 — płatność odrzucona

Stripe CLI wyświetli webhook signing secret (zaczynający się od whsec_), który musisz ustawić w zmiennej STRIPE_WEBHOOK_SECRET.

PCI Compliance#

Zgodność z PCI DSS jest obowiązkowa dla każdego, kto przetwarza dane kart płatniczych. Korzystając ze Stripe Elements lub Checkout, uzyskujesz najwyższy poziom uproszczenia:

  • SAQ A — formularz Checkout (hostowany przez Stripe) — najmniej wymagań
  • SAQ A-EP — Stripe Elements (embedded w Twojej stronie) — umiarkowane wymagania
  • Dane karty nigdy nie dotykają Twojego serwera — trafiają bezpośrednio do Stripe
  • Stripe jest certyfikowany jako PCI DSS Level 1 — najwyższy poziom bezpieczeństwa

Zadbaj o HTTPS w swoim środowisku produkcyjnym i nie przechowuj żadnych surowych danych kart.

Stripe Dashboard#

Stripe Dashboard to centrum zarządzania Twoimi płatnościami. Oferuje:

  • Podgląd transakcji w czasie rzeczywistym z filtrami i wyszukiwarką
  • Zarządzanie klientami — historia płatności, subskrypcje, metody płatności
  • Zwroty — pełne i częściowe, bezpośrednio z panelu
  • Raporty i analityka — przychody, wskaźniki konwersji, MRR (Monthly Recurring Revenue)
  • Środowisko testowe — osobne API key i dane, bez ryzyka
  • Logi API — podgląd każdego żądania z pełnym payloadem
  • Radar — system wykrywania oszustw z regułami ML

Porównanie z innymi procesorami płatności#

| Cecha | Stripe | PayU | Przelewy24 | PayPal | |---|---|---|---|---| | API dla deweloperów | Doskonałe | Dobre | Średnie | Dobre | | Dokumentacja | Wzorcowa | Dobra | Podstawowa | Dobra | | Wsparcie TypeScript | Pełne | Częściowe | Brak | Częściowe | | Subskrypcje | Wbudowane | Zewnętrzne | Brak | Wbudowane | | 3D Secure | Automatyczne | Manualne | Automatyczne | Automatyczne | | Prowizje (Europa) | 1.4% + 0.25€ | 1.2% + 0.20€ | 1.2% | 2.9% + 0.35€ | | BLIK | Tak | Tak | Tak | Nie | | Przelewy24 | Tak | Nie | Tak | Nie | | Webhooki | Zaawansowane | Podstawowe | Podstawowe | Zaawansowane |

Stripe wyróżnia się doskonałym DX (Developer Experience), kompletnym SDK w TypeScript i najbogatszym ekosystemem integracji.

Obsługa błędów — najlepsze praktyki#

Solidna obsługa błędów jest kluczowa w systemach płatności. Oto wzorzec, który obejmuje najczęstsze scenariusze:

// lib/stripe-errors.ts
import Stripe from 'stripe';

interface StripeErrorResponse {
  message: string;
  code: string;
  statusCode: number;
}

export function handleStripeError(
  error: unknown
): StripeErrorResponse {
  if (error instanceof Stripe.errors.StripeCardError) {
    return {
      message: getPolishCardErrorMessage(error.code),
      code: error.code || 'card_error',
      statusCode: 402,
    };
  }

  if (error instanceof Stripe.errors.StripeRateLimitError) {
    return {
      message: 'Zbyt wiele żądań. Spróbuj ponownie za chwilę.',
      code: 'rate_limit',
      statusCode: 429,
    };
  }

  if (error instanceof Stripe.errors.StripeInvalidRequestError) {
    return {
      message: 'Nieprawidłowe żądanie płatności.',
      code: 'invalid_request',
      statusCode: 400,
    };
  }

  if (error instanceof Stripe.errors.StripeAuthenticationError) {
    return {
      message: 'Błąd konfiguracji płatności.',
      code: 'auth_error',
      statusCode: 500,
    };
  }

  return {
    message: 'Wystąpił nieoczekiwany błąd. Spróbuj ponownie.',
    code: 'unknown',
    statusCode: 500,
  };
}

function getPolishCardErrorMessage(
  code: string | undefined
): string {
  const messages: Record<string, string> = {
    card_declined: 'Karta została odrzucona.',
    insufficient_funds: 'Niewystarczające środki na karcie.',
    expired_card: 'Karta wygasła.',
    incorrect_cvc: 'Nieprawidłowy kod CVC.',
    processing_error: 'Błąd przetwarzania. Spróbuj ponownie.',
    incorrect_number: 'Nieprawidłowy numer karty.',
  };

  return messages[code || ''] || 'Płatność nie powiodła się.';
}

Walidacja po stronie serwera#

Nigdy nie ufaj danym przychodzącym z klienta. Każde żądanie płatności powinno przejść walidację po stronie serwera:

// lib/validation.ts
import { z } from 'zod';

export const paymentSchema = z.object({
  amount: z
    .number()
    .int('Kwota musi być liczbą całkowitą')
    .min(200, 'Minimalna kwota to 2.00 PLN')
    .max(99999999, 'Kwota przekracza limit'),
  currency: z.enum(['pln', 'eur', 'usd', 'gbp']),
  description: z
    .string()
    .max(500, 'Opis nie może przekraczać 500 znaków')
    .optional(),
  customerEmail: z.string().email('Nieprawidłowy adres e-mail').optional(),
  metadata: z.record(z.string()).optional(),
});

export type PaymentInput = z.infer<typeof paymentSchema>;

// Użycie w API Route:
// const result = paymentSchema.safeParse(body);
// if (!result.success) {
//   return NextResponse.json(
//     { error: result.error.flatten() },
//     { status: 400 }
//   );
// }

Podsumowanie#

Integracja Stripe z Next.js daje ogromne możliwości — od prostych jednorazowych płatności po zaawansowane systemy subskrypcyjne. Kluczowe elementy udanej integracji to:

  1. Stripe Elements lub Checkout do bezpiecznego zbierania danych kart
  2. Payment Intents API do elastycznych przepływów płatności
  3. Webhooki jako jedyne źródło prawdy o statusie transakcji
  4. Walidacja po stronie serwera każdego żądania
  5. Obsługa błędów z czytelnymi komunikatami dla użytkowników
  6. Stripe CLI do testowania webhooków lokalnie
  7. SCA/3D Secure obsługiwane automatycznie przez Payment Intents

Stripe w połączeniu z Next.js i TypeScript tworzy solidne, typowane i bezpieczne rozwiązanie płatnicze, gotowe na skalowanie.


Potrzebujesz profesjonalnej integracji płatności Stripe w swojej aplikacji Next.js? Zespół MDS Software Solutions Group specjalizuje się w budowie bezpiecznych systemów e-commerce i SaaS z pełną obsługą płatności online. Skontaktuj się z nami, aby omówić Twój projekt i otrzymać bezpłatną wycenę.

Autor
MDS Software Solutions Group

Zespół ekspertów programistycznych specjalizujących się w nowoczesnych technologiach webowych.

Integracja płatności Stripe w aplikacji Next.js | MDS Software Solutions Group | MDS Software Solutions Group