1. 程式人生 > >Python實現微信小程式支付功能

Python實現微信小程式支付功能

由於最近自己在做小程式的支付,就在這裡簡單介紹一下講一下用python做小程式支付這個流程。當然在進行開發之前還是建議讀一下具體的流程,清楚支付的過程。

1.支付互動流程

 2.獲取openid(微信使用者標識)

 1 import requests
 2  
 3 from config import APPID, SECRET
 4  
 5  
 6 class OpenidUtils(object):
 7  
 8     def __init__(self, jscode):
 9         self.url = "https://api.weixin.qq.com/sns/jscode2session
" 10 self.appid = APPID # 小程式id 11 self.secret = SECRET # 不要跟後面支付的key搞混 12 self.jscode = jscode # 前端傳回的動態jscode 13 14 def get_openid(self): 15 # url一定要拼接,不可用傳參方式 16 url = self.url + "?appid=" + self.appid + "&secret=" + self.secret + "&js_code=
" + self.jscode + "&grant_type=authorization_code" 17 r = requests.get(url) 18 print(r.json()) 19 openid = r.json()['openid'] 20 21 return openid

3.支付請求

  1 # -*- coding:utf-8 -*-
  2 import requests
  3 import hashlib
  4 import xmltodict
  5 import time
  6 import
random 7 import string 8 import urllib2 9 import sys 10 11 12 class WX_PayToolUtil(): 13 """ 微信支付工具 """ 14 15 def __init__(self, APP_ID, MCH_ID, API_KEY, NOTIFY_URL): 16 self._APP_ID = APP_ID # 小程式ID 17 self._MCH_ID = MCH_ID # # 商戶號 18 self._API_KEY = API_KEY 19 self._UFDODER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder" # 介面連結 20 self._NOTIFY_URL = NOTIFY_URL # 非同步通知 21 22 def generate_sign(self, param): 23 '''生成簽名''' 24 stringA = '' 25 ks = sorted(param.keys()) 26 # 引數排序 27 for k in ks: 28 stringA += (k + '=' + param[k] + '&') 29 # 拼接商戶KEY 30 stringSignTemp = stringA + "key=" + self._API_KEY 31 # md5加密,也可以用其他方式 32 hash_md5 = hashlib.md5(stringSignTemp.encode('utf8')) 33 sign = hash_md5.hexdigest().upper() 34 return sign 35 36 ''' 37 # python2另外一種實現方法 38 def generate_sign(self, params): 39 ret = [] 40 for k in sorted(params.keys()): 41 if (k != 'sign') and (k != '') and (params[k] is not None): 42 ret.append('%s=%s' % (k, params[k])) 43 params_str = '&'.join(ret) 44 params_str = '%(params_str)s&key=%(partner_key)s' % {'params_str': params_str, 'partner_key': key} 45 46 reload(sys) 47 sys.setdefaultencoding('utf8') 48 49 params_str = hashlib.md5(params_str.encode('utf-8')).hexdigest() 50 sign = params_str.upper() 51 return sign 52 ''' 53 54 def getPayUrl(self, orderid, openid, goodsPrice, **kwargs): 55 """向微信支付端發出請求,獲取url""" 56 key = self._API_KEY 57 nonce_str = ''.join(random.sample(string.letters + string.digits, 30)) # 生成隨機字串,小於32位 58 params = { 59 'appid': self._APP_ID, # 小程式ID 60 'mch_id': self._MCH_ID, # 商戶號 61 'nonce_str': nonce_str, # 隨機字串 62 "body": '測試訂單', # 支付說明 63 'out_trade_no': orderid, # 生成的訂單號 64 'total_fee': str(goodsPrice), # 標價金額 65 'spbill_create_ip': "127.0.0.1", # 小程式不能獲取客戶ip,web用socekt實現 66 'notify_url': self._NOTIFY_URL, 67 'trade_type': "JSAPI", # 支付型別 68 "openid": openid, # 使用者id 69 } 70 # 生成簽名 71 params['sign'] = self.generate_sign(params) 72 73 # python3一種寫法 74 param = {'root': params} 75 xml = xmltodict.unparse(param) 76 response = requests.post(self._UFDODER_URL, data=xml.encode('utf-8'), headers={'Content-Type': 'text/xml'}) 77 # xml 2 dict 78 msg = response.text 79 xmlmsg = xmltodict.parse(msg) 80 # 4. 獲取prepay_id 81 if xmlmsg['xml']['return_code'] == 'SUCCESS': 82 if xmlmsg['xml']['result_code'] == 'SUCCESS': 83 prepay_id = xmlmsg['xml']['prepay_id'] 84 # 時間戳 85 timeStamp = str(int(time.time())) 86 # 5. 五個引數 87 data = { 88 "appId": self._APP_ID, 89 "nonceStr": nonce_str, 90 "package": "prepay_id=" + prepay_id, 91 "signType": 'MD5', 92 "timeStamp": timeStamp, 93 } 94 # 6. paySign簽名 95 paySign = self.generate_sign(data) 96 data["paySign"] = paySign # 加入簽名 97 # 7. 傳給前端的簽名後的引數 98 return data 99 100 # python2一種寫法 101 ''' 102 request_xml_str = '<xml>' 103 for key, value in params.items(): 104 if isinstance(value, str): 105 request_xml_str = '%s<%s><![CDATA[%s]]></%s>' % (request_xml_str, key, value, key,) 106 else: 107 request_xml_str = '%s<%s>%s</%s>' % (request_xml_str, key, value, key,) 108 request_xml_str = '%s</xml>' % request_xml_str 109 110 # 向微信支付發出請求,並提取回傳資料 111 res = urllib2.Request(self._UFDODER_URL, data=request_xml_str.encode("utf-8")) 112 res_data = urllib2.urlopen(res) 113 res_read = res_data.read() 114 doc = xmltodict.parse(res_read) 115 return_code = doc['xml']['return_code'] 116 if return_code == "SUCCESS": 117 result_code = doc['xml']['result_code'] 118 if result_code == "SUCCESS": 119 doc = doc['xml'] 120 data = { 121 "appId": self._APP_ID, 122 "nonceStr": nonce_str, 123 "package": "prepay_id=" + doc["prepay_id"], 124 "signType": 'MD5', 125 "timeStamp": str(int(time.time())), 126 } 127 # paySign簽名 128 paySign = self.generate_sign(data) 129 data["paySign"] = paySign # 加入簽名 130 return data 131 else: 132 err_des = doc['xml']['err_code_des'] 133 return err_des 134 else: 135 fail_des = doc['xml']['return_msg'] 136 return fail_des 137 '''

其他的支付方式獲取使用者的ip地址可以通過socket.gethostbyname(socket.gethostname())方法來獲取。

4.支付回撥

 1 # 統一下單回撥處理
 2  
 3 import xmltodict
 4  
 5 from django.http import HttpResponse
 6  
 7 def payback(request):
 8     msg = request.body.decode('utf-8')
 9     xmlmsg = xmltodict.parse(msg)
10  
11     return_code = xmlmsg['xml']['return_code']
12  
13     if return_code == 'FAIL':
14         # 官方發出錯誤
15         return HttpResponse("""<xml><return_code><![CDATA[FAIL]]></return_code>
16                             <return_msg><![CDATA[Signature_Error]]></return_msg></xml>""",
17                             content_type='text/xml', status=200)
18     elif return_code == 'SUCCESS':
19         # 拿到這次支付的訂單號
20         out_trade_no = xmlmsg['xml']['out_trade_no']
21  
22         # 根據需要處理業務邏輯
23  
24         return HttpResponse("""<xml><return_code><![CDATA[SUCCESS]]></return_code>
25                             <return_msg><![CDATA[OK]]></return_msg></xml>""",
26                             content_type='text/xml', status=200)

5.安全問題

在使用的過程中商戶系統對於支付結果通知的內容一定要做簽名驗證,並校驗返回的訂單金額是否與商戶側的訂單金額一致,防止資料洩漏導致出現“假通知”,造成資金損失。

我在開發過程中的解決方式是在向微信支付端發起請求的時候,把訂單號,金額,簽名等存入資料庫,然後在回撥函式那裡進行校驗判斷。在確認跟前面訂單情況一樣的情況下,才進行後續一系列的操作。

最後送給大家一段祝福

#                                  _oo8oo_
#                                 o8888888o
#                                 88" . "88
#                                 (| -_- |)
#                                 0\  =  /0
#                               ___/'==='\___
#                             .' \\|     |# '.
#                            / \\|||  :  |||# \
#                           / _||||| -:- |||||_ \
#                          |   | \\\  -  #/ |   |
#                          | \_|  ''\---/''  |_/ |
#                          \  .-\__  '-'  __/-.  /
#                        ___'. .'  /--.--\  '. .'___
#                     ."" '<  '.___\_<|>_/___.'  >' "".
#                    | | :  `- \`.:`\ _ /`:.`/ -`  : | |
#                    \  \ `-.   \_ __\ /__ _/   .-` /  /
#                =====`-.____`.___ \_____/ ___.`____.-`=====
#                                  `=---=`
#
#
#               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#
#                          強大爺保佑         永不宕機/永無bug
啦啦啦