【DailyFresh】课程记录7---订单模块(支付)

return_url

notify_url

这两个URL是django网站的地址

传入这些参数之后,支付宝平台会帮你生成一个支付订单,在处理完成之后,最终会返回一个应答

这个应答:返回一个支付页面的地址

拿到支付页面的地址之后给客户用,在客户的浏览器上要把页面显示出来。

因此拿到支付页面的地址有一个过程,该过程就是把用户引导到页面,让用户的浏览器能够打开支付页面。

接下来用户到支付页面登录账号,输入支付密码,这一过程和我们的网站无关,我们负责将用户引导至支付页面,下面是用户和支付宝的交互:用户登录支付宝,输入支付密码,点击确认支付。

当用户和支付宝进行交互的时候,我们无法确认用户是否支付完毕《—这里考虑上面提到的两个URL:return_url和 notify_url

只要用户付完款之后一点击确认支付,传过去的参数中传过去地址return_url,那么支付宝的平台在用户支付完成之后就会马上同步访问网站的return_url,并传递参数,告诉网站用户支付的结果《—此为同步访问

异步访问网站的notify_url,并传递参数,告诉网站用户支付的结果《—此为异步访问

支付宝在传递支付结果的时候是以异步通知为准

网站如果想让支付宝平台访问,需要有公网的IP,单纯的127.0.0.1支付宝无法访问。

这里没有公网IP的情况下,如何获取支付的结果?

目前我们可以访问支付宝,但是支付宝无法访问我们的网站。

这里有个alipay.trade.query统一收单线下交易查询接口,虽然return_url和 notify_url这两个地址不起作用,但是我们获取支付结果的话可以通过网站自己去访问支付宝,调用支付宝的接口alipay.trade.query,这个接口在处理完之后就会返回用户支付的结果

PS:最后5min是精髓《–总结整个支付流程

P97 订单支付——订单支付代码

目前支付流程已经明朗,如果我去付款的话,访问网站的地址/order/pay需不需要传参数?

需要传递订单id,用户要支付的是哪个订单,需要把订单id传过来,这一步涉及到传参数:订单id

传递参数的方式分析:

用户点击去付款,需要给django传递参数,因为用户支付完之后,会有一个支付的交易编号,我们最终要把支付的交易编号保存到表中,所以这里采用post。

在django网站中调用支付宝接口alipay.trade.page.pay,原生的参数需要自己调用,自己传参,这里有一个SDK的Python包:

https://github.com/fzlee/alipay/blob/master/README.zh-hans.md

别人封装好的,已经生成预付订单需要使用的签名

安装

pip3 install python-alipay-sdk --upgrade

在代码中使用包

截图:采用网络请求方式

生成自己电脑的公钥和私钥

使用openssl

openssl

OpenSSL> genrsa -out app_private_key.pem   2048  # 私钥 gen生成rsa代表算法 -out代表输出一个秘钥文件,输出到app_private_key.pem中

OpenSSL> rsa -in app_private_key.pem -pubout -out app_public_key.pem # 导出公钥 -in代表输入 输入刚才生成的私钥文件的内容,对它进行输出,输出到app_public_key.pem公钥文件中《—生成一个与私钥文件对应的公钥文件

OpenSSL> exit

# 查看私钥内容

cat app_private_key.pem

# 查看公钥内容

cat app_public_key.pem

接下来去沙箱环境中设置公钥,拷贝刚才生成的公钥内容到沙箱中

之后会看到有:查看支付宝公钥,将该公钥保存到项目的文件中

该公钥与订单相关,在订单下新建文件,

order/alipay_public_key.pem<—将支付宝的公钥放到文件该文件中,代表支付宝的公钥

注:这里在文件的开头和结尾加上-----BEGIN PUBLIC KEY-----和-----END PUBLIC KEY-----

加密还需要电脑的私钥,将其获取到,放入order文件夹下order/app_private_key.pem

请求的时候使用自己的私钥加密,收到信息之后使用支付宝的公钥解密

创建初始化对象

appid=“”<—沙箱中的APPid

# notify_url

# 应用文件中私钥的路径

# 支付宝的公钥路径

# 签名的类型、算法RSA或RSA2

#debug=False代表不是调试模式,即访问的是实际环境地址,如果是沙箱的话,要改成True

# 电脑网站支付:需要调用alipay.trade.page.pay接口,

这里帮我们封装了一个函数,函数名为alipay.api_alipay_trade_page_pay接口(按照命名转化规则进行转化),该方法是上面初始化对象的一个方法,该方法内部帮我们调用支付宝的支付接口,传参:订单id、总金额、订单标题、return_url、notify_url,最终会返回一个order_string。电脑网站支付,需要跳转到xxx页面,该页面就是支付页面。

以上为支付的使用,接下来分析在views中订单支付的视图该如何实现

# 对应地址: /order/pay

# 请求方式:ajax post

# 前端传递的参数:订单id(order_id)

class OrderPayView(View):

‘’‘订单支付’‘’

def post(self, request):

‘’‘订单支付’‘’

关于订单支付,同样的当用户未登录时不能支付

校验用户是否登录

user = request.user

if not user.is_authenticated():

return JsonResponse({‘res’: 0, ‘errmsg’: ‘用户未登录’})

接收参数 订单id

order_id = request.POST.get(‘order_id’)

校验参数

先判断有没有订单id,处理没有订单id的情况

if not order_id:

return JsonResponse({‘res’: 0, ‘errmsg’: ‘无效的订单id’})

查询一下订单id是不是确实存在

订单id到底是不是该用户的

支付结果需要对接支付宝,这里判断支付方式到底是不是支付宝

查看支付状态

满足以上条件且订单确实存在,则说明该订单为有效订单

try:

order = OrderInfo.objects.get(order_id=order_id,

user=user,

pay_method=3,

order_status=1)

except OrderInfo.DoesNotExist:

return JsonResponse({‘res’: 2, ‘errmsg’: ‘订单错误’})

业务处理:使用Python sdk调用支付宝的支付接口

使用我们安装的包

先去初始化一个对象

alipay = AliPay(…)

调用对象的方法—调用支付接口

电脑支付接口《—拿过来并传入必要的参数

电脑网站支付,需要跳转到https://openapi.alipay.com/gateway.do? + order_string<—实际地址

https://openapi.alipaydev.com/gateway.do? + order_string<—沙箱地址

我们在模型类中使用的类型是Decimal,这里拿过来的也是Decimal类型

total_pay = order.total_price + order.transit_price

直接放在这里的话,内部都会将数据转化成json,Decimal是不能被序列化的,如果直接写Decimal是会报错的

这里将其直接转化成字符串

order_string = alipay.api_alipay_trade_page_pay(

out_trade_no="order_id, # 订单id

total_amount=str(total_pay), # 支付总金额,应该等于总金额+运费,在上面进行计算

subject=‘天天生鲜%s’ %order_id, # 标题

return_url=None,

notify_url=None # 可选, 不填则使用默认notify url

)

返回应答

pay_url = ‘https://openapi.alipaydev.com/gateway.do?’ + order_string

最终返回数据,将地址带回去

在代码中可以将用户引导到该地址pay_url

return JsonResponse({‘res’: 3, ‘pay_url’: pay_url})

# 业务处理:使用Python sdk调用支付宝的支付接口

# 使用我们安装的包

# 导入包

from alipay import AliPay

# 进行初始化

# 关于path,借助settings.py以及os

from django.conf import settings

import os

# app_private_key_string = os.path.join(settings.BASE_DIR, ‘apps/order/app_private_key.pem’)

# alipay_public_key_string = os.path.join(settings.BASE_DIR, ‘apps/order/alipay_public_key.pem’)

alipay = AliPay(

appid=“2016092700608687”,  # 应用id《—沙箱中的id

app_notify_url=None,  # 默认回调url《—这里是127.0.0.1,所以即使传了支付宝也访问不了,这里不能写空 ‘’,必须写None,代表不传

app_private_key_string=app_private_key_string,

支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,

alipay_public_key_string=alipay_public_key_string,

sign_type=“RSA2”,  # RSA 或者 RSA2《—官网推荐RSA2

debug=True  # 默认False, 此处沙箱模拟True

)

# 整个过程:初始化之后,开始调用接口,最终要把支付页面地址返回

对应的地址是/order/pay,进行URL配置

path(‘pay’, OrderPayView.as_view(), name=‘pay’) # 订单支付

# 至此后端结束

# 分析前端user_center_order.html

在页面上点击去付款,会访问支付宝支付页面地址,这里也有可能显示:已完成、待评价,这样就不应该访问该地址,所以需要进行相应的判断。《–根据支付状态

这里应该绑定【去付款】出class="oper_btn"的点击事件

{% load static %} 《—导入js

{% block bottomfiles %}

{% endblock bottomfiles %}

P93 订单支付——获取支付结果

点击去支付,会跳转到支付宝的支付页面,要登录账号

进入沙箱找到支付的账号

在支付页面输入用户密码并进行付款,付款成功之后在用户的页面上会出现付款成功,但是目前我们的网站还不知道用户已经付款。这个是支付宝给用户看的,我们的网站并不知道结果,因为我们没有提供地址return_url、notify_url,支付宝无法告知我们是支付结果,但是网站又想获取支付结果,所以需要自己去调用支付宝的交易查询接口alipay.trade.query,获取用户支付结果,最终告诉用户是否支付成功,给用户显示支付结果。

如果用户的浏览器不访问我们的页面,我们就无法访问它,不能告诉用户支付结果。

用户如果不访问我们,便无法告诉用户支付结果。这里需要再添加一部分,需要让用户的浏览器去访问我们的网站,查询支付结果到底有没有成功。

【用户浏览器,访问/order/check获取交易支付结果】《----用户只要进行支付操作,就将交易结果显示给用户。

只要把用户引导到支付页面,写一段js代码,让他往服务器发一段请求,发请求的目的就是为了获取支付结果

// 浏览器访问/order/check,获取支付交易的结果《—考虑这里需不需要传参数?

// ajax post 传递参数:order_id

在回调函数对应data,data中应该告知我们有没有支付成功,根据data的返回结果告知用户该订单支付有没有成功

编写对应的视图函数,在初始化之前都与查询支付结果一致,最后调用的支付宝接口不同

# 请求方式: ajax post

# 前端请求的参数: 订单id(order_id)

# order/pay

class OrderPayView(View):

“”“订单支付”“”

def post(self, request):

用户是否登录

user = request.user

if not user.is_authenticated:

return JsonResponse({‘res’: 0, ‘errmsg’: ‘用户未登录’})

接收参数

order_id = request.POST.get(‘order_id’)

校验参数

if not order_id:

return JsonResponse({‘res’: 1, ‘errmsg’: ‘无效的订单’})

try:

order = OrderInfo.objects.get(order_id=order_id,

user=user,

pay_method=3,

order_status=1)

except OrderInfo.DoesNotExist:

return JsonResponse({‘res’: 2, ‘errmsg’: ‘订单错误’})

业务处理:使用python sdk调用支付宝的查询接口

alipay初始化

app_private_key_string = open(“apps/order/app_private_key.pem”).read()

alipay_public_key_string = open(“apps/order/alipay_public_key.pem”).read()

app_private_key_string == “”"

-----BEGIN RSA PRIVATE KEY-----

base64 encoded content

-----END RSA PRIVATE KEY-----

“”"

alipay_public_key_string == “”"

-----BEGIN PUBLIC KEY-----

base64 encoded content

-----END PUBLIC KEY-----

“”"

app_private_key_string = os.path.join(settings.BASE_DIR, ‘apps/order/app_private_key.pem’)

alipay_public_key_string = os.path.join(settings.BASE_DIR, ‘apps/order/alipay_public_key.pem’)

alipay = AliPay(

appid=“2016092700608687”,  # 应用id

app_notify_url=None,  # 默认回调url

app_private_key_string=app_private_key_string,

支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,

alipay_public_key_string=alipay_public_key_string,

sign_type=“RSA2”,  # RSA 或者 RSA2

debug=True  # 默认False, 此处沙箱模拟True

)

调用支付宝的交易查询接口

while True:

response = alipay.api_alipay_trade_query(order_id)

response = {

“trade_no”: “2017032121001004070200176844”,  # 支付宝交易号

“code”: “10000”,  # 接口调用是否成功

“invoice_amount”: “20.00”,

“open_id”: “20880072506750308812798160715407”,

“fund_bill_list”: [

{

“amount”: “20.00”,

“fund_channel”: “ALIPAYACCOUNT”

}

],

“buyer_logon_id”: “csq***@sandbox.com”,

“send_pay_date”: “2017-03-21 13:29:17”,

“receipt_amount”: “20.00”,

“out_trade_no”: “out_trade_no15”,

“buyer_pay_amount”: “20.00”,

“buyer_user_id”: “2088102169481075”,

“msg”: “Success”,

“point_amount”: “0.00”,

“trade_status”: “TRADE_SUCCESS”,  # 支付结果

“total_amount”: “20.00”

}

查询接口调用是否成功

code = response.get(‘code’)

接口调用是否成功 && 支付是否成功

if code == ‘10000’ and response.get(‘trade_status’) == ‘TRADE_SUCCESS’:

支付成功

获取支付宝交易号

trade_no = response.get(‘trade_no’)

更新订单状态

order.trade_no = trade_no

order.order_status = 4  # 待评价

order.save()

返回应答

return JsonResponse({‘res’: 3, ‘message’: ‘支付成功’})

elif code == ‘40004’ or (code == ‘10000’ and response.get(‘trade_status’) == ‘WAIT_BUYER_PAY’):

等待买家付款

import time

time.sleep(5)

continue

else:

支付出错

return JsonResponse({‘res’: 4, ‘errmsg’: ‘支付失败’})

注:当code返回值为’40004’ 时,需要进行处理

处理js

$.post(‘/order/check’, params, function () {

if (data.res == 3){

alert(‘支付成功’)

//重新加载

location.reload()

}

else{

alert(data.errmsg)

}

})

配置order/chexk的URL,进行测试

P98 订单评论代码走读

目前页面已经可以查询支付结果,页面上已经是待评价,但是后面依旧显示【去付款】,此时应该显示【待评论】,点击之后会跳转到相应的评论页面。查看源代码可以看到,此时td中的自定义属性status=4,这部分可以通过在页面中添加js,根据状态status的不同,改成不同的内容。

评论的代码:遍历每个获取到的a标签,遍历的时候要获取到自定义属性status,

如果status=1,则设置为去支付,

如果status=4,则设置为去评价,

如果status=5,则设置为已完成。

订单评论URL的设置:

path(‘comment/(?P<order_id>.+)’, CommentView.as_view(), name=‘comment’) # 订单评价

下面如果用户去点击它,不同的状态对应不同的处理

{% block bottomfiles %}

{% endblock bottomfiles %}

测试js代码无响应时:检查元素–》network

评价页面的地址:/order/comment/order_id

点击去评价—》跳转到对应的页面 /order/comment/order_id,去路由系统中查找对应的views函数。

分析评价页面显示内容:

基本信息和订单页面显示基本一致,只需 在下方添加一个评论的输入框

view中的类视图:【get基与订单页面显示时的处理过程一致,无需详述】

class OrderCommentView(LoginRequiredMixin, View):

def get(self, request, order_id):

“”“展示评论页”“”

在页面order_comment.html中,前面商品显示和订单页面一致,在下方每个商品对应一个评论的输入框,评论框中定义了一个元素input隐藏域

name=“sku_{{ forloop.counter }}”

该元素对应的值:评论的时候需要接收是哪一个商品的评论,隐藏域对应的值是跟这个商品关联的商品id。商品对应的评论内容对应在输入的文本框中,文本框中对应的内容是 name=“content_{{ forloop.counter }}”

将其通通放置在表单中,在表单中还包含了两个隐藏域

其一是订单id,你需要对那个订单中的商品信息进行评论,在其中写一个隐藏域为 name=“order_id”,其值为订单id—》value=“{{ order.order_id }}”

以及订单中有几个商品total_count,其值为订单中跟订单关联的商品的长度,即订单中商品的个数

用户输入完后点击提交,订单中的商品都会被提交。

点击提交之后,代表向当前地址发起一个post请求,在对应的视图中会有一个对应的post函数,处理评论内容

在该部分,首先要在地址中获取到订单的id,获取到之后进行数据校验,订单中有几个商品就应该对应多少评论,将total_count值进行接收,接收之后就可以获取到评论一共有多少条。

# 循环获取订单中商品的评论内容《—注:每个商品对应的名字是不一样的,通过forloop.counter1234往下数,遍历几次就应该有几个,在处理的时候也应该通过遍历拿到对应的值,每找到一个就应该获取商品的id,以及对应的评论内容,将其赋值给order_goods的comment,并做一个更新。

如果获取到了所有的订单商品的评论且进行了更新,则将该订单的状态设置为已完成,进行更新,最终将页面跳转到订单页面

拿到数字之后需要循环获取商品的评论,从1到total_count+1,获取到的 第一次就是1,在循环中获取被评论的商品id,名字对应的值是商品的id,获取到值之后即可获取到这是哪一条商品的评论。获取到评论内容sku_1对应下面的content_1,其余同理。没有内容则返回空。

根据order和sku_id查询到order_goods中,并将评论内容写到对应的order_goods.comment中,如果出错,则continue,继续看下一个。如果查到了,则需要将order_goods中的内容修改为接收到的content,并进行保存。

进行测试:下单,评论

此时商品详情页的评论部分应该会显示评论内容(此时在商品介绍下面显示的,进行修改),在detail.html页面写一段js代码

此时对应的元素中添加id=“tag_detail”,id=“tag_comment”

    • 商品介绍
    • 评论
    • 商品详情:
      {{ sku.goods.detail|safe }}

      # style="display: none;默认不显示

      {% for sku in sku_orders %}

      评论时间: {{ sku.update\_time }} 用户名:{{ sku.order.user.username }}
      评论内容: {{ sku.comment }}

      {% empty %}

      暂时没有评论信息

      {% endfor %}

      商品介绍和评论只显示一个,且点击哪一个,哪一个应该处于激活状态

      class=‘active’

      // 商品介绍与评论tag的切换

      $(‘#tag_detail’).click(function () {

      $(‘#tag_comment’).removeClass(‘active’)

      $(this).addClass(‘active’)

      $(‘#tab_detail’).show()
      自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

      深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

      因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

      既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

      由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

      如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

      img

      Java高频面试专题合集解析:

      阿里Java岗面试百题:Spring 缓存 JVM 微服务 数据库 RabbitMQ等

      当然在这还有更多整理总结的Java进阶学习笔记和面试题未展示,其中囊括了Dubbo、Redis、Netty、zookeeper、Spring cloud、分布式、高并发等架构资料和完整的Java架构学习进阶导图!

      阿里Java岗面试百题:Spring 缓存 JVM 微服务 数据库 RabbitMQ等

      更多Java架构进阶资料展示

      阿里Java岗面试百题:Spring 缓存 JVM 微服务 数据库 RabbitMQ等

      阿里Java岗面试百题:Spring 缓存 JVM 微服务 数据库 RabbitMQ等

      阿里Java岗面试百题:Spring 缓存 JVM 微服务 数据库 RabbitMQ等
      《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
      索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!**

      因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。[外链图片转存中…(img-AV1QGeC1-1713391391888)]

      [外链图片转存中…(img-D6pJ9Tpj-1713391391888)]

      [外链图片转存中…(img-gGEXcDgO-1713391391889)]

      既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

      由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

      如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

      img

      Java高频面试专题合集解析:

      [外链图片转存中…(img-bPMbzPEm-1713391391889)]

      当然在这还有更多整理总结的Java进阶学习笔记和面试题未展示,其中囊括了Dubbo、Redis、Netty、zookeeper、Spring cloud、分布式、高并发等架构资料和完整的Java架构学习进阶导图!

      [外链图片转存中…(img-JGnHddd5-1713391391889)]

      更多Java架构进阶资料展示

      [外链图片转存中…(img-ecnru6II-1713391391889)]

      [外链图片转存中…(img-sI605PRo-1713391391889)]

      [外链图片转存中…(img-19fqHEQT-1713391391890)]
      《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

    • 11
      点赞
    • 14
      收藏
      觉得还不错? 一键收藏
    • 0
      评论
    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

    当前余额3.43前往充值 >
    需支付:10.00
    成就一亿技术人!
    领取后你会自动成为博主和红包主的粉丝 规则
    hope_wisdom
    发出的红包
    实付
    使用余额支付
    点击重新获取
    扫码支付
    钱包余额 0

    抵扣说明:

    1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
    2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

    余额充值