Django对接支付宝实现支付宝充值金币功能示例


Posted in Python onDecember 17, 2019

很多网站里都有金币、积分之类的虚拟货币,获取这些往往需要充值。那么问题来了,如何在Django中对接支付宝实现支付宝充值金币的功能呢?网上很多资料都是电商的,那些都会带有订单系统之类比较复杂,而充值金币功能不需要实现那么多功能。

效果图如下:

Django对接支付宝实现支付宝充值金币功能示例

Django对接支付宝实现支付宝充值金币功能示例

Django对接支付宝实现支付宝充值金币功能示例

现在就来实现Django对接支付宝支付功能吧!

登录支付宝开放平台

点击进入蚂蚁金服开放平台https://open.alipay.com/platform/home.htm

进入支付宝沙箱环境https://openhome.alipay.com/platform/appDaily.htm?tab=info

Django对接支付宝实现支付宝充值金币功能示例

如图,这里是你沙箱环境的配置,左侧沙箱工具有沙箱支付宝安卓版下载,沙箱账号是你的测试账号。

下载支付宝开放平台开发助手

点击打开下载链接

Django对接支付宝实现支付宝充值金币功能示例

打开工具,生成密钥,然后妥善保管好!!

Django对接支付宝实现支付宝充值金币功能示例

Django对接支付宝实现支付宝充值金币功能示例

保存密钥

Django对接支付宝实现支付宝充值金币功能示例

把刚刚那个应用公钥2048重命名为:pub_2048.txt,把应用私钥2048重命名为:private_2048.txt,把这两个文件放在项目目录下。

注意:密钥的开始和结束一定要加上如下的字符串!!!

-----BEGIN PRIVATE KEY-----
这里粘贴里面的密钥
-----END PRIVATE KEY-----

Django对接支付宝实现支付宝充值金币功能示例

复制支付宝的公钥并保存

Django对接支付宝实现支付宝充值金币功能示例

把这段支付宝公钥复制,重命名为alipay_key_2048.txt,保存到项目目录中,注意加上如下字符串

-----BEGIN PRIVATE KEY-----
这里粘贴里面的密钥
-----END PRIVATE KEY-----

设置公钥

把刚刚的pub_2048.txt里面的字符串复制到如下:

Django对接支付宝实现支付宝充值金币功能示例

调试支付宝支付接口

from datetime import datetime


from Crypto.PublicKey import RSA
from Crypto.Signature import PKCS1_v1_5
from Crypto.Hash import SHA256
from urllib.parse import quote_plus
from base64 import decodebytes, encodebytes

import json


class AliPay(object):
 """
 支付宝支付接口
 """

 def __init__(self, appid, app_notify_url, app_private_key_path,
     alipay_public_key_path, return_url, debug=False):
  self.appid = appid
  self.app_notify_url = app_notify_url
  self.app_private_key_path = app_private_key_path
  self.app_private_key = None
  self.return_url = return_url
  with open(self.app_private_key_path) as fp:
   self.app_private_key = RSA.importKey(fp.read())

  self.alipay_public_key_path = alipay_public_key_path
  with open(self.alipay_public_key_path) as fp:
   self.alipay_public_key = RSA.import_key(fp.read())

  if debug is True:
   self.__gateway = "https://openapi.alipaydev.com/gateway.do"
  else:
   self.__gateway = "https://openapi.alipay.com/gateway.do"

 def direct_pay(self, subject, out_trade_no, total_amount, return_url=None, **kwargs):
  biz_content = {
   "subject": subject,
   "out_trade_no": out_trade_no,
   "total_amount": total_amount,
   "product_code": "FAST_INSTANT_TRADE_PAY",
   # "qr_pay_mode":4
  }

  biz_content.update(kwargs)
  data = self.build_body("alipay.trade.page.pay", biz_content, self.return_url)
  return self.sign_data(data)

 def build_body(self, method, biz_content, return_url=None):
  data = {
   "app_id": self.appid,
   "method": method,
   "charset": "utf-8",
   "sign_type": "RSA2",
   "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
   "version": "1.0",
   "biz_content": biz_content
  }

  if return_url is not None:
   data["notify_url"] = self.app_notify_url
   data["return_url"] = self.return_url

  return data

 def sign_data(self, data):
  data.pop("sign", None)
  # 排序后的字符串
  unsigned_items = self.ordered_data(data)
  unsigned_string = "&".join("{0}={1}".format(k, v) for k, v in unsigned_items)
  sign = self.sign(unsigned_string.encode("utf-8"))
  # ordered_items = self.ordered_data(data)
  quoted_string = "&".join("{0}={1}".format(k, quote_plus(v)) for k, v in unsigned_items)

  # 获得最终的订单信息字符串
  signed_string = quoted_string + "&sign=" + quote_plus(sign)
  return signed_string

 def ordered_data(self, data):
  complex_keys = []
  for key, value in data.items():
   if isinstance(value, dict):
    complex_keys.append(key)

  # 将字典类型的数据dump出来
  for key in complex_keys:
   data[key] = json.dumps(data[key], separators=(',', ':'))

  return sorted([(k, v) for k, v in data.items()])

 def sign(self, unsigned_string):
  # 开始计算签名
  key = self.app_private_key
  signer = PKCS1_v1_5.new(key)
  signature = signer.sign(SHA256.new(unsigned_string))
  # base64 编码,转换为unicode表示并移除回车
  sign = encodebytes(signature).decode("utf8").replace("\n", "")
  return sign

 def _verify(self, raw_content, signature):
  # 开始计算签名
  key = self.alipay_public_key
  signer = PKCS1_v1_5.new(key)
  digest = SHA256.new()
  digest.update(raw_content.encode("utf8"))
  if signer.verify(digest, decodebytes(signature.encode("utf8"))):
   return True
  return False

 def verify(self, data, signature):
  if "sign_type" in data:
   sign_type = data.pop("sign_type")
  # 排序后的字符串
  unsigned_items = self.ordered_data(data)
  message = "&".join(u"{}={}".format(k, v) for k, v in unsigned_items)
  return self._verify(message, signature)


def get_alipay_url(app_id, order_sn, order_mount):
 alipay = AliPay(
  appid=app_id,
  app_notify_url="http://127.0.0.1:8000/alipay/return/",
  app_private_key_path="../trade/keys/private_2048.txt",
  alipay_public_key_path="../trade/keys/alipay_key_2048.txt", # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
  debug=True, # 默认False,
  return_url="http://127.0.0.1:8000/alipay/return/"
 )

 url = alipay.direct_pay(
  subject=order_sn,
  out_trade_no=order_sn,
  total_amount=order_mount,
 )
 re_url = "https://openapi.alipaydev.com/gateway.do?{data}".format(data=url)

 return re_url


if __name__ == "__main__":
 url = get_alipay_url(
  '2016092600597838',
  '201902923423436',
  1.00
 )
 print(url)

如果输出的url能够打开并且使用沙箱账号支付,说明前面的配置成功了,可以进行视图的编写。

设计数据库订单模型

from django.db import models
from django.conf import settings
from django.utils.encoding import python_2_unicode_compatible

from questioning.utils.models import CreatedUpdatedMixin

@python_2_unicode_compatible
class OrderInfo(CreatedUpdatedMixin, models.Model):
 '''
 充值订单详情
 '''
 ORDER_STATUS = (
 ('TRADE_SUCCESS', '交易支付成功'),
 ('TRADE_CLOSED', '未付款交易超时关闭'),
 ('WAIT_BUYER_PAY', '交易创建'),
 ('TRADE_FINISHED', '交易结束'),
 ('paying', '待支付'),
)
 user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, verbose_name='用户')
 order_sn = models.CharField(max_length=30, null=True, blank=True, unique=True, verbose_name='订单号')
 trade_no = models.CharField(max_length=100, unique=True, null=True, blank=True, verbose_name='支付订单号')

 pay_status = models.CharField(choices=ORDER_STATUS, max_length=40, verbose_name='订单状态', default='paying')
 order_mount = models.DecimalField(verbose_name="充值金额", max_digits=10,
        decimal_places=2, default=0.00)


 class Meta:
  verbose_name = "充值订单"
  verbose_name_plural = verbose_name

 def __str__(self):
  return self.order_sn

注意充值的金额要设置为models.DecimalField货币类型

然后导入到数据库中

视图函数编写

class AlipayView(LoginRequiredMixin, AuthorRequiredMixin, View):
 """
 支付宝支付
 get方法实现支付宝return_url,如果没有实现也无所谓,post同样可以更新状态
 post方法实现支付宝notify_url,异步更新

 支付宝返回的url如下:
 #http://127.0.0.1:8000/alipay/return/?
 # charset=utf-8&
 # out_trade_no=201902923423436&
 # method=alipay.trade.page.pay.return&
 # total_amount=1.00&
 # sign=CDBMY9NBsp4KICdQoBEVxGWobd0N8y4%2BU09stzUWwlNtLr7ZpELJdM5js20wXv%2FCPp0FGPbRW1YS9DRx0CnKJULZZMqysBUMH2FL39sS0Fgstgy1ydTs7ySXdHziJV0inI%2BDWAsebQqtjk5gQEweUstc%2B%2BnzjdgAulpvWzfJsbknS%2BqUfktSdF2ZOWGhr1CFlfsMFEDS2nzQv4K3E%2BNaeylkzUnRe9M1sjIL%2FYR0wVZ5A3OfHLPf9HzC2B8%2FLu4g7N5Vctkqp2aerDvIkN5SNmDnRGyjOt2b%2BOsLMqG4X06JSsrZT6Ln8PimsrkSOIGbj0gCqscx7BwZfmCQePlCw%3D%3D&
 # trade_no=2019082622001426981000041778&
 # auth_app_id=2016092600597838&
 # version=1.0&app_id=2016092600597838&
 # sign_type=RSA2&
 # seller_id=2088102177296610&
 # timestamp=2019-08-26+13%3A51%3A01
 """
 def dispatch(self, request, *args, **kwargs):
  self.alipay = AliPay(
   appid=settings.ALIPAY_APPID,
   app_notify_url=settings.APP_NOTIFY_URL,
   app_private_key_path=settings.APP_PRIVATE_KEY_PATH,
   alipay_public_key_path=settings.ALIPAY_PUBLIC_KEY_PATH,
   debug=settings.ALIPAY_DEBUG,
   return_url=settings.RETURN_URL
  )
  #处理返回的url参数
  callback_data = {}
  for key, value in request.GET.items():
   callback_data[key] = value
  sign = callback_data.pop('sign', None)
  self.order_sn = callback_data.get('out_trade_no', None) #订单号
  self.trade_no = callback_data.get('trade_no', None) #支付宝订单号

  # 验证签名
  self.verify = self.alipay.verify(callback_data, sign)
  return super(AlipayView, self).dispatch(request, *args, **kwargs)


 def get(self, request):
  """处理支付宝return_url返回"""

  if self.verify:
   self.deposit()
   #返回个人中心页面
   return redirect(reverse('users:detail', kwargs={
    'username': request.user.username
   }))

 def post(self, request):
  """
  处理notify_url
  """
  if self.verify:
   self.deposit()

  return Response('success')

 def deposit(self):
  """充值操作

  1.更新用户的金币信息
  2.更新订单状态为交易成功
  """

  # 数据库中查询订单记录
  order = OrderInfo.objects.get(order_sn=self.order_sn)
  order.trade_no = self.trade_no # 支付宝订单号

  # 把人民币转换成对应的金币
  rmb = order.order_mount
  money = convert_rmb_to_money(rmb)

  # 更新用户的金币
  order.user.money += Decimal(money)
  order.user.save()
  # 订单状态置为交易成功
  order.pay_status = 'TRADE_SUCCESS'
  order.save()

当用户付款成功后,会跳转回来,那个就是return_url,处理这个的是get方法,用来更新用户的金币和订单。

以上这篇Django对接支付宝实现支付宝充值金币功能示例就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持三水点靠木。

Python 相关文章推荐
web.py获取上传文件名的正确方法
Aug 26 Python
跟老齐学Python之深入变量和引用对象
Sep 24 Python
详解python中executemany和序列的使用方法
Aug 12 Python
PyQt5打开文件对话框QFileDialog实例代码
Feb 07 Python
django 解决manage.py migrate无效的问题
May 27 Python
python实现比较类的两个instance(对象)是否相等的方法分析
Jun 26 Python
Python3内置模块之base64编解码方法详解
Jul 13 Python
Python学习笔记之函数的参数和返回值的使用
Nov 20 Python
Python sep参数使用方法详解
Feb 12 Python
关于多元线性回归分析——Python&SPSS
Feb 24 Python
Python3基于plotly模块保存图片表格
Aug 03 Python
详解anaconda离线安装pytorchGPU版
Sep 08 Python
Django后端发送小程序微信模板消息示例(服务通知)
Dec 17 #Python
Django项目使用ckeditor详解(不使用admin)
Dec 17 #Python
python主线程与子线程的结束顺序实例解析
Dec 17 #Python
Django通用类视图实现忘记密码重置密码功能示例
Dec 17 #Python
Django集成celery发送异步邮件实例
Dec 17 #Python
python学生信息管理系统实现代码
Dec 17 #Python
pymysql模块的操作实例
Dec 17 #Python
You might like
40个迹象表明你还是PHP菜鸟
2008/09/29 PHP
超级好用的一个php上传图片类(随机名,缩略图,加水印)
2010/06/30 PHP
php array的学习笔记
2012/05/10 PHP
PHP获取当前文件的父目录方法汇总
2016/07/21 PHP
JQuery通过Ajax提交表单并返回结果
2011/07/31 Javascript
js字符串的各种格式的转换 ToString,Format
2011/08/08 Javascript
js取模(求余数)隔行变色
2014/05/15 Javascript
jQuery中not()方法用法实例
2015/01/06 Javascript
jQuery插件FusionCharts绘制2D柱状图和折线图的组合图效果示例【附demo源码】
2017/04/10 jQuery
ReactNative短信验证码倒计时控件的实现代码
2017/07/20 Javascript
npm scripts 使用指南详解
2018/10/08 Javascript
小程序实现授权登陆的解决方案
2018/12/02 Javascript
[04:22]DOTA2上海特级锦标赛主赛事第四日TOP10
2016/03/06 DOTA
[34:39]Secret vs VG 2018国际邀请赛淘汰赛BO3 第二场 8.23
2018/08/24 DOTA
[36:05]完美世界DOTA2联赛循环赛 Forest vs DM 第一场 11.06
2020/11/06 DOTA
Centos5.x下升级python到python2.7版本教程
2015/02/14 Python
Python数据结构与算法之二叉树结构定义与遍历方法详解
2017/12/12 Python
使用Python实现一个栈判断括号是否平衡
2018/08/23 Python
Python 文件数据读写的具体实现
2020/01/24 Python
python读取excel进行遍历/xlrd模块操作
2020/07/12 Python
The Kooples美国官方网站:为情侣提供的法国当代时尚品牌
2019/01/03 全球购物
Columbia Sportswear法国官网:全球户外品牌
2020/09/25 全球购物
PHP如何去执行一个SQL语句
2016/03/05 面试题
介绍一下Linux文件的记录形式
2012/04/18 面试题
生产部统计员岗位职责
2014/01/05 职场文书
汽车销售员如何做职业生涯规划
2014/02/16 职场文书
敬老院标语
2014/06/27 职场文书
学生检讨书怎么写?
2014/10/10 职场文书
2014年药店店长工作总结
2014/11/17 职场文书
2014年幼儿园保育工作总结
2014/12/02 职场文书
贷款担保书
2015/01/20 职场文书
华清池导游词
2015/02/02 职场文书
留学推荐信英文范文
2015/03/26 职场文书
工地食品安全责任书
2015/05/09 职场文书
2016幼儿园中班开学寄语
2015/12/03 职场文书
靠谱的活动总结
2019/04/16 职场文书