Ödənişlər Django layihəsinin real pulla qarşılaşdığı yerdir, ona görə ssenari darıxdırıcı və proqnozlaşdırıla bilən olmalıdır. Stripe Checkout ilə kart məlumatını heç vaxt görmürsünüz: view Checkout Session yaradır, müştəri Stripe səhifəsində ödəyir, nəticəni Stripe webhook ilə bildirir. Bu mövzuda Django tərəfini qururuq: Order və StripeEvent modelləri, Checkout view-su, csrf_exempt və imza yoxlaması ilə webhook view-su, idempotent emal, testlər və dj-stripe icmalı. Stripe anlayışları Stripe Checkout və Stripe webhook-ları dərslərində izah olunub. Hər yerdə test rejimi və yer tutucu açarlar işlənir.

Ssenari

Ödəniş zamanı brauzer, Django, Stripe və Order cədvəli
Ödəniş zamanı brauzer, Django, Stripe və Order cədvəli
  1. Brauzer Checkout view-nuza POST göndərir.
  2. View Order (pending) və Stripe Session yaradır, sonra session.url-ə yönləndirir.
  3. Müştəri Stripe səhifəsində ödəyir.
  4. Stripe webhook-unuza checkout.session.completed POST edir; sifarişi yalnız bu paid edir.
HissəHarada yaşayır
Qiymət və məhsulsizin verilənlər bazası (Product), heç vaxt sorğu
Sifariş statusuOrder.status, yalnız webhook dəyişir
Gizli açar, webhook secretmühit dəyişənləri, settings.py-da oxunur
Təkrardan qorunmaunique=True ilə StripeEvent.event_id

💡 Uğur səhifəsi «təşəkkür edirik, ödənişi təsdiqləyirik» yaza və sifariş statusunu oxuya bilər. Sifarişi ödənilmiş etməməlidir.

Parametrlər və modellər

SDK pip install stripe ilə qurulur, açarlar mühitdən oxunur (parametrlər və mühitlər):

python
# settings.py
import os

STRIPE_SECRET_KEY = os.environ["STRIPE_SECRET_KEY"]  # sk_test_...
STRIPE_WEBHOOK_SECRET = os.environ["STRIPE_WEBHOOK_SECRET"]  # whsec_...

Order məbləği ən kiçik vahidlərlə (sentlərlə) tam ədəd kimi saxlayır, StripeEvent isə artıq emal olunmuş hər webhook event-ini yadda saxlayır:

python
from django.conf import settings
from django.db import models

class Order(models.Model):
    class Status(models.TextChoices):
        PENDING = "pending", "Pending"
        PAID = "paid", "Paid"
        CANCELED = "canceled", "Canceled"

    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.PROTECT)
    amount = models.PositiveIntegerField(help_text="minor units (cents)")
    currency = models.CharField(max_length=3, default="usd")
    status = models.CharField(
        max_length=10, choices=Status.choices, default=Status.PENDING
    )
    stripe_session_id = models.CharField(
        max_length=255, blank=True, db_index=True
    )
    created_at = models.DateTimeField(auto_now_add=True)

class StripeEvent(models.Model):
    event_id = models.CharField(max_length=255, unique=True)
    type = models.CharField(max_length=100)
    received_at = models.DateTimeField(auto_now_add=True)
Sifariş vəziyyətləri: pending, paid, canceled və StripeEvent təkrar süzgəci
Sifariş vəziyyətləri: pending, paid, canceled və StripeEvent təkrar süzgəci

⚠️ Pul üçün FloatField işlətməyin. Tam sentlər (burada olduğu kimi) və ya DecimalField götürün.

Checkout view-su