## 文档概述 - 接口总数:25个 - API版本:linkedmall/2023-09-30 - RegionId:cn-zhangjiakou(华北3 张家口) - Endpoint:`linkedmall.cn-zhangjiakou.aliyuncs.com` - HTTP基础Path前缀:`/opensaas-s2b/opensaas-s2b-biz-trade/v2` - 签名类型:ROA签名,SDK内部自动处理 - 金额单位:分(整数,long/int64) - 区域编码divisionCode:必须使用五级(乡镇街道级别)编码 ## 认证与安全 - 使用RAM子账号AccessKey(AccessKeyId / AccessKeySecret),禁止主账号AK - 授予LinkedMall对应OpenAPI最小权限 - 业务前置资源:purchaserId(采购方ID)、shopId(店铺ID),由LinkedMall控制台分配 - 签名类型为ROA签名,SDK内部自动处理,业务无需实现签名算法 - 使用通用OpenApiClient(darabonba-openapi/v2)发起ROA调用 ## 公共返回结构体 所有接口返回外层统一格式: ```go type CommonResponse struct { RequestId string `json:"RequestId"` Success bool `json:"Success"` Code string `json:"Code"` Message string `json:"Message"` SubCode string `json:"SubCode"` SubMessage string `json:"SubMessage"` Data interface{} `json:"Data"` // 业务数据,不同接口Data内部结构不同 } ``` 公共Code枚举: - `SUCCESS`:业务调用成功 - 其他:业务失败,看SubCode、SubMessage定位原因 > 注意:HTTP 200 不代表业务成功,必须判断 `Success==true` ## SDK客户端初始化 ```go package main import ( "github.com/alibabacloud-go/darabonba-openapi/v2/client" "github.com/alibabacloud-go/tea/v2" ) func NewLinkedMallClient(accessKeyId, accessKeySecret string) (*client.Client, error) { config := &client.Config{ AccessKeyId: tea.String(accessKeyId), AccessKeySecret: tea.String(accessKeySecret), RegionId: tea.String("cn-zhangjiakou"), Endpoint: tea.String("linkedmall.cn-zhangjiakou.aliyuncs.com"), ReadTimeout: tea.Int(30000), ConnectTimeout: tea.Int(10000), } return client.NewClient(config) } ``` 调用示例: ```go cli, err := NewLinkedMallClient("AKxxx", "SKxxx") resp, err := cli.RoaRequest( tea.String("POST"), tea.String("/opensaas-s2b/opensaas-s2b-biz-trade/v2/xxx"), nil, // query map[string]*string bodyObj, // post json body nil, // header ) ``` ## 接口列表 ### 接口 1:ListPurchaserShops 获取采购方店铺列表 - 路径:`/purchaser-shops` - 方法:GET - 描述:分页获取采购方店铺列表 #### 请求参数(Query) | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | PageNum | int | 否 | 页码,默认1 | | PageSize | int | 否 | 每页条数,最大100 | #### 响应Data结构体 ```go type ListPurchaserShopsRespData struct { Total int64 `json:"Total"` PageNum int `json:"PageNum"` PageSize int `json:"PageSize"` ShopList []PurchaserShopItem `json:"ShopList"` } type PurchaserShopItem struct { ShopId string `json:"ShopId"` PurchaserId string `json:"PurchaserId"` ShopName string `json:"ShopName"` ShopStatus string `json:"ShopStatus"` } ``` ### 接口 2:GetPurchaserShop 获取采购方店铺详情 - 路径:`/purchaser-shops/{ShopId}` - 方法:GET - 描述:获取单个采购店铺详情 #### 请求参数 | 参数名 | 类型 | 位置 | 必填 | 说明 | |--------|------|------|------|------| | ShopId | string | Path | 是 | 店铺ID | #### 响应Data结构体 ```go type GetPurchaserShopRespData struct { ShopId string `json:"ShopId"` PurchaserId string `json:"PurchaserId"` ShopName string `json:"ShopName"` ShopStatus string `json:"ShopStatus"` ContactInfo string `json:"ContactInfo"` } ``` ### 接口 3:ListSelectionProducts 查询选品池商品列表 - 路径:`/selection-products` - 方法:GET - 描述:分页查询选品池商品列表 #### 请求参数(Query) | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | PageNum | int | 否 | 页码 | | PageSize | int | 否 | 最大100 | | PurchaserId | string | 是 | 采购方ID | #### 响应Data结构体 ```go type ListSelectionProductsRespData struct { Total int64 `json:"Total"` PageNum int `json:"PageNum"` PageSize int `json:"PageSize"` ProductList []SelectionProductItem `json:"ProductList"` } type SelectionProductItem struct { ProductId string `json:"ProductId"` Title string `json:"Title"` MainImage string `json:"MainImage"` BrandName string `json:"BrandName"` CategoryId string `json:"CategoryId"` } ``` ### 接口 4:GetSelectionProduct 查询选品池商品详情 - 路径:`/selection-products/{ProductId}` - 方法:GET - 描述:查询单个商品详情,支持传入divisionCode校验区域可售库存 #### 请求参数 | 参数名 | 类型 | 位置 | 必填 | 说明 | |--------|------|------|------|------| | ProductId | string | Path | 是 | 商品ID | | PurchaserId | string | Query | 是 | 采购方ID | | DivisionCode | string | Query | 否 | 五级乡镇区域编码,传后返回该区域可售状态 | #### 响应Data结构体 ```go type GetSelectionProductRespData struct { ProductId string `json:"ProductId"` Title string `json:"Title"` MainImage string `json:"MainImage"` Images []string `json:"Images"` BrandName string `json:"BrandName"` CategoryId string `json:"CategoryId"` Desc string `json:"Desc"` SkuList []SelectionSkuItem `json:"SkuList"` CanSale bool `json:"CanSale"` // 传入divisionCode才有效,该区域是否可售 StockQuantity int64 `json:"StockQuantity"` } type SelectionSkuItem struct { SkuId string `json:"SkuId"` SkuSpec string `json:"SkuSpec"` SalePrice int64 `json:"SalePrice"` // 单位分 MarketPrice int64 `json:"MarketPrice"` StockQuantity int64 `json:"StockQuantity"` } ``` ### 接口 5:GetSelectionProductSaleInfo 查询选品池商品销售信息 - 路径:`/selection-products/{ProductId}/sale-info` - 方法:GET - 描述:查询商品可售、价格库存快照 #### 请求参数 | 参数名 | 类型 | 位置 | 必填 | 说明 | |--------|------|------|------|------| | ProductId | string | Path | 是 | 商品ID | | PurchaserId | string | Query | 是 | 采购方ID | | DivisionCode | string | Query | 否 | 五级乡镇区域编码 | ### 接口 6:ListSelectionProductSaleInfos 批量查询商品销售信息 - 路径:`/selection-products:batch-sale-info` - 方法:POST - 描述:批量查询商品销售信息 #### 请求Body ```json { "PurchaserId": "xxx", "ProductIdList": ["p1","p2"], "DivisionCode": "五级编码" } ``` ### 接口 7:ListSelectionSkuSaleInfos 批量SKU销售信息 - 路径:`/selection-skus:batch-sale-info` - 方法:POST - 描述:批量查询SKU销售信息 #### 请求Body参数 | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | PurchaserId | string | 是 | 采购方ID | | SkuIdList | []string | 是 | SKU ID列表 | | DivisionCode | string | 否 | 五级乡镇区域编码 | ### 接口 8:ListCategories 查询类目列表 - 路径:`/categories` - 方法:GET - 描述:查询类目列表 #### 请求参数(Query) | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | PurchaserId | string | 是 | 采购方ID | | ParentCategoryId | string | 否 | 父类目ID(不传查一级类目) | ### 接口 9:SearchProducts 搜索选品池商品 - 路径:`/selection-products:search` - 方法:POST - 描述:搜索选品池商品 #### 请求Body参数 | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | Keyword | string | 否 | 关键词 | | CategoryId | string | 否 | 类目ID | | PageNum | int | 否 | 页码 | | PageSize | int | 否 | 每页条数 | | PurchaserId | string | 是 | 采购方ID | ### 接口 10:SelectionGroupAddProduct 选品池商品入库 - 路径:`/selection-group/products:add` - 方法:POST - 描述:选品池商品入库 #### 请求Body参数 | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | PurchaserId | string | 是 | 采购方ID | | ShopId | string | 是 | 店铺ID | | ProductIdList | []string | 是 | 商品ID列表 | ### 接口 11:SelectionGroupRemoveProduct 选品池商品出库 - 路径:`/selection-group/products:remove` - 方法:POST - 描述:选品池商品出库 #### 请求Body参数 | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | PurchaserId | string | 是 | 采购方ID | | ShopId | string | 是 | 店铺ID | | ProductIdList | []string | 是 | 商品ID列表 | ### 接口 12:RenderPurchaseOrder 采购单渲染(下单预校验) - 路径:`/purchase-orders:render` - 方法:POST - 描述:下单前必须调用,获取实时价格、校验商品是否可售 #### 请求Body ```go type RenderPurchaseOrderReq struct { PurchaserId string `json:"PurchaserId"` ShopId string `json:"ShopId"` DivisionCode string `json:"DivisionCode"` // 五级乡镇编码 ItemList []RenderOrderItem `json:"ItemList"` ReceiverInfo ReceiverInfo `json:"ReceiverInfo"` } type RenderOrderItem struct { SkuId string `json:"SkuId"` Quantity int64 `json:"Quantity"` } type ReceiverInfo struct { ReceiverName string `json:"ReceiverName"` ReceiverPhone string `json:"ReceiverPhone"` ProvinceCode string `json:"ProvinceCode"` CityCode string `json:"CityCode"` DistrictCode string `json:"DistrictCode"` TownCode string `json:"TownCode"` DetailAddress string `json:"DetailAddress"` } ``` #### 响应Data 渲染后的价格、商品校验结果、不可售原因、预估运费。 ### 接口 13:SplitPurchaseOrder 采购单渲染并拆单 - 路径:`/purchase-orders:split-render` - 方法:POST - 描述:采购单渲染并拆单,返回拆成多份子单结构 #### 请求Body 结构和RenderPurchaseOrderReq完全一致 #### 响应Data 返回拆成多份子单结构,用于前端展示,拆单结果用于创建采购单入参。 ### 接口 14:CreatePurchaseOrder 创建采购单【异步】 - 路径:`/purchase-orders` - 方法:POST - 描述:⚠️重要:接口只返回采购单号,不代表下单成功,必须等待回调通知,再查询订单接口 #### 请求Body ```go type CreatePurchaseOrderReq struct { PurchaserId string `json:"PurchaserId"` ShopId string `json:"ShopId"` OuterPurchaseOrderId string `json:"OuterPurchaseOrderId"` // 外部业务唯一单号,幂等 DivisionCode string `json:"DivisionCode"` ReceiverInfo ReceiverInfo `json:"ReceiverInfo"` SubOrderList []CreateSubOrderItem `json:"SubOrderList"` // SplitPurchaseOrder返回的子单 } type CreateSubOrderItem struct { SkuId string `json:"SkuId"` Quantity int64 `json:"Quantity"` } ``` #### 响应Data结构体 ```go type CreatePurchaseOrderRespData struct { PurchaseOrderId string `json:"PurchaseOrderId"` // 阿里云采购单号 } ``` ### 接口 15:GetPurchaseOrderStatus 获取采购单状态 - 路径:`/purchase-orders/{PurchaseOrderId}/status` - 方法:GET - 描述:获取采购单状态 #### 请求参数 | 参数名 | 类型 | 位置 | 必填 | 说明 | |--------|------|------|------|------| | PurchaseOrderId | string | Path | 是 | 采购单ID | | PurchaserId | string | Query | 是 | 采购方ID | #### 响应Data 采购单状态枚举:INIT、PROCESS、SUCCESS、FAIL、CLOSED ### 接口 16:GetOrder 获取订单详情 - 路径:`/orders/{OrderId}` - 方法:GET - 描述:获取订单详情 #### 请求参数 | 参数名 | 类型 | 位置 | 必填 | 说明 | |--------|------|------|------|------| | OrderId | string | Path | 是 | 订单ID | | PurchaserId | string | Query | 是 | 采购方ID | #### 响应Data 订单主信息、商品明细、实付金额、状态、收货信息 ### 接口 17:QueryOrders 查询订单列表 - 路径:`/orders` - 方法:GET - 描述:查询订单列表 #### 请求参数(Query) | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | PurchaserId | string | 是 | 采购方ID | | PurchaseOrderId | string | 否 | 采购单ID | | PageNum | int | 否 | 页码 | | PageSize | int | 否 | 每页条数 | | StartTime | string | 否 | 开始时间 | | EndTime | string | 否 | 结束时间 | ### 接口 18:ListLogisticsOrders 查询订单物流信息 - 路径:`/orders/{OrderId}/logistics` - 方法:GET - 描述:查询订单物流信息 #### 请求参数 | 参数名 | 类型 | 位置 | 必填 | 说明 | |--------|------|------|------|------| | OrderId | string | Path | 是 | 订单ID | | PurchaserId | string | Query | 是 | 采购方ID | #### 响应Data 运单号、物流公司、物流轨迹数组 ### 接口 19:ConfirmDisburse 确认收货 - 路径:`/orders/{OrderId}:confirm-disburse` - 方法:POST - 描述:确认收货 #### 请求参数 | 参数名 | 类型 | 位置 | 必填 | 说明 | |--------|------|------|------|------| | OrderId | string | Path | 是 | 订单ID | #### 请求Body ```json {"PurchaserId":"xxx"} ``` ### 接口 20:RenderRefundOrder 售后渲染预校验 - 路径:`/refund-orders:render` - 方法:POST - 描述:售后渲染预校验 #### 请求Body参数 | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | PurchaserId | string | 是 | 采购方ID | | OrderId | string | 是 | 订单ID | | OrderItemId | string | 是 | 订单商品明细ID | | RefundQuantity | int64 | 是 | 退款数量 | | RefundType | string | 是 | 退款类型(仅退款/退货退款) | ### 接口 21:CreateRefundOrder 创建售后单 - 路径:`/refund-orders` - 方法:POST - 描述:创建售后单 #### 请求Body参数 | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | PurchaserId | string | 是 | 采购方ID | | OrderId | string | 是 | 订单ID | | OrderItemId | string | 是 | 订单商品明细ID | | RefundQuantity | int64 | 是 | 退款数量 | | RefundAmount | int64 | 是 | 退款金额(单位分) | | RefundType | string | 是 | 退款类型 | | RefundReason | string | 是 | 退款原因 | | OuterRefundNo | string | 是 | 外部售后单号 | ### 接口 22:CancelRefundOrder 取消售后单 - 路径:`/refund-orders/{RefundOrderId}:cancel` - 方法:POST - 描述:取消售后单 #### 请求参数 | 参数名 | 类型 | 位置 | 必填 | 说明 | |--------|------|------|------|------| | RefundOrderId | string | Path | 是 | 售后单ID | #### 请求Body ```json {"PurchaserId":"xxx"} ``` ### 接口 23:GetRefundOrder 获取售后单详情 - 路径:`/refund-orders/{RefundOrderId}` - 方法:GET - 描述:获取售后单详情 #### 请求参数 | 参数名 | 类型 | 位置 | 必填 | 说明 | |--------|------|------|------|------| | RefundOrderId | string | Path | 是 | 售后单ID | | PurchaserId | string | Query | 是 | 采购方ID | ### 接口 24:CreateGoodsShippingNotice 回填退货物流运单 - 路径:`/refund-orders/{RefundOrderId}:fill-logistics` - 方法:POST - 描述:回填退货物流运单 #### 请求参数 | 参数名 | 类型 | 位置 | 必填 | 说明 | |--------|------|------|------|------| | RefundOrderId | string | Path | 是 | 售后单ID | #### 请求Body参数 | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | PurchaserId | string | 是 | 采购方ID | | LogisticsCompany | string | 是 | 物流公司 | | TrackingNumber | string | 是 | 运单号 | ### 接口 25:QueryChildDivisionCode 查询子区域编码 - 路径:`/divisions:children` - 方法:GET - 描述:查询子区域编码,不传返回省级;传入省返回市,传入市返回区,传入区返回乡镇(五级) #### 请求参数(Query) | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | ParentDivisionCode | string | 否 | 父区域编码 | #### 响应Data DivisionCode列表、名称、层级;下单、商品可售校验必须拿到乡镇级DivisionCode ## 错误码 | 错误码 | 说明 | 处理建议 | |--------|------|----------| | ShopTypeInvalid | SKU属于经销集采店铺,不可下单 | 过滤SKU | | SkuPriceUnique | SKU价格非最新 | 重新调用RenderPurchaseOrder获取最新价格再下单 | | PurchaseOrderNotFound | 采购单号不存在 | 核对入参PurchaseOrderId | | RefundNumberMustLessThanOrder | 退款数量大于订单商品数量 | 修正退款数量 | | RefundAmountMustLessThanOrder | 退款金额大于实付金额 | 修正退款金额 | | HasNoPrivilege | RAM无接口权限 | 检查RAM授权策略 | | OrderNotFound | 订单ID不存在 | 核对OrderId | | ShopNotFind | 店铺不存在 | 核对ShopId/PurchaserId | | SavePurchaseOrderError | 保存采购单服务端异常 | 短间隔重试 | | OuterPurchaseOrderIdExist | 外部业务单号重复 | 更换OuterPurchaseOrderId,做好幂等 | ## 回调通知 ### 消息回调(异步通知)规范 CreatePurchaseOrder创建采购单后,阿里云主动POST回调业务配置的回调地址。 1. 请求方式:POST,Content-Type: application/json 2. 业务侧需要返回 `{"success":true}` 告知阿里云消费成功;否则阿里云会重试推送。 3. 回调Body公共字段: ```json { "EventCode": "PURCHASE_ORDER_CHANGE", "PurchaseOrderId": "xxx", "OrderIdList": ["附属真实订单ID数组"], "Status": "SUCCESS/FAIL", "RequestId": "xxx" } ``` EventCode枚举: - PURCHASE_ORDER_CHANGE:采购单状态变更 - ORDER_STATUS_CHANGE:订单状态变更 - REFUND_ORDER_CHANGE:售后单变更 业务处理逻辑:收到回调后,调用对应查询接口拉取最新业务数据做本地状态同步。 ## 完整业务调用流程 1. 调用 `QueryChildDivisionCode` 获取五级乡镇divisionCode(收货地址对应的区域编码) 2. 调用 `ListSelectionProducts` / `SearchProducts` 获取选品池商品SkuId 3. 调用 `RenderPurchaseOrder` 预渲染校验商品可售、价格 4. 调用 `SplitPurchaseOrder` 获取拆单结果 5. 调用 `CreatePurchaseOrder`,传入外部唯一业务单号OuterPurchaseOrderId,拿到PurchaseOrderId 6. **等待阿里云消息回调通知** 7. 回调收到事件,调用`GetPurchaseOrderStatus`看采购单状态;拿到附属OrderId调用`GetOrder`同步订单入库本地库 8. 后续:查物流、确认收货;产生售后调用售后接口 > ❗禁止:CreatePurchaseOrder同步返回成功直接标记本地订单成功。 ## 开发注意事项 1. 金额全部为**分(int64)**,Go使用int64,不要float64;JSON序列化不要丢失long精度 2. DivisionCode必须五级乡镇,否则商品可售校验错误 3. OuterPurchaseOrderId外部采购单号全局唯一,用于幂等防重复下单 4. CreatePurchaseOrder异步,依赖回调;不能轮询频率过高 5. RAM子账号最小权限,AK禁止硬编码代码,环境变量配置 6. 所有接口必须判断外层返回`Success==true`,HTTP 200不等于业务成功 7. 分页接口PageSize上限100,不要传超大值 8. 回调接口做好幂等,同一个事件可能多次推送