Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions docs/WECHAT_PAY_CHAIN_BRAND_USAGE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# 微信支付连锁品牌

`weixin-java-pay` 提供服务商模式下的品牌入驻、品牌门店和连锁品牌分账。它们与品牌红包转账、普通商户分账分别使用独立服务和接口。

品牌服务需要支持 APIv3 的 ApacheHttp 或 HttpComponents 客户端;Jodd 客户端的品牌服务入口会明确抛出 `UnsupportedOperationException`。

## 服务入口

```java
BrandApplymentService applyments = wxPayService.getBrandApplymentService();
BrandStoreService stores = wxPayService.getBrandStoreService();
BrandProfitSharingService profitSharing = wxPayService.getBrandProfitSharingService();
```

三个服务位于 `com.github.binarywang.wxpay.service`,请求与响应模型分别位于 `bean.brandapplyment`、`bean.brandstore`、`bean.brandprofitsharing`。

| 服务 | 支持操作 |
| --- | --- |
| 品牌入驻 | 提交、撤销、按业务编号查询、按申请单编号查询 |
| 品牌门店 | 创建、查询、更新、删除、绑定与解绑收款商户号 |
| 品牌分账 | 请求、查询、回退、查询回退、完结、查询剩余金额、查询最大比例、添加与删除接收方 |

## 使用示例

查询品牌入驻申请:

```java
BrandApplymentQueryResult application = applyments.queryApplymentByBusinessCode("brand_application_001");
```

查询或删除品牌门店:

```java
BrandStoreResult store = stores.query("BRAND_ID", "STORE_ID");
stores.delete("BRAND_ID", "STORE_ID");
```

查询连锁品牌分账:

```java
BrandProfitSharingQueryRequest request = new BrandProfitSharingQueryRequest();
request.setSubMchId("SUB_MCH_ID");
request.setTransactionId("WECHAT_TRANSACTION_ID");
request.setOutOrderNo("profit-sharing-001");
BrandProfitSharingResult result = profitSharing.query(request);
```

入驻与门店管理使用 `brand_id`,连锁品牌分账相关接口按文档使用 `brand_mchid` 与 `sub_mchid`,不要混用这些标识。分账是异步处理,应通过查询或通知确认最终状态,不能将受理成功等同于资金划拨完成。

## 接入条件

配置已有 APIv3 商户私钥、APIv3 密钥及微信支付平台证书或公钥。品牌入驻管理员姓名和证件号等敏感字段由 SDK 加密,请传入明文,不要预先加密。

SDK 不代替业务授权和审核:品牌入驻需要管理员授权、主体确认及审核;门店收款号需要先建立品牌关系;品牌分账需要申请连锁品牌工具箱并配置比例,支付下单按官方要求启用分账。

离线测试验证接口协议、序列化与加密;真实接口联调需要业务方提供已开通权限的测试账号。

官方文档:[品牌入驻](https://pay.weixin.qq.com/doc/v3/partner/4016985537)、[品牌门店](https://pay.weixin.qq.com/doc/v3/partner/4016628135)、[连锁品牌分账](https://pay.weixin.qq.com/doc/v3/partner/4012072637)。
1 change: 1 addition & 0 deletions weixin-java-pay/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback-slf4j1-test.version}</version>
<scope>test</scope>
</dependency>
<dependency>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.github.binarywang.wxpay.bean.brandapplyment;

import com.google.gson.annotations.SerializedName;
import lombok.Data;

import java.io.Serializable;

/** 撤销品牌入驻申请,业务申请编号与微信支付申请单号至少填写一个。 */
@Data
public class BrandApplymentCancelRequest implements Serializable {
private static final long serialVersionUID = 1L;
/** 业务申请编号。 */
@SerializedName("business_code")
private String businessCode;

/** 微信支付申请单号。 */
@SerializedName("applyment_id")
private String applymentId;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.github.binarywang.wxpay.bean.brandapplyment;

import com.google.gson.annotations.SerializedName;
import lombok.Data;

import java.io.Serializable;

/** 品牌入驻申请状态应答。 */
@Data
public class BrandApplymentQueryResult implements Serializable {
private static final long serialVersionUID = 1L;
/** 微信支付申请单号。 */
@SerializedName("applyment_id")
private String applymentId;

/** 业务申请编号。 */
@SerializedName("business_code")
private String businessCode;

/** 申请状态,使用微信支付 APPLYMENT_STATE_* 值。 */
@SerializedName("applyment_state")
private String applymentState;

/** 申请状态说明,包含并行环节状态。 */
@SerializedName("applyment_state_desc")
private String applymentStateDesc;

/** 授权确认二维码链接,管理员使用微信扫码。 */
@SerializedName("authorization_confirmation_qr_code")
private String authorizationConfirmationQrCode;

/** 审核驳回原因。 */
@SerializedName("reject_reason")
private String rejectReason;

/** 品牌 ID,入驻完成时返回。 */
@SerializedName("brand_id")
private String brandId;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
package com.github.binarywang.wxpay.bean.brandapplyment;

import com.github.binarywang.wxpay.v3.SpecEncrypt;
import com.google.gson.annotations.SerializedName;
import lombok.Data;

import java.io.Serializable;
import java.util.List;

/** 品牌入驻申请资料;图片资料填写预先上传的 MediaID。 */
@Data
public class BrandApplymentRequest implements Serializable {
private static final long serialVersionUID = 1L;
/** 服务商自定义业务申请编号,同编号可修改被驳回的申请。 */
@SerializedName("business_code")
private String businessCode;
/** 品牌管理员信息。 */
@SpecEncrypt
@SerializedName("admin_info")
private AdminInfo adminInfo;
/** 所属企业主体信息。 */
@SerializedName("subject_info")
private SubjectInfo subjectInfo;
/** 品牌基础信息。 */
@SerializedName("brand_basic_info")
private BrandBasicInfo brandBasicInfo;
/** 品牌商标资料。 */
@SerializedName("trademark")
private Trademark trademark;

/** 品牌管理员信息。 */
@Data
public static class AdminInfo implements Serializable {
private static final long serialVersionUID = 1L;

/** 管理员姓名。 */
@SpecEncrypt
@SerializedName("admin_name")
private String adminName;

/** 管理员证件类型。 */
@SerializedName("id_doc_type")
private String idDocType;

/** 管理员证件号。 */
@SpecEncrypt
@SerializedName("id_card_number")
private String idCardNumber;
}

/** 企业主体信息。 */
@Data
public static class SubjectInfo implements Serializable {
private static final long serialVersionUID = 1L;

/** 主体类型:企业或个体工商户。 */
@SerializedName("subject_type")
private String subjectType;

/** 主体名称。 */
@SerializedName("subject_name")
private String subjectName;

/** 统一社会信用代码。 */
@SerializedName("unified_social_credit_code")
private String unifiedSocialCreditCode;
}

/** 品牌基础信息。 */
@Data
public static class BrandBasicInfo implements Serializable {
private static final long serialVersionUID = 1L;

/** 品牌名称。 */
@SerializedName("brand_name")
private String brandName;

/** 品牌 Logo 图片 MediaID。 */
@SerializedName("brand_logo")
private String brandLogo;
}

/** 品牌商标资料。 */
@Data
public static class Trademark implements Serializable {
private static final long serialVersionUID = 1L;

/** 有无商标:TRADEMARK_EXISTS 或 TRADEMARK_NONE。 */
@SerializedName("trademark_exists")
private String trademarkExists;

/** 商标注册证信息。 */
@SerializedName("trademark_registration_certificate")
private TrademarkRegistrationCertificate trademarkRegistrationCertificate;

/** Logo 商标注册证信息。 */
@SerializedName("logo_trademark_registration_certificate")
private TrademarkRegistrationCertificate logoTrademarkRegistrationCertificate;

/** 无商标额外证明材料 MediaID,存量服务商字段,不可与列表同时填写。 */
@SerializedName("no_trademark_addition_prove")
private String noTrademarkAdditionProve;

/** 无商标额外证明材料 MediaID 列表,1 至 5 张。 */
@SerializedName("no_trademark_addition_prove_list")
private List<String> noTrademarkAdditionProveList;
}

/** 商标或 Logo 商标注册证信息。 */
@Data
public static class TrademarkRegistrationCertificate implements Serializable {
private static final long serialVersionUID = 1L;

/** 商标注册证 MediaID,存量服务商字段,不可与列表同时填写。 */
@SerializedName("certificate")
private String certificate;

/** 商标名称。 */
@SerializedName("name")
private String name;

/** 商标注册号。 */
@SerializedName("number")
private String number;

/** 商标有效期开始日期 YYYY-MM-DD,存量服务商字段。 */
@SerializedName("valid_begin_time")
private String validBeginTime;

/** 商标有效期结束日期 YYYY-MM-DD。 */
@SerializedName("valid_end_time")
private String validEndTime;

/** 商标国际分类,1 至 45。 */
@SerializedName("international_class")
private String internationalClass;

/** 商标注册人。 */
@SerializedName("holder")
private String holder;

/** 商标许可使用授权书 MediaID,存量服务商字段,不可与列表同时填写。 */
@SerializedName("license")
private String license;

/** 授权有效期开始日期 YYYY-MM-DD,存量服务商字段。 */
@SerializedName("authorization_begin_time")
private String authorizationBeginTime;

/** 授权有效期结束日期 YYYY-MM-DD,不得超过商标有效期。 */
@SerializedName("authorization_end_time")
private String authorizationEndTime;

/** 商标注册证 MediaID 列表,1 至 5 张。 */
@SerializedName("certificate_list")
private List<String> certificateList;

/** 许可使用授权书 MediaID 列表,1 至 5 张。 */
@SerializedName("license_list")
private List<String> licenseList;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.github.binarywang.wxpay.bean.brandapplyment;

import com.google.gson.annotations.SerializedName;
import lombok.Data;

import java.io.Serializable;

/** 提交或撤销品牌入驻申请应答。 */
@Data
public class BrandApplymentResult implements Serializable {
private static final long serialVersionUID = 1L;
/** 微信支付申请单号。 */
@SerializedName("applyment_id")
private String applymentId;

/** 业务申请编号。 */
@SerializedName("business_code")
private String businessCode;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.github.binarywang.wxpay.bean.brandprofitsharing;

import com.google.gson.annotations.SerializedName;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;

/** 订单剩余待分账金额。 */
@Data
@Builder(builderMethodName = "newBuilder")
@NoArgsConstructor
@AllArgsConstructor
public class BrandProfitSharingAmountResult implements Serializable {
private static final long serialVersionUID = 1L;

/** 微信支付订单号。 */
@SerializedName("transaction_id")
private String transactionId;

/** 剩余待分账金额,单位为分。 */
@SerializedName("unsplit_amount")
private Integer unsplitAmount;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.github.binarywang.wxpay.bean.brandprofitsharing;

import com.google.gson.annotations.SerializedName;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;

/** 完结分账并解冻剩余资金,需使用新的商户分账单号。 */
@Data
@Builder(builderMethodName = "newBuilder")
@NoArgsConstructor
@AllArgsConstructor
public class BrandProfitSharingFinishRequest implements Serializable {
private static final long serialVersionUID = 1L;

/** 订单收款商户号,可为品牌主或门店商户号。 */
@SerializedName("sub_mchid")
private String subMchId;

/** 微信支付订单号。 */
@SerializedName("transaction_id")
private String transactionId;

/** 商户分账单号。 */
@SerializedName("out_order_no")
private String outOrderNo;

/** 分账或回退原因描述。 */
@SerializedName("description")
private String description;
}
Loading
Loading