Shopify Offline Token 刷新机制改了什么?
如果你最近还在维护 Shopify App 的授权代码,这条更新值得单独看一眼。
2025 年 12 月 10 日,Shopify 在开发者 changelog 里写明:offline access token 现在支持过期和刷新。到 2026 年 3 月 20 日,另一条 changelog 又补了一句更硬的话:2026 年 4 月 1 日之后创建、并调用 Admin API 的新 public app,必须使用会过期的 offline access token。
这不是一个马上把所有老应用打断的改动。已有 public app、custom app、商家自己在后台创建的 app,现在不在这条 4 月 1 日规则里。可如果你在做新应用,或者今年会新开一个 public app,这套刷新逻辑最好从第一版就放进去。
以前那种“装完就一直用”的假设没了
很多 Shopify App 的后台任务都靠 offline token 活着。比如同步订单、处理 webhook、定时拉商品、给店铺跑报表。商家不在线的时候,App 也要能调用 Admin API,所以 offline token 一直是默认选择。
老代码里常见的处理很简单:商家安装时拿到 token,存进数据库;之后每次请求 Admin API,就把它塞进 X-Shopify-Access-Token。只要商家不卸载应用,团队通常就默认这个 token 还能用。
新的 expiring offline token 把这个假设拆掉了。官方文档现在写得很明确:expiring offline access token 的有效期是 1 小时,响应里的 expires_in 是 3600。同时返回一个 refresh_token,应用可以在不打扰商家的情况下换一个新的 access token。
比如一个卖宠物用品的独立站,两百来个 SKU,每 15 分钟跑一次库存同步。以前同步任务只要读店铺表里的 offline token。现在它至少要多看两个字段:access token 什么时候过期,refresh token 还在不在有效期内。否则凌晨的同步任务第一次遇到 401,后面一整晚的库存都可能停住。
刷新不是重走安装流程
Shopify 这次改动比较关键的一点,是刷新发生在 token endpoint,不需要商家重新点授权。
官方文档给出的 OAuth token endpoint 是:
POST https://{shop}.myshopify.com/admin/oauth/access_token
如果你用的是 token exchange,获取 offline token 时可以带上 requested_token_type=urn:shopify:params:oauth:token-type:offline-access-token,并把 expiring 设为 1。如果你用 authorization code grant,也是在换 code 的时候通过 expiring=1 请求会过期的 offline token。
响应里不只要保存 access_token。至少还要保存这些东西:
•expires_in:当前 access token 的剩余秒数,官方现在是 3600 秒
•refresh_token:下一次服务端刷新要用
•refresh_token_expires_in:refresh token 初始有效期,官方写的是 7776000 秒,也就是 90 天
这里不要把 3600 写死。官方也提醒,应用应该读响应里的 expires_in,不要假设有效期永远不变。
最容易踩坑的是并发刷新
刷新逻辑看起来很普通,真正麻烦的地方在轮换。
Shopify 文档写得很细:每次 refresh 都会返回新的 access token 和新的 refresh token。应用要把两个都存起来,下一次刷新用新的 refresh token。
旧 refresh token 不会在 Shopify 发出新 token 的那一秒立刻失效。它会在几个条件里最早发生的那个时间点失效:应用用过新的 refresh token;应用重新通过 token exchange 或 authorization code grant 获取了新 token;旧 refresh token 第一次被用来刷新后的 30 天;或者它原本的 90 天到期。
这段规则对实现影响很直接。假设你的 webhook worker 和定时任务同时发现 token 快过期,两个进程一起 refresh。它们都把结果写回数据库,后写入的那个会覆盖前一个。下一次请求到底拿到哪个 refresh token,就可能取决于竞态。
比较稳的做法是给店铺维度加锁,或者在数据库里做条件更新:只有当前记录里的 refresh token 还是你刚才读取的那个,才允许写入新 token。刷新失败时再重新读取一次记录,不要马上把商家踢回 OAuth。
401 不是唯一触发点
只在收到 401 之后才刷新,能救回来一部分请求,但后台任务会多一次失败。对 webhook 这类有重试的任务还好,对人工触发的导出、批量更新,体验会比较粗糙。
官方建议是提前几分钟刷新,并在收到 401 时再补一次刷新分支。实现上可以给每个店铺保存 expires_at,请求前判断是否快到期。比如离过期还有 5 分钟以内,就先刷新再请求 Admin API。
刷新失败也要单独报警。原因可能是 refresh token 过期、client secret 被轮换、商家卸载应用,或者你的存储没有正确保存新的 refresh token。它们最后可能都表现成 401,但处理方式不一样。
老应用现在可以缓一缓,但别继续新增技术债
2026 年 4 月 1 日这条要求,只点名新建 public app。Shopify 也写了,4 月 1 日之前创建的 public app 暂时不受影响。
不过 access tokens 文档里还有另一条时间线:public apps 到 2027 年 1 月 1 日必须使用 expiring offline access token 来请求 Admin API。custom app 和商家创建的 app 不在这条范围里。
所以老应用不是今天必须迁,但如果你已经在改授权层,最好顺手把表结构和刷新路径留好。至少先把“offline token 永不过期”这种注释删掉,后台任务里也别再把 401 统一归类成商家卸载。
我还没试过在真实 Shopify 模板项目里逐行确认最新版库的内部实现。官方说如果使用 Shopify app templates 和 libraries,这部分已经处理好。自建 OAuth、老 Rails App、或者改过模板授权层的项目,还是应该自己查一遍 token 存储和刷新路径。
可以先改这几处
不用一上来重写整套授权。先看四个地方:
• 数据库有没有位置存 refresh_token、expires_at、refresh_token_expires_at
• Admin API 请求封装里,是否能在请求前检查过期时间
• 后台任务和 webhook worker 是否会并发刷新同一家店铺的 token
• 401 日志里,能不能区分 token 过期、scope 不足、应用卸载
如果应用还在用非过期 offline token,新版本可以先在安装或 token exchange 时加上 expiring=1,再把刷新逻辑接到统一的 API client 里。这样商品同步、订单同步、报表任务都不需要各写一遍刷新代码。
这次改动不花哨,但很像 Shopify 在把 App 授权往更标准的 OAuth 行为上收。后台任务还能继续跑,只是以后不能再靠一个永久 token 撑完整个生命周期。
官方来源:
• https://shopify.dev/changelog/offline-access-tokens-now-support-expiry-and-refresh
• https://shopify.dev/changelog/expiring-offline-access-tokens-required-for-public-apps-april-1-2026
• https://shopify.dev/docs/apps/build/authentication-authorization/access-tokens