• 微信消息推送和支付宝支付


    微信服务号的消息推送

    - 公众号
    - 已认证公众号
    - 服务号
    - 已认证服务号
    - 企业号

    基于:微信认证服务号动推送微信消息。
    前提:关注服务号
    环境:沙箱环境

    微信公号平台:

    https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login

    实例:

    import json
    import functools
    import requests
    from django.conf import settings
    from django.shortcuts import render, redirect, HttpResponse
    from django_redis import get_redis_connection
    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):
        @functools.wraps(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):
        """
        生成二维码 (本质上就是一个url)
        扫描二维码,就是向这个url发送请求
        :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=settings.WECHAT_CONFIG["app_id"], # 'wx89085e915d351cae',
                redirect_uri=settings.WECHAT_CONFIG["redirect_uri"], # 'http://47.93.4.198/test/',
                # 回调地址;就是访问这个access_url后,微信会向 redirect_uri 发送请求(数据里面包含了用户的微信号,以及给用户生成的MD5值)
                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(openid),以后用于给他推送消息。
        :param request: 
        :return: 
        """
        code = request.GET.get("code")
        print(code)
        # 用户md5值
        state = request.GET.get("state")
        print(state)
        # 获取该用户openId(用户唯一,用于给用户发送消息)
        res = requests.get(
            url="https://api.weixin.qq.com/sns/oauth2/access_token",
            params={
                "appid": settings.WECHAT_CONFIG["app_id"],
                "secret": settings.WECHAT_CONFIG["appsecret"],
                "code": code,
                "grant_type": 'authorization_code',
            }
        ).json()
        # 获取的到openid表示用户授权成功
        openid = res.get("openid")
        print(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):
    
        conn = get_redis_connection('default')
    
        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()
    
            #在向微信发送信息的时候,需要先找微信拿一下token值,然后发送信息的时候,需要带上这个token值。
            if result.get("access_token"):
                access_token = result.get('access_token')
            else:
                access_token = None
            return access_token
    
        # token值 有效的时间为2小时,所以不用每次都请求。可以存放到redis(缓存中。)
        access_token=conn.get("token")
        if not access_token:
            print("not redis")
            access_token = get_access_token()
            conn.set("token",access_token,ex=60*60*2)
        print(access_token)
    
        id = request.session['user_info']['id']
        print(id)
        openid = models.UserInfo.objects.get(id=id).wx_id
    
        def send_custom_msg():
            body = {
                "touser": openid,
                "msgtype": "text",
                "text": {
                    "content": '云姐好美呀'
                }
            }
            response = requests.post(
                url="https://api.weixin.qq.com/cgi-bin/message/custom/send",
                params={
                    'access_token': access_token
                },
                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": 'DyfhSDx0oL-FomFwAbfATFnpV1DihaJBdl2NT4Lzzys',
                    "data": {
                        "student": {
                            "value": "云姐",
                            "color": "#173177"
                        },
                        "homwork": {
                            "value": "Django项目",
                            "color": "#173177"
                        },
                    }
                }
            )
            result = res.json()
            return result
    
        result = send_template_msg()
    
        if result.get('errcode') == 0:
            return HttpResponse('发送成功')
        return HttpResponse('发送失败')
    

    bind.html

    {% 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>
    <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);
                    $('#qrcode').empty().qrcode({text: result.data});
                }
            });
        }
    </script>
    
    </body>
    </html>
    

      Django中的

     settings.py 

    # ############# 微信 ##############
    WECHAT_CONFIG = {
        'app_id': 'wx4f7020c38e5578bb',
        'appsecret': 'd2f034a0b076156ab2007b99fcf32187',
        'redirect_uri': 'http://47.98.134.86/callback/',#授权回调页面域名 没有服务器
    }
    
    # redis配置
    CACHES = {
        "default": {
            "BACKEND": "django_redis.cache.RedisCache",
            "LOCATION": "redis://127.0.0.1:6379",
            "OPTIONS": {
                "CLIENT_CLASS": "django_redis.client.DefaultClient",
                "CONNECTION_POOL_KWARGS": {"max_connections": 100},
                "PASSWORD": "zh4350697",
            }
        }
    }
    

    总结:

    1. 注册账号
    				appID:wx89085e915d351cae
    				appsecret:64f87abfc664f1d4f11d0ac98b24c42d
    				
    				网页授权获取用户基本信息:47.98.134.86 或 域名 
    				
    			2. 关注公众号(已认证的服务号)
    			
    			3. 生成二维码,用户扫描;
    				将用户信息发送给微信,微信再将数据发送给设置redirect_uri地址(md5值)
    				
    			4. 回调地址:47.98.134.86/callback/
    				- 授权 (获取wx_id 就是授权成功了)
    				- 用户md5
    				- 获取wx_id 
    				在数据库中更新设置:wx_id 
    				
    			5. 发送消息(模板消息)
    				- wx_id 
    				- access_token(2小时有效期)
    

      

    支付宝支付 

    支付宝的沙箱环境

    https://openhome.alipay.com/platform/home.htm

    支付宝的官网的支付接口

    utils文件夹下的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 urllib.parse import urlparse, parse_qs
    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)
    

    支付的代码

    from django.shortcuts import render, redirect, HttpResponse
    from django.views.decorators.csrf import csrf_exempt
    from utils.pay import AliPay
    import time
    from django.conf import settings
    from alipay import models
    
    
    def aliPay():
    	obj = AliPay(
    		appid=settings.APPID,       #支付宝appID
    		app_notify_url=settings.NOTIFY_URL,  # 如果支付成功,支付宝会向这个地址发送POST请求(校验是否支付已经完成)
    		return_url=settings.RETURN_URL,  # 如果支付成功,重定向回到你的网站的地址。
    		alipay_public_key_path=settings.PUB_KEY_PATH,  # 支付宝公钥
    		app_private_key_path=settings.PRI_KEY_PATH,  # 应用私钥
    		debug=True,  # 默认False,
    	)
    	return obj
    
    
    def index(request):
    	if request.method == 'GET':
    		return render(request, 'index.html')
    
    	alipay = aliPay()
    
    	# 对购买的数据进行加密
    	title = request.POST.get('shop')
    	money = float(request.POST.get('price'))
    	out_trade_no = "x2" + str(time.time())
    	# 1. 在数据库创建一条数据:状态(待支付)
    	title = title
    	order_num = out_trade_no
    	models.Order.objects.create(title=title, order_num=order_num)
    
    	query_params = alipay.direct_pay(
    		subject=title,  # 商品简单描述
    		out_trade_no=out_trade_no,  # 商户订单号
    		total_amount=money,  # 交易金额(单位: 元 保留俩位小数)
    	)
    
    	pay_url = "https://openapi.alipaydev.com/gateway.do?{}".format(query_params)
    
    	return redirect(pay_url)
    
    
    def pay_result(request):
    	"""
    	支付完成后,跳转回的地址
    	:param request:
    	:return:
    	"""
    	params = request.GET.dict()
    	sign = params.pop('sign', None)
    
    	alipay = aliPay()
    
    	status = alipay.verify(params, sign)
    
    	if status:
    		# 原则上是在下面的接口进行修改订单的操作,但是没有服务器,就无法让支付宝发送POST请求,等到公司后就可以了
    		# 因此现在在这里进行修改订单的操作,方便测试.
    		order_num = params.get("out_trade_no")  # 订单号
    		# 支付成功后修改订单的状态
    		print(order_num)
    		models.Order.objects.update_or_create(order_num=order_num, defaults={"status": 1})
    		# order_obj = models.Order.objects.filter(order_num=order_num).first()
    		# order_obj.status = 1
    		return HttpResponse('支付成功')
    	return HttpResponse('支付失败')
    
    
    # 这个接口需要挂载到服务器上,让支付宝找到这个端口,这样才能发post请求
    @csrf_exempt
    def update_order(request):
    	"""
    	支付成功后,支付宝向该地址发送的POST请求(用于修改订单状态)
    	:param request:
    	:return:
    	"""
    	# 发送过来的一定是POST请求
    	if request.method == 'POST':
    		from urllib.parse import parse_qs
    
    		body_str = request.body.decode('utf-8')
    		post_data = parse_qs(body_str)
    
    		post_dict = {}
    		for k, v in post_data.items():
    			post_dict[k] = v[0]
    
    		alipay = aliPay()
    
    		sign = post_dict.pop('sign', None)
    		status = alipay.verify(post_dict, sign)
    		if status:
    			# 修改订单状态
    			out_trade_no = post_dict.get('out_trade_no')
    			print(out_trade_no)
    			# 2. 根据订单号将数据库中的数据进行更新
    			return HttpResponse('success')
    		else:
    			return HttpResponse('支付失败')
    	return HttpResponse('')
    

    index.html

    <!DOCTYPE html>
    <html lang="zh-CN">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <meta name="viewport" content="width=device-width, initial-scale=1">
    </head>
    <body>
        <form method="post">
            {% csrf_token %}
             购买的商品 <input type="text" name="shop">
            <input type="text" name="price" placeholder="请输入要支付的金额">
            <input type="submit" value="支付">
        </form>
    </body>
    </html>
    

      

      

    settings.py

    # 支付相关配置
    APPID = "2016101000655824"
    NOTIFY_URL = "http://127.0.0.1:8000/update_order/"
    RETURN_URL = "http://127.0.0.1:8000/pay_result/"
    PRI_KEY_PATH = "keys/app_private_2048.txt"
    PUB_KEY_PATH = "keys/alipay_public_2048.txt"
    

      

    keys文件夹下的
    alipay_public_2048.txt

    -----BEGIN PUBLIC KEY-----
    MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAozu/MQKIOeevh6HaYPaSTZ/dkFJQUTEi3jFiigx2ClYvivNbAlKwzdtcvBGr9SyV3oThU8mGkajv91Gk/c06iBtu968MWouygAOGamyRAUWhXjDSginM2R+WGSgaxUKrNOWqfh3IBe1YHA2N/ixaGy/Y1z/Dbl+mdBQPid0aH2sR9gvGYf4WxnUtqQu8An6zS0CGkPyKrlHyRy6nasKvWSfbXd6QgN4FHct7J4m6GbE4qIoUcyXTcUx23YJX/R+q7M0gzwQaBA5jQn7o8QUlfiCW2zN4fTU9+sutHuOEips3bzBdlcNt4Ndp5Skcv6AJcsJ+C4Md/3KwQ77urFj+nQIDAQAB
    -----END PUBLIC KEY-----

    app_private_2048.txt

    -----BEGIN RSA PRIVATE KEY-----
    MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCx92fMNvfhnJJogPyLuKZFyPPggGhwQLAE9SjYNTAJ8+Tbxh6VqztRt8hSmkWCa1Al01qncncUY3zmbucmpehVd9RwM7/rUhbaoMn6YC7+9WlfUj+k6McT1zIqG8MrkB9WbKQ06hCiJGz9DuuWlh+spklsq7RKopP7a4/2VuLF2XN+i6srvrczX6BoSA4fpOvHkOz/abHIBKZI8SUVgq2fMQc2h0lZFYjOXNrnFjBrckA4FRnTzeH7hvW4MDR9dRqxa0p3vx7gdRgQyBdmlFYf7QUFRDCizIS3UBsUbZWr8FflpVSTMEfL+A4aNaElRua605YOTTkqD7HXxBojKtmbAgMBAAECggEAXlx9B9W5d0Gqi4ig34CngOb9EHNlbOePrQFEh+cjspNELJeOzfL9v+V/bPTpmC5IT9YSAij6JLBfoFr7aw2a8/5WgKufvilHfuK0VXI8WOlM1sLohgr6y3VV3ufbgzSmuQ9fvcLB0QcZVtBbb/vnjYaZ9enQ7aXoau3sZkRJd3djcYZYwY8yhOMD9cigm+pr2st2BnLWubydtaTW8ogbwY7ctNWIhq3gDeauWdFFIJmnVpOutJIcvGeo+OgIB06tfYzHvZAMftBiHwkjPH33bWHy0v9IjF9ArIcvrYjYds/nK3e3vMJma8Vp8BRCk4Z/rXDZGK8Pw3RGl+fq29pfGQKBgQDeQMzU+qwXvG3p6g6CvsndWG0e2taYvbK0i5SAspSC2i/9aq85+jt8jqPOK90t0trPygD+wnahiOOVZwSQYT2C7JsvvguXxTDLki/t1j+BKYK8kgmV2SlWMqSpkc5tez9Iq2RzeMZpi+dQWi4xWplw8XXu0E0o2CE1z19HsX6ERQKBgQDM/SMq2vV2mT3WoyT63GbsPeNq/zHhSgGeCFcicUYRxJvqTdem9k8zpH4D4Te8bNrGbLco+3vi3Ad+GwPHLr2Bpqone5NvqHGbaHJpRwFEasiNFwdVNS8gjULDMuuc6tdeXqmdujSQ0/RDF2En7zXnrsREE6zMGqX/w00dy+70XwKBgBHIpZs1I6gSj8jzzY1wrr5jYPfjEuDN7Qq9UHir0W5W/xgL/VFqUHA7Cahpoh0UjiWqSEIaVVu/lFZUE+1pmn5raE99qXfPc4QWgndJeXNgWvGzzciLw9791mcrH5VrEzlBXZxPwbCYXT30uVWBpl1/NKyTRllKUf34Ret6rGDxAoGBAMr5eGIN73Ig6M9oOczAgpU37sDasgxPGGzf+0+ac/RSBsSpkXi8ec47+Z9j2amU68gAjBhjc9c9YZnnrAUFbhY77k4sGeA9HUjx0iAWc9XIGo9CFzuy7tg/p7Ta7dwx2VGTUEZiw3wIs9vfAY/mWCzxq1txU+/CD07CltCDRzfnAoGAWmUTu3YC1cPcZk4B5S6d6scCsQMrGAdwH5HFI7uCZpwx1HcVLTpEyfRgUGL1Buyc9/sq54pxBEVVprhDK0ja72wo9tIlxXUusqkoNCqSFZcq7dMMz2eu7zZUFrl2qjqv3CJAstLV6XIWicJK1X9Lnq1cyB1m+7/VLcC+41RX1F4=
    -----END RSA PRIVATE KEY-----
    

      

    支付宝支付的总结:

    a. 去支付宝申请 
    			- 正式:营业执照
    			- 测试:沙箱测试环境
    					APPID:2016082500309412
    					买家:
    						nbjsag5718@sandbox.com
    						111111
    						111111
    		b. 开发程序
    			SDK加密方式
    				- 官方
    				- github
    					pay.py 
    					依赖:pip3 install pycryptodome
    			
    			公钥私钥:
    				- 应用公钥
    					- 支付宝公钥
    				- 应用私钥
    				
    
    - 支付成功后,断电宕机
    						 要给支付宝返回一个success。这样支付宝才认为支付成功了。 不然会在一天内持续的给你发送post请求。
    				- 支付成功:return HttpResponse('success')
    

      

      

  • 相关阅读:
    configure.ac:91: error: possibly undefined macro: AC_SEARCH_LIBS
    debian上安装tmux
    ssh: Bad configuration option: usedns
    mysql启动失败“MySQL Daemon failed to start”
    批量查询文件的编码格式
    mysql计算QPS
    网络资源汇总
    jQuery Mobile + HTML5
    配置SQL Server 2008 R2 Reporting Services
    分享一个C#创建Barcode的DLL
  • 原文地址:https://www.cnblogs.com/zenghui-python/p/11704373.html
Copyright © 2020-2023  润新知