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编写简单的微博爬虫
Mar 04 Python
Python爬取qq music中的音乐url及批量下载
Mar 23 Python
Python用Pillow(PIL)进行简单的图像操作方法
Jul 07 Python
python清理子进程机制剖析
Nov 23 Python
django认证系统实现自定义权限管理的方法
Jul 16 Python
关于python下cv.waitKey无响应的原因及解决方法
Jan 10 Python
python多任务及返回值的处理方法
Jan 22 Python
python 实现手机自动拨打电话的方法(通话压力测试)
Aug 08 Python
详解Python并发编程之创建多线程的几种方法
Aug 23 Python
Python 列表的清空方式
Jan 13 Python
python读取当前目录下的CSV文件数据
Mar 11 Python
PyCharm配置anaconda环境的步骤详解
Jul 31 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
第二节--PHP5 的对象模型
2006/11/16 PHP
php上传图片存入数据库示例分享
2014/03/11 PHP
php使用PDO操作MySQL数据库实例
2014/12/30 PHP
PHP实现断点续传乱序合并文件的方法
2018/09/06 PHP
Ajax,UTF-8还是GB2312 eval 还是execScript
2008/11/13 Javascript
JQuery 插件模板 制作jquery插件的朋友可以参考下
2010/03/17 Javascript
鼠标滑在标题上显示图片的JS代码
2013/11/19 Javascript
JavaScript调用浏览器打印功能实例分析
2015/07/17 Javascript
JavaScript注入漏洞的原理及防范(详解)
2016/12/04 Javascript
JavaScript中利用构造器函数模拟类的方法
2017/02/16 Javascript
ES6学习教程之Map的常用方法总结
2017/08/03 Javascript
微信小程序之分享页面如何返回首页的示例
2018/03/28 Javascript
mpvue全局引入sass文件的方法步骤
2019/03/06 Javascript
vue router 组件的高级应用实例代码
2019/04/08 Javascript
微信小程序 wepy框架与iview-weapp的用法详解
2019/04/10 Javascript
layer.open回调获取弹出层参数的实现方法
2019/09/10 Javascript
[03:28]2014DOTA2国际邀请赛 EG战队官方纪录片
2014/07/21 DOTA
[02:28]DOTA2亚洲邀请赛附加赛 RECAP赛事回顾
2015/01/29 DOTA
用Python代码来绘制彭罗斯点阵的教程
2015/04/03 Python
R vs. Python 数据分析中谁与争锋?
2017/10/18 Python
Tornado高并发处理方法实例代码
2018/01/15 Python
对DataFrame数据中的重复行,利用groupby累加合并的方法详解
2019/01/30 Python
python async with和async for的使用
2019/06/20 Python
python判断正负数方式
2020/06/03 Python
利用css3径向渐变做一张优惠券的示例
2018/03/22 HTML / CSS
Bjorn Borg官方网上商店:国际运动时尚品牌
2016/08/27 全球购物
奥地利网上书店:Weltbild
2017/07/14 全球购物
CK加拿大官网:Calvin Klein加拿大
2020/03/14 全球购物
工商管理专业实习大学生自我鉴定
2013/09/19 职场文书
服装发布会策划方案
2014/05/22 职场文书
美术社团活动总结
2014/06/27 职场文书
中国梦演讲稿范文
2014/08/28 职场文书
学校领导干部民主生活会整改方案
2014/09/29 职场文书
有关朝花夕拾的读书笔记
2015/06/29 职场文书
vue-cropper插件实现图片截取上传组件封装
2021/05/27 Vue.js
python 常用的异步框架汇总整理
2021/06/18 Python