• 微信推送与支付宝支付


    支付宝支付和微信推送

    一、支付宝支付

    1、正式环境和沙箱环境

    正式环境:用营业执照,申请商户号,appid

    测试环境:沙箱环境(支付宝给开发者提供的调试环境应用) —— https://openhome.alipay.com/platform/appDaily.htm?tab=info

    2、使用沙箱环境

    • 第一步:要注册企业账号或者个人支付宝账号,使用企业账号或者个人账号进行登录的开放平台。
    • 第二步:首次进入系统界面有三个选项,用户可以根据个人自由选择第一项,第二项或者第三项的入住。
    • 第三步: 进入沙箱环境——沙箱应用栏目中可以看到系统自动分配的信息

    3、设置公钥

    • 应用公钥是给别人用的
    • 应用私钥是自己保存,用来匹配应用公钥
    • 支付宝公钥是支付宝用的,是在应用公钥设置好以后,支付宝自动生成

    4、代码实现

    (1)项目结构

    (2)在项目中配置公钥私钥

    把生成的 应用私钥 和 支付宝的公钥 放入keys目录下

    需要做修改,秘钥内容的头部和结尾加上两行字符串,注意所有秘钥都要加

    alipay_public_2048.txt  
        -----BEGIN PUBLIC KEY-----  # 加上这行  
           # 支付宝的公钥  
        -----END PUBLIC KEY-----   # 同上  
      
    app_private_2048.txt  
        -----BEGIN PUBLIC KEY-----  #同上  
           # 应用的私钥  
        -----END PUBLIC KEY-----   # 同上 
    

    (3)从Git上下载支付宝接口 pay.py

    需要安装模块:

    pip3 install pycryptodome
    
    1)pay.py
    # pay.py
    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)
    
    
    2)路由设置
    urlpatterns = [
        url('admin/', admin.site.urls),
        url(r'^page1/', views.page1),
        url(r'^page2/', views.page2),
    ]
    
    3)视图函数
    from django.shortcuts import render, redirect, HttpResponse
    from utils.pay import AliPay
    import json
    import time
    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/"
        merchant_private_key_path = "keys/app_private_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 = 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)
    
    
    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)
    
            sign = post_dict.pop('sign', None)
            status = alipay.verify(post_dict, sign)
            print('POST验证', status)
            return HttpResponse('POST返回')
    
        else:
            params = request.GET.dict()
            sign = params.pop('sign', None)
            status = alipay.verify(params, sign)
            print('GET验证', status)
            return HttpResponse('支付成功')
    
    
    4)模板层
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <link rel="stylesheet" href="dist/css/bootstrap.css">
    </head>
    <body>
        <form method="POST">
            {% csrf_token %}
            <input type="text" name="money">
            <input type="submit" value="去支付" />
        </form>
    
    
    <script></script>
    </body>
    </html>
    

    5、注意点

    • 如果支付成功,支付宝会回调,但是如果你的服务器挂掉了怎么办?

      支付宝24小时以内不定时再给你发,你修改掉订单状态即可

    • 支付成功,支付宝会有一个get回调,一个post回调:修改订单状态

    二、微信推送

    1、三种类型

    - 公众号(不能主动给用户发消息)
        -认证的公众号:需要营业执照,需要交钱,可以发多篇文章
        -未认证的公众号:一天只能发一篇文章
    - 服务号(微信推送)
        -需要申请,需要认证
        -可以主动给用户推送消息
        -能给推送的人,必须关注我的服务号
    	-沙箱环境
    - 企业号
    	-企业里用的:
    

    2、原理流程

    1. 浏览器访问服务器的登录页面
    2. 服务器返回登录成功,跳转首页。并生成底部的二维码,链接就是获取个人信息的
    3. 用户扫描二维码,关注公众号
    4. 引导用户进入授权页面同意授权,用户扫描下面的二维码
    5. 用户手机访问链接,用来获取微信个人信息的
    6. 微信服务器获取个人信息,访问回调地址redirect_uri,也就是个人服务器。
    7. 个人服务器访问微信服务器,通过code换取网页授权access_token(与基础支持中的access_token不同)
    8. 微信服务器将个人信息返回给服务器,服务器更新数据库
    

    3、代码实现

    (1)setting中配置各项地址

    WECHAT_CONFIG = {
        'app_id': 'wx3e1f0883236623f9',
        'appsecret': '508ec4590702c76e6863be6df01ad95a',
        'redirect_uri': 'http://42.56.89.12/callback/',
    }
    

    (2)视图层

    import json
    import functools
    import requests
    from django.conf import settings
    from django.shortcuts import render, redirect, HttpResponse
    from django.http import JsonResponse
    from app01 import models
    # 沙箱环境地质:https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login
    def index(request):
        obj = models.UserInfo.objects.get(id=1)
        return render(request,'index.html',{'obj':obj})
    
    
    def auth(func):
        def inner(request, *args, **kwargs):
            user_info = request.session.get('user_info')
            if not user_info:
                return redirect('/login/')
            return func(request, *args, **kwargs)
    
        return inner
    
    
    def login(request):
        """
        用户登录
        :param request: 
        :return: 
        """
        # models.UserInfo.objects.create(username='luffy',password=123)
    
        if request.method == "POST":
            user = request.POST.get('user')
            pwd = request.POST.get('pwd')
            obj = models.UserInfo.objects.filter(username=user, password=pwd).first()
            if obj:
                request.session['user_info'] = {'id': obj.id, 'name': obj.username, 'uid': obj.uid}
                return redirect('/bind/')
        else:
            return render(request, 'login.html')
    
    
    @auth
    def bind(request):
        """
        用户登录后,关注公众号,并绑定个人微信(用于以后消息推送)
        :param request: 
        :return: 
        """
        return render(request, 'bind.html')
    
    
    @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(
                # 商户的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)
    
        return JsonResponse(ret)
    
    
    def callback(request):
        """
        用户在手机微信上扫码后,微信自动调用该方法。
        用于获取扫码用户的唯一ID,以后用于给他推送消息。
        :param request: 
        :return: 
        """
        code = request.GET.get("code")
    
        # 用户md5值,用户唯一id
        state = request.GET.get("state")
    
        # 获取该用户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)
    
    
    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
    
        access_token = get_access_token()
    
        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('发送失败')
    
    

    (3)模板层

    {% load staticfiles %}
    
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <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>
    
    </body>
    </html>
    

    4、注意点

    • 我们所见的二维码:其实就是一个url地址
    • 在前端可以通过qrcode()将url生成二维码,例如qrcode({text: 'url'})

    微信推送亦可以参考博客:https://www.cnblogs.com/xiao987334176/articles/9469235.html

  • 相关阅读:
    ural 1146. Maximum Sum(动态规划)
    ural 1119. Metro(动态规划)
    ural 1013. K-based Numbers. Version 3(动态规划)
    Floyd算法
    杭电21题 Palindrome
    杭电20题 Human Gene Functions
    杭电15题 The Cow Lexicon
    杭电三部曲一、基本算法;19题 Cow Bowling
    杭电1002 Etaoin Shrdlu
    Qt 学习之路 2(37):文本文件读写
  • 原文地址:https://www.cnblogs.com/linagcheng/p/10191456.html
Copyright © 2020-2023  润新知