pip install django-simpleui
pip install django-import-export
pip install wechatpy[cryptography]
""" Django settings for wechatDemo project. Generated by 'django-admin startproject' using Django 2.2.6. For more information on this file, see https://docs.djangoproject.com/en/2.2/topics/settings/ For the full list of settings and their values, see https://docs.djangoproject.com/en/2.2/ref/settings/ """ import os # Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/2.2/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! SECRET_KEY = '#!2+_0h*+v-i&)yw0d(kkj_&p9smf&6^h0vakxv!^#@i&y3cct' # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True ALLOWED_HOSTS = ["*"] # Application definition INSTALLED_APPS = [ 'simpleui', 'import_export', 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'app' ] MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ] ROOT_URLCONF = 'wechatDemo.urls' TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(BASE_DIR, 'templates')] , 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], 'libraries': { 'apptags': 'app.templatetags.apptags' }, }, }, ] WSGI_APPLICATION = 'wechatDemo.wsgi.application' # Database # https://docs.djangoproject.com/en/2.2/ref/settings/#databases DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), } } # Password validation # https://docs.djangoproject.com/en/2.2/ref/settings/#auth-password-validators AUTH_PASSWORD_VALIDATORS = [ { 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', }, { 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', }, { 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', }, { 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', }, ] # Internationalization # https://docs.djangoproject.com/en/2.2/topics/i18n/ LANGUAGE_CODE = 'zh-hans' TIME_ZONE = 'Asia/Shanghai' USE_I18N = True USE_L10N = True USE_TZ = False # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/2.2/howto/static-files/ STATIC_URL = '/static/'
from django.db import models # Create your models here. # 商品表 class commodity(models.Model): name = models.CharField(max_length=225, verbose_name="商品名稱", blank=True, default="") desc = models.TextField(verbose_name="商品描述", blank=True) price = models.FloatField(verbose_name="價格", default=0) class Meta: verbose_name_plural = "商品表" def __str__(self): return self.name # 訂單表 class order(models.Model): ordercode = models.CharField(max_length=255, verbose_name="訂單號") openid = models.CharField(max_length=255, verbose_name="openid", default="") commodity = models.ManyToManyField(to='commodity', verbose_name="商品表", null=True, blank=True) STATE_CHOICES = ( (0, '待支付'), (1, '已支付') ) state = models.CharField(max_length=2, choices=STATE_CHOICES, default=0, ) # 支付返回結果 appid = models.CharField(max_length=255, verbose_name="appid", default="") bank_type = models.CharField(max_length=255, verbose_name="bank_type", default="") cash_fee = models.IntegerField(verbose_name="支付金額(分)", default=0) fee_type = models.CharField(max_length=255, verbose_name="支付類型", default="") is_subscribe = models.CharField(max_length=255, verbose_name="is_subscribe", default="") mch_id = models.CharField(max_length=255, verbose_name="mch_id", default="") nonce_str = models.CharField(max_length=255, verbose_name="nonce_str", default="") out_trade_no = models.CharField(max_length=255, verbose_name="商戶訂單號", default="") result_code = models.CharField(max_length=255, verbose_name="result_code", default="") return_code = models.CharField(max_length=255, verbose_name="return_code", default="") time_end = models.CharField(max_length=255, verbose_name="time_end", default="") total_fee = models.CharField(max_length=255, verbose_name="total_fee", default="") trade_type = models.CharField(max_length=255, verbose_name="trade_type", default="") transaction_id = models.CharField(max_length=255, verbose_name="transaction_id", default="") sign = models.CharField(max_length=255, verbose_name="sign", default="") # 對帳表 class bill(models.Model): jysh = models.CharField(max_length=255, verbose_name="交易時間", default="") appid = models.CharField(max_length=255, verbose_name="公衆帳號ID", default="") mch_id = models.CharField(max_length=255, verbose_name="商戶號", default="") tyshh = models.CharField(max_length=255, verbose_name="特約商戶號", default="") sbh = models.CharField(max_length=255, verbose_name="設備號", default="") wxdd = models.CharField(max_length=255, verbose_name="微信訂單號", default="") shdd = models.CharField(max_length=255, verbose_name="商戶訂單號", default="") openid = models.CharField(max_length=255, verbose_name="用戶標識", default="") jylx = models.CharField(max_length=255, verbose_name="交易類型", default="") jyzt = models.CharField(max_length=255, verbose_name="交易狀態", default="") fkyh = models.CharField(max_length=255, verbose_name="付款銀行", default="") hbzl = models.CharField(max_length=255, verbose_name="貨幣種類", default="") yjddje = models.CharField(max_length=255, verbose_name="應結訂單金額", default="") djjje = models.CharField(max_length=255, verbose_name="代金券金額", default="") wxtkdh = models.CharField(max_length=255, verbose_name="微信退款單號", default="") hstkdh = models.CharField(max_length=255, verbose_name="商戶退款單號", default="") tkje = models.CharField(max_length=255, verbose_name="退款金額", default="") czjtkje = models.CharField(max_length=255, verbose_name="充值券退款金額", default="") tklx = models.CharField(max_length=255, verbose_name="退款類型", default="") tkzt = models.CharField(max_length=255, verbose_name="退款狀態", default="") spmc = models.CharField(max_length=255, verbose_name="商品名稱", default="") shsjb = models.CharField(max_length=255, verbose_name="商戶數據包", default="") sxf = models.CharField(max_length=255, verbose_name="手續費", default="") fx = models.CharField(max_length=255, verbose_name="費率", default="") ddje = models.CharField(max_length=255, verbose_name="訂單金額", default="") sqtkje = models.CharField(max_length=255, verbose_name="申請退款金額", default="") flbz = models.CharField(max_length=255, verbose_name="費率備註", default="")
python manage.py makemigrations python manage.py migrate
from django.contrib import admin from app import models from import_export import resources from import_export.admin import ImportExportModelAdmin from django.apps import apps admin.site.site_header = "XXX後臺管理" admin.site.site_title = "XXX後臺" # Register your models here. class commodityResource(resources.ModelResource): def __init__(self): super(commodityResource, self).__init__() field_list = models.commodity._meta.fields self.vname_dict = {} for i in field_list: self.vname_dict[i.name] = i.verbose_name # 默認導入導出field的column_name爲字段的名稱,這裏修改成字段的verbose_name def get_export_fields(self): fields = self.get_fields() for field in fields: field_name = self.get_field_name(field) # 若是咱們設置過verbose_name,則將column_name替換爲verbose_name。不然維持原有的字段名 if field_name in self.vname_dict.keys(): field.column_name = self.vname_dict[field_name] return fields def after_import(self, dataset, result, using_transactions, dry_run, **kwargs): print("after_import") def after_import_instance(self, instance, new, **kwargs): print("after_import_instance") class Meta: model = models.commodity skip_unchanged = True report_skipped = True fields = ("id", "name", "desc", "price",) @admin.register(models.commodity) class AppTypeAdmin(ImportExportModelAdmin): list_display = ("name", "price", "desc") list_display_links = ("name", "price", "desc") search_fields = ('name', "price", 'desc') model_icon = "fa fa-tag" list_per_page = 50 resource_class = commodityResource def save_model(self, request, obj, form, change): super().save_model(request, obj, form, change) @admin.register(models.order) class orderAdmin(admin.ModelAdmin): list_display = ("ordercode", "openid") filter_horizontal = ["commodity"]
from wechatpy.oauth import WeChatOAuth from django.shortcuts import render, redirect from django.http import JsonResponse, HttpResponse, HttpResponseRedirect import time import datetime from django.conf import settings from django.http import JsonResponse from django.views.decorators.csrf import csrf_exempt from django.shortcuts import render import uuid from wechatpy import WeChatClient import os import json from wechatpy import WeChatPay from app import models from wechatpy.pay import dict_to_xml # Create your views here. # 公衆號id AppID = "appid" # 公衆號AppSecret AppSecret = "appsecret" # 商戶id MCH_ID = 'mch_id' # 商戶API祕鑰 API_KEY = 'api_key' # 接收微信支付異步通知回調地址 notify_url = "http://i157422s94.iok.la/wxjssdk/" # 微信認證文件,建議經過nginx配置 def wechatauth(request): return HttpResponse("b1reLtO1xRzEjqxJ") # 定義受權裝飾器 def getWeChatOAuth(redirect_url): return WeChatOAuth(AppID, AppSecret, redirect_url, 'snsapi_userinfo') def oauth(method): def warpper(request): if request.session.get('user_info', None) is None: code = request.GET.get('code', None) wechat_oauth = getWeChatOAuth(request.get_raw_uri()) url = wechat_oauth.authorize_url print(url) if code: try: wechat_oauth.fetch_access_token(code) user_info = wechat_oauth.get_user_info() print(user_info) except Exception as e: print(str(e)) # 這裏須要處理請求裏包含的 code 無效的狀況 # abort(403) else: # 建議存儲在用戶表 request.session['user_info'] = user_info else: return redirect(url) return method(request) return warpper # 獲取用戶信息UserInfo @oauth def userinfo(request): user_info = request.session.get('user_info') return render(request, 'userinfo.html', {"user_info": user_info}) # 微信JS SDK調用 @oauth def wxjssdk(request): user_info = request.session.get('user_info') trade_type = "JSAPI" body = "商品描述" total_fee = "10" notify_url = "http://wwww.wezoz.com/notify_url/" user_id = user_info["openid"] wechatPay = WeChatPay( appid=AppID, api_key=API_KEY, mch_id=MCH_ID, ) order = wechatPay.order.create(trade_type, body, total_fee, notify_url, user_id=user_id) wxpay_params = wechatPay.jsapi.get_jsapi_params(order['prepay_id']) print(wxpay_params) return render(request, 'index.html', {"wxpay_params": wxpay_params}) @oauth def commodity(request): commoditys = models.commodity.objects.all() return render(request, 'commodity.html', {"commoditys": commoditys}) @csrf_exempt def order_jsapi(request): user_info = request.session.get('user_info') cid = request.POST.get("cid") cty = models.commodity.objects.filter(id=cid).first() trade_type = "JSAPI" body = cty.desc total_fee = int(cty.price * 100) notify_url = "http://wwww.wezoz.com/notify_url/" user_id = user_info["openid"] wechatPay = WeChatPay( appid=AppID, api_key=API_KEY, mch_id=MCH_ID, ) order = wechatPay.order.create(trade_type, body, total_fee, notify_url, user_id=user_id, detail=cty.name, attach=cid) print(order) wxpay_params = wechatPay.jsapi.get_jsapi_params(order['prepay_id']) print(wxpay_params) prepay_id = order["prepay_id"] # 微信訂單號 params = wechatPay.order.get_appapi_params(prepay_id) print(params) obj = models.order.objects.create(ordercode=prepay_id, openid=user_id) obj.commodity.add(cid) return JsonResponse(wxpay_params) @csrf_exempt def notify_url(request): wechatPay = WeChatPay( appid=AppID, api_key=API_KEY, mch_id=MCH_ID, ) result = wechatPay.parse_payment_result(request.body) print(result) query = wechatPay.order.query(result["transaction_id"]) print(query) attach = result["attach"] # 附加參數 # 此處驗證須要添加業務邏輯,只經過openid 驗證不嚴謹 ord = models.order.objects.filter(openid=result["openid"]).first() ord.appid = result["appid"] ord.bank_type = result["bank_type"] ord.cash_fee = result["cash_fee"] ord.fee_type = result["fee_type"] ord.is_subscribe = result["is_subscribe"] ord.mch_id = result["mch_id"] ord.nonce_str = result["nonce_str"] ord.out_trade_no = result["out_trade_no"] ord.result_code = result["result_code"] ord.return_code = result["return_code"] ord.time_end = result["time_end"] ord.total_fee = result["total_fee"] ord.trade_type = result["trade_type"] ord.transaction_id = result["transaction_id"] ord.sign = result["sign"] ord.state = 1 ord.save() print(result) return HttpResponse("SUCCESS") def validate_date_str(date_str): try: datetime.datetime.strptime(date_str, '%Y-%m-%d') return True except ValueError: return False # 訪問地址以下 http://127.0.0.1:8009/downloadBill/2019-10-23.html @csrf_exempt def downloadBill(request, date): if validate_date_str(date): wechatPay = WeChatPay( appid=AppID, api_key=API_KEY, mch_id=MCH_ID ) result = wechatPay.tools.download_bill(date.replace("-", "")) print(result) billArray = result.split("\r\n") # 分割帳單,一行爲一組數據,分割後第一行爲數據標題,倒數第三行爲統計標題,倒數第二行爲統計金額,最後一行爲多餘的空行 titleArray = billArray[0].split(',') # 第一行爲標題 title_total = billArray[len(billArray) - 2] # 統計標題 data_total = billArray[len(billArray) - 1] # 統計金額 del billArray[0] # 去掉標題 del billArray[len(billArray) - 3] # 去掉總標題 del billArray[len(billArray) - 2] # 去掉總額 del billArray[len(billArray) - 1] # 去掉空行,剩下的爲帳單詳情數據 mybill = [] # 訂單詳細信息 # 循環帳單詳情數據 for i in billArray: _detail = i.split('`')[:-1] del _detail[0] # 去掉前邊的空數據 _detail_temp = [] for d in _detail: # 每個數據(去掉最後的逗號) _detail_val = d[:-1] _detail_temp.append(_detail_val) # TODO業務處理 # print(d[:-1]) # TODO業務處理 mybill.append(_detail_temp) # 帳單入庫 須要兩張表 日帳單表-->日帳單對應的詳細表 # 對帳邏輯需自行處理結合 訂單表 + 對帳表 逐條勾兌,建議對帳操做放在數據庫進行 # 對帳調度 Celery 下一章介紹 else: print("日期格式輸入有誤!") return HttpResponse("SUCCESS") @csrf_exempt def jsapi_signature(request): noncestr = uuid.uuid4() timestamp = int(time.time()) url = request.POST['url'] client = WeChatClient(AppID, AppSecret) ticket_response = client.jsapi.get_ticket() signature = client.jsapi.get_jsapi_signature( noncestr, ticket_response['ticket'], timestamp, url ) ret_dict = { 'noncestr': noncestr, 'timestamp': timestamp, 'url': url, 'signature': signature, } return JsonResponse(ret_dict) def log(request): print('Hello World!') return JsonResponse({ 'status': 'ok', })
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> <title>商品展現</title> <meta content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0" name="viewport"/> <meta content="yes" name="apple-mobile-web-app-capable"/> <meta content="black" name="apple-mobile-web-app-status-bar-style"/> <meta content="telephone=no" name="format-detection"/> <!-- 最新版本的 Bootstrap 核心 CSS 文件 --> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"> <!-- 可選的 Bootstrap 主題文件(通常不用引入) --> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous"> <script type="text/javascript" src="https://www.szyfd.xyz/static/js/jquery-3.4.1.js"></script> <!-- 最新的 Bootstrap 核心 JavaScript 文件 --> <script src="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script> </head> <body> <div class="container-fluid"> <table class="table table-bordered table-striped"> <thead> <tr> <th>商品名稱</th> <th>商品價格</th> </tr> </thead> <tbody> {% for c in commoditys %} <tr> <th scope="row"> <code>{{ c.name }}</code> </th> <td>價格{{ c.price }} <button type="button" class="btn btn-primary requestOrder" cid="{{ c.id }}" style="margin-left: 20px">點擊支付 </button> </td> </tr> {% endfor %} </tbody> </table> </div> <!--請勿應用外網js 連接--> <script type="text/javascript"> $(function () { $.ajaxSetup({ beforeSend: function (xhr, settings) { xhr.setRequestHeader('X-CSRFtoken', $.cookie('csrftoken')); } }); }) </script> <script type="text/javascript" src="https://www.szyfd.xyz/static/js/jquery.cookie.js"></script> <!--end--> <script type="text/javascript" src="http://res.wx.qq.com/open/js/jweixin-1.0.0.js"></script> <script type="text/javascript"> (function (window, $) { var fInitWeixin = function (d) { wx.config({ debug: true, // 開啓調試模式,調用的全部api的返回值會在客戶端alert出來,若要查看傳入的參數,能夠在pc端打開,參數信息會經過log打出,僅在pc端時纔會打印。 appId: 'wx975992a7ef6d6c9d', // 必填,公衆號的惟一標識 timestamp: d.timestamp, // 必填,生成簽名的時間戳 nonceStr: d.noncestr, // 必填,生成簽名的隨機串 signature: d.signature,// 必填,簽名,見附錄1 jsApiList: ['checkJsApi', 'onMenuShareTimeline', 'onMenuShareAppMessage', 'onMenuShareQQ', 'onMenuShareWeibo', 'onMenuShareQZone', 'hideMenuItems', 'showMenuItems', 'hideAllNonBaseMenuItem', 'showAllNonBaseMenuItem', 'translateVoice', 'startRecord', 'stopRecord', 'onVoiceRecordEnd', 'playVoice', 'onVoicePlayEnd', 'pauseVoice', 'stopVoice', 'uploadVoice', 'downloadVoice', 'chooseImage', 'previewImage', 'uploadImage', 'downloadImage', 'getNetworkType', 'openLocation', 'getLocation', 'hideOptionMenu', 'showOptionMenu', 'closeWindow', 'scanQRCode', 'chooseWXPay', 'openProductSpecificView', 'addCard', 'chooseCard', 'openCard'] // 必填,須要使用的JS接口列表,全部JS接口列表見附錄2 }); } var s = $.ajax({ type: 'post', url: '/get_signature/', dataType: 'json', data: {url: location.href}, success: function (d) { fInitWeixin(d) } }) })(window, jQuery) </script> <script type="text/javascript"> /* * 注意: * 1. 全部的JS接口只能在公衆號綁定的域名下調用,公衆號開發者須要先登陸微信公衆平臺進入「公衆號設置」的「功能設置」裏填寫「JS接口安全域名」。 * 2. 若是發如今 Android 不能分享自定義內容,請到官網下載最新的包覆蓋安裝,Android 自定義分享接口需升級至 6.0.2.58 版本及以上。 * 3. 完整 JS-SDK 文檔地址:http://mp.weixin.qq.com/wiki/7/aaa137b55fb2e0456bf8dd9148dd613f.html * * 若有問題請經過如下渠道反饋: * 郵箱地址:weixin-open@qq.com * 郵件主題:【微信JS-SDK反饋】具體問題 * 郵件內容說明:用簡明的語言描述問題所在,並交代清楚遇到該問題的場景,可附上截屏圖片,微信團隊會盡快處理你的反饋。 */ wx.ready(function () { // 1 判斷當前版本是否支持指定 JS 接口,支持批量判斷 document.querySelector('#checkJsApi').onclick = function () { wx.checkJsApi({ jsApiList: [ 'getNetworkType', 'previewImage' ], success: function (res) { alert(JSON.stringify(res)); } }); }; shareData = { title: '深圳易方達軟件', desc: '按時交付完美主義者', link: location.href, imgUrl: 'https://www.szyfd.xyz/static/HOME/style/images/shareLogo.png', trigger: function (res) { // 不要嘗試在trigger中使用ajax異步請求修改本次分享的內容,由於客戶端分享操做是一個同步操做,這時候使用ajax的回包會尚未返回 }, success: function (res) { }, cancel: function (res) { }, fail: function (res) { //alert(JSON.stringify(res)); } } // 2. 分享接口 wx.onMenuShareAppMessage(shareData); wx.onMenuShareTimeline(shareData); wx.onMenuShareQQ(shareData); wx.onMenuShareWeibo(shareData); wx.onMenuShareQZone(shareData); function decryptCode(code, callback) { } }); wx.error(function (res) { alert(res.errMsg); }); $(".requestOrder").click(function () { currentThis = $(this); cid = $(currentThis).attr("cid"); $.ajax({ type: 'post', url: '/order_jsapi', dataType: 'json', data: {cid: cid}, success: function (d) { wx.chooseWXPay({ timestamp: d.timeStamp, nonceStr: d.nonceStr, package: d.package, signType: d.signType, // 注意:新版支付接口使用 MD5 加密 paySign: d.paySign }); } }) }) </script> </body> </html>
商品表javascript