├── GetOrderList.py ├── GetQRcode.py ├── README.md ├── config.py ├── log └── log.log ├── record ├── record_123456002.txt └── record_123456003.txt ├── url123456002.txt └── url123456003.txt /GetOrderList.py: -------------------------------------------------------------------------------- 1 | # coding:utf8 2 | import uiautomator2 as u2 3 | import time 4 | import requests 5 | import re 6 | import hashlib 7 | import threading 8 | import logging 9 | import datetime 10 | import pymysql 11 | from config import phonelist as P 12 | from config import db 13 | 14 | """ 15 | 监控支付宝到账通知,获取订单信息,记录并发送回调给服务器 16 | """ 17 | 18 | class Phone(object): 19 | 20 | mysql = '' # Mysql连接 21 | cur = '' # 游标 22 | 23 | def __init__(self): 24 | self.accountinfo = account 25 | self.orderlast = [{'billAmount': '', 'billName': '', 'timeInfo': ''}] 26 | self.orderlast[0]['billAmount'], self.orderlast[0]['billName'], self.orderlast[0]['timeInfo'] = self.sql_search(account) 27 | print(self.orderlast) 28 | self.ordernew = [] 29 | self.pages = 0 # 记录滑动次数 30 | self.ct = u2.connect(ip) 31 | 32 | # 获取界面信息,匹配订单数据,筛选,返回新订单 33 | def findbill(self): 34 | time.sleep(0.2) 35 | info('Try get data') 36 | # 找到当前屏幕内所有的显示完整订单 37 | billlist = self.ct.dump_hierarchy() 38 | pattern = re.compile('(\\d{18,20})[\\s\\S]*?([+]\\d{1,5}\\.\\d\\d)[\\s\\S]*?(\\d\\d:\\d\\d)') #匹配订单备注,订单金额,交易时间 39 | bills = pattern.findall(billlist) 40 | if not bills: 41 | warning('No new order , try open app again') # 没有读取到订单 重新打开支付宝进入账单界面 42 | self.openapp(0) 43 | return 1 44 | 45 | for bill in bills: 46 | billname, billamount, timeinfo = bill[0], bill[1], bill[2].replace(':', '')+'00' 47 | billdata = {'billAmount': billamount[1:], 'billName': billname, 48 | 'timeInfo': '%s' % timeinfo} # 订单格式 49 | # input(billdata) 50 | if billdata in self.ordernew: # 如果已在本次订单列表中,即滑屏导致的重复读取,则跳过该笔订单 51 | continue 52 | elif billdata in self.orderlast: # 如果已在上次发送的订单列表中,则结束读取 53 | return 0 # 返回0,告知读取完毕 54 | else: 55 | # input('否则继续查找') 56 | self.ordernew.append(billdata) # 否则添加到新订单列表,继续查找 57 | info('all bills are new, continue find') 58 | self.pages += 1 59 | self.ct.swipe(0.5, 0.85, 0.5, 0.2, 0.5) # 上滑查看更多订单 60 | return 1 # 返回1,继续获取 61 | 62 | def get(self): 63 | info('start get new order') 64 | flag = self.findbill() # 先读取一次 65 | while flag: # 如果全屏都是新订单,就重复执行 66 | flag = self.findbill() 67 | info('Get complete') 68 | print('New order: ', self.ordernew) 69 | if self.ordernew: 70 | with open('record\\record_%s.txt' % account, 'a') as f: # 储存在本地 71 | f.write(str(self.ordernew)+'\n') 72 | self.orderlast = self.ordernew # 记录本次的获取订单列表 73 | return self.ordernew 74 | 75 | def openapp(self, restart): 76 | info('Open alipay app') 77 | self.ct.app_stop('com.eg.android.AlipayGphone') 78 | self.ct.app_start('com.eg.android.AlipayGphone') 79 | self.ct(text='我的').click_exists(5) 80 | self.ct(text='账单').click_exists(5) 81 | if restart == 1: 82 | return 1 83 | 84 | def run(self): 85 | info('Start Watching') 86 | print(self.accountinfo, 'is running') 87 | while 1: 88 | start = 0 89 | restart = 0 90 | time.sleep(1) 91 | if self.ct.info['currentPackageName'] != 'com.eg.android.AlipayGphone': 92 | restart = self.openapp(1) 93 | if self.ct(text='支付宝通知').exists: 94 | start = 1 95 | if start or restart == 1: 96 | info('Alipay information,ready to get bills') 97 | print('GET F5') 98 | ctime = datetime.datetime.now().strftime('%Y%m%d%H%M%S') # 当前时间 99 | self.ct.swipe(0.5, 0.3, 0.5, 0.6, 0.03) # 下拉刷新账单 100 | self.ordernew = [] # 新订单表清零 101 | 102 | # 获取订单信息 103 | st_time = time.time() 104 | order = self.get() 105 | use_time = time.time()-st_time 106 | info('Delay time:%s' % use_time) 107 | # print(order) 108 | if not order: 109 | print('No new order') 110 | warning('No new order!!!') 111 | continue 112 | # 发送订单线程 113 | info('Create new thread to send data') 114 | print('RETURN TOP') 115 | 116 | threading.Thread(target=self.send_url, args=(order, ctime)).start() 117 | 118 | # 返回订单顶部 119 | for swipe_times in range(self.pages): 120 | self.ct.swipe(0.5, 0.2, 0.5, 0.85, 0.3) 121 | self.pages = 0 122 | time.sleep(1) 123 | 124 | @classmethod 125 | def send_url(cls, order, ctime): # 发送回调 126 | info('Start send order data') 127 | print('SEND BEGIN') 128 | cls.host_state = 1 129 | # 测试接口连通性 130 | try: 131 | print('Test remote host connection') 132 | requests.post('http://112.74.40.81:11150/cgi-bin/v2.0/api_ali_pay_pqrcode_notify.cgi', data='123') 133 | print('Success, continue') 134 | except Exception as e: 135 | print('Send failed, save state') 136 | warning('........Failed......... , Error:%s, write 0 to db' % e) # 尝试连接失败,订单不发送,先写0入库 137 | cls.host_state = 0 138 | # 生成订单信息 139 | key = '123456' 140 | for oneorder in order: # 如存在多笔订单,则分别发送 141 | nums = len(order)-order.index(oneorder) # 笔数 142 | order_id = ctime + '%02d' % nums # 入库时订单id 143 | if cls.host_state == 0: 144 | info('for 0 , save') 145 | cls.sql_insert(ctime, oneorder, order_id, status='0') 146 | continue 147 | else: 148 | msg = 'billAmount=%s&billName=%s&timeInfo=%s' % \ 149 | (oneorder['billAmount'], oneorder['billName'], ctime[:8] + oneorder['timeInfo']) 150 | # info('Original str: %s' % msg) # 原始串 151 | msg_key = msg + '&key=' + key 152 | md5 = hashlib.md5() 153 | md5.update(msg_key.encode(encoding='utf-8')) 154 | sign = md5.hexdigest() 155 | msg_send = msg + '&sign=' + sign # 签名串 156 | # input(msg_send) 157 | info('Signed str: %s' % msg_send) 158 | response = requests.post('http://112.74.40.81:11150/cgi-bin/v2.0/api_ali_pay_pqrcode_notify.cgi', 159 | data=msg_send) 160 | recode = response.text 161 | print('Send success, Reply: %s' % recode) 162 | info('Send success, Reply: %s' % recode + ' write to db') 163 | if recode == 'SUCCESS': 164 | cls.sql_insert(ctime, oneorder, order_id, status='1') 165 | else: 166 | cls.sql_insert(ctime, oneorder, order_id, status='2') 167 | info('Save complete') 168 | continue 169 | info('ALL send over') 170 | print('SEND OVER') 171 | 172 | @classmethod 173 | def sql_insert(cls, ctime, oneorder, order_id, status): # 订单入库 174 | info('connect mysql and insert into table') 175 | cls.sql_conn() 176 | acountinfo, billname, billamount, timeinfo = \ 177 | oneorder['billName'][:9], oneorder['billName'], oneorder['billAmount'], oneorder['timeInfo'] 178 | sql = "INSERT INTO `orders`.`new_table` (`acountinfo`, `billname`, `billamount`, `timeinfo`, " \ 179 | "`ctime`, `status`,`order_id`) VALUES (%s,%s,%s,%s,%s,%s,%s);" % (acountinfo, billname, billamount, timeinfo, ctime, status, order_id) 180 | # input(sql) 181 | cls.cur.execute(sql) 182 | cls.mysql.commit() 183 | info('over , close connect') 184 | cls.sql_close() 185 | 186 | @classmethod 187 | def sql_search(cls, acountinfo): # 查找最近的一笔订单记录 188 | info('connect mysql and search the last order') 189 | cls.sql_conn() 190 | sql = "SELECT billamount,billname,timeinfo FROM `orders`.`new_table` WHERE acountinfo=%s ORDER BY order_id DESC limit 0,5" % acountinfo 191 | # input(sql) 192 | cls.cur.execute(sql) 193 | order_data = cls.cur.fetchmany(1)[0] # fetch返回元组,只要最新的一笔 194 | # input(order_data) 195 | info('over , close connect and return order') 196 | cls.sql_close() 197 | return order_data 198 | 199 | @classmethod 200 | def sql_conn(cls): # 连接数据库 201 | cls.mysql = pymysql.connect(host=db['host'], port=db['port'], user=db['user'], passwd=db['passwd'], db=db['dbname']) 202 | cls.cur = cls.mysql.cursor() 203 | 204 | @classmethod 205 | def sql_close(cls): # 关闭数据库连接 206 | cls.cur.close() 207 | cls.mysql.close() 208 | 209 | 210 | if __name__ == '__main__': 211 | print('\n----------地址配置如下----------') 212 | for i, p in zip(range(10), P): 213 | print(i, '--', p) 214 | while 1: 215 | ip, account = P[0]['ip'], P[0]['user']+P[0]['account'] # 默认使用配置0 216 | Input = input('请输入编号选择配置: ') 217 | if Input == '': 218 | print('未选择,使用默认配置0') 219 | elif Input in '0123456789': 220 | ip, account = P[int(Input)]['ip'], P[int(Input)]['user']+P[int(Input)]['account'] 221 | else: 222 | print('输入有误,请重新选择') 223 | continue 224 | GG = input('ip: %s account: %s 输入GG继续,否则请重新选择\n' % (ip, account)) 225 | if GG == 'GG': 226 | break 227 | continue 228 | 229 | logging.basicConfig(level=logging.INFO, 230 | format='%(asctime)s %(process)d %(threadName)s %(funcName)s %(levelname)s %(message)s ', 231 | datefmt='%Y-%m-%d %H:%M:%S', 232 | filename='log\log_%s.log' % account, 233 | filemode='a') 234 | 235 | info = logging.info 236 | warning = logging.warning 237 | 238 | phone = Phone() 239 | phone.run() 240 | 241 | 242 | 243 | -------------------------------------------------------------------------------- /GetQRcode.py: -------------------------------------------------------------------------------- 1 | #coding:utf8 2 | import uiautomator2 as u2 3 | import time 4 | from config import phonelist 5 | import threading 6 | 7 | d = [u2.connect(i['ip']) for i in phonelist] # 手机列表 8 | 9 | 10 | def getqr(d, user, account, qrmoney, qrnum): 11 | print('账户%s正在生成二维码' % account, end='') 12 | print(" %s%s%s%s" % (user, account, qrmoney, qrnum)) 13 | d.app_start('com.eg.android.AlipayGphone') 14 | d(text='设置金额').click() 15 | time.sleep(0.2) 16 | d.set_fastinput_ime(True) # 切换成FastInputIME输入法 17 | d.send_keys(str(float(qrmoney)/100)) # adb广播输入 18 | time.sleep(0.2) 19 | d(text='添加收款理由').click() 20 | time.sleep(0.2) 21 | d.send_keys("%s%s%s%s" % (user, account, qrmoney, qrnum)) # adb广播输入 22 | time.sleep(0.2) 23 | d(text='确定').click() 24 | time.sleep(0.2) 25 | d(text='保存图片').click() 26 | time.sleep(0.5) 27 | d(text='清除金额').click() 28 | time.sleep(0.2) 29 | 30 | print('账户%s正在扫描二维码' % account) 31 | d.app_start('mark.qrcode') 32 | time.sleep(0.5) 33 | d(text='从图库扫描…').click() 34 | 35 | if account == '001': # 三星 36 | time.sleep(0.2) 37 | d.tap(0.26, 0.32) 38 | elif account == '003': # 小米 39 | time.sleep(0.5) 40 | d.tap(0.282, 0.814) 41 | time.sleep(0.2) 42 | d.tap(0.125, 0.3) 43 | elif account == '002': # 中兴 44 | time.sleep(0.5) 45 | d.tap(0.26, 0.3) 46 | time.sleep(0.5) 47 | print('账户%s正在解析二维码' % account) 48 | url = d(resourceId="mark.qrcode:id/p").get_text() 49 | time.sleep(0.2) 50 | d.press('back') 51 | return user+account+qrmoney+qrnum+' '+url 52 | 53 | 54 | def creatbill(i, user, account): 55 | """ 56 | :param i:手机编码 57 | :param user:商户号 58 | :param account:支付宝账户编号 59 | :return: none 60 | """ 61 | print(threading.currentThread()) 62 | t0 = time.time() 63 | for qrmoney in [x for x in range(10, 21) if x % 10 == 0]: # 金额,单位分,七位数 64 | qrmoney = "%07d" % qrmoney 65 | for qrnum in range(1, 3): # 某金额二维码编号,两位数 66 | qrnum = "%02d" % qrnum 67 | qrcode=getqr(d[i], user, account, qrmoney, qrnum) # 生成一个二维码 68 | print('账户%s正在保存二维码' % account) 69 | with open('url%s.txt' % (user+account), 'a') as f: 70 | f.write(qrcode+'\n') 71 | print(time.time() - t0) 72 | 73 | 74 | for i in range(1, 3): # 三台手机 75 | user, account = phonelist[i]['user'], phonelist[i]['account'] 76 | threading.Thread(target=creatbill, args=(i, user, account)).start() 77 | print(threading.enumerate()) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AlipayQRcode 2 | 个人收款码支付方案 3 | 4 | 5 | 预先自动生成个人收款二位码 相同金额每种生产一定的数量 根据交易量来决定
6 | 备注为: 商户号-商户支付宝编号-金额-金额编号
7 | 把图片扫描为地址,入库
8 | 9 | 本地: 监听支付宝到账通知 获取订单信息 发送给服务端
10 | 服务器: 接受支付申请,生成内部订单,返回支付地址,接收本地通知,返回支付状态给商户
11 | 商户:发起订单,接受回调,查询订单
12 | 13 | to do:
14 | Server端
15 | 收单接口:生成内部订单号,返回支付连接,打开时判断是否过期或者已支付,否则跳转,唤醒支付宝付款
16 | 1外部订单号 2创建时间 3金额 4支付宝账户 5异步通知地址 6支付成功跳转地址
17 | 查单接口: 1234 5:null or 支付时间
18 | 接收本地通知接口:签名校验 订单判断 更新订单状态 19 | -------------------------------------------------------------------------------- /config.py: -------------------------------------------------------------------------------- 1 | 2 | phonelist = [{'ip':'192.168.31.97', 'user':'123456', 'account':'002'}, 3 | {'ip':'192.168.31.72', 'user':'123456', 'account':'003'}, 4 | {'ip':'192.168.31.97', 'user':'123456', 'account':'003'}, 5 | {'ip':'192.168.137.1', 'user':'123456', 'account':'004'}, 6 | {'ip':'192.168.137.1', 'user':'123456', 'account':'005'}, 7 | {'ip':'192.168.137.1', 'user':'123456', 'account':'006'}, 8 | {'ip':'192.168.137.1', 'user':'123456', 'account':'007'}, 9 | {'ip':'192.168.137.1', 'user':'123456', 'account':'008'}, 10 | {'ip':'192.168.137.1', 'user':'123456', 'account':'009'}, 11 | {'ip':'192.168.137.1', 'user':'123456', 'account':'010'}] 12 | 13 | 14 | db = dict(host='192.168.1.1', port=3306, user='admin', passwd='123456', dbname='orders') 15 | 16 | -------------------------------------------------------------------------------- /log/log.log: -------------------------------------------------------------------------------- 1 | log -------------------------------------------------------------------------------- /record/record_123456002.txt: -------------------------------------------------------------------------------- 1 | record -------------------------------------------------------------------------------- /record/record_123456003.txt: -------------------------------------------------------------------------------- 1 | record -------------------------------------------------------------------------------- /url123456002.txt: -------------------------------------------------------------------------------- 1 | 123456002000001001 HTTPS://QR.ALIPAY.COM/FKX01849104DKY8B1YNKD5 2 | 123456002000001002 HTTPS://QR.ALIPAY.COM/FKX01048QSZ7SFUSS2OO90 3 | 123456002000001003 HTTPS://QR.ALIPAY.COM/FKX038772VTXQSD86EYTC3 4 | 123456002000001004 HTTPS://QR.ALIPAY.COM/FKX04582RYK7YYDNOHBV3D 5 | 123456002000001005 HTTPS://QR.ALIPAY.COM/FKX03328SKY0CC8TWHUY98 6 | 123456002000002001 HTTPS://QR.ALIPAY.COM/FKX00768WBGFUT2QOYLF25 7 | 123456002000002002 HTTPS://QR.ALIPAY.COM/FKX08297CWIXHSI18NMH08 8 | 123456002000002003 HTTPS://QR.ALIPAY.COM/FKX090848IJGHZRX3GQX90 9 | 123456002000002004 HTTPS://QR.ALIPAY.COM/FKX08195VOS8YUFVVHNXEB 10 | 123456002000002005 HTTPS://QR.ALIPAY.COM/FKX04502R3H7K49YBPOGE6 11 | -------------------------------------------------------------------------------- /url123456003.txt: -------------------------------------------------------------------------------- 1 | 123456003000001001 HTTPS://QR.ALIPAY.COM/FKX09509BYU5B2ZISQDZ99 2 | 123456003000001002 HTTPS://QR.ALIPAY.COM/FKX03727SR0WI7J0I1WRC2 3 | 123456003000001003 HTTPS://QR.ALIPAY.COM/FKX01340KJT1BHEBG1HNE9 4 | 123456003000001004 HTTPS://QR.ALIPAY.COM/FKX00189D7IGEVQOQLQP16 5 | 123456003000001005 HTTPS://QR.ALIPAY.COM/FKX04897M4G9IUZSW8FH06 6 | 123456003000002001 HTTPS://QR.ALIPAY.COM/FKX00766PEIBMI1TVT8IAB 7 | 123456003000002002 HTTPS://QR.ALIPAY.COM/FKX02648DVBQGNORV1O5C7 8 | 123456003000002003 HTTPS://QR.ALIPAY.COM/FKX08672YHR6CTZPZI6N25 9 | 123456003000002004 HTTPS://QR.ALIPAY.COM/FKX000410ZRUKFM5CYKGD3 10 | 123456003000002005 HTTPS://QR.ALIPAY.COM/FKX091817ZTRD1QGTIKBA0 11 | --------------------------------------------------------------------------------