Django集成PayPal实现第三方支付功能完整教程
本文详解如何在Django项目中集成PayPal实现第三方支付功能。涵盖django-paypal模块安装、settings配置、URL路由设置及视图逻辑编写。重点讲解PayPal沙盒环境搭建、测试账号创建、Ngrok内网穿透处理IPN通知,以及信号监听与订单状态更新机制,帮助开发者快速完成支付功能落地。
实现思路:通过调用第三方支付API接口来完成支付功能。起初考虑使用支付宝,但发现其官方未提供Python接口,尽管网上有二次封装的库,但对于初学者而言上手难度较大。随后发现PayPal提供了现成的Django模块,出于学习目的,决定利用PayPal的电子支付功能进行实践。
第一步,安装PayPal的Django模块:django-paypal。具体使用说明可参考GitHub官方文档:https://github.com/spookylukey/django-paypal
pip install django-paypal
其次,在settings.py文件的INSTALLED_APPS列表中添加'paypal.standard.ipn'。此外,还需在settings.py中添加以下配置语句。
# 此付款机制作为测试用 PAYPAL_TEST = True # 设置收款的 PayPal 电子邮件账户 PAYPAL_REVEIVER_EMAIL = 'your email'
执行数据库同步操作,确保表结构更新。
./manage.py migrate
在urls.py文件中添加相应的URL配置,分别对应付款完成通知、账务处理、显示付款完成页面以及取消付款操作。
url(r'^paypal/', include('paypal.standard.ipn.urls')), # 付款完成通知
url(r'^payment/(\d+)/$', views.payment),
url(r'^done/$', views.payment_done),
url(r'^canceled/$', views.payment_canceled),
配置PayPal付款流程,构建包含正确数据的付款按钮。
@login_required
def payment(request, order_id):
all_categories = models.Category.objects.all()
try:
order = models.Order.objects.get(id=order_id)
except:
messages.add_message(request, messages.WARNING, "订单编号错误,无法处理付款。")
return redirect('/myorders/')
all_order_items = models.OrderItem.objects.filter(order=order)
items = list()
total = 0
for order_item in all_order_items:
t = dict()
t['name'] = order_item.product.name
t['price'] = order_item.product.price
t['quantity'] = order_item.quantity
t['subtotal'] = order_item.product.price * order_item.quantity
total = total + order_item.product.price
items.append(t)
host = request.get_host()
paypal_dict = {
"business": settings.PAYPAL_REVEIVER_EMAIL,
"amount": total,
"item_name": "迷你小电商商品编号:{}".format(order_id),
"invoice": "invoice-{}".format(order_id),
"currency_code": 'CNY',
"notify_url": "http://{}{}".format(host, reverse('paypal-ipn')),
"return_url": "http://{}/done/".format(host),
"cancel_return": "http://{}/canceled/".format(host),
}
paypal_form = PayPalPaymentsForm(initial=paypal_dict)
template = get_template('payment.html')
html = template.render(context=locals(), request=request)
return HttpResponse(html)
由于使用了django-paypal提供的PayPalPaymentForm类,因此需要在views.py中导入该类。同时,因为引用了settings.py中的常量,还需导入settings模块,具体语句如下:
from django.conf import settings from paypal.standard.forms import PayPalPaymentsForm from django.core.urlresolvers import reverse
付款流程结束后的处理。
@csrf_exempt #csrf 验证
def payment_done(request):
template = get_template('payment_done.html')
html = template.render(context=locals(), request=request)
return HttpResponse(html)
取消付款流程的处理。
@csrf_exempt
def payment_canceled(request):
template = get_template('payment_canceled.html')
html = template.render(context=locals(), request=request)
return HttpResponse(html)
PayPal付款界面展示。
<!-- payment.html (mshop project) -->
{% extends "base.html" %}
{% block title %}选择您的付款方式{% endblock %}
{% block content %}
<div class='container'>
{% for message in messages %}
<div class='alert alert-{{message.tags}}'>{{ message }}</div>
{% endfor %}
<div class='row'>
<div class='col-md-12'>
<div class='panel panel-default'>
<div class='panel-heading' align=center>
<h3>欢迎光临迷你小电商</h3>
{% if user.socialaccount_set.all.0.extra_data.name %}
{{user.socialaccount_set.all.0.extra_data.name}}<br/>
<img src='{{user.socialaccount_set.all.0.get_avatar_url}}' width='100'>
{% else %}
Welcome: {{ user.username }}
{% endif %}
</div>
</div>
</div>
</div>
<div class='row'>
<div class='col-sm-12'>
<div class='panel panel-info'>
<div class='panel panel-heading'>
<h4>在线付款(订单编号:{{order.id}})</h4>
</div>
<div class='panel panel-body'>
{% for item in items %}
{% if forloop.first %}
<table border=1>
<tr>
<td width=300 align=center>产品名称</td>
<td width=100 align=center>单价</td>
<td width=100 align=center>数量</td>
<td width=100 align=center>小计</td>
</tr>
{% endif %}
<div class='listgroup'>
<div class='listgroup-item'>
<tr>
<td>{{ item.name }}</td>
<td align=right>{{ item.price }}</td>
<td align=center>{{ item.quantity }}</td>
<td align=right>{{ item.subtotal }}</td>
</tr>
</div>
</div>
{% if forloop.last %}
</table>
{% endif %}
{% empty %}
<em>此订单是空的</em>
{% endfor %}
{{ paypal_form.render }}
</div>
<div class='panel panel-footer'>
NT$:{{ total }}元
</div>
</div>
</div>
</div>
</div>
{% endblock %}
付款成功页面展示。
<!-- payment_done.html (mshop project) -->
{% extends "base.html" %}
{% block title %}Pay using PayPal{% endblock %}
{% block content %}
<div class='container'>
{% for message in messages %}
<div class='alert alert-{{message.tags}}'>{{ message }}</div>
{% endfor %}
<div class='row'>
<div class='col-md-12'>
<div class='panel panel-default'>
<div class='panel-heading' align=center>
<h3>欢迎光临迷你小电商</h3>
{% if user.socialaccount_set.all.0.extra_data.name %}
{{user.socialaccount_set.all.0.extra_data.name}}<br/>
<img src='{{user.socialaccount_set.all.0.get_avatar_url}}' width='100'>
{% else %}
Welcome: {{ user.username }}
{% endif %}
</div>
</div>
</div>
</div>
<div class='row'>
<div class='col-sm-12'>
<div class='panel panel-info'>
<div class='panel panel-heading'>
<h4>从PayPal付款成功</h4>
</div>
<div class='panel panel-body'>
感谢您的支持,我们会尽快处理您的订单。
</div>
<div class='panel panel-footer'>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
取消付款页面展示。
<!-- payment_canceled.html (mshop project) -->
{% extends "base.html" %}
{% block title %}PayPal 付款取消通知{% endblock %}
{% block content %}
<div class='container'>
{% for message in messages %}
<div class='alert alert-{{message.tags}}'>{{ message }}</div>
{% endfor %}
<div class='row'>
<div class='col-md-12'>
<div class='panel panel-default'>
<div class='panel-heading' align=center>
<h3>欢迎光临迷你小电商</h3>
{% if user.socialaccount_set.all.0.extra_data.name %}
{{user.socialaccount_set.all.0.extra_data.name}}<br/>
<img src='{{user.socialaccount_set.all.0.get_avatar_url}}' width='100'>
{% else %}
Welcome: {{ user.username }}
{% endif %}
</div>
</div>
</div>
</div>
<div class='row'>
<div class='col-sm-12'>
<div class='panel panel-info'>
<div class='panel panel-heading'>
<h4>您刚刚取消了PayPal的付款</h4>
</div>
<div class='panel panel-body'>
<p>请再次检查您的付款,或是返回<a href='/myorders/'>我的订单</a>选用其它付款方式。</p>
</div>
<div class='panel panel-footer'>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
PayPal在处理完在线支付流程后,会向网站发送额外的HTTP数据。我们需要编写一个处理该信号的函数,以更新数据库中的订单状态。为了确保监听函数能在系统启动时被加载并保持运行,需在views.py同级目录下创建一个名为signal.py的文件。
from mysite import models
from paypal.standard.models import ST_PP_COMPLETED
from paypal.standard.ipn.signals import valid_ipn_received
def payment_notfication(sender, **kwargs):
ipn_obj = sender
if ipn_obj.payment_status == ST_PP_COMPLETED:
order_id = ipn_obj.invocie.split('-')[-1]
order = models.Order.objects.get(id = order_id)
order_id.paid = True
order.save()
valid_ipn_received.connect(payment_notfication)
在同一文件夹下再创建一个名为apps.py的文件,以确保上述编写的函数在应用初始化时能够被正确加载。
from django.apps import AppConfig class PaymentConfig(AppConfig): name = 'mysite' verbose_name = 'Mysite' def ready(self): import mysite.signal
在__init__.py文件中加入以下语句,确保在应用程序初始化加载时,能将自定义的应用程序环境设置为能够加载自定义的工作。
default_app_config = 'mysite.apps.PaymentConfig'
经过上述配置,网站已能正确接收订单并使用PayPal进行付款。我们可以在PayPal开发者网站(https://developer.paypal.com/)申请一个测试账号来进行付款测试。
进入Dashboard界面,点击Sandbox下的Account选项,在此处可以创建一个测试账号。

点击创建账号下的Profile选项,进入详情页,设置该账号的密码,并将Payment Review功能设置为Off。

接下来,便可以在网站中使用该测试账号进行付款。点击前往付款,调用payment函数,加载包含正确数据的付款按钮,点击后跳转至PayPal的沙盒付款页面。在其中填入之前建立的测试账号信息,登录后即可完成付款。

付款成功后,页面将返回至之前编写的付款成功页面。

注意:中国大陆的PayPal账号无法用于测试实际支付,必须使用大陆以外的PayPal账户才能进行测试。(这确实是一个常见的坑。)
否则,在付款时会出现如下界面提示。

至此,付款流程看似已成功,但PayPal无法将支付状态通知发送到我们的应用,这是因为项目运行在外部无法访问的127.0.0.1上。为了解决这个问题,我们使用Ngrok来实现对开发环境的互联网访问。
在Ngrok官网https://ngrok.com/下载解压文件并关联账号后,运行以下命令。
./ngrok http 8000
该命令将在8000端口为本地主机创建一个通道,并为其分配一个可被网络访问的主机名,得到如下输出结果:

我们可以通过访问Forwarding中的网址来连接构建在本地的网站。
随后,在本地网站的后台管理中即可看到PayPal IPN的信息。此处显示的状态为pending,理论上应为completed。这可能需要调整PayPal的设置。若遇到此情况,需将signal.py中的ST_PP_COMPLETED修改为ST_PP_PENDING,这样signal.py便能正常处理PayPal返回的信息,并将订单状态更改为已完成。

至此,我们完成了调用PayPal实现第三方网站支付的功能。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持本站。
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













