ShopifyController.py 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507
  1. import re
  2. from datetime import datetime
  3. import concurrent.futures
  4. import pytz
  5. import requests
  6. from django.db.models import Q, F
  7. from django.views import View
  8. from Crypto.Cipher import AES
  9. from Crypto.Util.Padding import pad
  10. from django.contrib.auth.hashers import check_password, make_password
  11. import concurrent.futures
  12. from Controller.CheckUserData import DataValid
  13. from Model.models import Device_User, CountryModel, LanguageModel, CountryLanguageModel
  14. from Object.RedisObject import RedisObject
  15. from Object.ResponseObject import ResponseObject
  16. import base64
  17. import hmac
  18. import hashlib
  19. import os
  20. import json
  21. from Ansjer.config import SHOPIFY_CONFIG, CONFIG_INFO, CONFIG_EUR, CONFIG_US
  22. from Service.CommonService import CommonService
  23. class ShopifyMultipass:
  24. @staticmethod
  25. def generate_multipass_token(secret, customer_data):
  26. """
  27. 使用指定的密钥对加密并签名JSON数据,返回Base64编码的Multipass令牌
  28. """
  29. # 第一步:将客户数据转换为JSON格式
  30. json_data = json.dumps(customer_data)
  31. # 第二步:生成加密密钥和签名密钥
  32. hash_digest = hashlib.sha256(secret.encode()).digest()
  33. encryption_key = hash_digest[:16] # 128位加密密钥
  34. signature_key = hash_digest[16:32] # 128位签名密钥
  35. # 第三步:加密JSON数据
  36. iv = os.urandom(16) # 随机初始化向量
  37. cipher = AES.new(encryption_key, AES.MODE_CBC, iv)
  38. ciphertext = cipher.encrypt(pad(json_data.encode(), AES.block_size))
  39. # 第四步:签名加密数据
  40. data_to_sign = iv + ciphertext
  41. signature = hmac.new(signature_key, data_to_sign, hashlib.sha256).digest()
  42. # 第五步:Base64编码
  43. multipass_token = base64.urlsafe_b64encode(iv + ciphertext + signature).decode()
  44. return multipass_token
  45. @staticmethod
  46. def search_customer_by_email(store_name, access_token, email):
  47. # 设置请求URL
  48. url = f"https://{store_name}.myshopify.com/admin/api/2024-10/customers/search.json"
  49. params = {
  50. "query": f"email:{email}"
  51. }
  52. # 设置请求头
  53. headers = {
  54. "X-Shopify-Access-Token": access_token,
  55. }
  56. # 发送GET请求
  57. response = requests.get(url, headers=headers, params=params)
  58. # 返回响应的JSON数据
  59. return response.json()
  60. @staticmethod
  61. def get_timezone_by_country(iso2):
  62. timezone_map = {
  63. "us": "America/New_York", # 美国时区(例如:纽约)
  64. "jp": "Asia/Tokyo", # 日本时区
  65. "de": "Europe/Berlin", # 德国时区
  66. "uk": "Europe/London", # 英国时区
  67. "eu": "Europe/Paris", # 欧盟时区(默认设为法国巴黎)
  68. }
  69. # 获取当前时间并格式化时间戳
  70. now = datetime.now(pytz.timezone(timezone_map[iso2]))
  71. timestamp = now.strftime('%Y-%m-%dT%H:%M:%S%z')
  72. timestamp = timestamp[:-2] + ':' + timestamp[-2:]
  73. return timestamp
  74. class ShopifyView(View):
  75. def get(self, request, *args, **kwargs):
  76. request.encoding = 'utf-8'
  77. operation = kwargs.get('operation')
  78. request_dict = request.GET
  79. return self.validation(request, request_dict, operation)
  80. def post(self, request, *args, **kwargs):
  81. request.encoding = 'utf-8'
  82. operation = kwargs.get('operation')
  83. request_dict = request.POST
  84. return self.validation(request, request_dict, operation)
  85. def validation(self, request, request_dict, operation):
  86. language = request_dict.get('language', 'cn')
  87. response = ResponseObject(language, "pc")
  88. if operation == 'shopifyLogin':
  89. return self.shopify_login(request_dict, response)
  90. elif operation == 'shopifyRegister':
  91. return self.shopify_register(request_dict, response)
  92. # 查询FAPP注册账号情况
  93. elif operation == 'searchCustomer':
  94. return self.search_customer(request_dict, response)
  95. # 官网检测账号接口
  96. elif operation == 'searchAccount':
  97. return self.search_account(request_dict, response)
  98. elif operation == 'getCountryDomainList':
  99. return self.get_country_domain_list(request_dict, response)
  100. # 忘记密码
  101. elif operation == 'shopifyChangePassword':
  102. return self.shopify_change_password(request_dict, response)
  103. elif operation == 'verifyAuthcode':
  104. return self.verify_authcode(request_dict, response)
  105. else:
  106. return response.json(414)
  107. @staticmethod
  108. def shopify_login(request_dict, response):
  109. email = request_dict.get("email", None)
  110. password = request_dict.get("password", None)
  111. account_iso2 = request_dict.get("accountCountry", None)
  112. shopify_country = request_dict.get("shopifyCountry", "")
  113. if not all([email, password, account_iso2]):
  114. return response.json(444)
  115. user_qs = Device_User.objects.filter(Q(username=email) | Q(userEmail=email))
  116. if not user_qs.exists():
  117. return response.json(104)
  118. users = user_qs.values(
  119. 'role__rid', 'role__roleName', 'userID', 'NickName',
  120. 'username', 'userEmail', 'phone', 'password', 'userIconPath'
  121. )[0]
  122. if not check_password(password, users['password']):
  123. return response.json(111)
  124. # 根据条件选择配置键
  125. if shopify_country:
  126. timestamp = ShopifyMultipass.get_timezone_by_country(shopify_country)
  127. secret_key = f"{shopify_country}_multipass_secret"
  128. multipass_secret = SHOPIFY_CONFIG[secret_key]
  129. if shopify_country == "eu":
  130. customer_data = {
  131. "email": email,
  132. "created_at": timestamp,
  133. }
  134. token = ShopifyMultipass.generate_multipass_token(multipass_secret, customer_data)
  135. redirect_url = f"https://eu.zositech.com/account/login/multipass/{token}"
  136. elif shopify_country == "uk":
  137. customer_data = {
  138. "email": email,
  139. "created_at": timestamp,
  140. }
  141. token = ShopifyMultipass.generate_multipass_token(multipass_secret, customer_data)
  142. redirect_url = f"https://www.zositech.co.uk/account/login/multipass/{token}"
  143. elif shopify_country == "us":
  144. customer_data = {
  145. "email": email,
  146. "created_at": timestamp,
  147. }
  148. token = ShopifyMultipass.generate_multipass_token(multipass_secret, customer_data)
  149. redirect_url = f"https://www.zositech.com/account/login/multipass/{token}"
  150. else:
  151. customer_data = {
  152. "email": email,
  153. "created_at": timestamp,
  154. }
  155. token = ShopifyMultipass.generate_multipass_token(multipass_secret, customer_data)
  156. redirect_url = f"https://www.zositech.{shopify_country}/account/login/multipass/{token}"
  157. return response.json(0, redirect_url)
  158. elif account_iso2 in ["de", "jp"]:
  159. timestamp = ShopifyMultipass.get_timezone_by_country(account_iso2)
  160. secret_key = f"{account_iso2}_multipass_secret"
  161. multipass_secret = SHOPIFY_CONFIG[secret_key]
  162. customer_data = {
  163. "email": email,
  164. "created_at": timestamp,
  165. }
  166. token = ShopifyMultipass.generate_multipass_token(multipass_secret, customer_data)
  167. redirect_url = f"https://www.zositech.{account_iso2}/account/login/multipass/{token}"
  168. return response.json(0, redirect_url)
  169. elif account_iso2 == "uk":
  170. timestamp = ShopifyMultipass.get_timezone_by_country(account_iso2)
  171. secret_key = f"{account_iso2}_multipass_secret"
  172. multipass_secret = SHOPIFY_CONFIG[secret_key]
  173. customer_data = {
  174. "email": email,
  175. "created_at": timestamp,
  176. }
  177. token = ShopifyMultipass.generate_multipass_token(multipass_secret, customer_data)
  178. redirect_url = f"https://www.zositech.co.uk/account/login/multipass/{token}"
  179. return response.json(0, redirect_url)
  180. elif CONFIG_INFO == CONFIG_EUR:
  181. timestamp = ShopifyMultipass.get_timezone_by_country("eu")
  182. secret_key = "eu_multipass_secret"
  183. multipass_secret = SHOPIFY_CONFIG[secret_key]
  184. customer_data = {
  185. "email": email,
  186. "created_at": timestamp,
  187. }
  188. token = ShopifyMultipass.generate_multipass_token(multipass_secret, customer_data)
  189. redirect_url = f"https://eu.zositech.com/account/login/multipass/{token}"
  190. return response.json(0, redirect_url)
  191. elif CONFIG_INFO == CONFIG_US:
  192. timestamp = ShopifyMultipass.get_timezone_by_country("us")
  193. secret_key = "us_multipass_secret"
  194. multipass_secret = SHOPIFY_CONFIG[secret_key]
  195. customer_data = {
  196. "email": email,
  197. "created_at": timestamp,
  198. }
  199. token = ShopifyMultipass.generate_multipass_token(multipass_secret, customer_data)
  200. redirect_url = f"https://www.zositech.com/account/login/multipass/{token}"
  201. return response.json(0, redirect_url)
  202. else:
  203. return response.json(444)
  204. @staticmethod
  205. def shopify_register(request_dict, response):
  206. email = request_dict.get("email", None)
  207. country_code = request_dict.get("countryCode", None)
  208. password = request_dict.get("password", None)
  209. authcode = request_dict.get("authCode", None)
  210. if not all([email, password, authcode, country_code]):
  211. return response.json(444)
  212. data_valid = DataValid()
  213. if data_valid.email_validate(email) is not True:
  214. return response.json(105)
  215. re_flag = data_valid.password_validate(password)
  216. has_upper = bool(re.search(r"[A-Z]", password)) # 大写字母
  217. has_lower = bool(re.search(r"[a-z]", password)) # 小写字母
  218. has_digit = bool(re.search(r"[0-9]", password)) # 数字
  219. has_special = bool(re.search(r"[!@#$%^&*()_+\-=\[\]{}|;:'\",.<>?/]", password)) # 特殊字符
  220. # 至少包含任意两类字符
  221. categories = sum([has_upper, has_lower, has_digit, has_special])
  222. if re_flag is not True and categories > 2:
  223. return response.json(109)
  224. reds = RedisObject()
  225. identifyingCode = reds.get_data(key=email + '_identifyingCode')
  226. # 判断验证码是否过期
  227. if identifyingCode is False:
  228. return response.json(120)
  229. # 验证码是否正确
  230. if authcode != identifyingCode:
  231. return response.json(121)
  232. # 注册
  233. if Device_User.objects.filter(Q(username=email) | Q(userEmail=email)).exists():
  234. return response.json(103)
  235. # 创建用户
  236. password = make_password(password)
  237. new_userID = CommonService.getUserID(μs=False, setOTAID=True)
  238. country_code = country_code.upper()
  239. countr_qs = CountryModel.objects.filter(country_code=country_code).values("id")
  240. if countr_qs.exists():
  241. region_country = countr_qs[0]['id']
  242. else:
  243. return response.json(173)
  244. user_data = {
  245. "username": email,
  246. "NickName": email,
  247. "userEmail": email,
  248. "password": password,
  249. "userID": new_userID,
  250. "is_active": True,
  251. "user_isValid": True,
  252. "region_country": region_country,
  253. "region_status": 1
  254. }
  255. Device_User.objects.create(**user_data)
  256. reds.del_data(key=email + '_identifyingCode')
  257. return response.json(0)
  258. def search_account(self, request_dict, response):
  259. email = request_dict.get("email")
  260. if not email:
  261. return response.json(444)
  262. store_configs = [
  263. ("us", SHOPIFY_CONFIG["us_store_name"], SHOPIFY_CONFIG["us_token"]),
  264. ("eu", SHOPIFY_CONFIG["eu_store_name"], SHOPIFY_CONFIG["eu_token"]),
  265. ("de", SHOPIFY_CONFIG["de_store_name"], SHOPIFY_CONFIG["de_token"]),
  266. ("uk", SHOPIFY_CONFIG["uk_store_name"], SHOPIFY_CONFIG["uk_token"]),
  267. ("jp", SHOPIFY_CONFIG["jp_store_name"], SHOPIFY_CONFIG["jp_token"]),
  268. ]
  269. def search_customer(store_name, token):
  270. return ShopifyMultipass.search_customer_by_email(store_name, token, email)
  271. try:
  272. shopify_results = {}
  273. with concurrent.futures.ThreadPoolExecutor() as executor:
  274. future_to_country = {executor.submit(search_customer, store_name, token): country for
  275. country, store_name, token in store_configs}
  276. for future in concurrent.futures.as_completed(future_to_country):
  277. country = future_to_country[future]
  278. shopify_results[country] = future.result()
  279. shopify_country = next((country for country, result in shopify_results.items() if result["customers"]), "")
  280. account_country = self.call_search_customer(email)
  281. servers_continent = "us" if account_country["us"] else "eu" if account_country["eu"] else ""
  282. if servers_continent:
  283. account_region_list = []
  284. if account_country.get("us"):
  285. account_region_list.append({
  286. "url": "https://www.dvema.com/shopify/shopifyLogin",
  287. "accountCountry": account_country["us"].lower(),
  288. "shopifyCountry": shopify_country
  289. })
  290. if account_country.get("eu"):
  291. account_region_list.append({
  292. "url": "https://api.zositeche.com/shopify/shopifyLogin",
  293. "accountCountry": account_country["eu"].lower(),
  294. "shopifyCountry": shopify_country
  295. })
  296. return response.json(0, {"accountStatus": 3, "accountRegionList": account_region_list})
  297. elif shopify_country:
  298. if shopify_country == "eu":
  299. url = "https://eu.zositech.com/account/login"
  300. elif shopify_country == "jp":
  301. url = "https://www.zosi.jp/account/login"
  302. elif shopify_country == "de":
  303. url = "https://www.zositech.de/account/login"
  304. elif shopify_country == "uk":
  305. url = "https://www.zositech.co.uk/account/login"
  306. else:
  307. url = "https://www.zositech.com/account/login"
  308. return response.json(0, {"accountStatus": 2, "url": url})
  309. else:
  310. url = "注册链接"
  311. return response.json(0, {"accountStatus": 1, "url": url})
  312. except Exception as e:
  313. print(e)
  314. return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
  315. @staticmethod
  316. def call_search_customer(email):
  317. urls = {
  318. "us": "https://www.dvema.com/shopify/searchCustomer",
  319. "eu": "https://api.zositeche.com/shopify/searchCustomer"
  320. }
  321. params = {"email": email} # Use the provided email parameter
  322. customer_region = {}
  323. def fetch_customer(region, url):
  324. try:
  325. response = requests.get(url=url, params=params)
  326. response.raise_for_status() # Raise an error for bad responses
  327. customer_country = response.json()["data"]
  328. if customer_country == "":
  329. return region, None
  330. return region, customer_country
  331. except requests.RequestException:
  332. return region, None
  333. with concurrent.futures.ThreadPoolExecutor() as executor:
  334. future_to_region = {executor.submit(fetch_customer, region, url): region for region, url in urls.items()}
  335. for future in concurrent.futures.as_completed(future_to_region):
  336. region, customer_country = future.result()
  337. customer_region[region] = customer_country
  338. return customer_region
  339. @staticmethod
  340. def search_customer(request_dict, response):
  341. email = request_dict.get("email")
  342. user_qs = Device_User.objects.filter(Q(username=email) | Q(userEmail=email))
  343. if not user_qs.exists():
  344. return response.json(104)
  345. user_region_id = user_qs.values_list('region_country', flat=True).first()
  346. country_code = CountryModel.objects.filter(id=user_region_id).values_list("country_code", flat=True).first()
  347. if country_code:
  348. return response.json(0, country_code)
  349. elif CONFIG_INFO == CONFIG_EUR:
  350. return response.json(0, "uk")
  351. elif CONFIG_INFO == CONFIG_US:
  352. return response.json(0, "us")
  353. return response.json(173)
  354. @staticmethod
  355. def get_country_domain_list(request_dict, response):
  356. lang = request_dict.get('lang', 'en')
  357. time_stamp = request_dict.get('time_stamp', None)
  358. time_stamp_token = request_dict.get('time_stamp_token', None)
  359. if not all([time_stamp, time_stamp_token]):
  360. return response.json(444)
  361. try:
  362. # 时间戳token校验
  363. if not CommonService.check_time_stamp_token(time_stamp_token, time_stamp):
  364. return response.json(13)
  365. lang_qs = LanguageModel.objects.filter(lang=lang)
  366. language = lang_qs[0]
  367. country_qs = CountryLanguageModel.objects.filter(language_id=language.id)
  368. country_qs = country_qs.annotate(api=F('country__region__zosi_api'), country_code=F('country__country_code'))
  369. country_qs = country_qs.values('country_id', 'country_name', 'country_code', 'api').order_by('country_id')
  370. country_list = []
  371. for country in country_qs:
  372. country['api'] = country['api']
  373. country_list.append(country)
  374. return response.json(0, country_list)
  375. except Exception as e:
  376. print(e)
  377. return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
  378. @staticmethod
  379. def shopify_change_password(request_dict, response):
  380. email = request_dict.get("email", None)
  381. password = request_dict.get("password", None)
  382. authcode = request_dict.get("authCode", None)
  383. if not all([email, password, authcode]):
  384. return response.json(444)
  385. try:
  386. data_valid = DataValid()
  387. if data_valid.email_validate(email) is not True:
  388. return response.json(105)
  389. re_flag = data_valid.password_validate(password)
  390. has_upper = bool(re.search(r"[A-Z]", password)) # 大写字母
  391. has_lower = bool(re.search(r"[a-z]", password)) # 小写字母
  392. has_digit = bool(re.search(r"[0-9]", password)) # 数字
  393. has_special = bool(re.search(r"[!@#$%^&*()_+\-=\[\]{}|;:'\",.<>?/]", password)) # 特殊字符
  394. # 至少包含任意两类字符
  395. categories = sum([has_upper, has_lower, has_digit, has_special])
  396. if re_flag is not True and categories > 2:
  397. return response.json(109)
  398. reds = RedisObject()
  399. identifyingCode = reds.get_data(key=email + '_forgetPwdResetCode')
  400. # 判断验证码是否过期
  401. if identifyingCode is False:
  402. return response.json(120)
  403. # 验证码是否正确
  404. if authcode != identifyingCode:
  405. return response.json(121)
  406. user_qs = Device_User.objects.filter(Q(username=email) | Q(userEmail=email))
  407. if not user_qs.exists():
  408. return response.json(173)
  409. password = make_password(password)
  410. user_qs.update(password=password)
  411. reds.del_data(key=email + '_forgetPwdResetCode')
  412. return response.json(0)
  413. except Exception as e:
  414. return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
  415. @staticmethod
  416. def verify_authcode(request_dict, response):
  417. """
  418. 在修改密码的时候验证验证码
  419. """
  420. email = request_dict.get("email", None)
  421. authcode = request_dict.get("authCode", None)
  422. code_type = request_dict.get("codeType", None)
  423. if not all([email, authcode, code_type]):
  424. return response.json(444)
  425. try:
  426. code_type = int(code_type)
  427. if code_type == 1:
  428. reds_key = "_identifyingCode"
  429. user_qs = Device_User.objects.filter(Q(username=email) | Q(userEmail=email))
  430. if user_qs.exists():
  431. return response.json(174)
  432. elif code_type == 2:
  433. reds_key = "_forgetPwdResetCode"
  434. user_qs = Device_User.objects.filter(Q(username=email) | Q(userEmail=email))
  435. if not user_qs.exists():
  436. return response.json(173)
  437. else:
  438. return response.json(444)
  439. reds = RedisObject()
  440. identifyingCode = reds.get_data(key=email + reds_key)
  441. if identifyingCode is False:
  442. return response.json(120)
  443. # 验证码是否正确
  444. if authcode != identifyingCode:
  445. return response.json(121)
  446. return response.json(0)
  447. except Exception as e:
  448. return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))