网上商城之支付
支付宝介绍
支付宝开放平台入口
- https://open.alipay.com/platform/home.htm
1. 创建应用和沙箱环境
- 创建应用
- 沙箱环境
支付宝提供给开发者的模拟支付的环境。跟真实环境是分开的。
这个不至于把钱付出去,钱可以自己去冲
- 沙箱应用:https://openhome.alipay.com/platform/appDaily.htm?tab=info
请求支付宝提供服务时,要向这个服务器发送请求
密钥是用于保证信息安全的
密钥就是用来打开数据箱子的,这里有两把钥匙,缺一把都不行
- 沙箱账号:https://openhome.alipay.com/platform/appDaily.htm?tab=account
商家的钱只会多,买家的钱只会少
沙箱应用就是开发阶段用的
2. 支付宝开发文档
对接开发平台就去找文档,点击电脑网站,查看快速接入文档
- 文档主页:https://openhome.alipay.com/developmentDocument.htm
- 电脑网站支付产品介绍:https://docs.open.alipay.com/270
- 电脑网站支付快速接入:https://docs.open.alipay.com/270/105899/
- API列表:https://docs.open.alipay.com/270/105900/
- SDK文档:https://docs.open.alipay.com/270/106291/
- Python支付宝SDK:https://github.com/fzlee/alipay/blob/master/README.zh-hans.md
- SDK安装:
pip install python-alipay-sdk --upgrade
3. 电脑网站支付流程
1.1 用来索取支付宝支付的页面,涉及第一次使用
6,7,8 是返回支付状态的三种方式
选方式6,支付完成之后收到结果之后再进行跳转
4. 配置RSA2公私钥
提示:
- 美多商城私钥加密数据,美多商城公钥解密数据。
- 支付宝私钥加密数据,支付宝公钥解密数据。
- 生成美多商城公私钥
openssl是一个命令
$ openssl
# 生成rsa标准的私钥,强度为2048 -out 指定输出文件
$ OpenSSL> genrsa -out app_private_key.pem 2048 # 制作私钥RSA2
# 使用私钥制作公钥
$ OpenSSL> rsa -in app_private_key.pem -pubout -out app_public_key.pem # 导出公钥
$ OpenSSL> exit
- 配置美多商城公私钥
-
配置美多商城私钥
- 新建子应用payment,在该子应用下新建文件夹keys用于存储公私钥。
- 将制作的美多商城私钥app_private_key.pem拷贝到keys文件夹中。
-
配置美多商城公钥
- 将payment.keys.app_public_key.pem文件中内容上传到支付宝。
- 配置支付宝公钥
- 将支付宝公钥内容拷贝到payment.keys.alipay_public_key.pem文件中。
-----BEGIN PUBLIC KEY-----
支付宝公钥内容
-----END PUBLIC KEY-----
配置公私钥结束后
对接支付宝系统
订单支付功能
提示:
- 订单支付触发页面:《order_success.html》 和 《user_center_order.html》
- 我们实现订单支付功能时,只需要向支付宝获取登录链接即可,进入到支付宝系统后就是用户向支付宝进行支付的行为。
- 请求方式
选项 | 方案 |
---|---|
请求方法 | GET |
请求地址 | /payment/(?P<order_id>\d+)/ |
- 请求参数:路径参数
参数名 | 类型 | 是否必传 | 说明 |
---|---|---|---|
order_id | int | 是 | 订单编号 |
- 响应结果:JSON
字段 | 说明 |
---|---|
code | 状态码 |
errmsg | 错误信息 |
alipay_url | 支付宝登录链接 |
- 后端接口定义和实现
# 测试账号:pqcanx4910@sandbox.com
class PaymentView(LoginRequiredJSONMixin, View):
"""订单支付功能"""
def get(self,request, order_id):
# 查询要支付的订单
user = request.user
try:
order = OrderInfo.objects.get(order_id=order_id, user=user, status=OrderInfo.ORDER_STATUS_ENUM['UNPAID'])
except OrderInfo.DoesNotExist:
return http.HttpResponseForbidden('订单信息错误')
# 创建支付宝支付对象,直接从github文档复制,这三步都是从文档中获得的,可以直接拷贝到自己的项目中
alipay = AliPay(
appid=settings.ALIPAY_APPID,
app_notify_url=None, # 默认回调url
app_private_key_path=os.path.join(os.path.dirname(os.path.abspath(__file__)), "keys/app_private_key.pem"),
alipay_public_key_path=os.path.join(os.path.dirname(os.path.abspath(__file__)), "keys/alipay_public_key.pem"),
sign_type="RSA2",
debug=settings.ALIPAY_DEBUG
)
# 生成登录支付宝连接,直接复制
order_string = alipay.api_alipay_trade_page_pay(
out_trade_no=order_id,
total_amount=str(order.total_amount),
subject="美多商城%s" % order_id,
return_url=settings.ALIPAY_RETURN_URL,
)
# 响应登录支付宝连接
# 真实环境电脑网站支付网关:https://openapi.alipay.com/gateway.do? + order_string
# 沙箱环境电脑网站支付网关:https://openapi.alipaydev.com/gateway.do? + order_string
alipay_url = settings.ALIPAY_URL + "?" + order_string
return http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK', 'alipay_url': alipay_url})
- 支付宝SDK配置参数
ALIPAY_APPID = '2016082100308405'
ALIPAY_DEBUG = True
ALIPAY_URL = 'https://openapi.alipaydev.com/gateway.do'
ALIPAY_RETURN_URL = 'http://www.meiduo.site:8000/payment/status/'
笔记
扫码链接页面由支付宝交给美多,美多响应给用户
3. 内置模块直接import
4.
将参数写到配置文件中
-
github python 接口名为 上面的接口 . 换成 _ 前面添加api -
http 模块直接位于Django包下
-
可以从模块导具体内容,也可从包导模块
-
10.
输入测试账号
付款成功会进行页面跳转
保存订单支付结果
1. 支付结果数据说明
-
用户订单支付成功后,支付宝会将用户重定向到 http://www.meiduo.site:8000/payment/status/,并携带支付结果数据。
-
参考统一收单下单并支付页面接口:https://docs.open.alipay.com/270/alipay.trade.page.pay
提示:
我们需要将订单编号和交易流水号进行关联存储,方便用户和商家后续使用。
2. 定义支付结果模型类
class Payment(BaseModel):
"""支付信息"""
order = models.ForeignKey(OrderInfo, on_delete=models.CASCADE, verbose_name='订单')
trade_id = models.CharField(max_length=100, unique=True, null=True, blank=True, verbose_name="支付编号")
class Meta:
db_table = 'tb_payment'
verbose_name = '支付信息'
verbose_name_plural = verbose_name
3. 保存订单支付结果
- 请求方式
选项 | 方案 |
---|---|
请求方法 | GET |
请求地址 | /payment/status/ |
- 请求参数:路径参数
参考统一收单下单并支付页面接口中的《页面回跳参数》
- 响应结果:HTML
pay_success.html
- 后端接口定义和实现
注意:保存订单支付结果的同时,还需要修改订单的状态为待评价
# 测试账号:pqcanx4910@sandbox.com
class PaymentStatusView(View):
"""保存订单支付结果"""
def get(self, request):
# 获取前端传入的请求参数
query_dict = request.GET
data = query_dict.dict()
# 获取并从请求参数中剔除signature
signature = data.pop('sign')
# 创建支付宝支付对象
alipay = AliPay(
appid=settings.ALIPAY_APPID,
app_notify_url=None,
app_private_key_path=os.path.join(os.path.dirname(os.path.abspath(__file__)), "keys/app_private_key.pem"),
alipay_public_key_path=os.path.join(os.path.dirname(os.path.abspath(__file__)), "keys/alipay_public_key.pem"),
sign_type="RSA2",
debug=settings.ALIPAY_DEBUG
)
# 校验这个重定向是否是alipay重定向过来的
success = alipay.verify(data, signature)
if success:
# 读取order_id
order_id = data.get('out_trade_no')
# 读取支付宝流水号
trade_id = data.get('trade_no')
# 保存Payment模型类数据
Payment.objects.create(
order_id=order_id,
trade_id=trade_id
)
# 修改订单状态为待评价(直接进行拷贝)
OrderInfo.objects.filter(order_id=order_id, status=OrderInfo.ORDER_STATUS_ENUM['UNPAID']).update(
status=OrderInfo.ORDER_STATUS_ENUM["UNCOMMENT"])
# 响应trade_id
context = {
'trade_id':trade_id
}
return render(request, 'pay_success.html', context)
else:
# 订单支付失败,重定向到我的订单
return http.HttpResponseForbidden('非法请求')
- 渲染支付成功页面信息
<div class="common_list_con clearfix">
<div class="order_success">
<p><b>订单支付成功</b></p>
<p>您的订单已成功支付,支付交易号:{{ trade_id }}</p>
<p><a href="{{ url('orders:info', args=(1, )) }}">您可以在【用户中心】->【我的订单】查看该订单</a></p>
</div>
</div>
笔记
-
out_trade_no 和 trade_no 是订单号,第一个是美多自己的,第二个是支付宝的
sign 是用来保证安全的,判断是支付宝回调过来的,还是黑客回调过来的 -
这是支付时传送给支付宝的参数
支付宝传送回调地址时,会将参数进行加密并放在sign字段中
当用户接受到时候应将sign去掉,进行加密,然后对比sign和加密后的结果是不是一样,如果一样是支付宝发的,如果不是,是黑客发的 -
request.form 为queryDict , to_dict 转换标准字典
pop移除sign -
request.GET 查询到的是QueryDict类型的对象,要通过dict 转换为字典
-
提取的数据名,返回数据中查,也可以从开发文档中查
-
create的参数直接从models 中粘贴过来就行 -
在填写参数的过程中,数据项和逗号之间可以有空白符
-
弹出字典中数据 -
10.queryDict 只提供了query 和queryList ,所以要转换字典 -
建议对接一下其他的功能
评价订单商品(项目实训时完成)
提示:
点击《我的订单》页面中的《待评价》按钮,进入到订单商品评价页面。
评价订单商品(只需查询未评价的订单)
1. 展示商品评价页面
- 请求方式
选项 | 方案 |
---|---|
请求方法 | GET |
请求地址 | /orders/comment/ |
- 请求参数:查询参数
参数名 | 类型 | 是否必传 | 说明 |
---|---|---|---|
order_id | int | 是 | 订单编号 |
- 响应结果:HTML
goods_judge.html
- 后端接口定义和实现
class OrderCommentView(LoginRequiredMixin, View):
"""订单商品评价"""
def get(self, request):
"""展示商品评价页面"""
# 接收参数
order_id = request.GET.get('order_id')
# 校验参数
try:
OrderInfo.objects.get(order_id=order_id, user=request.user)
except OrderInfo.DoesNotExist:
return http.HttpResponseNotFound('订单不存在')
# 查询订单中未被评价的商品信息
try:
uncomment_goods = OrderGoods.objects.filter(order_id=order_id, is_commented=False)
except Exception:
return http.HttpResponseServerError('订单商品信息出错')
# 构造待评价商品数据
uncomment_goods_list = []
for goods in uncomment_goods:
uncomment_goods_list.append({
'order_id':goods.order.order_id,
'sku_id':goods.sku.id,
'name':goods.sku.name,
'price':str(goods.price),
'default_image_url':goods.sku.default_image.url,
'comment':goods.comment,
'score':goods.score,
'is_anonymous':str(goods.is_anonymous),
})
# 渲染模板
context = {
'uncomment_goods_list': uncomment_goods_list
}
return render(request, 'goods_judge.html', context)
2. 评价订单商品(往数据库中填一下就行了)
- 请求方式
选项 | 方案 |
---|---|
请求方法 | POST |
请求地址 | /orders/comment/ |
- 请求参数:查询参数
参数名 | 类型 | 是否必传 | 说明 |
---|---|---|---|
order_id | int | 是 | 订单编号 |
- 响应结果:JSON
字段 | 说明 |
---|---|
code | 状态码 |
errmsg | 错误信息 |
- 后端接口定义和实现
class OrderCommentView(LoginRequiredMixin, View):
"""订单商品评价"""
def get(self, request):
"""展示商品评价页面"""
......
def post(self, request):
"""评价订单商品"""
# 接收参数
json_dict = json.loads(request.body.decode())
order_id = json_dict.get('order_id')
sku_id = json_dict.get('sku_id')
score = json_dict.get('score')
comment = json_dict.get('comment')
is_anonymous = json_dict.get('is_anonymous')
# 校验参数
if not all([order_id, sku_id, score, comment]):
return http.HttpResponseForbidden('缺少必传参数')
try:
OrderInfo.objects.filter(order_id=order_id, user=request.user, status=OrderInfo.ORDER_STATUS_ENUM['UNCOMMENT'])
except OrderInfo.DoesNotExist:
return http.HttpResponseForbidden('参数order_id错误')
try:
sku = SKU.objects.get(id=sku_id)
except SKU.DoesNotExist:
return http.HttpResponseForbidden('参数sku_id错误')
if is_anonymous:
if not isinstance(is_anonymous, bool):
return http.HttpResponseForbidden('参数is_anonymous错误')
# 保存订单商品评价数据
OrderGoods.objects.filter(order_id=order_id, sku_id=sku_id, is_commented=False).update(
comment=comment,
score=score,
is_anonymous=is_anonymous,
is_commented=True
)
# 累计评论数据
sku.comments += 1
sku.save()
sku.spu.comments += 1
sku.spu.save()
# 如果所有订单商品都已评价,则修改订单状态为已完成
if OrderGoods.objects.filter(order_id=order_id, is_commented=False).count() == 0:
OrderInfo.objects.filter(order_id=order_id).update(status=OrderInfo.ORDER_STATUS_ENUM['FINISHED'])
return http.JsonResponse({'code': RETCODE.OK, 'errmsg': '评价成功'})
详情页展示评价信息
- 请求方式
选项 | 方案 |
---|---|
请求方法 | POST |
请求地址 | /comments/(?P<sku_id>\d+)/ |
- 请求参数:查询参数
参数名 | 类型 | 是否必传 | 说明 |
---|---|---|---|
sku_id | int | 是 | 商品SKU编号 |
- 响应结果:JSON
字段 | 说明 |
---|---|
code | 状态码 |
errmsg | 错误信息 |
comment_list[ ] | 评价列表 |
username | 发表评价的用户 |
comment | 评价内容 |
score | 分数 |
{
"code":"0",
"errmsg":"OK",
"comment_list":[
{
"username":"itcast",
"comment":"这是一个好手机!",
"score":4
}
]
}
- 后端接口定义和实现
class GoodsCommentView(View):
"""订单商品评价信息"""
def get(self, request, sku_id):
# 获取被评价的订单商品信息
order_goods_list = OrderGoods.objects.filter(sku_id=sku_id, is_commented=True).order_by('-create_time')[:30]
# 序列化
comment_list = []
for order_goods in order_goods_list:
username = order_goods.order.user.username
comment_list.append({
'username': username[0] + '***' + username[-1] if order_goods.is_anonymous else username,
'comment':order_goods.comment,
'score':order_goods.score,
})
return http.JsonResponse({'code':RETCODE.OK, 'errmsg':'OK', 'comment_list': comment_list})
- 渲染商品评价信息
<div @click="on_tab_content('comment')" class="tab_content" :class="tab_content.comment?'current':''">
<ul class="judge_list_con">
<li class="judge_list fl" v-for="comment in comments">
<div class="user_info fl">
<b>[[comment.username]]</b>
</div>
<div class="judge_info fl">
<div :class="comment.score_class"></div>
<div class="judge_detail">[[comment.comment]]</div>
</div>
</li>
</ul>
</div>
<li @click="on_tab_content('comment')" :class="tab_content.comment?'active':''">商品评价([[ comments.length ]])</li>
<div class="price_bar">
<span class="show_pirce">¥<em>{{ sku.price }}</em></span>
<a href="javascript:;" class="goods_judge">[[ comments.length ]]人评价</a>
</div>
提示:订单商品评价完成后,一个订单的流程就结束了,订单状态修改为已完成。
笔记
- 对接第三方工具时,一般有平台
- 只要对接第三方工具,就要成为开发者,有支付宝就有开发者
对接的使用要使用appid
点击支付接入就可以
创建的是真实应用,可能会在开发的时候把钱给支付宝- 在github上有支付宝的SDK
有新版本直接更新,没有新版本下载最新的就行
正式支付的请求地址
这是开发环境
7. 公共参数是指对于各个接口都要进行传送
8.
对于接口而言,只有必传的还有用户相关的才会暴露给用户
这里主要填写公共参数
这里填写独有参数
-
python版本的SDK在github上
10.
了解支付宝提供的接口 -
支付宝使用rsa加密算法,这是一种非对称加密算法(加密和解密的钥匙不一样),加密的强度非常大,算法很复杂,性能很差,只能用来加密小数据,加密大数据一般用对称加密算法(加密和解密的钥匙一样)
-
私钥越复杂加密的结果越安全,
-
列表可换行,写成
[
表达式1,
表达式2,
]
的格式 -
对于搭建开发环境只需要pip install 就可以
-
debug模式检查代码运行方向和参数情况
-
评价的代码不会自己去看
-
至此项目所有功能已经完成