正式環境:用營業執照,申請商戶號,appidhtml
測試環境:沙箱環境(支付寶給開發者提供的調試環境應用) —— https://openhome.alipay.com/platform/appDaily.htm?tab=info前端
把生成的 應用私鑰 和 支付寶的公鑰 放入keys目錄下python
須要作修改,祕鑰內容的頭部和結尾加上兩行字符串,注意全部祕鑰都要加jquery
alipay_public_2048.txt -----BEGIN PUBLIC KEY----- # 加上這行 # 支付寶的公鑰 -----END PUBLIC KEY----- # 同上 app_private_2048.txt -----BEGIN PUBLIC KEY----- #同上 # 應用的私鑰 -----END PUBLIC KEY----- # 同上
須要安裝模塊:git
pip3 install pycryptodome
# 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("\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)
urlpatterns = [ url('admin/', admin.site.urls), url(r'^page1/', views.page1), url(r'^page2/', views.page2), ]
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('支付成功')
<!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>
若是支付成功,支付寶會回調,可是若是你的服務器掛掉了怎麼辦?ajax
支付寶24小時之內不定時再給你發,你修改掉訂單狀態便可數據庫
支付成功,支付寶會有一個get回調,一個post回調:修改訂單狀態django
- 公衆號(不能主動給用戶發消息) -認證的公衆號:須要營業執照,須要交錢,能夠發多篇文章 -未認證的公衆號:一天只能發一篇文章 - 服務號(微信推送) -須要申請,須要認證 -能夠主動給用戶推送消息 -能給推送的人,必須關注個人服務號 -沙箱環境 - 企業號 -企業裏用的:
1. 瀏覽器訪問服務器的登陸頁面 2. 服務器返回登陸成功,跳轉首頁。並生成底部的二維碼,連接就是獲取我的信息的 3. 用戶掃描二維碼,關注公衆號 4. 引導用戶進入受權頁面贊成受權,用戶掃描下面的二維碼 5. 用戶手機訪問連接,用來獲取微信我的信息的 6. 微信服務器獲取我的信息,訪問回調地址redirect_uri,也就是我的服務器。 7. 我的服務器訪問微信服務器,經過code換取網頁受權access_token(與基礎支持中的access_token不一樣) 8. 微信服務器將我的信息返回給服務器,服務器更新數據庫
WECHAT_CONFIG = { 'app_id': 'wx3e1f0883236623f9', 'appsecret': '508ec4590702c76e6863be6df01ad95a', 'redirect_uri': 'http://42.56.89.12/callback/', }
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('發送失敗')
{% 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> {# 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>
qrcode()
將url生成二維碼,例如qrcode({text: 'url'})
微信推送亦能夠參考博客:https://www.cnblogs.com/xiao987334176/articles/9469235.htmljson