微信支付分账开发实例分析和部分源码讲解

1.微信支付分账简介

普通商户分账主要用于商户将交易成功的资金,按照一定的周期,分账给其他方,可以是合作伙伴、员工、用户或者其他分润方。

c95348de31584716a486a5b55603309e

 


2. 使用场景

员工奖励:

零售、餐饮等行业中,当销售人员销售完成后,达到可奖励的条件,可以通过分账,将销售奖励分给员工。

管理资金到账时间:

在 酒店行业 中,利用分账功能中的“冻结/解冻”能力,避免用户退款时商户账户资金不足的情况。当用户预订/入住酒店时,交易资金先冻结在酒店的账户中;当用户确认消费离店后,再利用“分账”功能中的“分账完结”解冻资金到酒店的账户中。

分润给合作伙伴:

在与他方合作的情况下,可以用“分账”功能,将交易资金分给合作伙伴,例如物流合作商。
3. 名词解释

分账方: 将部分交易资金分出的一方,这里指直连商户本身。

分账接收方: 接收资金的一方,可以是商户,可以是个人。
4. 商户分账全流程

微信支付分账开发
5. 特性说明

待分账资金冻结,可实时分账或延时分账,实现管理账期

微信支付平台提供资金冻结的能力,所有需要分账的订单,待分账资金将先冻结在商户的商户号中,商户可根据实际业务情况,在交易完成后,实时(建议1分钟后)或30天内调分账接口,传输分账指令, 微信 支付平台将根据商户的指令进行分账。

基于每笔订单按金额进行分账,可单次或多次分账,每次分给多方

实际分账时,商户传入具体的分账接收方和分账金额进行订单分账。一笔订单最多可以分50次,每一次可以分给50方。

可分给微信商户账户或个人零钱

分账是在微信体系内进行的,分账接收方的账户可以是商户微信账户或个人零钱账户。

商户的超管须配置允许分账的最大比例

 

以下是分账的源码 实例                

package com.java.demo;
 
import com.java.utils.WXPayUtility; // 引用微信支付工具库,参考:https://pay.weixin.qq.com/doc/v3/merchant/4014931831
 
import com.google.gson.annotations.SerializedName;
import com.google.gson.annotations.Expose;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
 
import java.io.IOException;
import java.io.UncheckedIOException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
 
/**
 * 请求分账
 */
public class CreateOrder {
  private static String HOST = "https://api.mch.weixin.qq.com";
  private static String METHOD = "POST";
  private static String PATH = "/v3/profitsharing/orders";
 
  public static void main(String[] args) {
    // TODO: 请准备商户开发必要参数,参考:https://pay.weixin.qq.com/doc/v3/merchant/4013070756
    CreateOrder client = new CreateOrder(
      "19xxxxxxxx",                    // 商户号,是由微信支付系统生成并分配给每个商户的唯一标识符,商户号获取方式参考 https://pay.weixin.qq.com/doc/v3/merchant/4013070756
      "1DDE55AD98Exxxxxxxxxx",         // 商户API证书序列号,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013053053
      "/path/to/apiclient_key.pem",     // 商户API证书私钥文件路径,本地文件路径
      "PUB_KEY_ID_xxxxxxxxxxxxx",      // 微信支付公钥ID,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013038816
      "/path/to/wxp_pub.pem"           // 微信支付公钥文件路径,本地文件路径
    );
 
    CreateOrderRequest request = new CreateOrderRequest();
    request.appid = "wx8888888888888888";
    request.transactionId = "4208450740201411110007820472";
    request.outOrderNo = "P20150806125346";
    request.receivers = new ArrayList<>();
    {
      CreateOrderReceiver receiversItem = new CreateOrderReceiver();
      receiversItem.type = "MERCHANT_ID";
      receiversItem.account = "86693852";
      receiversItem.name = client.encrypt("name");
      receiversItem.amount = 888L;
      receiversItem.description = "分给商户A";
      request.receivers.add(receiversItem);
    };
    request.unfreezeUnsplit = true;
    try {
      OrdersEntity response = client.run(request);
        // TODO: 请求成功,继续业务逻辑
        System.out.println(response);
    } catch (WXPayUtility.ApiException e) {
        // TODO: 请求失败,根据状态码执行不同的逻辑
        e.printStackTrace();
    }
  }
 
  public OrdersEntity run(CreateOrderRequest request) {
    String uri = PATH;
    String reqBody = WXPayUtility.toJson(request);
 
    Request.Builder reqBuilder = new Request.Builder().url(HOST + uri);
    reqBuilder.addHeader("Accept", "application/json");
    reqBuilder.addHeader("Wechatpay-Serial", wechatPayPublicKeyId);
    reqBuilder.addHeader("Authorization", WXPayUtility.buildAuthorization(mchid, certificateSerialNo,privateKey, METHOD, uri, reqBody));
    reqBuilder.addHeader("Content-Type", "application/json");
    RequestBody requestBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), reqBody);
    reqBuilder.method(METHOD, requestBody);
    Request httpRequest = reqBuilder.build();
 
    // 发送HTTP请求
    OkHttpClient client = new OkHttpClient.Builder().build();
    try (Response httpResponse = client.newCall(httpRequest).execute()) {
      String respBody = WXPayUtility.extractBody(httpResponse);
      if (httpResponse.code() >= 200 && httpResponse.code() < 300) {
        // 2XX 成功,验证应答签名
        WXPayUtility.validateResponse(this.wechatPayPublicKeyId, this.wechatPayPublicKey,
            httpResponse.headers(), respBody);
 
        // 从HTTP应答报文构建返回数据
        return WXPayUtility.fromJson(respBody, OrdersEntity.class);
      } else {
        throw new WXPayUtility.ApiException(httpResponse.code(), respBody, httpResponse.headers());
      }
    } catch (IOException e) {
      throw new UncheckedIOException("Sending request to " + uri + " failed.", e);
    }
  }
 
  private final String mchid;
  private final String certificateSerialNo;
  private final PrivateKey privateKey;
  private final String wechatPayPublicKeyId;
  private final PublicKey wechatPayPublicKey;
 
  public CreateOrder(String mchid, String certificateSerialNo, String privateKeyFilePath, String wechatPayPublicKeyId, String wechatPayPublicKeyFilePath) {
    this.mchid = mchid;
    this.certificateSerialNo = certificateSerialNo;
    this.privateKey = WXPayUtility.loadPrivateKeyFromPath(privateKeyFilePath);
    this.wechatPayPublicKeyId = wechatPayPublicKeyId;
    this.wechatPayPublicKey = WXPayUtility.loadPublicKeyFromPath(wechatPayPublicKeyFilePath);
  }
 
  public String encrypt(String plainText) {
    return WXPayUtility.encrypt(this.wechatPayPublicKey, plainText);
  }
 
  public static class CreateOrderRequest {
    @SerializedName("appid")
    public String appid;
  
    @SerializedName("transaction_id")
    public String transactionId;
  
    @SerializedName("out_order_no")
    public String outOrderNo;
  
    @SerializedName("receivers")
    public List<CreateOrderReceiver> receivers = new ArrayList<CreateOrderReceiver>();
  
    @SerializedName("unfreeze_unsplit")
    public Boolean unfreezeUnsplit;
  }
  
  public static class OrdersEntity {
    @SerializedName("transaction_id")
    public String transactionId;
  
    @SerializedName("out_order_no")
    public String outOrderNo;
  
    @SerializedName("order_id")
    public String orderId;
  
    @SerializedName("state")
    public OrderStatus state;
  
    @SerializedName("receivers")
    public List<OrderReceiverDetail> receivers = new ArrayList<OrderReceiverDetail>();
  }
  
  public static class CreateOrderReceiver {
    @SerializedName("type")
    public String type;
  
    @SerializedName("account")
    public String account;
  
    @SerializedName("name")
    public String name;
  
    @SerializedName("amount")
    public Long amount;
  
    @SerializedName("description")
    public String description;
  }
  
  public enum OrderStatus {
    @SerializedName("PROCESSING")
    PROCESSING,
    @SerializedName("FINISHED")
    FINISHED
  }
  
  public static class OrderReceiverDetail {
    @SerializedName("amount")
    public Long amount;
  
    @SerializedName("description")
    public String description;
  
    @SerializedName("type")
    public ReceiverType type;
  
    @SerializedName("account")
    public String account;
  
    @SerializedName("result")
    public DetailStatus result;
  
    @SerializedName("fail_reason")
    public DetailFailReason failReason;
  
    @SerializedName("create_time")
    public String createTime;
  
    @SerializedName("finish_time")
    public String finishTime;
  
    @SerializedName("detail_id")
    public String detailId;
  }
  
  public enum ReceiverType {
    @SerializedName("MERCHANT_ID")
    MERCHANT_ID,
    @SerializedName("PERSONAL_OPENID")
    PERSONAL_OPENID
  }
  
  public enum DetailStatus {
    @SerializedName("PENDING")
    PENDING,
    @SerializedName("SUCCESS")
    SUCCESS,
    @SerializedName("CLOSED")
    CLOSED
  }
  
  public enum DetailFailReason {
    @SerializedName("ACCOUNT_ABNORMAL")
    ACCOUNT_ABNORMAL,
    @SerializedName("NO_RELATION")
    NO_RELATION,
    @SerializedName("RECEIVER_HIGH_RISK")
    RECEIVER_HIGH_RISK,
    @SerializedName("RECEIVER_REAL_NAME_NOT_VERIFIED")
    RECEIVER_REAL_NAME_NOT_VERIFIED,
    @SerializedName("NO_AUTH")
    NO_AUTH,
    @SerializedName("RECEIVER_RECEIPT_LIMIT")
    RECEIVER_RECEIPT_LIMIT,
    @SerializedName("PAYER_ACCOUNT_ABNORMAL")
    PAYER_ACCOUNT_ABNORMAL,
    @SerializedName("INVALID_REQUEST")
    INVALID_REQUEST
  }
  
}

查询分账结果

package com.java.demo;
 
import com.java.utils.WXPayUtility; // 引用微信支付工具库,参考:https://pay.weixin.qq.com/doc/v3/merchant/4014931831
 
import com.google.gson.annotations.SerializedName;
import com.google.gson.annotations.Expose;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
 
import java.io.IOException;
import java.io.UncheckedIOException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
 
/**
 * 查询分账结果
 */
public class QueryOrder {
  private static String HOST = "https://api.mch.weixin.qq.com";
  private static String METHOD = "GET";
  private static String PATH = "/v3/profitsharing/orders/{out_order_no}";
 
  public static void main(String[] args) {
    // TODO: 请准备商户开发必要参数,参考:https://pay.weixin.qq.com/doc/v3/merchant/4013070756
    QueryOrder client = new QueryOrder(
      "19xxxxxxxx",                    // 商户号,是由微信支付系统生成并分配给每个商户的唯一标识符,商户号获取方式参考 https://pay.weixin.qq.com/doc/v3/merchant/4013070756
      "1DDE55AD98Exxxxxxxxxx",         // 商户API证书序列号,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013053053
      "/path/to/apiclient_key.pem",     // 商户API证书私钥文件路径,本地文件路径
      "PUB_KEY_ID_xxxxxxxxxxxxx",      // 微信支付公钥ID,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013038816
      "/path/to/wxp_pub.pem"           // 微信支付公钥文件路径,本地文件路径
    );
 
    QueryOrderRequest request = new QueryOrderRequest();
    request.outOrderNo = "P20150806125346";
    request.transactionId = "4208450740201411110007820472";
    try {
      OrdersEntity response = client.run(request);
        // TODO: 请求成功,继续业务逻辑
        System.out.println(response);
    } catch (WXPayUtility.ApiException e) {
        // TODO: 请求失败,根据状态码执行不同的逻辑
        e.printStackTrace();
    }
  }
 
  public OrdersEntity run(QueryOrderRequest request) {
    String uri = PATH;
    uri = uri.replace("{out_order_no}", WXPayUtility.urlEncode(request.outOrderNo));
    Map<String, Object> args = new HashMap<>();
    args.put("transaction_id", request.transactionId);
    String queryString = WXPayUtility.urlEncode(args);
    if (!queryString.isEmpty()) {
        uri = uri + "?" + queryString;
    }
 
    Request.Builder reqBuilder = new Request.Builder().url(HOST + uri);
    reqBuilder.addHeader("Accept", "application/json");
    reqBuilder.addHeader("Wechatpay-Serial", wechatPayPublicKeyId);
    reqBuilder.addHeader("Authorization", WXPayUtility.buildAuthorization(mchid, certificateSerialNo, privateKey, METHOD, uri, null));
    reqBuilder.method(METHOD, null);
    Request httpRequest = reqBuilder.build();
 
    // 发送HTTP请求
    OkHttpClient client = new OkHttpClient.Builder().build();
    try (Response httpResponse = client.newCall(httpRequest).execute()) {
      String respBody = WXPayUtility.extractBody(httpResponse);
      if (httpResponse.code() >= 200 && httpResponse.code() < 300) {
        // 2XX 成功,验证应答签名
        WXPayUtility.validateResponse(this.wechatPayPublicKeyId, this.wechatPayPublicKey,
            httpResponse.headers(), respBody);
 
        // 从HTTP应答报文构建返回数据
        return WXPayUtility.fromJson(respBody, OrdersEntity.class);
      } else {
        throw new WXPayUtility.ApiException(httpResponse.code(), respBody, httpResponse.headers());
      }
    } catch (IOException e) {
      throw new UncheckedIOException("Sending request to " + uri + " failed.", e);
    }
  }
 
  private final String mchid;
  private final String certificateSerialNo;
  private final PrivateKey privateKey;
  private final String wechatPayPublicKeyId;
  private final PublicKey wechatPayPublicKey;
 
  public QueryOrder(String mchid, String certificateSerialNo, String privateKeyFilePath, String wechatPayPublicKeyId, String wechatPayPublicKeyFilePath) {
    this.mchid = mchid;
    this.certificateSerialNo = certificateSerialNo;
    this.privateKey = WXPayUtility.loadPrivateKeyFromPath(privateKeyFilePath);
    this.wechatPayPublicKeyId = wechatPayPublicKeyId;
    this.wechatPayPublicKey = WXPayUtility.loadPublicKeyFromPath(wechatPayPublicKeyFilePath);
  }
 
  public static class QueryOrderRequest {
    @SerializedName("transaction_id")
    @Expose(serialize = false)
    public String transactionId;
  
    @SerializedName("out_order_no")
    @Expose(serialize = false)
    public String outOrderNo;
  }
  
  public static class OrdersEntity {
    @SerializedName("transaction_id")
    public String transactionId;
  
    @SerializedName("out_order_no")
    public String outOrderNo;
  
    @SerializedName("order_id")
    public String orderId;
  
    @SerializedName("state")
    public OrderStatus state;
  
    @SerializedName("receivers")
    public List<OrderReceiverDetail> receivers = new ArrayList<OrderReceiverDetail>();
  }
  
  public enum OrderStatus {
    @SerializedName("PROCESSING")
    PROCESSING,
    @SerializedName("FINISHED")
    FINISHED
  }
  
  public static class OrderReceiverDetail {
    @SerializedName("amount")
    public Long amount;
  
    @SerializedName("description")
    public String description;
  
    @SerializedName("type")
    public ReceiverType type;
  
    @SerializedName("account")
    public String account;
  
    @SerializedName("result")
    public DetailStatus result;
  
    @SerializedName("fail_reason")
    public DetailFailReason failReason;
  
    @SerializedName("create_time")
    public String createTime;
  
    @SerializedName("finish_time")
    public String finishTime;
  
    @SerializedName("detail_id")
    public String detailId;
  }
  
  public enum ReceiverType {
    @SerializedName("MERCHANT_ID")
    MERCHANT_ID,
    @SerializedName("PERSONAL_OPENID")
    PERSONAL_OPENID
  }
  
  public enum DetailStatus {
    @SerializedName("PENDING")
    PENDING,
    @SerializedName("SUCCESS")
    SUCCESS,
    @SerializedName("CLOSED")
    CLOSED
  }
  
  public enum DetailFailReason {
    @SerializedName("ACCOUNT_ABNORMAL")
    ACCOUNT_ABNORMAL,
    @SerializedName("NO_RELATION")
    NO_RELATION,
    @SerializedName("RECEIVER_HIGH_RISK")
    RECEIVER_HIGH_RISK,
    @SerializedName("RECEIVER_REAL_NAME_NOT_VERIFIED")
    RECEIVER_REAL_NAME_NOT_VERIFIED,
    @SerializedName("NO_AUTH")
    NO_AUTH,
    @SerializedName("RECEIVER_RECEIPT_LIMIT")
    RECEIVER_RECEIPT_LIMIT,
    @SerializedName("PAYER_ACCOUNT_ABNORMAL")
    PAYER_ACCOUNT_ABNORMAL,
    @SerializedName("INVALID_REQUEST")
    INVALID_REQUEST
  }
  
}

 

posted @ 2026-04-16 12:10  15889726201  阅读(4)  评论(0)    收藏  举报