mirror of
https://git.oceanpay.cc/danial/kami_jd_ck.git
synced 2025-12-18 22:56:11 +00:00
1111 lines
50 KiB
Python
1111 lines
50 KiB
Python
import base64
|
||
import hashlib
|
||
import json
|
||
import os
|
||
import platform
|
||
import re
|
||
import time
|
||
|
||
import execjs
|
||
import requests
|
||
import ddddocr
|
||
|
||
from logger import get_logger
|
||
|
||
logger = get_logger()
|
||
|
||
|
||
class GameArea:
|
||
|
||
@staticmethod
|
||
def get_details(cookies, sku_id):
|
||
headers = {
|
||
"accept": "application/json, text/plain, */*",
|
||
"accept-language": "zh-CN,zh;q=0.9",
|
||
"cache-control": "no-cache",
|
||
"origin": "https://recharge.m.jd.com",
|
||
"pragma": "no-cache",
|
||
"priority": "u=1, i",
|
||
"referer": "https://recharge.m.jd.com/",
|
||
"sec-fetch-dest": "empty",
|
||
"sec-fetch-mode": "cors",
|
||
"sec-fetch-site": "same-site",
|
||
"user-agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 16_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.6 Mobile/15E148 Safari/604.1",
|
||
"x-referer-page": "https://recharge.m.jd.com/cardSettlement",
|
||
"x-rp-client": "h5_1.0.0",
|
||
"cookie": cookies
|
||
}
|
||
params = {
|
||
"appid": "tsw-m",
|
||
"functionId": "getGameDetailBySkuId",
|
||
"t": f"{int(time.time() * 1000)}",
|
||
"body": "{\"skuId\":\"%s\",\"appKey\":\"apple\",\"source\":41}" % sku_id,
|
||
"client": "iPhone",
|
||
"uuid": "1731377463937218663686",
|
||
"osVersion": "16.6",
|
||
"screen": "1170.000046491623*2532.0001006126404",
|
||
# "h5st": "20241112145441915;l055lflh9lr1i4k8;8e94a;tk03w92d21bda18nCxAp7H5xKn6xhgPjl2DU239CUZMhS1OwR9VyMZc5hQGRyFwDwYV8pE9DyQ7wpjquSVNSz5Kj3B5v;cbe697aecf5adbdd1ee2ddb4a1943f24;4.9;1731394481915;pjbMhjpdAaYR6jkQyLlQF6Ve2roQJrJdJrESJrpjh7Jf6rJdJz1TIipjLDrgJTISJSVS6PYd1jof0bFTKqIfJqoe1rYTImFf1LofzfITJrJdJrEa-OFTGOEjLrJp-jJS5ToeyT4e6nodGSld4j4TJeYe7PYS4bVT6T1fFSYTyjpjxj5PKSEQKeFjLrJp-jJf9HIg3T0UG6VRFuWeDipjxjJOJrpjh7JjbKUXzfUSnWYQfe2XJrJdJ31QHyVT5ipjLDrgJj4f9G1WJrJdJTlPJrpjh7ZMLrJp7rJdJLYOJipjLrpjh7JjJrJdJPYOJipjLrpjh7peLDIj1XETJrpjLrJp-rojxjZe2iFjLrpjLDrg7rJdJbYOJipjLrpjh7Je2rJdJfYOJipjLrpjh7Jf_rJdJjYOJipjLrpjh7Jj2zZf9rIjLDIj6XETJrpjLrJp-rojxj5R0ipjLrpjh7pfLDIj46FjLrpjLDrg7rJdJ7FjLrpjLDrg7rJdJb1OJrpjLrJpwqJdJbFQGakNGipjLDrguqpjhjZVl6VS5C2OqmHXi_1UHCFjLDIj6rEjLrpjLD7NLDIj7qEjLrJp-jpVLf2YLfVTeqZSAGlQLT4U1nojYunjGy1QDqWRLXmXoq5dGy1QDqWRJrJdJnVO4ipjLD7N;949f348da7bbb53fcee1f3a592db0ad3",
|
||
"x-api-eid-token": "jdd03MOMPSVKGBFF6WCM3KNQK34LGPGSCNKB2WACDOVKFUNXQWAWDEVXSHMGQEQLJ6EUKKXZ7ARQA4CPF6EMRRUP5P7ETLEAAAAMTD4S5X6IAAAAADZXKZJY7RRZL4AX"
|
||
}
|
||
jd_api = "https://api.m.jd.com/api"
|
||
response = requests.get(jd_api, headers=headers, params=params)
|
||
return response.json()
|
||
|
||
|
||
class AppStoreSpider:
|
||
SKU_MAP = {
|
||
"10.00": 10022039398507,
|
||
"50.00": 11170365589,
|
||
"100.00": 11183343342,
|
||
"200.00": 11183368356,
|
||
"500.00": 11183445154,
|
||
"1000.00": 10066407336810,
|
||
}
|
||
|
||
def __init__(
|
||
self,
|
||
cookies,
|
||
order_num,
|
||
face_price=None
|
||
):
|
||
self.order_num = order_num
|
||
self.cookies = cookies
|
||
self.face_price = face_price
|
||
self.user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36 Edg/130.0.0.0"
|
||
self.md5_key = "e7c398ffcb2d4824b4d0a703e38eb0bb"
|
||
self.time_stamp = int(time.time() * 1000)
|
||
self.submit_order_url = "https://api.m.jd.com/appstore/submitorder"
|
||
self.ticket_url = "https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkcaptcha"
|
||
self.jd_api = "https://api.m.jd.com/api"
|
||
self.action_url = "https://api.m.jd.com/client.action"
|
||
self.check_captcha_url = "https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkcaptcha"
|
||
self.headers = {
|
||
"content-type": "application/x-www-form-urlencoded",
|
||
"cookie": self.cookies,
|
||
"origin": "https://txsm-m.jd.com",
|
||
"referer": "https://txsm-m.jd.com/",
|
||
"user-agent": self.user_agent
|
||
}
|
||
self.current_os = platform.system()
|
||
self.js = None
|
||
self.init_js()
|
||
|
||
def init_js(self):
|
||
if self.current_os == "Linux":
|
||
js_path = r"/app/js"
|
||
node_modules_path = r"/app/node_modules"
|
||
else:
|
||
js_path = "js"
|
||
node_modules_path = "./node_modules"
|
||
captcha_path = os.path.join(js_path, 'decrypt_des.js')
|
||
self.js = execjs.compile(
|
||
open(captcha_path, encoding='utf8').read(),
|
||
cwd=node_modules_path
|
||
)
|
||
|
||
def format_number(self, number):
|
||
if not number:
|
||
number = 0
|
||
number = float(number)
|
||
formatted_number = "{:.2f}".format(number)
|
||
return formatted_number
|
||
|
||
def get_s(self):
|
||
use_bean = 0
|
||
buy_num = 1
|
||
face_price = self.face_price
|
||
type = 1
|
||
brand_id = 999440
|
||
eid = "QXVELAP4INIRMWVGXUQZWOOO46TSRY6NV7IRIZPZVYRRDA3JSYOXRK34SAMFD5JCMHYTABOYJXTUZOTOR5ATVDLMSM"
|
||
pay_mode = 0
|
||
coupon_ids = ""
|
||
sku_id = self.SKU_MAP[self.format_number(self.face_price)]
|
||
total_price = self.format_number(self.face_price)
|
||
order_source = 2
|
||
order_source_type = 2
|
||
s = f"{use_bean}{buy_num}{face_price}{type}{brand_id}{eid}{pay_mode}{coupon_ids}{sku_id}{total_price}{order_source}{order_source_type}"
|
||
return s
|
||
|
||
def get_u(self):
|
||
u = f"{self.time_stamp}{self.md5_key}"
|
||
return u
|
||
|
||
def get_enc_str(self):
|
||
s = self.get_s()
|
||
u = self.get_u()
|
||
_str = f"{s}{u}"
|
||
enc_str = hashlib.md5(_str.encode()).hexdigest()
|
||
return enc_str
|
||
|
||
def get_body(self, enc_str):
|
||
item = {
|
||
"useBean": 0,
|
||
"buyNum": 1,
|
||
"facePrice": self.face_price,
|
||
"type": 1,
|
||
"brandId": 999440,
|
||
"eid": "QXVELAP4INIRMWVGXUQZWOOO46TSRY6NV7IRIZPZVYRRDA3JSYOXRK34SAMFD5JCMHYTABOYJXTUZOTOR5ATVDLMSM",
|
||
"payMode": "0",
|
||
"couponIds": "",
|
||
"skuId": self.SKU_MAP[self.format_number(self.face_price)],
|
||
"totalPrice": self.format_number(self.face_price),
|
||
"orderSource": 2,
|
||
"orderSourceType": 2,
|
||
"t": self.time_stamp,
|
||
"channelSource": "txzs",
|
||
"encStr": enc_str,
|
||
"babelChannel": "ttt35"
|
||
}
|
||
json_string = json.dumps(item)
|
||
encrypted_message = base64.b64encode(json_string.encode('utf-8')).decode('utf-8').rstrip('=')
|
||
return encrypted_message
|
||
|
||
def submit_order(self, body):
|
||
data = {
|
||
"appid": "txsm-m",
|
||
"client": "iPhone",
|
||
"functionId": "appstore_order_submit_new",
|
||
"uuid": "1717649136178703924319",
|
||
"osVersion": "16.6",
|
||
"screen": "1170.000046491623*2532.0001006126404",
|
||
"t": self.time_stamp,
|
||
# "h5st": "20241028200339289;8fvel000191i1i93;583cc;tk03w8c3f1be418nQa6EtyNN1i0efjq2_3Af0KKS2OvDxI_UCy_hC2FuA-nJZtpgK3VtOi--6wEtccGDVuRfa3EpowsE;0b371d677eb468de9653cda6840a8bfc;4.9;1730117019289;pjbMhj5fyHVfCmYd6rof77VS1SldJrJdJrESJrpjh7Jf6rJdJz1TIipjLDrgJTISJSVS6PYd1jof0bFTKqIfJqoe1rYTImFf1LofzfITJrJdJrEa-OFTGOEjLrJp-jJS5ToeyT4e6nodGSld4j4TJeYe7PYS4bVT6T1fFSYTyjpjxj5PKSEQKeFjLrJp-jZf9HIg3T0UG6VRFuWeDipjxjJOJrpjh7JjRiWPRG2ZTSVSyjIVJrJdJ31QHyVT5ipjLDrgJj4f9G1WJrJdJTlPJrpjh7ZMLrJp7rJdJLYOJipjLrpjh7JjJrJdJPYOJipjLrpjh7Jf5rJdJTYOJipjLrpjh7pfLDIj2XETJrpjLrJp-rojxjpe2iFjLrpjLDrg6bojxj5f2iFjLrpjLDrg63pjxjJf2iFjLrpjLDrgJbIg6zpfJrJdJnYOJipjLrpjh7pfLDIjAOEjLrpjLDrg7rJdJfkQJrpjLrJp-rojxjpQJrpjLrJp-rojxjpS0ipjLrpjh-kjxjpS9WlOzWFjLrJp-3kjLDLjSSnQGiEV0mXbZG3RMaFRJrJdJjoPJrpjLrJp-jZVl6VS5C2OqmHXi_1UHCFjLDIj6rEjLrpjLD7NLDIj7qEjLrJp-jpVLf2YLfVTeqZSAGlQLT4U1nojYunjGy1QDqWRLXmXoq5dGy1QDqWRJrJdJnVO4ipjLD7N;5fbb8b3774fd7e25bc3742f4156c87d8",
|
||
"loginType": "2",
|
||
"x-api-eid-token": "jdd03MOMPSVKGBFF6WCM3KNQK34LGPGSCNKB2WACDOVKFUNXQWAWDEVXSHMGQEQLJ6EUKKXZ7ARQA4CPF6EMRRUP5P7ETLEAAAAMS2MA4W7AAAAAADYFXHMH6GITT4EX",
|
||
"body": body
|
||
}
|
||
response = requests.post(
|
||
url=self.submit_order_url,
|
||
headers=self.headers,
|
||
data=data
|
||
)
|
||
return response.json()
|
||
|
||
def get_ticket_res(self):
|
||
headers = {
|
||
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
|
||
"Accept-Language": "zh-CN,zh;q=0.9",
|
||
"Cache-Control": "no-cache",
|
||
"Connection": "keep-alive",
|
||
"Pragma": "no-cache",
|
||
"Upgrade-Insecure-Requests": "1",
|
||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36"
|
||
}
|
||
url = "http://127.0.0.1:99/api/TX?aid=2093769752&host=https://t.captcha.qq.com&ip="
|
||
response = requests.get(url, headers=headers, verify=False)
|
||
return response.json()
|
||
|
||
def decrypt_card_info(self, message):
|
||
key = "2E1ZMAF88CCE5EBE551FR3E9AA6FF322"
|
||
card_info = self.js.call("decryptDes", message, key)
|
||
card_info = json.loads(card_info)
|
||
return card_info[0]
|
||
|
||
def get_card_res(self, order_id):
|
||
headers = {
|
||
"accept": "application/json, text/plain, */*",
|
||
"accept-language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
|
||
"content-type": "application/x-www-form-urlencoded",
|
||
"origin": "https://recharge.m.jd.com",
|
||
"priority": "u=1, i",
|
||
"referer": "https://recharge.m.jd.com/",
|
||
"sec-fetch-dest": "empty",
|
||
"sec-fetch-mode": "cors",
|
||
"sec-fetch-site": "same-site",
|
||
"user-agent": self.user_agent,
|
||
"x-referer-page": "https://recharge.m.jd.com/orderDetail",
|
||
"x-rp-client": "h5_1.0.0",
|
||
"cookie": self.cookies
|
||
}
|
||
data = {
|
||
"appid": "tsw-m",
|
||
"functionId": "getGPOrderDetail",
|
||
"t": self.time_stamp,
|
||
"body": "{\"appKey\":\"apple\",\"source\":41,\"orderId\":\"%s\",\"version\":\"1.10\",\"rechargeversion\":\"12.8\",\"moduleName\":\"JDReactVirtualRecharge\",\"apiVersion\":\"new\"}" % (
|
||
order_id),
|
||
"client": "iPhone",
|
||
"uuid": "1717649136178703924319",
|
||
"osVersion": "16.6",
|
||
"screen": "1170.000046491623*2532.0001006126404",
|
||
"x-api-eid-token": "jdd03MOMPSVKGBFF6WCM3KNQK34LGPGSCNKB2WACDOVKFUNXQWAWDEVXSHMGQEQLJ6EUKKXZ7ARQA4CPF6EMRRUP5P7ETLEAAAAMS2MA4W7AAAAAADYFXHMH6GITT4EX"
|
||
}
|
||
response = requests.post(self.jd_api, headers=headers, data=data)
|
||
return response.json()
|
||
|
||
def get_pay_res(self, order_id):
|
||
headers = {
|
||
"accept": "*/*",
|
||
"accept-language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
|
||
"content-type": "application/json;charset=UTF-8",
|
||
"origin": "https://trade.m.jd.com",
|
||
"priority": "u=1, i",
|
||
"referer": "https://trade.m.jd.com/",
|
||
"sec-ch-ua": "\"Chromium\";v=\"130\", \"Microsoft Edge\";v=\"130\", \"Not?A_Brand\";v=\"99\"",
|
||
"sec-ch-ua-mobile": "?0",
|
||
"sec-ch-ua-platform": "\"Windows\"",
|
||
"sec-fetch-dest": "empty",
|
||
"sec-fetch-mode": "cors",
|
||
"sec-fetch-site": "same-site",
|
||
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36 Edg/130.0.0.0",
|
||
"x-referer-page": "https://trade.m.jd.com/order/orderlist_jdm.shtml",
|
||
"x-rp-client": "h5_1.0.0",
|
||
"cookie": self.cookies
|
||
}
|
||
params = {
|
||
"t": f"{self.time_stamp}",
|
||
"loginType": "2",
|
||
"loginWQBiz": "golden-trade",
|
||
"appid": "m_core",
|
||
"client": "Win32",
|
||
"clientVersion": "",
|
||
"build": "",
|
||
"osVersion": "null",
|
||
"screen": "2048*1152",
|
||
"networkType": "4g",
|
||
"partner": "",
|
||
"forcebot": "",
|
||
"d_brand": "",
|
||
"d_model": "",
|
||
"lang": "zh-CN",
|
||
"scope": "",
|
||
"sdkVersion": "",
|
||
"openudid": "",
|
||
"uuid": "17295878824571442265187",
|
||
"x-api-eid-token": "jdd03PBZHV4O4RF5SAA7QGYPZEPRPAYOCCF3WTUQYMWEFASLCJNYX2HWO7C35L5TYQUL66FGXVVMXDWKTBEEE24LW42XEWMAAAAMSWN4KSOAAAAAACY2DMACM6CADIQX",
|
||
"functionId": "pay_info_m",
|
||
"body": "{\"appType\":3,\"bizType\":\"2\",\"deviceUUId\":\"\",\"platform\":3,\"sceneval\":\"2\",\"source\":\"m_inner_myJd.orderFloor_orderlist\",\"systemBaseInfo\":\"{\\\"pixelRatio\\\":1.25,\\\"screenWidth\\\":2048,\\\"screenHeight\\\":1152,\\\"windowWidth\\\":414,\\\"windowHeight\\\":1034,\\\"statusBarHeight\\\":null,\\\"safeArea\\\":{\\\"bottom\\\":0,\\\"height\\\":0,\\\"left\\\":0,\\\"right\\\":0,\\\"top\\\":0,\\\"width\\\":0},\\\"bluetoothEnabled\\\":false,\\\"locationEnabled\\\":false,\\\"wifiEnabled\\\":false,\\\"deviceOrientation\\\":\\\"landscape\\\",\\\"benchmarkLevel\\\":-1,\\\"brand\\\":\\\"\\\",\\\"model\\\":\\\"\\\",\\\"system\\\":null,\\\"platform\\\":\\\"Win32\\\",\\\"SDKVersion\\\":\\\"\\\",\\\"enableDebug\\\":false,\\\"language\\\":\\\"zh-CN\\\",\\\"version\\\":\\\"\\\",\\\"theme\\\":\\\"light\\\",\\\"fontSizeSetting\\\":null,\\\"albumAuthorized\\\":false,\\\"cameraAuthorized\\\":false,\\\"locationAuthorized\\\":false,\\\"microphoneAuthorized\\\":false,\\\"notificationAuthorized\\\":false,\\\"notificationAlertAuthorized\\\":false,\\\"notificationBadgeAuthorized\\\":false,\\\"notificationSoundAuthorized\\\":false,\\\"phoneCalendarAuthorized\\\":false,\\\"locationReducedAccuracy\\\":false,\\\"environment\\\":\\\"\\\"}\",\"orderId\":\"%s\",\"origin\":10,\"tenantCode\":\"jgm\",\"bizModelCode\":\"2\",\"bizModeClientType\":\"M\",\"bizModeFramework\":\"Taro\",\"externalLoginType\":1,\"token\":\"3852b12f8c4d869b7ed3e2b3c68c9436\",\"appId\":\"m91d27dbf599dff74\"}" % (
|
||
order_id),
|
||
"h5st": "20241025142424843;miziz93wtzzzzgh5;9b070;tk03wa71a1ca518nFJq8OPxuvCEgJZrrjKB2ONH5SManNhGNGW2uSVhhFFx0bCkdZKQw0OH2FyaleaKKyybvEMaCxrXo;2a3300f2f9a346ca68af9fc8cccb393097d0fa2cd49da944c008aa90ce57490e;4.2;1729837464843;0aeefaf52c5a7fa31a1ad5e06c8551fda85ad5f4536e7e4aa639c27c742cb035bc404e042da71a6faa85368294121c998970b119166b80123d8bdc72009e29dac35c297c81be0d1b37dbbc13d4b1aad4964ea0ad73c8ffb64f0db8be2ca213a9654d5e12b62f9391d200f62c0d4c7b1f3a7a55a1522198915f6bc11bfe4a8737e68502fbb62d9c2f817c7e93c036750464be6c7061419b46e3e74149948c9b8b339efa0f072f393ee70d5d03450a479d0d88aa3d57ec6402effae31c81c82a9d4d094a606e51aed4cad77a8457f3c31b29ff4380e14c580fdc068ad0d134b77e1a5928debd815d06cc53c4c267935e3509ce7b17f119c71a038eee0aacaa3ccf98f635a23c272662788e7e04454c5327f46b18904e93ca95463fcb7617b8a4481d5409a29b0afdd1a59513f34184fc6a"
|
||
}
|
||
response = requests.get(self.action_url, headers=headers, params=params)
|
||
return response.json()
|
||
|
||
def plat_pay_channel_res(self, pay_id):
|
||
headers = {
|
||
"accept": "application/json, text/plain, */*",
|
||
"accept-language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
|
||
"content-type": "application/x-www-form-urlencoded",
|
||
"origin": "https://mpay.m.jd.com",
|
||
"priority": "u=1, i",
|
||
"referer": "https://mpay.m.jd.com/",
|
||
"sec-ch-ua": "\"Chromium\";v=\"130\", \"Microsoft Edge\";v=\"130\", \"Not?A_Brand\";v=\"99\"",
|
||
"sec-ch-ua-mobile": "?0",
|
||
"sec-ch-ua-platform": "\"Windows\"",
|
||
"sec-fetch-dest": "empty",
|
||
"sec-fetch-mode": "cors",
|
||
"sec-fetch-site": "same-site",
|
||
"user-agent": self.user_agent,
|
||
"x-referer-page": "https://mpay.m.jd.com/mpay.623f9498223cf9b9de9f.html",
|
||
"x-rp-client": "h5_1.0.0",
|
||
"cookie": self.cookies
|
||
}
|
||
params = {
|
||
"functionId": "platPayChannel",
|
||
"appid": "mcashier",
|
||
"scval": "mpay"
|
||
}
|
||
data = {
|
||
"body": "{\"appId\":\"m_D1vmUq63\",\"payId\":\"%s\",\"source\":\"mcashier\",\"origin\":\"h5\",\"mcashierTraceId\":1729842386189}" % (
|
||
pay_id),
|
||
"x-api-eid-token": "jdd03PBZHV4O4RF5SAA7QGYPZEPRPAYOCCF3WTUQYMWEFASLCJNYX2HWO7C35L5TYQUL66FGXVVMXDWKTBEEE24LW42XEWMAAAAMSYKR67JYAAAAACKNZVOOO4PUVKQX",
|
||
"h5st": "20241025154626341;0587023779148689;303a7;tk03w7da11b8b18nOQ6HZGNLj6DdtLQBS695YHMu7RyONolcwWCRc8ihMUs5ITCem6HIGhdYo_DpJ62yYLkdrxIBxE0N;ab8a935407baae929b0d3e267f67693f9c00a5d876ad149c10e904371f87726f;3.1;1729842386341;24c9ee85e67cf80746dd82817ecbeafc7a829b35c7f446a4c7d476cc9faa1d8834a93323ad7bce9bef1bba682b93d2e3694e425ff68d304875c1ae9e2ae398cfd94e4ff03cd3bdd9f0f600a0d75c92d537baaa944d39072a92db7dc20c99e7f80889e289e78a1f8f93c57f8471890c464b78b61e9b3bbffea712e6d6c671ad12"
|
||
}
|
||
response = requests.post(self.action_url, headers=headers, params=params, data=data)
|
||
return response.json()
|
||
|
||
def plat_wx_pay_res(self, pay_id):
|
||
headers = {
|
||
"accept": "application/json, text/plain, */*",
|
||
"accept-language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
|
||
"content-type": "application/x-www-form-urlencoded",
|
||
"origin": "https://mpay.m.jd.com",
|
||
"priority": "u=1, i",
|
||
"referer": "https://mpay.m.jd.com/",
|
||
"sec-ch-ua": "\"Chromium\";v=\"130\", \"Microsoft Edge\";v=\"130\", \"Not?A_Brand\";v=\"99\"",
|
||
"sec-ch-ua-mobile": "?0",
|
||
"sec-ch-ua-platform": "\"Windows\"",
|
||
"sec-fetch-dest": "empty",
|
||
"sec-fetch-mode": "cors",
|
||
"sec-fetch-site": "same-site",
|
||
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36 Edg/130.0.0.0",
|
||
"x-referer-page": "https://mpay.m.jd.com/mpay.623f9498223cf9b9de9f.html",
|
||
"x-rp-client": "h5_1.0.0",
|
||
"cookie": self.cookies
|
||
}
|
||
params = {
|
||
"functionId": "platWapWXPay",
|
||
"appid": "mcashier",
|
||
"scval": "mpay"
|
||
}
|
||
data = {
|
||
"body": "{\"appId\":\"m_D1vmUq63\",\"payId\":\"%s\",\"eid\":\"PBZHV4O4RF5SAA7QGYPZEPRPAYOCCF3WTUQYMWEFASLCJNYX2HWO7C35L5TYQUL66FGXVVMXDWKTBEEE24LW42XEWM\",\"source\":\"mcashier\",\"origin\":\"h5\",\"mcashierTraceId\":1729837716957}" % (
|
||
pay_id),
|
||
"x-api-eid-token": "jdd03PBZHV4O4RF5SAA7QGYPZEPRPAYOCCF3WTUQYMWEFASLCJNYX2HWO7C35L5TYQUL66FGXVVMXDWKTBEEE24LW42XEWMAAAAMSYJMLE3IAAAAAC5OTMMGGUM5SYIX",
|
||
# "h5st": "20241025142845015;0587023779148689;303a7;tk03w7da11b8b18nOQ6HZGNLj6DdtLQBS695YHMu7RyONolcwWCRc8ihMUs5ITCem6HIGhdYo_DpJ62yYLkdrxIBxE0N;48aee025d65fab059f98c546832f7a1d7bca99dba6bc0df9bd6328b9817cf394;3.1;1729837725015;24c9ee85e67cf80746dd82817ecbeafc7a829b35c7f446a4c7d476cc9faa1d8834a93323ad7bce9bef1bba682b93d2e3694e425ff68d304875c1ae9e2ae398cfd94e4ff03cd3bdd9f0f600a0d75c92d537baaa944d39072a92db7dc20c99e7f80889e289e78a1f8f93c57f8471890c464b78b61e9b3bbffea712e6d6c671ad12"
|
||
}
|
||
response = requests.post(self.action_url, headers=headers, params=params, data=data)
|
||
return response.json()
|
||
|
||
def get_deep_link_res(self, mweb_url):
|
||
headers = {
|
||
"Host": "wx.tenpay.com",
|
||
"sec-ch-ua-platform": "\"Windows\"",
|
||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36 Edg/130.0.0.0",
|
||
"sec-ch-ua": "\"Chromium\";v=\"130\", \"Microsoft Edge\";v=\"130\", \"Not?A_Brand\";v=\"99\"",
|
||
"sec-ch-ua-mobile": "?0",
|
||
"Accept": "*/*",
|
||
"Sec-Fetch-Site": "same-origin",
|
||
"Sec-Fetch-Mode": "cors",
|
||
"Sec-Fetch-Dest": "empty",
|
||
"Referer": mweb_url,
|
||
"Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6"
|
||
}
|
||
prepay_id = re.search(r'prepay_id=(wx\w+)', mweb_url).group(1)
|
||
package = re.search(r'package=(\d+)', mweb_url).group(1)
|
||
ticket_res = self.get_ticket_res()
|
||
logger.info(f"订单号:{self.order_num},获取ticket返回:{ticket_res}")
|
||
ticket = ticket_res["ticket"]
|
||
randstr = ticket_res["randstr"]
|
||
params = {
|
||
"ticket": ticket,
|
||
"randstr": randstr,
|
||
"prepayid": prepay_id,
|
||
"package": package
|
||
}
|
||
response = requests.get(self.check_captcha_url, headers=headers, params=params)
|
||
return response.json()
|
||
|
||
def get_card_secret(self, jd_order_num):
|
||
card_res = self.get_card_res(jd_order_num)
|
||
logger.info(f"获取卡密信息返回:{card_res}")
|
||
card_info = card_res.get("result").get("cardInfos")
|
||
if card_res.get("code") != "0" or not card_info:
|
||
return 110, card_res
|
||
card_info = self.decrypt_card_info(card_info)
|
||
return 100, card_info
|
||
|
||
def run(self):
|
||
# 获取加密参数
|
||
try:
|
||
enc_str = self.get_enc_str()
|
||
except KeyError as e:
|
||
return 110, {"msg": "充值面值有误"}
|
||
# 获取请求body
|
||
body = self.get_body(enc_str)
|
||
# 提交预付款订单
|
||
order_res = self.submit_order(body)
|
||
# order_res = {'code': 200, 'msg': '成功', 'dispCode': '200', 'dispPhone': '', 'data': '304550362538', 'viewMsg': ''}
|
||
logger.info(f"订单号:{self.order_num},app_store提交预付款订单返回:{order_res}")
|
||
if order_res.get("code") != 200:
|
||
return 110, order_res
|
||
order_id = order_res["data"]
|
||
# 获取支付信息
|
||
pay_res = self.get_pay_res(order_id)
|
||
logger.info(f"订单号:{self.order_num},app_store获取支付信息返回:{pay_res}")
|
||
if pay_res.get("code") != "0":
|
||
return 110, pay_res
|
||
pay_id = pay_res["body"]["payId"]
|
||
# 获取微信支付信息
|
||
pay_channel_res = self.plat_pay_channel_res(pay_id)
|
||
logger.info(f"订单号:{self.order_num},app_store请求微信渠道返回:{pay_channel_res}")
|
||
if pay_channel_res.get("code") != "0":
|
||
return 110, pay_channel_res
|
||
wx_pay_res = self.plat_wx_pay_res(pay_id)
|
||
logger.info(f"订单号:{self.order_num},app_store获取微信支付信息返回:{wx_pay_res}")
|
||
if wx_pay_res.get("code") != "0":
|
||
return 110, wx_pay_res
|
||
if wx_pay_res.get("errorCode") == "-1":
|
||
wx_pay_res["order_id"] = order_id
|
||
wx_pay_res["pay_id"] = pay_id
|
||
wx_pay_res["face_price"] = self.face_price
|
||
return 110, wx_pay_res
|
||
mweb_url = wx_pay_res["payInfo"]["mweb_url"]
|
||
# 获取支付链接信息
|
||
deep_link_res = self.get_deep_link_res(mweb_url)
|
||
logger.info(f"订单号:{self.order_num},app_store获取支付链接deep_link信息返回:{deep_link_res}")
|
||
if deep_link_res.get("retcode") != 1:
|
||
return 110, deep_link_res
|
||
return 100, {
|
||
"deeplink": deep_link_res["deeplink"],
|
||
"order_id": pay_channel_res["orderId"],
|
||
"pay_id": pay_id,
|
||
"face_price": self.face_price,
|
||
}
|
||
|
||
|
||
class GoodsAppleCard(AppStoreSpider):
|
||
|
||
def __init__(
|
||
self,
|
||
cookies,
|
||
order_num,
|
||
brand_id,
|
||
face_price,
|
||
sku_id,
|
||
username,
|
||
game_srv=None,
|
||
game_area=None,
|
||
recharge_type=1
|
||
):
|
||
super(GoodsAppleCard, self).__init__(
|
||
cookies,
|
||
order_num,
|
||
face_price=face_price
|
||
)
|
||
self.sku_id = sku_id
|
||
self.brand_id = brand_id
|
||
self.username = username
|
||
self.game_srv = game_srv
|
||
self.game_area = game_area
|
||
self.face_price = face_price
|
||
self.recharge_type = recharge_type
|
||
|
||
def encrypt_username(self, username):
|
||
key = "2E1ZMAF88CCE5EBE551FR3E9AA6FF322"
|
||
username = self.js.call("encryptDes", username, key)
|
||
return username
|
||
|
||
def submit_gp_order(self):
|
||
headers = {
|
||
"Host": "api.m.jd.com",
|
||
"pragma": "no-cache",
|
||
"cache-control": "no-cache",
|
||
"user-agent": self.user_agent,
|
||
"accept": "application/json, text/plain, */*",
|
||
"x-referer-page": "https://recharge.m.jd.com/cardSettlement",
|
||
"content-type": "application/x-www-form-urlencoded",
|
||
"x-rp-client": "h5_1.0.0",
|
||
"origin": "https://recharge.m.jd.com",
|
||
"sec-fetch-site": "same-site",
|
||
"sec-fetch-mode": "cors",
|
||
"sec-fetch-dest": "empty",
|
||
"referer": "https://recharge.m.jd.com/",
|
||
"accept-language": "zh-CN,zh;q=0.9",
|
||
"priority": "u=1, i",
|
||
"cookie": self.cookies
|
||
}
|
||
username = self.encrypt_username(self.username)
|
||
data = {}
|
||
if self.recharge_type == 1:
|
||
data = {
|
||
"appid": "tsw-m",
|
||
"functionId": "submitGPOrder",
|
||
"t": f"{self.time_stamp}",
|
||
"body": "{\"skuId\":\"%s\",\"brandId\":\"%s\",\"type\":2,\"buyNum\":1,\"payMode\":\"0\",\"totalPrice\":\"%s\",\"username\":\"%s\",\"appKey\":\"apple\",\"source\":41,\"version\":\"1.10\",\"orderSource\":41}" % (
|
||
self.sku_id, self.brand_id, self.face_price, username
|
||
),
|
||
"client": "iPhone",
|
||
"uuid": "1731377463937218663686",
|
||
"osVersion": "16.6",
|
||
"screen": "1170.000046491623*2532.0001006126404",
|
||
# "h5st": "20241112141707038;l055lflh9lr1i4k8;8e94a;tk03w92d21bda18nCxAp7H5xKn6xhgPjl2DU239CUZMhS1OwR9VyMZc5hQGRyFwDwYV8pE9DyQ7wpjquSVNSz5Kj3B5v;8effdf684f396d5889d2bde7e769f965;4.9;1731392227038;pjbMhjpdAaYR6jkQyLlQF6Ve2roQJrJdJrESJrpjh7Jf6rJdJz1TIipjLDrgJTISJSVS6PYd1jof0bFTKqIfJqoe1rYTImFf1LofzfITJrJdJrEa-OFTGOEjLrJp-jJS5ToeyT4e6nodGSld4j4TJeYe7PYS4bVT6T1fFSYTyjpjxj5PKSEQKeFjLrJp-jJf9HIg3T0UG6VRFuWeDipjxjJOJrpjh7JjxOYRhiFPyK3Z2f2XJrJdJ31QHyVT5ipjLDrgJj4f9G1WJrJdJTlPJrpjh7ZMLrJp7rJdJLYOJipjLrpjh7JjJrJdJPYOJipjLrpjh7ZeLDIj1XETJrpjLrJp-rojxjZe2iFjLrpjLDrg7rJdJbYOJipjLrpjh75e2rJdJfYOJipjLrpjh7Jf_rJdJjYOJipjLrpjh7Jj2zZf9rIjLDIj6XETJrpjLrJp-rojxj5R0ipjLrpjh7pfLDIj46FjLrpjLDrg7rJdJ7FjLrpjLDrg7rJdJb1OJrpjLrJpwqJdJbFQGakNGipjLDrguqpjhjZVl6VS5C2OqmHXi_1UHCFjLDIj6rEjLrpjLD7NLDIj7qEjLrJp-jpVLf2YLfVTeqZSAGlQLT4U1nojYunjGy1QDqWRLXmXoq5dGy1QDqWRJrJdJnVO4ipjLD7N;204790ef3f0380d87102b52131fd50d7",
|
||
"x-api-eid-token": "jdd03MOMPSVKGBFF6WCM3KNQK34LGPGSCNKB2WACDOVKFUNXQWAWDEVXSHMGQEQLJ6EUKKXZ7ARQA4CPF6EMRRUP5P7ETLEAAAAMTDYRX6SIAAAAACB66T3PYQZLCEEX"
|
||
}
|
||
if self.recharge_type == 2:
|
||
data = {
|
||
"appid": "tsw-m",
|
||
"functionId": "submitGPOrder",
|
||
"t": f"{self.time_stamp}",
|
||
"body": "{\"skuId\":\"%s\",\"brandId\":\"%s\",\"type\":2,\"buyNum\":1,\"payMode\":\"0\",\"totalPrice\":\"%s\",\"gamesrv\":\"%s\",\"gamearea\":\"%s\",\"username\":\"%s\",\"appKey\":\"apple\",\"source\":41,\"version\":\"1.10\",\"orderSource\":41}" % (
|
||
self.sku_id, self.brand_id, self.face_price, self.game_srv, self.game_area, username
|
||
),
|
||
"client": "iPhone",
|
||
"uuid": "1731377463937218663686",
|
||
"osVersion": "16.6",
|
||
"screen": "1170.000046491623*2532.0001006126404",
|
||
# "h5st": "20241112172954252;l055lflh9lr1i4k8;8e94a;tk03w92d21bda18nCxAp7H5xKn6xhgPjl2DU239CUZMhS1OwR9VyMZc5hQGRyFwDwYV8pE9DyQ7wpjquSVNSz5Kj3B5v;dcf2dab0edd6fadd62550cc4ca5e28c4;4.9;1731403794252;pjbMhjpdAaYR6jkQyLlQF6Ve2roQJrJdJrESJrpjh7Jf6rJdJz1TIipjLDrgJTISJSVS6PYd1jof0bFTKqIfJqoe1rYTImFf1LofzfITJrJdJrEa-OFTGOEjLrJp-jJS5ToeyT4e6nodGSld4j4TJeYe7PYS4bVT6T1fFSYTyjpjxj5PKSEQKeFjLrJp-jJf9HIg3T0UG6VRFuWeDipjxjJOJrpjh7JjWW1f5LnageHNqSEOJrJdJ31QHyVT5ipjLDrgJj4f9G1WJrJdJTlPJrpjh7ZMLrJp7rJdJLYOJipjLrpjh7JjJrJdJPYOJipjLrpjh7ZeLDIj1XETJrpjLrJp-rojxjZe2iFjLrpjLDrg7rJdJbYOJipjLrpjh75e2rJdJfYOJipjLrpjh7Jf_rJdJjYOJipjLrpjh7Jj2zZf9rIjLDIj6XETJrpjLrJp-rojxj5R0ipjLrpjh7pfLDIj46FjLrpjLDrg7rJdJ7FjLrpjLDrg7rJdJb1OJrpjLrJpwqJdJbFQGakNGipjLDrguqpjhjZVl6VS5C2OqmHXi_1UHCFjLDIj6rEjLrpjLD7NLDIj7qEjLrJp-jpVLf2YLfVTeqZSAGlQLT4U1nojYunjGy1QDqWRLXmXoq5dGy1QDqWRJrJdJnVO4ipjLD7N;651ee3204b43d0d514092fa61f5629f1",
|
||
"x-api-eid-token": "jdd03MOMPSVKGBFF6WCM3KNQK34LGPGSCNKB2WACDOVKFUNXQWAWDEVXSHMGQEQLJ6EUKKXZ7ARQA4CPF6EMRRUP5P7ETLEAAAAMTD6ZVZVQAAAAADSCNVUR4IR7W54X"
|
||
}
|
||
response = requests.post(self.jd_api, headers=headers, data=data)
|
||
return response.json()
|
||
|
||
def run(self):
|
||
# 提交预付款订单
|
||
gp_order_res = self.submit_gp_order()
|
||
# gp_order_res = {'result': {'orderId': 304636667875, 'paySuccessUrl': ''}, 'code': '0'}
|
||
logger.info(f"订单号:{self.order_num},商品下单提交预付款订单返回:{gp_order_res}")
|
||
if gp_order_res.get("code") != '0':
|
||
return 110, gp_order_res
|
||
order_id = gp_order_res["result"]["orderId"]
|
||
# 获取支付信息
|
||
pay_res = self.get_pay_res(order_id)
|
||
logger.info(f"订单号:{self.order_num},商品下单获取支付信息返回:{pay_res}")
|
||
if pay_res.get("code") != "0":
|
||
return 110, pay_res
|
||
pay_id = pay_res["body"]["payId"]
|
||
# 获取微信支付信息
|
||
pay_channel_res = self.plat_pay_channel_res(pay_id)
|
||
logger.info(f"订单号:{self.order_num},商品下单请求微信渠道返回:{pay_channel_res}")
|
||
if pay_channel_res.get("code") != "0":
|
||
return 110, pay_channel_res
|
||
wx_pay_res = self.plat_wx_pay_res(pay_id)
|
||
logger.info(f"订单号:{self.order_num},商品下单获取微信支付信息返回:{wx_pay_res}")
|
||
if wx_pay_res.get("code") != "0":
|
||
return 110, wx_pay_res
|
||
if wx_pay_res.get("errorCode") == "-1":
|
||
wx_pay_res["order_id"] = order_id
|
||
wx_pay_res["pay_id"] = pay_id
|
||
wx_pay_res["face_price"] = self.face_price
|
||
return 110, wx_pay_res
|
||
mweb_url = wx_pay_res["payInfo"]["mweb_url"]
|
||
# 获取支付链接信息
|
||
deep_link_res = self.get_deep_link_res(mweb_url)
|
||
logger.info(f"订单号:{self.order_num},商品下单获取支付链接deep_link信息返回:{deep_link_res}")
|
||
if deep_link_res.get("retcode") != 1:
|
||
return 110, deep_link_res
|
||
return 100, {
|
||
"deeplink": deep_link_res["deeplink"],
|
||
"order_id": pay_channel_res["orderId"],
|
||
"pay_id": pay_id,
|
||
"face_price": self.face_price
|
||
}
|
||
|
||
|
||
class DeleteOrder:
|
||
|
||
def __init__(self, cookie, order_id):
|
||
self.cookie = cookie
|
||
self.order_id = order_id
|
||
self.timestamp = int(time.time() * 1000)
|
||
self.h5st_ctx = self.get_js_obj("h5st4.2.js")
|
||
self.ai = "8108f"
|
||
self.version = "4.2"
|
||
self.p1 = None
|
||
self.fp = None
|
||
self.tk = None
|
||
self.mode = None
|
||
self.rd = None
|
||
self.sign = None
|
||
self.timestamp_sha256 = None
|
||
self.t_sha256 = None
|
||
self.t_send_data = None
|
||
|
||
def get_js_obj(self, js):
|
||
with open(f'./js/{js}', 'r', encoding='utf-8') as file:
|
||
js_code = file.read()
|
||
return execjs.compile(js_code, cwd="./node_modules")
|
||
|
||
def encrypt_body(self):
|
||
key = "e7c398ffcb2d4824b4d0a703e38eb0bb"
|
||
str_to_hash = f"{self.order_id}1sx{self.timestamp}{key}"
|
||
enc_str = hashlib.md5(str_to_hash.encode('utf-8')).hexdigest()
|
||
body = {
|
||
"orderId": self.order_id,
|
||
"source": "1",
|
||
"channelSource": "sx",
|
||
"t": self.timestamp,
|
||
"encStr": enc_str,
|
||
}
|
||
return json.dumps(body)
|
||
|
||
def cancel_order(self):
|
||
body = self.encrypt_body()
|
||
url = "https://api.m.jd.com/api"
|
||
headers = {
|
||
"accept": "application/json, text/plain, */*",
|
||
"accept-language": "zh-CN,zh;q=0.9",
|
||
"cache-control": "no-cache",
|
||
"content-type": "application/x-www-form-urlencoded",
|
||
"origin": "https://recharge.m.jd.com",
|
||
"pragma": "no-cache",
|
||
"priority": "u=1, i",
|
||
"referer": "https://recharge.m.jd.com/",
|
||
"sec-fetch-dest": "empty",
|
||
"sec-fetch-mode": "cors",
|
||
"sec-fetch-site": "same-site",
|
||
"user-agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 16_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.6 Mobile/15E148 Safari/604.1",
|
||
"x-referer-page": "https://recharge.m.jd.com/orderDetail",
|
||
"x-rp-client": "h5_1.0.0",
|
||
"cookie": self.cookie
|
||
}
|
||
data = {
|
||
"appid": "tsw-m",
|
||
"functionId": "huafei_orderCancel",
|
||
"t": f"{self.timestamp}",
|
||
"body": body,
|
||
"client": "iPhone",
|
||
"uuid": "1735283651510338525254",
|
||
"osVersion": "16.6",
|
||
"screen": "828.0000123381615*1792.0000267028809",
|
||
"x-api-eid-token": "jdd03MOMPSVKGBFF6WCM3KNQK34LGPGSCNKB2WACDOVKFUNXQWAWDEVXSHMGQEQLJ6EUKKXZ7ARQA4CPF6EMRRUP5P7ETLEAAAAMUC37NLWQAAAAADDQSI4QCFE6GAAX"
|
||
}
|
||
response = requests.post(url, headers=headers, data=data)
|
||
print(response.text)
|
||
|
||
def get_fp(self):
|
||
self.fp = self.h5st_ctx.call("iC")
|
||
|
||
def get_env(self):
|
||
ctx = self.get_js_obj("h5st4.2.js")
|
||
env = ctx.call("expandParams", self.fp, self.p1)
|
||
return env
|
||
|
||
def request_algo(self):
|
||
env = self.get_env()
|
||
url = "https://cactus.jd.com/request_algo"
|
||
params = {
|
||
"g_ty": "ajax"
|
||
}
|
||
timestamp_ms = int(time.time() * 1000)
|
||
data = {
|
||
"version": "4.2",
|
||
"fp": self.fp,
|
||
"appId": "8108f",
|
||
"timestamp": timestamp_ms,
|
||
"platform": "web",
|
||
"expandParams": env,
|
||
"fv": "h5_npm_v4.2.0"
|
||
}
|
||
headers = {
|
||
"accept": "application/json",
|
||
"accept-language": "zh-CN,zh;q=0.9",
|
||
"cache-control": "no-cache",
|
||
"content-type": "application/json",
|
||
"origin": "https://txsm-m.jd.com",
|
||
"pragma": "no-cache",
|
||
"referer": "https://txsm-m.jd.com/",
|
||
"sec-ch-ua": "\"Google Chrome\";v=\"123\", \"Not:A-Brand\";v=\"8\", \"Chromium\";v=\"123\"",
|
||
"sec-ch-ua-mobile": "?0",
|
||
"sec-ch-ua-platform": "\"Windows\"",
|
||
"sec-fetch-dest": "empty",
|
||
"sec-fetch-mode": "cors",
|
||
"sec-fetch-site": "same-site",
|
||
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36"
|
||
}
|
||
data = json.dumps(data, separators=(',', ':'))
|
||
response = requests.post(url, headers=headers, params=params, data=data)
|
||
return response.json().get('data')
|
||
|
||
def get_algo(self):
|
||
algo_data = self.request_algo()
|
||
print(algo_data)
|
||
algo = algo_data['result']['algo']
|
||
self.tk = algo_data['result']['tk']
|
||
self.mode = re.findall(r"algo\.(.*)\(", algo)[0]
|
||
self.rd = re.findall(r"rd='(.*)';", algo)[0]
|
||
|
||
def get_p1(self):
|
||
self.p1 = re.findall(r"pin=(.*);", self.cookie)[0]
|
||
|
||
def get_tm(self, timestamp):
|
||
import datetime
|
||
# 将13位时间戳转换为datetime对象
|
||
dt = datetime.datetime.fromtimestamp(timestamp / 1000)
|
||
year = dt.year
|
||
month = str(dt.month).zfill(2)
|
||
day = str(dt.day).zfill(2)
|
||
hour = str(dt.hour).zfill(2)
|
||
minute = str(dt.minute).zfill(2)
|
||
second = str(dt.second).zfill(2)
|
||
# 将微秒转换为毫秒
|
||
microsecond = dt.microsecond // 1000
|
||
return f"{year}{month}{day}{hour}{minute}{second}{microsecond}"
|
||
|
||
def get_oe(self, ts):
|
||
oe = self.h5st_ctx.call("test", self.mode, self.tk, self.fp, ts, self.ai, self.rd)
|
||
return oe
|
||
|
||
def get_body(self):
|
||
self.t_send_data = int(time.time() * 1000)
|
||
self.t_sha256 = int(time.time() * 1000)
|
||
self.timestamp_sha256 = self.get_tm(self.t_sha256)
|
||
ts = self.timestamp_sha256 + "74"
|
||
oe = self.get_oe(ts)
|
||
body_hex = self.h5st_ctx.call("encrypt_body", self.order_id, "delete")
|
||
t_string = f"{oe}appid:m_core&body:{body_hex}&client:Win32&clientVersion:&functionId:order_recycle_m&t:{self.t_send_data}{oe}"
|
||
self.sign = self.h5st_ctx.call("__genSign", t_string)
|
||
body = self.h5st_ctx.call("build_params", self.order_id, "delete")
|
||
return body
|
||
|
||
def get_h5st(self):
|
||
h5st = f"{self.timestamp_sha256};{self.fp};{self.ai};{self.tk};{self.sign};{self.version};{self.t_sha256}"
|
||
return h5st
|
||
|
||
def request_jd_recycle(self, body, h5st):
|
||
headers = {
|
||
"accept": "*/*",
|
||
"accept-language": "zh-CN,zh;q=0.9",
|
||
"cache-control": "no-cache",
|
||
"content-type": "application/x-www-form-urlencoded",
|
||
"origin": "https://trade.m.jd.com",
|
||
"pragma": "no-cache",
|
||
"referer": "https://trade.m.jd.com/",
|
||
"sec-ch-ua": "\"Google Chrome\";v=\"123\", \"Not:A-Brand\";v=\"8\", \"Chromium\";v=\"123\"",
|
||
"sec-ch-ua-mobile": "?0",
|
||
"sec-ch-ua-platform": "\"Windows\"",
|
||
"sec-fetch-dest": "empty",
|
||
"sec-fetch-mode": "cors",
|
||
"sec-fetch-site": "same-site",
|
||
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36",
|
||
"x-referer-page": "https://trade.m.jd.com/order/orderlist_jdm.shtml",
|
||
"x-rp-client": "h5_1.0.0",
|
||
"cookie": self.cookie
|
||
}
|
||
url = "https://api.m.jd.com/client.action"
|
||
data = {
|
||
"t": f"{self.t_send_data}",
|
||
"loginType": "2",
|
||
"loginWQBiz": "golden-trade",
|
||
"appid": "m_core",
|
||
"client": "Win32",
|
||
"clientVersion": "",
|
||
"build": "",
|
||
"osVersion": "null",
|
||
"screen": "1440*900",
|
||
"networkType": "4g",
|
||
"partner": "",
|
||
"forcebot": "",
|
||
"d_brand": "",
|
||
"d_model": "",
|
||
"lang": "zh-CN",
|
||
"scope": "",
|
||
"sdkVersion": "",
|
||
"openudid": "",
|
||
"uuid": "1658065822",
|
||
"x-api-eid-token": "jdd03EZ4U2HD6OVXEWMTFQVIXNMASQWBFCYSPX37R7H6QIQDPWYYPIESUHF2YNEGTGXKWHGCH5VEWPMZA4PWPPKX6ZJOKWIAAAAMOR6CEGEIAAAAADWOVM6433SKKPAX",
|
||
"functionId": "order_recycle_m",
|
||
"body": body,
|
||
"h5st": h5st
|
||
}
|
||
response = requests.post(url, headers=headers, data=data)
|
||
print(response.text)
|
||
|
||
def recycle_order(self):
|
||
self.get_p1()
|
||
self.get_fp()
|
||
self.get_algo()
|
||
body = self.get_body()
|
||
h5st = self.get_h5st()
|
||
self.request_jd_recycle(body=body, h5st=h5st)
|
||
|
||
def run(self):
|
||
# 取消订单
|
||
self.cancel_order()
|
||
# 删除订单
|
||
self.recycle_order()
|
||
|
||
|
||
class LoginSpider:
|
||
|
||
def __init__(self, phone):
|
||
self.phone = phone
|
||
self.headers = {
|
||
'accept': 'application/json, text/plain, */*',
|
||
'accept-language': 'zh-CN,zh;q=0.9',
|
||
'cache-control': 'no-cache',
|
||
'content-type': 'application/x-www-form-urlencoded',
|
||
'origin': 'https://plogin.m.jd.com',
|
||
'pragma': 'no-cache',
|
||
'priority': 'u=1, i',
|
||
'referer': 'https://plogin.m.jd.com/login/login?appid=300&returnurl=https%3A%2F%2Fm.jd.com%2F&source=wq_passport',
|
||
'sec-ch-ua': '"Microsoft Edge";v="131", "Chromium";v="131", "Not_A Brand";v="24"',
|
||
'sec-ch-ua-mobile': '?0',
|
||
'sec-ch-ua-platform': '"Windows"',
|
||
'sec-fetch-dest': 'empty',
|
||
'sec-fetch-mode': 'cors',
|
||
'sec-fetch-site': 'same-origin',
|
||
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36 Edg/131.0.0.0',
|
||
}
|
||
self.cookies = {}
|
||
self.current_os = platform.system()
|
||
self.js_path = None
|
||
self.node_modules_path = None
|
||
self.load_directory()
|
||
self.slide_ctw = self.init_verify_data_js()
|
||
self.user_ctw = self.init_user_encrypt_js()
|
||
self.h5st_ctw = self.init_h5st_js()
|
||
|
||
self.session = requests.Session()
|
||
self.ocr = ddddocr.DdddOcr()
|
||
|
||
self.eid = ""
|
||
self.fp = "26114226dc0c6ee7f1eaf3d4abb30cf2"
|
||
self.s_token = None
|
||
self.rsa_modulus = None
|
||
self.api_st = None
|
||
self.api_fp = None
|
||
self.img1 = None
|
||
self.img2 = None
|
||
self.vt = None
|
||
self.img = None
|
||
self.jd_risk_token_id = None
|
||
self.jcap_sid = None
|
||
self.ck = None
|
||
|
||
def load_directory(self):
|
||
if self.current_os == "Linux":
|
||
self.js_path = r"/app/js"
|
||
self.node_modules_path = r"/app/node_modules"
|
||
else:
|
||
self.js_path = "js"
|
||
self.node_modules_path = "./node_modules"
|
||
|
||
def do_execjs(self, path):
|
||
return execjs.compile(
|
||
open(path, encoding='utf8').read(),
|
||
cwd=self.node_modules_path
|
||
)
|
||
|
||
def init_verify_data_js(self):
|
||
js_path = os.path.join(self.js_path, 'modules.js')
|
||
return execjs.compile(
|
||
open(js_path, encoding='gbk', errors='ignore').read(),
|
||
cwd=self.node_modules_path
|
||
)
|
||
|
||
def init_user_encrypt_js(self):
|
||
js_path = os.path.join(self.js_path, 'user_encrypt.js')
|
||
return execjs.compile(
|
||
open(js_path, encoding='gbk', errors='ignore').read(),
|
||
cwd=self.node_modules_path
|
||
)
|
||
|
||
def init_h5st_js(self):
|
||
js_path = os.path.join(self.js_path, 'h5st-5.0.js')
|
||
return execjs.compile(
|
||
open(js_path, encoding='gbk', errors='ignore').read(),
|
||
cwd=self.node_modules_path
|
||
)
|
||
|
||
def request_jd_risk_token_id(self):
|
||
response = self.session.get('https://payrisk.jd.com/m.html', cookies=self.cookies, headers=self.headers)
|
||
return response.text
|
||
|
||
def build_jcapsid_data(self):
|
||
return self.user_ctw.call('init', self.phone, self.fp, self.jd_risk_token_id, self.s_token)
|
||
|
||
def request_jcapsid(self):
|
||
url = "https://plogin.m.jd.com/cgi-bin/mm/jcapsid"
|
||
data = self.build_jcapsid_data()
|
||
response = self.session.post(url, headers=self.headers, data=data)
|
||
print(f"request_jcapsid返回:{response.text}")
|
||
return response.json()
|
||
|
||
def request_new_login_entrance(self):
|
||
url = "https://plogin.m.jd.com/cgi-bin/mm/new_login_entrance"
|
||
params = {
|
||
"lang": "chs",
|
||
"returnurl": "https://my.m.jd.com/",
|
||
"risk_jd\\[eid\\]": self.eid,
|
||
"risk_jd\\[fp\\]": self.fp
|
||
}
|
||
response = self.session.get(url, headers=self.headers, cookies=self.cookies, params=params)
|
||
print(response.text)
|
||
return response.json()
|
||
|
||
def get_jcap_sid(self):
|
||
jcapsid_res = self.request_jcapsid()
|
||
self.jcap_sid = jcapsid_res["jcap_sid"]
|
||
|
||
def get_login_entrance(self):
|
||
login_entrance_res = self.request_new_login_entrance()
|
||
self.s_token = login_entrance_res["s_token"]
|
||
self.rsa_modulus = login_entrance_res["rsa_modulus"]
|
||
|
||
def build_fp_data(self):
|
||
return self.slide_ctw.call('getFp', self.phone, self.jcap_sid)
|
||
|
||
def request_data_fp(self):
|
||
data = self.build_fp_data()
|
||
url = "https://jcap.m.jd.com/cgi-bin/api/fp"
|
||
response = self.session.post(url, headers=self.headers, data=data)
|
||
print(response.text)
|
||
return response.json()
|
||
|
||
def get_api_params(self):
|
||
fp_res = self.request_data_fp()
|
||
self.api_st = fp_res["st"]
|
||
self.api_fp = fp_res["fp"]
|
||
|
||
def build_captcha_data(self):
|
||
return self.slide_ctw.call('getImage', self.phone, self.jcap_sid, self.api_st)
|
||
|
||
def request_captcha(self):
|
||
url = "https://jcap.m.jd.com/cgi-bin/api/check"
|
||
data = self.build_captcha_data()
|
||
response = requests.post(url, headers=self.headers, data=data)
|
||
print(response.text)
|
||
return response.json()
|
||
|
||
def set_img(self, captcha_res):
|
||
self.img = captcha_res['img']
|
||
img_json = json.loads(captcha_res['img'])
|
||
self.api_st = captcha_res['st']
|
||
self.img1 = img_json['b1']
|
||
self.img2 = img_json['b2']
|
||
|
||
def get_captcha(self):
|
||
captcha_res = self.request_captcha()
|
||
if captcha_res.get("vt"):
|
||
self.vt = captcha_res["vt"]
|
||
self.api_st = captcha_res["st"]
|
||
return True
|
||
self.set_img(captcha_res)
|
||
return False
|
||
|
||
def verify_captcha(self):
|
||
bg = self.img1.replace('data:image/jpg;base64,', '')
|
||
background = base64.b64decode(bg)
|
||
fg = self.img2.replace('data:image/png;base64,', '')
|
||
target = base64.b64decode(fg)
|
||
res = self.ocr.slide_match(target, background, simple_target=True)
|
||
x = round(res['target'][0] * (290 / 275))
|
||
url = "https://jcap.m.jd.com/cgi-bin/api/check"
|
||
data = self.slide_ctw.call("verify", self.phone, self.jcap_sid, self.img, self.api_st, x)
|
||
response = self.session.post(url, headers=self.headers, data=data)
|
||
print(response.text)
|
||
return response.json()
|
||
|
||
def check_captcha(self):
|
||
captcha_res = self.verify_captcha()
|
||
if captcha_res.get("vt"):
|
||
self.vt = captcha_res["vt"]
|
||
self.api_st = captcha_res["st"]
|
||
return True
|
||
self.set_img(captcha_res)
|
||
return False
|
||
|
||
def build_send_code_data(self):
|
||
return self.user_ctw.call(
|
||
'sendMsg',
|
||
self.phone,
|
||
self.vt,
|
||
self.jd_risk_token_id,
|
||
self.s_token,
|
||
self.rsa_modulus
|
||
)
|
||
|
||
def send_code(self):
|
||
url = "https://plogin.m.jd.com/cgi-bin/mm/dosendlogincode"
|
||
data = self.build_send_code_data()
|
||
response = self.session.post(url, headers=self.headers, data=data)
|
||
print(response.text)
|
||
self.ck = self.get_cookie()
|
||
return response.json()
|
||
|
||
def get_h5st(self, code):
|
||
return self.h5st_ctw.call('getH5st', code, self.s_token)
|
||
|
||
def build_verify_data(self, phone, code, s_token, jd_risk_token_id, rsa_modulus):
|
||
h5st = self.get_h5st(code)
|
||
return self.user_ctw.call(
|
||
'login',
|
||
phone,
|
||
code,
|
||
s_token,
|
||
h5st,
|
||
jd_risk_token_id,
|
||
rsa_modulus
|
||
)
|
||
|
||
def get_cookie(self):
|
||
cookie_dict = self.session.cookies.get_dict()
|
||
return "; ".join([f"{key}={value}" for key, value in cookie_dict.items()]) + ";"
|
||
|
||
def request_sms_login(self, ck, code, s_token, jd_risk_token_id, rsa_modulus):
|
||
url = "https://plogin.m.jd.com/cgi-bin/mm/dosmslogin"
|
||
h5st = self.get_h5st(code)
|
||
data = self.user_ctw.call(
|
||
'login',
|
||
phone,
|
||
code,
|
||
s_token,
|
||
h5st,
|
||
jd_risk_token_id,
|
||
rsa_modulus
|
||
)
|
||
self.headers["cookie"] = ck
|
||
response = requests.post(url, headers=self.headers, data=data)
|
||
print(response.text)
|
||
self.ck = self.get_cookie()
|
||
return response.json()
|
||
|
||
def get_jd_risk_token_id(self):
|
||
res = self.request_jd_risk_token_id()
|
||
self.jd_risk_token_id = res.split('var jd_risk_token_id = ')[1].strip(";").strip("'")
|
||
|
||
def get_code_res(self):
|
||
send_res = self.send_code()
|
||
if send_res.get("err_code") == 0:
|
||
return 100, {
|
||
"ck": self.ck,
|
||
"s_token": self.s_token,
|
||
"jd_risk_token_id": self.jd_risk_token_id,
|
||
"rsa_modulus": self.rsa_modulus
|
||
}
|
||
else:
|
||
return 101, {
|
||
"ck": "",
|
||
"s_token": "",
|
||
"jd_risk_token_id": "",
|
||
"rsa_modulus": ""
|
||
}
|
||
|
||
@staticmethod
|
||
def my_response(status_code, data):
|
||
return {
|
||
"code": status_code,
|
||
"data": data,
|
||
"msg": "请求成功"
|
||
}
|
||
|
||
def run_get_ck(self, ck, code, s_token, jd_risk_token_id, rsa_modulus):
|
||
login_res = self.request_sms_login(ck, code, s_token, jd_risk_token_id, rsa_modulus)
|
||
if login_res.get("err_code") == 0:
|
||
data = {
|
||
"ck": self.ck
|
||
}
|
||
return self.my_response(status_code=100, data=data)
|
||
else:
|
||
data = {
|
||
"ck": ""
|
||
}
|
||
return self.my_response(status_code=101, data=data)
|
||
|
||
def run_send_code(self):
|
||
try:
|
||
# 获取risk_token_id
|
||
self.get_jd_risk_token_id()
|
||
# 获取登录所需初始sid
|
||
self.get_login_entrance()
|
||
self.get_jcap_sid()
|
||
# 获取验证码
|
||
self.get_api_params()
|
||
captcha_status = self.get_captcha()
|
||
|
||
if not captcha_status:
|
||
print("出现滑块验证")
|
||
# 验证验证码
|
||
check_status = self.check_captcha()
|
||
if check_status:
|
||
print("滑块验证通过,发送验证码")
|
||
status_code, data = self.get_code_res()
|
||
return self.my_response(status_code, data)
|
||
else:
|
||
print("没有出现滑块验证,直接发送验证码")
|
||
status_code, data = self.get_code_res()
|
||
return self.my_response(status_code, data)
|
||
except:
|
||
return self.my_response(status_code=111, data={})
|
||
|
||
|
||
if __name__ == '__main__':
|
||
phone = "13071534209"
|
||
res = LoginSpider(
|
||
phone=phone,
|
||
).run_send_code()
|
||
print(res)
|
||
|
||
|
||
|
||
res = LoginSpider(
|
||
phone=phone,
|
||
).run_get_ck(
|
||
ck="jcap_dvzw_fp=7Hq5_QuYK92sDlclFxhJ8m28JaYU1hDYJ-U-gC59LmJRPuo3ERCbiMGzn5vdc3WcTX0ndWGFZNOs3BDenMr7lw==; guid=a5bb2dc650ab23a2cf5cb58afef19b3f35adba9613421b4cba79aa59ea891803; lang=chs; lsid=503346699194megeguncgrf6gvwb9tdujp2md5p27pkt1737276375048; lstoken=vvwcnbi4;",
|
||
code="315572",
|
||
s_token='vvwcnbi4',
|
||
jd_risk_token_id='SH6IDCXBUWZNAV3SJPRGIXLVNHGLZR6AYHJ3244TGU5Z35SFJU76TAQ3OMNTXE7XA3SBRMPVTZJVW',
|
||
rsa_modulus='B03744DE9EAB28F6FA6B9C8FB1873CF57D42A2B6D382B79B276C2079A42B24C11D641EA642CF62485A632AE244DE6DE05A92A20EEFE8B6C7743F09FCE0BF78E6D614C115CDAEC2F4825F82E06770A2599D69BBADBE678DD25F2E5B9E2D0E3E15BEB749B436860872D30676794D3C3E8C37B71372DE52F223917FA730EC21F047'
|
||
)
|
||
print(res)
|