前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >django 实现电子支付功能

django 实现电子支付功能

作者头像
希希里之海
发布2018-08-02 15:14:26
2.1K0
发布2018-08-02 15:14:26
举报
文章被收录于专栏:weixuqin 的专栏weixuqin 的专栏

  思路:调用第三方支付 API 接口实现支付功能。本来想用支付宝来实现第三方网站的支付功能的,但是在实际操作中发现支付宝没有 Python 接口,网上虽然有他人二次封装的的 Python 接口,但是对我这个小白白来说上手还是有点难度,后来发现 PayPal 有现成的 Django 模块,想着以学习的目的来实现这一功能(其实还是自己辣鸡),就决定以 PayPal 的电子支付功能来练手。

首先,安装 PayPal 的 Django 模块:django-paypal,具体介绍可以参考 GitHub上说明:https://github.com/spookylukey/django-paypal

代码语言:javascript
复制
pip install django-paypal

然后在 settings.py 中的 INSTALLED_APPS 将 'paypal.standard.ipn' 加入。并在 settings.py 中添加下列语句。

代码语言:javascript
复制
# 此付款机制作为测试用
PAYPAL_TEST = True
# 设置收款的 PayPal 电子邮件账户
PAYPAL_REVEIVER_EMAIL = 'your email'

执行同步数据库操作。

代码语言:javascript
复制
./manage.py migrate

urls.py 中加入下列样式。分别为付款完成通知,处理账务,显示完成付款,取消付款操作。

代码语言:javascript
复制
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 付款操作,建立含有正确数据的付款按钮。

代码语言:javascript
复制
@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,语句如下:

代码语言:javascript
复制
from django.conf import settings
from paypal.standard.forms import PayPalPaymentsForm
from django.core.urlresolvers import reverse

付款完成。

代码语言:javascript
复制
@csrf_exempt    #csrf 验证
def payment_done(request):
    template = get_template('payment_done.html')
    html = template.render(context=locals(), request=request)
    return HttpResponse(html)

取消付款。

代码语言:javascript
复制
@csrf_exempt
def payment_canceled(request):
    template = get_template('payment_canceled.html')
    html = template.render(context=locals(), request=request)
    return HttpResponse(html)

PayPal 付款页面。

代码语言:javascript
复制
<!-- 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 %}

付款完成页面。

代码语言:javascript
复制
<!-- 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 %}

取消付款页面。

代码语言:javascript
复制
<!-- 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 文件。

代码语言:javascript
复制
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 的文件,确保上述编写的函数在一开始的时候就能够加载。

代码语言:javascript
复制
from django.apps import AppConfig


class PaymentConfig(AppConfig):
    name = 'mysite'
    verbose_name = 'Mysite'

    def ready(self):
        import mysite.signal

在同一文件夹下的 __init__.py 中加入以下语句,确保我们在应用程序初始化加载的时候,可以把我们自定义的应用程序环境设置成能够加载自定义的工作。

代码语言:javascript
复制
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/ 下载解压文件并关联账号后,运行下列命令。

代码语言:javascript
复制
./ngrok http 8000

这个命令将在 8000 端口为本地主机创建一个通道并为其设置一个网络可以访问的主机名称,得到以下输出:

我们可以通过访问 Forwarding 中的网址来连接我们构建在本地的网站。

然后付款后便能在自己本地网站的后台管理看到 paypal ipn 的信息,我这里显示的状态是 pending,按理来说应该是 completed ,可能 paypal 设置中需要更改,这样的话需要将 signal.py 中 ST_PP_COMPLETED 修改为 ST_PP_PENDING,这样 signal.py 便能正常处理 paypal 返回的信息,将订单状态更改为已完成。

至此,我们便完成了调用 paypal 实现第三方网站支付的功能。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018-07-24 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档