You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Django电商项目中实现PayPal支付登录弹窗功能

Implement PayPal Payment in Your Django E-Commerce Project

Let’s walk through how to get the PayPal login window to open when users select the PayPal radio button on your checkout page, while saving their shipping/billing addresses first. Here’s a step-by-step solution tailored to your existing code:


1. Update Your Payment Model

First, add a field to track PayPal transactions in your models.py:

class Payment(models.Model):
    stripe_charge_id = models.CharField(max_length=50, blank=True, null=True)
    paypal_transaction_id = models.CharField(max_length=50, blank=True, null=True)  # New field
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, blank=True, null=True)
    amount = models.FloatField()
    timestamp = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.user.username if self.user else "Anonymous Payment"

Run migrations to apply this change:

python manage.py makemigrations
python manage.py migrate

2. Add PayPal Credentials to Settings

Add your PayPal sandbox (or live) credentials to settings.py:

# PayPal Settings (use live credentials for production)
PAYPAL_CLIENT_ID = "your-sandbox-client-id"
PAYPAL_SECRET = "your-sandbox-secret"
PAYPAL_CURRENCY = "USD"  # Adjust to your project's currency

You can get these credentials from the PayPal Developer Dashboard.

3. Create PayPal API Views

Add these views to views.py to handle creating and capturing PayPal orders:

import json
import requests
from django.http import JsonResponse
from django.conf import settings
from django.views import View
from .models import Order, Payment

class PayPalCreateOrderView(View):
    def post(self, *args, **kwargs):
        try:
            order = Order.objects.get(user=self.request.user, ordered=False)
        except Order.DoesNotExist:
            return JsonResponse({"error": "No active order found"}, status=400)

        # Get PayPal access token
        def get_access_token():
            url = "https://api-m.sandbox.paypal.com/v1/oauth2/token"
            payload = "grant_type=client_credentials"
            headers = {"Content-Type": "application/x-www-form-urlencoded"}
            response = requests.post(
                url,
                data=payload,
                headers=headers,
                auth=(settings.PAYPAL_CLIENT_ID, settings.PAYPAL_SECRET)
            )
            return response.json()["access_token"]

        # Create PayPal order
        access_token = get_access_token()
        url = "https://api-m.sandbox.paypal.com/v2/checkout/orders"
        headers = {
            "Content-Type": "application/json",
            "Authorization": f"Bearer {access_token}"
        }
        payload = {
            "intent": "CAPTURE",
            "purchase_units": [
                {
                    "amount": {
                        "currency_code": settings.PAYPAL_CURRENCY,
                        "value": str(order.get_total())  # Ensure your Order model has this method
                    }
                }
            ]
        }
        response = requests.post(url, json=payload, headers=headers)
        return JsonResponse(response.json())

class PayPalCaptureOrderView(View):
    def post(self, *args, **kwargs):
        try:
            order = Order.objects.get(user=self.request.user, ordered=False)
        except Order.DoesNotExist:
            return JsonResponse({"error": "No active order found"}, status=400)

        data = json.loads(self.request.body)
        order_id = data.get("order_id")

        # Get PayPal access token
        def get_access_token():
            url = "https://api-m.sandbox.paypal.com/v1/oauth2/token"
            payload = "grant_type=client_credentials"
            headers = {"Content-Type": "application/x-www-form-urlencoded"}
            response = requests.post(
                url,
                data=payload,
                headers=headers,
                auth=(settings.PAYPAL_CLIENT_ID, settings.PAYPAL_SECRET)
            )
            return response.json()["access_token"]

        # Capture PayPal order
        access_token = get_access_token()
        url = f"https://api-m.sandbox.paypal.com/v2/checkout/orders/{order_id}/capture"
        headers = {
            "Content-Type": "application/json",
            "Authorization": f"Bearer {access_token}"
        }
        response = requests.post(url, headers=headers)
        response_data = response.json()

        if response_data["status"] == "COMPLETED":
            # Save payment record and mark order as completed
            payment = Payment.objects.create(
                user=self.request.user,
                amount=order.get_total(),
                paypal_transaction_id=response_data["id"]
            )
            order.ordered = True
            order.payment = payment
            order.save()

            return JsonResponse({"status": "success", "message": "Payment completed!"})
        else:
            return JsonResponse({"status": "error", "message": "Payment failed"}, status=400)

4. Modify CheckoutView for AJAX Support

Update your CheckoutView to handle AJAX requests when PayPal is selected:

class CheckoutView(View):
    # ... (keep your existing get method)

    def post(self, *args, **kwargs):
        form = CheckoutForm(self.request.POST or None)
        try:
            order = Order.objects.get(user=self.request.user, ordered=False)
            if form.is_valid():
                # ... (keep all your existing address saving logic here)

                payment_option = form.cleaned_data.get("payment_option")
                if payment_option == "S":
                    return redirect("core:payment", payment_option="stripe")
                elif payment_option == "P":
                    # Handle AJAX request for PayPal
                    if self.request.headers.get("X-Requested-With") == "XMLHttpRequest":
                        return JsonResponse({"status": "success", "message": "Addresses saved"})
                    else:
                        return redirect("core:payment", payment_option="paypal")
                else:
                    messages.warning(self.request, "Invalid payment option selected")
                    return redirect("core:checkout")
        except ObjectDoesNotExist:
            messages.warning(self.request, "You do not have an active order")
            return redirect("core:order-summary")

5. Update Checkout Template to Trigger PayPal Window

Modify your checkout.html to intercept form submission for PayPal and load the PayPal SDK:

<form id="checkout-form" method="post">
    {% csrf_token %}
    <!-- ... (keep all your existing form fields) -->

    <div class="form-group">
        <label>Payment Option</label>
        {% for choice in form.payment_option %}
            <div class="form-check">
                {{ choice.tag }}
                <label class="form-check-label" for="{{ choice.id_for_label }}">{{ choice.choice_label }}</label>
            </div>
        {% endfor %}
    </div>

    <button type="submit" class="btn btn-primary">Proceed to Payment</button>
</form>

<!-- Hidden PayPal button container -->
<div id="paypal-button-container" style="display: none; margin-top: 20px;"></div>

<script src="https://www.paypal.com/sdk/js?client-id={{ settings.PAYPAL_CLIENT_ID }}&currency={{ settings.PAYPAL_CURRENCY }}"></script>
<script>
    document.getElementById('checkout-form').addEventListener('submit', function(e) {
        const selectedPayment = document.querySelector('input[name="payment_option"]:checked').value;
        
        // Handle PayPal submission via AJAX
        if (selectedPayment === 'P') {
            e.preventDefault();
            
            // Submit form to save addresses
            fetch('{% url "core:checkout" %}', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                    'X-CSRFToken': '{{ csrf_token }}'
                },
                body: JSON.stringify(Object.fromEntries(new FormData(this)))
            })
            .then(response => response.json())
            .then(data => {
                if (data.status === 'success') {
                    // Show PayPal button and trigger it
                    document.getElementById('paypal-button-container').style.display = 'block';
                    
                    paypal.Buttons({
                        createOrder: (data, actions) => {
                            return fetch('{% url "core:paypal-create-order" %}', {
                                method: 'POST',
                                headers: {
                                    'Content-Type': 'application/json',
                                    'X-CSRFToken': '{{ csrf_token }}'
                                }
                            })
                            .then(response => response.json())
                            .then(orderData => orderData.id);
                        },
                        onApprove: (data, actions) => {
                            return fetch('{% url "core:paypal-capture-order" %}', {
                                method: 'POST',
                                headers: {
                                    'Content-Type': 'application/json',
                                    'X-CSRFToken': '{{ csrf_token }}'
                                },
                                body: JSON.stringify({order_id: data.orderID})
                            })
                            .then(response => response.json())
                            .then(result => {
                                alert(result.message);
                                window.location.href = '{% url "core:order-confirmation" %}'; // Redirect to confirmation page
                            });
                        },
                        onError: err => {
                            console.error(err);
                            alert('An error occurred. Please try again later.');
                        }
                    }).render('#paypal-button-container');
                } else {
                    alert('Failed to save your address. Please check your input.');
                }
            })
            .catch(err => {
                console.error(err);
                alert('Something went wrong. Please try again.');
            });
        }
        // Stripe submission proceeds normally via redirect
    });
</script>

6. Add URLs for PayPal Views

Update your urls.py to include the new PayPal endpoints:

from django.urls import path
from .views import (
    CheckoutView,
    PayPalCreateOrderView,
    PayPalCaptureOrderView,
    # ... other views
)

urlpatterns = [
    path('checkout/', CheckoutView.as_view(), name='checkout'),
    path('paypal/create-order/', PayPalCreateOrderView.as_view(), name='paypal-create-order'),
    path('paypal/capture-order/', PayPalCaptureOrderView.as_view(), name='paypal-capture-order'),
    # ... other URLs
]

Key Notes

  • Sandbox vs Live: Use sandbox credentials for testing, then switch to live credentials when you’re ready to launch.
  • Error Handling: Expand the error handling in views/templates to cover edge cases (e.g., expired orders, failed API calls).
  • Order Confirmation: Create an order-confirmation view to show users their successful order details.
  • Dependencies: Install the requests package if you haven’t already:
    pip install requests
    

内容的提问来源于stack exchange,提问作者A_K

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.09 12:32:52