Files
order_site/apps/extensions/pdd-cookie-sync/background.js
T
yml2213 9fb27b0ad2 feat(pdd): 拼多多订单支持 out_sku_sn 匹配与浏览器插件加密保活
- 拼多多进单 Cookie 失效即时拦截与告警修复(支持 43001 会话过期等错误识别)
- 拼多多订单映射优先使用 out_sku_sn(外部商家编码)作为商品规格与工单匹配核心字段
- 新增 Chrome/Edge 浏览器扩展 (Manifest V3),支持端到端 RSA-OAEP + AES-256-GCM 信封加密自动保活
- 后台店铺管理新增插件配对面板、专属密钥管理与一键导入配置 JSON
2026-09-14 12:24:48 +08:00

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
}
})