From 0e2186632a30a40b854609d9732477cdfc184d1b Mon Sep 17 00:00:00 2001 From: LinXiaoHuChong Date: Sun, 29 Nov 2020 23:00:38 +0800 Subject: [PATCH] =?UTF-8?q?:new:=20#1746:=20=E3=80=90=E4=BC=81=E4=B8=9A?= =?UTF-8?q?=E5=BE=AE=E4=BF=A1=E3=80=91=E7=AC=AC=E4=B8=89=E6=96=B9=E5=BA=94?= =?UTF-8?q?=E7=94=A8=E5=A2=9E=E5=8A=A0=E6=8E=88=E6=9D=83=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3=EF=BC=8C=E5=90=8C=E6=97=B6=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E5=90=91=E5=91=98=E5=B7=A5=E4=BB=98=E6=AC=BE=E7=9A=84=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../weixin/cp/tp/service/WxCpTpService.java | 16 +- .../service/impl/BaseWxCpTpServiceImpl.java | 25 ++ .../bean/entwxpay/EntWxEmpPayRequest.java | 229 ++++++++++++++++++ .../wxpay/service/EntPayService.java | 12 + .../wxpay/service/impl/EntPayServiceImpl.java | 19 ++ .../binarywang/wxpay/util/SignUtils.java | 25 +- 6 files changed, 324 insertions(+), 2 deletions(-) create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entwxpay/EntWxEmpPayRequest.java diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/tp/service/WxCpTpService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/tp/service/WxCpTpService.java index 9bc3684ef..104736883 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/tp/service/WxCpTpService.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/tp/service/WxCpTpService.java @@ -154,6 +154,20 @@ public interface WxCpTpService { */ String getPreAuthUrl(String redirectUri, String state) throws WxErrorException; + /** + *
+   *   获取预授权链接,测试环境下使用
+   *   @Link https://work.weixin.qq.com/api/doc/90001/90143/90602
+   * 
+ * + * @param redirectUri 授权完成后的回调网址 + * @param state a-zA-Z0-9的参数值(不超过128个字节),用于第三方自行校验session,防止跨域攻击 + * @param authType 授权类型:0 正式授权, 1 测试授权。 + * @return pre auth url + * @throws WxErrorException the wx error exception + */ + String getPreAuthUrl(String redirectUri, String state, int authType) throws WxErrorException; + /** * 获取企业的授权信息 * @@ -278,7 +292,7 @@ public interface WxCpTpService { *
    * 获取访问用户敏感信息
    * 
- * + * * @param userTicket * @return */ diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/tp/service/impl/BaseWxCpTpServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/tp/service/impl/BaseWxCpTpServiceImpl.java index c3bbe95c6..5726204fb 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/tp/service/impl/BaseWxCpTpServiceImpl.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/tp/service/impl/BaseWxCpTpServiceImpl.java @@ -1,6 +1,7 @@ package me.chanjar.weixin.cp.tp.service.impl; import com.google.common.base.Joiner; +import com.google.gson.Gson; import com.google.gson.JsonObject; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; @@ -210,6 +211,30 @@ public abstract class BaseWxCpTpServiceImpl implements WxCpTpService, Requ return preAuthUrl; } + @Override + @SneakyThrows + public String getPreAuthUrl(String redirectUri, String state, int authType) throws WxErrorException { + String result = get(configStorage.getApiUrl(GET_PREAUTH_CODE), null); + WxCpTpPreauthCode preAuthCode = WxCpTpPreauthCode.fromJson(result); + String setSessionUrl = "https://qyapi.weixin.qq.com/cgi-bin/service/set_session_info"; + + Map sessionInfo = new HashMap<>(1); + sessionInfo.put("auth_type", authType); + Map param = new HashMap<>(2); + param.put("pre_auth_code", preAuthCode.getPreAuthCode()); + param.put("session_info", sessionInfo); + String postData = new Gson().toJson(param); + + post(setSessionUrl, postData); + + String preAuthUrl = "https://open.work.weixin.qq.com/3rdapp/install?suite_id=" + configStorage.getSuiteId() + + "&pre_auth_code=" + preAuthCode.getPreAuthCode() + "&redirect_uri=" + URLEncoder.encode(redirectUri, "utf-8"); + if (StringUtils.isNotBlank(state)) { + preAuthUrl += "&state=" + state; + } + return preAuthUrl; + } + @Override public WxCpTpAuthInfo getAuthInfo(String authCorpId, String permanentCode) throws WxErrorException { JsonObject jsonObject = new JsonObject(); diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entwxpay/EntWxEmpPayRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entwxpay/EntWxEmpPayRequest.java new file mode 100644 index 000000000..37c0d038d --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entwxpay/EntWxEmpPayRequest.java @@ -0,0 +1,229 @@ +package com.github.binarywang.wxpay.bean.entwxpay; + +import com.github.binarywang.wxpay.bean.request.BaseWxPayRequest; +import com.github.binarywang.wxpay.exception.WxPayException; +import com.thoughtworks.xstream.annotations.XStreamAlias; +import lombok.*; +import me.chanjar.weixin.common.annotation.Required; + +import java.util.Map; + +/** + * Created on 2020/11/29. + * 向员工付款请求对象 + * @author 拎小壶冲 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@Builder(builderMethodName = "newBuilder") +@NoArgsConstructor +@AllArgsConstructor +@XStreamAlias("xml") +public class EntWxEmpPayRequest extends BaseWxPayRequest { + + /** + *
+   * 字段名:商户订单号.
+   * 变量名:partner_trade_no
+   * 是否必填:是
+   * 示例值:10000098201411111234567890
+   * 类型:String
+   * 描述:商户订单号
+   * 
+ */ + @Required + @XStreamAlias("partner_trade_no") + private String partnerTradeNo; + + /** + *
+   * 字段名:需保持唯一性 用户openid.
+   * 变量名:openid
+   * 是否必填:是
+   * 示例值:oxTWIuGaIt6gTKsQRLau2M0yL16E
+   * 类型:String
+   * 描述:商户appid下,某用户的openid
+   * 
+ */ + @Required + @XStreamAlias("openid") + private String openid; + + /** + *
+   * 字段名:设备号.
+   * 变量名:device_info
+   * 是否必填:否
+   * 示例值:13467007045764
+   * 类型:String(32)
+   * 描述:微信支付分配的终端设备号
+   * 
+ */ + @XStreamAlias("device_info") + private String deviceInfo; + + /** + *
+   * 字段名:校验用户姓名选项.
+   * 变量名:check_name
+   * 是否必填:是
+   * 示例值:OPTION_CHECK
+   * 类型:String
+   * 描述:NO_CHECK:不校验真实姓名 
+   * FORCE_CHECK:强校验真实姓名(未实名认证的用户会校验失败,无法转账) 
+   * OPTION_CHECK:针对已实名认证的用户才校验真实姓名(未实名认证用户不校验,可以转账成功)
+   * 
+ */ + @Required + @XStreamAlias("check_name") + private String checkName; + + /** + *
+   * 字段名:收款用户姓名.
+   * 变量名:re_user_name
+   * 是否必填:可选
+   * 示例值:马花花
+   * 类型:String
+   * 描述:收款用户真实姓名。
+   * 如果check_name设置为FORCE_CHECK或OPTION_CHECK,  则必填用户真实姓名
+   * 
+ */ + @XStreamAlias("re_user_name") + private String reUserName; + + /** + *
+   * 字段名:金额.
+   * 变量名:amount
+   * 是否必填:是
+   * 示例值:10099
+   * 类型:int
+   * 描述:企业付款金额, 单位为分
+   * 
+ */ + @Required + @XStreamAlias("amount") + private Integer amount; + + /** + *
+   * 字段名:企业付款描述信息.
+   * 变量名:desc
+   * 是否必填:是
+   * 示例值:理赔
+   * 类型:String
+   * 描述:企业付款操作说明信息。必填。
+   * 
+ */ + @Required + @XStreamAlias("desc") + private String description; + + /** + *
+   * 字段名:Ip地址.
+   * 变量名:spbill_create_ip
+   * 是否必填:是
+   * 示例值:192.168.0.1
+   * 类型:String(32)
+   * 描述:调用接口的机器Ip地址
+   * 
+ */ + @Required + @XStreamAlias("spbill_create_ip") + private String spbillCreateIp; + + /** + *
+   *   字段名: 付款消息类型
+   *   变量名: ww_msg_type
+   *   是否必填: 是
+   *   示例值:NORMAL_MSG
+   *   描述:NORMAL_MSG:普通付款消息 APPROVAL _MSG:审批付款消息
+   * 
+ */ + @Required + @XStreamAlias("ww_msg_type") + private String wwMsgType; + + /** + *
+   *   字段名: 审批单号
+   *   变量名: approval_number
+   *   是否必填: 否
+   *   示例值: 201705160008
+   *   描述:ww_msg_type为APPROVAL _MSG时,需要填写approval_number
+   * 
+ */ + @XStreamAlias("approval_number") + private String approvalNumber; + + /** + *
+   *   字段名: 审批类型
+   *   变量名: approval_type
+   *   是否必填: 否
+   *   示例值: 1
+   *   描述:ww_msg_type为APPROVAL _MSG时,需要填写1
+   * 
+ */ + @XStreamAlias("approval_type") + private Integer approvalType; + + + /** + *
+   *   字段名: 项目名称
+   *   变量名: act_name
+   *   是否必填: 是
+   *   示例值: 产品部门报销
+   *   描述:项目名称,最长50个utf8字符
+   * 
+ */ + @Required + @XStreamAlias("act_name") + private String actName; + + /** + *
+   *   字段名: 付款的应用id
+   *   变量名: agentid
+   *   是否必填: 否
+   *   示例值: 1
+   *   描述:以企业应用的名义付款,企业应用id,整型,可在企业微信管理端应用的设置页面查看。
+   * 
+ */ + @XStreamAlias("agentid") + private Integer agentId; + + + @Override + protected void checkConstraints() throws WxPayException { + + } + + @Override + protected boolean isWxWorkSign() { + return true; + } + + @Override + protected void storeMap(Map map) { + map.put("appid", appid); + map.put("mch_id", mchId); + map.put("device_info", deviceInfo); + map.put("partner_trade_no", partnerTradeNo); + map.put("openid", openid); + map.put("check_name", checkName); + map.put("re_user_name", reUserName); + map.put("amount", amount.toString()); + map.put("desc", description); + map.put("spbill_create_ip", spbillCreateIp); + map.put("act_name", actName); + map.put("ww_msg_type", wwMsgType); + map.put("approval_number", approvalNumber); + map.put("approval_type", approvalType.toString()); + map.put("agentid", agentId.toString()); + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/EntPayService.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/EntPayService.java index 1b1b76b15..ed159275b 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/EntPayService.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/EntPayService.java @@ -1,6 +1,7 @@ package com.github.binarywang.wxpay.service; import com.github.binarywang.wxpay.bean.entpay.*; +import com.github.binarywang.wxpay.bean.entwxpay.EntWxEmpPayRequest; import com.github.binarywang.wxpay.exception.WxPayException; /** @@ -143,4 +144,15 @@ public interface EntPayService { * @throws WxPayException the wx pay exception */ EntPayRedpackQueryResult queryEnterpriseRedpack(EntPayRedpackQueryRequest request) throws WxPayException; + + /** + * 向员工付款 + * 文档详见 https://work.weixin.qq.com/api/doc/90000/90135/90278 + * 接口链接 https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/paywwsptrans2pocket + * + * @param request 请求对象 + * @return EntPayResult the ent pay result + * @throws WxPayException the wx pay exception + */ + EntPayResult toEmpPay(EntWxEmpPayRequest request) throws WxPayException; } diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/EntPayServiceImpl.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/EntPayServiceImpl.java index be823c8da..db464936c 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/EntPayServiceImpl.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/EntPayServiceImpl.java @@ -1,6 +1,7 @@ package com.github.binarywang.wxpay.service.impl; import com.github.binarywang.wxpay.bean.entpay.*; +import com.github.binarywang.wxpay.bean.entwxpay.EntWxEmpPayRequest; import com.github.binarywang.wxpay.bean.request.WxPayDefaultRequest; import com.github.binarywang.wxpay.bean.result.BaseWxPayResult; import com.github.binarywang.wxpay.constant.WxPayConstants; @@ -151,6 +152,24 @@ public class EntPayServiceImpl implements EntPayService { return result; } + @Override + public EntPayResult toEmpPay(EntWxEmpPayRequest request) throws WxPayException { + //企业微信签名,需要在请求签名之前 + request.setNonceStr(String.valueOf(System.currentTimeMillis())); + request.setWorkWxSign(SignUtils.createEntSign(request.getAmount(), request.getAppid(), request.getDescription(), + request.getMchId(), request.getNonceStr(), request.getOpenid(), request.getPartnerTradeNo(), + request.getWwMsgType(), payService.getConfig().getEntPayKey(), WxPayConstants.SignType.MD5)); + + request.checkAndSign(this.payService.getConfig()); + + String url = this.payService.getPayBaseUrl() + "/mmpaymkttransfers/promotion/paywwsptrans2pocket"; + String responseContent = this.payService.post(url, request.toXML(), true); + final EntPayResult result = BaseWxPayResult.fromXML(responseContent, EntPayResult.class); + + result.checkResult(this.payService, request.getSignType(), true); + return result; + } + private String encryptRSA(File publicKeyFile, String srcString) throws WxPayException { try { Security.addProvider(new BouncyCastleProvider()); diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/util/SignUtils.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/util/SignUtils.java index 0ce39a731..9e005a813 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/util/SignUtils.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/util/SignUtils.java @@ -127,6 +127,30 @@ public class SignUtils { sortedMap.put("total_amount", totalAmount + ""); sortedMap.put("wxappid", wxAppId); + return toSignBuilder(sortedMap, signKey, signType); + } + + /** + * 企业微信签名 + * @param signType md5 目前接口要求使用的加密类型 + */ + public static String createEntSign(Integer totalAmount, String appId, String description, String mchId, + String nonceStr, String openid, String partnerTradeNo, String wwMsgType, + String signKey, String signType) { + Map sortedMap = new HashMap<>(8); + sortedMap.put("amount", String.valueOf(totalAmount)); + sortedMap.put("appid", appId); + sortedMap.put("desc", description); + sortedMap.put("mch_id", mchId); + sortedMap.put("nonce_str", nonceStr); + sortedMap.put("openid", openid); + sortedMap.put("partner_trade_no", partnerTradeNo); + sortedMap.put("ww_msg_type", wwMsgType); + + return toSignBuilder(sortedMap, signKey, signType); + } + + private static String toSignBuilder(Map sortedMap, String signKey, String signType) { Iterator> iterator = new TreeMap<>(sortedMap).entrySet().iterator(); StringBuilder toSign = new StringBuilder(); while (iterator.hasNext()) { @@ -148,7 +172,6 @@ public class SignUtils { } else { return DigestUtils.md5Hex(toSign.toString()).toUpperCase(); } - } /**