# 拼多多跳转与复制的来源、实现和验证边界

核查日期：2026-10-19。参考入口：[用户指定页面](http://bz.5138zhuan.com/index-new.html)。只读取 HTML、JS 与静态资源，没有在浏览器执行参考站脚本，也没有请求它的取码、提交、标记使用接口。

## 实际源码证据

HTML 原样保存于 `research/reference/index-new.html`，第 681 行附近的 `onekeyBtn` 对应「智能直达」按钮，第 752 行加载 `/linux2.js?v=24`。此文件经过字符串表与控制流混淆，原样保存于 `research/reference/linux2.v24.js`。

`research/reference/static_decode.py` 只解析受限格式的 Base64 字符串字面量，使用自编解码算法和有限枚举计算旋转量 92；不会 `eval`、`exec`、导入或运行第三方 JS。结果保存在 `linux2.string-table.json` 和只供阅读的 `linux2.decoded-evidence.txt`。表项 `0x21a` 的内容原样为：

```text
pinduoduo://com.xunmeng.pinduoduo/search_result.html?search_key=
```

解码后 `onekeyJump()` 与 `jumpAndMark()` 都将数字 `encodeURIComponent` 后拼到这一前缀。该证据能证明参考页面使用此 Scheme；**不能证明所有拼多多版本、浏览器与操作系统都成功唤起**，也不是拼多多官方稳定接口承诺。

参考行为与本项目处理：

| 参考脚本观察 | 本项目决定 |
| --- | --- |
| `onekeyJump()` 先请求 `api.php?action=onekey` 得到第三方分配数字，再复制并跳转。 | 仅处理用户图片本地识别的数字，不请求该 API，也不提交数字给参考站。 |
| `jumpAndMark()` 可能 POST `api.php?action=mark_used`，存取 `lastJumpedData`。 | 不复制该站业务流程、共享列表或使用标记。 |
| `navigator.clipboard.writeText`，失败后创建 textarea 并 `execCommand('copy')`。 | 同步复制尝试优先，失败则在当前点击中立即启动 `writeText`；最终只按 API 结果报告复制成功与否。不读剪贴板。 |
| `isWeChat()` 对微信提示手动去拼多多粘贴。 | 保留可单独复制的数字、显式链接与手动打开说明。微信／QQ等内置浏览器可能拦截唤起。 |
| `isOldAndroid()` 判断 Android 版本 ≤9；该分支使用 100ms 延迟 location + 隐藏 iframe。其他设备常使用 300ms 延迟。 | 移除定时器和 iframe 多次唤起；识别成功后直接请求一次导航；受限时实际点击补救按钮再请求，不循环重试，不依据系统版本臆测浏览器能力。 |
| 没有观察到单独 iOS 专用 Scheme、Android Intent、或 HTTPS Universal Link 分支。 | 默认 Scheme 沿用源证据；其他方式显式标为补充兼容尝试，不能宣称来自原网页。 |
| HTML 的图片识别按钮现指向小程序说明，覆盖了旧 `PddOcr.pick/open`。 | 本项目完全独立本地 OCR；不以参考页当前识图入口为本地能力证据。 |

此外，`ts.v1.js` 的独立静态解码脚本是 `static_decode_ts.py`（字符串表旋转量 142）。旧脚本包含 Canvas 像素读取、连通域、7×5 网格特征、字形原型与 `PddOcr.recognizeImage/extractPddInviteCode`；本次读取未发现该旧文件中的 fetch/XHR 调用。但后续 HTML 覆盖其公开入口，因此「旧文件有本地 OCR」与「当前页面识图按钮运行本地 OCR」是两回事。它还有清除非数字、截断候选及 8–9 位限制，与本项目完整 7–9 位、无空格拼接的要求不符，未复制进应用。

`wechat-guard.20260917.js` 另有微信遮罩和 50 秒后前往公众号文章的行为，本项目也未复用。

## 当前模块接口

`web/deeplink.js` 导出：

```js
validateCode('159965822'); // true
const links = getLinks('159965822'); // { code, native, https, intent }
const copied = copyCode('159965822'); // Promise<boolean>
const result = openCode('159965822', { variant: 'native' });
// result = { url, requested, copied: Promise<boolean> }
```

只有完整 7–9 位 ASCII 字符串可通过。空格、尾换行、全角数字、Unicode 数字、10 位数字、URL 注入、数字类型都拒绝，前导零保留。不会 `trim()`、移除空格或从较长字符串截取。

`native` 使用上面的原 Scheme。`https` 构造为 `https://mobile.yangkeduo.com/search_result.html?search_key=数字`，只是网页版搜索兜底，不保证该页未登录可搜索、保留查询或唤起 App。`intent` 按 Chrome Android 的 Intent URI 语法构造，固定 Scheme/host/package 和编码的 HTTPS fallback；它是额外尝试，不是参考页提供的官方链接，也未做拼多多真机验证。浏览器不一定支持 Intent URL。补充 HEAD 探测对网页版路径收到 HTTP 200，但 `Content-Type: text/plain`、长度仅 2 B，故不能把状态码视为搜索功能验证；未附带用户数字查询。

r6 按用户要求，在 OCR 得到唯一符合红色场景条件的数字后直接调用 `openCode()`，不等待复制 Promise，也不再要求确认勾选。异步 OCR 可能超过浏览器临时用户激活期限，所以自动唤起只是一次尝试：页面仍在前台时约1.5秒后显示一键补救按钮，该按钮在真实点击处理函数中立即调用同一接口。多组合格数字由用户点击选择，点击后立即打开；合成示例不自动打开。模块没有 `fetch`、XHR、图片上传、自动点击或隐藏 iframe。页面的定时器只显示补救按钮，不重复导航。

`requested` 仅表示已调用导航，不表示 App 已安装或跳转成功。可见数字和独立复制按钮始终可用，自动打开未离开页面时显示直接打开按钮及 HTTPS 备选链接。复制 Promise 尚未完成时就离开页面，异步复制可能受浏览器影响；需要可靠粘贴时可先单独点击复制再打开 App。

浏览器依据：Chrome 说明外部 App 启动需要用户手势且可能因 Activity／Intent 不匹配失败，并提供 `browser_fallback_url` 语法。[Chrome Android Intent](https://developer.chrome.com/docs/android/intents)。WebKit 说明临时激活可能超时或被某些 API 消耗；没有理由依赖参考脚本中的延迟唤起。[WebKit 用户激活](https://webkit.org/blog/13862/the-user-activation-api/)。Safari Clipboard 写入需要安全上下文和用户手势；`execCommand` 是老兼容路径，不保证成功。[WebKit Clipboard](https://webkit.org/blog/10855/async-clipboard-api/)。

## 已做与未做的测试

`node --test tests/deeplink.test.mjs` 覆盖：严格数字验证、前导零、固定目标与 Intent fallback 编码、复制未完成时导航已同步调用、复制权限拒绝、非法输入无副作用、老复制路径 DOM 清理与焦点恢复。测试使用隔离环境，不实际打开第三方站或 App。

桌面测试无法证明 iPhone、各国产 Android 浏览器和微信内置浏览器会成功唤起拼多多；需要在真实手机、实际安装的拼多多版本上实际观察目标搜索页。r6 已有用户实际安卓手机的自动唤起证据，详见[本轮验收](acceptance.md)与[自动跳转审查](review-r6-auto-open.md)；不能推广到所有内核。无法通过网页可靠判断 App 是否安装，也不将页面失焦等启发式信号当作成功证据。
