Skip to content

Commit 2d496ed

Browse files
authored
🆕 #4070【微信支付】新增 V3服务商电子发票能力封装,覆盖邀约、抬头、开票、冲红、文件上传/下载等核心接口
1 parent 6d58434 commit 2d496ed

30 files changed

Lines changed: 1183 additions & 4 deletions
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# 服务商电子发票接口体系对比
2+
3+
## 结论
4+
5+
Gitee Issue `IDMETW` 补充的官方链接
6+
[获取开通服务商电子发票能力邀请链接](https://pay.weixin.qq.com/doc/v3/partner/4015941495)
7+
与 GitHub Issue [#4066](https://github.com/binarywang/WxJava/issues/4066) 给出的
8+
[开发接入准备](https://pay.weixin.qq.com/doc/v3/partner/4015792554)**同一套**微信支付
9+
V3 服务商电子发票产品文档:前者是 API 列表中的具体接口,后者是该产品的接入总览。
10+
11+
但是,当前 `weixin-java-mp``WxMpMerchantInvoiceService` 是另一套旧的公众号
12+
`/card/invoice/*` 接口;不能把它当作这两个 Issue 所要求的支付 V3 能力实现。
13+
14+
## 当前仓库既有的公众号体系
15+
16+
- 当前代码将该能力归入公众号模块:
17+
`weixin-java-mp`
18+
`WxMpMerchantInvoiceService`
19+
- 该接口的 Javadoc 明确引用公众号官方文档:
20+
[商户开票模式说明](https://developers.weixin.qq.com/doc/offiaccount/WeChat_Invoice/E_Invoice/Vendor_and_Invoicing_Platform_Mode_Instruction.html)
21+
22+
[商户开票接口列表](https://developers.weixin.qq.com/doc/offiaccount/WeChat_Invoice/E_Invoice/Vendor_API_List.html)
23+
后者当前重定向至
24+
[新版服务号文档](https://developers.weixin.qq.com/doc/service/guide/product/weixin_invoice/E_Invoice/Vendor_API_List.html)
25+
- 代码实际请求公众号 API:`/card/invoice/getauthurl`
26+
`/card/invoice/getauthdata``/card/invoice/makeoutinvoice`
27+
`/card/invoice/clearoutinvoice``/card/invoice/queryinvoceinfo` 等;见
28+
`WxMpApiUrl.Invoice`。这套接口围绕授权页、获取用户授权数据、开票、冲红和
29+
查询发票信息。
30+
- 历史提交 `058ce62a2b932633931e30762f44c561481dde5f` 将该服务及其请求/响应
31+
Bean 加入 `weixin-java-mp`;提交说明关联 #1305
32+
33+
> 说明:调研时 Gitee 页面/API 的 TLS 连接失败,未能独立读取 IDMETW 的评论;
34+
> 但用户随后提供的 `4015941495` 链接已明确该 Issue 实际指向支付 V3 服务商电子发票。
35+
36+
## 两个 Issue 共同指向的支付 V3 体系
37+
38+
- #4066 链接的[开发接入准备](https://pay.weixin.qq.com/doc/v3/partner/4015792554)
39+
属于
40+
“微信电子发票”产品,要求在**微信支付服务商号**申请“服务商电子发票”权限,
41+
并提到数电发票资源。
42+
- IDMETW 链接的[获取开通服务商电子发票能力邀请链接](https://pay.weixin.qq.com/doc/v3/partner/4015941495)
43+
位于相同产品的“API 列表”下,路径为
44+
`GET /v3/new-tax-control-fapiao/fapiaomerchant/getspinviteurl`
45+
- 同一官方文档导航的 API 列表包含:邀请子商户开通、检查子商户开票状态、创建电子
46+
发票卡券模板、配置开发选项、抬头填写链接/信息、各行业开票、冲红、查询、下载/上传
47+
发票文件、插入用户卡包,以及多个异步通知。
48+
- 例如“开具通用行业电子发票”接口为
49+
`POST /v3/new-tax-control-fapiao/fapiao-applications/issue-general`,请求域名为
50+
`https://api.mch.weixin.qq.com`,并要求微信支付 API 证书签名、服务商模式的
51+
`sub_mchid` 和唯一开票申请单号 `fapiao_apply_id`;官方文档:
52+
[开具通用行业电子发票](https://pay.weixin.qq.com/doc/v3/partner/4015792574)
53+
该页还规定敏感字段使用微信支付公钥或平台证书加密。
54+
55+
## 直接差异
56+
57+
| 维度 | 既有公众号能力 | 两个 Issue 指向的微信支付能力 |
58+
| --- | --- | --- |
59+
| SDK 模块 | `weixin-java-mp` | 应位于 `weixin-java-pay` |
60+
| 官方文档产品线 | `developers.weixin.qq.com` 的公众号电子发票 | `pay.weixin.qq.com/doc/v3/partner` 的微信支付合作伙伴电子发票 |
61+
| API 形态 | `/card/invoice/*` | `/v3/new-tax-control-fapiao/*` |
62+
| 身份/鉴权上下文 | 公众号 access token、用户授权页/授权数据 | 微信支付服务商号、子商户号、V3 签名与敏感字段加密 |
63+
| 覆盖范围 | 授权、开票、冲红、查询及公众号商户配置 | 子商户邀约/状态、模板/开发配置、行业开票、文件和卡包、通知 |
64+
65+
因此,两个 Issue 都是同一个微信支付 V3 服务商电子发票接入需求,当前代码库检索未
66+
发现对应的 `new-tax-control-fapiao` API 实现。
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package com.github.binarywang.wxpay.bean.invoice;
2+
import com.google.gson.annotations.SerializedName;
3+
import lombok.Data;
4+
import java.io.Serializable;
5+
/** 电子发票购买方抬头信息。敏感字段 phone、email 由调用方按支付文档加密。 */
6+
@Data public class BuyerInformation implements Serializable {
7+
private static final long serialVersionUID = 1L;
8+
private String type;
9+
private String name;
10+
@SerializedName("taxpayer_id") private String taxpayerId;
11+
private String address;
12+
private String telephone;
13+
@SerializedName("bank_name") private String bankName;
14+
@SerializedName("bank_account") private String bankAccount;
15+
private String phone;
16+
private String email;
17+
private Integer amount;
18+
@SerializedName("out_trade_no") private String outTradeNo;
19+
@SerializedName("fapiao_bill_type") private String fapiaoBillType;
20+
@SerializedName("user_apply_message") private String userApplyMessage;
21+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package com.github.binarywang.wxpay.bean.invoice;
2+
3+
import com.google.gson.annotations.SerializedName;
4+
import lombok.Data;
5+
import java.io.Serializable;
6+
7+
/** 创建电子发票卡券模板请求。 */
8+
@Data
9+
public class CardTemplateRequest implements Serializable {
10+
private static final long serialVersionUID = 1L;
11+
@SerializedName("sub_mchid") private String subMchid;
12+
@SerializedName("card_appid") private String cardAppid;
13+
@SerializedName("card_template_information") private TemplateInformation cardTemplateInformation;
14+
@Data public static class TemplateInformation implements Serializable {
15+
private static final long serialVersionUID = 1L;
16+
@SerializedName("payee_name") private String payeeName;
17+
@SerializedName("logo_url") private String logoUrl;
18+
@SerializedName("custom_cell") private CustomCell customCell;
19+
}
20+
@Data public static class CustomCell implements Serializable {
21+
private static final long serialVersionUID = 1L;
22+
private String words;
23+
private String description;
24+
@SerializedName("jump_url") private String jumpUrl;
25+
@SerializedName("miniprogram_user_name") private String miniprogramUserName;
26+
@SerializedName("miniprogram_path") private String miniprogramPath;
27+
}
28+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package com.github.binarywang.wxpay.bean.invoice;
2+
import com.google.gson.annotations.SerializedName;
3+
import lombok.Data;
4+
import java.io.Serializable;
5+
/** 电子发票卡券模板结果。 */
6+
@Data public class CardTemplateResult implements Serializable {
7+
private static final long serialVersionUID = 1L;
8+
@SerializedName("card_appid") private String cardAppid;
9+
@SerializedName("card_id") private String cardId;
10+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package com.github.binarywang.wxpay.bean.invoice;
2+
import com.google.gson.annotations.SerializedName;
3+
import lombok.Data;
4+
import java.io.Serializable;
5+
/** 服务商电子发票开发配置请求。 */
6+
@Data public class DevelopmentConfigRequest implements Serializable {
7+
private static final long serialVersionUID = 1L;
8+
@SerializedName("callback_url") private String callbackUrl;
9+
@SerializedName("sub_mch_code") private String subMchCode;
10+
@SerializedName("show_fapiao_cell") private Boolean showFapiaoCell;
11+
@SerializedName("support_vat_fapiao") private Boolean supportVatFapiao;
12+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package com.github.binarywang.wxpay.bean.invoice;
2+
import com.google.gson.annotations.SerializedName;
3+
import lombok.Data;
4+
import java.io.Serializable;
5+
/** 服务商电子发票开发配置结果。 */
6+
@Data public class DevelopmentConfigResult implements Serializable {
7+
private static final long serialVersionUID = 1L;
8+
@SerializedName("callback_url") private String callbackUrl;
9+
@SerializedName("show_fapiao_cell") private Boolean showFapiaoCell;
10+
@SerializedName("support_vat_fapiao") private Boolean supportVatFapiao;
11+
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package com.github.binarywang.wxpay.bean.invoice;
2+
3+
import com.google.gson.annotations.SerializedName;
4+
import lombok.Data;
5+
6+
import java.io.Serializable;
7+
import java.util.List;
8+
9+
/**
10+
* 服务商开具通用行业电子发票请求。
11+
*
12+
* @see <a href="https://pay.weixin.qq.com/doc/v3/partner/4015792574">开具通用行业电子发票</a>
13+
*/
14+
@Data
15+
public class GeneralInvoiceRequest implements Serializable {
16+
private static final long serialVersionUID = 1L;
17+
18+
@SerializedName("sub_mchid")
19+
private String subMchid;
20+
@SerializedName("fapiao_apply_id")
21+
private String fapiaoApplyId;
22+
@SerializedName("buyer_information")
23+
private BuyerInformation buyerInformation;
24+
@SerializedName("fapiao_information")
25+
private FapiaoInformation fapiaoInformation;
26+
27+
@Data
28+
public static class FapiaoInformation implements Serializable {
29+
private static final long serialVersionUID = 1L;
30+
@SerializedName("fapiao_id")
31+
private String fapiaoId;
32+
@SerializedName("total_amount")
33+
private Integer totalAmount;
34+
private List<InvoiceItem> items;
35+
@SerializedName("export_business_policy_code")
36+
private Integer exportBusinessPolicyCode;
37+
@SerializedName("vat_refund_levy_code")
38+
private Integer vatRefundLevyCode;
39+
@SerializedName("billing_person_id")
40+
private String billingPersonId;
41+
@SerializedName("billing_person")
42+
private String billingPerson;
43+
@SerializedName("fapiao_bill_type")
44+
private String fapiaoBillType;
45+
@SerializedName("transaction_information")
46+
private List<TransactionInformation> transactionInformation;
47+
private String remark;
48+
}
49+
50+
@Data
51+
public static class InvoiceItem implements Serializable {
52+
private static final long serialVersionUID = 1L;
53+
@SerializedName("tax_code")
54+
private String taxCode;
55+
@SerializedName("goods_name")
56+
private String goodsName;
57+
private String specification;
58+
private String unit;
59+
private Integer quantity;
60+
@SerializedName("total_amount")
61+
private Integer totalAmount;
62+
@SerializedName("tax_rate")
63+
private Integer taxRate;
64+
private Boolean discount;
65+
@SerializedName("preferential_policy_code")
66+
private Integer preferentialPolicyCode;
67+
}
68+
69+
@Data
70+
public static class TransactionInformation implements Serializable {
71+
private static final long serialVersionUID = 1L;
72+
@SerializedName("pay_channel")
73+
private String payChannel;
74+
@SerializedName("transaction_id")
75+
private String transactionId;
76+
@SerializedName("out_trade_no")
77+
private String outTradeNo;
78+
private Integer amount;
79+
}
80+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package com.github.binarywang.wxpay.bean.invoice;
2+
import com.google.gson.annotations.SerializedName;
3+
import lombok.Data;
4+
import java.io.Serializable;
5+
/** 行业电子发票请求;fapiaoInformation 对应不动产租赁或成品油官方请求对象。 */
6+
@Data public class IndustryInvoiceRequest implements Serializable {
7+
private static final long serialVersionUID = 1L;
8+
@SerializedName("sub_mchid") private String subMchid;
9+
@SerializedName("fapiao_apply_id") private String fapiaoApplyId;
10+
@SerializedName("buyer_information") private BuyerInformation buyerInformation;
11+
@SerializedName("fapiao_information") private Object fapiaoInformation;
12+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.github.binarywang.wxpay.bean.invoice;
2+
import com.google.gson.annotations.SerializedName;
3+
import lombok.Data;
4+
import java.io.Serializable;
5+
import java.util.List;
6+
/** 将电子发票插入微信用户卡包请求。 */
7+
@Data public class InsertCardRequest implements Serializable {
8+
private static final long serialVersionUID = 1L;
9+
@SerializedName("sub_mchid") private String subMchid;
10+
private String scene;
11+
@SerializedName("fapiao_apply_id") private String fapiaoApplyId;
12+
@SerializedName("buyer_information") private BuyerInformation buyerInformation;
13+
@SerializedName("fapiao_card_information") private List<Object> fapiaoCardInformation;
14+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.github.binarywang.wxpay.bean.invoice;
2+
import lombok.Data;
3+
import java.io.Serializable;
4+
/** 查询服务商邀请开通电子发票能力的商户条件。 */
5+
@Data public class InviteMerchantQuery implements Serializable {
6+
private static final long serialVersionUID = 1L;
7+
private String queryTimeStart;
8+
private String queryTimeEnd;
9+
private Integer offset;
10+
private Integer limit;
11+
private String inviteCode;
12+
private String mchInviteStatus;
13+
}

0 commit comments

Comments
 (0)