MaeTown 开放平台 API 接入文档
本文档面向接入 MaeTown 开放平台的第三方合作方开发者,说明如何完成账号绑定、商品报价、下单支付、订单状态查询等对接工作。
内部设计细节参见
docs/20260914_第三方开放平台接入API设计方案.md(内部工程文档,第三方开发者无需阅读)。
1. 概述
MaeTown 开放平台允许已经与我方签约的第三方平台,在自己的产品内为已拥有 MaeTown 账号的用户提供"一键购买"能力:由贵方展示商品信息,用户点击购买后,通过本文档描述的接口在 MaeTown 完成报价确认与余额支付,实际的采购、物流、售后仍由 MaeTown 承担。
接入前提
- 用户必须已拥有 MaeTown 账号(手机号或微信注册均可),MaeTown 不提供免密自动开户,未注册用户需要先在 MaeTown 完成注册。
- 支付方式固定为 MaeTown 账号余额支付,用户余额不足时下单会直接失败,不支持透支或使用信用额度。
- 商品必须是 MaeTown 已支持的采购平台商品(即贵方需要知道商品所属平台的
platformCode,由我方在签约时提供对照表),不支持任意网址直接下单。
2. 接入准备
- 联系 MaeTown 商务/技术对接人完成签约,请提前准备好贵方用来接收授权绑定跳转回调的域名(见第4节的
redirectUrl),我方会在后台为贵方创建一个"渠道"并登记该域名,同时生成:appKey:渠道身份标识,明文,长期使用appSecret:签名密钥,仅在创建时展示一次,请妥善保存;一旦泄露请立即联系我方重置(重置后旧密钥立即失效)渠道编号(channelCode):账号绑定跳转链接中会用到- 出于安全考虑,
redirectUrl只有当其域名与我方登记的回调域名一致(或是其子域名)时,MaeTown 授权页才会自动跳转回去;不一致会被拒绝跳转,请确保贵方使用的回调域名和签约时登记的一致,域名变更需提前通知我方更新。
- 我方会同步提供当前支持的
platformCode对照表(如 Mercari、Yahoo 等各平台对应的编码)。 - 所有开放 API 均为 HTTPS 接口,域名以我方实际提供的对接环境为准(测试环境/生产环境域名不同)。
3. 签名认证机制
所有 /openapi/** 接口都需要在 HTTP 请求头中携带签名信息,未通过验签的请求会被直接拒绝。
3.1 请求头
| Header | 说明 |
|---|---|
X-App-Key | 分配给贵方的 appKey |
X-Timestamp | 当前 Unix 时间戳(秒级) |
X-Nonce | 随机字符串,建议 UUID,每次请求必须唯一 |
X-Sign | 签名值,见下方计算方法 |
3.2 签名计算方法
- 收集全部参与签名的参数:
appKey、timestamp、nonce(即上面三个请求头的值,参数名不带X-前缀、小写)- GET 请求:全部 URL 查询参数
- POST/PUT 请求:请求体 JSON 的所有顶层字段(要求请求体是扁平结构,不支持嵌套对象/数组作为签名参数——本期所有接口的入参都是扁平结构)
- 不包含
sign本身
- 按参数名的字典序(ASCII)排序,拼接成
key1=value1&key2=value2&...的字符串 - 在末尾拼接
&key={appSecret} - 对拼接后的完整字符串计算 HMAC-SHA256(密钥为
appSecret),取十六进制小写字符串,即为X-Sign
伪代码示例:
params = { appKey: "xxx", timestamp: "1757836800", nonce: "a1b2c3", openUserId: "u_001", platformCode: "Mercari", goodsId: "m123456789" }
# 按 key 字典序排序后拼接
stringToSign = "appKey=xxx&goodsId=m123456789&nonce=a1b2c3&openUserId=u_001&platformCode=Mercari×tamp=1757836800&key={appSecret}"
sign = hex(hmac_sha256(stringToSign, key=appSecret))
3.3 校验规则(请注意,触发以下任一情况都会直接拒绝请求)
X-Timestamp与服务器当前时间偏差超过 ±5分钟,判定为过期请求- 同一个
(appKey, nonce)组合在 10分钟内重复出现,判定为重放请求(请确保 nonce 真正随机,不要固定值或自增序列) - 渠道不存在,或渠道已被我方停用
- 签名值与服务端计算结果不一致
3.4 签名失败返回示例
{ "success": false, "code": 21007, "message": "E21007: 签名校验失败" }
4. 用户账号绑定
在调用报价/下单接口之前,用户必须先完成一次"授权绑定",把贵方的用户标识(openUserId,由贵方自行定义,只需保证在你方系统内唯一)关联到该用户的 MaeTown 账号。
4.1 绑定方式
绑定动作在 MaeTown 自己的页面上完成(用户输入手机号/微信登录等敏感操作全部发生在我方域名下,贵方拿不到用户的 MaeTown 账号密码,也拿不到任何 MaeTown 侧的登录凭证)。
推荐方式:调接口生成短链接(不用自己拼接又长又要手动URL编码的完整地址):
POST/openapi/auth/link
请求参数:
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| openUserId | string | 是 | 贵方的用户标识 |
| redirectUrl | string | 是 | 绑定完成后跳回的地址,不需要自己做URL编码,接口内部会处理 |
返回数据(data):
| 字段 | 类型 | 说明 |
|---|---|---|
| shortUrl | string | 短链接,直接嵌入商品卡片/推送消息,用户点击后会自动跳转到授权绑定页 |
| expireSeconds | int | 有效期(秒),当前为 604800(7天) |
请求示例:
{ "openUserId": "u_10001", "redirectUrl": "https://partner.com/callback?userId=10001" }
返回示例:
{ "success": true, "code": 10000, "message": "操作成功", "data": { "shortUrl": "https://<我方接口域名>/web/openapi/authlink/1a2b3c4d5e", "expireSeconds": 604800 } }
短链接有效期内可以重复点击(不是一次性的),过期后需要重新调用本接口生成新的。
备用方式:自己拼接完整地址(如果不想多调一次接口):
https://<MaeTown-H5域名>/#/pages/openapi/auth/auth?channelCode={渠道编号}&openUserId={你方用户标识}&redirectUrl={绑定完成后跳回的地址}
具体域名以我方实际提供的对接环境为准;注意路由是hash模式(地址里有个
#);redirectUrl需要自己做 URL 编码。
- 用户点击后:
- 已安装 MaeTown App 且已登录:直接进入"确认绑定"页
- 未安装/未登录:引导登录(手机验证码/微信/账号密码,MaeTown 现有登录方式),登录后进入"确认绑定"页
- 用户点击"确认授权"后,MaeTown 侧完成绑定,页面跳转回贵方提供的
redirectUrl - 后续调用报价/下单接口时,只需要在业务参数里带上这个
openUserId,我方会自动换算成对应的 MaeTown 账号,贵方无需也不能感知具体是哪个账号
4.2 绑定的唯一性约束
- 同一渠道下,一个
openUserId只能绑定一个 MaeTown 账号;一个 MaeTown 账号在同一渠道下也只能绑定一个openUserId - 用户可以随时在 MaeTown「我的 → 已授权的第三方平台」里自主解绑,解绑后该
openUserId调用需要身份的接口会失败(报价接口不受影响,下单接口会报"绑定关系不存在"),需要引导用户重新走一遍绑定流程 - MaeTown 客服也可能因用户投诉等原因发起强制解绑,贵方应对"绑定关系不存在"这个错误码做好兜底处理(引导用户重新授权),不要假设绑定关系永久有效
5. 接口列表
| 接口 | 方法 | 说明 |
|---|---|---|
/openapi/auth/link | POST | 生成授权绑定短链接(见第4节) |
/openapi/product/quote | POST | 商品报价 |
/openapi/order/pay | POST | 下单 + 余额扣款 |
/openapi/order/status | GET | 订单状态查询 |
统一返回结构:
{
"success": true,
"code": 10000,
"message": "操作成功",
"data": { }
}
success=false 时 data 通常为空,请以 code/message 判断具体错误原因(错误码见第7节)。
6. 接口详情
6.1 商品报价
POST/openapi/product/quote
下单前必须先调用本接口获取报价和 quoteToken,报价有一定时效性,避免用户确认购买时的价格与实际扣款价格不一致。
请求参数
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| openUserId | string | 是 | 贵方的用户标识 |
| platformCode | string | 是 | 商品所属平台编码(我方提供的对照表) |
| goodsId | string | 是 | 商品在该平台的 ID |
| count | int | 否 | 购买数量,默认 1 |
请求示例
{
"openUserId": "u_10001",
"platformCode": "Mercari",
"goodsId": "m123456789",
"count": 1
}
返回数据(data)
| 字段 | 类型 | 说明 |
|---|---|---|
| goodsName | string | 商品标题 |
| goodsImage | string | 商品图片 |
| originalPrice | decimal | 商品原价(原币种,如日元) |
| payPrice | decimal | 应付价(含手续费/汇率折算后) |
| exchangeRate | decimal | 本次报价使用的汇率 |
| quoteToken | string | 报价凭证,下单时必须携带 |
| expireSeconds | int | 有效期(秒),当前为 600(10分钟) |
返回示例
{
"success": true,
"code": 10000,
"message": "操作成功",
"data": {
"goodsName": "任天堂 Switch 游戏卡带",
"goodsImage": "https://xxx/goods.jpg",
"originalPrice": 5980,
"payPrice": 328.50,
"exchangeRate": 0.048,
"quoteToken": "1234567890123456789",
"expireSeconds": 600
}
}
重要说明:
quoteToken一次性使用,且10分钟内有效,过期或已被使用需要重新调用报价接口获取- 用户在贵方页面上停留过久才点击购买时,建议贵方在用户点击"立即购买"的那一刻才调用报价接口,而不是提前批量报价缓存
6.2 下单 + 余额扣款
POST/openapi/order/pay
请求参数
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| openUserId | string | 是 | 贵方的用户标识,须与报价接口传入的一致 |
| quoteToken | string | 是 | 报价接口返回的凭证 |
请求示例
{
"openUserId": "u_10001",
"quoteToken": "1234567890123456789"
}
返回数据(data)
| 字段 | 类型 | 说明 |
|---|---|---|
| orderCode | string | MaeTown 订单编号,后续查询状态用 |
| orderStatus | int | 订单状态码(见下方状态表) |
| orderStatusName | string | 订单状态中文名称 |
| payPrice | decimal | 实际支付金额 |
返回示例
{
"success": true,
"code": 10000,
"message": "操作成功",
"data": {
"orderCode": "MO20260914000123",
"orderStatus": 9,
"orderStatusName": "待采购",
"payPrice": 328.50
}
}
重要说明——同步结果 vs 后续状态变化:
- 本接口返回的是"下单+扣款"这一瞬间的结果:成功即代表余额已经扣款成功、订单已创建
- 余额不足时接口会直接返回失败(见错误码
60001),请引导用户前往 MaeTown 完成余额充值后重新下单,不会自动重试或排队等待余额到账- 订单创建成功后,MaeTown 还会进行真实的"采购"(从对应电商平台买下商品)动作,采购结果(成功发货 / 采购失败退款)是异步的,本接口不会等待采购结果。请调用下方 6.3 订单状态查询接口轮询获取最新状态
- 本期暂不提供状态变化的主动推送(webhook),需要贵方主动轮询;如果轮询体验有问题,请与我方对接人反馈,后续版本会评估补充主动通知能力
6.3 订单状态查询
GET/openapi/order/status?orderCode=MO20260914000123
请求参数
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| orderCode | string | 是 | 下单接口返回的订单编号 |
(本接口无请求体,orderCode 作为签名参数的方式和请求头三项一起参与签名计算,见第3节)
返回数据(data)
| 字段 | 类型 | 说明 |
|---|---|---|
| orderCode | string | 订单编号 |
| orderStatus | int | 订单状态码 |
| orderStatusName | string | 订单状态中文名称 |
| payPrice | decimal | 支付金额 |
订单状态码对照表(贵方只需重点关注是否到达"已发货"或"购买失败/已取消"这类终态,中间状态仅供展示参考)
| 状态码 | 名称 | 说明 |
|---|---|---|
| 8 | 未支付 | 正常不会出现(开放API下单即扣款) |
| 9 | 待采购 | 已扣款,MaeTown 正在准备采购 |
| 10 | 已采购待发货 | 采购成功 |
| 11 | 已发货待入库 | 已从海外发出,正在往 MaeTown 仓库运输 |
| 12 | 已入库 | 已到达 MaeTown 仓库 |
| 16 | 交易完成 | 全流程完成(终态) |
| 21 | 已取消 | 订单取消,会有相应退款处理(终态) |
| 25 | 购买失败 | 采购失败(如商品被抢先买走),会自动退款到用户余额(终态) |
说明:只有贵方渠道自己带来的订单才能查询到,查询他人订单会返回"订单来源记录不存在"的错误。
7. 错误码
7.1 开放平台专属错误码(21xxx)
| code | 说明 | 建议处理方式 |
|---|---|---|
| 21000 | 渠道不存在 | 检查 appKey 是否正确 |
| 21001 | 渠道已停用 | 联系我方对接人确认渠道状态 |
| 21004 | 签名参数缺失 | 检查请求头是否完整携带四项签名信息 |
| 21005 | 请求时间戳已过期 | 检查本地服务器时间是否准确(NTP同步),偏差需在±5分钟内 |
| 21006 | 请求已被处理,请勿重复提交 | 检查 nonce 是否真正随机、未复用 |
| 21007 | 签名校验失败 | 检查签名算法、参数拼接顺序、appSecret 是否正确 |
| 21008 | 绑定关系不存在 | 引导用户重新走一遍第4节的授权绑定流程 |
| 21009 | 该账号已绑定此渠道 | 通常发生在重复调用绑定确认接口时,可忽略 |
| 21011 | 报价已过期,请重新获取 | quoteToken 超过10分钟未使用,重新调用报价接口 |
| 21012 | 报价已被使用,请重新获取 | quoteToken 只能使用一次,重新调用报价接口 |
| 21013 | 订单来源记录不存在 | 查询了不属于本渠道的订单,检查 orderCode 来源 |
| 21016 | 商品链接解析失败 | platformCode/goodsId 有误,或该商品已下架/不存在 |
| 21017 | 请求过于频繁,请稍后再试 | 触发了按渠道维度的限流(默认每渠道每60秒最多120次请求,含报价/下单/查询三个接口合计),降低调用频率后重试;如果贵方业务量确实需要更高的限额,请联系我方对接人评估调整 |
| 21018 | 授权链接不存在或已过期 | 短链接有效期7天,过期需要重新调用 /openapi/auth/link 生成新的(这个错误只会出现在用户浏览器点击短链接时,不是调用签名API时返回的) |
7.2 常见通用错误码
| code | 说明 |
|---|---|
| 10000 | 成功 |
| 60001 | 用户余额不足 |
| 11009 | 订单不存在 |
8. 典型调用时序
9. 联调与上线检查清单
- 已拿到测试环境的 appKey/appSecret,并确认签名算法联调通过(可先用
/openapi/order/status查一个不存在的订单号,确认能收到签名校验通过、但订单不存在的响应,验证签名链路本身是通的) - 已确认所需商品对应的
platformCode在我方支持范围内 - 已实现绑定跳转链接的拼接与
redirectUrl回调处理 - 已实现余额不足、报价过期、绑定关系不存在等异常情况的用户引导文案
- 已实现订单状态轮询机制(建议间隔不低于30秒,避免过于频繁的请求触发限流)
如有疑问请联系 MaeTown 技术对接人。