支付宝沙箱环境配置

https://openhome.alipay.com/platform/appDaily.htm?tab=info

使用支付宝账号进行登陆

RSA私钥及公钥生成

https://docs.open.alipay.com/291/105971/

下载工具,按文档提示使用

在trade文件夹下新建keys文件夹,新建public.txtprivate.txt两个文件

trade/keys/private.txt

-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAvRYNqz/3Ye+ewQeRfvLXuAu5N/+bdC3KBj8L63j
-----END RSA PRIVATE KEY-----

trade/keys/public.txt

-----BEGIN RSA PRIVATE KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvRYN
-----END RSA PRIVATE KEY-----

中间的字符串是工具中生成的公钥和私钥(应用公钥)

将应用公钥上传到沙箱应用中

在新建一个alipay_key.txt文件

trade/keys/alipay_key.txt

-----BEGIN RSA PRIVATE KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCA
-----END RSA PRIVATE KEY-----

中间的是支付宝公钥

支付文档说明

https://docs.open.alipay.com/270

代码实现

首先的安装一个模块

pip install pycryptodome

utils中新建alipay.py

from datetime import datetime
from Crypto.PublicKey import RSA from Crypto.Signature import PKCS1_v1_5
from Crypto.Hash import SHA256
from base64 import b64encode, b64decode
from urllib.parse import quote_plus
from urllib.parse import urlparse, parse_qs
from urllib.request import urlopen
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
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):
# build_body主要生产消息的格式
# 公共请求参数
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"))
# 对url进行处理
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) if __name__ == "__main__":
return_url = 'http://127.0.0.1:8000/?total_amount=100.00&timestamp=2017-08-15+23%3A53%3A34&sign=e9E9UE0AxR84NK8TP1CicX6aZL8VQj68ylugWGHnM79zA7BKTIuxxkf%2FvhdDYz4XOLzNf9pTJxTDt8tTAAx%2FfUAJln4WAeZbacf1Gp4IzodcqU%2FsIc4z93xlfIZ7OLBoWW0kpKQ8AdOxrWBMXZck%2F1cffy4Ya2dWOYM6Pcdpd94CLNRPlH6kFsMCJCbhqvyJTflxdpVQ9kpH%2B%2Fhpqrqvm678vLwM%2B29LgqsLq0lojFWLe5ZGS1iFBdKiQI6wZiisBff%2BdAKT9Wcao3XeBUGigzUmVyEoVIcWJBH0Q8KTwz6IRC0S74FtfDWTafplUHlL%2Fnf6j%2FQd1y6Wcr2A5Kl6BQ%3D%3D&trade_no=2017081521001004340200204115&sign_type=RSA2&auth_app_id=2016080600180695&charset=utf-8&seller_id=2088102170208070&method=alipay.trade.page.pay.return&app_id=2016080600180695&out_trade_no=20170202185&version=1.0'
o = urlparse(return_url)
query = parse_qs(o.query)
processed_query = {}
ali_sign = query.pop("sign")[0] # 测试用例
alipay = AliPay(
# 沙箱里面的appid值
appid="2016091500517596",
# notify_url是异步的url
app_notify_url="http://127.0.0.1:8000/",
# 我们自己商户的密钥
app_private_key_path="../trade/keys/private.txt",
# 支付宝的公钥
alipay_public_key_path="../trade/keys/alipay_key.txt", # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
# debug为true时使用沙箱的url。如果不是用正式环境的url
debug=True, # 默认False,
return_url="http://127.0.0.1:8000/alipay/return/"
) for key, value in query.items():
processed_query[key] = value[0]
# print (alipay.verify(processed_query, ali_sign)) # 直接支付:生成请求的字符串。
url = alipay.direct_pay(
# 订单标题
subject="测试订单",
# 我们商户自行生成的订单号
out_trade_no="201809270840",
# 订单金额
total_amount=100,
# 成功付款后跳转到的页面,return_url同步的url
# return_url="http://127.0.0.1:8000/"
)
# 将生成的请求字符串拿到我们的url中进行拼接
re_url = "https://openapi.alipaydev.com/gateway.do?{data}".format(data=url) print(re_url)

运行这个文件,点击生成的链接,就跳转到了支付界面

django集成支付宝

urls.py

# 配置支付宝支付相关接口的url
path('alipay/return/', AlipayView.as_view())

alipay.py

# 把return_url和notify_url都改成远程服务器的地址

return_url="http://xxx.xxx.xxx.xxx:8000/alipay/return/"

app_notify_url="http://xxx.xxx.xxx.xxx:8000/alipay/return/"

settings.py中配置公钥私钥路径

# 支付宝相关的key
private_key_path = os.path.join(BASE_DIR, 'apps/trade/keys/private.txt')
ali_pub_key_path = os.path.join(BASE_DIR, 'apps/trade/keys/alipay_key.txt')

trade/views.py

from datetime import datetime
from utils.alipay import AliPay
from rest_framework.views import APIView
from MxShop.settings import ali_pub_key_path, private_key_path
from rest_framework.response import Response class AlipayView(APIView):
def get(self, request):
"""
处理支付宝的return_url返回
"""
processed_dict = {}
# 1. 获取GET中参数
for key, value in request.GET.items():
processed_dict[key] = value
# 2. 取出sign
sign = processed_dict.pop("sign", None) # 3. 生成ALipay对象
alipay = AliPay(
appid="201609xxxx456",
app_notify_url="http://xxx.xxx.xxx.xxx:8000/alipay/return/",
app_private_key_path=private_key_path,
alipay_public_key_path=ali_pub_key_path, # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
debug=True, # 默认False,
return_url="http://xxx.xxx.xxx.xxx:8000/alipay/return/"
) verify_re = alipay.verify(processed_dict, sign) # 这里可以不做操作。因为不管发不发return url。notify url都会修改订单状态。
if verify_re is True:
order_sn = processed_dict.get('out_trade_no', None)
trade_no = processed_dict.get('trade_no', None)
trade_status = processed_dict.get('trade_status', None) existed_orders = OrderInfo.objects.filter(order_sn=order_sn)
for existed_order in existed_orders:
existed_order.pay_status = trade_status
existed_order.trade_no = trade_no
existed_order.pay_time = datetime.now()
existed_order.save() def post(self, request):
"""
处理支付宝的notify_url
"""
# 存放post里面所有的数据
processed_dict = {}
# 取出post里面的数据
for key, value in request.POST.items():
processed_dict[key] = value
# 把signpop掉,文档有说明
sign = processed_dict.pop("sign", None) # 生成一个Alipay对象
alipay = AliPay(
appid="201609xxxx456",
app_notify_url="http://xxx.xxx.xxx.xxx:8000/alipay/return/",
app_private_key_path=private_key_path,
alipay_public_key_path=ali_pub_key_path, # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
debug=True, # 默认False,
return_url="http://xxx.xxx.xxx.xxx:8000/alipay/return/"
) # 进行验证
verify_re = alipay.verify(processed_dict, sign) # 如果验签成功
if verify_re is True:
# 商户网站唯一订单号
order_sn = processed_dict.get('out_trade_no', None)
# 支付宝系统交易流水号
trade_no = processed_dict.get('trade_no', None)
# 交易状态
trade_status = processed_dict.get('trade_status', None) # 查询数据库中订单记录
existed_orders = OrderInfo.objects.filter(order_sn=order_sn)
for existed_order in existed_orders:
# 订单商品项
order_goods = existed_order.goods.all()
# 商品销量增加订单中数值
for order_good in order_goods:
goods = order_good.goods
goods.sold_num += order_good.goods_num
goods.save() # 更新订单状态
existed_order.pay_status = trade_status
existed_order.trade_no = trade_no
existed_order.pay_time = datetime.now()
existed_order.save()
# 需要返回一个'success'给支付宝,如果不返回,支付宝会一直发送订单支付成功的消息
return Response("success")

trade/serializers.py

创建订单的时候生成一个支付的url,这个逻辑OderSerializerOrderDetailSerializer中都添加

 #支付订单的url
alipay_url = serializers.SerializerMethodField(read_only=True) def get_alipay_url(self, obj):
alipay = AliPay(
appid="20160xxxx17456",
app_notify_url="http://4xx.xxx.xxx.xx59:8000/alipay/return/",
app_private_key_path=private_key_path,
alipay_public_key_path=ali_pub_key_path, # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
debug=True, # 默认False,
return_url="http://4xx.xxx.xxx.xx59:8000/alipay/return/"
) url = alipay.direct_pay(
subject=obj.order_sn,
out_trade_no=obj.order_sn,
total_amount=obj.order_mount,
)
re_url = "https://openapi.alipaydev.com/gateway.do?{data}".format(data=url) return re_url

服务器测试

把vue项目中api.js里面local_host改为服务器ip

let local_host = 'http://47.93.198.159:8000';

在pycharm中调用服务器的解释器运行代码

浏览器访问地址:http://47.93.198.159:8000/orders/

输入数据进行测试

进入返回的alipay的url进行测试

测试的时候发现,明明支付成功了,数据库中的状态却没了,

既不是paying,也不是TRADE_SUCCESS,就是一个空字符串

这是因为支付宝的同步跳转参数中没有支付状态,而我们却在get请求中去获取这个参数

这里需要将get函数中的获取交易状态删除(注释掉)

https://docs.open.alipay.com/270/alipay.trade.page.pay

# trade_status = processed_dict.get('trade_status', None)

vue静态文件放到django中

运行npm run build会生成一个dist目录,打开这个目录

把index.html拷贝到templates目录下

把index.entry.js考到django的static目录下面

把dist/static下的两个文件夹拷贝到static目录下

修改js路径

配置index的url

复制代码
from django.views.generic import TemplateView # 首页
path('index/', TemplateView.as_view(template_name='index.html'),name='index')

配置支付成功return的地址

        if verify_re is True:
order_sn = processed_dict.get('out_trade_no', None)
trade_no = processed_dict.get('trade_no', None)
# trade_status = processed_dict.get('trade_status', None) existed_orders = OrderInfo.objects.filter(order_sn=order_sn)
for existed_order in existed_orders:
# existed_order.pay_status = trade_status
existed_order.trade_no = trade_no
existed_order.pay_time = datetime.now()
existed_order.save() response = redirect("/index/#/app/home/member/order")
return response
else:
response = redirect("index")
return response

同步到服务器,浏览器访问进行测试

http://IP:8000/index/#/app/home/index

class AlipayView(APIView)

from datetime import datetime
from utils.alipay import AliPay
from rest_framework.views import APIView
from MxShop.settings import ali_pub_key_path, private_key_path
from rest_framework.response import Response class AlipayView(APIView):
def get(self, request):
"""
处理支付宝的return_url返回
"""
processed_dict = {}
# 1. 获取GET中参数
for key, value in request.GET.items():
processed_dict[key] = value
# 2. 取出sign
sign = processed_dict.pop("sign", None) # 3. 生成ALipay对象
alipay = AliPay(
appid="2016091500517596",
app_notify_url="http://120.79.65.88:8000/alipay/return/",
app_private_key_path=private_key_path,
alipay_public_key_path=ali_pub_key_path, # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
debug=True, # 默认False,
return_url="http://120.79.65.88:8000/alipay/return/"
) verify_re = alipay.verify(processed_dict, sign) # 这里可以不做操作。因为不管发不发return url。notify url都会修改订单状态。
if verify_re is True:
order_sn = processed_dict.get('out_trade_no', None)
trade_no = processed_dict.get('trade_no', None)
# trade_status = processed_dict.get('trade_status', None) existed_orders = OrderInfo.objects.filter(order_sn=order_sn)
for existed_order in existed_orders:
# existed_order.pay_status = trade_status
existed_order.trade_no = trade_no
existed_order.pay_time = datetime.now()
existed_order.save() response = redirect("/index/#/app/home/member/order")
return response
else:
response = redirect("index")
return response def post(self, request):
"""
处理支付宝的notify_url
"""
# 存放post里面所有的数据
processed_dict = {}
# 取出post里面的数据
for key, value in request.POST.items():
processed_dict[key] = value
# 把signpop掉,文档有说明
sign = processed_dict.pop("sign", None) # 生成一个Alipay对象
alipay = AliPay(
appid="2016091500517596",
app_notify_url="http://120.79.65.88:8000/alipay/return/",
app_private_key_path=private_key_path,
alipay_public_key_path=ali_pub_key_path, # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
debug=True, # 默认False,
return_url="http://120.79.65.88:8000/alipay/return/"
) # 进行验证
verify_re = alipay.verify(processed_dict, sign) # 如果验签成功
if verify_re is True:
# 商户网站唯一订单号
order_sn = processed_dict.get('out_trade_no', None)
# 支付宝系统交易流水号
trade_no = processed_dict.get('trade_no', None)
# 交易状态
trade_status = processed_dict.get('trade_status', None) # 查询数据库中订单记录
existed_orders = OrderInfo.objects.filter(order_sn=order_sn)
for existed_order in existed_orders:
# 订单商品项
order_goods = existed_order.goods.all()
# 商品销量增加订单中数值
for order_good in order_goods:
goods = order_good.goods
goods.sold_num += order_good.goods_num
goods.save() # 更新订单状态
existed_order.pay_status = trade_status
existed_order.trade_no = trade_no
existed_order.pay_time = datetime.now()
existed_order.save()
# 需要返回一个'success'给支付宝,如果不返回,支付宝会一直发送订单支付成功的消息
return Response("success")

Django+Vue打造购物网站(九)的更多相关文章

  1. Django+Vue打造购物网站(十)

    首页.商品数量.缓存和限速功能开发 将环境切换为本地,vue也切换为本地 轮播图 goods/serializers.py class BannerSerializer(serializers.Mod ...

  2. Django+Vue打造购物网站(五)

    注册和登陆 drf的认证 http://www.django-rest-framework.org/api-guide/authentication/ settings.py文件的配置 INSTALL ...

  3. Django+Vue打造购物网站(八)

    购物车.订单管理和远程调试 添加商品到购物车 trade/serializers.py from rest_framework import serializers from goods.models ...

  4. Django+Vue打造购物网站(四)

    首页商品类别数据显示 商品分类接口 大概需要两个,一个显示三个类别 一个显示类别及类别下的全部商品 现在开始写商品的接口 首先编写三个分类的serializer class CategorySeria ...

  5. Django+Vue打造购物网站(十一)

    第三方登录 微博创建应用,修改回调地址 http://open.weibo.com/authentication 安装第三方登录插件 https://github.com/python-social- ...

  6. Django+Vue打造购物网站(三)

    商品列表页 通过商品列表页面来学习drf django的view实现商品列表页 在goods目录下新建一个views_base.py文件,用来区分drf的view和Dajngo自带的view的区别 利 ...

  7. Django+Vue打造购物网站(二)

    配置后台管理 xadmin直接使用之前的在线教育的那个就可以了 users/adminx.py #!/usr/bin/env python # -*- coding: utf-8 -*- # @Tim ...

  8. Django+Vue打造购物网站(一)

    环境搭建 python == 3.6 Django == 2.0 创建工程 django-admin startproject MxShop 配置setting.py文件 # 数据库 DATABASE ...

  9. Django+Vue打造购物网站(七)

    个人中心功能开发 drf文档注释 http://www.django-rest-framework.org/topics/documenting-your-api/ 动态设置serializer和pe ...

随机推荐

  1. 一文看懂HTTPS的核心知识

    1.HTTPS历史 由于HTTP的消息传输的安全隐患,于是网景公司在1994年设计了SSL(Secure Sockets Layer,安全套接字层)协议,目的是保障网上交易安全,从而就诞生了HTTPS ...

  2. Valgrind.Callgrind使用

    Callgrind介绍 用来对统计程序的函数调用之间的关系, 并统计每个函数的耗时 Callgrind之所以能够发现函数调用的关系, 依赖于平台的明确返回和调用指令. 在x86和amd64平台上wor ...

  3. 模拟ES6中的Promise实现,让原理一目了然

    简介 Promise 对象用于延迟(deferred) 计算和异步(asynchronous )计算.一个Promise对象代表着一个还未完成,但预期将来会完成的操作.Promise 对象是一个返回值 ...

  4. spring学习总结——装配Bean学习三(xml装配bean)

    通过XML装配bean Spring现在有了强大的自动化配置和基于Java的配置,XML不应该再是你的第一选择了.不过,鉴于已经存在那么多基于XML的Spring配置,所以理解如何在Spring中使用 ...

  5. 关于 MongoDB 与 SQL Server 通过本身自带工具实现数据快速迁移 及 注意事项 的探究

    背景介绍 随着业务的发展.需求的变化,促使我们追求使用不同类型的数据库,充分发挥其各自特性.如果决定采用新类型的数据库,就需要将既有的数据迁移到新的数据库中.在这类需求中,将SQL Server中的数 ...

  6. [转]QQ空间、新浪微博、腾讯微博等一键分享API链接代码

    转自------ 1.新浪微博:http://service.weibo.com/share/share.php?url= count=表示是否显示当前页面被分享数量(1显示)(可选,允许为空)&am ...

  7. CSS盒子模型(Box Model)

    一.背景 作为CSS的重点,三大模块之一的盒子模型,这部分无论如何也要精通透彻.在任何一个网页当中,都有自己的布局方式,所谓网页布局方式就是如何把网页里面的文字.图片,很好的排版成美工设计的样式,这时 ...

  8. VS2015 IIS Express Web服务器无法启动解决办法

    1.运行和调试vs2015项目 提示无法运行项目,打开vs2013项目发现可以正常运行,所以推测试vs2015项目配置有问题. 2.找到项目启动项中 .csproj文件,定位到<WebProje ...

  9. LeetCode算法题-Kth Largest Element in a Stream(Java实现)

    这是悦乐书的第296次更新,第315篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第164题(顺位题号是703).设计一个类来查找流中第k个最大元素.请注意,它是排序顺序 ...

  10. MySQL 数据查询

    SELECT子句:用来指定查询返回字段,星号(*)表示返回所有字段 SELECT  [DISTINCT]*|字段列表        #DISTINCT 用来过滤重复数据 FROM子句:用来指定数据来源 ...