# -*- coding: utf-8 -*- import os import sys import json import time import datetime import hashlib import binascii import pymysql import traceback import urllib.request import tornado.ioloop import tornado.web mysql_cluster_json = json.loads(open('../config/pay_backend.mysql.cluster.json', 'r').read()) notifyapi_json = json.loads(open('../config/pay_backend.notifyapi.json', 'r').read()) last_idx = 0 confirmed_order_list = [] def info(msg): print(str(datetime.datetime.now()) + '[INFO] ' + msg, flush = True) def getMysqlConn(accountid): hash_code = binascii.crc32(accountid.encode()) assert(hash_code >= 0) mysql_conf = mysql_cluster_json[hash_code % len(mysql_cluster_json)] return pymysql.connect(host = mysql_conf['host'], port = mysql_conf['port'], user = mysql_conf['user'], passwd = mysql_conf['passwd'], db = 'paydb', charset = 'utf8' ) def md5Sign(params, secret, timestamp, connstr = '&', secret_connstr = ':'): params_str = '' for key in sorted(params.keys()): params_str = params_str + key + '=' + str(params[key]) + connstr if params_str != '' and connstr != '': params_str = params_str[0:-1] str1 = params_str + secret_connstr + str(timestamp) + secret print(str1) try: m5 = hashlib.md5() m5.update(str1.encode('utf-8')) return m5.hexdigest() except Exception as e: info('md5Sign error: ' + str(e)) def urlencodeParams(params): return urllib.parse.urlencode(params) params_str = '' for key in sorted(params.keys()): params_str = params_str + key + '=' + urllib.urlencode(str(params[key])) + '&' return params_str def getDaySeconds(time_val, incdays): time_zone = 8 dayseconds = int((time_val + time_zone * 3600)/3600/24 + incdays) * 3600 * 24 - 3600 * time_zone; info(dayseconds) return dayseconds def fetchConfirmedOrderList(): global last_idx global confirmed_order_list try: conn = getMysqlConn('') cursor = conn.cursor() cursor.execute('SELECT idx, orderid ' 'FROM confirmed_order WHERE idx>%d AND status=0;' % last_idx) rows = cursor.fetchall(); for row in rows: confirmed_order_list.append({ 'idx' : int(row[0]), 'orderid': row[1] }) last_idx = int(row[0]) except Exception as e: print(e) return def confirmOneOrder(orderid): try: conn = getMysqlConn('') cursor = conn.cursor() cursor.execute('UPDATE orderinfo SET status=1, confirmtime=%d ' 'WHERE orderid="%s";' % ( time.time(), conn.escape_string(orderid)) ) conn.commit() cursor.execute('UPDATE confirmed_order SET status=1, confirm_time=%d ' 'WHERE orderid="%s";' % ( time.time(), conn.escape_string(orderid)) ) conn.commit() except Exception as e : info('confirOneOrder orderid:' + orderid + ' error:' + str(e)) traceback.print_exc(file=sys.stdout) def sendOneOrder(conf, idx, orderid): global notifyapi_json try: conn = getMysqlConn('') cursor = conn.cursor() cursor.execute('SELECT sp_pay_result, accountid, itemid, price, orderid, gameid, status ' 'FROM orderinfo WHERE orderid="%s";' % conn.escape_string(orderid)) row = cursor.fetchone() if (not row) or (row[0] != 1): return True info(row[4]) if row[6] == 0: timestamp = time.time() params = { 'account_id' : row[1], 'orderid' : row[4], 'itemid' : row[2], 'itemnum': 1, 'amount' : row[3], } secret = 'fc38349c5d084e920925e614c420be9f' md5signstr = md5Sign(params, secret, timestamp) url = notifyapi_json[str(row[5])]['notify_url'] + '×tamp=' + str(timestamp) + '&sign=' + md5signstr + \ '&' + urlencodeParams(params) req = urllib.request.Request(url) data = urllib.request.urlopen(req).read() info(str(data)) jsonobj = json.loads(data) if jsonobj['errcode'] != 0: return False confirmOneOrder(orderid) info('sendNotify end') return True except Exception as e: print('sendOneOrder error:' + e) traceback.print_exc(file=sys.stdout) return False def sendNotify(conf, sendtime): try: if len(confirmed_order_list) <= 0: fetchConfirmedOrderList(); handled_cout = 0 while len(confirmed_order_list) > 0 and handled_cout < 50: confirmed_order = confirmed_order_list[0] if sendOneOrder(conf, confirmed_order['idx'], confirmed_order['orderid']): confirmed_order_list.pop(0) handled_cout += 1 else: break except Exception as e: info('sendNotify error: ' + str(e)) traceback.print_exc(file=sys.stdout) #进入下一次循环 if len(confirmed_order_list) > 0: tornado.ioloop.IOLoop.current().call_at(time.time() + 1, lambda : sendNotify(conf, sendtime)) else: tornado.ioloop.IOLoop.current().call_at(time.time() + sendtime, lambda : sendNotify(conf, sendtime)) class SelfCheckingHandler(tornado.web.RequestHandler): def get(self): self.write(json.dumps({ 'errcode': 0, 'errmsg': '', 'healthy': 1, 'max_rundelay': 10 }, separators=(',', ':'))) def make_app(): return tornado.web.Application([ (r"/webapp/index[\.]php", SelfCheckingHandler), ]) if __name__ == "__main__": info('start!') conf = json.loads(open('../config/pay_backend.json', 'r').read()) app = make_app() app.listen(conf['listen_port']) tornado.ioloop.IOLoop.current().call_at(time.time(), lambda : sendNotify(conf, conf['notifytime'])) tornado.ioloop.IOLoop.current().start()