侯体宗的博客
  • 首页
  • Hyperf版
  • beego仿版
  • 人生(杂谈)
  • 技术
  • 关于我
  • 更多分类
    • 文件下载
    • 文字修仙
    • 中国象棋ai
    • 群聊
    • 九宫格抽奖
    • 拼图
    • 消消乐
    • 相册

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

框架(架构)  /  管理员 发布于 7年前   199

思路:调用第三方支付 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_requireddef 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 settingsfrom paypal.standard.forms import PayPalPaymentsFormfrom 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_exemptdef 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='https:/article/{{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='https:/article/{{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='https:/article/{{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='https:myorders/'>我的订单</a>选用其它付款方式。</p>    </div>    <div class='panel panel-footer'>    </div>   </div>  </div> </div></div>{% endblock %}

PayPal 在处理完在线付款流程后会另外发送一个 HTTP 数据给我们的网站,我们应该编写一个处理这个信号的函数,更改我们数据库中的内容,为了确保我们设置的监听函数可以被系统加载且保持运行,在 views.py 的同级目录中建立一个名为 signal.py 文件。

from mysite import modelsfrom paypal.standard.models import ST_PP_COMPLETEDfrom paypal.standard.ipn.signals import valid_ipn_receiveddef 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 AppConfigclass 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 实现第三方网站支付的功能。

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


  • 上一条:
    Flask和Django框架中自定义模型类的表名、父类相关问题分析
    下一条:
    Django中的Model操作表的实现
  • 昵称:

    邮箱:

    0条评论 (评论内容有缓存机制,请悉知!)
    最新最热
    • 分类目录
    • 人生(杂谈)
    • 技术
    • linux
    • Java
    • php
    • 框架(架构)
    • 前端
    • ThinkPHP
    • 数据库
    • 微信(小程序)
    • Laravel
    • Redis
    • Docker
    • Go
    • swoole
    • Windows
    • Python
    • 苹果(mac/ios)
    • 相关文章
    • Filament v3.1版本发布(0个评论)
    • docker + gitea搭建一个git服务器流程步骤(0个评论)
    • websocket的三种架构方式使用优缺点浅析(0个评论)
    • ubuntu20.4系统中宿主机安装nginx服务,docker容器中安装php8.2实现运行laravel10框架网站(0个评论)
    • phpstudy_pro(小皮面板)中安装最新php8.2.9版本流程步骤(0个评论)
    • 近期文章
    • 在go中实现一个常用的先进先出的缓存淘汰算法示例代码(0个评论)
    • 在go+gin中使用"github.com/skip2/go-qrcode"实现url转二维码功能(0个评论)
    • 在go语言中使用api.geonames.org接口实现根据国际邮政编码获取地址信息功能(1个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf分页文件功能(0个评论)
    • gmail发邮件报错:534 5.7.9 Application-specific password required...解决方案(0个评论)
    • 欧盟关于强迫劳动的规定的官方举报渠道及官方举报网站(0个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf文件功能(0个评论)
    • Laravel从Accel获得5700万美元A轮融资(0个评论)
    • 在go + gin中gorm实现指定搜索/区间搜索分页列表功能接口实例(0个评论)
    • 在go语言中实现IP/CIDR的ip和netmask互转及IP段形式互转及ip是否存在IP/CIDR(0个评论)
    • 近期评论
    • 122 在

      学历:一种延缓就业设计,生活需求下的权衡之选中评论 工作几年后,报名考研了,到现在还没认真学习备考,迷茫中。作为一名北漂互联网打工人..
    • 123 在

      Clash for Windows作者删库跑路了,github已404中评论 按理说只要你在国内,所有的流量进出都在监控范围内,不管你怎么隐藏也没用,想搞你分..
    • 原梓番博客 在

      在Laravel框架中使用模型Model分表最简单的方法中评论 好久好久都没看友情链接申请了,今天刚看,已经添加。..
    • 博主 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 @1111老铁这个不行了,可以看看近期评论的其他文章..
    • 1111 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 网站不能打开,博主百忙中能否发个APP下载链接,佛跳墙或极光..
    • 2018-05
    • 2020-02
    • 2020-03
    • 2020-05
    • 2020-06
    • 2020-07
    • 2020-08
    • 2020-11
    • 2021-03
    • 2021-09
    • 2021-10
    • 2021-11
    • 2022-01
    • 2022-02
    • 2022-03
    • 2022-08
    • 2023-08
    • 2023-10
    • 2023-12
    Top

    Copyright·© 2019 侯体宗版权所有· 粤ICP备20027696号 PHP交流群

    侯体宗的博客