| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788 |
- package com.cyksj.service.wechat.impl;
- import cn.hutool.core.date.DateTime;
- import cn.hutool.core.date.DateUtil;
- import cn.hutool.core.util.CharsetUtil;
- import cn.hutool.core.util.RandomUtil;
- import cn.hutool.core.util.StrUtil;
- import cn.hutool.http.ssl.SSLSocketFactoryBuilder;
- import cn.hutool.json.JSONObject;
- import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
- import com.baomidou.mybatisplus.core.toolkit.Wrappers;
- import com.cyksj.common.constant.Constant;
- import com.cyksj.common.exception.BusinessRuntimeException;
- import com.cyksj.common.util.*;
- import com.cyksj.config.BaseWeChatConfig;
- import com.cyksj.config.WeChatConfig;
- import com.cyksj.config.WeChatFactory;
- import com.cyksj.config.YhCertMiniConfig;
- import com.cyksj.enums.WeChatAuthRedirectUrl;
- import com.cyksj.mapper.WxAppMapper;
- import com.cyksj.mapper.channel.ShopConfigMapper;
- import com.cyksj.model.dto.*;
- import com.cyksj.model.entity.ShopConfig;
- import com.cyksj.model.entity.SysConfig;
- import com.cyksj.model.entity.WxApp;
- import com.cyksj.model.request.TargetAppletSchemeReq;
- import com.cyksj.model.response.WxGzhQrCodeTicketRep;
- import com.cyksj.model.wechat.MiniURLLink;
- import com.cyksj.redis.RedisService;
- import com.cyksj.service.sys.SysConfigService;
- import com.cyksj.service.wechat.WeChatService;
- import com.cyksj.service.wechat.WxMpAccountOps;
- import lombok.RequiredArgsConstructor;
- import lombok.extern.slf4j.Slf4j;
- import org.apache.commons.lang3.StringUtils;
- import org.apache.logging.log4j.util.Strings;
- import org.springframework.stereotype.Service;
- import javax.crypto.Mac;
- import javax.crypto.spec.SecretKeySpec;
- import javax.net.ssl.KeyManagerFactory;
- import java.io.File;
- import java.io.FileInputStream;
- import java.io.InputStream;
- import java.math.BigDecimal;
- import java.net.URLEncoder;
- import java.net.http.HttpRequest;
- import java.net.http.HttpResponse;
- import java.security.KeyStore;
- import java.util.*;
- /**
- * @author chan
- * @description
- * @ClassName WeChatServiceImpl
- * @date 2020-08-05 11:00 上午
- */
- @Slf4j
- @Service
- @RequiredArgsConstructor
- public class WeChatServiceImpl implements WeChatService {
- private final WeChatConfig weChatConfig;
- private final WxMpAccountOps wxMpAccountOps;
- private final WxAppMapper wxAppMapper;
- private final ShopConfigMapper shopConfigMapper;
- private final RedisService redisService;
- private final SysConfigService sysConfigService;
- private final YhCertMiniConfig certMiniConfig;
- @Override
- public String createUrl(String state, String redirectUrl) {
- return createUrl(state,redirectUrl,weChatConfig.getAppid(), WeChatAuthRedirectUrl.BASE.getScope());
- }
- @Override
- public String createUrl(String state, String redirectUrl,String appId,String scope) {
- return "https://open.weixin.qq.com/connect/oauth2/authorize" +
- "?appid=" + appId +
- "&redirect_uri=" + URLEncoder.encode(redirectUrl, IoKit.Charsets.UTF_8.getCharset()) +
- "&response_type=code" +
- "&scope=" + scope +
- "&state=" + state + "#wechat_redirect";
- }
- @Override
- public AuthToken getAuthToken(String code) throws Exception {
- return getAuthToken(code, weChatConfig.getAppid());
- }
- public String getSecret(String appId){
- WxApp wxApp = wxAppMapper.selectOne(new QueryWrapper<WxApp>().lambda().eq(WxApp::getAppId, appId));
- if (Optional.ofNullable(wxApp).isPresent()) {
- return wxApp.getAppSecret();
- }else {
- return Strings.EMPTY;
- }
- }
- @Override
- public AuthToken getAuthToken(String code,String appId) throws Exception {
- String secret = getSecret(appId);
- if(StringUtils.isBlank(secret)){
- throw BusinessRuntimeException.getInstance("授权公众号不存在!");
- }
- String url = "https://api.weixin.qq.com/sns/oauth2/access_token" +
- "?appid=" + appId +
- "&secret=" + secret +
- "&code=" + code +
- "&grant_type=authorization_code";
- HttpResponse<byte[]> res =
- J11HttpC.custom()
- .ofGet()
- .url(url)
- .send(HttpResponse.BodyHandlers.ofByteArray());
- if (200 != res.statusCode()) {
- throw BusinessRuntimeException.getInstance("请求user_token失败: " + IoKit.toString(res.body()));
- }
- return Jsons.parseObject(res.body(), AuthToken.class);
- }
- @Override
- public GzhOAuth2UserInfo getUserInfo(String accessToken, String openid) throws Exception {
- String url = "https://api.weixin.qq.com/sns/userinfo" +
- "?access_token=" + accessToken +
- "&openid=" + openid +
- "&lang=zh_CN";
- HttpResponse<byte[]> res =
- J11HttpC.custom()
- .ofGet()
- .url(url)
- .send(HttpResponse.BodyHandlers.ofByteArray());
- if (200 != res.statusCode()) {
- throw BusinessRuntimeException.getInstance("请求userinfo失败: " + IoKit.toString(res.body()));
- }
- return Jsons.parseObject(res.body(), GzhOAuth2UserInfo.class);
- }
- @Override
- public GzhUnionidUserinfo getUserInfo2(String accessToken, String openid) throws Exception {
- String url = "https://api.weixin.qq.com/cgi-bin/user/info" +
- "?access_token=" + accessToken +
- "&openid=" + openid +
- "&lang=zh_CN";
- HttpResponse<byte[]> res =
- J11HttpC.custom()
- .ofGet()
- .url(url)
- .send(HttpResponse.BodyHandlers.ofByteArray());
- if (200 != res.statusCode()) {
- throw BusinessRuntimeException.getInstance("请求userinfo失败: " + IoKit.toString(res.body()));
- }
- GzhUnionidUserinfo info = Jsons.parseObject(res.body(), GzhUnionidUserinfo.class);
- if (null == info.getSubscribe() || info.getSubscribe() == 0) {
- return null;
- }
- return info;
- }
- @Override
- public String getJsTicket() throws Exception {
- return getJsTicket(null);
- }
- @Override
- public String getJsTicket(String appId) throws Exception {
- appId = Optional.ofNullable(appId).orElse(weChatConfig.getAppid());
- String jsTicket = wxMpAccountOps.jsTicket(appId);
- if (StringUtils.isBlank(jsTicket)) {
- throw BusinessRuntimeException.getInstance("获取用户信息失败");
- }
- return jsTicket;
- }
- @Override
- public String getAccessToken() throws Exception {
- return getAccessToken(null);
- }
- @Override
- public String getAccessToken(String appId) throws Exception {
- appId = Optional.ofNullable(appId).orElse(weChatConfig.getAppid());
- String accessToken = wxMpAccountOps.accessToken(appId);
- log.info("------------------accessToken:{} \n",accessToken);
- if (StringUtils.isBlank(accessToken)) {
- throw BusinessRuntimeException.getInstance("获取用户信息失败:");
- }
- return accessToken;
- }
- @Override
- public H5JsPayParams getJsPayParams(String appid, String shopId, String openid, Integer totalFee, String outTradeNo, String notifyUrl, String attach, String body, String tradeType, Date expireTime) throws Exception {
- log.info("------------------- 调用微信h5支付 开始 -------------------");
- ShopConfig shopConfig = shopConfigMapper.selectOne(Wrappers.lambdaQuery(ShopConfig.class).eq(ShopConfig::getAppId, shopId).last(" limit 1"));
- // 拼接统一下单实体类
- UnifiedOrder order = new UnifiedOrder()
- .setAppid(appid)
- .setAttach(attach)
- .setBody(body)
- .setMchId(shopConfig.getAppId())
- .setNonceStr("chan.123")
- .setNotifyUrl(notifyUrl)
- .setOutTradeNo(outTradeNo)
- .setSpbillCreateIp("127.0.0.1")
- .setTotalFee(totalFee)
- .setTradeType(tradeType);
- if (expireTime != null) {
- //5分钟过期
- order.setTimeExpire(DateUtil.format(DateUtil.offsetMinute(expireTime, 5), "yyyyMMddHHmmss"));
- }
- if (!"NATIVE".equals(tradeType)) {
- order.setOpenid(openid);
- }
- String sign = Codec.DoDigest.custom()
- .setAlgorithm(Codec.DoDigest.Algorithm.MD5)
- .setStringData(order.getPrintln(tradeType, shopConfig.getSecret()))
- .toHexString()
- .toUpperCase();
- order.setSign(sign);
- // xml
- String xml = Xmls.toXml(order);
- log.info("调用微信h5支付, 请求参数: \n{}", xml);
- HttpResponse<byte[]> res =
- J11HttpC.custom()
- .ofPost()
- .url("https://api.mch.weixin.qq.com/pay/unifiedorder")
- .cacheURI()
- .headers(J11HttpC.ReqType.raw_xml)
- .body(HttpRequest.BodyPublishers.ofString(xml, IoKit.Charsets.UTF_8.getCharset()))
- .send(HttpResponse.BodyHandlers.ofByteArray());
- if (200 != res.statusCode()) {
- throw BusinessRuntimeException.getInstance("请求wx_prepay失败: " + IoKit.toString(res.body()));
- }
- Map<String, String> wxResult = Xmls.toMap(res.body());
- log.info("微信返回值 xml转map: \n{}", wxResult);
- if (!StringUtils.equals(wxResult.get("return_code"), "SUCCESS") || !StringUtils.equals(wxResult.get("result_code"), "SUCCESS")) {
- throw BusinessRuntimeException.getInstance("调用微信h5支付失败. 微信返回: " + wxResult);
- }
- if (StringUtils.equals(wxResult.get("return_code"), "SUCCESS") && !StringUtils.equals(wxResult.get("result_code"), "SUCCESS")) {
- throw BusinessRuntimeException.getInstance("订单异常, 原因: " + wxResult.get("err_code") + "[" + wxResult.get("err_code_des") + "]");
- }
- if (StringUtils.isBlank(wxResult.get("prepay_id"))) {
- throw BusinessRuntimeException.getInstance("无法获取prepay_id.");
- }
- // 校验签名
- boolean f = this.checkSignWithMd5(wxResult,shopConfig.getSecret());
- if (!f) {
- throw BusinessRuntimeException.getInstance("调用微信h5支付, 微信返回值的签名验证失败.");
- }
- H5JsPayParams params = new H5PayParams();
- params.setAppId(appid);
- params.setNonceStr(order.getNonceStr());
- params.setSignType("MD5");
- params.setTimestamp(System.currentTimeMillis());
- params.setPrepayId("prepay_id=" + wxResult.get("prepay_id"));
- sign = Codec.DoDigest.custom()
- .setAlgorithm(Codec.DoDigest.Algorithm.MD5)
- .setStringData(params.println(shopConfig.getSecret()))
- .toHexString()
- .toUpperCase();
- params.setPaySign(sign);
- //二维码链接
- params.setCodeUrl(wxResult.get("code_url"));
- log.info("------------------- 调用微信h5支付 结束 -------------------");
- return params;
- }
- @Override
- public boolean checkSignWithMd5(Map<String, String> map) throws Exception {
- return checkSignWithMd5(map,weChatConfig.getShopKey());
- }
- @Override
- public boolean checkSignWithMd5(Map<String, String> map,String shopKey) throws Exception {
- // 将map的key按ASCii升序排列
- String[] keys = map.keySet().toArray(new String[0]);
- Arrays.sort(keys);
- // 将参数有序排列
- StringBuilder str = new StringBuilder();
- for (int i = 0; i < keys.length; i++) {
- if (StringUtils.equals(keys[i], "sign")) {
- continue;
- }
- if (StringUtils.isBlank(map.get(keys[i]))) {
- continue;
- }
- str.append(keys[i]).append("=").append(map.get(keys[i])).append("&");
- }
- str.append("key=").append(shopKey);
- log.info("签名验证, 排列好的有序字符串: {}", str.toString());
- String sign = Codec.DoDigest.custom().setAlgorithm(Codec.DoDigest.Algorithm.MD5).setStringData(str.toString()).toHexString().toUpperCase();
- return StringUtils.equals(map.get("sign"), sign);
- }
- @Override
- public H5PayParams getH5PayParams(String appid, Integer totalFee, String outTradeNo, String notifyUrl, String attach, String ip,String body) throws Exception {
- log.info("------------------- 调用微信h5支付 开始 -------------------");
- BaseWeChatConfig weChatConfigStrategy = WeChatFactory.getWeChatConfigStrategy(appid);
- // 拼接统一下单实体类
- UnifiedOrder order = new UnifiedOrder();
- order.setAppid(appid);
- order.setAttach(attach);
- order.setBody(body);
- order.setMchId(weChatConfigStrategy.getShopid());
- order.setNonceStr("chan123");
- order.setNotifyUrl(notifyUrl);
- order.setOutTradeNo(outTradeNo);
- order.setSpbillCreateIp(ip);
- order.setTotalFee(totalFee);
- order.setTradeType("MWEB");
- String sceneInfo= "{\"h5_info\": {\"type\":\"Wap\",\"wap_url\": \"" + weChatConfigStrategy.getDomain() +"\",\"wap_name\": \"善行\"}}";
- order.setSceneInfo(sceneInfo);
- // order.setSignType("MD5");
- String sign = Codec.DoDigest.custom()
- .setAlgorithm(Codec.DoDigest.Algorithm.MD5)
- .setStringData(order.H5Println(weChatConfigStrategy.getShopKey()))
- .toHexString()
- .toUpperCase();
- order.setSign(sign);
- // xml
- String xml = Xmls.toXml(order);
- log.info("调用微信h5支付, 请求参数: \n{}", xml);
- HttpResponse<byte[]> res =
- J11HttpC.custom()
- .ofPost()
- .url("https://api.mch.weixin.qq.com/pay/unifiedorder")
- .cacheURI()
- .headers(J11HttpC.ReqType.raw_xml)
- .body(HttpRequest.BodyPublishers.ofString(xml, IoKit.Charsets.UTF_8.getCharset()))
- .send(HttpResponse.BodyHandlers.ofByteArray());
- if (200 != res.statusCode()) {
- throw BusinessRuntimeException.getInstance("请求wx_prepay失败: " + IoKit.toString(res.body()));
- }
- Map<String, String> wxResult = Xmls.toMap(res.body());
- log.info("微信返回值 xml转map: \n{}", wxResult);
- if (!StringUtils.equals(wxResult.get("return_code"), "SUCCESS") || !StringUtils.equals(wxResult.get("result_code"), "SUCCESS")) {
- throw BusinessRuntimeException.getInstance("调用微信h5支付失败. 微信返回: " + wxResult);
- }
- if (StringUtils.equals(wxResult.get("return_code"), "SUCCESS") && !StringUtils.equals(wxResult.get("result_code"), "SUCCESS")) {
- throw BusinessRuntimeException.getInstance("订单异常, 原因: " + wxResult.get("err_code") + "[" + wxResult.get("err_code_des") + "]");
- }
- if (StringUtils.isBlank(wxResult.get("prepay_id"))) {
- throw BusinessRuntimeException.getInstance("无法获取prepay_id.");
- }
- // 校验签名
- boolean f = this.checkSignWithMd5(wxResult,weChatConfigStrategy.getShopKey());
- if (!f) {
- throw BusinessRuntimeException.getInstance("调用微信h5支付, 微信返回值的签名验证失败.");
- }
- H5PayParams params = new H5PayParams();
- params.setAppId(appid);
- params.setNonceStr(order.getNonceStr());
- params.setSignType("MD5");
- params.setTimestamp(System.currentTimeMillis());
- params.setPrepayId("prepay_id=" + wxResult.get("prepay_id"));
- params.setMWebUrl(wxResult.get("mweb_url"));
- sign = Codec.DoDigest.custom()
- .setAlgorithm(Codec.DoDigest.Algorithm.MD5)
- .setStringData(params.println(weChatConfigStrategy.getShopKey()))
- .toHexString()
- .toUpperCase();
- params.setPaySign(sign);
- log.info("------------------- 调用微信h5支付 结束 -------------------");
- return params;
- }
- @Override
- public void sendTemplateMessage(String accessToken, String json) throws Exception{
- String url = "https://api.weixin.qq.com/cgi-bin/message/template/send" +
- "?access_token=" + accessToken;
- HttpResponse<String> send = J11HttpC.custom()
- .ofPost()
- .url(url)
- .headers(J11HttpC.ReqType.raw_json)
- .body(HttpRequest.BodyPublishers.ofString(json))
- .send(HttpResponse.BodyHandlers.ofString());
- log.info("发送模板消息结束,返回值:{}", send != null ? send.body() : null);
- }
- @Override
- public void refundWxOrder(String appId,String shopId, String transactionId, String outRefundNo, BigDecimal totalFee, BigDecimal refundMoney, String notifyUrl) throws Exception {
- ShopConfig shopConfig = shopConfigMapper.selectOne(Wrappers.lambdaQuery(ShopConfig.class).eq(ShopConfig::getAppId, shopId).last(" limit 1"));
- WxRefundOrder wxRefundOrder = new WxRefundOrder();
- wxRefundOrder.setAppid(appId);
- //商户号
- wxRefundOrder.setMchId(shopConfig.getAppId());
- wxRefundOrder.setOutRefundNo(outRefundNo);
- wxRefundOrder.setNonceStr("chan123");
- wxRefundOrder.setTotalFee(totalFee.intValue());
- //退款金额
- wxRefundOrder.setRefundFee(refundMoney.intValue());
- wxRefundOrder.setTransactionId(transactionId);
- // wxRefundOrder.setRefundDesc("订单退款");
- wxRefundOrder.setNotifyUrl(notifyUrl);
- String sign = Codec.DoDigest.custom()
- .setAlgorithm(Codec.DoDigest.Algorithm.MD5)
- .setStringData(wxRefundOrder.println(shopConfig.getSecret()))
- .toHexString()
- .toUpperCase();
- wxRefundOrder.setSign(sign);
- String xml = Xmls.toXml(wxRefundOrder);
- KeyStore keyStore = KeyStore.getInstance("PKCS12");
- File file = new File("cert/" + shopId + ".p12");
- InputStream inputStream = new FileInputStream(file);
- keyStore.load(inputStream, shopId.toCharArray());
- // 初始化密钥库
- KeyManagerFactory factory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
- factory.init(keyStore, shopId.toCharArray());
- cn.hutool.http.HttpRequest request = cn.hutool.http.HttpRequest.post("https://api.mch.weixin.qq.com/secapi/pay/refund")
- // 自定义返回编码
- .charset(CharsetUtil.CHARSET_UTF_8)
- // 禁用缓存
- .disableCache()
- .setSSLSocketFactory(SSLSocketFactoryBuilder.create()
- .setKeyManagers(factory.getKeyManagers())
- .setProtocol(SSLSocketFactoryBuilder.TLSv1).build());
- cn.hutool.http.HttpResponse res = request.body(xml).execute();
- if (200 != res.getStatus()) {
- throw BusinessRuntimeException.getInstance("请求wx_refund失败: " + res.body());
- }
- Map<String, String> wxRefund = Xmls.toMap(res.body());
- log.info("微信返回值 xml 转 map: \n{}", wxRefund);
- if (!StringUtil.equals(wxRefund.get("return_code"), "SUCCESS") || !StringUtil.equals(wxRefund.get("result_code"), "SUCCESS")) {
- throw BusinessRuntimeException.getInstance("调用微信申请退款失败. 微信返回: " + wxRefund);
- }
- if (StringUtil.equals(wxRefund.get("return_code"), "SUCCESS") && !StringUtil.equals(wxRefund.get("result_code"), "SUCCESS")) {
- throw BusinessRuntimeException.getInstance("订单异常: " + wxRefund.get("err_code") + "[" + wxRefund.get("err_code_des") + "]");
- }
- // 校验签名
- boolean f = this.checkSignWithMd5(wxRefund, shopConfig.getSecret());
- if (!f) {
- throw BusinessRuntimeException.getInstance("调用微信h5申请退款, 微信返回值的签名验证失败.");
- }
- log.info("------------------- 调用微信申请退款 结束 -------------------");
- }
- @Override
- public Map<String, String> getWxRefundDetail(String appId, String shopId, String outRefundNo) throws Exception {
- ShopConfig shopConfig = shopConfigMapper.selectOne(Wrappers.lambdaQuery(ShopConfig.class)
- .eq(ShopConfig::getAppId, shopId).last("limit 1"));
- if (shopConfig == null || StrUtil.isBlank(shopConfig.getSecret())) {
- throw BusinessRuntimeException.getInstance("微信退款查询商户不存在");
- }
- Map<String, String> requestData = new TreeMap<>();
- requestData.put("appid", appId);
- requestData.put("mch_id", shopId);
- requestData.put("out_refund_no", outRefundNo);
- requestData.put("nonce_str", UUID.randomUUID().toString().replace("-", ""));
- requestData.put("sign", signWithMd5(requestData, shopConfig.getSecret()));
- HttpResponse<String> response = J11HttpC.custom()
- .ofPost()
- .url("https://api.mch.weixin.qq.com/pay/refundquery")
- .headers(J11HttpC.ReqType.raw_xml)
- .body(HttpRequest.BodyPublishers.ofString(Xmls.toXml(requestData), IoKit.Charsets.UTF_8.getCharset()))
- .send(HttpResponse.BodyHandlers.ofString());
- if (response.statusCode() != 200) {
- throw BusinessRuntimeException.getInstance("微信退款查询失败: " + response.body());
- }
- Map<String, String> result = Xmls.toMap(response.body());
- if (!"SUCCESS".equals(result.get("return_code"))) {
- throw BusinessRuntimeException.getInstance("微信退款查询通信失败: " + result);
- }
- if (StrUtil.isNotBlank(result.get("mch_id")) && !shopId.equals(result.get("mch_id"))) {
- throw BusinessRuntimeException.getInstance("微信退款查询商户不匹配");
- }
- if (StrUtil.isNotBlank(result.get("appid")) && !appId.equals(result.get("appid"))) {
- throw BusinessRuntimeException.getInstance("微信退款查询应用不匹配");
- }
- if (!"SUCCESS".equals(result.get("result_code"))) {
- return result;
- }
- if (!checkSignWithMd5(result, shopConfig.getSecret())) {
- throw BusinessRuntimeException.getInstance("微信退款查询签名校验失败");
- }
- return result;
- }
- private String signWithMd5(Map<String, String> values, String secret) throws Exception {
- StringBuilder data = new StringBuilder();
- values.entrySet().stream()
- .filter(entry -> !"sign".equals(entry.getKey()) && StrUtil.isNotBlank(entry.getValue()))
- .sorted(Map.Entry.comparingByKey())
- .forEach(entry -> data.append(entry.getKey()).append('=').append(entry.getValue()).append('&'));
- data.append("key=").append(secret);
- return Codec.DoDigest.custom().setAlgorithm(Codec.DoDigest.Algorithm.MD5)
- .setStringData(data.toString()).toHexString().toUpperCase();
- }
- @Override
- public WxGzhQrCodeTicketRep getWxGzhQrCode(GzhBodyDto gzhBodyDto) throws Exception {
- SysConfig sysConfig = sysConfigService.getOne(Wrappers.lambdaQuery(SysConfig.class)
- .eq(SysConfig::getSysKey, Constant.QR_CODE_WX_APP_ID).last("limit 1"));
- //pc公众号扫码
- String appid = "wx30f48135b1fd96bb";
- if (sysConfig != null) {
- String sysValue = sysConfig.getSysValue();
- if (StrUtil.isNotBlank(sysValue)) {
- appid = sysValue;
- }
- }
- String loginPhone = gzhBodyDto.getLoginPhone();
- String loginEmail = gzhBodyDto.getLoginEmail();
- String gzhQrCodeUrl = String.format("https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=%s", this.getAccessToken(appid));
- //scene_str:场景值ID(字符串形式的ID),字符串类型,长度限制为1到64
- String scene_str = RandomUtil.randomString(16);
- int expireTime = 60;
- if (StrUtil.isNotBlank(loginPhone) || StrUtil.isNotBlank(loginEmail)) {
- expireTime = 25;
- }
- DateTime dateTime = DateUtil.offsetSecond(DateTime.now(), expireTime);
- String body = String.format("{\"expire_seconds\": %s, \"action_name\": \"%s\", \"action_info\": {\"scene\": {\"scene_str\": \"%s\"}}}",
- expireTime,
- "QR_STR_SCENE",
- scene_str);
- HttpResponse<String> response = J11HttpC.custom()
- .url(gzhQrCodeUrl)
- .ofPost()
- .body(HttpRequest.BodyPublishers.ofString(body))
- .send(HttpResponse.BodyHandlers.ofString());
- if (200 != response.statusCode()) {
- throw BusinessRuntimeException.getInstance("获取微信登录二维码失败: " + response.body());
- }
- String state = Codec.DoBase64.custom().setData(Jsons.toJson(gzhBodyDto)).encodeAsText();
- redisService.set(RedisService.key.WX_GZH_QRCODE_SHARED_LOGIN.getNameFormat(scene_str), state, RedisService.key.WX_GZH_QRCODE_SHARED_LOGIN.getTimeout());
- JSONObject re = Jsons.parseObject(response.body(), JSONObject.class);
- String ticket = re.getStr("ticket");
- WxGzhQrCodeTicketRep wxGzhQrCodeTicketRep = new WxGzhQrCodeTicketRep();
- wxGzhQrCodeTicketRep.setQrCodeUrl(String.format("https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=%s", ticket));
- wxGzhQrCodeTicketRep.setSceneStr(scene_str);
- wxGzhQrCodeTicketRep.setExpireTime(dateTime.getTime());
- return wxGzhQrCodeTicketRep;
- }
- @Override
- public String getNotExpiredWxGzhQrCode(String sceneKey) throws Exception {
- String gzhQrCodeUrl = String.format("https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=%s", this.getAccessToken());
- String body = String.format("{\"action_name\": \"%s\", \"action_info\": {\"scene\": {\"scene_str\": \"%s\"}}}",
- "QR_LIMIT_STR_SCENE",
- sceneKey);
- HttpResponse<String> response = J11HttpC.custom()
- .url(gzhQrCodeUrl)
- .ofPost()
- .body(HttpRequest.BodyPublishers.ofString(body))
- .send(HttpResponse.BodyHandlers.ofString());
- if (200 != response.statusCode()) {
- throw BusinessRuntimeException.getInstance("获取永久渠道二维码失败: " + response.body());
- }
- JSONObject re = Jsons.parseObject(response.body(), JSONObject.class);
- String ticket = re.getStr("ticket");
- String url = String.format("https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=%s", ticket);
- return url;
- }
- @Override
- public Map<String, String> getWxOrderDetail(String shopId, String orderNo) throws Exception {
- String url = "https://api.mch.weixin.qq.com/pay/orderquery";
- // 拼接统一下单实体类
- Map<String, String> map = new TreeMap<>();
- map.put("appid", weChatConfig.getAppid());
- map.put("mch_id", shopId);
- map.put("out_trade_no", orderNo);
- map.put("nonce_str", "d1234");
- String sign = getMd5Sign(map);
- map.put("sign", sign);
- String xml = Xmls.toXml(map);
- HttpResponse<String> send = J11HttpC.custom()
- .ofPost()
- .url(url)
- .headers(J11HttpC.ReqType.raw_xml)
- .body(HttpRequest.BodyPublishers.ofString(xml, IoKit.Charsets.UTF_8.getCharset()))
- .send(HttpResponse.BodyHandlers.ofString());
- Map<String, String> orderSearch = Xmls.toMap(send.body());
- ShopConfig shopConfig = shopConfigMapper.getByAppId(shopId);
- if (shopConfig == null || !checkSignWithMd5(orderSearch, shopConfig.getSecret())) {
- throw BusinessRuntimeException.getInstance("微信订单查询签名校验失败");
- }
- log.info("微信返回值 xml转map: \n{}", orderSearch);
- if (!StringUtils.equals(orderSearch.get("result_code"), "SUCCESS")) {
- log.error("调用微信h5查询订单详情接口失败. 微信返回: " + orderSearch);
- // Preserve verified failure codes such as ORDERNOTEXIST for tri-state handling.
- return orderSearch;
- }
- return orderSearch;
- }
- @Override
- public String createWxAuthLoginUrl(String state, String appId, String redirectUri) {
- String authLoginUrl = String.format("https://open.weixin.qq.com/connect/qrconnect?" +
- "appid=%s&redirect_uri=%s&response_type=code&fast_login=0&scope=snsapi_login&state=%s#wechat_redirect", appId, redirectUri, state);
- return authLoginUrl;
- }
- @Override
- public String getMiniURLLink(String accessToken, MiniURLLink miniURLLink) throws Exception{
- String url = String.format("https://api.weixin.qq.com/wxa/generate_urllink?access_token=%s", accessToken);
- HttpResponse<String> send = J11HttpC.custom()
- .url(url)
- .ofPost()
- .body(HttpRequest.BodyPublishers.ofString(Jsons.toJson(miniURLLink)))
- .send(HttpResponse.BodyHandlers.ofString());
- if (send.statusCode() != 200) {
- throw BusinessRuntimeException.getInstance("获取小程序链接错误");
- }
- JSONObject body = Jsons.parseObject(send.body(), JSONObject.class);
- if (body.getInt("errcode") != 0) {
- throw BusinessRuntimeException.getInstance(body.getStr("errmsg"));
- }
- return body.getStr("url_link");
- }
- /**
- * sha256_HMAC加密
- * @param message 消息
- * @param secret 秘钥
- * @return 加密后字符串
- */
- public static String sha256_HMAC(String message,String secret) {
- String hash = "";
- try {
- Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
- SecretKeySpec secret_key = new SecretKeySpec(secret.getBytes(IoKit.Charsets.UTF_8.getCharset()), "HmacSHA256");
- sha256_HMAC.init(secret_key);
- byte[] bytes = sha256_HMAC.doFinal(message.getBytes());
- hash = byteArrayToHexString(bytes);
- } catch (Exception e) {
- System.out.println("Error HmacSHA256 ===========" + e.getMessage());
- }
- return hash;
- }
- /**
- * 将加密后的字节数组转换成字符串
- *
- * @param b 字节数组
- * @return 字符串
- */
- public static String byteArrayToHexString(byte[] b) {
- StringBuilder hs = new StringBuilder();
- String stmp;
- for (int n = 0; b!=null && n < b.length; n++) {
- stmp = Integer.toHexString(b[n] & 0XFF);
- if (stmp.length() == 1) {
- hs.append('0');
- }
- hs.append(stmp);
- }
- return hs.toString().toLowerCase();
- }
- public String getMd5Sign(Map<String, String> map) throws Exception {
- StringBuilder sb = new StringBuilder();
- map.forEach((k, v) -> {
- sb.append(k).append("=").append(v).append("&");
- });
- sb.append("key").append("=").append(shopConfigMapper.getByAppId(map.get("mch_id")).getSecret());
- String sign = Codec.DoDigest.custom()
- .setAlgorithm(Codec.DoDigest.Algorithm.MD5)
- .setStringData(sb.toString())
- .toHexString()
- .toUpperCase();
- return sign;
- }
- @Override
- public MiniProgramSession code2Session(String appId,String code) throws Exception {
- appId = Optional.ofNullable(appId).orElse(weChatConfig.getAppid());
- return wxMpAccountOps.miniProgramCode2Session(appId,code);
- }
- @Override
- public void getMiniStudentIdentity(String openId, String code) throws Exception {
- String url = "https://api.weixin.qq.com/intp/quickcheckstudentidentity?access_token=" + getAccessToken(certMiniConfig.getAppId());
- JSONObject params = new JSONObject();
- params.putOpt("openid", openId);
- params.putOpt("wx_studentcheck_code", code);
- HttpResponse<String> response = J11HttpC.custom().url(url).ofPost().body(HttpRequest.BodyPublishers.ofString(params.toString())).send(HttpResponse.BodyHandlers.ofString());
- JSONObject re = Jsons.parseObject(response.body(), JSONObject.class);
- if (re.getInt("errcode") != 0) {
- log.info("获取学生信息失败:{}", re.getStr("errmsg"));
- throw BusinessRuntimeException.getInstance("获取认证信息失败");
- }
- Integer bindStatus = Optional.ofNullable(re.getInt("bind_status")).orElse(1);
- //绑定状态:
- //1-未绑定
- //2-审核中
- //3-已绑定
- if (bindStatus == 1) {
- throw BusinessRuntimeException.getInstance("大学生身份未绑定");
- }
- if (bindStatus == 2) {
- throw BusinessRuntimeException.getInstance("大学生身份审核中");
- }
- }
- @Override
- public String generateSchema(String accessToken, TargetAppletSchemeReq targetAppletSchemeReq) throws Exception {
- String url = "https://api.weixin.qq.com/wxa/generatescheme?access_token=" + accessToken;
- HttpResponse<byte[]> res = J11HttpC.custom()
- .ofPost()
- .url(url)
- .body(HttpRequest.BodyPublishers.ofString(Jsons.toJson(targetAppletSchemeReq)))
- .send(HttpResponse.BodyHandlers.ofByteArray());
- if (200 != res.statusCode()) {
- throw BusinessRuntimeException.getInstance("获取用户信息失败: " + IoKit.toString(res.body()));
- }
- JSONObject jsonObject = Jsons.parseObject(res.body(), JSONObject.class);
- if (jsonObject.getInt("errcode") != 0) {
- throw BusinessRuntimeException.getInstance(jsonObject.getStr("errmsg"));
- }
- if (StrUtil.isEmpty(jsonObject.getStr("openlink"))) {
- throw BusinessRuntimeException.getInstance("系统异常..");
- }
- return jsonObject.getStr("openlink");
- }
- @Override
- public byte[] generateCode(String accessToken, String path) throws Exception {
- String url = "https://api.weixin.qq.com/wxa/getwxacode?access_token=" + accessToken;
- JSONObject params = new JSONObject();
- params.putOpt("path", path);
- HttpResponse<byte[]> res = J11HttpC.custom()
- .ofPost()
- .url(url)
- .body(HttpRequest.BodyPublishers.ofString(params.toString()))
- .send(HttpResponse.BodyHandlers.ofByteArray());
- if (200 != res.statusCode()) {
- throw BusinessRuntimeException.getInstance("获取用户信息失败: " + IoKit.toString(res.body()));
- }
- return res.body();
- }
- }
|