当前位置: 首页 > news >正文

网上商城之支付

支付宝介绍

支付宝开放平台入口

  • https://open.alipay.com/platform/home.htm

在这里插入图片描述
在这里插入图片描述

1. 创建应用和沙箱环境

  1. 创建应用

在这里插入图片描述
在这里插入图片描述

  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公私钥

提示:

  • 美多商城私钥加密数据,美多商城公钥解密数据。
  • 支付宝私钥加密数据,支付宝公钥解密数据。
    在这里插入图片描述
  1. 生成美多商城公私钥

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

在这里插入图片描述

  1. 配置美多商城公私钥
  • 配置美多商城私钥

    • 新建子应用payment,在该子应用下新建文件夹keys用于存储公私钥。
    • 将制作的美多商城私钥app_private_key.pem拷贝到keys文件夹中。
  • 配置美多商城公钥

    • 将payment.keys.app_public_key.pem文件中内容上传到支付宝。

在这里插入图片描述
在这里插入图片描述

  1. 配置支付宝公钥
  • 将支付宝公钥内容拷贝到payment.keys.alipay_public_key.pem文件中。

在这里插入图片描述

-----BEGIN PUBLIC KEY-----
支付宝公钥内容
-----END PUBLIC KEY-----

配置公私钥结束后
在这里插入图片描述

对接支付宝系统

订单支付功能

提示:

  • 订单支付触发页面:《order_success.html》 和 《user_center_order.html》
  • 我们实现订单支付功能时,只需要向支付宝获取登录链接即可,进入到支付宝系统后就是用户向支付宝进行支付的行为
    在这里插入图片描述
  1. 请求方式
选项方案
请求方法GET
请求地址/payment/(?P<order_id>\d+)/
  1. 请求参数:路径参数
参数名类型是否必传说明
order_idint订单编号
  1. 响应结果:JSON
字段说明
code状态码
errmsg错误信息
alipay_url支付宝登录链接
  1. 后端接口定义和实现
# 测试账号: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})
  1. 支付宝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/'

笔记

  1. 在这里插入图片描述
    扫码链接页面由支付宝交给美多,美多响应给用户

在这里插入图片描述
3. 内置模块直接import
4. 在这里插入图片描述
将参数写到配置文件中

  1. 在这里插入图片描述
    github python 接口名为 上面的接口 . 换成 _ 前面添加api

  2. http 模块直接位于Django包下

  3. 在这里插入图片描述

  4. 可以从模块导具体内容,也可从包导模块

  5. 在这里插入图片描述
    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. 保存订单支付结果

  1. 请求方式
选项方案
请求方法GET
请求地址/payment/status/
  1. 请求参数:路径参数

参考统一收单下单并支付页面接口中的《页面回跳参数》

  1. 响应结果:HTML

pay_success.html

  1. 后端接口定义和实现

注意:保存订单支付结果的同时,还需要修改订单的状态为待评价

# 测试账号: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('非法请求')
  1. 渲染支付成功页面信息
<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>

在这里插入图片描述
在这里插入图片描述

笔记

  1. 在这里插入图片描述
    out_trade_no 和 trade_no 是订单号,第一个是美多自己的,第二个是支付宝的
    sign 是用来保证安全的,判断是支付宝回调过来的,还是黑客回调过来的

  2. 在这里插入图片描述
    这是支付时传送给支付宝的参数
    支付宝传送回调地址时,会将参数进行加密并放在sign字段中
    当用户接受到时候应将sign去掉,进行加密,然后对比sign和加密后的结果是不是一样,如果一样是支付宝发的,如果不是,是黑客发的

  3. 在这里插入图片描述
    在这里插入图片描述
    request.form 为queryDict , to_dict 转换标准字典
    pop移除sign

  4. request.GET 查询到的是QueryDict类型的对象,要通过dict 转换为字典

  5. 提取的数据名,返回数据中查,也可以从开发文档中查

  6. 在这里插入图片描述
    create的参数直接从models 中粘贴过来就行

  7. 在填写参数的过程中,数据项和逗号之间可以有空白符

  8. 在这里插入图片描述
    弹出字典中数据

  9. 在这里插入图片描述
    10.queryDict 只提供了query 和queryList ,所以要转换字典

  10. 建议对接一下其他的功能

评价订单商品(项目实训时完成)

提示:

点击《我的订单》页面中的《待评价》按钮,进入到订单商品评价页面。

评价订单商品(只需查询未评价的订单)

1. 展示商品评价页面

在这里插入图片描述

  1. 请求方式
选项方案
请求方法GET
请求地址/orders/comment/
  1. 请求参数:查询参数
参数名类型是否必传说明
order_idint订单编号
  1. 响应结果:HTML

goods_judge.html

  1. 后端接口定义和实现
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. 评价订单商品(往数据库中填一下就行了)

在这里插入图片描述

  1. 请求方式
选项方案
请求方法POST
请求地址/orders/comment/
  1. 请求参数:查询参数
参数名类型是否必传说明
order_idint订单编号
  1. 响应结果:JSON
字段说明
code状态码
errmsg错误信息
  1. 后端接口定义和实现
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': '评价成功'})

详情页展示评价信息

在这里插入图片描述
在这里插入图片描述

  1. 请求方式
选项方案
请求方法POST
请求地址/comments/(?P<sku_id>\d+)/
  1. 请求参数:查询参数
参数名类型是否必传说明
sku_idint商品SKU编号
  1. 响应结果:JSON
字段说明
code状态码
errmsg错误信息
comment_list[ ]评价列表
username发表评价的用户
comment评价内容
score分数
{
    "code":"0",
    "errmsg":"OK",
    "comment_list":[
        {
            "username":"itcast",
            "comment":"这是一个好手机!",
            "score":4
        }
    ]
}
  1. 后端接口定义和实现
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})
  1. 渲染商品评价信息
<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>

提示:订单商品评价完成后,一个订单的流程就结束了,订单状态修改为已完成。
在这里插入图片描述

笔记

  1. 对接第三方工具时,一般有平台
  2. 只要对接第三方工具,就要成为开发者,有支付宝就有开发者
  3. 在这里插入图片描述
    在这里插入图片描述
    对接的使用要使用appid
    点击支付接入就可以
    在这里插入图片描述
    创建的是真实应用,可能会在开发的时候把钱给支付宝
  4. 在github上有支付宝的SDK
  5. 在这里插入图片描述
    有新版本直接更新,没有新版本下载最新的就行

在这里插入图片描述
正式支付的请求地址
在这里插入图片描述
这是开发环境
7. 公共参数是指对于各个接口都要进行传送
8. 在这里插入图片描述
对于接口而言,只有必传的还有用户相关的才会暴露给用户
这里主要填写公共参数
在这里插入图片描述
这里填写独有参数

在这里插入图片描述

  1. python版本的SDK在github上
    10.在这里插入图片描述
    了解支付宝提供的接口

  2. 支付宝使用rsa加密算法,这是一种非对称加密算法(加密和解密的钥匙不一样),加密的强度非常大,算法很复杂,性能很差,只能用来加密小数据,加密大数据一般用对称加密算法(加密和解密的钥匙一样)

  3. 私钥越复杂加密的结果越安全,

  4. 列表可换行,写成
    [
    表达式1,
    表达式2,
    ]
    的格式

  5. 对于搭建开发环境只需要pip install 就可以

  6. debug模式检查代码运行方向和参数情况

  7. 评价的代码不会自己去看

  8. 至此项目所有功能已经完成

相关文章:

  • 一次搞懂Java如何调用Kotlin的高级特性
  • MyBatis各种SQL操作及执行添加功能获取自增的主键
  • 【学习笔记】模拟赛题解
  • node.js 使用教程-3.gulp-file-include 详细教程
  • 【可视化大屏教程】用Python开发智慧城市数据分析大屏
  • 【云原生 | 从零开始学Kubernetes】二十三、Kubernetes控制器Statefulset
  • git三板斧--Linux
  • 内存分配.
  • 谷粒商城超详细笔记+踩坑(2)——分布式组件、前端基础(回顾知识点)
  • 为 TiDB 客户端服务端间通信开启加密传输
  • C语言函数解决问题:1.求二进制中不同位的个数;2.交换二进制的奇数位和偶数位;3.使用指针打印数组内容
  • PyQT5入门案例(一)工资统计系统
  • Life:歌曲学习之教一个不会唱歌的人学会唱出《情非得已》、《海阔天空》、《红日》、《老男孩》等歌曲
  • 24.STM32的IO口扩展PCF8574
  • 论文阅读_知识蒸馏_TinyBERT
  • 分享一款快速APP功能测试工具
  • Create React App 使用
  • extjs4学习之配置
  • GDB 调试 Mysql 实战(三)优先队列排序算法中的行记录长度统计是怎么来的(上)...
  • iOS 系统授权开发
  • JavaWeb(学习笔记二)
  • Linux中的硬链接与软链接
  • niucms就是以城市为分割单位,在上面 小区/乡村/同城论坛+58+团购
  • Spring Cloud Feign的两种使用姿势
  • ⭐ Unity 开发bug —— 打包后shader失效或者bug (我这里用Shader做两张图片的合并发现了问题)
  • 笨办法学C 练习34:动态数组
  • 分享自己折腾多时的一套 vue 组件 --we-vue
  • 紧急通知:《观止-微软》请在经管柜购买!
  • 通过获取异步加载JS文件进度实现一个canvas环形loading图
  • 我看到的前端
  • 译自由幺半群
  • 云大使推广中的常见热门问题
  • ​​​​​​​Installing ROS on the Raspberry Pi
  • ​DB-Engines 12月数据库排名: PostgreSQL有望获得「2020年度数据库」荣誉?
  • # include “ “ 和 # include < >两者的区别
  • # 深度解析 Socket 与 WebSocket:原理、区别与应用
  • #pragma data_seg 共享数据区(转)
  • #设计模式#4.6 Flyweight(享元) 对象结构型模式
  • (Matlab)基于蝙蝠算法实现电力系统经济调度
  • (react踩过的坑)antd 如何同时获取一个select 的value和 label值
  • (待修改)PyG安装步骤
  • (附源码)ssm高校志愿者服务系统 毕业设计 011648
  • (附源码)ssm经济信息门户网站 毕业设计 141634
  • (一)Java算法:二分查找
  • (一)u-boot-nand.bin的下载
  • (转) RFS+AutoItLibrary测试web对话框
  • .FileZilla的使用和主动模式被动模式介绍
  • .NET CF命令行调试器MDbg入门(二) 设备模拟器
  • .net core MVC 通过 Filters 过滤器拦截请求及响应内容
  • .NET WebClient 类下载部分文件会错误?可能是解压缩的锅
  • @开发者,一文搞懂什么是 C# 计时器!
  • [ vulhub漏洞复现篇 ] Grafana任意文件读取漏洞CVE-2021-43798
  • [] 与 [[]], -gt 与 > 的比较
  • [20190416]完善shared latch测试脚本2.txt
  • [C++基础]-入门知识