商户进件
特约商户进件
基础支付
JSAPI支付
APP支付
H5支付
Native支付
小程序支付
合单支付
付款码支付
经营能力
支付即服务
点金计划
行业方案
平台收付通
智慧商圈
微信支付分停车服务
电子发票
营销工具
代金券
商家券
委托营销
支付有礼
小程序发券插件
H5发券
图片上传(营销专用)
现金红包
资金应用
分账
连锁品牌分账
风险合规
商户开户意愿确认
消费者投诉2.0
商户平台处置通知
其他能力
图片上传
视频上传
微信支付平台证书

连锁品牌分账开发指引

1. 接口规则

为了在保证支付安全的前提下,带给商户简单、一致且易用的开发体验,我们推出了全新的微信支付APIv3接口。该版本API的具体规则请参考“APIv3接口规则

2. 开发准备

2.1. 搭建和配置开发环境

为了帮助开发者调用开放接口,我们提供了JAVA、PHP、GO三种语言版本的开发库,封装了签名生成、签名验证、敏感信息加/解密、媒体文件上传等基础功能(更多语言版本的开发库将在近期陆续提供

测试步骤

1、根据自身开发语言,选择对应的开发库并构建项目,具体配置请参考下面链接的详细说明:

    • wechatpay-java(推荐)wechatpay-apache-httpclient,适用于Java开发者。

    • wechatpay-php(推荐)、wechatpay-guzzle-middleware,适用于PHP开发者

    注:当前开发指引接口PHP示例代码采用wechatpay-guzzle-middleware版本

    • wechatpay-go,适用于Go开发者

更多资源可前往微信支付开发者社区搜索查看

2、创建加载商户私钥、加载平台证书、初始化httpClient的通用方法


@Before
public void setup() throws IOException {
    // 加载商户私钥(privateKey:私钥字符串)
    PrivateKey merchantPrivateKey = PemUtil
            .loadPrivateKey(new ByteArrayInputStream(privateKey.getBytes("utf-8")));
 
    // 加载平台证书(mchId:商户号,mchSerialNo:商户证书序列号,apiV3Key:V3密钥)
    AutoUpdateCertificatesVerifier verifier = new AutoUpdateCertificatesVerifier(
            new WechatPay2Credentials(mchId, new PrivateKeySigner(mchSerialNo, merchantPrivateKey)),apiV3Key.getBytes("utf-8"));
 
    // 初始化httpClient
    httpClient = WechatPayHttpClientBuilder.create()
            .withMerchant(mchId, mchSerialNo, merchantPrivateKey)
            .withValidator(new WechatPay2Validator(verifier)).build();
}
 
@After
public void after() throws IOException {
    httpClient.close();
}

use GuzzleHttp\Exception\RequestException;
use WechatPay\GuzzleMiddleware\WechatPayMiddleware;
use WechatPay\GuzzleMiddleware\Util\PemUtil;
use GuzzleHttp\HandlerStack;
 
// 商户相关配置,
$merchantId = '1000100'; // 商户号
$merchantSerialNumber = 'XXXXXXXXXX'; // 商户API证书序列号
$merchantPrivateKey = PemUtil::loadPrivateKey('./path/to/mch/private/key.pem'); // 商户私钥文件路径
 
// 微信支付平台配置
$wechatpayCertificate = PemUtil::loadCertificate('./path/to/wechatpay/cert.pem'); // 微信支付平台证书文件路径
 
// 构造一个WechatPayMiddleware
$wechatpayMiddleware = WechatPayMiddleware::builder()
    ->withMerchant($merchantId, $merchantSerialNumber, $merchantPrivateKey) // 传入商户相关配置
    ->withWechatPay([ $wechatpayCertificate ]) // 可传入多个微信支付平台证书,参数类型为array
    ->build();
 
// 将WechatPayMiddleware添加到Guzzle的HandlerStack中
$stack = GuzzleHttp\HandlerStack::create();
$stack->push($wechatpayMiddleware, 'wechatpay');
 
// 创建Guzzle HTTP Client时,将HandlerStack传入,接下来,正常使用Guzzle发起API请求,WechatPayMiddleware会自动地处理签名和验签
$client = new GuzzleHttp\Client(['handler' => $stack]);

/*
    Package core 微信支付api v3 go http-client 基础库,你可以使用它来创建一个client,并向微信支付发送http请求
    只需要你在初始化客户端的时候,传递credential以及validator
    credential用来生成http header中的authorization信息
    validator则用来校验回包是否被篡改
    如果http请求返回的err为nil,一般response.Body 都不为空,你可以尝试对其进行序列化
    请注意及时关闭response.Body
    注意:使用微信支付apiv3 go库需要引入相关的包,该示例代码必须引入的包名有以下信息

    "context"
    "crypto/x509"
    "fmt"
    "io/ioutil"
    "log"
    "github.com/wechatpay-apiv3/wechatpay-go/core"
    "github.com/wechatpay-apiv3/wechatpay-go/core/option"
    "github.com/wechatpay-apiv3/wechatpay-go/utils"

    */
func SetUp() (opt []option.ClientOption, err error) {
    //商户号
    mchID := ""
    //商户证书序列号
    mchCertSerialNumber := ""
    //商户私钥文件路径
    privateKeyPath := ""
    //平台证书文件路径
    wechatCertificatePath := ""

    // 加载商户私钥
    privateKey, err := utils.LoadPrivateKeyWithPath(privateKeyPath)
    if err != nil {
        log.Printf("load private err:%s", err.Error())
        return nil, err
    }
    // 加载微信支付平台证书
    wechatPayCertificate, err := utils.LoadCertificateWithPath(wechatCertificatePath)
    if err != nil {
        log.Printf("load certificate err:%s",err)
        return nil, err
    }
    //设置header头中authorization信息
    opts := []option.ClientOption{
        option.WithMerchant(mchID, mchCertSerialNumber, privateKey), // 设置商户相关配置
        option.WithWechatPay([]*x509.Certificate{wechatPayCertificate}), // 设置微信支付平台证书,用于校验回包信息用
    }
    return opts, nil
}

3、基于接口的示例代码,替换请求参数后可发起测试

说明:

• 上面的开发库为微信支付官方开发库,其它没有审核或者控制下的第三方工具和库,微信支付不保证它们的安全性和可靠性

通过包管理工具引入SDK后,可根据下面每个接口的示例代码替换相关参数后进行快速测试

开发者如果想详细了解签名生成、签名验证、敏感信息加/解密、媒体文件上传等常用方法的具体代码实现,可阅读下面的详细说明:

    1.签名生成

    2.签名验证

    3.敏感信息加解密

    4.merchantPrivateKey(私钥)

    5.wechatpayCertificates(平台证书)

    6.APIV3Key(V3 key)

如想更详细的了解我们的接口规则,可查看我们的接口规则指引文档

2.2. 业务开发配置

2.2.1. 服务商开通连锁品牌工具箱

服务商可登录【服务商平台-合作工具箱】申请开通【连锁品牌工具箱】。进行品牌申请、查看品牌申请进度、邀请品牌总部授权、品牌门店管理、品牌流量场景管理等日常管理。

2.2.2. 设置分账比例

连锁工具箱开通后,品牌主在商户平台进入:“品牌专区-品牌交易-品牌供应链分账-供应链分账管理”中可进行分账比例的调整。

3. 快速接入

3.1. 业务流程图

业务流程时序图:


步骤4 分账前需调用《添加分账接收方》接口添加分账接收方。

步骤6 分账订单发起分账需调用《请求分账》接口进行分账,微信支付将会把结算资金分给分账接收方。

步骤8 分账完成后,微信会通过《分账动账通知》接口,主动通知商户。商户也可以通过《查询分账结果》接口,主动查询分账结果。

步骤12分账后若产生退款,则需先调用《请求分账回退》接口,请求将已经分给分账方的资金回退,再处理退款。

步骤13分账回退完成后,微信同样会通过《分账动账通知》接口,主动通知商户。商户也可以通过《查询分账回退结果》接口,主动查询分账结果。

步骤17分账结束后,商户需调用《完结分账》接口结束分账订单。

3.2. API接入(含示例代码)

文档展示了如何使用微信支付服务端 SDK 快速接入连锁品牌分账产品,完成与微信支付对接的部分。

注意

  • 文档中的代码示例是用来阐述 API 基本使用方法,代码中的示例参数需替换成商户自己账号及请求参数才能跑通
  • 以下接入步骤仅提供参考,请商户结合自身业务需求进行评估、修改。
3.2.1. 【服务端】请求分账

步骤说明:微信订单支付成功后,由服务商发起分账请求,将结算后的资金分给分账接收方。

示例代码


public void ProfitSharing() throws Exception{
    //请求URL
    HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/brand/profitsharing/orders");
    // 请求body参数
    String reqdata = "{"
            + "\"brand_mchid\":\"1900000108\","
            + "\"sub_mchid\":\"1900000109\","
            + "\"appid\":\"wx8888888888888888\","
            + "\"sub_appid\":\"wx8888888888888889\","
            + "\"transaction_id\":\"4208450740201411110007820472\","
            + "\"out_order_no\":\"P20150806125346\","
            + "\"receivers\": ["
            + "{"
            + "\"type\":\"MERCHANT_ID\","
            + "\"account\":\"1900000110\","
            + "\"amount\":100,"
            + "\"description\":\"1900000109分给商户1900000110\""
            + "}"
            + "],"
            + "\"finish\":true"
            + "}";
    StringEntity entity = new StringEntity(reqdata,"utf-8");
    entity.setContentType("application/json");
    httpPost.setEntity(entity);
    httpPost.setHeader("Accept", "application/json");

    //完成签名并执行请求
    CloseableHttpResponse response = httpClient.execute(httpPost);

    try {
        int statusCode = response.getStatusLine().getStatusCode();
        if (statusCode == 200) { //处理成功
            System.out.println("success,return body = " + EntityUtils.toString(response.getEntity()));
        } else if (statusCode == 204) { //处理成功,无返回Body
            System.out.println("success");
        } else {
            System.out.println("failed,resp code = " + statusCode+ ",return body = " + EntityUtils.toString(response.getEntity()));
            throw new IOException("request failed");
        }
    } finally {
        response.close();
    }
}

try {
    $resp = $client->request(
        'POST',
        'https://api.mch.weixin.qq.com/v3/brand/profitsharing/orders', //请求URL
        [
            // JSON请求体
            'json' => [
                "brand_mchid" => "1900000108", 
                "sub_mchid" => "1900000109", 
                "appid" => "wx8888888888888888", 
                "sub_appid" => "wx8888888888888889", 
                "transaction_id" => "4208450740201411110007820472", 
                "out_order_no" => "P20150806125346", 
                "receivers" => [
                    [
                        "type" => "MERCHANT_ID", 
                        "account" => "1900000110", 
                        "amount" => 100, 
                        "description" => "1900000109分给商户1900000110", 
                    ],
                ],
                "finish" => true,
            ],
            'headers' => [ 'Accept' => 'application/json' ]
        ]
    );
    $statusCode = $resp->getStatusCode();
    if ($statusCode == 200) { //处理成功
        echo "success,return body = " . $resp->getBody()->getContents()."\n";
    } else if ($statusCode == 204) { //处理成功,无返回Body
        echo "success";
    }
} catch (RequestException $e) {
    // 进行错误处理
    echo $e->getMessage()."\n";
    if ($e->hasResponse()) {
        echo "failed,resp code = " . $e->getResponse()->getStatusCode() . " return body = " . $e->getResponse()->getBody() . "\n";
    }
    return;
}
      

func ProfitSharing() {
       // 初始化客户端
    ctx := context.TODO()
    opts, err := SetUp()
    if err != nil {
        return
    }
    client, err := core.NewClient(ctx, opts...,)
    if err != nil{
        log.Printf("init client err:%s",err)
        return
    }
 //设置请求地址
  URL := "https://api.mch.weixin.qq.com/v3/brand/profitsharing/orders"
  //设置请求信息,此处也可以使用结构体来进行请求
  mapInfo := map[string]interface{}{
    "brand_mchid": "1900000108",
    "sub_mchid": "1900000109",
    "appid": "wx8888888888888888",
    "sub_appid": "wx8888888888888889",
    "transaction_id": "4208450740201411110007820472",
    "out_order_no": "P20150806125346",
    "receivers": []map[string]interface{}{
      map[string]interface{}{
        "type": "MERCHANT_ID",
        "account": "1900000110",
        "amount": 100,
        "description": "1900000109分给商户1900000110",
     },
    },
    "finish": true,
  }

  // 发起请求
  response, err := client.Post(ctx, URL, mapInfo)
  if err != nil{
    log.Printf("client post err:%s",err)
    return
  }
  // 校验回包内容是否有逻辑错误
  err = core.CheckResponse(response)
  if err != nil{
    log.Printf("check response err:%s",err)
    return
  }
  // 读取回包信息
  body, err := ioutil.ReadAll(response.Body)
  if err != nil{
    log.Printf("read response body err:%s",err)
    return
  }
  fmt.Println(string(body))
}
    

重要入参说明

brand_mchid:品牌主商户号,填写微信支付分配的商户号。• coupon_code:券code,券的唯一标识。

sub_mchid:子商户号,订单收款方商户号,可以是品牌主商户号,也可以是门店商户号,填写微信支付分配的商户号。

appid:公众账号ID,微信分配的公众账号ID,这里指服务商的appid

out_order_no:商户分账单号,商户系统内部的分账单号,在商户系统内部唯一(单次分账、多次分账、完结分账应使用不同的商户分账单号),同一分账单号多次请求等同一次,只能是数字、大小写字母_-|*@。

注意

更多参数、响应详情及错误码请参见请求分账接口文档

3.2.2. 【服务端】查询分账结果

步骤说明:发起分账请求后,可调用此接口查询分账结果 。发起分账完结请求后,可调用此接口查询分账完结的结果。

示例代码


public void QueryProfitSharing() throws Exception{
    //请求URL
    HttpGet httpGet = new HttpGet("https://api.mch.weixin.qq.com/v3/brand/profitsharing/orders?sub_mchid=1900000109&transaction_id=4208450740201411110007820472&out_order_no=P20150806125346");
    httpGet.setHeader("Accept", "application/json");

    //完成签名并执行请求
    CloseableHttpResponse response = httpClient.execute(httpGet);

    try {
        int statusCode = response.getStatusLine().getStatusCode();
        if (statusCode == 200) { //处理成功
            System.out.println("success,return body = " + EntityUtils.toString(response.getEntity()));
        } else if (statusCode == 204) { //处理成功,无返回Body
            System.out.println("success");
        } else {
            System.out.println("failed,resp code = " + statusCode+ ",return body = " + EntityUtils.toString(response.getEntity()));
            throw new IOException("request failed");
        }
    } finally {
        response.close();
    }
}

try {
    $resp = $client->request(
        'GET',
        'https://api.mch.weixin.qq.com/v3/brand/profitsharing/orders?sub_mchid=1900000109&transaction_id=4208450740201411110007820472&out_order_no=P20150806125346', //请求URL
        [
            'headers' => [ 'Accept' => 'application/json']
        ]
    );
    $statusCode = $resp->getStatusCode();
    if ($statusCode == 200) { //处理成功
        echo "success,return body = " . $resp->getBody()->getContents()."\n";
    } else if ($statusCode == 204) { //处理成功,无返回Body
        echo "success";
    }
} catch (RequestException $e) {
    // 进行错误处理
    echo $e->getMessage()."\n";
    if ($e->hasResponse()) {
        echo "failed,resp code = " . $e->getResponse()->getStatusCode() . " return body = " . $e->getResponse()->getBody() . "\n";
    }
    return;
}
      

func QueryProfitSharing() {
       // 初始化客户端
    ctx := context.TODO()
    opts, err := SetUp()
    if err != nil {
        return
    }
    client, err := core.NewClient(ctx, opts...,)
    if err != nil{
        log.Printf("init client err:%s",err)
        return
    }
   //设置请求地址
   URL := "https://api.mch.weixin.qq.com/v3/brand/profitsharing/orders?sub_mchid=1900000109&transaction_id=4208450740201411110007820472&out_order_no=P20150806125346"
  // 发起请求
  response, err := client.Get(ctx, URL)
  if err != nil{
    log.Printf("client get err:%s",err)
    return
  }
  // 校验回包内容是否有逻辑错误
  err = core.CheckResponse(response)
  if err != nil{
    log.Printf("check response err:%s",err)
    return
  }
  // 读取回包信息
  body, err := ioutil.ReadAll(response.Body)
  if err != nil{
    log.Printf("read response body err:%s",err)
    return
  }
  fmt.Println(string(body))
}
    

重要入参说明

transaction_id:微信订单号, 微信支付订单号

sub_mchid:子商户号,订单收款方商户号,可以是品牌主商户号,也可以是门店商户号,填写微信支付分配的商户号。

out_order_no:商户分账单号,商户系统内部的分账单号,在商户系统内部唯一(单次分账、多次分账、完结分账应使用不同的商户分账单号),同一分账单号多次请求等同一次,只能是数字、大小写字母_-|*@。

注意

更多参数、响应详情及错误码请参见查询分账结果接口文档

3.2.3.【服务端】分账动账通知

步骤说明:此功能仅针对分账接收方,当分账动账金额发生变动时,微信会把相关变动结果发送给需要实时关注的分账接收方

注意:

  • 分账动账通知是以POST 方法访问商户设置的通知url,通知的数据以JSON 格式通过请求主体(BODY)传输。通知的数据包括了加密的支付结果详情
  • 加密不能保证通知请求来自微信。微信会对发送给商户的通知进行签名,并将签名值放在通知的HTTP头Wechatpay-Signature。商户应当验证签名,以确认请求来自微信,而不是其他的第三方。签名验证的算法请参考 《微信支付API v3签名验证》
  • 分账动账通知http应答码为200或204才会当作正常接收,当回调处理异常时,应答的HTTP状态码应为500,或者4xx
  • 商户成功接收到回调通知后应返回成功的http应答码为200或204
  • 同样的通知可能会多次发送给商户系统。商户系统必须能够正确处理重复的通知。 推荐的做法是,当商户系统收到通知进行处理时,先检查对应业务数据的状态,并判断该通知是否已经处理。如果未处理,则再进行处理;如果已处理,则直接返回结果成功。在对业务数据进行状态检查和处理之前,要采用数据锁进行并发控制,以避免函数重入造成的数据混乱。

更多参数、响应详情及错误码请参见分账动账通知接口文档

3.2.4. 【服务端】请求分账回退

步骤说明:如果订单已经分账,在退款时,可以先调此接口,将已分账的资金从商户类型的分账接收方的账户回退给分账方,再发起退款。

示例代码


public void ReturnProfitSharing() throws Exception{
    //请求URL
    HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/brand/profitsharing/returnorders");
    // 请求body参数
    String reqdata = "{"
            + "\"sub_mchid\":\"1900000109\","
            + "\"order_id\":\"3008450740201411110007820472\","
            + "\"out_order_no\":\"P20150806125346\","
            + "\"out_return_no\":\"R20190516001\","
            + "\"return_mchid\":\"86693852\","
            + "\"amount\":10,"
            + "\"description\":\"分账回退\""
            + "}";
    StringEntity entity = new StringEntity(reqdata,"utf-8");
    entity.setContentType("application/json");
    httpPost.setEntity(entity);
    httpPost.setHeader("Accept", "application/json");

    //完成签名并执行请求
    CloseableHttpResponse response = httpClient.execute(httpPost);

    try {
        int statusCode = response.getStatusLine().getStatusCode();
        if (statusCode == 200) { //处理成功
            System.out.println("success,return body = " + EntityUtils.toString(response.getEntity()));
        } else if (statusCode == 204) { //处理成功,无返回Body
            System.out.println("success");
        } else {
            System.out.println("failed,resp code = " + statusCode+ ",return body = " + EntityUtils.toString(response.getEntity()));
            throw new IOException("request failed");
        }
    } finally {
        response.close();
    }
}

try {
    $resp = $client->request(
        'POST',
        'https://api.mch.weixin.qq.com/v3/brand/profitsharing/returnorders', //请求URL
        [
            // JSON请求体
            'json' => [
                "sub_mchid" => "1900000109", 
                "order_id" => "3008450740201411110007820472", 
                "out_order_no" => "P20150806125346", 
                "out_return_no" => "R20190516001", 
                "return_mchid" => "86693852", 
                "amount" => 10, 
                "description" => "分账回退",
            ],
            'headers' => [ 'Accept' => 'application/json' ]
        ]
    );
    $statusCode = $resp->getStatusCode();
    if ($statusCode == 200) { //处理成功
        echo "success,return body = " . $resp->getBody()->getContents()."\n";
    } else if ($statusCode == 204) { //处理成功,无返回Body
        echo "success";
    }
} catch (RequestException $e) {
    // 进行错误处理
    echo $e->getMessage()."\n";
    if ($e->hasResponse()) {
        echo "failed,resp code = " . $e->getResponse()->getStatusCode() . " return body = " . $e->getResponse()->getBody() . "\n";
    }
    return;
}
      

func ReturnProfitSharing() {
       // 初始化客户端
    ctx := context.TODO()
    opts, err := SetUp()
    if err != nil {
        return
    }
    client, err := core.NewClient(ctx, opts...,)
    if err != nil{
        log.Printf("init client err:%s",err)
        return
    }
   //设置请求地址
  URL := "https://api.mch.weixin.qq.com/v3/brand/profitsharing/returnorders"
  //设置请求信息,此处也可以使用结构体来进行请求
  mapInfo := map[string]interface{}{
    "sub_mchid": "1900000109",
    "order_id": "3008450740201411110007820472",
    "out_return_no": "R20190516001",
    "return_mchid": "86693852",
    "amount": 10,
    "description": "分账回退",
  }

  // 发起请求
  response, err := client.Post(ctx, URL, mapInfo)
  if err != nil{
    log.Printf("client post err:%s",err)
    return
  }
  // 校验回包内容是否有逻辑错误
  err = core.CheckResponse(response)
  if err != nil{
    log.Printf("check response err:%s",err)
    return
  }
  // 读取回包信息
  body, err := ioutil.ReadAll(response.Body)
  if err != nil{
    log.Printf("read response body err:%s",err)
    return
  }
  fmt.Println(string(body))
}
    

重要入参说明

sub_mchid:子商户号,订单收款方商户号,可以是品牌主商户号,也可以是门店商户号,填写微信支付分配的商户号。

out_order_no:商户分账单号,商户系统内部的分账单号,在商户系统内部唯一(单次分账、多次分账、完结分账应使用不同的商户分账单号),同一分账单号多次请求等同一次,只能是数字、大小写字母_-|*@。

out_return_no:商户回退单号,此回退单号是商户在自己后台生成的一个新的回退单号,在商户后台唯一,只能是数字、大小写字母_-|*@。

return_mchid:回退商户号,回退商户号只能填写原分账请求中接收分账的商户号

注意

更多参数、响应详情及错误码请参见请求分账回退接口文档

3.2.5. 【服务端】查询分账回退结果

步骤说明:商户需要核实回退结果,可调用此接口查询回退结果。如果分账回退接口返回状态为处理中,可调用此接口查询回退结果

示例代码


public void QueryReturnOrder() throws Exception{
    //请求URL
    HttpGet httpGet = new HttpGet("https://api.mch.weixin.qq.com/v3/brand/profitsharing/returnorders?sub_mchid=1900000109&out_return_no=R20190516001&order_id=4208450740201411110007820472");
    httpGet.setHeader("Accept", "application/json");

    //完成签名并执行请求
    CloseableHttpResponse response = httpClient.execute(httpGet);

    try {
        int statusCode = response.getStatusLine().getStatusCode();
        if (statusCode == 200) { //处理成功
            System.out.println("success,return body = " + EntityUtils.toString(response.getEntity()));
        } else if (statusCode == 204) { //处理成功,无返回Body
            System.out.println("success");
        } else {
            System.out.println("failed,resp code = " + statusCode+ ",return body = " + EntityUtils.toString(response.getEntity()));
            throw new IOException("request failed");
        }
    } finally {
        response.close();
    }
}

try {
    $resp = $client->request(
        'GET',
        'https://api.mch.weixin.qq.com/v3/brand/profitsharing/returnorders?sub_mchid=1900000109&out_return_no=R20190516001&order_id=4208450740201411110007820472', //请求URL
        [
            'headers' => [ 'Accept' => 'application/json']
        ]
    );
    $statusCode = $resp->getStatusCode();
    if ($statusCode == 200) { //处理成功
        echo "success,return body = " . $resp->getBody()->getContents()."\n";
    } else if ($statusCode == 204) { //处理成功,无返回Body
        echo "success";
    }
} catch (RequestException $e) {
    // 进行错误处理
    echo $e->getMessage()."\n";
    if ($e->hasResponse()) {
        echo "failed,resp code = " . $e->getResponse()->getStatusCode() . " return body = " . $e->getResponse()->getBody() . "\n";
    }
    return;
}
      

func QueryReturnOrder() {
       // 初始化客户端
    ctx := context.TODO()
    opts, err := SetUp()
    if err != nil {
        return
    }
    client, err := core.NewClient(ctx, opts...,)
    if err != nil{
        log.Printf("init client err:%s",err)
        return
    }
    URL := "https://api.mch.weixin.qq.com/v3/brand/profitsharing/returnorders?sub_mchid=1900000109&out_return_no=R20190516001&order_id=4208450740201411110007820472"
  // 发起请求
  response, err := client.Get(ctx, URL)
  if err != nil{
    log.Printf("client get err:%s",err)
    return
  }
  // 校验回包内容是否有逻辑错误
  err = core.CheckResponse(response)
  if err != nil{
    log.Printf("check response err:%s",err)
    return
  }
  // 读取回包信息
  body, err := ioutil.ReadAll(response.Body)
  if err != nil{
    log.Printf("read response body err:%s",err)
    return
  }
  fmt.Println(string(body))
}
    

重要入参说明

sub_mchid:子商户号,订单收款方商户号,可以是品牌主商户号,也可以是门店商户号,填写微信支付分配的商户号。

out_order_no:商户分账单号,商户系统内部的分账单号,在商户系统内部唯一(单次分账、多次分账、完结分账应使用不同的商户分账单号),同一分账单号多次请求等同一次,只能是数字、大小写字母_-|*@。

out_return_no:商户回退单号,调用回退接口提供的商户系统内部的回退单号。

注意

更多参数、响应详情及错误码请参见查询分账回退结果接口文档

3.2.6. 【服务端】完结分账

步骤说明:不需要进行分账的订单,可直接调用本接口将订单的金额全部解冻给分账方商户

示例代码


public void FinishProfitSharing() throws Exception{
    //请求URL
    HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/brand/profitsharing/finish-order");
    // 请求body参数
    String reqdata = "{"
            + "\"sub_mchid\":\"1900000109\","
            + "\"transaction_id\":\"4208450740201411110007820472\","
            + "\"out_order_no\":\"P20150806125346\","
            + "\"description\":\"分账完结\""
            + "}";
    StringEntity entity = new StringEntity(reqdata,"utf-8");
    entity.setContentType("application/json");
    httpPost.setEntity(entity);
    httpPost.setHeader("Accept", "application/json");

    //完成签名并执行请求
    CloseableHttpResponse response = httpClient.execute(httpPost);

    try {
        int statusCode = response.getStatusLine().getStatusCode();
        if (statusCode == 200) { //处理成功
            System.out.println("success,return body = " + EntityUtils.toString(response.getEntity()));
        } else if (statusCode == 204) { //处理成功,无返回Body
            System.out.println("success");
        } else {
            System.out.println("failed,resp code = " + statusCode+ ",return body = " + EntityUtils.toString(response.getEntity()));
            throw new IOException("request failed");
        }
    } finally {
        response.close();
    }
}

try {
    $resp = $client->request(
        'POST',
        'https://api.mch.weixin.qq.com/v3/brand/profitsharing/finish-order', //请求URL
        [
            // JSON请求体
            'json' => [
                "sub_mchid" => "1900000109", 
                "transaction_id" => "4208450740201411110007820472", 
                "out_order_no" => "P20150806125346", 
                "description" => "分账完结",
            ],
            'headers' => [ 'Accept' => 'application/json' ]
        ]
    );
    $statusCode = $resp->getStatusCode();
    if ($statusCode == 200) { //处理成功
        echo "success,return body = " . $resp->getBody()->getContents()."\n";
    } else if ($statusCode == 204) { //处理成功,无返回Body
        echo "success";
    }
} catch (RequestException $e) {
    // 进行错误处理
    echo $e->getMessage()."\n";
    if ($e->hasResponse()) {
        echo "failed,resp code = " . $e->getResponse()->getStatusCode() . " return body = " . $e->getResponse()->getBody() . "\n";
    }
    return;
}
      

func FinishProfitSharing() {
       // 初始化客户端
    ctx := context.TODO()
    opts, err := SetUp()
    if err != nil {
        return
    }
    client, err := core.NewClient(ctx, opts...,)
    if err != nil{
        log.Printf("init client err:%s",err)
        return
    }
    //设置请求地址
  URL := "https://api.mch.weixin.qq.com/v3/brand/profitsharing/finish-order"
  //设置请求信息,此处也可以使用结构体来进行请求
  mapInfo := map[string]interface{}{
    "sub_mchid": "1900000109",
    "transaction_id": "4208450740201411110007820472",
    "out_order_no": "P20150806125346",
    "description": "分账完结",
  }

  // 发起请求
  response, err := client.Post(ctx, URL, mapInfo)
  if err != nil{
    log.Printf("client post err:%s",err)
    return
  }
  // 校验回包内容是否有逻辑错误
  err = core.CheckResponse(response)
  if err != nil{
    log.Printf("check response err:%s",err)
    return
  }
  // 读取回包信息
  body, err := ioutil.ReadAll(response.Body)
  if err != nil{
    log.Printf("read response body err:%s",err)
    return
  }
  fmt.Println(string(body))
}
    

重要入参说明

transaction_id:微信订单号, 微信支付订单号

sub_mchid:子商户号,订单收款方商户号,可以是品牌主商户号,也可以是门店商户号,填写微信支付分配的商户号。

out_order_no:商户分账单号,商户系统内部的分账单号,在商户系统内部唯一(单次分账、多次分账、完结分账应使用不同的商户分账单号),同一分账单号多次请求等同一次,只能是数字、大小写字母_-|*@。

注意

更多参数、响应详情及错误码请参见完结分账接口文档

3.2.7. 【服务端】查询订单剩余待分金额

步骤说明:可调用此接口查询订单剩余待分金额

示例代码


public void QueryRemainAmounts() throws Exception{
    //请求URL
    HttpGet httpGet = new HttpGet("https://api.mch.weixin.qq.com/v3/brand/profitsharing/orders/4208450740201411110007820472/amounts");
    httpGet.setHeader("Accept", "application/json");

    //完成签名并执行请求
    CloseableHttpResponse response = httpClient.execute(httpGet);

    try {
        int statusCode = response.getStatusLine().getStatusCode();
        if (statusCode == 200) { //处理成功
            System.out.println("success,return body = " + EntityUtils.toString(response.getEntity()));
        } else if (statusCode == 204) { //处理成功,无返回Body
            System.out.println("success");
        } else {
            System.out.println("failed,resp code = " + statusCode+ ",return body = " + EntityUtils.toString(response.getEntity()));
            throw new IOException("request failed");
        }
    } finally {
        response.close();
    }
}

try {
    $resp = $client->request(
        'GET',
        'https://api.mch.weixin.qq.com/v3/brand/profitsharing/orders/4208450740201411110007820472/amounts', //请求URL
        [
            'headers' => [ 'Accept' => 'application/json']
        ]
    );
    $statusCode = $resp->getStatusCode();
    if ($statusCode == 200) { //处理成功
        echo "success,return body = " . $resp->getBody()->getContents()."\n";
    } else if ($statusCode == 204) { //处理成功,无返回Body
        echo "success";
    }
} catch (RequestException $e) {
    // 进行错误处理
    echo $e->getMessage()."\n";
    if ($e->hasResponse()) {
        echo "failed,resp code = " . $e->getResponse()->getStatusCode() . " return body = " . $e->getResponse()->getBody() . "\n";
    }
    return;
}
      

func QueryRemainAmounts() {
       // 初始化客户端
    ctx := context.TODO()
    opts, err := SetUp()
    if err != nil {
        return
    }
    client, err := core.NewClient(ctx, opts...,)
    if err != nil{
        log.Printf("init client err:%s",err)
        return
    }
   //设置请求地址
   URL := "https://api.mch.weixin.qq.com/v3/brand/profitsharing/orders/4208450740201411110007820472/amounts"
  // 发起请求
  response, err := client.Get(ctx, URL)
  if err != nil{
    log.Printf("client get err:%s",err)
    return
  }
  // 校验回包内容是否有逻辑错误
  err = core.CheckResponse(response)
  if err != nil{
    log.Printf("check response err:%s",err)
    return
  }
  // 读取回包信息
  body, err := ioutil.ReadAll(response.Body)
  if err != nil{
    log.Printf("read response body err:%s",err)
    return
  }
  fmt.Println(string(body))
}
    

重要入参说明

transaction_id:微信订单号, 微信支付订单号

注意

更多参数、响应详情及错误码请参见查询订单剩余待分金额接口文档

3.2.8. 【服务端】查询最大分账比例

步骤说明:服务商可通过此接口添加分账接收方,建立分账接收方列表。连锁加盟模式下,服务商添加的分账接收方统一在品牌主商户号维度进行管理。

示例代码


public void QueryBrandConfigs() throws Exception{
    //请求URL
    HttpGet httpGet = new HttpGet("https://api.mch.weixin.qq.com/v3/brand/profitsharing/brand-configs/1900000109");
    httpGet.setHeader("Accept", "application/json");

    //完成签名并执行请求
    CloseableHttpResponse response = httpClient.execute(httpGet);

    try {
        int statusCode = response.getStatusLine().getStatusCode();
        if (statusCode == 200) { //处理成功
            System.out.println("success,return body = " + EntityUtils.toString(response.getEntity()));
        } else if (statusCode == 204) { //处理成功,无返回Body
            System.out.println("success");
        } else {
            System.out.println("failed,resp code = " + statusCode+ ",return body = " + EntityUtils.toString(response.getEntity()));
            throw new IOException("request failed");
        }
    } finally {
        response.close();
    }
}

try {
    $resp = $client->request(
        'GET',
        'https://api.mch.weixin.qq.com/v3/brand/profitsharing/brand-configs/1900000109', //请求URL
        [
            'headers' => [ 'Accept' => 'application/json']
        ]
    );
    $statusCode = $resp->getStatusCode();
    if ($statusCode == 200) { //处理成功
        echo "success,return body = " . $resp->getBody()->getContents()."\n";
    } else if ($statusCode == 204) { //处理成功,无返回Body
        echo "success";
    }
} catch (RequestException $e) {
    // 进行错误处理
    echo $e->getMessage()."\n";
    if ($e->hasResponse()) {
        echo "failed,resp code = " . $e->getResponse()->getStatusCode() . " return body = " . $e->getResponse()->getBody() . "\n";
    }
    return;
}
      

func QueryBrandConfigs() {
       // 初始化客户端
    ctx := context.TODO()
    opts, err := SetUp()
    if err != nil {
        return
    }
    client, err := core.NewClient(ctx, opts...,)
    if err != nil{
        log.Printf("init client err:%s",err)
        return
    }
   //设置请求地址
    URL := "https://api.mch.weixin.qq.com/v3/brand/profitsharing/brand-configs/1900000109"
  // 发起请求
  response, err := client.Get(ctx, URL)
  if err != nil{
    log.Printf("client get err:%s",err)
    return
  }
  // 校验回包内容是否有逻辑错误
  err = core.CheckResponse(response)
  if err != nil{
    log.Printf("check response err:%s",err)
    return
  }
  // 读取回包信息
  body, err := ioutil.ReadAll(response.Body)
  if err != nil{
    log.Printf("read response body err:%s",err)
    return
  }
  fmt.Println(string(body))
}
    

重要入参说明

• brand_mchid:品牌主商户号,通过微信支付认证的品牌主商户号,填写微信支付分配的商户号

注意

更多参数、响应详情及错误码请参见查询最大分账比例接口文档

3.2.9. 【服务端】添加分账接收方

步骤说明:服务商可通过此接口添加分账接收方,建立分账接收方列表。连锁加盟模式下,服务商添加的分账接收方统一在品牌主商户号维度进行管理。

示例代码


public void AddReceivers() throws Exception{
    //请求URL
    HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/brand/profitsharing/receivers/add");
    // 请求body参数
    String reqdata = "{"
            + "\"brand_mchid\":\"1900000108\","
            + "\"appid\":\"wx8888888888888888\","
            + "\"sub_appid\":\"wx8888888888888889\","
            + "\"type\":\"MERCHANT_ID\","
            + "\"account\":\"1900000109\","
            + "\"name\":\"张三网络公司\","
            + "\"relation_type\":\"SUPPLIER\""
            + "}";
    StringEntity entity = new StringEntity(reqdata,"utf-8");
    entity.setContentType("application/json");
    httpPost.setEntity(entity);
    httpPost.setHeader("Accept", "application/json");

    //完成签名并执行请求
    CloseableHttpResponse response = httpClient.execute(httpPost);

    try {
        int statusCode = response.getStatusLine().getStatusCode();
        if (statusCode == 200) { //处理成功
            System.out.println("success,return body = " + EntityUtils.toString(response.getEntity()));
        } else if (statusCode == 204) { //处理成功,无返回Body
            System.out.println("success");
        } else {
            System.out.println("failed,resp code = " + statusCode+ ",return body = " + EntityUtils.toString(response.getEntity()));
            throw new IOException("request failed");
        }
    } finally {
        response.close();
    }
}

try {
    $resp = $client->request(
        'POST',
        'https://api.mch.weixin.qq.com/v3/brand/profitsharing/receivers/add', //请求URL
        [
            // JSON请求体
            'json' => [
                "brand_mchid" => "1900000108", 
                "appid" => "wx8888888888888888", 
                "sub_appid" => "wx8888888888888889", 
                "type" => "MERCHANT_ID", 
                "account" => "1900000109", 
                "name" => "张三网络公司", 
                "relation_type" => "SUPPLIER",
            ],
            'headers' => [ 'Accept' => 'application/json' ]
        ]
    );
    $statusCode = $resp->getStatusCode();
    if ($statusCode == 200) { //处理成功
        echo "success,return body = " . $resp->getBody()->getContents()."\n";
    } else if ($statusCode == 204) { //处理成功,无返回Body
        echo "success";
    }
} catch (RequestException $e) {
    // 进行错误处理
    echo $e->getMessage()."\n";
    if ($e->hasResponse()) {
        echo "failed,resp code = " . $e->getResponse()->getStatusCode() . " return body = " . $e->getResponse()->getBody() . "\n";
    }
    return;
}
      

func AddReceivers() {
       // 初始化客户端
    ctx := context.TODO()
    opts, err := SetUp()
    if err != nil {
        return
    }
    client, err := core.NewClient(ctx, opts...,)
    if err != nil{
        log.Printf("init client err:%s",err)
        return
    }
    //设置请求地址
  URL := "https://api.mch.weixin.qq.com/v3/brand/profitsharing/receivers/add"
  //设置请求信息,此处也可以使用结构体来进行请求
  mapInfo := map[string]interface{}{
    "brand_mchid": "1900000108",
    "appid": "wx8888888888888888",
    "sub_appid": "wx8888888888888889",
    "type": "MERCHANT_ID",
    "account": "1900000109",
    "name": "张三网络公司",
    "relation_type": "SUPPLIER",
  }

  // 发起请求
  response, err := client.Post(ctx, URL, mapInfo)
  if err != nil{
    log.Printf("client post err:%s",err)
    return
  }
  // 校验回包内容是否有逻辑错误
  err = core.CheckResponse(response)
  if err != nil{
    log.Printf("check response err:%s",err)
    return
  }
  // 读取回包信息
  body, err := ioutil.ReadAll(response.Body)
  if err != nil{
    log.Printf("read response body err:%s",err)
    return
  }
  fmt.Println(string(body))
}
本系统由微信支付联合产品部承担策划建设


    

重要入参说明

• brand_mchid:品牌主商户号,通过微信支付认证的品牌主商户号,填写微信支付分配的商户号

• appid:公众账号ID,微信分配的公众账号ID,这里指服务商的appid

• type:分账接收方类型,枚举值:

MERCHANT_ID:商户号(mch_id或者sub_mch_id)

PERSONAL_OPENID:个人openid(由服务商的APPID转换得到)

PERSONAL_SUB_OPENID:个人sub_openid(由品牌主的APPID转换得到)

• account:分账接收方账号

分账接收方类型为:MERCHANT_ID时,分账接收方账号为商户号(mch_id或者sub_mch_id)

分账接收方类型为:PERSONAL_OPENID时,分账接收方账号为个人openid

分账接收方类型为:PESONAL_SUB_OPENID时,分账接收方账号为个人sub_openid

注意

更多参数、响应详情及错误码请参见添加分账接收方接口文档

3.2.10. 【服务端】 删除分账接收方

步骤说明:服务商发起删除分账接收方请求。删除后,不再支持品牌主或门店分到该分账接收方。

示例代码


public void DelReceivers() throws Exception{
    //请求URL
    HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/brand/profitsharing/receivers/delete");
    // 请求body参数
    String reqdata = "{"
            + "\"brand_mchid\":\"1900000108\","
            + "\"appid\":\"wx8888888888888888\","
            + "\"sub_appid\":\"wx8888888888888889\","
            + "\"type\":\"MERCHANT_ID\","
            + "\"account\":\"1900000109\""
            + "}";
    StringEntity entity = new StringEntity(reqdata,"utf-8");
    entity.setContentType("application/json");
    httpPost.setEntity(entity);
    httpPost.setHeader("Accept", "application/json");

    //完成签名并执行请求
    CloseableHttpResponse response = httpClient.execute(httpPost);

    try {
        int statusCode = response.getStatusLine().getStatusCode();
        if (statusCode == 200) { //处理成功
            System.out.println("success,return body = " + EntityUtils.toString(response.getEntity()));
        } else if (statusCode == 204) { //处理成功,无返回Body
            System.out.println("success");
        } else {
            System.out.println("failed,resp code = " + statusCode+ ",return body = " + EntityUtils.toString(response.getEntity()));
            throw new IOException("request failed");
        }
    } finally {
        response.close();
    }
}

try {
    $resp = $client->request(
        'POST',
        'https://api.mch.weixin.qq.com/v3/brand/profitsharing/receivers/delete', //请求URL
        [
            // JSON请求体
            'json' => [
                "brand_mchid" => "1900000108", 
                "appid" => "wx8888888888888888", 
                "sub_appid" => "wx8888888888888889", 
                "type" => "MERCHANT_ID", 
                "account" => "1900000109",
            ],
            'headers' => [ 'Accept' => 'application/json' ]
        ]
    );
    $statusCode = $resp->getStatusCode();
    if ($statusCode == 200) { //处理成功
        echo "success,return body = " . $resp->getBody()->getContents()."\n";
    } else if ($statusCode == 204) { //处理成功,无返回Body
        echo "success";
    }
} catch (RequestException $e) {
    // 进行错误处理
    echo $e->getMessage()."\n";
    if ($e->hasResponse()) {
        echo "failed,resp code = " . $e->getResponse()->getStatusCode() . " return body = " . $e->getResponse()->getBody() . "\n";
    }
    return;
}
      

func DelReceivers() {
       // 初始化客户端
    ctx := context.TODO()
    opts, err := SetUp()
    if err != nil {
        return
    }
    client, err := core.NewClient(ctx, opts...,)
    if err != nil{
        log.Printf("init client err:%s",err)
        return
    }
    //设置请求地址
  URL := "https://api.mch.weixin.qq.com/v3/brand/profitsharing/receivers/delete"
  //设置请求信息,此处也可以使用结构体来进行请求
  mapInfo := map[string]interface{}{
    "brand_mchid": "1900000108",
    "appid": "wx8888888888888888",
    "sub_appid": "wx8888888888888889",
    "type": "MERCHANT_ID",
    "account": "1900000109",
  }

  // 发起请求
  response, err := client.Post(ctx, URL, mapInfo)
  if err != nil{
    log.Printf("client post err:%s",err)
    return
  }
  // 校验回包内容是否有逻辑错误
  err = core.CheckResponse(response)
  if err != nil{
    log.Printf("check response err:%s",err)
    return
  }
  // 读取回包信息
  body, err := ioutil.ReadAll(response.Body)
  if err != nil{
    log.Printf("read response body err:%s",err)
    return
  }
  fmt.Println(string(body))
}
    

重要入参说明

• brand_mchid:品牌主商户号,通过微信支付认证的品牌主商户号,填写微信支付分配的商户号

• appid:公众账号ID,微信分配的公众账号ID,这里指服务商的appid

• type:分账接收方类型,枚举值:

MERCHANT_ID:商户号(mch_id或者sub_mch_id)

PERSONAL_OPENID:个人openid(由服务商的APPID转换得到)

PERSONAL_SUB_OPENID:个人sub_openid(由品牌主的APPID转换得到)

• account:分账接收方账号

分账接收方类型为:MERCHANT_ID时,分账接收方账号为商户号(mch_id或者sub_mch_id)

分账接收方类型为:PERSONAL_OPENID时,分账接收方账号为个人openid

分账接收方类型为:PESONAL_SUB_OPENID时,分账接收方账号为个人sub_openid

注意

更多参数、响应详情及错误码请参见删除分账接收方接口文档

4. 常见问题

Q:分账调用“请求分账接口”返回“非分账订单不支持分账”

A:请按照以下几点检查:

1. 微信订单号填写错误

2. 下单的时候未传分账标识(profit_sharing)的订单,是没有分账权限的

Q:分账调用“请求分账接口”返回“分账金额不足”

A:请按照以下几点检查:

1. 该订单已全额退款,没有资金可以分账

2. 已经调用过“请求单次分账”,订单剩余的待分账金额已经解冻,已无分账资金

2. 超过订单可分账金额或者该订单已无可分账金额,请检查确认

Q:分账调用“请求分账接口”返回“订单处理中,暂时无法分账,请稍后再试”

A:请按照以下几点检查:

1. 请在订单支付成功1分钟后再调用分账接口

2. 未结算的订单,请在结算后再调用分账接口请求分账

3. 老资金流商户的订单,不支持分账

4. 商户开通了收支分离但手续费账户余额不足(手续费账户最低余额要求是100元以上,在充值手续费账户1小时后,订单会正常结算,即可正常调用分账接口)

Q:分账调用“请求分账接口”返回“分账接收商户全称不匹配”

A:请按照以下几点检查:

1. 分账接收商户全称填写错误,请填写正确的商户全称

2. 接口需要使用UTF-8编码

Q:分账调用“添加分账接收方接口”返回“系统繁忙,请稍后重试”

A:receiver中的参数account错误,参数account的规则如下:

1. 类型是MERCHANT_ID时,是商户ID

2. 类型是PERSONAL_OPENID时,是个人openid

3. 类型是PERSONAL_SUB_OPENID时,是个人sub_openid

4. APPID与mchid不匹配也会报该错误,请检查确认

5. 签名类型错误,分账接口签名类型目前只支持HMAC-SHA256



技术咨询

文档反馈