├── 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 |
--------------------------------------------------------------------------------