• django支付宝支付集成


    概要

    本文是关于django集成支付宝【电脑网站支付】功能的过程记录。

    功能逻辑主要分为两块:

    1. 用户下单跳转至支付宝支付界面
    2. 支付成功的回调验证

    本文用到的alipay-sdk是基于阿里开发文档http接口请求规则的初步封装,也并非是本人原创。但真实上线门槛略高,需要企业营业执照,对个体程序员不是很友好,而且还要收取0.6%的手续费,不得不吐糟一下。


    python 支付接口

    # pip install pycryptodome
    
    from datetime import datetime
    from Crypto.PublicKey import RSA
    from Crypto.Signature import PKCS1_v1_5
    from Crypto.Hash import SHA256
    from base64 import b64encode, b64decode
    from urllib.parse import quote_plus
    from urllib.parse import urlparse, parse_qs
    from urllib.request import urlopen
    from base64 import decodebytes, encodebytes
    
    import json
    
    
    class AliPay(object):
    
        def __init__(self, appid, app_notify_url, app_private_key_path,
                     alipay_public_key_path, return_url, debug=False):
            self.appid = appid
            self.app_notify_url = app_notify_url
            self.app_private_key_path = app_private_key_path
            self.app_private_key = None
            self.return_url = return_url
            with open(self.app_private_key_path) as fp:
                self.app_private_key = RSA.importKey(fp.read())
    
            self.alipay_public_key_path = alipay_public_key_path
            with open(self.alipay_public_key_path) as fp:
                self.alipay_public_key = RSA.import_key(fp.read())
    
    
            if debug is True:
                self.__gateway = "https://openapi.alipaydev.com/gateway.do"
            else:
                self.__gateway = "https://openapi.alipay.com/gateway.do"
    
        def direct_pay(self, subject, out_trade_no, total_amount, return_url=None, **kwargs):
            biz_content = {
                "subject": subject,
                "out_trade_no": out_trade_no,
                "total_amount": total_amount,
                "product_code": "FAST_INSTANT_TRADE_PAY",
                # "qr_pay_mode":4
            }
    
            biz_content.update(kwargs)
            data = self.build_body("alipay.trade.page.pay", biz_content, self.return_url)
            return self.sign_data(data)
    
        def build_body(self, method, biz_content, return_url=None):
            data = {
                "app_id": self.appid,
                "method": method,
                "charset": "utf-8",
                "sign_type": "RSA2",
                "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
                "version": "1.0",
                "biz_content": biz_content
            }
    
            if return_url is not None:
                data["notify_url"] = self.app_notify_url
                data["return_url"] = self.return_url
    
            return data
    
        def sign_data(self, data):
            data.pop("sign", None)
            # 排序后的字符串
            unsigned_items = self.ordered_data(data)
            unsigned_string = "&".join("{0}={1}".format(k, v) for k, v in unsigned_items)
            sign = self.sign(unsigned_string.encode("utf-8"))
            # ordered_items = self.ordered_data(data)
            quoted_string = "&".join("{0}={1}".format(k, quote_plus(v)) for k, v in unsigned_items)
    
            # 获得最终的订单信息字符串
            signed_string = quoted_string + "&sign=" + quote_plus(sign)
            return signed_string
    
        def ordered_data(self, data):
            complex_keys = []
            for key, value in data.items():
                if isinstance(value, dict):
                    complex_keys.append(key)
    
            # 将字典类型的数据dump出来
            for key in complex_keys:
                data[key] = json.dumps(data[key], separators=(',', ':'))
    
            return sorted([(k, v) for k, v in data.items()])
    
        def sign(self, unsigned_string):
            # 开始计算签名
            key = self.app_private_key
            signer = PKCS1_v1_5.new(key)
            signature = signer.sign(SHA256.new(unsigned_string))
            # base64 编码,转换为unicode表示并移除回车
            sign = encodebytes(signature).decode("utf8").replace("
    ", "")
            return sign
    
        def _verify(self, raw_content, signature):
            # 开始计算签名
            key = self.alipay_public_key
            signer = PKCS1_v1_5.new(key)
            digest = SHA256.new()
            digest.update(raw_content.encode("utf8"))
            if signer.verify(digest, decodebytes(signature.encode("utf8"))):
                return True
            return False
    
        def verify(self, data, signature):
            if "sign_type" in data:
                sign_type = data.pop("sign_type")
            # 排序后的字符串
            unsigned_items = self.ordered_data(data)
            message = "&".join(u"{}={}".format(k, v) for k, v in unsigned_items)
            return self._verify(message, signature)
    

    跳转支付界面

    class OrderView(View):
        def get(self,request,*args,**kwargs):
            total_amount = request.GET.get('total_amount')
            subject = request.GET.get('subject')
            out_trade_no = request.GET.get('out_trade_no')
            # 业务逻辑【保存订单】
            # 跳转支付页面
            alipay = AliPay(
                appid="2016101100660254",
                app_notify_url="http://127.0.0.1:8000/checkout/verify/",
                app_private_key_path=private_key_path,
                alipay_public_key_path=ali_pub_key_path,  # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥
                debug=True,  # 默认False,
                return_url="http://127.0.0.1:8000/checkout/verify/"
            )
    
            query_params = alipay.direct_pay(
                subject=subject,  # 商品简单描述
                out_trade_no=out_trade_no,  # 商户订单号
                total_amount=total_amount, # 金额
            )
            pay_url = "https://openapi.alipaydev.com/gateway.do?{}".format(query_params)
            return redirect(pay_url)
    

    回调验证

    class AlipayView(View):
        def get(self, request):
            """
            处理支付宝的return_url返回
            :param request:
            :return:
            """
            processed_dict = {}
            for key, value in request.GET.items():
                processed_dict[key] = value
    
            sign = processed_dict.pop("sign", None)
    
            alipay = AliPay(
                appid="2016101100660254",
                app_notify_url="http://127.0.0.1:8000/checkout/verify/",
                app_private_key_path=private_key_path,
                alipay_public_key_path=ali_pub_key_path,  # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
                debug=True,  # 默认False,
                return_url="http://127.0.0.1:8000/checkout/verify/"
            )
    
            condition = alipay.verify(processed_dict, sign)
    
            if condition:
                # 更改订单状态
                return HttpResponse('支付成功')
    
        def post(self, request):
            """
            处理支付宝的notify_url
            :param request:
            :return:
            """
            processed_dict = {}
            for key, value in request.POST.items():
                processed_dict[key] = value
    
            sign = processed_dict.pop("sign", None)
    
            alipay = AliPay(
                appid="",
                app_notify_url="http://127.0.0.1:8000/checkout/verify/",
                app_private_key_path=private_key_path,
                alipay_public_key_path=ali_pub_key_path,  # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
                debug=True,  # 默认False,
                return_url="http://127.0.0.1:8000/checkout/verify/"
            )
    
            condition = alipay.verify(processed_dict, sign)
    
            if condition:
                # 更改订单状态
                return HttpResponse("支付成功")
    

    关于return_url 和notify_url

    注意点

  • 相关阅读:
    elk.postman_collection.json
    win10 env
    run fink local
    run kafka local
    打码util
    DataFormatVerifyUtil
    springboot logging.config=classpath:log4j2.xml -Dlogging.config=config/log4j2.xml
    server.port server.servlet.context-path resources freemarker mybatis mail
    maven-war-plugin
    html头部有一行信息导致所有的css和jss都是 https了
  • 原文地址:https://www.cnblogs.com/zenan/p/11212274.html
Copyright © 2020-2023  润新知