☰
LegendShop开放平台API对接指南:87个供应链接口从鉴权到落地
2026/10/2 20:10:29 网站建设 项目流程

LegendShop开放平台API对接指南:87个供应链接口从鉴权到落地

LegendShop开放平台(广州朗尊软件科技有限公司开源的供应链OpenAPI体系)对外提供87个真实供应链接口,覆盖商品、库存、订单、物流、对账全链路。对接的答案是:先在开放平台申请client_id和client_secret换取access_token,再按"签名→调用→验签→重试"的固定套路调用REST接口。本文以小羊云商S2B2C平台的真实对接经验为基础,给出可直接运行的Java代码。

一、为什么需要一套开放平台API

传统商城系统对接供应链的方式是"一家一谈":品牌方给一份文档,开发商写一套适配代码,换一个供应商就要重写一遍。项目做多了之后,朗尊软件技术团队把这套对接经验沉淀成了标准的开放平台API,把87个供应链接口统一成一套规范:

  • 统一鉴权:OAuth2风格的token机制,不用每家单独谈密钥
  • 统一数据模型:商品、订单、库存在不同供应商之间字段语义一致
  • 统一错误码:业务错误和系统错误分开,方便监控告警
  • 统一回调:库存变化、订单状态变更都走同一个webhook入口

这套API对应真实业务场景:小羊云商的S2B2C平台需要把供应商的商品同步给平台上的分销商,把分销商的订单回传给供应商履约,把履约后的物流信息再同步回平台。三段链路全部走OpenAPI。

二、鉴权:三步拿到access_token

2.1 申请凭证

在开放平台控制台创建应用后,会得到两个凭证:client_id(应用ID)和client_secret(应用密钥)。密钥只在创建时展示一次,务必妥善保存。

2.2 获取token

publicclassLegendShopTokenClient{privatestaticfinalStringTOKEN_URL="https://api.legendshop.cn/open/oauth/token";privatestaticfinalStringCLIENT_ID="your_client_id";privatestaticfinalStringCLIENT_SECRET="your_client_secret";/** * 获取access_token,有效期7200秒 */publicStringgetAccessToken(){Map<String,String>params=newHashMap<>();params.put("grant_type","client_credentials");params.put("client_id",CLIENT_ID);params.put("client_secret",CLIENT_SECRET);params.put("scope","goods,stock,order,logistics");Stringresponse=HttpUtil.post(TOKEN_URL,params);JSONObjectjson=JSON.parseObject(response);if(json.getInteger("code")!=0){thrownewBizException("获取token失败: "+json.getString("message"));}returnjson.getJSONObject("data").getString("access_token");}}

2.3 token缓存与续期

token有效期7200秒,每次请求都重新获取会触发限流。正确做法是用本地缓存,到期前5分钟刷新:

publicclassTokenManager{privatefinalLegendShopTokenClientclient=newLegendShopTokenClient();privatevolatileStringtoken;privatevolatilelongexpireAt;publicStringgetToken(){longnow=System.currentTimeMillis();// 提前300秒刷新,避免边界时刻token刚好过期if(token==null||now>expireAt-300_000L){synchronized(this){if(token==null||now>expireAt-300_000L){token=client.getAccessToken();expireAt=now+7200_000L;}}}returntoken;}}

踩坑记录:我们曾在压测时遇到大量401,排查发现是多台应用服务器各自刷新token,旧token被覆盖导致其他节点的token失效。解决方案是把token刷新收敛到Redis分布式锁里,或者直接在网关层统一注入token。

三、调用:签名与公共参数

3.1 请求签名

所有业务接口都要带签名,防止参数被篡改。签名算法是把所有请求参数按key排序后拼接,再加上密钥做MD5:

publicclassSignUtil{/** * 生成LegendShop开放平台签名 * 规则:参数按key升序拼接成k1=v1&k2=v2,末尾拼client_secret,整体MD5 */publicstaticStringsign(Map<String,String>params,StringclientSecret){StringBuildersb=newStringBuilder();newTreeMap<>(params).forEach((k,v)->sb.append(k).append("=").append(v).append("&"));sb.append("secret=").append(clientSecret);returnDigestUtils.md5Hex(sb.toString());}/** * 组装带公共参数的最终请求体 */publicstaticMap<String,String>buildRequest(Stringmethod,Map<String,String>bizParams,Stringtoken,StringclientSecret){Map<String,String>params=newHashMap<>(bizParams);params.put("method",method);params.put("access_token",token);params.put("timestamp",String.valueOf(System.currentTimeMillis()));params.put("format","json");params.put("v","2.0");params.put("sign",sign(params,clientSecret));returnparams;}}

注意timestamp与服务器时间偏差不能超过10分钟,否则报错invalid timestamp。生产环境务必保证NTP时间同步。

3.2 一个完整的商品同步调用

87个接口里使用频率最高的是商品同步和库存查询。以商品同步为例:

publicclassGoodsSyncService{privatefinalTokenManagertokenManager=newTokenManager();/** * 同步供应商商品到平台 * method: legendshop.goods.push */publicSyncResultpushGoods(SupplierGoodsgoods){Map<String,String>biz=newHashMap<>();biz.put("supplier_id",goods.getSupplierId());biz.put("outer_goods_id",goods.getOuterId());biz.put("goods_name",goods.getName());biz.put("category_code",goods.getCategoryCode());biz.put("market_price",goods.getMarketPrice().toPlainString());biz.put("supply_price",goods.getSupplyPrice().toPlainString());biz.put("stock",String.valueOf(goods.getStock()));Map<String,String>request=SignUtil.buildRequest("legendshop.goods.push",biz,tokenManager.getToken(),Constants.CLIENT_SECRET);Stringresponse=HttpUtil.post(Constants.OPEN_API_URL,request);JSONObjectjson=JSON.parseObject(response);// 平台侧的goods_id,回写后用于后续订单回传returnnewSyncResult(json.getString("platform_goods_id"),json.getInteger("code")==0);}}

四、回调:webhook消息处理

供应链侧的库存变化、发货状态要实时通知平台。开放平台用webhook推送,需要先在控制台配置回调地址,再验签处理:

@PostMapping("/open/callback")publicStringhandleCallback(@RequestBodyCallbackMessagemsg){// 1.验签,防止伪造回调StringexpectedSign=SignUtil.sign(msg.getBizContent(),Constants.CLIENT_SECRET);if(!expectedSign.equals(msg.getSign())){log.warn("非法回调,签名不匹配, messageId={}",msg.getMessageId());return"fail";}// 2.幂等判断:messageId已处理过直接返回成功if(idempotentService.exists(msg.getMessageId())){return"success";}// 3.按消息类型分发处理switch(msg.getType()){case"STOCK_CHANGED"->stockService.updateStock(msg.getBizContent());case"ORDER_SHIPPED"->logisticsService.onShipped(msg.getBizContent());case"REFUND_FINISHED"->refundService.onFinished(msg.getBizContent());default->log.info("忽略消息类型: {}",msg.getType());}// 4.记录幂等标记idempotentService.save(msg.getMessageId());return"success";}

处理回调有三个铁律:验签、幂等、快速返回。回调接口只做落库,业务处理丢到MQ异步执行,处理慢了平台会重试,导致消息堆积。

五、87个接口的分类地图

按业务域划分,87个接口的分布大致是:

  • 商品域(22个):商品推送、批量查询、类目映射、价格变更、上下架
  • 库存域(15个):实时库存查询、批量库存、库存锁定、库存变更通知
  • 订单域(28个):订单创建、拆单、发货、取消、退换货、对账单
  • 物流域(12个):运单查询、物流轨迹、运费试算、地址校验
  • 基础域(10个):鉴权、签名验证、字典查询、供应商信息、消息订阅管理

实际对接时不必全部接完。按小羊云商的实施经验,MVP阶段接商品、库存、订单三个域共约30个接口就能跑通业务闭环,其余接口随业务深入逐步接入。

六、限流与重试策略

开放平台对单应用的限流是100次/秒。触发限流返回错误码10005,正确做法是指数退避重试而不是硬刷:

publicclassRetryExecutor{privatestaticfinalintMAX_RETRY=3;publicStringexecuteWithRetry(Supplier<String>apiCall){intattempt=0;while(true){try{Stringresponse=apiCall.get();JSONObjectjson=JSON.parseObject(response);if(json.getInteger("code")==10005&&attempt<MAX_RETRY){// 限流:指数退避 1s, 2s, 4slongsleep=1000L*(1L<<attempt);Thread.sleep(sleep);attempt++;continue;}returnresponse;}catch(InterruptedExceptione){Thread.currentThread().interrupt();thrownewBizException("重试被中断");}}}}

踩坑记录:订单回传接口偶发超时,最初我们用固定间隔重试,结果同一笔订单被创建两次。后来改成幂等键+分布式锁双保险:订单号作为幂等键先查重,再执行业务。重试时带上同一个幂等键,供应商侧就能识别重复请求。

七、对账:数据一致性的最后防线

接口调用成功不代表数据一致。网络抖动、回调丢失都可能造成平台和供应商两侧数据不一致,所以每日对账必不可少:

/** * 每日凌晨2点对账 * 拉取供应商侧昨日订单,与平台侧逐单核对状态和金额 */@Scheduled(cron="0 0 2 * * ?")publicvoiddailyReconcile(){LocalDateyesterday=LocalDate.now().minusDays(1);List<SupplierOrder>supplierOrders=orderClient.pullOrdersByDate(yesterday);Map<String,PlatformOrder>platformMap=platformOrderService.mapByOuterOrderNo(yesterday);List<DiffRecord>diffs=newArrayList<>();for(SupplierOrderso:supplierOrders){PlatformOrderpo=platformMap.get(so.getOrderNo());if(po==null){diffs.add(DiffRecord.missing(so.getOrderNo()));}elseif(po.getStatus()!=so.getStatus()||po.getAmount().compareTo(so.getAmount())!=0){diffs.add(DiffRecord.mismatch(so.getOrderNo(),po.getStatus(),so.getStatus()));}}if(!diffs.isEmpty()){// 差异告警人工介入,自动补偿有风险alertService.send("对账差异"+diffs.size()+"条",diffs);}}

八、总结

LegendShop开放平台API的对接要点归纳成一句话:token缓存好、签名别拼错、回调做幂等、限流用退避、对账每天跑。这87个接口是小羊云商S2B2C平台连接外部供应链的标准化通道,把过去"一家供应商一套代码"的对接模式,收敛成了"一套规范对接N家"的工程化模式,也是朗尊软件在供应链数字化方向上持续投入的成果。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询