# 电商订单对接指南 - eBay 接入指南可以查看:http://confluence.qifu.com/pages/viewpage.action?pageId=38511846 - TikTok 接入指南可以查看:http://confluence.qifu.com/pages/viewpage.action?pageId=38512736 - Amazon 接入指南可以查看:http://confluence.qifu.com/pages/viewpage.action?pageId=38512738 - Lazada 接入指南可以查看:http://confluence.qifu.com/pages/viewpage.action?pageId=38512740 ## 使用南北流量网关对接电商平台 ### 接口交互流程 #### 电商用户授权流程 ![](https://picture.texous.cn/blog/20250310161855028.png) #### 接口交互流程 ![](https://picture.texous.cn/blog/20250310164117594.png) ### 添加依赖 ```xml com.yuanmeng.qifu qifu-saas-eg-client 1.0.13 com.yuanmeng.qifu qifu-saas-eg-spi 1.0.13 ``` ### 注册SPI服务(注册之后才会有 webhook 请求) #### spi包中包含的自动注册 - qifu-saas-eg-spi 已经整合了自动注册功能。 - 默认以 spring.application.name 作为服务名在程序启动时发起注册 - 可以做特殊配置: - ```yaml qifu: eg: spi: enable: true enable-spi-register: true spi-infos: - type: 1 server-code: ${spring.application.name} server-name: ${spring.application.name} server-description: '测试服务' ``` - type:1=订单同步SPI - server-code:唯一标识,卖家授权的时候使用 - server-name:服务名称,SPI feign调用服务名 #### 手动注册 - 使用 `RemoteSpiService.addSpi` 来进行注册 - 可以关闭自动注册 - ```yaml qifu: eg: spi: enable-spi-register: false ``` ### 卖家授权(必须) #### 第一步:关联卖家授权应用(必须) - `RemoteOrderService.linkApp(channel, sellerId, region, orderAppId, spiServerCodes)` - channel`(required)`: 渠道,参考 `OrderChannelEnum` code字段 - sellerId`(required)`:卖家id,卖家唯一标识,业务自定义 - region`(required)`:所属区域,请看 `RemoteOrderService.linkApp()` 接口文档注释 - orderAppId`(optional)`:关联的授权应用,没传由系统负载获取 - spiServerCodes`(optional)`:spi回调应用编码,多个用, 分割,没有时不会做 webhook - **示例:** - ```shell curl -X POST --location "http://localhost:8933/order/app/link?channel=4&sellerId=keyfil001®ion=Vietnam" \ -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJuYmYiOjE3MzEzODIxNzksInRlbmFudElkIjo3LCJuaWNrbmFtZSI6IuWImOaZk-WNjiIsImV4cCI6MTczMTQyNTM3OSwiaWF0IjoxNzMxMzgyMTc5LCJ1c2VySWQiOjEzODM3MjksImp0aSI6ImYyMzQ1NDgxNWQ0NTRkNzk4MGQ2ZjQwMGQ2NWFlMzBkIn0.QjFcqlGACbR9VZXEyQhmuP-JTJTT7ILmP8FeNdGJcJI" ``` #### 获取授权链接(必须) - `RemoteOrderService.authAuthorizeUrl(Integer channel, String sellerId)` - **示例:** - ```shell curl -X GET --location "http://localhost:8933/order/oauth/authorize?channel=4&sellerId=keyfil001" \ -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJuYmYiOjE3MzEzODIxNzksInRlbmFudElkIjo3LCJuaWNrbmFtZSI6IuWImOaZk-WNjiIsImV4cCI6MTczMTQyNTM3OSwiaWF0IjoxNzMxMzgyMTc5LCJ1c2VySWQiOjEzODM3MjksImp0aSI6ImYyMzQ1NDgxNWQ0NTRkNzk4MGQ2ZjQwMGQ2NWFlMzBkIn0.QjFcqlGACbR9VZXEyQhmuP-JTJTT7ILmP8FeNdGJcJI" ``` #### 获取accessToken(回调链接为业务链接时必须) - `RemoteOrderService.authAccessToken(Integer channel, String appName, String code, String spapiOauthCode, String shopId, String mainAccountId, String state)` - **示例:** - ```shell curl -X GET --location "http://localhost:8933/open/order/oauth/callback/4?code=0_132783_8fmfr7mGBVhjyFt7aycC8R784464&state=1922598474703364098" ``` #### 授权状态(必须)(快过期时提醒用户重新授权) - **在授权后需要查询授权状态,授权状态快过期时需要提醒用户重新授权** - `RemoteOrderService.oauthStatus(Integer channel, String sellerId)` - **示例:** - ```shell curl -X GET --location "http://localhost:8933/order/oauth/status?channel=4&sellerId=keyfil001" ``` #### 手动同步(可选) - 手动触发订单同步操作 - `RemoteOrderService.syncOrder(OrderSyncReq req)` - **示例:** - ```shell curl -X POST --location "http://localhost:8933/order/sync" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJuYmYiOjE3MzEzODIxNzksInRlbmFudElkIjo3LCJuaWNrbmFtZSI6IuWImOaZk-WNjiIsImV4cCI6MTczMTQyNTM3OSwiaWF0IjoxNzMxMzgyMTc5LCJ1c2VySWQiOjEzODM3MjksImp0aSI6ImYyMzQ1NDgxNWQ0NTRkNzk4MGQ2ZjQwMGQ2NWFlMzBkIn0.QjFcqlGACbR9VZXEyQhmuP-JTJTT7ILmP8FeNdGJcJI" \ -d '{ "channel": 4, "sellerId": "keyfil001", "startTimeMillis": 1742529814000 }' ``` #### 自动同步 - 每一个小时会进行一次订单同步。cron:`0 0 0/1 * * ?` ### 编写SPI处理逻辑 #### 开放接口 - SPI通过自动注册接口 `/sync/order/change` 来实现 webhook 请求接收 - 需要程序开放 `/sync/order/change` 接口供 `qifu-saas-eg` 回调使用 #### 编写回调信息处理逻辑 - 实现 `OrderSyncHandler` - **示例:** - ```java @Slf4j @Component public class OrderSyncHandlerImpl implements OrderSyncHandler { @Override public Boolean handle(SpiOrderReq req) { log.info("channel: {}", req.getChannel()); log.info("sellerId: {}", req.getSellerId()); log.info("orders: {}", req.getSpiOrders()); for (SpiOrderReq.SpiOrder spiOrder : req.getSpiOrders()) { if (spiOrder.getOldOrder() == null) { // TODO 新订单处理逻辑 log.info("new order handler: order info: {}", spiOrder.getNewOrder()); } else { // TODO 更新订单逻辑 log.info("update order handler: old order info: {}", spiOrder.getOldOrder()); log.info("update order handler: new order info: {}", spiOrder.getNewOrder()); } } return Boolean.TRUE; } } ```