OfpayHelper.py 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457
  1. # -*- coding: utf-8 -*-
  2. import os
  3. import re
  4. import sys
  5. import time
  6. import pymysql
  7. import logging
  8. # import jwt
  9. import json
  10. from datetime import datetime, timezone, timedelta
  11. from mitmproxy import flowfilter
  12. from mitmproxy import http
  13. from mitmproxy import ctx
  14. # from http.cookies import SimpleCookie
  15. sys.path.append('../')
  16. import utils.Utils as Utils
  17. sys.path.pop()
  18. """
  19. #http.HTTPFlow 实例 flow
  20. flow.request.http_version #HTTP 版本
  21. flow.request.headers #获取所有头信息,包含Host、User-Agent、Content-type等字段
  22. flow.request.cookies #cookie头
  23. flow.request.url #完整的请求地址,包含域名及请求参数,但是不包含放在body里面的请求参数
  24. flow.request.pretty_url #同flow.request.url目前没看出什么差别
  25. flow.request.host #域名
  26. flow.request.port #请求的目标端口
  27. flow.request.method #请求方式。POST、GET等
  28. flow.request.scheme #什么请求 ,如https
  29. flow.request.path # 请求的路径,url除域名之外的内容
  30. flow.request.get_text() #请求中body内容,有一些http会把请求参数放在body里面,那么可通过此方法获取,返回字典类型
  31. flow.request.replace() # 使用正则替换content中的内容
  32. flow.request.query #返回MultiDictView类型的数据,url直接带的键值参数
  33. flow.request.get_content()#bytes,结果如flow.request.get_text()
  34. flow.request.raw_content #bytes,结果如flow.request.get_content()
  35. flow.request.urlencoded_form #MultiDictView,content-type:application/x-www-form-urlencoded时的请求参数,不包含url直接带的键值参数
  36. flow.request.multipart_form #MultiDictView,content-type:multipart/form-data
  37. flow.request.timestamp_start #请求开始的时间戳
  38. flow.request.timestamp_end #请求结束的时间戳
  39. 时的请求参数,不包含url直接带的键值参数
  40. #以上均为获取request信息的一些常用方法,对于response,同理
  41. flow.response.status_code #状态码
  42. flow.response.headers #获取所有头信息
  43. flow.response.cookies #cookie头
  44. flow.response.text#返回内容,已解码
  45. flow.response.content #返回内容,二进制
  46. flow.response.set_text() #修改返回内容,不需要转码
  47. flow.response.replace() # 使用正则替换content中的内容
  48. flow.response.timestamp_start #响应开始的时间戳
  49. flow.response.timestamp_end #响应结束的时间戳
  50. """
  51. def seconds_to_beijing_time(seconds):
  52. utc_time = datetime.fromtimestamp(seconds, tz=timezone.utc);
  53. beijing_time = utc_time.astimezone(timezone(timedelta(hours=8)));
  54. formatted_time = beijing_time.strftime('%Y-%m-%d %H:%M:%S');
  55. return formatted_time;
  56. class OfpayHelper:
  57. order_simple_data = {
  58. "awardId": "W1155090378949787660",
  59. "activityId": "A923605206137307136",
  60. "activityName": "采集",
  61. "activityState": "2",
  62. "activityStartTime": "2022-01-01 00:00",
  63. "activityEndTime": "2888-12-31 23:59",
  64. "businessType": "4005",
  65. "outActivityCode": "eCoffee",
  66. "mobile": "",
  67. "prizeId": "sku14117",
  68. "prizeName": "数据采集成功",
  69. "prizeAlias": "",
  70. "prizeDesc": "",
  71. "prizeDescUrl": "https://mstatic.ofpay.com/marketing/upload/ca2ed3a05b2846b7909debf2df8e3495.png",
  72. "prizeBannerUrl": "https://mstatic.ofpay.com/marketing/upload/c4d1a0b94b50462eb0f040306a9badf4.png",
  73. "categoryId": "1",
  74. "rechargeType": "09",
  75. "goodsScene": "0",
  76. "goodsList": [],
  77. "orderNum": 1,
  78. "createTime": "",
  79. "imgUrl": "https://mstatic.ofpay.com/marketing/upload/fc0cc0a86db64f638d4e193913d7efea.png",
  80. "orderStatus": "3",
  81. "detailId": "T123456789",
  82. "clientAccount": "13430389115",
  83. "redeemCode": "",
  84. "redeemCodeStatus": "",
  85. "dynamicCodeSign": "1",
  86. "startEffectTime": "",
  87. "endEffectTime": "",
  88. "toExpireFlag": "0",
  89. "faceVal": "",
  90. "orderId": "T240311090006428",
  91. "tenantId": "0000000191",
  92. "price": "30",
  93. "awardPrice": "20.8",
  94. "salePrice": "20.8",
  95. "rechargeId": "R1216679598197055488",
  96. "rechargeTime": "2024-01-01 00:00:00",
  97. "payStatus": "2",
  98. "discountPrice": "",
  99. "activityPrice": "",
  100. "customerInfo": "{\"device_id\":\"D29ED082-549A-4882-98FC-8BB881D1552B\",\"loginType\":\"interactiveIGoChoose\",\"gameAccount\":\"13430389115\",\"city_code\":\"440100\",\"cisno\":\"ZbHv0CEM2cGjx0DB9DXVJg==\",\"isNewUser\":\"0\",\"marketId\":\"M923156289016692736\",\"city_name\":\"广州市\",\"phone\":\"13430389115\",\"fromEntry\":\"APP\",\"currentTimeMillis\":\"1710119786257\",\"userUuid\":\"Pfd6kjTSmjCfQ8boswe1PpAmfgZW0acz\",\"cust_id\":\"Pfd6kjTSmjCfQ8boswe1PpAmfgZW0acz\",\"invitationCode\":\"BGCKWC\"}",
  101. "callbackOrder": "",
  102. "activityRechargeEffectStartTime": "",
  103. "activityRechargeEffectEndTime": "",
  104. "accountType": "",
  105. "payFlag": "1",
  106. "activityPayFlag": True,
  107. "thirdInfo": "{\"faceValue\":\"30.00\",\"customGatewayId\":\"ZDY_ICBC_ZJWN\",\"showSign\":\"1\",\"xcxShowSign\":\"2\",\"order\":\"28\",\"toBPrice\":\"30.00\",\"showPhone\":\"1\",\"pointActivity\":\"HD0460132E7oLMG1mH\",\"stockShowSign\":\"2\"}",
  108. "vendorVoucher": "",
  109. "productUseMsg": "",
  110. "proof": "",
  111. "amount": 1,
  112. "parentActivityNo": "",
  113. "parentDetailId": "",
  114. "subOrderExt": "{\"orderStatus\":\"\",\"payStatus\":\"\"}",
  115. "logisticsNo": "",
  116. "company": "",
  117. "promoteId": "",
  118. "version": 1,
  119. "gateWayId": "",
  120. "payType": "",
  121. "needRechargeNum": "0"
  122. }
  123. def __init__(self):
  124. self.domain_name = 'market-web.ofpay.com';
  125. self.host_ip = None;
  126. ip_address = Utils.get_ip_address(self.domain_name);
  127. if ip_address:
  128. self.host_ip = ip_address;
  129. self.db_conn = None;
  130. self.connect_mysql();
  131. def connect_mysql(self):
  132. config = {
  133. 'host':'47.106.225.136',
  134. 'port':3306,
  135. 'user':'root',
  136. 'passwd':'sjojo123456',
  137. 'database':'mitmproxy',
  138. 'charset':'utf8'
  139. };
  140. db_conn = None;
  141. while True:
  142. try:
  143. db_conn = pymysql.connect(**config);
  144. db_conn.ping(reconnect=True);
  145. except pymysql.OperationalError as e:
  146. print(e);
  147. print('连接断开,正在尝试重新连接...');
  148. if db_conn:
  149. db_conn.close();
  150. db_conn = pymysql.connect(**config);
  151. time.sleep(1);
  152. else:
  153. break;
  154. self.db_conn = db_conn;
  155. def check_mysql_connect(self):
  156. try:
  157. with self.db_conn.cursor() as cursor:
  158. cursor.execute('SELECT 1');
  159. except pymysql.MySQLError as e:
  160. print(e);
  161. self.db_conn.close();
  162. print('mysql重连...');
  163. self.connect_mysql();
  164. def check_host_pass(self, host):
  165. if self.host_ip:
  166. if host != self.host_ip and host != self.domain_name:
  167. return False;
  168. else:
  169. if host != self.domain_name:
  170. return False;
  171. return True;
  172. def request(self, flow: http.HTTPFlow):
  173. if not self.check_host_pass(flow.request.host):
  174. return;
  175. url = flow.request.url;
  176. path = flow.request.path;
  177. request = flow.request;
  178. def response(self, flow: http.HTTPFlow):
  179. if not self.check_host_pass(flow.request.host):
  180. return;
  181. url = flow.request.url;
  182. path = flow.request.path;
  183. print("###[OfpayHelper]path=%s"%path);
  184. if path.startswith('/h5/union/interactiveIGoChoose/index'):
  185. self.handle_login(flow);
  186. elif path.startswith('/h5/union/api/interactiveIGoChoose/indexConfigRebuild'):
  187. self.handle_activitylist(flow);
  188. elif path.startswith('/h5/union/api/interactiveIGoChoose/orderList'):
  189. self.handle_orderlist(flow);
  190. def get_jwt_token_data(self, flow: http.HTTPFlow):
  191. request = flow.request;
  192. headers = dict(request.headers);
  193. jwt_data = None;
  194. try:
  195. jwt_str = None;
  196. if 'Authorization' in headers:
  197. jwt_str = headers['Authorization'];
  198. else:
  199. cookies = dict(request.cookies);
  200. if 'unionToken_interactiveIGoChoose' in cookies:
  201. jwt_str = cookies['unionToken_interactiveIGoChoose'];
  202. if jwt_str:
  203. jwt_data = Utils.parse_jwt(jwt_str);
  204. except jwt.PyJWTError as e:
  205. print('jwt token解析失败');
  206. else:
  207. pass
  208. finally:
  209. pass
  210. if jwt_data:
  211. payload = jwt_data['payload'];
  212. if 'customerInfo' in payload:
  213. info_str = payload['customerInfo'];
  214. customer_info = json.loads(info_str);
  215. payload['customerInfo'] = customer_info;
  216. return jwt_data;
  217. def handle_login(self, flow: http.HTTPFlow):
  218. ctx.log.info('###handle_login###');
  219. request = flow.request;
  220. response = flow.response;
  221. if not response.cookies:
  222. return;
  223. jwt_data = self.get_jwt_token_data(flow);
  224. if not jwt_data:
  225. return;
  226. payload = jwt_data['payload'];
  227. account = None;
  228. if 'customerInfo' not in payload:
  229. account = payload['customerInfo']['phone'];
  230. login_params = flow.request.query.get('loginParams');
  231. cookies = dict(request.cookies);
  232. for name, morsel in response.cookies.items():
  233. value = morsel[0];
  234. cookies[name] = value;
  235. # # 获取所有的Set-Cookie头部
  236. # set_cookie_headers = flow.response.headers.get_all("Set-Cookie")
  237. # for cookie_header in set_cookie_headers:
  238. # cookie = SimpleCookie();
  239. # cookie.load(cookie_header);
  240. # # SimpleCookie对象可以像字典一样工作
  241. # for key, morsel in cookie.items():
  242. # # 这里可以添加进一步的逻辑来处理cookie的键和值
  243. # # 例如,可以检查cookie的过期时间,路径等属性
  244. # print("Attributes:", morsel);
  245. authorization = cookies['unionToken_interactiveIGoChoose'];
  246. sign_time = None;
  247. expire_time = None;
  248. try:
  249. jwt_data = Utils.parse_jwt(authorization);
  250. if not jwt_data:
  251. return;
  252. payload = jwt_data['payload'];
  253. if 'customerInfo' in payload:
  254. info_str = payload['customerInfo'];
  255. customer_info = json.loads(info_str);
  256. payload['customerInfo'] = customer_info;
  257. account = customer_info['phone'];
  258. sign_time = seconds_to_beijing_time(payload['iat']);
  259. expire_time = seconds_to_beijing_time(payload['exp']);
  260. except Exception as e:
  261. print(e);
  262. if not account:
  263. return;
  264. try:
  265. sql_query = f'''
  266. UPDATE elife_account_data
  267. SET
  268. authorization = %s,
  269. cookies = %s,
  270. update_time = %s,
  271. expire_time = %s,
  272. login_params = %s
  273. WHERE account = %s;
  274. ''';
  275. sql_params = (authorization, repr(cookies), sign_time, expire_time, login_params, account);
  276. print(sql_params);
  277. self.check_mysql_connect();
  278. cursor = self.db_conn.cursor();
  279. cursor.execute(sql_query, sql_params);
  280. self.db_conn.commit();
  281. cursor.close();
  282. except pymysql.OperationalError as e:
  283. print(e);
  284. def handle_activitylist(self, flow: http.HTTPFlow):
  285. ctx.log.info('###handle_activitylist###');
  286. request = flow.request;
  287. response = flow.response;
  288. jwt_data = self.get_jwt_token_data(flow);
  289. if not jwt_data:
  290. return;
  291. payload = jwt_data['payload'];
  292. if 'customerInfo' not in payload:
  293. return;
  294. account = payload['customerInfo']['phone'];
  295. if not account:
  296. return;
  297. if account != '13430389115':
  298. return;
  299. rsp_params = json.loads(response.get_text());
  300. if rsp_params['code'] != 'success':
  301. return;
  302. rsp_data = rsp_params['data'];
  303. sql_activity_query = f'''
  304. CALL UpdateElifeActivities(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s);
  305. ''';
  306. sql_activity_params = [];
  307. sql_award_query = f'''
  308. CALL UpdateElifeActivityAwards(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s);
  309. ''';
  310. sql_award_params = [];
  311. activity_list = rsp_params['data'];
  312. activity_sort_num = 0;
  313. for activity_info in activity_list:
  314. sql_activity_params.append(('1',
  315. activity_info['activityId'], activity_info['activityAlias'], activity_info['outActivityCode'],
  316. activity_info['activityTitle'], activity_info['activityState'], activity_info['activityIcon'],
  317. activity_info['activityLink'], activity_info['activityBanner'], activity_info['subLoginType'],
  318. activity_info['subActivityId'] if 'subActivityId' in activity_info else '', activity_info['activityDesc'], activity_sort_num));
  319. activity_sort_num += 1;
  320. award_list = activity_info['awardList'];
  321. for award_info in award_list:
  322. # print(award_info)
  323. sql_award_params.append((
  324. award_info['awardId'], award_info['prizeName'], award_info['activityId'],
  325. award_info['prizeId'], award_info['prizeDesc'], award_info['prizeBannerUrl'],
  326. award_info['prizeDescUrl'], award_info['imgUrl'], int(award_info['stockNum']),
  327. float(award_info['price']), award_info['categoryType'], award_info['goodsScene'],
  328. award_info['rechargeType'], int(award_info['useStock']), int(award_info['remainStock']),
  329. int(award_info['cycleStock']), int(award_info['cycleRemainStock']), int(award_info['orderNum']),
  330. award_info['payFlag'], award_info['thirdInfo'], award_info['awardType'],
  331. int(award_info['firstSignAward']), int(award_info['renewSignAward']), award_info['prizeAlias'] if 'prizeAlias' in award_info else '',
  332. award_info['parentAwardId'] if 'parentAwardId' in award_info else ''));
  333. try:
  334. self.check_mysql_connect();
  335. cursor = self.db_conn.cursor();
  336. cursor.executemany(sql_activity_query, sql_activity_params);
  337. cursor.executemany(sql_award_query, sql_award_params);
  338. self.db_conn.commit();
  339. cursor.close();
  340. except pymysql.OperationalError as e:
  341. print(e);
  342. def handle_orderlist(self, flow: http.HTTPFlow):
  343. ctx.log.info('###handle_orderlist###');
  344. request = flow.request;
  345. response = flow.response;
  346. cookies = dict(request.cookies); # 转换cookies格式为dict
  347. # if 'unionToken_interactiveIGoChoose' not in cookies:
  348. # return;
  349. # account = None;
  350. # try:
  351. # jwt_str = cookies['unionToken_interactiveIGoChoose'];
  352. # # payload = jwt.decode(jwt_str, '', algorithms=['HS256'], verify=False, options={'verify_signature':False});
  353. # # info_str = payload.get('customerInfo');
  354. # # 不依赖库,简单方法解析
  355. # jwt_data = Utils.parse_jwt(jwt_str);
  356. # if jwt_data:
  357. # payload = jwt_data['payload'];
  358. # info_str = payload['customerInfo'];
  359. # customer_info = json.loads(info_str);
  360. # account = customer_info['phone'];
  361. # except jwt.PyJWTError as e:
  362. # print('jwt token解析失败');
  363. # else:
  364. # pass
  365. # finally:
  366. # pass
  367. jwt_data = self.get_jwt_token_data(flow);
  368. if not jwt_data:
  369. return;
  370. payload = jwt_data['payload'];
  371. if 'customerInfo' not in payload:
  372. return;
  373. account = payload['customerInfo']['phone'];
  374. if not account:
  375. return;
  376. headers = dict(request.headers);
  377. uuid = headers['UUID'];
  378. authorization = headers['Authorization'];
  379. user_agent = headers['User-Agent'];
  380. market_id = request.query.get('marketId');
  381. event_visitor_id = request.query.get('eventVisitorId');
  382. clientAccount = account if account else '13400000000';
  383. # create_time = '2024-01-01 00:00:00';
  384. create_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S');
  385. capture_code = Utils.generate_random_code(6);
  386. simple_data = OfpayHelper.order_simple_data;
  387. simple_data['prizeDesc'] = capture_code;
  388. simple_data['createTime'] = create_time;
  389. simple_data['endEffectTime'] = create_time;
  390. # simple_data['rechargeTime'] = create_time;
  391. simple_data['clientAccount'] = clientAccount;
  392. rsp_params = json.loads(response.get_text());
  393. if rsp_params['code'] == 'success':
  394. rsp_data = rsp_params['data'];
  395. rsp_data['list'].insert(0, simple_data);
  396. update_time = create_time;
  397. sign_time = seconds_to_beijing_time(payload['iat']);
  398. expire_time = seconds_to_beijing_time(payload['exp']);
  399. simple_data['createTime'] = expire_time;
  400. simple_data['endEffectTime'] = expire_time;
  401. # simple_data['rechargeTime'] = expire_time;
  402. sql_query = f'''
  403. CALL UpdateElifeAccountData(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s);
  404. ''';
  405. sql_params = (account, uuid, authorization, repr(cookies), user_agent, market_id, event_visitor_id, capture_code , update_time, expire_time);
  406. try:
  407. self.check_mysql_connect();
  408. cursor = self.db_conn.cursor();
  409. cursor.execute(sql_query, sql_params);
  410. self.db_conn.commit();
  411. cursor.close();
  412. simple_data['prizeName'] = '数据采集成功';
  413. except pymysql.OperationalError as e:
  414. print(e);
  415. simple_data['prizeName'] = '数据采集失败';
  416. simple_data['prizeDesc'] = '';
  417. response.set_text(json.dumps(rsp_params));