小程序提现功能升级改造

0 阅读6分钟

一、问题发现

1.1 遇到的问题

在测试小程序提现功能时,调用微信支付商家转账接口时遇到权限错误:

错误信息

{
  "code": "NO_AUTH",
  "message": "当前商户号没有相关权限,暂不支持使用"
}

1.2 问题原因

微信支付在 2025年1月15日 对商家转账功能进行了重大升级:

2025年1月15号之后申请的权限都是新的商家转账产品(对应转账接口 /v3/fund-app/mch-transfer/transfer-bills),需要用户确认才能收款。2025年1月15号之前已有权限的商户仍可使用旧的商家转账到零钱产品权限(对应转账接口 /v3/transfer/batches)。

1.3 新旧接口对比

对比维度旧版接口新版接口
用户体验后台直接打款,用户无感知必须用户手动确认收款
接口路径/v3/transfer/batches/v3/fund-app/mch-transfer/transfer-bills
核心流程商户发起 → 微信直接打款商户发起 → 用户确认 → 微信打款
超时机制无确认环节24小时未确认自动关单退款

二、流程改造方案

2.1 整体流程对比

旧版流程

用户申请提现 → 后台审核通过 → 直接调用微信支付接口 → 打款成功

新版流程

用户申请提现 → 后台审核通过 → 调用微信支付接口获取packageInfo 
→ 用户在小程序中点击"确认收款" → 调起微信支付确认页 
→ 用户确认 → 打款成功

2.2 流程时序图

sequenceDiagram
    participant 用户
    participant 小程序
    participant 后端服务
    participant 微信支付

    用户->>小程序: 1. 申请提现
    小程序->>后端服务: 2. 提交提现申请
    后端服务->>后端服务: 3. 创建订单(待审核)
    
    Note over 后端服务: 管理员审核
    
    后端服务->>微信支付: 4. 调用转账接口
    微信支付-->>后端服务: 5. 返回 package_info
    后端服务->>后端服务: 6. 更新订单状态(待确认收款)
    
    用户->>小程序: 7. 查看提现记录
    小程序->>小程序: 显示"确认收款"按钮
    用户->>小程序: 8. 点击"确认收款"
    小程序->>后端服务: 9. 获取 packageInfo
    后端服务-->>小程序: 返回 packageInfo
    小程序->>微信支付: 10. 调起 wx.requestMerchantTransfer
    微信支付->>用户: 11. 显示确认收款页
    用户->>微信支付: 12. 确认收款
    微信支付->>后端服务: 13. 回调通知打款结果
    后端服务->>后端服务: 14. 更新订单状态(打款成功)

2.3 订单状态流转

待审核(0) → 待确认收款(1) → 打款成功(2)
    ↓              ↓
审核驳回(3)    打款失败(4)

状态说明

  • 0 - 待审核:用户提交申请,等待管理员审核
  • 1 - 待确认收款:审核通过,等待用户确认收款
  • 2 - 打款成功:用户确认收款,打款成功
  • 3 - 审核驳回:管理员审核拒绝
  • 4 - 打款失败:打款失败(余额不足、用户信息错误等)

三、小程序端实现

3.1 提现记录页面改造

核心改动:新增"确认收款"按钮和确认收款逻辑

3.1.1 显示确认收款按钮

当订单状态为 1(待确认收款)时,显示"确认收款"按钮:

<template>
  <div v-if="item.status === 1" class="withdrawal-item__actions">
    <button
      class="withdrawal-item__btn"
      :disabled="isConfirming"
      @click="handleConfirmTransfer(item)"
    >
      {{ isConfirming ? '处理中...' : '确认收款' }}
    </button>
  </div>
</template>

3.1.2 实现确认收款逻辑

const handleConfirmTransfer = async (item) => {
  // 1. 版本检测
  if (!wx.canIUse('requestMerchantTransfer')) {
    wx.showModal({
      content: '你的微信版本过低,请更新至8.0.30及以上版本后重试',
      showCancel: false
    });
    return;
  }

  try {
    isConfirming.value = true;
    wx.showLoading({ title: '处理中...', mask: true });

    // 2. 调用后端接口,获取 packageInfo
    const result = await apiConfirmWithdrawalTransfer(item.withdrawalOrderNo);

    wx.hideLoading();

    // 3. 调用微信支付接口
    wx.requestMerchantTransfer({
      mchId: result.mchId,
      appId: result.appId,
      package: result.packageInfo,
      success: (res) => {
        showToast('收款确认成功,请稍候查看到账情况');
        setTimeout(() => refreshList(), 2000);
      },
      fail: (err) => {
        if (err.errMsg?.includes('cancel')) {
          showToast('已取消收款');
        } else {
          showToast('收款失败:' + err.errMsg);
        }
      },
      complete: () => {
        isConfirming.value = false;
      }
    });
  } catch (error) {
    wx.hideLoading();
    isConfirming.value = false;
    showToast(error.message || '确认打款失败,请稍后重试');
  }
};

3.1.3 状态文本映射

const getStatusText = (status: number) => {
  const statusMap = {
    0: '待审核',
    1: '待确认收款',  // 新增状态
    2: '打款成功',
    3: '审核驳回',
    4: '打款失败'
  };
  return statusMap[status] || '未知状态';
};

3.2 API 接口封装

新增确认打款接口:

/**
 * 确认提现打款(获取 packageInfo)
 */
export const apiConfirmWithdrawalTransfer = (withdrawalOrderNo: string) => {
  return request('/commission/confirmWithdrawalTransfer', {
    type: 'post',
    data: { withdrawalOrderNo }
  }) as Promise<{
    packageInfo: string;
    appId: string;
    mchId: string;
  }>;
};

四、关键技术点

4.1 微信支付新版接口

4.1.1 发起转账接口

接口路径POST /v3/fund-app/mch-transfer/transfer-bills

关键参数

{
  "appid": "wx1234567890abcdef",
  "out_bill_no": "商户单号",
  "transfer_amount": 10000,  // 单位:分
  "transfer_scene": "佣金发放",
  "openid": "用户openid",
  "user_name": "加密后的用户姓名"
}

响应示例

{
  "out_bill_no": "商户单号",
  "transfer_bill_no": "微信转账单号",
  "state": "WAIT_USER_CONFIRM",
  "package_info": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."
}

关键注意事项

  • transfer_amount 单位是分,需要将元转换为分
  • user_name 需要使用微信支付公钥加密
  • 0.3元以下不支持传入 user_name 校验
  • 返回的 package_info 需要保存,供小程序调起确认页使用

4.1.2 小程序调起确认收款

APIwx.requestMerchantTransfer

参数说明

wx.requestMerchantTransfer({
  mchId: '商户号',
  appId: '小程序appid',
  package: '从后端获取的 packageInfo',
  success: (res) => {
    // 用户确认成功
  },
  fail: (err) => {
    // 用户取消或失败
  }
});

版本要求

  • 微信版本 >= 8.0.30
  • 基础库版本 >= 3.4.5

兼容性检测

if (!wx.canIUse('requestMerchantTransfer')) {
  wx.showModal({
    content: '你的微信版本过低,请更新至8.0.30及以上版本后重试',
    showCancel: false
  });
  return;
}

4.2 超时处理

用户需在 24小时内 确认收款,超时后系统会自动关单并将资金退回商户运营账户。

建议处理方式

  1. 设置定时任务,检查超过24小时未确认的订单
  2. 调用微信支付查询接口确认订单状态
  3. 如果订单已关闭,更新订单状态为"打款失败"
  4. 解冻用户的提现金额

4.3 回调处理

微信支付会通过回调通知转账结果。

回调内容示例

{
  "out_bill_no": "商户单号",
  "transfer_bill_no": "微信转账单号",
  "state": "SUCCESS",
  "fail_reason": "",
  "transfer_success_time": "2026-03-12T15:30:00+08:00"
}

处理逻辑

  1. 验证签名
  2. 解密回调数据
  3. 根据商户单号查询订单
  4. 更新订单状态为"打款成功"
  5. 更新用户账户余额
  6. 返回成功响应

注意事项

  • 必须同时接入查询接口兜底,以防收不到回调通知
  • 回调接口需要做幂等性处理,避免重复处理

五、常见问题

5.1 权限类错误

问题:调用旧接口报错 NO_AUTH

原因:商户号是2025年1月15日之后开通的,只能使用新版接口

解决方案:改用新版接口 /v3/fund-app/mch-transfer/transfer-bills

5.2 参数类错误

问题Openid格式错误或者不属于商家公众账号

原因:openid 与 appid 不匹配

解决方案

  1. 确保使用该 appid 下获取的 openid
  2. 检查 appid 是否已绑定商户号

5.3 前端调起错误

问题package_info信息有误

原因:package 参数与后端返回的不一致

解决方案

  1. 检查参数是否完整、正确
  2. 确保没有对 packageInfo 进行额外处理(如 trim、encode 等)

问题system:access_denied

原因:在非小程序环境调起(如 webview)

解决方案:只能在小程序页面内直接调用,不支持 webview

5.4 用户体验问题

问题:用户不知道需要确认收款

解决方案

  1. 在提现记录页面显著位置显示"待确认收款"状态
  2. 提供明确的"确认收款"按钮
  3. 可以考虑推送模板消息提醒用户确认

问题:用户24小时未确认导致提现失败

解决方案

  1. 在提现申请时明确告知用户需要在24小时内确认
  2. 提供重新申请提现的入口
  3. 审核通过后立即引导用户确认收款

六、相关文档