Title

支付宝支付

支付宝支付

支付宝支付流程

-我们自己的网站:点击购买按钮---》向我们后端发送请求---》携带购买商品信息---》生成订单,入库,订单是未支付状态-----》生成支付宝支付链接----》返回给前端
    -前端拿到支付链接---》get请求打开----》咱们的前端就来到了支付宝的页面--》用户掏出手机扫描支付---》付款完成----》支付宝收到了钱----》get回调(咱们配置回调地址)----》跳回我们自己的网页---》支付宝还会发送post请求给我们后端----》我们要验签,通过后,把订单状态改为已支付状态
目前基于沙箱环境测试

python使用沙箱环境模拟支付

基于sdk
 -pip install python-alipay-sdk --upgrade

封装支付宝支付

libs
    ├── iPay  							# aliapy二次封装包
    │   ├── __init__.py 				# 包文件
    │   ├── pem							# 公钥私钥文件夹
    │   │   ├── alipay_public_key.pem	# 支付宝公钥文件
    │   │   ├── app_private_key.pem		# 应用私钥文件
    │   ├── pay.py						# 支付文件
    └── └── settings.py  				# 应用配置 
alipay_public_key.pem
-----BEGIN PUBLIC KEY-----
拿应用公钥跟支付宝换来的支付宝公钥
-----END PUBLIC KEY-----
app_private_key.pem
-----BEGIN RSA PRIVATE KEY-----
通过支付宝公钥私钥签发软件签发的应用私钥
-----END RSA PRIVATE KEY-----
settings.py
import os

# 应用私钥
APP_PRIVATE_KEY_STRING = open(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'pem', 'app_private_key.pem')).read()

# 支付宝公钥
ALIPAY_PUBLIC_KEY_STRING = open(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'pem', 'alipay_public_key.pem')).read()

# 应用ID
APP_ID = 应用ID

# 加密方式
SIGN = 'RSA2'

# 是否是支付宝测试环境(沙箱环境),如果采用真是支付宝环境,配置False
DEBUG = True

# 支付网关
GATEWAY = 'https://openapi-sandbox.dl.alipaydev.com/gateway.do?' if DEBUG else 'https://openapi.alipay.com/gateway.do?'
pay.py
from alipay import AliPay
from alipay.utils import AliPayConfig
from . import settings

alipay = AliPay(
    appid=settings.APP_ID,
    app_notify_url=None,  # 默认回调 url
    app_private_key_string=settings.APP_PRIVATE_KEY_STRING,
    # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
    alipay_public_key_string=settings.ALIPAY_PUBLIC_KEY_STRING,
    sign_type=settings.SIGN,  # RSA 或者 RSA2
    debug=settings.DEBUG,  # 默认 False
    verbose=settings.DEBUG,  # 输出调试数据
    config=AliPayConfig(timeout=15)  # 可选,请求超时时间
)


init.py
from .pay import alipay
from .settings import GATEWAY
补充:在自己项目的配置文件中配置支付宝回调接口:settings.py | dev.py
# 上线后必须换成公网地址
# 后台基URL
BASE_URL = 'http://127.0.0.1:8000'
# 前台基URL
LUFFY_URL = 'http://127.0.0.1:8080'
# 支付宝同步异步回调接口配置
# 后台异步回调接口
NOTIFY_URL = BASE_URL + "/order/success/"
# 前台同步回调接口,没有 / 结尾
RETURN_URL = LUFFY_URL + "/pay/success"


BACKEND_URL = "http://127.0.0.1:8000"
LUFFY_URL = "http://127.0.0.1:8080"
RETURN_URL = LUFFY_URL + "/order/success"
NOTIFY_URL = BACKEND_URL + "/api/v1/order/paysuccess/"
"""
1)支付接口(需要登录认证:是谁):前台提交商品等信息,得到支付链接
    post方法

分析:支付宝回调
    同步:get给前台 => 前台可以在收到支付宝同步get回调时,ajax异步在给消息同步给后台,也采用get,后台处理前台的get请求
    异步:post给后台 => 后台直接处理支付宝的post请求
2)支付回调接口(不需要登录认证:哪个订单(订单信息中有非对称加密)、支付宝压根不可能有你的token):
    get方法:处理前台来的同步回调(不一定能收得到,所有不能在该方法完成后台订单状态等信息操作)
    post方法:处理支付宝来的异步回调
    
3)订单状态确认接口:随你前台任何时候来校验订单状态的接口
"""

简单支付案例

视图

class payView(GenericViewSet, CreateModelMixin):
    # @action(methods=["post"],detail=False)
    # def pay(self, request):
    #     res = alipay.api_alipay_trade_page_pay(subject="衣服", out_trade_no="sdfsfdsf", total_amount='9999')
    #     print(GATEWAY + res)
    #     return APIResponse(pay_url=GATEWAY + res)
    queryset = Order.objects.all()
    serializer_class = OrderSerializer

    def create(self, request, *args, **kwargs):
        print(request.data)
        serializer = self.get_serializer(data=request.data, context={'request': request})
        serializer.is_valid(raise_exception=True)
        self.perform_create(serializer)
        pay_url = serializer.context.get('pay_url')
        return APIResponse(pay_url=pay_url)

序列化类

class OrderSerializer(serializers.ModelSerializer):
    '''
    1 取出所有id号,拿到数据
    2 统计总价格,跟传入的total_amount做比较,如果一样,继续往后
    3 获取购买人信息:登录后才能访问的接口 request.user
    4 生成订单号 支付链接需要,存订单表需要
    5 生成支付链接:支付宝支付生成,
    6 生成订单记录,订单是待支付状态(order,order_detail)
    7 返回前端支付链接
    '''
    courses = serializers.PrimaryKeyRelatedField(queryset=Course.objects.all(), many=True)

    class Meta:
        model = Order
        fields = ['courses', 'total_amount', 'subject']

    # 5生成支付链接:支付宝支付生成,
    def _order_url(self, subject, out_trade_no, total_amount):
        res = alipay.api_alipay_trade_page_pay(subject=subject, out_trade_no=out_trade_no, total_amount=total_amount,
                                               return_url=settings.RETURN_URL,  # 前端的
                                               notify_url=settings.NOTIFY_URL)
        print(res)
        self.context["pay_url"] =GATEWAY + res

    # 4生成订单号支付链接需要,存订单表需要
    def _order_id(self):
        return str(uuid.uuid4())

    # 3获取购买人信息:登录后才能访问的接口
    def _user(self):
        print(self.context.get("request"))
        return self.context.get("request").user

    def validate(self, attrs):
        subject = attrs.get("subject")
        out_trade_no = self._order_id()
        total_amount = int(self._total_amount(attrs))
        user=self._user()
        print(self._order_url(subject=subject,out_trade_no=out_trade_no,total_amount=total_amount))
        attrs["subject"] = subject
        attrs["out_trade_no"] = out_trade_no
        attrs["total_amount"] = total_amount
        attrs["user"] = user
        return attrs

    # 2统计总价格,跟传入的total_amount做比较,如果一样,继续往后
    def _total_amount(self, attrs):
        courses = attrs.get("courses")
        total_amount = attrs.get("total_amount")
        common_total_amount = 0
        for course in courses:
            common_total_amount += course.price
        if total_amount == common_total_amount:
            return common_total_amount
        raise APIException("价格有误")

    def create(self, validated_data):
        print(validated_data)
        # validated_data:{subject,total_amount,user,out_trade_no,courses}
        courses = validated_data.pop('courses')
        order = Order.objects.create(**validated_data)
        # 存订单详情表,存几条,取决于courses有几个
        for course in courses:
            OrderDetail.objects.create(order=order, course=course, price=course.price, real_price=course.price)

        return order

前段回调

let params = location.search.substring(1);  // 去除? => a=1&b=2
            let items = params.length ? params.split('&') : [];  // ['a=1', 'b=2']
            //逐个将每一项添加到args对象中
            for (let i = 0; i < items.length; i++) {  // 第一次循环a=1,第二次b=2
                let k_v = items[i].split('=');  // ['a', '1']
                //解码操作,因为查询字符串经过编码的
                if (k_v.length >= 2) {
                    // url编码反解
                    let k = decodeURIComponent(k_v[0]);
                    this.result[k] = decodeURIComponent(k_v[1]);
                    // 没有url编码反解
                    // this.result[k_v[0]] = k_v[1];
                }

            }
            // 解析后的结果
            // console.log(this.result);


            // 把地址栏上面的支付结果,再get请求转发给后端
            this.$axios({
                url: this.$settings.base_url + '/order/success/' + location.search,
                method: 'get',
            }).then(response => {
                console.log(response.data);
            }).catch(() => {
                console.log('支付结果同步失败');
            })

后端回调接口

class PaySuccess(APIView):
    authentication_classes = []

    def get(self, request):  # 咱们用的
        out_trade_no = request.query_params.get('out_trade_no')
        order = Order.objects.filter(out_trade_no=out_trade_no, order_status=1).first()
        if order:  # 支付宝回调完, 订单状态改了
            return APIResponse()
        else:
            return APIResponse(code=101, msg='暂未收到您的付款,请稍后刷新再试')

    def post(self, request):  # 给支付宝用的,项目需要上线后才能看到  内网中,无法回调成功【使用内网穿透】
        try:
            result_data = request.data.dict()  # requset.data 是post提交的数据,如果是urlencoded格式,requset.data是QueryDict对象,方法dict()---》转成真正的字典
            out_trade_no = result_data.get('out_trade_no')
            signature = result_data.pop('sign')
            # 验证签名的---》验签
            result = alipay.alipay.verify(result_data, signature)
            if result and result_data["trade_status"] in ("TRADE_SUCCESS", "TRADE_FINISHED"):
                # 完成订单修改:订单状态、流水号、支付时间
                Order.objects.filter(out_trade_no=out_trade_no).update(order_status=1)
                # 完成日志记录
                logging.warning('%s订单支付成功' % out_trade_no)
                return Response('success')  # 都是支付宝要求的
            else:
                logging.error('%s订单支付失败' % out_trade_no)
        except:
            pass
        return Response('failed')  # 都是支付宝要求的

    def put(self, request):  # 咱们用的
        out_trade_no = request.query_params.get('out_trade_no')
        order = Order.objects.filter(out_trade_no=out_trade_no)
        if order:
            order.update(order_status=1)
        return APIResponse(mag="订单修改成功")

posted @ 2023-07-04 15:47  哈哈哈哼  阅读(135)  评论(0编辑  收藏  举报