<!-- Space: qifu -->
<!-- Parent: 后端技术&知识&规范 -->
<!-- Parent: 技术方案 -->
<!-- Parent: 基建 -->
<!-- Parent: 03-接入指南 -->
<!-- Parent: 0301-电商接入指南 -->
<!-- Title: 20250515-电商订单对接指南 -->

<!-- Macro: :anchor\((.*)\):
     Template: ac:anchor
     Anchor: ${1} -->
<!-- Macro: \!\[.*\]\((.+)\)\<\!\-\- width=(.*) \-\-\>
     Template: ac:image
     Url: ${1}
     Width: ${2} -->
<!-- Macro: \<\!\-\- :toc: \-\-\>
     Template: ac:toc
     Printable: 'false'
     MinLevel: 2 
     MaxLevel: 4 -->
<!-- Include: 杂项/声明文件.md -->

<!-- :toc: -->

# 电商订单对接指南

- 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

<dependencys>
    <!-- RPC接口 -->
    <dependency>
        <groupId>com.yuanmeng.qifu</groupId>
        <artifactId>qifu-saas-eg-client</artifactId>
        <version>1.0.13</version>
    </dependency>

    <!-- SPI接收订单同步信息 -->
    <dependency>
        <groupId>com.yuanmeng.qifu</groupId>
        <artifactId>qifu-saas-eg-spi</artifactId>
        <version>1.0.13</version>
    </dependency>
</dependencys>

```

### 注册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&region=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;
      }
  
  }
  ```