🆕 #1746: 【企业微信】第三方应用增加授权配置接口,同时增加向员工付款的接口

This commit is contained in:
LinXiaoHuChong 2020-11-29 23:00:38 +08:00 committed by GitHub
parent c0af379370
commit 0e2186632a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 324 additions and 2 deletions

View File

@ -154,6 +154,20 @@ public interface WxCpTpService {
*/
String getPreAuthUrl(String redirectUri, String state) throws WxErrorException;
/**
* <pre>
* 获取预授权链接测试环境下使用
* @Link https://work.weixin.qq.com/api/doc/90001/90143/90602
* </pre>
*
* @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 {
* <pre>
* 获取访问用户敏感信息
* </pre>
*
*
* @param userTicket
* @return
*/

View File

@ -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<H, P> 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<String,Object> sessionInfo = new HashMap<>(1);
sessionInfo.put("auth_type", authType);
Map<String,Object> 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();

View File

@ -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 {
/**
* <pre>
* 字段名商户订单号.
* 变量名partner_trade_no
* 是否必填
* 示例值10000098201411111234567890
* 类型String
* 描述商户订单号
* </pre>
*/
@Required
@XStreamAlias("partner_trade_no")
private String partnerTradeNo;
/**
* <pre>
* 字段名需保持唯一性 用户openid.
* 变量名openid
* 是否必填
* 示例值oxTWIuGaIt6gTKsQRLau2M0yL16E
* 类型String
* 描述商户appid下某用户的openid
* </pre>
*/
@Required
@XStreamAlias("openid")
private String openid;
/**
* <pre>
* 字段名设备号.
* 变量名device_info
* 是否必填
* 示例值13467007045764
* 类型String(32)
* 描述微信支付分配的终端设备号
* </pre>
*/
@XStreamAlias("device_info")
private String deviceInfo;
/**
* <pre>
* 字段名校验用户姓名选项.
* 变量名check_name
* 是否必填
* 示例值OPTION_CHECK
* 类型String
* 描述NO_CHECK不校验真实姓名 
* FORCE_CHECK强校验真实姓名未实名认证的用户会校验失败无法转账 
* OPTION_CHECK针对已实名认证的用户才校验真实姓名未实名认证用户不校验可以转账成功
* </pre>
*/
@Required
@XStreamAlias("check_name")
private String checkName;
/**
* <pre>
* 字段名收款用户姓名.
* 变量名re_user_name
* 是否必填可选
* 示例值马花花
* 类型String
* 描述收款用户真实姓名
* 如果check_name设置为FORCE_CHECK或OPTION_CHECK 则必填用户真实姓名
* </pre>
*/
@XStreamAlias("re_user_name")
private String reUserName;
/**
* <pre>
* 字段名金额.
* 变量名amount
* 是否必填
* 示例值10099
* 类型int
* 描述企业付款金额 单位为分
* </pre>
*/
@Required
@XStreamAlias("amount")
private Integer amount;
/**
* <pre>
* 字段名企业付款描述信息.
* 变量名desc
* 是否必填
* 示例值理赔
* 类型String
* 描述企业付款操作说明信息必填
* </pre>
*/
@Required
@XStreamAlias("desc")
private String description;
/**
* <pre>
* 字段名Ip地址.
* 变量名spbill_create_ip
* 是否必填
* 示例值192.168.0.1
* 类型String(32)
* 描述调用接口的机器Ip地址
* </pre>
*/
@Required
@XStreamAlias("spbill_create_ip")
private String spbillCreateIp;
/**
* <pre>
* 字段名: 付款消息类型
* 变量名: ww_msg_type
* 是否必填
* 示例值NORMAL_MSG
* 描述NORMAL_MSG普通付款消息 APPROVAL _MSG审批付款消息
* </pre>
*/
@Required
@XStreamAlias("ww_msg_type")
private String wwMsgType;
/**
* <pre>
* 字段名 审批单号
* 变量名 approval_number
* 是否必填
* 示例值 201705160008
* 描述ww_msg_type为APPROVAL _MSG时需要填写approval_number
* </pre>
*/
@XStreamAlias("approval_number")
private String approvalNumber;
/**
* <pre>
* 字段名 审批类型
* 变量名 approval_type
* 是否必填
* 示例值 1
* 描述ww_msg_type为APPROVAL _MSG时需要填写1
* </pre>
*/
@XStreamAlias("approval_type")
private Integer approvalType;
/**
* <pre>
* 字段名 项目名称
* 变量名 act_name
* 是否必填
* 示例值 产品部门报销
* 描述项目名称最长50个utf8字符
* </pre>
*/
@Required
@XStreamAlias("act_name")
private String actName;
/**
* <pre>
* 字段名 付款的应用id
* 变量名 agentid
* 是否必填
* 示例值 1
* 描述以企业应用的名义付款企业应用id整型可在企业微信管理端应用的设置页面查看
* </pre>
*/
@XStreamAlias("agentid")
private Integer agentId;
@Override
protected void checkConstraints() throws WxPayException {
}
@Override
protected boolean isWxWorkSign() {
return true;
}
@Override
protected void storeMap(Map<String, String> 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());
}
}

View File

@ -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;
}

View File

@ -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());

View File

@ -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<String, String> 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<String, String> sortedMap, String signKey, String signType) {
Iterator<Map.Entry<String, String>> 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();
}
}
/**