昨日内容回顾

1. django请求生命周期?
- 当用户在浏览器中输入url时,浏览器会生成请求头和请求体发给服务端
请求头和请求体中会包含浏览器的动作(action),这个动作通常为get或者post,体现在url之中. - url经过Django中的wsgi,再经过Django的中间件,最后url到过路由映射表,在路由中一条一条进行匹配,
一旦其中一条匹配成功就执行对应的视图函数,后面的路由就不再继续匹配了.
- 视图函数根据客户端的请求查询相应的数据.返回给Django,然后Django把客户端想要的数据做为一个字符串返回给客户端.
- 客户端浏览器接收到返回的数据,经过渲染后显示给用户. 2. 对于django版本:
一:
1.7
1.8
1.9
我们不一样:
1. 路由编写
2. ORM on_delete
3. 中间件
4. 模板配置
TEMPLATES_DIRS = (
"",
"", )
二:
1.10
1.11
三:
2.0
3. ORM注意掌握以下知识
a. 增删改查
b. 正反向查询 4. 什么时候会用到反向查询?
- OneToOne,ForeignKey,ManyToMany - 举例:路飞学城项目
一对一: 课程表、课程详细表 -> 表名小写
一对多: 课程表、章节、课时(学位课和奖学金) -> 表名小写_set
多对多: 课程表、老师 -> 表名小写_set 5. 连表查询时,inner join和left join的区别?
left join(左联接) 返回包括左表中的所有记录和右表中联结字段相等的记录,不配的显示为Null   inner join(等值连接) 只返回两个表中联结字段相等的行,不配的不显示 6. 使用Python如何操作数据库? 原生SQL:
- pymysql
- MySQLDB
ORM:
- django ORM
- SQLALchemy 7. 原生SQL和ORM的区别?
原生SQL:
优点:
- 执行效率高
缺点:
- 开发效率慢
ORM:
优点:
- 开发效率高
缺点:
- 执行效率低 开发程序时,优缺点几乎无差别。
- for循环少
- 连表少
- 用索引 8. 为什么使用redis做购物车?
- 因为购物车操作比较频繁,如果采用数据库,速度太慢,影响用户体验
- 购物车用完之后,记录就消失了!它是一个临时数据 9. redis时用到了哪些操作?
set
get hset
hmset
hget
hgetall keys
expire
delete
flushall 10. 购物车的数据结构? {
shopping_car_用户id_课程id:{
id:'',
name:'..'
price:1
}
} 为什么要这么设计?
- 因为redis不支持嵌套字典

一、支付宝支付

沙箱环境介绍

蚂蚁沙箱环境(Beta)是协助开发者进行接口功能开发及主要功能联调的辅助环境。沙箱环境模拟了开放平台部分产品的主要功能和主要逻辑(当前沙箱支持产品请参考“沙箱支持产品列表”)。
在开发者应用上线审核前,开发者可以根据自身需求,先在沙箱环境中了解、组合和调试各种开放接口,进行开发调通工作,从而帮助开发者在应用上线审核完成后,能更快速、更顺利的进行线上调试和验收工作。

详情,请参考链接:

https://docs.open.alipay.com/200/105311/

商家要使用支付宝支付功能,需要申请一个支付接口。需要提交企业的营业执照等相关信息!

但是对于个人开发者来说,并没有这些资料。所以,为了帮助开发者进行接口测试,个人可以使用沙箱环境测试。只要你有支付宝账号就行。里面提供了测试账号,钱可以随便充值。注意:数据是假的,金额可能会被清洗!

申请账号

打开链接

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

打开手机支付宝,扫描二维码,就可以登录了

登录成功之后,点击自研开发者

点击导航的开发者中心-->研发服务

填写相关信息,完成实名认证

再次进入沙箱应用,点击查看应用公钥

点击设置应用公钥

点击查看秘钥生成方法

下载windows版本的压缩包,解压一下,效果如下:

点击RSA签名验签工具,直接点击生成秘钥

不要管JAVA和非JAVA,默认的就可以了

效果如下:

它会在当前压缩包里面的RSA秘钥,生成2个txt文件

打开应用公钥2048.txt,将里面的内容复制一下

进入到刚才的页面,输入公钥,点击保存。

如果保存时,提示公钥格式不对。再生成一次公钥,再次输入,就可以了!

SDK

SDK的英文全名是:software development kit,翻译成中文的意思就是“软件开发工具包”

通俗一点的理解,是指由第三方服务商提供的实现软件产品某项功能的工具包。一般以集合api和文档、范例、工具的形式出现。

通常SDK是由专业性质的公司提供专业服务的集合,比如提供安卓开发工具、或者基于硬件开发的服务等。也有针对某项软件功能的SDK,如推送技术、图像识别技术、移动支付技术等,同时资源优势类的公司也提供资源共享的SDK,如一些广告SDK提供盈利渠道,分发SDK提供产品下载渠道。

注意:支付宝的SDK,采用了rsa加密算法。对于发送,返回的数据,它有一定的加密算法以及解密算法。

所以,我们要使用支付宝服务,发送的数据,必须符合它的加密要求才行。有了SDK之后,我们不需要关心加密算法,只需要关系业务逻辑就行!

打开支付宝提供的SDK

https://docs.open.alipay.com/54/103419/

下载链接:

https://pypi.org/project/alipay-sdk-python/

不需要下载SDK!

不需要下载SDK!

不需要下载SDK!

这里使用的是,根据官方SDK封装好的一个python文件,需要安装依赖包pycryptodome

pip3 install pycryptodome

pay.py代码如下

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 urllib.parse import urlparse, parse_qs
from base64 import decodebytes, encodebytes
import json class AliPay(object):
"""
支付宝支付接口(PC端支付接口)
""" 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.importKey(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)

这个pay.py就是sdk

支付宝密钥处理体系

为了保证交易双方(商户和支付宝)的身份和数据安全,开发者在调用接口前,需要配置双方密钥,对交易数据进行双方校验。

集成和开发

在开始集成和开发前,首先了解一下常用的接入方式和架构建议:

其次,为了保证交易安全,支付宝采用了一系列的安全手段:

  1. 采用HTTPS协议传输交易数据,防止数据被截获,解密。

  2. 采用RSA非对称密钥,明确交易双方的身份,保证交易主体的正确性和唯一性

项目部署

新建一个django项目alipay,版本为django 2.x

在项目根目录创建utils,将上面的pay.py复制到此目录下

在项目根目录创建keys

进入目录secret_key_tools_RSA_win\RSA签名验签工具windows_V1.4\RSA密钥

将里面的应用公钥2048.txt,应用私钥2048.txt这2个文件,复制到keys目录

python代码里面的文件名,不要出现中文!

重命名为英文!!!!!!!!

将这2个txt重命名为alipay_public_2048.txt和app_private_2048.txt

注意:!!!!!!!!!!!!!!!!!!!!!!!!

alipay_public_2048.txt这个是公钥,不能用本地的。要用网页的!

点击查看支付宝公钥,不是应用公钥!

复制里面的公钥

打开alipay_public_2048.txt,必须增加头部和尾部,将网页复制的公钥粘贴到里面!

-----BEGIN PUBLIC KEY-----
MIIBI...
-----END PUBLIC KEY-----

效果如下:

一定要覆盖公钥!!!!!!!!!!!!!!!!!!!

打开app_private_2048.txt,必须增加头部和尾部

-----BEGIN RSA PRIVATE KEY-----
MIIE...
-----END RSA PRIVATE KEY-----

效果如下:

 注意:这2个txt文件,必须为utf-8编码,否则启动django项目会报错!

修改urls.py,增加路径

from django.contrib import admin
from django.urls import path,re_path from app01 import views
urlpatterns = [
path('admin/', admin.site.urls),
path('', views.index),
path('index/', views.index),
re_path('buy/(?P<gid>\d+)/', views.buy),
path('check_order/', views.check_order),
re_path('show', views.show),
path('order_list/', views.order_list),
]

修改models.py,增加2个表

from django.db import models

# Create your models here.
class Goods(models.Model): # 商品
name = models.CharField(max_length=32,verbose_name="名称")
price = models.FloatField(verbose_name="价格") class Order(models.Model): # 订单
no = models.CharField(max_length=64,verbose_name="订单号")
goods = models.ForeignKey(to='Goods',on_delete=models.CASCADE,verbose_name="商品ID")
status_choices = (
(1,'未支付'),
(2,'已支付'),
)
status = models.SmallIntegerField(choices=status_choices,default=1,verbose_name="支付状态")

修改views.py,增加视图函数

注意:appid改成自己的,后6位我改成xx了

from django.shortcuts import render, HttpResponse, redirect
from app01 import models
import uuid
from utils.pay import AliPay # 导入sdk # Create your views here.
def index(request):
goods_list = models.Goods.objects.all() return render(request, 'index.html', {'goods_list': goods_list}) # 全局变量
alipay = AliPay(
appid="2016091700xxxxxx", # 注意,改成自己的!
app_notify_url="http://127.0.0.1:8000/check_order/", # POST,发送支付状态信息
return_url="http://127.0.0.1:8000/show/", # GET,将用户浏览器地址重定向回原网站
app_private_key_path="keys/app_private_2048.txt",
alipay_public_key_path="keys/alipay_public_2048.txt",
debug=True, # 默认True测试环境、False正式环境
) def buy(request, gid):
"""
去购买并支付
:param request:
:param gid:
:return:
"""
obj = models.Goods.objects.get(pk=gid) # 生成订单(未支付)
no = str(uuid.uuid4())
models.Order.objects.create(no=no, goods_id=obj.id) # 根据
# APPID
# 支付宝网关
# 公钥和私钥
# 生成要跳转的地址
# 沙箱环境地址:https://openhome.alipay.com/platform/appDaily.htm?tab=info query_params = alipay.direct_pay(
subject=obj.name, # 商品简单描述
out_trade_no=no, # 商户订单号
total_amount=obj.price, # 交易金额(单位: 元 保留俩位小数)
) pay_url = "https://openapi.alipaydev.com/gateway.do?{0}".format(query_params) return redirect(pay_url) def check_order(request):
"""
POST请求,支付宝通知支付信息,我们修改订单状态
:param request:
:return:
"""
if request.method == 'POST': from urllib.parse import parse_qs
body_str = request.body.decode('utf-8')
post_data = parse_qs(body_str) post_dict = {}
for k, v in post_data.items():
post_dict[k] = v[0]
sign = post_dict.pop('sign', None)
status = alipay.verify(post_dict, sign)
if status:
# 支付成功,获取订单号将订单状态更新
out_trade_no = post_dict['out_trade_no']
models.Order.objects.filter(no=out_trade_no).update(status=2)
return HttpResponse('success')
else:
return HttpResponse('支持失败')
else:
return HttpResponse('只支持POST请求') def show(request):
"""
回到我们页面
:param request:
:return:
""" if request.method == "GET":
params = request.GET.dict()
sign = params.pop('sign', None)
status = alipay.verify(params, sign)
if status:
return HttpResponse('支付成功')
else:
return HttpResponse('失败')
else:
return HttpResponse('只支持GET请求') def order_list(request):
"""
查看所有订单状态
:param request:
:return:
"""
orders = models.Order.objects.all()
return render(request, 'order_list.html', {'orders': orders})

增加index.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<ul>
{% for row in goods_list %}
<li>{{ row.name }},价格:{{ row.price }} <a href="/buy/{{ row.id }}/">购买</a></li>
{% endfor %}
</ul>
</body>
</html>

增加order_list.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<table border="">
{% for item in orders %}
<tr>
<td>{{ item.id }}</td>
<td>{{ item.no }}</td>
<td>{{ item.goods.name }}</td>
<td>{{ item.get_status_display }}</td>
</tr>
{% endfor %}
</table>
</body>
</html>

修改settings.py,关闭csrf

MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
# 'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

项目结构如下:

alipay/
├── alipay
│   ├── __init__.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
├── app01
│   ├── admin.py
│   ├── apps.py
│   ├── __init__.py
│   ├── migrations
│   │   ├── 0001_initial.py
│   │   ├── 0002_auto_20180810_1623.py
│   │   └── __init__.py
│   ├── models.py
│   ├── tests.py
│   └── views.py
├── db.sqlite3
├── keys
│   ├── alipay_public_2048.txt
│   └── app_private_2048.txt
├── manage.py
├── templates
│   ├── index.html
│   └── order_list.html
└── utils
└── pay.py

使用2个命令,生成表

python manage.py makemigrations
python manage.py migrate

使用navicat打开sqlite3数据库,增加几条数据

INSERT INTO app01_goods ("id", "name", "price") VALUES (1, 'Apple iPad Pro', 5588.0);
INSERT INTO app01_goods ("id", "name", "price") VALUES (2, 'i5 8500', 1599.0);
INSERT INTO app01_goods ("id", "name", "price") VALUES (3, '拯救者Y7000', 7299.0);
INSERT INTO app01_goods ("id", "name", "price") VALUES (4, '大白兔奶糖', 1.0);

启动django项目,访问首页

本地测试支付

点击购买大白兔奶糖,它会自动跳转到支付宝的支付页面

注意:要沙箱环境的APP

它只提供了安卓版本,下载之后,进行安装

点击沙箱账号

它提供了商家信息和买家信息

默认的买家信息,账号有9万多块。注意:这个钱是虚拟的,不能体现!

如果钱不够了,可以输入任意金额进行充值!

手机登录买家账号,密码为111111

打开扫一扫,进行支付

提示支付成功

注意:如果出现失败,可能是公钥错了!一定是网页的支付宝公钥,不是本地的公钥!

查看买家信息,少了一块钱

查看商家信息,多了一块钱。那一分钱,是扣了手续费!

注意:这个是本地环境测试的。

如果需要服务器测试,请修改views.py中的全局变量,将127.0.0.1改为服务器的公网IP即可!

查看订单状态,这里的状态是未支付。为什么呢?因为支付宝要发送一个POST请求到

http://127.0.0.1:8000/check_order/ 才能修改状态。由于在本地,支付宝无法访问此地址!

注意:商品价格,必须保证最多为小数点2位。这个是支付宝规定的!

线上测试支付

将代码上传到公网服务器,修改views.py,将里面的127.0.0.1改成公网IP。

再次支付一次,查看订单状态。状态为已支付

完整代码,请参数github

https://github.com/987334176/alipay

总结:

1. 什么是SDK?
- 第三方服务商提供的实现软件产品某项功能的工具包 2. 沙箱环境是什么?
- 是测试环境 3. 简述支付宝支付流程:
- 用户点击支持,根据支付宝的AppID/网关/公钥私钥/SDK生成地址
- 用户跳转到支付宝
- 支付成功之后,支付宝会给我发送两个请求:
- GET, 从支付宝网站跳转回自己网站。
- POST,支付发送支付信息(修改订单状态) 4. 支付宝金额的精度?
- 小数点后两位 5. 支付宝用的什么加密?
- RSA 6. 支付完成之后,服务器宕机了,怎么办?
- 24小时内,支付依然发送通知。

第3题,是一道面试题

python 全栈开发,Day102(支付宝支付)的更多相关文章

  1. python全栈开发目录

    python全栈开发目录 Linux系列 python基础 前端~HTML~CSS~JavaScript~JQuery~Vue web框架们~Django~Flask~Tornado 数据库们~MyS ...

  2. Python全栈开发【面向对象进阶】

    Python全栈开发[面向对象进阶] 本节内容: isinstance(obj,cls)和issubclass(sub,super) 反射 __setattr__,__delattr__,__geta ...

  3. Win10构建Python全栈开发环境With WSL

    目录 Win10构建Python全栈开发环境With WSL 启动WSL 总结 对<Dev on Windows with WSL>的补充 Win10构建Python全栈开发环境With ...

  4. Python全栈开发【面向对象】

    Python全栈开发[面向对象] 本节内容: 三大编程范式 面向对象设计与面向对象编程 类和对象 静态属性.类方法.静态方法 类组合 继承 多态 封装 三大编程范式 三大编程范式: 1.面向过程编程 ...

  5. Python全栈开发【模块】

    Python全栈开发[模块] 本节内容: 模块介绍 time random os sys json & picle shelve XML hashlib ConfigParser loggin ...

  6. Python全栈开发【基础四】

    Python全栈开发[基础四] 本节内容: 匿名函数(lambda) 函数式编程(map,filter,reduce) 文件处理 迭代器 三元表达式 列表解析与生成器表达式 生成器 匿名函数 lamb ...

  7. Python全栈开发【基础三】

    Python全栈开发[基础三]  本节内容: 函数(全局与局部变量) 递归 内置函数 函数 一.定义和使用 函数最重要的是减少代码的重用性和增强代码可读性 def 函数名(参数): ... 函数体 . ...

  8. Python全栈开发【基础二】

    Python全栈开发[基础二] 本节内容: Python 运算符(算术运算.比较运算.赋值运算.逻辑运算.成员运算) 基本数据类型(数字.布尔值.字符串.列表.元组.字典) 其他(编码,range,f ...

  9. Python全栈开发【基础一】

    Python全栈开发[第一篇] 本节内容: Python 的种类 Python 的环境 Python 入门(解释器.编码.变量.input输入.if流程控制与缩进.while循环) if流程控制与wh ...

  10. python 全栈开发之路 day1

    python 全栈开发之路 day1   本节内容 计算机发展介绍 计算机硬件组成 计算机基本原理 计算机 计算机(computer)俗称电脑,是一种用于高速计算的电子计算机器,可以进行数值计算,又可 ...

随机推荐

  1. JavaSE学习总结(六)——接口、抽象类、内部类

    一.不需要实例化的原因 看一个示例: package com.zhangguo.chapter5.s1; /**动物园*/ public class Zoo { public static void ...

  2. 在同一个类中,一个方法调用另外一个有注解(比如@Async,@Transational)的方法,注解失效的原因和解决方法

    参考原贴地址:https://blog.csdn.net/clementad/article/details/47339519 在同一个类中,一个方法调用另外一个有注解(比如@Async,@Trans ...

  3. Calendar add 方法 和set方法

    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Calendar c = Calendar. ...

  4. C# Winform中慎用Application.DoEvents

    private void Add() { ; i < ; i++) { Button button = new Button(); button.Width = ; button.Height ...

  5. Java——分页 Servlet + Jsp+Jdbc 有点瑕疵

    1.创建数据库,插入多条数据 2.java连接DB 3.Person类: package com.phome.po; public class Person { private int id; pri ...

  6. C# 一般处理程序下载文件

    using System; using System.Collections; using System.Data; using System.Linq; using System.Web; usin ...

  7. 并行动画组QParallelAnimationGroup

    QParallelAnimationGroup会同时执行添加到该组的所有动画 import sys from PyQt5.QtGui import QPixmap from PyQt5.QtCore ...

  8. 液晶数字显示屏QLCDNumbe

    import sys from PyQt5.QtWidgets import QApplication, QWidget, QLCDNumber, QVBoxLayout class Demo(QWi ...

  9. vue插件开发实践与要点

    其实就跟组件差不多意思,组件也可以实现相关的效果,但要在用到的地方都引用插件就可以全局注册,不需引用 试着撸一个插件,有2个功能,提示和对话框 网上找了个toast插件的代码,改了改,扩展加了个dia ...

  10. D - Milk Patterns (出现k次可重复的最长子串的长度)

    题目链接:https://cn.vjudge.net/contest/283743#problem/D 题目大意:给你n个数,然后问你出现m次的最长子串的长度. 具体思路:和上一篇博客的内容差不多,这 ...