الانتقال إلى المحتوى الرئيسي

GitHub Repository

إكمال نموذج FastAPI + Dodo Payments الجاهز

نظرة عامة

يوفر نموذج FastAPI نقطة انطلاق جاهزة للإنتاج لدمج مدفوعات دودي مع بايثون الخلفية الخاصة بك. يتضمن هذا القالب معالجة جلسات الدفع، والتحقق من الويب هوك، ودمج بوابة العملاء، وأنماط API غير المتزامنة لمساعدتك في البدء في قبول المدفوعات بسرعة.
يستخدم هذا النموذج FastAPI مع أنماط async/await، وPydantic للتحقق، وواجهة dodopayments الخاصة بـ Python للتكامل السلس مع واجهة برمجة التطبيقات.

الميزات

  • إعداد سريع - ابدأ في أقل من 5 دقائق
  • دعم غير متزامن - مبني على أنماط async/await الأصلية لـ FastAPI
  • جلسات الدفع - تدفق دفع مُعد مسبقًا باستخدام SDK بايثون
  • معالجة الويب هوك - نقطة نهاية ويب هوك آمنة مع التحقق من التوقيع
  • بوابة العملاء - إنشاء جلسات بوابة العملاء بسهولة
  • أمان النوع - تحقق كامل من Pydantic وتلميحات النوع
  • تكوين البيئة - إعداد متغيرات البيئة الجاهزة للاستخدام

المتطلبات المسبقة

قبل أن تبدأ، تأكد من أن لديك:
  • بايثون 3.9+ (موصى به: بايثون 3.11+)
  • pip أو uv لإدارة الحزم
  • حساب مدفوعات دودي (للوصول إلى مفاتيح API وWebhook من لوحة التحكم)

بدء سريع

1

Clone the Repository

git clone https://github.com/dodopayments/fastapi-boilerplate.git
cd fastapi-boilerplate
2

Create Virtual Environment

قم بإعداد بيئة Python معزولة:
python -m venv venv
source venv/bin/activate  # On Windows: venv\Scripts\activate
أو باستخدام uv لإدارة التبعيات بشكل أسرع:
uv venv
source .venv/bin/activate
3

Install Dependencies

pip install -r requirements.txt
أو مع uv:
uv pip install -r requirements.txt
4

Get API Credentials

سجل في Dodo Payments واحصل على بيانات الاعتماد من لوحة التحكم:
تأكد من أنك في وضع الاختبار أثناء التطوير!
5

Configure Environment Variables

قم بإنشاء ملف .env في الدليل الجذري:
cp .env.example .env
قم بتحديث القيم باستخدام بيانات اعتماد مدفوعات دودي الخاصة بك:
.env
DODO_PAYMENTS_API_KEY=your_api_key_here
DODO_PAYMENTS_WEBHOOK_KEY=your_webhook_signing_key_here
DODO_PAYMENTS_ENVIRONMENT=test_mode
لا تقم مطلقًا بإضافة ملف .env إلى نظام التحكم في الإصدارات. فهو مدرج بالفعل في .gitignore.
6

Run the Development Server

uvicorn main:app --reload
افتح http://localhost:8000/docs لرؤية وثائق API التفاعلية!
ينبغي أن ترى واجهة Swagger الخاصة بـ FastAPI مع جميع النقاط النهائية المتاحة جاهزة للاختبار.

هيكل المشروع

fastapi-boilerplate/
├── main.py                 # FastAPI application entry point
├── routers/
│   ├── checkout.py         # Checkout session endpoints
│   ├── webhook.py          # Webhook handler endpoint
│   └── customer_portal.py  # Customer portal endpoints
├── config.py               # Environment configuration
├── requirements.txt        # Python dependencies
├── .env.example            # Environment template
└── README.md

نقاط نهاية API

يتضمن النموذج نقاط النهاية المُعدة مسبقًا التالية:
النقطة النهائيةالطريقةالوصف
/checkoutPOSTإنشاء جلسة دفع جديدة
/webhookPOSTمعالجة webhooks الخاصة بـ Dodo Payments
/customer-portalPOSTتوليد رابط بوابة العميل

أمثلة على الكود

إنشاء جلسة دفع

from fastapi import APIRouter, HTTPException
from dodopayments import AsyncDodoPayments
from pydantic import BaseModel
from config import settings

router = APIRouter()
dodo = AsyncDodoPayments(
    bearer_token=settings.DODO_PAYMENTS_API_KEY,
    environment=settings.DODO_PAYMENTS_ENVIRONMENT
)

class CheckoutRequest(BaseModel):
    product_id: str
    quantity: int = 1
    customer_email: str | None = None
    customer_name: str | None = None

@router.post("/checkout")
async def create_checkout(request: CheckoutRequest):
    try:
        session = await dodo.checkout_sessions.create(
            product_cart=[{
                "product_id": request.product_id,
                "quantity": request.quantity
            }],
            customer={
                "email": request.customer_email,
                "name": request.customer_name
            } if request.customer_email else None,
            return_url="http://localhost:8000/success"
        )
        return {"checkout_url": session.url, "session_id": session.session_id}
    except Exception as e:
        raise HTTPException(status_code=400, detail=str(e))

معالجة الويب هوك

from fastapi import APIRouter, Request, HTTPException
import hmac
import hashlib
from config import settings

router = APIRouter()

def verify_webhook_signature(payload: bytes, signature: str) -> bool:
    expected = hmac.new(
        settings.DODO_PAYMENTS_WEBHOOK_KEY.encode(),
        payload,
        hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(expected, signature)

@router.post("/webhook")
async def handle_webhook(request: Request):
    payload = await request.body()
    signature = request.headers.get("webhook-signature", "")
    
    if not verify_webhook_signature(payload, signature):
        raise HTTPException(status_code=401, detail="Invalid signature")
    
    event = await request.json()
    event_type = event.get("type")
    
    match event_type:
        case "payment.succeeded":
            # Handle successful payment
            payment_id = event["data"]["payment_id"]
            print(f"Payment succeeded: {payment_id}")
            
        case "subscription.active":
            # Handle subscription activation
            subscription_id = event["data"]["subscription_id"]
            print(f"Subscription activated: {subscription_id}")
            
        case "refund.succeeded":
            # Handle refund
            refund_id = event["data"]["refund_id"]
            print(f"Refund processed: {refund_id}")
    
    return {"status": "received"}

دمج بوابة العملاء

from fastapi import APIRouter, HTTPException
from dodopayments import AsyncDodoPayments
from pydantic import BaseModel
from config import settings

router = APIRouter()
dodo = AsyncDodoPayments(
    bearer_token=settings.DODO_PAYMENTS_API_KEY,
    environment=settings.DODO_PAYMENTS_ENVIRONMENT
)

class PortalRequest(BaseModel):
    customer_id: str

@router.post("/customer-portal")
async def create_portal_session(request: PortalRequest):
    try:
        session = await dodo.customers.create_customer_portal(
            customer_id=request.customer_id
        )
        return {"portal_url": session.url}
    except Exception as e:
        raise HTTPException(status_code=400, detail=str(e))

أحداث الويب هوك

يظهر النموذج كيفية التعامل مع أحداث الويب هوك الشائعة:
الحدثالوصف
payment.succeededتم اكتمال الدفع بنجاح
payment.failedفشل محاولة الدفع
subscription.activeأصبحت الاشتراك نشطًا الآن
subscription.cancelledتم إلغاء الاشتراك
refund.succeededتم معالجة الاسترداد بنجاح
أضف منطق عملك داخل معالج الويب هوك لـ:
  • تحديث أذونات المستخدم في قاعدة البيانات الخاصة بك
  • إرسال رسائل تأكيد عبر البريد الإلكتروني
  • توفير الوصول إلى المنتجات الرقمية
  • تتبع التحليلات والقياسات

اختبار الويب هوك محليًا

للتطوير المحلي، استخدم أدوات مثل ngrok لفتح خادمك المحلي:
ngrok http 8000
قم بتحديث عنوان URL للويب هوك في لوحة تحكم مدفوعات دودي:
https://your-ngrok-url.ngrok.io/webhook

النشر

دوكر

FROM python:3.11-slim

WORKDIR /app

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
قم بالبناء والتشغيل:
docker build -t fastapi-dodo .
docker run -p 8000:8000 --env-file .env fastapi-dodo

اعتبارات الإنتاج

قبل النشر في الإنتاج:
  • قم بتبديل DODO_PAYMENTS_ENVIRONMENT إلى live_mode
  • استخدم مفاتيح API الخاصة بالإنتاج من لوحة التحكم
  • حدّث عنوان URL الخاص بالـ webhook إلى نطاق الإنتاج
  • فعّل HTTPS لكل النقاط النهائية

استكشاف الأخطاء وإصلاحها

تأكد من تفعيل البيئة الافتراضية وتثبيت التبعيات:
source venv/bin/activate
pip install -r requirements.txt
الأسباب الشائعة:
  • معرّف منتج غير صالح - تحقق من وجوده في لوحة Dodo
  • مفتاح API خاطئ أو إعداد بيئة خاطئ في .env
  • تحقق من سجلات FastAPI للحصول على رسائل خطأ مفصلة
للاختبار المحلي، استخدم ngrok لعرض الخادم الخاص بك:
ngrok http 8000
قم بتحديث عنوان URL الخاص بالـ webhook في لوحة Dodo إلى عنوان ngrok. تأكد من تحديث ملف .env بمفتاح التحقق الصحيح للـ webhook.
  • تأكد من تعيين DODO_PAYMENTS_WEBHOOK_KEY بشكل صحيح في ملف .env
  • تحقق من أنك تستخدم جسم الطلب الخام للتحقق من التوقيع
  • تأكد من قراءة رأس webhook-signature بشكل صحيح

تعرف على المزيد

الدعم

تحتاج إلى مساعدة بشأن النموذج؟