背景Vinte 是欧洲增长最快的二手/电商交易平台之一,主要市场在欧洲,其中法国和英国是其规模最大、最核心的两个国家,其次涵盖德国、意大利、波兰、荷兰和西班牙等欧洲主要经济体,同时也在积极拓展北美(美国)市场。做欧洲电商或者跨境交易的朋友,往往需要多个 Vinte 账号。但登录时,却不得不面对法国/英国等欧洲手机验证码的问题。iOS 注册的账号权重高,更抗封,但登录时需要短信验证码,所以需要用 SyncMeIn 来同步登录态,绕过验证码的限制。
由于苹果设备的唯一标识符机制,比如 IDFA 或者其他硬件信息,让 Vinte 系统认为账号更可信,所以权重高,不容易被封。此外,iOS 系统的封闭性可能让账号行为更稳定,减少被封的风险。 但是绝大部分身在国内的 Vinte 卖家,并没有长期使用的法国/英国等欧洲手机号。导致注册养号后,无法使用注册时的手机号接收验证码,从而在新设备完成登录。这时候 SyncMeIn 的作用就是同步已登录的 cookie,绕过登录验证。
正是这个痛点,促成了这个工具诞生。
用户背后更深层需求是希望高效管理多个账号,提高业务效率,同时降低被封带来的损失。借助 SyncMeIn 能够批量管理账号,提升运营效果。
具体操作由于手机 App 还在审核,这里以 PC 端抓包为例。
下载安装 PC 端首先,在电脑下载安装 SyncMeIn PC 版,并安装根证书,详细步骤:《SyncMeIn(ProxyPin)安装根证书详细步骤 》这一步的目的是将苹果手机的流量转发到电脑,从而分析抓取其中的 cookie,这是目前最稳定可靠的方式。
⚠️ 脚本口令是 SyncMeIn 特有的分享方式,所以请下载上文中的 SyncMeIn 客户端,而不是 proxypin 客户端。 SyncMeIn 基于 proxypin 项目,针对跨境卖家需求,站在巨人的肩膀上,实现了口令分享脚本等一系列改进,感谢原项目作者。
设置代理由于 Vinte 网站需要通过纯净欧洲 ip 访问(法国/英国账号建议分别走对应国家住宅 ip),这里需要多一步设置,让流量走🇫🇷代理。
按照下图步骤填写🇫🇷代理服务器信息
以 v2 为例,这里有两个需要注意的点
点击”手机”图标,可以看到电脑的局域网 IP 和端口,用于填写到苹果手机
在苹果手机依次点击下图标注的位置,完成抓包配置:
手机端安装证书并信任在苹果手机安装并信任证书
按照下图操作指引完成安装和信任操作后,打开上图中的”启用 HTTPS 代理”开关,应该可以在软件界面看到捕获到的手机网络流量
用 SyncMeIn 口令 安装 Vinte 脚本先进入脚本界面
点击创建脚本,输入如下内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 // 在请求到达服务器之前,调用此函数,您可以在此处修改请求数据 // e.g. Add/Update/Remove:Queries、Headers、Body async function onRequest (context, request ) { //URL queries // request.queries["name"] = "value"; //Update or add Header // request.headers["Authorization"] = "Bearer 14ce021359b5470917ad75cd3c545258f4459cdb"; if ( request.url.indexOf("https://api.vinted.com/api/v2/products/aggregate/" ) === 0 ) { if (request.queries["what" ] === "SyncMein" ) { const at = request.headers["Authorization" ].replace("Bearer " , '' ); // console.log(at); await pushCookie(context, ['access_token=' + at]); } } // Update Body use fetch API request,具体文档可网上搜索fetch API //request.body = await fetch('https://www.baidu.com/').then(response => response.text()); return request; } async function pushCookie (context, cookieArr ) { const data = JSON .stringify({ cookieArr, "domain" : "vinted.com" }); const options = { method: 'POST' , headers: { 'Content-Type' : 'application/json' , 'Authorization' : 'Bearer ' + context.token }, body: data }; // console.log('options: ', data, context.token) await fetch('https://kainy.cn/SyncMein/api/cookie/update' , options) .then(res => res.text()) .then(result => console .log('同步完成,可前往 SyncMein 浏览器插件合并 cookie ' , result)) .catch(error => console .log('error' , error)); } async function onResponse (context, request, response ) { // 拉取云端 cookie 到本地 if (request.url.indexOf('https://www.vinted.com/oauth/token' ) === 0 ) { const requestBody = typeof request.body === 'string' ? request.body : '' ; if (requestBody.indexOf('username' ) > -1 ) { response.statusCode = 200 ; response.headers['Content-Type' ] = 'application/json' ; response.body = JSON .stringify({ "code" : 0 }); return response; } if (requestBody.indexOf('grant_type' ) > -1 ) { const res = await pullCookie(context, 'vinted.com' ); response.statusCode = 200 ; response.headers['Content-Type' ] = 'application/json' ; response.body = JSON .stringify({ "access_token" : res.cookies.find(c => c.name === 'access_token' )?.value, "token_type" : "bearer" , "expires_in" : 3599 , "refresh_token" : res.cookies.find(c => c.name === 'refresh_token' )?.value, "scope" : "public" , "created_at" : Math .floor(Date .now() / 1000 ) }); return response; } } if (request.url.indexOf('https://api.vinted.com/oauth2/access_token' ) === 0 && request.body.indexOf('magic_link_token' ) > -1 ) { const res = await pullCookie(context, 'vinted.com' ); if (res.status === 'success' ) { const cookieArr = getSetCookieArr(res.cookies) console .log('setCookieArr: ' , request.body, res.cookies) response.headers['Set-Cookie' ] = cookieArr; response.statusCode = 200 ; const userId = Array .isArray(res.cookies) ? ((res.cookies.find(c => c.name === 'user_id' ) || {}).value) : undefined ; const accessToken = Array .isArray(res.cookies) ? ((res.cookies.find(c => c.name === 'access_token' ) || {}).value) : undefined ; response.body = JSON .stringify({ "username" : "kainyguo" , "user_id" : userId, "access_token" : accessToken, "expires_in" : 2592000 , "token_type" : "Bearer" , "scope" : "read" , "refresh_token" : "bcd629304990557d9f5d2f5ef6d40704e13947fa" }) return response; } else { console .log('获取cookie失败' ) } } // 返回修改后的响应 return response; } function getSetCookieArr (cookies ) { const maxAge = 31536000 ; // 一年的秒数 return cookies.map(cookie => { return `${cookie.name} =${cookie.value} ; domain=.vinted.com; Path=${cookie.path || "/" } ; Max-Age=${maxAge} ;${cookie.httpOnly ? " HttpOnly;" : "" } Secure; SameSite=None` ; }); } async function pullCookie (context, domain ) { const options = { method: 'GET' , headers: { 'Content-Type' : 'application/json' , 'Authorization' : 'Bearer ' + context.token // 抓包软件和插件登录同一个 SyncMein 账号,就可获取插件推送的电脑 CK 信息 } }; console .log('pullCookie options: ' , options, context.token) const rs = await fetch('https://kainy.cn/SyncMein/api/cookie/get?domain=' + domain, options) const text = await rs.text(); console .log('拉取云端cookie完成: ' , text.substring(0 , 200 )) try { return JSON .parse(text); } catch (e) { // Fix malformed JSON with unescaped quotes in value field // Find and properly escape JSON in value field const fixedText = text.replace(/"value":"(\{.*?\})"/g , (match, p1 ) => { const escapedValue = JSON .stringify(p1); return `"value":${escapedValue} ` ; }); console .log('Fixed JSON:' , fixedText.substring(0 , 200 )); return JSON .parse(fixedText); } }
在脚本窗口,点右上角”日志”按钮,打开日志窗口
然后在手机访问 Vinte 网站,通过小红点确保已登录,
确认完毕后,在登录页选择 email 登录,输入任意登录信息后提交。
这个页面只是为了告诉脚本获取 cookie,实际并不存在,所以会打开个空白页面提示”Safari 浏览器打不开该页面… “,无需理会
只要回到刚才打开的”日志”窗口,如果出现如下图的 log,则说明 cookie 已提取成功:
同步完成,可前往 SyncMeIn 插件合并 cookie {"status":"success","message":"Cookie updated successfully"}
这时候,我们就可以到浏览器扩展 中,合并已提取的 cookie,获得登录态。这里需要注意:扩展中和客户端登录的必须是同一个 SyncMeIn 账号,才能同步成功。 [忘记截屏了,以下两张截图借用煤炉的]
手机端界面
以上就完成了从苹果手机中,提取 ck,从而完成账号同步到 PC 端的操作。
当然,以上方法并不限于苹果手机,任何可以抓包的设备都可以使用。只不过苹果设备的唯一标识符机制,让 Vinte 系统认为账号更可信,权重更高,不易被封,所以比较常用。
这个工具的用途也不止于 Vinte,任何需要登录态的网站都可以使用。只要按需求,编写对应脚本即可。有需求的老板可以提需求给我。
常见问题 点合并后,没有登上账号清除一下浏览器缓存,然后重新点合并。
点桌面软件的停止按钮,停止抓包,然后刷新页面。
点更新后,还是登录上一次同步的账号偶现问题是:手机上重新登录,并访问推送网址,但是在电脑点合并后,还是旧的账号。清缓存也没用。
解决方法:点击 syncmein 浏览器插件面板底部的 vinte.jp 后面的 ❌ ,清除 cookie 后重试。
Vinte 切换账号后网页仍显示旧账号Vinte 网页端对登录态做了较激进的本地缓存,单纯合并 cookie 后如果未生效,需要:
关闭所有 vinte.jp 标签页(包括后台挂起的) 在浏览器扩展面板点 ❌ 清除 vinte.jp 域 cookie 重新点”合并” 用无痕窗口打开 vinte.jp 验证,确认无误后再切回正常窗口 多账号矩阵切换顺序建议Vinte 对短时间内频繁切换设备指纹较敏感,建议:
同一台 PC 每天切换不超过 3 个账号 切换间隔大于 30 分钟 切换前先停止抓包,避免代理流量混入风控特征 优先用”电脑→手机”方向把养号好的 PC 登录态推到新 iPhone,再日常用手机操作 windows 客户端启动不了有的时候双击图标没有反应
我们任务栏,鼠标右键点击,弹出菜单中,打开”任务管理器”
找到”ProxyPin”这一项,右键结束进程,然后重新打开,看是否正常启动。
如果还是无法启动,再右键点击”最大化”,一般就可以找到主窗口了。
(注:本文仅作技术研究,请遵守 Vinte 用户协议)