购物功能模块
项目babys的购物功能模块分为购物车页面和在线支付功能,购物车页面主要讲述如何将商品加入购物车并设置购买结算;在线支付功能以支付宝为例,讲述如何在项目中引入支付宝的支付接口。
购物车功能
购物车页面分为3个功能区域:商品搜索功能、网站导航、商品的购买费用核算,如图9-1所示。商品的购买费用核算允许用户编辑商品的购买数量、选择购买的商品和删除商品,结算按钮根据购买信息自动跳转到支付页面。
从图9-1看到,商品搜索功能和网站导航在首页、商品列表页和商品详细页已实现,整个购物车页面最主要的是实现商品的购买费用核算,其功能说明如下:
(1)在商品详细页单击“加入购物车”按钮,程序会触发JavaScript脚本代码,通过脚本代码访问购物车页面并把商品详细页的“数量”和商品的主键id作为购物车页面的请求参数,如图9-2所示。
(2)当浏览器访问购物车页面的时候,程序将获取路由地址的请求参数id和quantity,然后将这些参数写入模型CartInfos,记录当前用户的购物车信息。
(3)在购物车页面中,购物车列举了多条商品信息,每条信息设置勾选框、商品主图、名称、单价、数量、总价和删除按钮。勾选框用于勾选需要购买付款的商品;总价是当前商品的单价乘以数量计算得出;删除按钮可以删除当前的商品信息,即删除模型CartInfos对应的数据内容。
(4)购物车的最下方设有勾选框、“删除全部”按钮、应付费用和“结算”按钮,勾选框是对所有商品进行勾选操作,如果商品较多,用户只需单击该勾选框即可选中全部商品,无须在每个商品前面的勾选框逐个单击;“删除全部”按钮是删除当前用户全部商品信息;应付的价钱是对已勾选的商品进行价格汇总;“结算”按钮是执行支付功能,即向支付宝发起支付请求。
路由shopcart是定义购物车页面的网页地址,网页的业务逻辑是由视图函数shopcartView实现,我们在项目应用shopper的views.py定义视图函数logoutView,代码如下:
视图函数shopcartView定义了变量title和classContent,这是用于设置共用模板base.html的模板变量;然后从当前请求中获取请求参数id和quantity,以及当前用户的主键id;最后分别对模型CartInfos和CommodityInfos进行数据操作,整个业务逻辑的详细过程说明如下:(1)使用Django的内置装饰器login_required设置用户的登录访问权限,如果当前用户在尚未登录的状态下访问购物车页面,程序就会自动跳转到指定的路由地址,即用户注册登录页,只有用户完成登录后才能允许访问购物车页面。
(2)从当前请求中获取请求参数id和quantity,以及当前用户的主键id,并且分别赋值给变量id、quantity和userID,由于视图函数设置了装饰器login_required,所以能通过当前请求对象request获取用户的相关信息。
(3)判断变量id是否为空,如果不为空,则说明当前请求设有请求参数id和quantity,证明了该请求是用户通过单击商品详细页的“加入购物车”按钮所触发的,因此需要将变量id、quantity和userID新增或更新到购物车信息表(即模型CartInfos),模型CartInfos调用update_or_create()方法可以实现数据的新增或更新操作;最后重新访问路由shopcart,再次执行视图shopcartView。
(4)变量getUserId是在模型CartInfos查询当前用户的购物车信息,生成查询对象getUserId;然后对查询对象getUserId进行遍历,将模型字段quantity和commodityInfos_id转换为字典格式,并以变量commodityDcit表示。
(5)由于模型CartInfos只记录了商品购买数量和商品的主键id,而购物车页面记录了商品主图、名称和单价,这些商品信息只记录在模型CommodityInfos,所以我们从变量commodityDcit获取字典所有的键(即当前用户购物车所有商品的主键id),并作为模型CommodityInfos的查询条件,从而得到购物车所有商品的主图、名称和单价。
视图函数shopcartView使用模板文件shopcart.html作为响应内容,下一步在PyCharm中打开模板文件shopcart.html,针对视图函数shopcartView定义的变量编写相应的模板语法,详细代码如下:
模板文件shopcart.html按照功能划分可分为3个部分,在代码中依次标记了①②③,每个部分的功能说明如下:
(1)标注①是调用模板文件base.html并重写接口content,然后遍历变量commodityInfos生成购物车的商品列表,每次遍历对象c代表模型CommodityInfos的某条数据,购物车的商品列表展示了商品主图、名称、单价、数量、总价和删除按钮。其中商品数量是通过遍历commodityDcit和判断遍历对象c的主键id,从commodityDcit中获取准确的商品数量。
(2)标注②是实现购物车最下方的勾选框、“删除全部”按钮、应付费用和“结算”按钮。其中“删除全部”按钮设置隐藏控件p,它使用内置模板变量user记录了当前用户的主键id,便于JavaScript实现全部商品删除。
(3)标注③是重写base.html的接口script,首先是引入静态文件car.js的脚本代码,然后还编写了购物车最下方的应付费用的计算方法和“结算”按钮的触发事件。“结算”按钮首先获取购物车最下方的应付费用,然后将应付费用作为路由pays的请求参数total,最后由浏览器访问带请求参数total的路由pays。
Ajax删除购物车的商品
从购物车页面看到,每一条商品信息设有“删除”按钮,并且在购物车最下方还设有“删除全部”按钮,换句话说,删除购物车的商品信息可分为两种情况:单独删除某条商品信息和删除全部商品信息。
不管是哪一种商品删除方式,我们都可以将两者放在同一个功能里实现,只需设置不同的删除条件,从而执行不同的删除操作即可。删除商品可以使用Ajax实现,无须重新加载整个网页,只要更新部分网页内容即可,既能提高用户体验,又能减少网页加载时间。
首先在项目应用shopper的urls.py定义API接口,该接口由Ajax访问,实现后台数据操作和网页动态渲染,API接口命名为路由delete,定义过程如下:
路由delete的HTTP请求交由视图函数deleteAPI处理和响应,下一步在项目应用shopper的views.py定义视图函数deleteAPI,代码如下:
视图函数deleteAPI分别定义了变量result、userId、commodityId,每个变量的功能以及函数的处理过程说明如下:
(1)变量result以字典格式表示,它将作为HTTP的响应内容,字典的state等于success则说明该请求成功地执行了商品删除操作;如果字典的state等于fail则说明商品删除失败。
(2)变量userId用于获取请求参数userId,如果不存在请求参数userId,则变量userId设为空字符;请求参数userId代表当前用户的主键id。
(3)变量commodityId用于获取请求参数commodityId,如果该请求参数不存在,则变量commodityId设为空字符;请求参数commodityId代表某一商品的主键id。
(4)视图函数首先判断变量userId是否为空,如果不为空,则说明当前请求是删除全部商品,代表用户是单击了“删除全部”按钮;如果变量userId为空,程序继续判断变量commodityId是否为空,如果变量commodityId不为空,则说明当前请求是单独删除某一商品,代表用户单击某商品的“删除”按钮;如果变量userId和commodityId皆为空,则说明当前请求没有设置任何请求参数,将变量result的state改为fail,说明当前请求不是删除商品操作。
(5)最后使用Django内置函数JsonResponse将变量result转换为JSON格式,并作为当前HTTP请求的响应内容。
完成API接口(路由delete)的定义之后,我们可以在网页中编写Ajax脚本代码调用API接口。分析模板文件shopcart.html可知,标注③是重写base.html的接口script,并且引入了静态文件car.js的脚本代码。我们在PyCharm中打开静态文件car.js,其脚本代码如下:
从静态文件car.js看到,该文件在变量car的初始化过程(即var car={init : function(){}})中定义了5个函数方法,每个函数方法依次标记了①②③④⑤,详细的功能说明如下:(1)标注①是对已勾选的商品逐一累计应付费用和数量,并将计算结果分别显示在购物车最下方的应付费用和已选数量。函数首先遍历购物车的每条商品,然后对每条商品的勾选框状态进行判断,如果当前遍历的商品信息已选中勾选框,则获取商品的购买数量和费用,将两者分别累加到变量price和seleted,当遍历完成后,变量price和seleted分别显示在购物车最下方的应付费用和已选数量中,如图9-3所示。
(2)标注②是对某一商品的应付费用进行计算,函数设置了函数参数ul,它代表某一商品的元素定位,从函数参数ul获取商品的单价和数量,分别赋值给变量unitprice和count,然后计算该商品的应付费用SubTotal并显示在网页上,如图9-4所示。
(3)标注③是对商品勾选框添加事件触发,当单击某商品的勾选框将触发checkInputs[i].onclick= function(){},然后判断勾选框的class样式是否等于check-all check,如果符合判断条件,则当前单击状态(即this.checked)赋值给当前勾选框的属性checked(即checkInputs[j].checked);如果当前单击状态等于false,将购物车列表上方的全选按钮(即checkAll[k])取消勾选,最后调用函数getTotal()重新计算所有商品的应付费用,如图9-5所示。
(4)标注④是遍历所有商品并对商品数量的“+”、“-”和“删除”按钮进行事件触发。函数首先绑定某个商品的单击事件(即uls[i].onclick= function(e){}),然后再获取商品的某些元素信息,最后使用switch()语法对元素的class样式进行判断,判断过程如下:
如果当前元素的class样式等于add layui-btn,说明当前用户单击商品数量的“+”按钮,程序将商品购买数量累加1。如果当前元素的class样式等于less layui-btn,说明当前用户单击商品数量的“-”按钮,程序判断商品购买数量是否大于1,若数量大于1则再对商品购买数量累减1。如果当前元素的class样式等于dele-btn,说明当前用户单击商品数量的“删除”按钮。首先提示“你确定要删除吗”信息,当单击“Yes”按钮后,程序从隐藏控件
获取商品主键id,它将作为API接口(即路由delete)的请求参数commodityId,最后向API接口(即路由delete)发送HTTP的GET请求,当Django接收到Ajax请求后,视图函数deleteAPI获取请求参数commodityId,在模型CartInfos中删除相应的商品信息。当商品删除成功后,Ajax接收视图函数deleteAPI的响应内容,响应内容等于success则说明商品删除成功,浏览器重新访问购物车页面。
(5)标注⑤是对购物车下方的“删除全部”按钮绑定事件触发(即batchdeletion.onclick=function(){}),当用户单击“删除全部”按钮的时候,首先提示“你确定要删除吗”信息,单击“Yes”按钮后,从网页的隐藏控件
获取用户主键id,它作为API接口(即路由delete)的请求参数userId,最后向API接口(即路由delete)发送HTTP的GET请求,视图函数deleteAPI获取请求参数userId,在模型CartInfos中删除所有user_id=userId的商品信息,从而删除用户的所有商品信息,整个功能的业务逻辑与删除某一商品的业务逻辑大致相同,只不过两者的请求参数各有不同。
支付宝的支付配置
在线支付是任何电商平台必不可少的功能,目前最大的支付平台为支付宝、微信支付和京东支付,它们可以绑定银行卡完成支付过程。支付平台的支付流程需要与银行签订商务协议,再由银行提供接口由支付平台进行调度,完成整个支付(退款)流程。
整个支付过程看似简单,但实际中涉及了财务计算、接口的每次调度费用、授权认证等多方面的协议,而且银行需要考察公司的资质和规模,综合考虑在线支付的可行性与安全性。一般而言,大多数自主开发的电商平台都会首选支付宝、微信支付或京东支付实现在线支付功能。
支付宝、微信支付和京东支付提供了开发文档,但使用支付接口必须为商家或公众号账号,而且还要设置相关信息。以支付宝为例,在浏览器中打开openhome.alipay.com/docCenter/docCenter.htm,在网页中找到“快速入门”并单击“平台入驻”,如图9-6所示。
成功访问“平台入驻”页面后,我们可以根据文档说明注册入驻开放平台,平台身份可以选择“系统服务商ISV”或“自研开发者”。完成用户入驻后,使用支付宝登录开放平台,然后单击“开发者中心”,如图9-7所示。
在开发者中心页面中,在线支付可以分为两种模式:上线应用和沙箱应用。首先讲述如何创建上线应用,在页面中找到并单击“创建应用”,然后在弹出的下拉列表中选择“网页&移动应用”,最后单击“支付接入”,如图9-8所示。在图9-9中填写应用信息并单击“确认创建”即可创建上线应用。
上线应用建议在网站运营上线或上线调试阶段使用,因为API接口需要商户签约,商户签约需要提供营业执照,并且每次调用API接口需要收取一定的费用,费用计算如图9-10所示。如果使用上线应用开发网站功能,每次测试支付功能的时候,都需要支付相关费用给支付宝平台,这样就提高了项目开发成本。
在网站的开发阶段,支付宝为开发者提供了研发服务,我们在图9-8中单击“研发服务”,浏览器访问“沙箱”页面,如图9-11所示。沙箱应用是协助开发者进行接口功能开发及主要功能联调的模拟环境,在沙箱完成接口开发及主要功能调试后,可以在正式环境进行完整的功能验收测试。
上线应用与沙箱应用的信息配置是相同的,换句话说,如果网站在开发阶段使用沙箱应用,当网站上线运营的时候,只需将沙箱应用的信息配置改为上线应用的信息配置即可。它们设有三个重要参数:APPID、支付宝网关和RSA2(SHA256)密钥,每个参数的说明如下:
APPID是发起请求的应用ID,不管是上线应用还是沙箱应用,它们都有唯一的APPID。支付宝网关由支付宝提供,上线应用的网关为https://openapi.alipay.com/gateway.do,沙箱应用的网关为https://openapi.alipaydev.com/gateway.do。RSA2(SHA256)密钥是保证接口中使用的私钥与公钥匹配成功,否则无法调用接口,这是接口调用的加密设置,每个应用的私钥和公钥都是唯一的。
RSA2(SHA256)密钥有两种加密方式:公钥证书和公钥。我们单击RSA2(SHA256)密钥的“设置/查看”按钮,然后选择公钥作为加密方式,如图9-12所示。公钥字符可以使用支付宝密钥生成器或OpenSSL(第三方工具)生成密钥,支付宝密钥生成器仅支持Windows版本和Mac OS版本。单击图9-12中的支付宝密钥生成器可跳转到相关文档页面,如图9-13所示,然后单击“WINDOWS”下载链接即可下载支付宝密钥生成器。
安装并运行支付宝密钥生成器,在软件界面的左侧选择“生成密钥”,然后依次单击选择“RSA2”->“PKCS1(非JAVA适用)”->“生成密钥”,支付宝密钥生成器将自动创建应用私钥和应用公钥,如图9-14所示,并且将应用私钥和应用公钥以文件形式保存到本地电脑,如图9-15所示。
下一步是将支付宝密钥生成器创建的应用公钥复制到沙箱应用的RSA2(SHA256)密钥中,然后单击“保存设置”即可完成RSA2(SHA256)密钥的配置,如图9-16所示。
如果RSA2(SHA256)密钥的加密模式选择公钥证书,可以在支付宝密钥生成器创建证书文件,具体的操作流程可以查看官方文档(https://opendocs.alipay.com/open/291/105971)。综合上述,支付宝的支付接口配置步骤如下:
(1)使用支付宝账号入驻开放平台,平台身份可以选择“系统服务商ISV”或“自研开发者”。
(2)在线支付可以分为两种模式:上线应用和沙箱应用。每个应用设有三个重要参数:APPID、支付宝网关和RSA2(SHA256)密钥。
(3)下载安装支付宝密钥生成器,并使用支付宝密钥生成器创建应用私钥和应用公钥。
(4)将应用公钥复制到沙箱应用的RSA2(SHA256)密钥,然后单击“保存设置”即可。
alipay-sdk-python的安装与使用
在支付宝开放平台完成沙箱应用的配置后,下一步是编写代码调试支付接口,完成支付功能的开发。从开放平台文档中心了解到,支付宝提供了Python的SDK。SDK(软件开发工具包)是一些软件工程师为特定的软件包、软件框架、硬件平台、操作系统等建立应用软件的开发工具集合。简单来说,支付宝将支付接口封装成第三方模块,开发者只需安装并调用模块即可实现支付功能。
我们使用浏览器访问opendocs.alipay.com/open/54/103419/,并在网页上找到SDK列表,如图9-17所示。在SDK列表中,单击“PyPI项目依赖”链接即可访问第三方模块alipay-sdk-python,如图9-18所示。
在本地计算机的CMD窗口或者PyCharm的Terminal窗口下输入第三方模块alipay-sdk-python的安装指令。由于第三方模块alipay-sdk-python依赖PyCrypto模块(PyCrypto模块用来实现RSA加密算法),所以在安装alipay-sdk-python之前需要安装模块PyCrypto。但在CMD窗口使用pip指令安装PyCrypto模块会提示安装失败,如图9-19所示,这是因为PyCrypto模块与Windows 10系统存在兼容问题。
为了解决PyCrypto模块与Windows 10系统的兼容问题,我们可以修改本地系统的环境设置,将CMD窗口切换到C:\Program Files(x86)\Microsoft Visual Studio 14.0\VC,然后依次执行操作指令,详细指令如图9-20所示,由于指令的执行时间相对较长,需要耐心等待。
成功安装PyCrypto模块之后,在CMD窗口或者PyCharm的Terminal窗口下输入pip installalipay-sdk-python并等待模块安装成功即可。我们回看alipay-sdk-python的网页(即图9-18),在网页中找到页面接口示例的代码,如图9-21所示。
图9-21的代码示例就是项目babys需要实现的支付功能,首先实例化AlipayTradePagePayModel(),生成实例化对象model,然后设置实例化对象model的属性,这些属性将作为支付接口的请求参数,页面接口的请求参数说明可以参考官方文档(opendocs.alipay.com/apis/api_1/alipay.trade.page.pay)。由于alipay-sdk-python已有具体的示例代码,我们只需在此基础上进行调整修改就能实现网页在线支付功能。在项目babys的项目应用shopper创建pays_new.py文件,在该文件上编写以下代码:
分析上述代码,我们将代码分为两部分:配置沙箱应用信息和调用支付接口,详细的说明如下:
(1)使用Python内置模块logging创建日志对象logger,主要用于记录每次接口的调用情况,便于统计和核算。
(2)将支付宝密钥生成器创建的应用私钥和应用公钥文件内容赋予变量app_private_key_string和alipay_public_key_string,换句话说,就是将图9-15的文件内容写入变量app_private_key_string和alipay_public_key_string。文件内容是一串已加密且不规则的字符串,在写入变量的时候,它们必须遵从以下格式:
(3)实例化AlipayClientConfig()生成alipay_client_config对象,设置属性server_url、app_id、app_private_key和alipay_public_key,这些属性都是沙箱应用的APPID、支付宝网关和RSA2(SHA256)密钥。
(4)实例化DefaultAlipayClient()生成client对象,将对象alipay_client_config和日志对象logger作为DefaultAlipayClient()的参数。
(5)函数get_pay的函数参数out_trade_no、total_amount和return_url分别为订单号、支付金额和回调地址(回调地址即支付成功后跳转的网页地址)。函数首先实例化AlipayTradePagePayModel()对象model,再设置属性out_trade_no、total_amount和subject等相关的支付信息;然后将对象model作为AlipayTradePagePayRequest()参数,生成request对象并设置属性notify_url和return_url;最后由client对象调用page_execute()函数方法,并把request对象作为函数参数向支付接口发送HTTP请求,支付接口的响应内容作为函数get_pay的返回值,从而完成整个支付过程。
python-alipay-sdk的安装与使用
除了使用支付宝官方提供的alipay-sdk-python之外,还可以使用第三方模块python-alipay-sdk,它是开发人员自行封装支付接口并开源到GitHub社区(https://github.com/fzlee/alipay),在安装与使用上,读者认为python-alipay-sdk比alipay-sdk-python更为简便。
python-alipay-sdk依赖PyCryptodome模块,其实alipay-sdk-python依赖的PyCrypto已经超过三年时间无人维护了,因此才会出现与Windows系统不兼容的情况,而Github的开发者Varbin在PyCrypto项目的Github issue里呼吁开发人员不要再使用PyCrypto,应该将PyCrypto替换为PyCryptodome,对于使用PyCrypto的已有项目而言,PyCryptodome保持了与PyCrypto相当高的兼容性并且处于良好的维护状态,因此便于更换。
尽管如此,现在支付宝官方提供的alipay-sdk-python版本还是无法使用PyCryptodome,目前只能使用PyCrypto;而python-alipay-sdk模块支持使用PyCryptodome实现RSA加密算法。我们在本地计算机的CMD窗口或者PyCharm的Terminal窗口下依次安装PyCryptodome和python-alipay-sdk,安装指令如下:
PyCryptodome和python-alipay-sdk模块安装成功后,本地系统就会将alipay-sdk-python模块自行删除,换句话说,一台计算机不能同时保留alipay-sdk-python和python-alipay-sdk,两者只能选其一。
我们在Github中参考python-alipay-sdk的文档说明,通过修改调整文档提供的代码示例就能实现网页在线支付功能。在项目babys的项目应用shopper中创建pays.py文件,在该文件上编写以下代码:
上述代码与项目应用shopper的pays_new.py定义的函数get_pay对比发现,两者的函数参数完全相同,但上述代码只需实例化AliPay()和api_alipay_trade_page_pay()即可实现网页在线支付功能。
如果从代码量、代码逻辑和模块安装对比,python-alipay-sdk略胜一筹;但从功能解耦的角度来看,alipay-sdk-python更有优势。两者各有优缺点,在开发中应按照实际需求选择合理的方案实现。
商城的在线支付功能
我们在项目应用shopper的pays_new.py和pays.py中定义了函数get_pay,它们是通过不同模块实现支付宝的网页在线支付功能。本书以pays_new.py定义的函数get_pay为例,讲述如何在项目中使用支付宝的支付功能。
在购物车页面看到,我们在购物车的最下方设置了“结算”按钮,单击该按钮的时候,网页将会触发JavaScript脚本,首先获取购物车最下方的应付费用,然后将应付费用作为路由pays的请求参数total,最后由浏览器访问带请求参数total的路由pays。在项目babys中,我们并没有设置路由pays,因此在项目应用shopper的urls.py定义路由pays,路由定义过程如下:
从上述代码看到,路由pays的HTTP请求交由视图函数paysView处理和响应,下一步在项目应用shopper的views.py定义视图函数paysView,代码如下:
视图函数paysView从HTTP请求中获取请求参数total,再对请求参数total进行简单的数据处理,去掉货币符号¥并对其数据进行判断处理,根据判断结果执行相应的处理,详细说明如下:
(1)如果请求参数total为空或等于0,说明当前HTTP请求没有设置请求参数或者购物车的应付费用等于0,遇到这两种情况,程序将重新访问购物车页面。
(2)如果请求参数total大于0,则购物车页面的应付费用大于0,说明用户正在对某商品执行结算操作,程序首先使用time模块根据当前时间生成时间戳,将时间戳作为订单编号out_trade_no。
(3)将请求参数total、当前用户的主键id以字典格式写入变量payInfo,变量payInfo代表模型OrderInfos的订单信息,由于订单尚未完成支付过程,所以暂时保存变量payInfo在当前用户的会话session。
(4)变量return_url是设置支付函数get_pay的回调地址,回调地址为路由shopper。然后调用支付函数get_pay,并将订单编号out_trade_no、请求参数total和变量return_url作为函数参数。
(5)把支付函数get_pay的返回值赋予给变量url,变量url是成功调用支付宝API接口所生成的支付页面,因此可以使用redirect()跳转访问变量url的网站地址。
整个支付流程涉及了视图函数paysView、视图函数shopperView和支付函数get_pay,订单信息贯穿整个支付流程,并且在三个函数之间相互传递,如图9-22所示,详细的说明如下:
(1)视图函数paysView定义的订单编号out_trade_no作为支付函数get_pay的函数参数之一,当用户支付成功后,支付函数get_pay的return_url(回调地址)将订单编号out_trade_no作为请求参数t;由于return_url(回调地址)是跳转访问路由shopper,所以视图函数shopperView能通过请求参数t获取订单编号out_trade_no。
(2)由于视图函数paysView定义的变量payInfo已写入用户会话session,视图函数shopperView也可以从用户会话session中获取变量payInfo,将变量payInfo里面的payTime与请求参数t进行对比(变量payInfo的payTime与请求参数t均代表订单编号out_trade_no)。
(3)在视图函数shopperView中,如果变量payInfo的payTime与请求参数t相同,则说明该订单已支付成功,订单信息payInfo将写入模型OrderInfos。请求参数t是支付函数get_pay的return_url(回调地址)的请求参数,视图函数shopperView能接收到请求参数t则说明用户在支付宝页面完成了支付过程;会话session的payInfo由视图函数paysView定义,但在视图函数shopperView读取和删除,它主要是与请求参数t进行校验,校验成功则说明用户已为当前订单完成了支付过程。
我们在浏览器打开商品列表页,并单击某一商品进入该商品的详细页,选择购买数量并单击“加入购物车”按钮,如图9-23所示。
单击“加入购物车”按钮后,浏览器将触发JavaScript脚本,访问购物车页面并把商品主键id和购买数量作为该页面的请求参数id和quantity,网站获取请求参数并记录在模型CartInfos中,然后重新访问购物车页面,其目的是去除购物车页面的请求参数并把模型CartInfos的数据展示在购物车列表中,如图9-24所示。
在购物车页面中,选择需要结算的商品并单击“结算”按钮,浏览器将访问支付接口pays,Django向支付宝发起HTTP请求,发送当前的支付信息(支付费用、订单编号等),再由支付宝生成相应的支付页面,如图9-25所示。
由于我们使用支付宝的沙箱应用,因此无法使用手机支付宝扫码支付,只能单击右侧的“登录账号付款”按钮,使用沙箱应用提供的虚拟账号进行支付,沙箱应用的虚拟账号可以在支付宝开放平台的沙箱应用配置中查看,如图9-26所示。
在支付页面输入沙箱应用的虚拟账号密码,单击“下一步”按钮,如图9-27所示,当账号登录成功后,再次输入支付宝的支付密码,单击“确认付款”按钮,如图9-28所示,等待完成支付即可。支付成功后,浏览器将自动访问个人中心页,并将订单信息显示在个人中心页右侧。