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 相关文章推荐
Python中捕捉详细异常信息的代码示例
Sep 18 Python
python通过正则查找微博@(at)用户的方法
Mar 13 Python
Python自定义线程类简单示例
Mar 23 Python
Pycharm 设置自定义背景颜色的图文教程
May 23 Python
Python pyinotify模块实现对文档的实时监控功能方法
Oct 13 Python
django小技巧之html模板中调用对象属性或对象的方法
Nov 30 Python
django与小程序实现登录验证功能的示例代码
Feb 19 Python
python 寻找离散序列极值点的方法
Jul 10 Python
基于keras输出中间层结果的2种实现方式
Jan 24 Python
python3环境搭建过程(利用Anaconda+pycharm)完整版
Aug 19 Python
Python Web项目Cherrypy使用方法镜像
Nov 05 Python
Selenium浏览器自动化如何上传文件
Apr 06 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上传图片之时间戳命名(保存路径)
2014/08/15 PHP
PHP通过curl获取接口URL的数据方法
2018/05/31 PHP
学习ExtJS(一) 之基础前提
2009/10/07 Javascript
用js实现输入提示(自动完成)的实例代码
2013/06/14 Javascript
Bootstrap组件系列之福利篇几款好用的组件(推荐)
2016/06/23 Javascript
Bootstrap组合上、下拉框简单实现代码
2017/03/06 Javascript
JavaScript实现隐藏省略文字效果的方法
2017/04/27 Javascript
javascript实现Java中的Map对象功能的实例详解
2017/08/21 Javascript
React Native验证码倒计时工具类分享
2017/10/24 Javascript
jQuery实现标签子元素的添加和赋值方法
2018/02/24 jQuery
Bootstrap的aria-label和aria-labelledby属性实例详解
2018/11/02 Javascript
微信小程序和百度的语音识别接口详解
2019/05/06 Javascript
Element MessageBox弹框的具体使用
2020/07/27 Javascript
基于vue--key值的特殊用处详解
2020/07/31 Javascript
[04:29]【TI9采访】OG.N0tail在胜者组决赛后接受采访
2019/08/25 DOTA
[48:48]完美世界DOTA2联赛PWL S3 Magama vs GXR 第一场 12.19
2020/12/24 DOTA
Python中的元类编程入门指引
2015/04/15 Python
好用的Python编辑器WingIDE的使用经验总结
2016/08/31 Python
Python实现识别手写数字 简易图片存储管理系统
2018/01/29 Python
python正则表达式之对号入座篇
2018/07/24 Python
Python从文件中读取指定的行以及在文件指定位置写入
2019/09/06 Python
Django Admin设置应用程序及模型顺序方法详解
2020/04/01 Python
Python基于wordcloud及jieba实现中国地图词云图
2020/06/09 Python
Python如何读写二进制数组数据
2020/08/01 Python
基于python调用jenkins-cli实现快速发布
2020/08/14 Python
Pandas直接读取sql脚本的方法
2021/01/21 Python
Ted Baker英国官网:男士和女士服装及配件
2017/03/13 全球购物
客服部班长工作责任制
2014/02/25 职场文书
我的未来不是梦演讲稿
2014/09/02 职场文书
公司党的群众路线教育实践活动领导班子对照检查材料
2014/09/25 职场文书
党的群众路线教育实践活动专题组织生活会发言材料
2014/10/17 职场文书
2015年志愿者服务工作总结
2015/04/20 职场文书
在职证明范本
2015/06/15 职场文书
毕业晚宴祝酒词
2015/08/11 职场文书
高二数学教学反思
2016/02/18 职场文书
如何写好闭幕词
2019/04/02 职场文书