- 拼多多进单 Cookie 失效即时拦截与告警修复(支持 43001 会话过期等错误识别) - 拼多多订单映射优先使用 out_sku_sn(外部商家编码)作为商品规格与工单匹配核心字段 - 新增 Chrome/Edge 浏览器扩展 (Manifest V3),支持端到端 RSA-OAEP + AES-256-GCM 信封加密自动保活 - 后台店铺管理新增插件配对面板、专属密钥管理与一键导入配置 JSON
220 lines
6.3 KiB
JavaScript
220 lines
6.3 KiB
JavaScript
/**
|
|
* Pinduoduo Cookie Sync Background Service Worker (Manifest V3)
|
|
* 拼多多 Cookie 自动嗅探与加密保活后台服务
|
|
*/
|
|
|
|
try {
|
|
importScripts('crypto.js')
|
|
} catch (e) {
|
|
console.error('导入加密工具失败:', e)
|
|
}
|
|
|
|
const ALARM_NAME = 'pdd_cookie_sync_alarm'
|
|
let debounceTimer = null
|
|
|
|
// 设置扩展图标角标状态
|
|
function setExtensionBadge(text, color) {
|
|
chrome.action.setBadgeText({ text })
|
|
if (color) {
|
|
chrome.action.setBadgeBackgroundColor({ color })
|
|
}
|
|
}
|
|
|
|
// 获取拼多多全量 Cookie 字符串
|
|
async function getPddCookieString() {
|
|
try {
|
|
const cookies = await chrome.cookies.getAll({ domain: 'pinduoduo.com' })
|
|
if (!cookies || cookies.length === 0) {
|
|
return ''
|
|
}
|
|
// 拼接成标准 Cookie 请求头格式
|
|
return cookies.map((c) => `${c.name}=${c.value}`).join('; ')
|
|
} catch (err) {
|
|
console.error('读取拼多多 Cookie 异常:', err)
|
|
return ''
|
|
}
|
|
}
|
|
|
|
// 核心同步逻辑
|
|
async function syncCookieToServer(isManual = false) {
|
|
const config = await chrome.storage.local.get([
|
|
'serverUrl',
|
|
'shopId',
|
|
'syncToken',
|
|
'lastCookieHash',
|
|
'lastSyncAt',
|
|
])
|
|
|
|
const serverUrl = String(config.serverUrl || '').trim().replace(/\/+$/, '')
|
|
const shopId = String(config.shopId || '').trim()
|
|
const syncToken = String(config.syncToken || '').trim()
|
|
|
|
if (!serverUrl || !shopId || !syncToken) {
|
|
setExtensionBadge('CFG', '#faad14') // 待配置
|
|
return {
|
|
success: false,
|
|
message: '请先打开插件弹窗完成服务器地址与店铺配对密钥配置',
|
|
}
|
|
}
|
|
|
|
// 1. 读取当前 Cookie
|
|
const cookieStr = await getPddCookieString()
|
|
if (!cookieStr) {
|
|
setExtensionBadge('OFF', '#8c8c8c') // 未登录
|
|
await chrome.storage.local.set({
|
|
lastStatus: 'offline',
|
|
lastMessage: '未在浏览器中检测到拼多多商家后台登录 Cookie',
|
|
})
|
|
return {
|
|
success: false,
|
|
message: '未检测到拼多多登录 Cookie,请先在当前浏览器中登录 mms.pinduoduo.com',
|
|
}
|
|
}
|
|
|
|
// 2. 计算 Cookie 哈希并进行指纹去重
|
|
const currentHash = await computeSha256(cookieStr)
|
|
const now = Date.now()
|
|
const lastSyncTime = Number(config.lastSyncAt || 0)
|
|
|
|
// 若非手动强制触发,且 Cookie 未变动,且距离上次同步不足 1 小时,跳过重复上报
|
|
if (!isManual && config.lastCookieHash === currentHash && now - lastSyncTime < 3600_000) {
|
|
return {
|
|
success: true,
|
|
skipped: true,
|
|
message: 'Cookie 无实质变动,无需重复上报',
|
|
}
|
|
}
|
|
|
|
try {
|
|
// 3. 请求服务端公钥
|
|
const pkUrl = `${serverUrl}/api/v1/pdd/extension/public-key`
|
|
const pkResp = await fetch(pkUrl, {
|
|
method: 'GET',
|
|
headers: { Accept: 'application/json' },
|
|
})
|
|
|
|
if (!pkResp.ok) {
|
|
throw new Error(`获取公钥失败 HTTP ${pkResp.status}`)
|
|
}
|
|
|
|
const pkJson = await pkResp.json()
|
|
const serverPublicKeySpki = pkJson?.data?.publicKeySpki
|
|
if (!serverPublicKeySpki) {
|
|
throw new Error('服务端返回的 RSA 公钥格式异常')
|
|
}
|
|
|
|
// 4. 构建明文载荷(syncToken 在加密包内外双重保护)
|
|
const payload = {
|
|
shopId,
|
|
syncToken,
|
|
cookie: cookieStr,
|
|
userAgent: navigator.userAgent,
|
|
timestamp: Date.now(),
|
|
nonce: 'ext_' + Math.random().toString(36).slice(2) + Date.now().toString(36),
|
|
}
|
|
|
|
// 5. 执行端到端混合信封加密 (RSA-OAEP + AES-256-GCM)
|
|
const envelope = await encryptPayloadWithEnvelope(payload, serverPublicKeySpki)
|
|
|
|
// 6. 安全回传至服务端
|
|
const syncUrl = `${serverUrl}/api/v1/pdd/extension/sync-cookie`
|
|
const syncResp = await fetch(syncUrl, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'X-Shop-Token': syncToken,
|
|
},
|
|
body: JSON.stringify({ envelope }),
|
|
})
|
|
|
|
const syncJson = await syncResp.json()
|
|
|
|
if (!syncResp.ok || syncJson.code !== 0) {
|
|
const errMsg = syncJson?.message || `服务端返回错误 HTTP ${syncResp.status}`
|
|
throw new Error(errMsg)
|
|
}
|
|
|
|
// 同步成功,更新持久化状态
|
|
await chrome.storage.local.set({
|
|
lastCookieHash: currentHash,
|
|
lastSyncAt: now,
|
|
lastStatus: 'ok',
|
|
lastMessage: syncJson.message || '同步成功并已生效',
|
|
totalOrders: syncJson.data?.totalOrders,
|
|
})
|
|
|
|
setExtensionBadge('OK', '#52c41a') // 绿色成功
|
|
return {
|
|
success: true,
|
|
message: 'Cookie 同步成功!服务端已完成在线校验并持久化生效',
|
|
totalOrders: syncJson.data?.totalOrders,
|
|
}
|
|
} catch (err) {
|
|
const errMsg = err instanceof Error ? err.message : String(err)
|
|
console.error('Cookie 自动同步失败:', errMsg)
|
|
|
|
await chrome.storage.local.set({
|
|
lastStatus: 'error',
|
|
lastMessage: errMsg,
|
|
})
|
|
|
|
setExtensionBadge('ERR', '#ff4d4f') // 红色错误
|
|
return {
|
|
success: false,
|
|
message: errMsg,
|
|
}
|
|
}
|
|
}
|
|
|
|
// 监听拼多多 Cookie 实时变动事件(带防抖延时 3 秒)
|
|
chrome.cookies.onChanged.addListener((changeInfo) => {
|
|
const domain = changeInfo.cookie?.domain || ''
|
|
if (domain.includes('pinduoduo.com')) {
|
|
if (debounceTimer) {
|
|
clearTimeout(debounceTimer)
|
|
}
|
|
debounceTimer = setTimeout(() => {
|
|
syncCookieToServer(false).catch(() => null)
|
|
}, 3000)
|
|
}
|
|
})
|
|
|
|
// 定时任务:每隔 30 分钟进行一次轻量巡检与必要保活上报
|
|
chrome.alarms.onAlarm.addListener((alarm) => {
|
|
if (alarm.name === ALARM_NAME) {
|
|
syncCookieToServer(false).catch(() => null)
|
|
}
|
|
})
|
|
|
|
// 扩展安装或更新时注册定时闹钟
|
|
chrome.runtime.onInstalled.addListener(() => {
|
|
chrome.alarms.create(ALARM_NAME, {
|
|
periodInMinutes: 30,
|
|
})
|
|
// 首次运行尝试同步一次
|
|
syncCookieToServer(false).catch(() => null)
|
|
})
|
|
|
|
// 响应来自 popup.js 的前端交互消息
|
|
chrome.runtime.onMessage.addListener((req, _sender, sendResponse) => {
|
|
if (req.action === 'trigger_sync') {
|
|
syncCookieToServer(true).then(sendResponse).catch((err) => {
|
|
sendResponse({ success: false, message: String(err) })
|
|
})
|
|
return true // 保持异步通信通道
|
|
}
|
|
|
|
if (req.action === 'get_status') {
|
|
getPddCookieString().then((ck) => {
|
|
chrome.storage.local.get(null).then((data) => {
|
|
sendResponse({
|
|
hasCookie: Boolean(ck && ck.length > 20),
|
|
cookieLength: ck ? ck.length : 0,
|
|
storage: data,
|
|
})
|
|
})
|
|
})
|
|
return true
|
|
}
|
|
})
|