微信消息推送和支付寶支付

微信服務號的消息推送html

- 公衆號
- 已認證公衆號
- 服務號
- 已認證服務號
- 企業號

基於:微信認證服務號動推送微信消息。
前提:關注服務號
環境:沙箱環境python

微信公號平臺:jquery

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

 

實例:github

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.htmlajax

{% load staticfiles %}

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div style="width: 600px;margin: 0 auto">
    <h1>請關注路飛學城服務號,並綁定我的用戶(用於之後的消息提醒)</h1>
    <div>
        <h3>第一步:關注路飛學城微信服務號</h3>
        <img style="height: 100px;width: 100px" src="{% static "img/luffy.jpeg" %}">
    </div>
    <input type="button" value="下一步【獲取綁定二維碼】" onclick="getBindUserQcode()">
    <div>
        <h3>第二步:綁定我的帳戶</h3>
        <div id="qrcode" style="width: 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中的redis

 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",
        }
    }
}

 

總結:django

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小時有效期)

  

支付寶支付 json

支付寶的沙箱環境

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("\n", "")
        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')
相關文章
相關標籤/搜索