Files
kami_jd_ck/babel_channel/app.py
danial aa25b9e17e ci: 更新 Docker 部署配置和环境设置
- 修改 .drone.yml 中的 Docker Compose 命令,使用 jd_bind_card_server 代替 bind_card
- 更新 .gitignore 文件,使用通配符路径以更全面地忽略文件和目录
- 在 babel_channel/app.py 中,将 unidbg_url 的 IP 地址改为服务名 unidbg_boot_server
- 修改 unidbg_boot_server/Dockerfile,将暴露的端口从 8080 改为 9999
2025-04-13 23:02:56 +08:00

550 lines
20 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import ctypes
import hashlib
import json
import time
import traceback
from urllib.parse import parse_qs
import logging
import logging.config
import os
import requests
from flask import Flask, request, jsonify
from flask_cors import CORS
from logger import get_logger
from spider import AppStoreSpider, GoodsAppleCard, GameArea, DeleteOrder, LoginSpider
app = Flask(__name__)
# 跨域
CORS(app)
# 配置日志
logging.config.fileConfig('logging.conf')
logger = logging.getLogger('app')
# 苹果权益充值
@app.route('/jd/app/store', methods=['GET', 'POST'], strict_slashes=False)
def app_store():
if request.method == 'GET':
return 'app_store'
elif request.method == 'POST':
# 接收参数
data = json.loads(request.get_data())
face_price = data.get("face_price")
order_num = data.get("order_num")
cookies = data.get("cookies")
# 打印参数日志
logger.info(f"订单ID{order_num}cookies{cookies}")
logger.info(f"订单ID{order_num}card_pwd{face_price}")
app_store = AppStoreSpider(
cookies=cookies,
face_price=face_price,
order_num=order_num
)
code, res = app_store.run()
item = {
"code": code,
"data": res
}
logger.info(f"订单ID{order_num},最终返回日志:{item}")
# 打印响应日志
return jsonify(item)
# 查卡密
@app.route('/jd/query/card', methods=['GET', 'POST'], strict_slashes=False)
def query_card():
if request.method == 'GET':
return 'query_card'
elif request.method == 'POST':
# 接收参数
data = json.loads(request.get_data())
jd_order_num = data.get("jd_order_num")
cookies = data.get("cookies")
# 打印参数日志
logger.info(f"订单ID{jd_order_num}cookies{cookies}")
app_store = AppStoreSpider(
cookies=cookies,
order_num=jd_order_num
)
code, res = app_store.get_card_secret(jd_order_num)
item = {
"code": code,
"data": res
}
logger.info(f"订单ID{jd_order_num},最终返回日志:{item}")
# 打印响应日志
return jsonify(item)
# 获取sku参数
@app.route('/jd/sku', methods=['GET', 'POST'], strict_slashes=False)
def game_area():
if request.method == 'GET':
return 'game_area'
elif request.method == 'POST':
data = json.loads(request.get_data())
cookies = data.get("cookies")
sku_id = data.get("sku_id")
details = GameArea.get_details(cookies, sku_id)
data = details.get("result", {})
item = {
"code": 100 if data else 110,
"data": data
}
return jsonify(item)
# 账号类下单
@app.route('/jd/goods/store', methods=['GET', 'POST'], strict_slashes=False)
def goods_store():
if request.method == 'GET':
return 'goods_store'
elif request.method == 'POST':
data = json.loads(request.get_data())
# 参数
face_price = data.get("face_price")
order_num = data.get("order_num")
cookies = data.get("cookies")
brand_id = data.get("brand_id")
sku_id = data.get("sku_id")
username = data.get("username")
game_srv = data.get("gamesrv")
game_area = data.get("gamearea")
# 类型 1username账号充值2游戏区服充值
recharge_type = data.get("recharge_type")
# 打印参数日志
logger.info(f"订单ID{order_num}cookies{cookies}")
logger.info(f"订单ID{order_num}card_pwd{face_price}")
app_store = GoodsAppleCard(
cookies=cookies,
face_price=face_price,
order_num=order_num,
sku_id=sku_id,
brand_id=brand_id,
username=username,
game_srv=game_srv,
game_area=game_area,
recharge_type=recharge_type,
)
code, res = app_store.run()
item = {
"code": code,
"data": res
}
logger.info(f"订单ID{order_num}goods_store最终返回日志{item}")
# 打印响应日志
return jsonify(item)
def r(data_string):
def int_overflow(val):
maxint = 2147483647
if not -maxint - 1 <= val <= maxint:
val = (val + (maxint + 1)) % (2 * (maxint + 1)) - maxint - 1
return val
def unsigned_right_shitf(n, i):
# 数字小于0则转为32位无符号uint
if n < 0:
n = ctypes.c_uint32(n).value
# 正常位移位数是为正数但是为了兼容js之类的负数就右移变成左移好了
if i < 0:
return -int_overflow(n << abs(i))
# print(n)
return int_overflow(n >> i)
char_list = []
aae = ['K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'U', 'V',
'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'e', 'f', 'g', 'h',
'i', 'j', 'k', 'l', 'm', 'n', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/']
bArr = data_string.encode('utf-8')
for i in range(0, len(bArr), 3):
bArr2 = [None for i in range(4)]
b2 = 0
for i2 in range(0, 3):
i3 = i + i2
if i3 <= len(bArr) - 1:
bArr2[i2] = b2 | unsigned_right_shitf((bArr[i3] & 255), ((i2 * 2) + 2))
b2 = unsigned_right_shitf(((bArr[i3] & 255) << (((2 - i2) * 2) + 2)) & 255, 2)
else:
bArr2[i2] = b2
b2 = 64
bArr2[3] = b2
for i4 in range(4):
if bArr2[i4] <= 63:
char_list.append(aae[bArr2[i4]])
else:
char_list.append("=")
return "".join(char_list)
def encode_cipher(cipher_dict):
for k, v in cipher_dict.items():
cipher_dict[k] = r(v)
def gen_cipher_ep(uuid, ts):
"""
:param uid:
:param aid:
:param open_udid:
:param ts:
:return:
"""
cipher_dict = {
"d_model": "SM-N9760",
"wifiBssid": "unknown",
"osVersion": "9",
"d_brand": "samsung",
"screen": "960*540",
"uuid": uuid,
"aid": uuid,
}
encode_cipher(cipher_dict)
data_dict = {
"hdid": "JM9F1ywUPwflvMIpYPok0tt5k9kW4ArJEU3lfLhxBqw=",
"ts": ts,
'ridx': -1,
'cipher': cipher_dict,
'ciphertype': 5,
"version": "1.2.0",
'appname': "com.jingdong.app.mall"
}
ep = json.dumps(data_dict, separators=(',', ':'))
return ep
def get_pay_sign(order_id, face_price):
app_id = "jd_android_app4"
pay_app_key = "e53jfgRgd7Hk"
input_str = f"{app_id};{order_id};37;{face_price};{pay_app_key}"
# 获取 MD5 实例
input_bytes = input_str.encode("GBK")
md5_hash = hashlib.md5()
md5_hash.update(input_bytes)
return md5_hash.hexdigest()
def get_sign(func, body, uuid, version):
headers_jd = {
'Content-Type': 'application/json'
}
payload = json.dumps({
"func": func,
"body": body,
"uid": uuid,
"platform": "android",
"version": version
})
unidbg_url = "http://unidbg_boot_server:9999/api/jd/encrypt"
response_jd = requests.request("POST", url=unidbg_url, headers=headers_jd, data=payload)
res = response_jd.json()
params = parse_qs(res["data"])
formatted_params = {key: value[0] for key, value in params.items()}
return formatted_params
def my_json(code, data, msg):
return {
"code": code,
"data": data,
"msg": msg
}
@app.route('/api/v1/jd/wx/plat_pay_channel', methods=["GET", "POST"], strict_slashes=False)
def plat_pay_channel():
if request.method == "GET":
return "okk"
elif request.method == "POST":
import uuid
# 接收参数
data = json.loads(request.get_data())
app.logger.info(f"接收到的请求数据: {data}")
# 获取数据
order_id = data.get("order_id")
face_price = data.get("face_price")
face_price = str("{:.2f}".format(float(face_price)))
pay_id = data.get("pay_id")
cookies = data.get("cookies")
# 打印日志
app.logger.info(f"获取微信app端支付参数")
app.logger.info(f"cookie{cookies}")
app.logger.info(f"order_id{order_id}")
app.logger.info(f"face_price{face_price}")
app.logger.info(f"pay_id{pay_id}")
headers = {
"Host": "api.m.jd.com",
"charset": "UTF-8",
"user-agent": "okhttp/3.12.1;jdmall;android;version/11.1.0;build/98139",
"cache-control": "no-cache",
"content-type": "application/x-www-form-urlencoded; charset=UTF-8",
"cookie": cookies
}
url = "https://api.m.jd.com/client.action"
func = "platPayChannel"
version = "11.1.0"
uuid = str(uuid.uuid4()).replace("-", "")
ts = int(time.time() * 1000)
ep = gen_cipher_ep(uuid, ts)
pay_sign = get_pay_sign(order_id=order_id, face_price=face_price)
body = '{"appId":"jd_android_app4","client":"android","fk_aid":"%s","fk_appId":"com.jingdong.app.mall","fk_latitude":"NfUaIOrNMes=","fk_longtitude":"NfUaIOrNMes=","fk_terminalType":"02","fk_traceIp":"26.26.26.1","hasCyberMoneyPay":"0","hasHuaweiPay":"0","hasOCPay":"0","hasUPPay":"0","orderId":"%s","orderPrice":"%s","orderType":"37","orderTypeCode":"0","origin":"native","payId":"%s","paySign":"%s","paySourceId":"2","payablePrice":"%s","sdkToken":"jdd016CSEHGQ3IPOGEXVOBUPDLTBSKZZUMESDDOP4PPRC2E2R7CC2K4LXQN63E6A3P3Y76GN4M5TMPIZGOWYQJG4MO6ET75VQFVQL2MIYRFQ01234567","source":"jdapp","style":"normal","supportNFC":"1"}' % (
uuid, order_id, face_price, pay_id, pay_sign, face_price
)
formatted_params = get_sign(func, body, uuid, version)
params = {
"functionId": func,
"clientVersion": version,
"build": "98139",
"client": "android",
"partner": "wandoujia",
# "eid": "eidAf760812200s2xOZPVA0sThGrvhABq1zrPMTmUOM1tv8FFg1FjE8yRJtYdV/UFhJuIkVZbrk/xl XPeoFQTNgMiYJpXeeyACCVPbt0/3R7R3Gd 8y",
"sdkVersion": "28",
"lang": "zh_CN",
"harmonyOs": "0",
"networkType": "wifi",
# "uts": "0f31TVRjBSsqndu4/jgUPz6uymy50MQJ8fZr7wet7pLPYx9jEXMpd8VCD64sq/eBbsH5zZBXnKZMkN1vxnjOrpfx7GiQBINsuAELLpjOiZsCHkTDoRW/d9talOxyn2bo1YZLq8uq5Kdx/Fd7diA023Qwr+5V5TzeOnca3cc6QzMFh8p+DS2gR6Bimgz0BiNqbeN1q1NA9rJ/t5QOAIH9EA==",
"uemps": "0-0",
"ext": "{\"prstate\":\"0\",\"pvcStu\":\"1\"}",
"ef": "1",
"ep": ep,
"st": formatted_params["st"],
"sign": formatted_params["sign"],
"sv": formatted_params["sv"]
}
data = {
"body": body,
"": ""
}
client_res = None
# try:
response = requests.post(
url=url,
params=params,
headers=headers,
data=data
)
client_res = response.json()
app.logger.info(f"订单id{order_id}获取支付参数res返回{client_res}")
# except Exception as e:
# app.logger.info(traceback.format_exc())
# 不支持该支付类型
if client_res.get("errorCode") == "-2":
return my_json(code=20014, data={}, msg="订单类型不支持")
# ck失效
if client_res.get("errorCode") == "3":
return my_json(code=20002, data={}, msg="ck失效")
# 订单失效
if client_res.get("errorCode") == "-3":
return my_json(code=20005, data={}, msg="订单失效")
# 订单与下单账号不同
if client_res.get("errorCode") == "-5":
return my_json(code=20015, data={}, msg="订单与下单账号不匹配")
# 订单已取消
if client_res.get("errorCode") == "-100":
return my_json(code=20006, data={}, msg="订单已支付完成或已取消")
# 订单已支付完成或已取消
if client_res.get("mcashierConfirmInfo", {}).get("errorCode", "") == "-100":
return my_json(code=20006, data={}, msg="订单已支付完成或已取消")
# 获取微信支付参数
url = "https://api.m.jd.com/client.action"
headers = {
"Host": "api.m.jd.com",
"charset": "UTF-8",
"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",
"cache-control": "no-cache",
"content-type": "application/x-www-form-urlencoded; charset=UTF-8",
"cookie": cookies
}
import uuid
uuid = str(uuid.uuid4()).replace("-", "")
ts = int(time.time() * 1000)
ep = gen_cipher_ep(uuid, ts)
pay_sign = get_pay_sign(order_id=order_id, face_price=face_price)
sdk_token = "jdd01XYE6SS5ZXXG7F74OZC3PKG5LWF7W3GTVLTSUH56A2YAUXOT6NEEX4TRT3I3XDDYO7NQSIV4BSK3XGKWMROTBRKO4ENETDD4IEHFWZHA01234567"
body = '{"appId":"jd_android_app4","backUrl":"","client":"android","orderId":"%s","orderPrice":"%s","orderType":"37","orderTypeCode":"0","origin":"native","payId":"%s","paySign":"%s","sdkToken":"%s","source":"jdapp"}' % (
order_id, face_price, pay_id, pay_sign, sdk_token)
func = "platWXPay"
version = "11.0.0"
formatted_params = get_sign(func, body, uuid, version)
params = {
"functionId": "platWXPay",
"clientVersion": "11.0.0",
# "build": "97235",
"client": "android",
"partner": "huawei",
# "eid": "eidAd2c08121b3s9eOeub59TR2G1LS3GtRpmFHjnr/5tBkXKUdbZOahaF5ejedAIDQFfwVV2GDYUzftxnEQRjdhD5bpPNR4B0qK9tPJ 5liDjiHAhDR3",
"sdkVersion": "28",
"lang": "zh_CN",
"harmonyOs": "0",
"networkType": "wifi",
# "uts": "0f31TVRjBSsqndu4/jgUPz6uymy50MQJVJk5AARu8sQVESbyLWRZZNMf5XbN+023PgE2PL4I0aLSvISDbN3u1a1oIB1KTvzrqacX+46wkkSjXxvvYKogR9YCbfnMf2pdC5H/VDcJc2u6uHTxPVwvhLoJ8vX8cN45ZijAbikou9B5o2KTvMTzCfrSYgi3+mls/cA+6k+Ao5sFIIdtimZ6bw==",
"uemps": "0-0",
"ext": "{\"prstate\":\"0\",\"pvcStu\":\"1\"}",
"ef": "1",
"ep": ep,
"st": formatted_params["st"],
"sign": formatted_params["sign"],
"sv": formatted_params["sv"]
}
data = {
"body": body,
}
pay_channel_res = None
for i in range(3):
try:
response = requests.post(
url=url,
params=params,
headers=headers,
data=data,
timeout=2
)
pay_channel_res = response.json()
print(pay_channel_res)
app.logger.info(f"订单id{order_id},获取微信支付参数返回:{pay_channel_res}")
break
except Exception as e:
app.logger.info(traceback.format_exc())
continue
# 当前支付方式不可用,建议选择其他支付方式
if pay_channel_res.get("errorCode") == "-1":
return my_json(code=60001, data={}, msg="爬虫异常")
# 加密算法失效
if pay_channel_res.get("code") == "600":
return my_json(code=60001, data={}, msg="加密算法异常")
# 不支持该支付类型
if pay_channel_res.get("errorCode") == "-2":
return my_json(code=20014, data={}, msg="订单类型不支持")
# ck失效
if pay_channel_res.get("errorCode") == "3":
return my_json(code=20002, data={}, msg="ck失效")
# 订单失效
if pay_channel_res.get("errorCode") == "-3":
return my_json(code=20005, data={}, msg="订单失效")
# 订单与下单账号不同
if pay_channel_res.get("errorCode") == "-5":
return my_json(code=20015, data={}, msg="订单与下单账号不匹配")
# 订单已取消
if pay_channel_res.get("errorCode") == "-100":
return my_json(code=20006, data={}, msg="订单已支付完成或已取消")
# 订单已支付完成或已取消
if pay_channel_res.get("mcashierConfirmInfo", {}).get("errorCode", "") == "-100":
return my_json(code=20006, data={}, msg="订单已支付完成或已取消")
# 获取数据
pay_info = pay_channel_res["payInfo"]
item = {
"pay_order_id": pay_channel_res["payOrderId"],
"pay_id": pay_channel_res["payId"],
"ts": pay_info["timeStamp"],
"package_name": pay_info["package"],
"pay_enum": pay_info["payEnum"],
"jd_pay_id": pay_info["jdPayId"],
"sign": pay_info["sign"],
"pre_pay_id": pay_info["prepayId"],
"partner_id": pay_info["partnerId"],
"nonce_str": pay_info["nonceStr"],
}
print(item)
return my_json(
code=2000,
data=item,
msg="请求成功"
)
@app.route('/api/v1/jd/delete_order', methods=["GET", "POST"], strict_slashes=False)
def delete_order():
if request.method == "GET":
return "okk"
elif request.method == "POST":
# 接收参数
data = json.loads(request.get_data())
app.logger.info(f"接收到的请求数据: {data}")
# 获取数据
cookie = data.get("cookie")
order_id = data.get("order_id")
delete_res = DeleteOrder(
cookie=cookie,
order_id=order_id
).run()
if isinstance(delete_res.get("body"), bool):
code = 2000
else:
code = 2001
return my_json(
code=code,
data={},
msg="请求成功"
)
@app.route('/jd/sms/code', methods=["GET", "POST"], strict_slashes=False)
def get_code():
if request.method == "GET":
return "okk"
elif request.method == "POST":
data = json.loads(request.get_data())
# 参数
phone = data.get("phone")
res = LoginSpider(
phone=phone,
).run_send_code()
logger.info(f"发送验证码返回:{res}")
# 打印响应日志
return jsonify(res)
@app.route('/jd/sms/login', methods=["GET", "POST"], strict_slashes=False)
def sms_login():
if request.method == "GET":
return "okk"
elif request.method == "POST":
data = json.loads(request.get_data())
# 参数
phone = data.get("phone")
ck = data.get("ck")
code = data.get("code")
s_token = data.get("s_token")
jd_risk_token_id = data.get("jd_risk_token_id")
rsa_modulus = data.get("rsa_modulus")
res = LoginSpider(
phone=phone,
).run_get_ck(
ck=ck,
code=code,
s_token=s_token,
jd_risk_token_id=jd_risk_token_id,
rsa_modulus=rsa_modulus
)
logger.info(f"短信登录返回:{res}")
# 打印响应日志
return jsonify(res)
@app.route('/')
def index():
logger.info('访问首页')
return 'Hello World'
@app.route('/api/endpoint')
def api_endpoint():
try:
logger.info('处理API请求')
# ... existing code ...
except Exception as e:
logger.error(f'API请求处理失败: {str(e)}')
return {'error': str(e)}, 500
if __name__ == '__main__':
app.run(host="0.0.0.0", port=8289)