Дикий Григорий

Full-stack веб-разработчик

Интернет-магазин на Django. Интеграция Paypal. Часть 8

Сегодня речь пойдет о платежных системах. Все современные интернет-магазины предоставляют пользователю возможность оплатить товары посредством электронных денег. Наш онлайн-магазин тоже нуждается в такой функции. В этом уроке мы будем интегрировать Paypal. Эта платежная система является одной из самых популярных в мире, также у него богатое API, которое предоставляет широкий ассортимент опций.

Первым делом вам нужно зарегистрироваться в PayPal и создать бизнес-аккаунт.

Установим Paypal:

$ pip install django-paypal

Добавим его в используемые приложения в файле settings.py:

INSTALLED_APPS = [
    ...
    'orders',
    'paypal.standard.ipn'
]

Синхронизируемся с базой данных:

$ python manage.py migrate

Добавим настройки для paypal в settings.py:

# Paypal

PAYPAL_RECEIVER_EMAIL = 'dikiigr@gmail.com'
PAYPAL_TEST = True

PAYPAL_RECEIVER_EMAIL - хранит ваш email в paypal, в моем случае это dikiigr@gmail.com, а PAYPAL_TEST - сообщает, что это тестовые операции. Добавим urls для приложения paypal, для этого отредактируем главный urls.py из директории myshop:

urlpatterns = [
    ...
    url(r'^order/', include('orders.urls', namespace='orders')),
    url(r'^paypal/', include('paypal.standard.ipn.urls')),
    url(r'^', include('shop.urls', namespace='shop'))
]

Создадим новое приложение под названием payment задача которого помогать в оплате продуктов посредством онлайн-платежа.

$ python manage.py startapp payment

Подключим его к используемым приложениям в settigns.py:

INSTALLED_APPS = [
    ...
    'paypal.standard.ipn',
    'payment'
]

Изменим содержимое views.py нашего приложения orders следующим образом:

...
from django.shortcuts import render, redirect
from django.core.urlresolvers import reverse


def OrderCreate(request):
            ...
            # Асинхронная отправка сообщения
            OrderCreated.delay(order.id)
            request.session['order_id'] = order.id
            return redirect(reverse('payment:process'))

После успешного создания мы перенаправим на payment:process url. Так же мы записали в сессию наш идентификатор. Теперь нужно изменить файл views.py в директории payment:

from django.shortcuts import render, get_object_or_404
from decimal import Decimal
from django.conf import settings
from django.core.urlresolvers import reverse
from paypal.standard.forms import PayPalPaymentsForm
from orders.models import Order


# Create your views here.
def PaymentProcess(request):
    order_id = request.session.get('order_id')
    order = get_object_or_404(Order, id=order_id)
    host = request.get_host()

    paypal_dict = {
        'business': settings.PAYPAL_RECEIVER_EMAIL,
        'amount': '%.2f' % order.get_total_cost().guantize(Decimal('.01')),
        'item_name': 'Заказ {}'.format(order.id),
        'invoice': str(order.id),
        'currency_code': 'RUB',
        'notify_url': 'http://{}{}'.format(host, reverse('paypal-ipn')),
        'return_url': 'http://{}{}'.format(host, reverse('payment:done')),
        'cancel_return': 'http://{}{}'.format(host, reverse('payment:canceled'))
    }

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

Мы импортируем форму из пакета pypal и создаем словарь с данными где отображаем основную информацию о нашем заказе как указано в pypal руководстве.Теперь мы создали вьюху для заказа и нам надо добавить еще пару методов в эту вьюху при успешном заказ и также когда заказ отклонен.

...
from django.views.decorators.csrf import csrf_exempt


@csrf_exempt
def PaymentDone(request):
    return render(request, 'payment/done.html')

@csrf_exempt
def PaymentCanceled(request):
    return render(request, 'payment/canceled.html')

Декоратор @csrf_exemp нужен для того, чтобы не использовать CSRF от pypal. Теперь настала пора настроить роутер на эти шаблоны, для этого создадим и настроим наш urls.py в директории payments следующим образом:

from django.conf.urls import url
from . import views


urlpatterns = [
    url(r'^process/$', views.PaymentProcess, name='process'),
    url(r'^done/$', views.PaymentDone, name='done'),
    url(r'^canceled/$', views.PaymentCanceled, name='canceled')
]

Теперь добавим этот файл в главный urls.py нашего приложения в директории myshop:

url(r'^payment/', include('payment.urls', namespace='payment')),

И настала пора работы с нашими шаблонами. Первым делом давайте создадим следующую структуру файлов в директории payment:

templates/
    payment/
        process.html
        done.html
        canceled.html

Заполним файл process.html следующим содержимым:

{% extends "shop/base.html" %}

{% block title %}Оплата PayPal{% endblock %}

{% block content %}
  <h1>Оплата PayPal</h1>
  {{ form.render }}
{% endblock %}

Этот шаблон отображает нашу форму и добавляют кнопку оплаты. Теперь давайте заполним шаблон done.html следующим содержимым:

{% extends "shop/base.html" %}

{% block title %}Успешная оплата{% endblock %}

{% block content %}
  <h1>Успешная оплата</h1>
  <p>Благодарим вас за покупку!</p>
{% endblock %}

И наконец canceled.html:

{% extends "shop/base.html" %}

{% block title %}Оплата отклонена{% endblock %}

{% block content %}
  <h1>Оплата отклонена</h1>
  <p>Возникли проблемы с оплатой!</p>
{% endblock %}

PayPal Sandbox

PayPal SandBox - это среда для тестирования ваших переводов, причем деньги естественно нереальные. Она нужна для отладки приложений. Зайдите на сайт https://developer.paypal.com и нажмите на верхнем меню на вкладку Dashboard. После выберите с левой стороны вкладку Accounts ниже надписи SandBox и в итоге вы должны видеть примерно следующее:

Как видите я уже создал тестовый аккаунт dikiigr-facilitator-1@gmail.com простым копированием бизнес аккаунта. Теперь давайте запустим наш сервер:

$ python manage.py runserver
Теперь давайте опробуем оплату и сделаем заказ. После удачного заказа вы должны получить примерно следующее:


Нажав на кнопку возвращения на сайт вы перейдете на страницу со следующим содержанием:


Но так как мы используем локальный сервер, то мы не можем получать IPN сообщения от PayPal.

Получение сообщений об оплате

IPN - это метод предоставляемый PayPal для отслеживания вашей оплаты в реально времени. Благодаря ему вы можете получить статус вашего сообщения и оно содержит все детали вашего платежа. Django-paypal по умолчанию уже имеет два сигнала для IPN:

  • valid_ipn_received - когда произошла успешная оплата и все заполнено верно
  • invalid_ipn_recived - когда информация заполнена неверно

Создадим файл signals.py в директории payment со следующим содержимым:

from django.shortcuts import get_object_or_404
from paypal.standard.models import ST_PP_COMPLETED
from paypal.standard.ipn.signals import valid_ipn_received
from orders.models import Order


def PaymentNotification(sender, **kwargs):
    ipn_obj = sender
    if ipn_obj.payment_status == ST_PP_COMPLETED:
        order = get_object_or_404(Order, id=ipn_obj.invoice)
        order.paid = True
        order.save()

valid_ipn_received.connect(PaymentNotification)

Таким образом если операция произошла успешно, то мы помечаем у ордер поле paid True и сохраняем наш объект. Послу чего мы связываем valid_ipn_received c PaymentNotification.

Сконфигуригуем наше приложение, для этого изменим файл apps.py внутри директории payment со следующим содержанием:

from django.apps import AppConfig


class PaymentConfig(AppConfig):
    name = 'payment'
    verbose_name = 'Оплата'

    def ready(self):
        import payment.signals

Этим самым мы подключим сигналы импортирую их в функции ready которая выполнится когда приложение будет готово. Также нам нужно изменить файл __init__.py директории payment, чтобы указать что использовать в качестве файла конфигурации:

default_app_config = 'payment.apps.PaymentConfig'

Так как мы используем локальный сервер, то мы не можем быть уверены, что все работает правильно. Есть несколько способов сделать наш онлайн-магазин видимым в интернете, мы будем использовать Ngrok. Для этого перейдите по ссылке https://ngrok.com/download и скачайте его, затем можно его запустить через команду:

$ ./ngrok http 8000

Вы должны в итоге в консоли увидеть следующее:

Tunnel Status                 online                                           
Version                       2.1.3                                            
Region                        United States (us)                               
Web Interface                 http://127.0.0.1:4040                            
Forwarding                    http://c40ef6b3.ngrok.io -> localhost:8000       
Forwarding                    https://c40ef6b3.ngrok.io -> localhost:8000      
                                                                               
Connections                   ttl     opn     rt1     rt5     p50     p90      
                              0       0       0.00    0.00    0.00    0.00

Теперь зайдя по адресу http://c40ef6b3.ngrok.io/ вы увидите (вы получите другой адрес) наше приложение. Давайте сделаем заказ с этого адреса и посмотрим, что мы получим. У меня оплата была успешна и так как наш сайт доступен в интернете, то мы можем видеть в админке наш IPN номер:

Теперь мы успешно подключили наше приложение к платежной системе.

Репозиторий проекта: github