Zeyad Shaban
Zeyad Shaban

Reputation: 1016

Users can't pay each other django-paypal

I have an online store where users can pay each other to buy things, I have been testing it with sandbox accounts but I don't think it's working. I really can't get where the issue is

Here is my views.py:

def payment_process(request, trade_id):
    trade = get_object_or_404(Trade, id=trade_id)
    host = request.get_host()

    paypal_dict = {
        'business': trade.seller.email, 
        'amount': Decimal(trade.price),
        'item_name': trade.filename,
        'invoice': str(trade.id),
        'currency_code': 'USD',
        'notify_url': 'https://{}{}'.format(host,
                                           reverse('paypal-ipn')),
        'return_url': 'https://{}{}/{}'.format(host,
                                           *reverse('payment_done', kwargs={'trade_id': trade.id})),
        'cancel_return': 'https://{}{}'.format(host,
                                              reverse('home')),
    }

    form = PayPalPaymentsForm(initial=paypal_dict)
    return render(request, 'payment/payment_process.html', {'trade': trade, 'form': form})


@csrf_exempt
def payment_done(request, trade_id):
    # Do some very important stuff after paying ...
    # It would be really nice if someone can help me with a checker
    messages.success(request, 'Your product is in your inbox now')
    return redirect('trade:inbox')

My urls.py:

urlpatterns = [
    path('admin/', admin.site.urls),
    ...
    # Prodbox Payment
    path('payment/process/<int:trade_id>/', payment_views.payment_process, name="payment_process"),
    path('payment/done/<int:trade_id>/', payment_views.payment_done, name="payment_done"),
    # Prodbox packages
    path('paypal/', include('paypal.standard.ipn.urls')),
]

The template which handles paying:

{% extends 'users/base.html' %}

{% block title %}Payment Process | Prodbox {% endblock title %}

{% block content %}
<div class="container row justify-content-center">
    <div class="shadow-lg p-3 mb-5 col-md-8 bg-white rounded m-4 p-4">
        <section>
            <p>Seller: {{ trade.seller.email }}</p>
            <p>Product: {{ trade.thing }}</p>
            <p style="color: #2ecc71;">Price: ${{ trade.price }}</p>
        </section>
        <section>
            <h4>Pay with PayPal</h4>
            {{ form.render }}
        </section>
    </div>
</div>
{% endblock content %}

It's very very important to redirect users to payment_done view after finish paying, (it would be nice if I had a checker to check if the payment is completed before running done function)

Also, note that I'm emphasizing on users to use their PayPal email account

So why it's not working?!

Extra information (may not help)

User model:

from django.db import models
from django.contrib.auth.models import AbstractUser, BaseUserManager


class CustomUserManager(BaseUserManager):
    """
    Custom user model manager where email is the unique identifiers
    for authentication instead of usernames.
    """

    def create_user(self, email, password, **extra_fields):
        """
        Create and save a User with the given email and password.
        """
        if not email:
            raise ValueError(_('The Email must be set'))
        email = self.normalize_email(email)
        user = self.model(email=email, **extra_fields)
        user.set_password(password)
        user.save()
        return user

    def create_superuser(self, email, password, **extra_fields):
        """
        Create and save a SuperUser with the given email and password.
        """
        extra_fields.setdefault('is_staff', True)
        extra_fields.setdefault('is_superuser', True)
        extra_fields.setdefault('is_active', True)

        if extra_fields.get('is_staff') is not True:
            raise ValueError(_('Superuser must have is_staff=True.'))
        if extra_fields.get('is_superuser') is not True:
            raise ValueError(_('Superuser must have is_superuser=True.'))
        return self.create_user(email, password, **extra_fields)


class User(AbstractUser):
    username = None
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)
    email = models.EmailField(('email address'), unique=True)
    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = []
    objects = CustomUserManager()

    def username(self):
        return f'{self.first_name} {self.last_name}'
    
    def __str__(self):
        return self.email

settings.py:

AUTH_USER_MODEL = 'users.User'
PAYPAL_RECEIVER_EMAIL = '[email protected]'

PAYPAL_TEST = False

local_settings.py:

DEBUG = True
ALLOWED_HOSTS = []
PAYPAL_RECEIVER_EMAIL = '[email protected]'
PAYPAL_TEST = True

Upvotes: 0

Views: 499

Answers (1)

Preston PHX
Preston PHX

Reputation: 30377

You say it's not working yet provide no information about the problem behavior and why it's not working.

But I suppose that doesn't matter too much, since you're using a bad integration (django-paypal, based on payments standard) for the desired behavior you want of it being "very very important" that the payer return.


The integration you should switch to is to integrate v2/checkout/orders, with or without the Checkout-Python-SDK. You will need two routes on your server, one for 'Set Up Transaction' and one for 'Capture Transaction', documented here: https://developer.paypal.com/docs/checkout/reference/server-integration/

The best front-end UI to use for approval is here: https://developer.paypal.com/demo/checkout/#/pattern/server . Get this working well in a standalone HTML file that calls your 2 django backend routes (via fetch), before you then integrate it as a front-end into your django templates and checkout flow.

For the feature of a user paying another user, use the payee object, documented here: https://developer.paypal.com/docs/checkout/integration-features/pay-another-account/

Upvotes: 1

Related Questions