zoukankan      html  css  js  c++  java
  • DAY108

    一、支付宝支付

    正式环境:

    ​ 用营业执照,申请商户号,appid

    测试环境:

    ​ 沙箱环境:https://openhome.alipay.com/platform/appDaily.htm?tab=info

    第一步

    # 调用AliPay接口
    from utils.pay import AliPay
    # 配置一些信息
    def ali():
        # 沙箱环境地址:https://openhome.alipay.com/platform/appDaily.htm?tab=info
        app_id = "2016092000554611"
        # 支付宝收到用户的支付,会向商户发两个请求,一个get请求,一个post请求
        # POST请求,用于最后的检测
        notify_url = "http://42.56.89.12:80/page2/"
        # GET请求,用于页面的跳转展示,支付成功后商家的页面
        return_url = "http://42.56.89.12:80/page2/"
    
        # 应用私钥
        # https://docs.open.alipay.com/291/105971
        # 从这个网址下载软件生成应用公钥和应用私钥
        # 并配置到keys/app_private_2048.txt中
        merchant_private_key_path = "keys/app_private_2048.txt"
        # 支付宝公钥
        # 把应用公钥配置到这里:https://openhome.alipay.com/platform/appDaily.htm?tab=info
        # 获得支付宝公钥,并配置到keys/alipay_public_2048.txt
        alipay_public_key_path = "keys/alipay_public_2048.txt"
        # 生成一个AliPay的对象
        alipay = AliPay(
            appid=app_id,
            app_notify_url=notify_url,
            return_url=return_url,
            app_private_key_path=merchant_private_key_path,
            alipay_public_key_path=alipay_public_key_path,  # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥
            debug=True,  # 默认False,
        )
        return alipay
    

    第二步

    # 确认付款页面
    def page1(request):
        if request.method == "GET":
            return render(request, 'page1.html')
        else:
            # money:支付的金额
            money = float(request.POST.get('money'))
            # 生成一个对象
            alipay = ali()
            # 生成支付的url
            # 对象调用direct_pay,生成了一个地址
            query_params = alipay.direct_pay(
                subject="充气娃娃",  # 商品简单描述
                out_trade_no="x2" + str(time.time()),  # 商户订单号
                total_amount=money,  # 交易金额(单位: 元 保留俩位小数)
            )
    
            # 拼接地址
            pay_url = "https://openapi.alipaydev.com/gateway.do?{}".format(query_params)
            print(pay_url)
            # 朝这个地址重定向,发get请求
            return redirect(pay_url)
    

    第三步

    # 当支付宝收到钱以后,由于我们在ali中配置了回调地址page2,就会跳转到page2
    # 并发送两个请求:POST请求处理订单信息;GET请求显示支付成功页面
    def page2(request):
        alipay = ali()
        if request.method == "POST":
            # 检测是否支付成功
            # 去请求体中获取所有返回的数据:状态/订单号
            from urllib.parse import parse_qs
            body_str = request.body.decode('utf-8')
            print(body_str)
    
            post_data = parse_qs(body_str)
            print('支付宝给我的数据:::---------',post_data)
            post_dict = {}
            for k, v in post_data.items():
                post_dict[k] = v[0]
            print('转完之后的字典',post_dict)
            # 从post_dict可以获取订单号:out_trade_no,从而去数据库查找并修改订单状态
    
            sign = post_dict.pop('sign', None)
            status = alipay.verify(post_dict, sign)
            print('POST验证', status)
            return HttpResponse('POST返回')
    
        else:
            # 获得params
            params = request.GET.dict()
            # 获得sign
            sign = params.pop('sign', None)
            # 把params和sign放入verify()里处理,返回的结果status
            status = alipay.verify(params, sign)
            print('GET验证', status)
            return HttpResponse('支付成功')
    

    支付宝python接口

    # 需要安装模块:pip3 install pycryptodome
    from datetime import datetime
    from Crypto.PublicKey import RSA
    from Crypto.Signature import PKCS1_v1_5
    from Crypto.Hash import SHA256
    from urllib.parse import quote_plus
    from base64 import decodebytes, encodebytes
    import json
    
    class AliPay(object):
        """
        支付宝支付接口(PC端支付接口)
        """
        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.importKey(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)
    
    

    二、微信推送

    目前情况

    ​ 我们的网站,想要对我们的用户进行微信推送,用户也扫描了我们的二维码,成为了我们的微信用户,但是要微信推送,就需要该用户的微信的唯一ID,可我们本地服务器里还没有微信的ID,所以现在就需要拿到这个ID

    流程

    第一步:

    我们要生成一个二维码,让用户扫描

    1.点击按钮

    <div style=" 600px;margin: 0 auto">
        <h1>请关注路飞学城服务号,并绑定个人用户(用于以后的消息提醒)</h1>
        <div>
            <h3>第一步:关注路飞学城微信服务号</h3>
            <img style="height: 100px; 100px" src="{% static "img/luffy.jpeg" %}">
        </div>
        <input type="button" value="下一步【获取绑定二维码】" onclick="getBindUserQcode()">
        <div>
            <h3>第二步:绑定个人账户</h3>
            <div id="qrcode" style=" 250px;height: 250px;background-color: white;margin: 100px auto;"></div>
        </div>
    </div>
    <script src="{% static "js/jquery.min.js" %}"></script>
    {#qrcode 可以生成二维码 #}
    <script src="{% static "js/jquery.qrcode.min.js" %}"></script>
    <script src="{% static "js/qrcode.js" %}"></script>
    <script>
        function getBindUserQcode() {
            $.ajax({
                url: '/bind_qcode/',
                type: 'GET',
                success: function (result) {
                    console.log(result);
                    //result.data 取出来的是什么?是后台生成的一个地址
                    //通过js生成一个二维码图片放到div中
                    $('#qrcode').empty().qrcode({text: result.data});
                }
            });
        }
    </script>
    

    2.向/bind_qcode/发送了get请求获得二维码

    # 二维码的本质就是URL地址,你扫描二维码,就是向这个URL地址发请求
    @auth
    def bind_qcode(request):
        """
        生成二维码
        :param request: 
        :return: 
        """
        ret = {'code': 1000}
        try:
            access_url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid={appid}&redirect_uri={redirect_uri}&response_type=code&scope=snsapi_userinfo&state={state}#wechat_redirect"
            access_url = access_url.format(
                # 这三个参数,配置在了setting里,并且是从微信的沙箱里获得的虚拟数据
                # 商户的appid
                appid=settings.WECHAT_CONFIG["app_id"], # 'wx6edde7a6a97e4fcd',
                # 回调地址
                redirect_uri=settings.WECHAT_CONFIG["redirect_uri"],
                # 当前登录用户的唯一id
                state=request.session['user_info']['uid'] # 为当前用户生成MD5值
            )
            ret['data'] = access_url
        except Exception as e:
            ret['code'] = 1001
            ret['msg'] = str(e)
    
        # 返回的就是一个拼接好的URL地址
        return JsonResponse(ret)
    
    

    第二步:

    ​ 用户扫描的二维码,其实就是让用户的微信向微信服务器发送一个请求,并且这个请求携带了一个回调地址redirect_uri,微信服务器一旦检测到,就会向这个地址重定向,并且携带参数,也就是向我们的服务器发送了微信的数据

    # 回调地址指向了这个视图callback
    def callback(request):
        """
        用户在手机微信上扫码后,微信自动调用该方法。
        用于获取扫码用户的唯一ID,以后用于给他推送消息。
        :param request: 
        :return: 
        """
        # 从微信服务端获得数据
        code = request.GET.get("code")
    
        # 用户md5值,用户唯一id
        state = request.GET.get("state")
    
        
        获得微信服务器发来的数据后,我们再向微信的https://api.weixin.qq.com/sns/oauth2/access_token地址发get请求,并携带参数
        
        这个地址返回了一个JSON的数据,转成字符串,该数据里面就拥有用户的微信唯一ID------openid
        
        # 获取该用户openId(用户唯一,用于给用户发送消息)
        # request模块朝https://api.weixin.qq.com/sns/oauth2/access_token地址发get请求
        res = requests.get(
            url="https://api.weixin.qq.com/sns/oauth2/access_token",
            params={
                "appid": 'wx3e1f0883236623f9',
                "secret": '508ec4590702c76e6863be6df01ad95a',
                "code": code,
                "grant_type": 'authorization_code',
            }
        ).json()
        # res.data   是json格式
        # res=json.loads(res.data)
        # res是一个字典
        # 获取的到openid表示用户授权成功
        openid = res.get("openid")
        if openid:
            models.UserInfo.objects.filter(uid=state).update(wx_id=openid)
            response = "<h1>授权成功 %s </h1>" % openid
        else:
            response = "<h1>用户扫码之后,手机上的提示</h1>"
        return HttpResponse(response)
    

    微信支付流程

    第三步

    获得微信用户的ID之后,就可以开始推送了

    def sendmsg(request):
        def get_access_token():
            """
            获取微信全局接口的凭证(默认有效期俩个小时)
            如果不每天请求次数过多, 通过设置缓存即可
            """
            result = requests.get(
                url="https://api.weixin.qq.com/cgi-bin/token",
                params={
                    "grant_type": "client_credential",
                    "appid": settings.WECHAT_CONFIG['app_id'],
                    "secret": settings.WECHAT_CONFIG['appsecret'],
                }
            ).json()
            if result.get("access_token"):
                access_token = result.get('access_token')
            else:
                access_token = None
            return access_token
    
        # 获得微信用户的TOKEN
        access_token = get_access_token()
    	
        # 从数据库拿到当前用户的微信ID
        openid = models.UserInfo.objects.get(id=1).wx_id
    
        # 自定义推送信息
        def send_custom_msg():
            body = {
                "touser": openid,
                "msgtype": "text",
                "text": {
                    "content": 'lqz大帅哥'
                }
            }
            response = requests.post(
                url="https://api.weixin.qq.com/cgi-bin/message/custom/send",
                # 放到路径?后面的东西
                params={
                    'access_token': access_token
                },
                # 这是post请求body体中的内容
                data=bytes(json.dumps(body, ensure_ascii=False), encoding='utf-8')
            )
            # 这里可根据回执code进行判定是否发送成功(也可以根据code根据错误信息)
            result = response.json()
            return result
    	
        # 模板信息
        def send_template_msg():
            """
            发送模版消息
            """
            res = requests.post(
                url="https://api.weixin.qq.com/cgi-bin/message/template/send",
                params={
                    'access_token': access_token
                },
                json={
                    "touser": openid,
                    "template_id": 'IaSe9s0rukUfKy4ZCbP4p7Hqbgp1L4hG6_EGobO2gMg',
                    "data": {
                        "first": {
                            "value": "lqz",
                            "color": "#173177"
                        },
                        "keyword1": {
                            "value": "大帅哥",
                            "color": "#173177"
                        },
                    }
                }
            )
            result = res.json()
            return result
    
        result = send_custom_msg()
    
        if result.get('errcode') == 0:
            return HttpResponse('发送成功')
        return HttpResponse('发送失败')
    
  • 相关阅读:
    实现一个与内容合二为一的ActionBar动画效果
    hdoj 1506&amp;&amp;1505(City Game) dp
    remine chart2安装
    zoom的学习
    海哥:T2C时代的到来了,那么什么叫T2C?
    minhash算法
    动态创建按钮的JS
    socket编程在windows和linux下的区别
    http staus汇总
    MySQL HINT:Straight_JOIN
  • 原文地址:https://www.cnblogs.com/xvchengqi/p/10197739.html
Copyright © 2011-2022 走看看