发起转账

更新时间:2025.03.21

商家转账用户确认模式下,用户申请收款时,商户可通过此接口申请创建转账单

  • 接口返回的HTTP状态码及错误码,仅代表本次请求的结果,不能代表订单状态。

  • 接口返回的HTTP状态码为200,且状态为WAIT_USER_CONFIRM时,可认为创建转账单成功,可引导用户确认收款。其余状态请参考开发指引中的状态机描述进行处理。

  • 接口返回的HTTP状态码不为200时,请商户务必不要立即更换商户订单号重试。可根据错误码列表中的描述和接口返回的信息进行处理,并在查询原订单结果为失败或者联系客服确认情况后,再更换商户订单号进行重试。否则会有重复转账的资金风险。

注:单个商户的接口频率限制为100次/s

接口说明

支持商户:【普通商户】

请求方式:【POST】/v3/fund-app/mch-transfer/transfer-bills

请求域名:【主域名】https://api.mch.weixin.qq.com 使用该域名将访问就近的接入点

     【备域名】https://api2.mch.weixin.qq.com 使用该域名将访问异地的接入点 ,指引点击查看

请求参数

Header HTTP头参数

Authorization  必填 string

请参考签名认证生成认证信息


Accept  必填 string

请设置为application/json


Content-Type  必填 string

请设置为application/json


Wechatpay-Serial  选填 string

【微信支付公钥ID】或【微信支付平台证书序列号】 请求参数中的敏感字段,需要使用微信支付公钥加密(推荐),请参考获取微信支付公钥ID说明以及微信支付公钥加密敏感信息指引;也可以使用微信支付平台证书公钥加密,参考获取平台证书序列号平台证书加密敏感信息指引


body 包体参数

appid  必填 string(32)

【商户AppID】 是微信开放平台和微信公众平台为开发者的应用程序(APP、小程序、公众号、企业号corpid即为此AppID)提供的一个唯一标识。此处,可以填写这四种类型中的任意一种APPID,但请确保该appid与商户号有绑定关系。详见:普通商户模式开发必要参数说明


out_bill_no  必填 string(32)

【商户单号】 商户系统内部的商家单号,要求此参数只能由数字、大小写字母组成,在商户系统内部唯一


transfer_scene_id  必填 string(36)

【转账场景ID】 该笔转账使用的转账场景,可前往“商户平台-产品中心-商家转账”中申请。如:1000(现金营销),1006(企业报销)等


openid  必填 string(64)

【收款用户OpenID】 用户在商户appid下的唯一标识。发起转账前需获取到用户的OpenID,获取方式详见参数说明


user_name  选填 string

【收款用户姓名】 收款方真实姓名。需要加密传入,支持标准RSA算法和国密算法,公钥由微信侧提供。
转账金额 >= 2,000元时,该笔明细必须填写
若商户传入收款用户姓名,微信支付会校验收款用户与输入姓名是否一致,并提供电子回单


transfer_amount  必填 integer

【转账金额】 转账金额单位为“分”。


transfer_remark  必填 string(32)

【转账备注】 转账备注,用户收款时可见该备注信息,UTF8编码,最多允许32个字符


notify_url  选填 string(256)

【通知地址】 异步接收微信支付结果通知的回调地址,通知url必须为公网可访问的URL,必须为HTTPS,不能携带参数。


user_recv_perception  选填 string

【用户收款感知】 用户收款时感知到的收款原因将根据转账场景自动展示默认内容。如有其他展示需求,可在本字段传入。各场景展示的默认内容和支持传入的内容,可查看产品文档了解。


transfer_scene_report_infos  必填 array[object]

【转账场景报备信息】 各转账场景下需报备的内容,商户需要按照所属转账场景规则传参,详见转账场景报备信息字段说明

属性

请求示例

Java
Go
curl

需配合微信支付工具库 WXPayUtility 使用,请参考 Java 

1package com.java.demo;
2
3import com.java.utils.WXPayUtility; // 引用微信支付工具库,参考:https://pay.weixin.qq.com/doc/v3/merchant/4014931831
4
5import com.google.gson.annotations.SerializedName;
6import com.google.gson.annotations.Expose;
7import okhttp3.MediaType;
8import okhttp3.OkHttpClient;
9import okhttp3.Request;
10import okhttp3.RequestBody;
11import okhttp3.Response;
12
13import java.io.IOException;
14import java.io.UncheckedIOException;
15import java.security.PrivateKey;
16import java.security.PublicKey;
17import java.util.ArrayList;
18import java.util.HashMap;
19import java.util.List;
20import java.util.Map;
21
22/**
23 * 发起转账
24 */
25public class TransferToUser {
26  private static String HOST = "https://api.mch.weixin.qq.com";
27  private static String METHOD = "POST";
28  private static String PATH = "/v3/fund-app/mch-transfer/transfer-bills";
29
30  public static void main(String[] args) {
31    // TODO: 请准备商户开发必要参数,参考:https://pay.weixin.qq.com/doc/v3/merchant/4013070756
32    TransferToUser client = new TransferToUser(
33      "填入 商户号",  // 商户号,是由微信支付系统生成并分配给每个商户的唯一标识符,商户号获取方式参考 https://pay.weixin.qq.com/doc/v3/merchant/4013070756
34      "填入 商户API证书序列号", // 商户API证书序列号,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013053053
35      "填入 商户API证书私钥文件路径", // 商户API证书私钥文件路径,本地文件路径
36      "填入 微信支付公钥ID", // 微信支付公钥ID,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013038816
37      "填入 微信支付公钥文件路径" // 微信支付公钥文件路径,本地文件路径
38    );
39
40    TransferToUserRequest request = new TransferToUserRequest();
41    request.appid = "wxf636efh567hg4356";
42    request.outBillNo = "plfk2020042013";
43    request.transferSceneId = "1000";
44    request.openid = "o-MYE42l80oelYMDE34nYD456Xoy";
45    request.userName = client.encrypt("user_name");
46    request.transferAmount = 400000L;
47    request.transferRemark = "新会员开通有礼";
48    request.notifyUrl = "https://www.weixin.qq.com/wxpay/pay.php";
49    request.userRecvPerception = "现金奖励";
50    request.transferSceneReportInfos = new ArrayList<>();
51    {
52      TransferSceneReportInfo item0 = new TransferSceneReportInfo();
53      item0.infoType = "活动名称";
54      item0.infoContent = "新会员有礼";
55      request.transferSceneReportInfos.add(item0);
56      TransferSceneReportInfo item1 = new TransferSceneReportInfo();
57      item1.infoType = "奖励说明";
58      item1.infoContent = "注册会员抽奖一等奖";
59      request.transferSceneReportInfos.add(item1);
60    };
61    try {
62      TransferToUserResponse response = client.run(request);
63
64      // TODO: 请求成功,继续业务逻辑
65      System.out.println(response);
66    } catch (WXPayUtility.ApiException e) {
67      // TODO: 请求失败,根据状态码执行不同的逻辑
68      e.printStackTrace();
69    }
70  }
71
72  public TransferToUserResponse run(TransferToUserRequest request) {
73    String uri = PATH;
74    String reqBody = WXPayUtility.toJson(request);
75
76    Request.Builder reqBuilder = new Request.Builder().url(HOST + uri);
77    reqBuilder.addHeader("Accept", "application/json");
78    reqBuilder.addHeader("Wechatpay-Serial", wechatPayPublicKeyId);
79    reqBuilder.addHeader("Authorization", WXPayUtility.buildAuthorization(mchid, certificateSerialNo,privateKey, METHOD, uri, reqBody));
80    reqBuilder.addHeader("Content-Type", "application/json");
81    RequestBody requestBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), reqBody);
82    reqBuilder.method(METHOD, requestBody);
83    Request httpRequest = reqBuilder.build();
84
85    // 发送HTTP请求
86    OkHttpClient client = new OkHttpClient.Builder().build();
87    try (Response httpResponse = client.newCall(httpRequest).execute()) {
88      String respBody = WXPayUtility.extractBody(httpResponse);
89      if (httpResponse.code() >= 200 && httpResponse.code() < 300) {
90        // 2XX 成功,验证应答签名
91        WXPayUtility.validateResponse(this.wechatPayPublicKeyId, this.wechatPayPublicKey,
92            httpResponse.headers(), respBody);
93
94        // 从HTTP应答报文构建返回数据
95        return WXPayUtility.fromJson(respBody, TransferToUserResponse.class);
96      } else {
97        throw new WXPayUtility.ApiException(httpResponse.code(), respBody, httpResponse.headers());
98      }
99    } catch (IOException e) {
100      throw new UncheckedIOException("Sending request to " + uri + " failed.", e);
101    }
102  }
103
104  private final String mchid;
105  private final String certificateSerialNo;
106  private final PrivateKey privateKey;
107  private final String wechatPayPublicKeyId;
108  private final PublicKey wechatPayPublicKey;
109
110  public TransferToUser(String mchid, String certificateSerialNo, String privateKeyFilePath, String wechatPayPublicKeyId, String wechatPayPublicKeyFilePath) {
111    this.mchid = mchid;
112    this.certificateSerialNo = certificateSerialNo;
113    this.privateKey = WXPayUtility.loadPrivateKeyFromPath(privateKeyFilePath);
114    this.wechatPayPublicKeyId = wechatPayPublicKeyId;
115    this.wechatPayPublicKey = WXPayUtility.loadPublicKeyFromPath(wechatPayPublicKeyFilePath);
116  }
117
118  public String encrypt(String plainText) {
119    return WXPayUtility.encrypt(this.wechatPayPublicKey, plainText);
120  }
121
122   public static class TransferToUserResponse {
123        @SerializedName("out_bill_no")
124        public String outBillNo;
125
126        @SerializedName("transfer_bill_no")
127        public String transferBillNo;
128
129        @SerializedName("create_time")
130        public String createTime;
131
132        @SerializedName("state")
133        public TransferBillStatus state;
134
135        @SerializedName("package_info")
136        public String packageInfo;
137    }
138
139    public enum TransferBillStatus {
140        @SerializedName("ACCEPTED")
141        ACCEPTED,
142        @SerializedName("PROCESSING")
143        PROCESSING,
144        @SerializedName("WAIT_USER_CONFIRM")
145        WAIT_USER_CONFIRM,
146        @SerializedName("TRANSFERING")
147        TRANSFERING,
148        @SerializedName("SUCCESS")
149        SUCCESS,
150        @SerializedName("FAIL")
151        FAIL,
152        @SerializedName("CANCELING")
153        CANCELING,
154        @SerializedName("CANCELLED")
155        CANCELLED
156    }
157
158    public static class TransferSceneReportInfo {
159        @SerializedName("info_type")
160        public String infoType;
161
162        @SerializedName("info_content")
163        public String infoContent;
164    }
165
166    public static class TransferToUserRequest {
167        @SerializedName("appid")
168        public String appid;
169
170        @SerializedName("out_bill_no")
171        public String outBillNo;
172
173        @SerializedName("transfer_scene_id")
174        public String transferSceneId;
175
176        @SerializedName("openid")
177        public String openid;
178
179        @SerializedName("user_name")
180        public String userName;
181
182        @SerializedName("transfer_amount")
183        public Long transferAmount;
184
185        @SerializedName("transfer_remark")
186        public String transferRemark;
187
188        @SerializedName("notify_url")
189        public String notifyUrl;
190
191        @SerializedName("user_recv_perception")
192        public String userRecvPerception;
193
194        @SerializedName("transfer_scene_report_infos")
195        public List<TransferSceneReportInfo> transferSceneReportInfos;
196    }
197  
198}
199

应答参数

200 OK

out_bill_no  必填 string(32)

【商户单号】 商户系统内部的商家单号,要求此参数只能由数字、大小写字母组成,在商户系统内部唯一


transfer_bill_no  必填 string(64)

【微信转账单号】 微信转账单号,微信商家转账系统返回的唯一标识


create_time  必填 string

【单据创建时间】 单据受理成功时返回,按照使用rfc3339所定义的格式,格式为yyyy-MM-DDThh:mm:ss+TIMEZONE


state  必填 string

【单据状态】 商家转账订单状态

可选取值

  • ACCEPTED: 转账已受理

  • PROCESSING: 转账锁定资金中。如果一直停留在该状态,建议检查账户余额是否足够,如余额不足,可充值后再原单重试。

  • WAIT_USER_CONFIRM: 待收款用户确认,可拉起微信收款确认页面进行收款确认

  • TRANSFERING: 转账中,可拉起微信收款确认页面再次重试确认收款

  • SUCCESS: 转账成功

  • FAIL: 转账失败

  • CANCELING: 商户撤销请求受理成功,该笔转账正在撤销中

  • CANCELLED: 转账撤销完成


package_info  选填 string

【跳转领取页面的package信息】 跳转微信支付收款页的package信息,APP调起用户确认收款或者JSAPI调起用户确认收款 时需要使用的参数。

单据创建后,用户24小时内不领取将过期关闭,建议拉起用户确认收款页面前,先查单据状态:如单据状态为待收款用户确认,可用之前的package信息拉起;单据到终态时需更换单号重新发起转账。

应答示例

200 OK

1{
2  "out_bill_no" : "plfk2020042013",
3  "transfer_bill_no" : "1330000071100999991182020050700019480001",
4  "create_time" : "2015-05-20T13:29:35.120+08:00",
5  "state" : "ACCEPTED"
6  "package_info" : "affffddafdfafddffda=="
7}
8

 

错误码

公共错误码

状态码

错误码

描述

解决方案

400

PARAM_ERROR

参数错误

请根据错误提示正确传入参数

400

INVALID_REQUEST

请求不符合业务规则

请参阅 产品介绍开发指引 和 接口规则

403

NO_AUTH

没有相关权限

请参阅 产品介绍开发指引

401

SIGN_ERROR

签名验证不通过

请参阅 签名常见问题

500

SYSTEM_ERROR

系统异常,请稍后重试

请稍后重试

业务错误码

状态码

错误码

描述

解决方案

403

NOT_ENOUGH

资金不足

确认出资商户余额充足

429

FREQUENCY_LIMIT_EXCEED

频率超限

接口请求频率超限,当前请求结果不明确,请降低请求接口频率后,使用相同参数重试

429

RATELIMIT_EXCEEDED

频率超限

接口或同一单号请求频率超限,当前请求结果不明确,请降低请求接口频率,使用相同参数重试

429

FREQUENCY_LIMIT

频率超限

接口或同一单号请求频率超限,当前请求结果不明确,请降低请求接口频率,使用相同参数重试

400

ALREADY_EXISTS

订单已存在

单据状态已经到终态,无法发起转账,请通过查单接口确认转账结果

 

 

反馈
咨询
目录
置顶