子商户进件

代分销商及其下游卖家完成微信进件

子商户 = 实际收单的微信支付商户。一个 platform 可以有多个子商户:自身一份,每个下游卖家一份。 在 SMP 内部,每条子商户记录对应数据库的 payment_onboarding,按(tenant_id, distributor_external_user_id, provider=wechat) 三元组唯一。

所有接口的 X-Service-Codepayments

完整流程

diagram
POST /v1/payments/sub-merchants    ──创建草稿
            │
            ▼
POST /v1/payments/sub-merchants/:id/submit  ──提交到微信
            │
            ▼ (微信审核 + 签约)
status = active, sub_mchid 写入
            │
            ▼
可下单 / 收款

列出本 platform 名下子商户

GET/v1/payments/sub-merchants?distributor_external_user_id=可选

查询单条

GET/v1/payments/sub-merchants/{id}
ts
interface SubMerchant {
  id: string;
  platformId: string;
  distributorExternalUserId?: string;
  status: "pending" | "active" | "suspended";
  wxSubMchid?: string;
  wxApplymentId?: string;
  wxApplymentState?: string;     // submitted/auditing/signing/finished/rejected ...
  commissionRate: number;
  createdAt: string;
  updatedAt: string;
}

创建草稿

POST/v1/payments/sub-merchants
ts
const sub = await smp("POST", "/v1/payments/sub-merchants", "payments", {
  distributorExternalUserId: seller.id,   // 接入方系统中卖家 id;自身进件时省略
  formData: { /* 微信进件字段,可后续 submit 时再补全 */ },
});

幂等:同 (tenant_id, distributor_external_user_id) 已存在时直接返回旧记录。

提交到微信

POST/v1/payments/sub-merchants/{id}/submit
ts
const result = await smp(
  "POST",
  `/v1/payments/sub-merchants/${sub.id}/submit`,
  "payments",
  {
    formData: {
      contactName: "张三",
      contactMobile: "13800138000",
      contactEmail: "zhang@example.com",
      // 营业执照
      licenseCopy: "<上传得到的 mediaId>",
      licenseNumber: "...",
      merchantName: "...",
      legalPerson: "...",
      // 法人证件
      idCardCopy: "<mediaId>",
      idCardNational: "<mediaId>",
      idCardName: "...",
      idCardNumber: "...",
      idCardPeriodBegin: "2020-01-01",
      idCardPeriodEnd: "2030-01-01",
      // 商户信息
      merchantShortname: "...",
      servicePhone: "...",
      settlementId: "...",
      qualificationType: "...",
      // 结算账户
      accountName: "...",
      accountBank: "...",
      accountNumber: "...",
    },
  },
);

会把 formData 与已有数据合并、做完整字段校验、调微信 ISV 进件 API。返回更新后的 onboarding 记录。 如果之前已经在审核中,自动改为 query 当前状态并返回 replayed: true

必填字段一览

text
基础:
  contactName, contactMobile, contactEmail
  licenseCopy(mediaId), licenseNumber, merchantName, legalPerson
  idCardCopy, idCardNational, idCardName, idCardNumber,
  idCardPeriodBegin, idCardPeriodEnd
  merchantShortname, servicePhone, settlementId, qualificationType
  accountName, accountBank, accountNumber

按 subjectType 追加:
  SUBJECT_TYPE_ENTERPRISE
    → idCardAddress, bankAccountType=BANK_ACCOUNT_TYPE_CORPORATE

按 salesScene 追加:
  SALES_SCENES_STORE         → storeName, storeAddressCode, storeAddress,
                               storeEntrancePics[], storeIndoorPics[]
  SALES_SCENES_MINI_PROGRAM  → miniProgramAppid
  SALES_SCENES_WEB           → websiteDomain

图片字段如何上传

所有 *Copy / *Pics 字段都需要先把图片上传到微信,换 mediaId 再填回 formData。 SMP 提供 POST /admin/payment-onboardings/{tenantId}/media(multipart)做这件事; 或接入方自己直接调微信 /v3/merchant/media/upload(更省事的话用 SMP 的代理路由)。

更新

PUT/v1/payments/sub-merchants/{id}
ts
await smp("PUT", `/v1/payments/sub-merchants/${id}`, "payments", {
  wxSubMchid: "...",
  status: "active",
});

⚠️ 这个 PUT 主要用于补登 sub_mchid 等元数据。不要用它跳过进件流程。

状态轮询

进件提交后到完成可能要数小时(含人工审核 + 签约)。建议接入方做后台轮询:每 5–10 分钟调一次GET /v1/payments/sub-merchants/{id},看 wxApplymentState 变到finished + wxSubMchid 写入即可下单。