django 实现电子支付功能的示例代码


Posted in Python onJuly 25, 2018

思路:调用第三方支付 API 接口实现支付功能。本来想用支付宝来实现第三方网站的支付功能的,但是在实际操作中发现支付宝没有 Python 接口,网上虽然有他人二次封装的的 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(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 选项,我们可以在此创建一个测试账号。

django 实现电子支付功能的示例代码

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

django 实现电子支付功能的示例代码

接下来我们便可以在我们的网站中使用这个测试账号付款了,点击前往付款,调用 payment 函数,加载含有正确数据的付款按钮,点击后便跳转到 paypal 的沙盒付款页面,我们在其中填入我们之前建立好的测试账号信息,登录后便可以付款了。

django 实现电子支付功能的示例代码

付款成功后便返回我们之前编写好的付款成功页面。

django 实现电子支付功能的示例代码

注意:中国大陆的 paypal 账号不能用来测试实际支付,需要大陆以外的 paypal 账户才可测试实际支付。(真是坑。。。)

不然付款的时候会出现下列界面。

django 实现电子支付功能的示例代码

到这里,我们的付款便已经成功了,但是 PayPal 无法将支付状态通知发送到我们的应用,这是由于我们的项目运行在外部无法访问的 127.0.0.1 上。我们使用 Ngrok 来实现因特网访问开发环境。

在 Ngrok 官网  https://ngrok.com/ 下载解压文件并关联账号后,运行下列命令。

./ngrok http 8000

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

django 实现电子支付功能的示例代码

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

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

django 实现电子支付功能的示例代码

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

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
Mac OS X10.9安装的Python2.7升级Python3.3步骤详解
Dec 04 Python
python实现保存网页到本地示例
Mar 16 Python
python基础教程之自定义函数介绍
Aug 29 Python
利用Celery实现Django博客PV统计功能详解
May 08 Python
python万年历实现代码 含运行结果
May 20 Python
python2.7到3.x迁移指南
Feb 01 Python
Python代码缩进和测试模块示例详解
May 07 Python
对Pyhon实现静态变量全局变量的方法详解
Jan 11 Python
pyqt5 使用label控件实时显示时间的实例
Jun 14 Python
Django 实现将图片转为Base64,然后使用json传输
Mar 27 Python
了解一下python内建模块collections
Sep 07 Python
基于Python实现nc批量转tif格式
Aug 14 Python
python 去除txt文本中的空格、数字、特定字母等方法
Jul 24 #Python
Python将文本去空格并保存到txt文件中的实例
Jul 24 #Python
python批量修改图片大小的方法
Jul 24 #Python
python 自动去除空行的实例
Jul 24 #Python
python读取图片并修改格式与大小的方法
Jul 24 #Python
Flask模拟实现CSRF攻击的方法
Jul 24 #Python
Python全排列操作实例分析
Jul 24 #Python
You might like
PHP define函数的使用说明
2008/08/27 PHP
比较strtr, str_replace和preg_replace三个函数的效率
2013/06/26 PHP
PHP删除数组中空值的方法介绍
2014/04/14 PHP
PHP生成RSS文件类实例
2014/12/05 PHP
PHP随机生成唯一HASH值自定义函数
2015/04/20 PHP
静态页面的值传递(三部曲)
2006/09/25 Javascript
JQuery的一些小应用收集
2010/03/27 Javascript
需要做特殊处理的DOM元素属性的访问
2010/11/05 Javascript
用nodejs访问ActiveX对象,以操作Access数据库为例。
2011/12/15 NodeJs
javascript事件冒泡详解和捕获、阻止方法
2014/04/12 Javascript
jquery ajax应用中iframe自适应高度问题解决方法
2014/04/12 Javascript
Node.js安装教程和NPM包管理器使用详解
2014/08/16 Javascript
js Calender控件使用详解
2015/01/05 Javascript
基于jQuery Bar Indicator 插件实现进度条展示效果
2015/09/30 Javascript
javascript跨域的方法汇总
2015/10/23 Javascript
基于javascript实现彩票随机数生成(简单版)
2020/04/17 Javascript
jQuery+CSS3文字跑马灯特效的简单实现
2016/06/25 Javascript
Javascript表单特效之十大常用原理性样例代码大总结
2016/07/12 Javascript
值得分享的JavaScript实现图片轮播组件
2016/11/21 Javascript
Vue源码学习之初始化模块init.js解析
2017/11/02 Javascript
基于vue.js 2.x的虚拟滚动条的示例代码
2018/01/23 Javascript
Node.js使用cookie保持登录的方法
2018/05/11 Javascript
如何给element添加一个抽屉组件的方法步骤
2019/07/14 Javascript
[01:10:16]DOTA2上海特级锦标赛B组资格赛#2 Fnatic VS Spirit第一局
2016/02/27 DOTA
Python模块学习 datetime介绍
2012/08/27 Python
Python中random模块用法实例分析
2015/05/19 Python
python使用arcpy.mapping模块批量出图
2017/03/06 Python
使用Python实现微信提醒备忘录功能
2018/12/04 Python
如何使用Python处理HDF格式数据及可视化问题
2020/06/24 Python
介绍一下JNDI的基本概念
2013/07/26 面试题
会计毕业自我鉴定
2014/02/05 职场文书
优秀医生事迹材料
2014/02/12 职场文书
歌唱比赛主持词
2014/03/18 职场文书
赔偿协议书范本
2014/09/12 职场文书
毕业纪念册寄语大全
2015/02/26 职场文书
python实现简易自习室座位预约系统
2021/06/30 Python