parent
bd12b21d5e
commit
a138546449
93 changed files with 3388 additions and 2526 deletions
@ -1,12 +1,13 @@ |
||||
package com.ynxbd.common; |
||||
|
||||
import com.ynxbd.common.helper.common.Base64Helper; |
||||
|
||||
import java.io.UnsupportedEncodingException; |
||||
import com.ynxbd.wx.utils.DesEncryptHelper; |
||||
|
||||
public class TestA { |
||||
|
||||
|
||||
public static void main(String[] args) throws UnsupportedEncodingException { |
||||
public static void main(String[] args) { |
||||
String a = ""; |
||||
String s = DesEncryptHelper.deCode(a); |
||||
System.out.println(s); |
||||
} |
||||
} |
||||
|
||||
@ -1,102 +1,15 @@ |
||||
package com.ynxbd.common.service; |
||||
|
||||
import com.ynxbd.common.bean.enums.MerchantEnum; |
||||
import com.ynxbd.common.bean.pay.Order; |
||||
import com.ynxbd.common.dao.his.HisRecipeDao; |
||||
import com.ynxbd.common.helper.common.CodeHelper; |
||||
import com.ynxbd.common.helper.common.DateHelper; |
||||
import com.ynxbd.common.helper.common.ErrorHelper; |
||||
import com.ynxbd.common.helper.common.JsonHelper; |
||||
import com.ynxbd.common.result.JsonResult; |
||||
import com.ynxbd.wx.wxfactory.WxPayHelper; |
||||
import com.ynxbd.wx.config.WeChatConfig; |
||||
import lombok.extern.slf4j.Slf4j; |
||||
import org.apache.commons.codec.digest.DigestUtils; |
||||
import weixin.popular.api.PayMchAPI; |
||||
import weixin.popular.bean.paymch.MchOrderInfoResult; |
||||
import weixin.popular.bean.paymch.MchOrderquery; |
||||
|
||||
import java.math.BigDecimal; |
||||
import java.util.ArrayList; |
||||
|
||||
@Slf4j |
||||
public class Test { |
||||
|
||||
|
||||
public static void main(String[] args) { |
||||
// System.out.println(JsonHelper.toJsonString(queryOrder("WX7c1ea238c08f420d2bb8262f7254")));
|
||||
String piMd5 = "9444144" + "Z2007" + "xbd"; |
||||
piMd5 = DigestUtils.md5Hex(piMd5).toUpperCase(); |
||||
System.out.println(piMd5); |
||||
} |
||||
|
||||
/** |
||||
* 查询订单 |
||||
* |
||||
* @param outTradeNo 订单号 |
||||
* @return 订单信息 |
||||
*/ |
||||
public static Order queryOrder(String outTradeNo) { |
||||
Order order = new Order(); |
||||
order.setOutTradeNo(outTradeNo); |
||||
try { |
||||
MchOrderquery mchOrderquery = new MchOrderquery(); |
||||
String appId = WeChatConfig.APP_ID; |
||||
String mchId = WeChatConfig.MCH_ID; |
||||
String mchKey = WeChatConfig.MCH_KEY; |
||||
|
||||
mchOrderquery.setAppid(appId); |
||||
mchOrderquery.setMch_id(mchId); |
||||
mchOrderquery.setOut_trade_no(outTradeNo); |
||||
mchOrderquery.setNonce_str(CodeHelper.get32UUID()); |
||||
|
||||
MchOrderInfoResult wxResult = PayMchAPI.payOrderquery(mchOrderquery, mchKey); |
||||
if (wxResult == null) { |
||||
order.setErrorMsg("【微信】查询订单失败"); |
||||
return order; |
||||
} |
||||
|
||||
// 调用错误
|
||||
if (WxPayHelper.FAIL.equals(wxResult.getReturn_code())) { |
||||
order.setErrorCode(wxResult.getReturn_code()); |
||||
order.setErrorMsg(wxResult.getReturn_msg()); |
||||
return order; |
||||
} |
||||
|
||||
// 业务错误
|
||||
if (WxPayHelper.FAIL.equals(wxResult.getResult_code())) { |
||||
order.setErrorCode(wxResult.getErr_code()); |
||||
order.setErrorMsg(wxResult.getErr_code_des()); |
||||
return order; |
||||
} |
||||
|
||||
String tradeState = wxResult.getTrade_state(); |
||||
order.setTradeState(tradeState); |
||||
|
||||
if (WxPayHelper.SUCCESS.equals(tradeState)) { |
||||
order.setSuccess(true); // 订单支付成功(判断条件)
|
||||
|
||||
} else if (WxPayHelper.REFUND.equals(tradeState)) { |
||||
order.setErrorMsg(wxResult.getTrade_state_desc()); |
||||
order.setRefund(true); // 订单发生过退款
|
||||
} else { |
||||
// 交易错误
|
||||
order.setErrorCode(tradeState); |
||||
order.setErrorMsg(wxResult.getTrade_state_desc()); |
||||
return order; |
||||
} |
||||
|
||||
// 交易成功
|
||||
order.setOpenid(wxResult.getOpenid()); |
||||
order.setBankTransNo(wxResult.getTransaction_id()); |
||||
Integer totalFee = wxResult.getTotal_fee(); |
||||
if (totalFee != null) { |
||||
order.setTotalFee(new BigDecimal(totalFee).movePointLeft(2)); |
||||
} |
||||
} catch (Exception e) { |
||||
ErrorHelper.println(e); |
||||
order.setErrorMsg("异常信息:" + e.getMessage()); |
||||
} |
||||
return order; |
||||
} |
||||
} |
||||
|
||||
@ -1,189 +0,0 @@ |
||||
package com.ynxbd.wx.servlet.oldpay; |
||||
|
||||
import com.ynxbd.common.bean.enums.MerchantEnum; |
||||
import com.ynxbd.common.bean.pay.Recipe; |
||||
import com.ynxbd.common.dao.RecipeDao; |
||||
import com.ynxbd.common.helper.common.CodeHelper; |
||||
import com.ynxbd.common.helper.common.HttpHelper; |
||||
import com.ynxbd.wx.config.WeChatConfig; |
||||
import lombok.extern.slf4j.Slf4j; |
||||
import org.apache.commons.lang3.ObjectUtils; |
||||
import org.slf4j.MDC; |
||||
import weixin.popular.api.PayMchAPI; |
||||
import weixin.popular.bean.paymch.MchPayNativeReply; |
||||
import weixin.popular.bean.paymch.Unifiedorder; |
||||
import weixin.popular.bean.paymch.UnifiedorderResult; |
||||
import weixin.popular.util.PayUtil; |
||||
import weixin.popular.util.SignatureUtil; |
||||
import weixin.popular.util.XMLConverUtil; |
||||
|
||||
import javax.servlet.ServletException; |
||||
import javax.servlet.annotation.WebServlet; |
||||
import javax.servlet.http.HttpServlet; |
||||
import javax.servlet.http.HttpServletRequest; |
||||
import javax.servlet.http.HttpServletResponse; |
||||
import java.io.BufferedReader; |
||||
import java.io.IOException; |
||||
import java.io.InputStream; |
||||
import java.io.InputStreamReader; |
||||
import java.math.BigDecimal; |
||||
import java.nio.charset.StandardCharsets; |
||||
import java.util.Map; |
||||
import java.util.UUID; |
||||
|
||||
/** |
||||
* 扫码支付接口(旧版) |
||||
* |
||||
* @author 张剑峰 |
||||
* @version v1.0.0 |
||||
* @Project:微信公众号 |
||||
* @date 2018年6月4日上午10:44:31 |
||||
* @Copyright: 2018云南新八达科技有限公司 All rights reserved. |
||||
*/ |
||||
@Slf4j |
||||
@WebServlet("/qrpay2") |
||||
public class QRPayServlet2 extends HttpServlet { |
||||
|
||||
@Override |
||||
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { |
||||
MDC.remove("ip"); |
||||
MDC.put("ip", HttpHelper.getIpAddress(request)); |
||||
|
||||
log.info("[旧版]收到扫码支付请求,开始解析..."); |
||||
|
||||
// 读取参数
|
||||
InputStream inputStream = request.getInputStream(); |
||||
StringBuilder sb = new StringBuilder(); |
||||
String s; |
||||
BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)); |
||||
while ((s = in.readLine()) != null) { |
||||
sb.append(s); |
||||
} |
||||
in.close(); |
||||
inputStream.close(); |
||||
|
||||
// 解析xml成map
|
||||
Map<String, String> map = XMLConverUtil.convertToMap(sb.toString()); |
||||
// 过滤空 设置 TreeMap
|
||||
// SortedMap<Object, Object> payParams = new TreeMap<>();
|
||||
// Iterator it = map.keySet().iterator();
|
||||
// while (it.hasNext()) {
|
||||
// String parameter = (String) it.next();
|
||||
// String parameterValue = map.get(parameter);
|
||||
// String v = "";
|
||||
// if (null != parameterValue) {
|
||||
// v = parameterValue.trim();
|
||||
// }
|
||||
// payParams.put(parameter, v);
|
||||
// }
|
||||
|
||||
// 验证请求签名
|
||||
if (!map.get("sign").equals(SignatureUtil.generateSign(map, WeChatConfig.MCH_KEY))) { |
||||
log.info("收到扫码支付请求:签名无效!"); |
||||
HttpHelper.outRespAlert(response, "收到扫码支付请求:签名无效!"); |
||||
return; |
||||
} |
||||
|
||||
String product_id = map.get("product_id"); |
||||
log.info("收到扫码支付请求:product_id=" + product_id + ", open_id=" + map.get("openid")); |
||||
if (!product_id.contains("XBD")) { |
||||
log.info("扫码支付:不合规则的字符串,不允许支付!product_id=" + product_id); |
||||
HttpHelper.outRespAlert(response, "扫码支付:不合规则的字符串,不允许支付!"); |
||||
return; |
||||
} |
||||
|
||||
String[] array = product_id.split("XBD"); |
||||
if (array.length < 5) { |
||||
log.info("扫码支付:参数少于5个,不允许支付!product_id=" + product_id); |
||||
HttpHelper.outRespAlert(response, "扫码支付:参数少于5个"); |
||||
return; |
||||
} |
||||
|
||||
String patientId = array[0]; |
||||
String mzNum = array[1]; |
||||
String recipeId = array[2]; |
||||
String payMoney = array[3]; |
||||
String flag = array[4]; |
||||
// String recipeIdJson = "[{\"id\":" + "\"" + recipeId + "\"" + ",\"fee\":" + payMoney + "}]";
|
||||
|
||||
log.info("扫码支付:patientId={} treatNum={}, recipeId={}, payMoney={}, flag={}", patientId, mzNum, recipeId, payMoney, flag); |
||||
|
||||
if (ObjectUtils.isEmpty(patientId) || ObjectUtils.isEmpty(mzNum) || ObjectUtils.isEmpty(payMoney)) { |
||||
log.info("扫码支付:参数中有空值,不允许支付!product_id={}", product_id); |
||||
HttpHelper.outRespAlert(response, "扫码支付:参数中有空值,不允许支付!"); |
||||
return; |
||||
} |
||||
|
||||
// 是否重复支付
|
||||
log.info("判断是否支付过:patientId={}, mzNum={}, recipeId={}", patientId, mzNum, recipeId); |
||||
if (new RecipeDao().hasHisPaidByPatient(patientId, mzNum, recipeId)) { |
||||
log.info("该码已经缴费,无需支付!product_id={}", product_id); |
||||
HttpHelper.outRespAlert(response, "该码已经缴费,无需支付!"); |
||||
return; |
||||
} |
||||
|
||||
// 统一下单
|
||||
Unifiedorder unifiedorder = new Unifiedorder(); |
||||
String appID = WeChatConfig.APP_ID; |
||||
String mchID = WeChatConfig.MCH_ID; |
||||
String mchKey = WeChatConfig.MCH_KEY; |
||||
|
||||
unifiedorder.setAppid(appID); |
||||
unifiedorder.setMch_id(mchID); |
||||
unifiedorder.setNonce_str(UUID.randomUUID().toString().replace("-", "")); |
||||
if (flag.equals("1")) { |
||||
unifiedorder.setBody("ID:" + patientId + " 处方单号:" + recipeId); |
||||
} else if (flag.equals("2")) { |
||||
unifiedorder.setBody("ID:" + patientId + " 申请单号:" + recipeId); |
||||
} |
||||
|
||||
// unifiedorder.setBody("pay");
|
||||
unifiedorder.setOut_trade_no(CodeHelper.getOutTradeNo(MerchantEnum.WX)); |
||||
|
||||
// 此处使用Float或者Double转换金额会因为精度问题丢失1分钱,使用BigDecimal来转换
|
||||
BigDecimal v1 = new BigDecimal(payMoney); |
||||
BigDecimal v2 = new BigDecimal("100"); |
||||
double b = v1.multiply(v2).doubleValue(); |
||||
int fFee = (int) b; |
||||
|
||||
unifiedorder.setTotal_fee(String.valueOf(fFee)); |
||||
unifiedorder.setSpbill_create_ip(request.getRemoteAddr()); |
||||
unifiedorder.setNotify_url(WeChatConfig.getBaseURL() + "old_pay_notify_servlet"); |
||||
unifiedorder.setTrade_type("NATIVE"); |
||||
log.info("扫码回调地址:" + WeChatConfig.getBaseURL() + "old_pay_notify_servlet"); |
||||
|
||||
UnifiedorderResult unifiedorderResult = PayMchAPI.payUnifiedorder(unifiedorder, mchKey); |
||||
|
||||
Recipe recipe = new Recipe(); |
||||
|
||||
recipe.setOpenid(map.get("openid")); |
||||
recipe.setPatientId(patientId); |
||||
recipe.setTreatNum(mzNum); |
||||
recipe.setOutTradeNo(unifiedorder.getOut_trade_no()); |
||||
recipe.setPayWay("1"); |
||||
recipe.setPayMoney(new BigDecimal(payMoney)); |
||||
recipe.setRecipeId(recipeId); |
||||
// recipe.setTradeNo(CodeHelper.getHisTradeNo()); // 本次his交易流水号
|
||||
recipe.setTotalFee(new BigDecimal(payMoney)); |
||||
recipe.setHisStatus(-1); |
||||
recipe.setPayStatus(-1); |
||||
if (!new RecipeDao().insert(recipe)) { |
||||
log.info("[扫码支付]存储失败"); |
||||
} |
||||
|
||||
MchPayNativeReply reply = new MchPayNativeReply(); |
||||
reply.setAppid(appID); |
||||
reply.setMch_id(mchID); |
||||
reply.setNonce_str(UUID.randomUUID().toString().replace("-", "")); |
||||
reply.setPrepay_id(unifiedorderResult.getPrepay_id()); |
||||
reply.setResult_code("SUCCESS"); |
||||
reply.setReturn_code("SUCCESS"); |
||||
String string = PayUtil.generateMchPayNativeReplyXML(reply, mchKey); |
||||
response.getWriter().write(string); |
||||
} |
||||
|
||||
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { |
||||
doPost(request, response); |
||||
} |
||||
|
||||
} |
||||
@ -1,96 +0,0 @@ |
||||
package com.ynxbd.wx.servlet.test; |
||||
|
||||
import com.ynxbd.common.bean.pay.Order; |
||||
import com.ynxbd.common.bean.pay.Recipe; |
||||
import com.ynxbd.common.dao.RecipeDao; |
||||
import com.ynxbd.common.helper.common.CodeHelper; |
||||
import com.ynxbd.common.helper.common.ErrorHelper; |
||||
import com.ynxbd.wx.config.WeChatConfig; |
||||
import weixin.popular.api.PayMchAPI; |
||||
import weixin.popular.bean.paymch.MchOrderInfoResult; |
||||
import weixin.popular.bean.paymch.MchOrderquery; |
||||
|
||||
import java.math.BigDecimal; |
||||
|
||||
/** |
||||
* @Author wsq |
||||
* @Date 2021/6/28 16:10 |
||||
* @Copyright @ 2020 云南新八达科技有限公司 All rights reserved. |
||||
*/ |
||||
public class OrderTest { |
||||
|
||||
// public static void main(String[] args) {
|
||||
// Pay pay = queryOrder("WX560aae435d5940a677044b52df27");
|
||||
// System.out.println(JsonHelper.toJsonString(pay));
|
||||
// }
|
||||
|
||||
/** |
||||
* 查询订单 |
||||
* |
||||
* @param outTradeNo 订单号 |
||||
* @return 订单信息 |
||||
*/ |
||||
public static Order queryOrder(String outTradeNo) { |
||||
Order order = new Order(); |
||||
try { |
||||
MchOrderquery mchOrderquery = new MchOrderquery(); |
||||
String appId = WeChatConfig.APP_ID; |
||||
String mchId = WeChatConfig.MCH_ID; |
||||
String mchKey = WeChatConfig.MCH_KEY; |
||||
|
||||
mchOrderquery.setAppid(appId); |
||||
mchOrderquery.setMch_id(mchId); |
||||
mchOrderquery.setOut_trade_no(outTradeNo); |
||||
mchOrderquery.setNonce_str(CodeHelper.get32UUID()); |
||||
|
||||
MchOrderInfoResult mchOrderInfoResult = PayMchAPI.payOrderquery(mchOrderquery, mchKey); |
||||
if (!"SUCCESS".equals(mchOrderInfoResult.getReturn_code())) { |
||||
order.setErrorCode(mchOrderInfoResult.getReturn_code()); |
||||
order.setErrorMsg(mchOrderInfoResult.getReturn_msg()); |
||||
return order; |
||||
} |
||||
|
||||
// 业务发生错误
|
||||
if (!"SUCCESS".equals(mchOrderInfoResult.getResult_code())) { |
||||
order.setErrorCode(mchOrderInfoResult.getErr_code()); |
||||
order.setErrorMsg(mchOrderInfoResult.getErr_code_des()); |
||||
return order; |
||||
} |
||||
|
||||
// 交易成功
|
||||
if ("SUCCESS".equals(mchOrderInfoResult.getTrade_state())) { |
||||
order.setSuccess(true); |
||||
order.setBankTransNo(mchOrderInfoResult.getTransaction_id()); |
||||
} else { |
||||
order.setErrorCode(mchOrderInfoResult.getTrade_state()); |
||||
order.setErrorMsg(mchOrderInfoResult.getTrade_state_desc()); |
||||
} |
||||
|
||||
order.setOutTradeNo(outTradeNo); |
||||
order.setTotalFee(new BigDecimal(mchOrderInfoResult.getTotal_fee()).movePointLeft(2)); |
||||
} catch (Exception e) { |
||||
ErrorHelper.println(e); |
||||
order.setErrorMsg(e.getMessage()); |
||||
} |
||||
return order; |
||||
} |
||||
|
||||
public static void main(String[] args){ |
||||
// String piMd5 = "9444144" + "T2001" + "xbd";
|
||||
// piMd5 = DigestUtils.md5Hex(piMd5).toUpperCase();
|
||||
// System.out.println(piMd5);
|
||||
Recipe recipe = new Recipe(); |
||||
recipe.setOpenid("test"); |
||||
recipe.setPatientId("test"); |
||||
recipe.setOutTradeNo("test"); |
||||
recipe.setRecipeFee(new BigDecimal(10)); |
||||
recipe.setTotalFee(new BigDecimal(10)); |
||||
recipe.setTreatNum("test"); |
||||
recipe.setPayWay("1"); |
||||
recipe.setRecipeId("1"); |
||||
recipe.setHisStatus(-1); |
||||
recipe.setPayStatus(-1); |
||||
new RecipeDao().insert(recipe); |
||||
|
||||
} |
||||
} |
||||
@ -1,30 +0,0 @@ |
||||
//package com.ynxbd.wx.servlet;
|
||||
//
|
||||
//import com.ynxbd.wx.wxfactory.WxAuthHelper;
|
||||
//import lombok.extern.slf4j.Slf4j;
|
||||
//
|
||||
//import javax.servlet.ServletException;
|
||||
//import javax.servlet.annotation.WebServlet;
|
||||
//import javax.servlet.http.HttpServlet;
|
||||
//import javax.servlet.http.HttpServletRequest;
|
||||
//import javax.servlet.http.HttpServletResponse;
|
||||
//import java.io.IOException;
|
||||
//
|
||||
///**
|
||||
// * 微信认证回调处理
|
||||
// *
|
||||
// * @author antgan
|
||||
// */
|
||||
//@Slf4j
|
||||
//@WebServlet("/u_auth")
|
||||
//public class WxUAuthServlet extends HttpServlet {
|
||||
//
|
||||
// protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
||||
// WxAuthHelper.auth(request, response, true);
|
||||
// }
|
||||
//
|
||||
// protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
||||
// doGet(request, response);
|
||||
// }
|
||||
//
|
||||
//}
|
||||
@ -1,284 +1,284 @@ |
||||
package com.ynxbd.wx.wxfactory; |
||||
|
||||
import com.ynxbd.common.bean.Patient; |
||||
import com.ynxbd.common.bean.User; |
||||
import com.ynxbd.common.helper.common.*; |
||||
import com.ynxbd.common.helper.http.OkHttpHelper; |
||||
import com.ynxbd.common.result.Result; |
||||
import com.ynxbd.common.service.PatientService; |
||||
import com.ynxbd.wx.config.WeChatConfig; |
||||
import com.ynxbd.wx.wxfactory.bean.SnsOath2AccessToken; |
||||
import com.ynxbd.wx.wxfactory.bean.SnsUserInfo; |
||||
import com.ynxbd.wx.wxfactory.bean.auth.AuthData; |
||||
import lombok.extern.slf4j.Slf4j; |
||||
import okhttp3.FormBody; |
||||
import okhttp3.RequestBody; |
||||
import org.apache.commons.lang3.ObjectUtils; |
||||
import org.ehcache.Cache; |
||||
|
||||
import javax.servlet.http.HttpServletRequest; |
||||
import javax.servlet.http.HttpServletResponse; |
||||
import javax.servlet.http.HttpSession; |
||||
import java.net.URLDecoder; |
||||
import java.net.URLEncoder; |
||||
import java.util.Date; |
||||
import java.util.HashMap; |
||||
import java.util.List; |
||||
import java.util.Map; |
||||
|
||||
@Slf4j |
||||
public class WxAuthHelper { |
||||
private static final int SESSION_MAX_INACTIVE_INTERVAL = 60 * 60; // session最大存活时间 1H
|
||||
|
||||
public static String auth(HttpServletRequest request, HttpServletResponse response, boolean isUserInfo) { |
||||
String code = request.getParameter("code"); |
||||
String state = request.getParameter("state"); // base64
|
||||
String enuId = request.getParameter("enuId"); |
||||
|
||||
log.info("[授权] code={}, state={}, enuId={}", code, state, enuId); |
||||
try { |
||||
state = state == null ? "" : URLDecoder.decode(Base64Helper.decode(state), "UTF-8"); |
||||
String enUnionId = null; |
||||
String protocolState = null; |
||||
|
||||
if (!ObjectUtils.isEmpty(enuId)) { |
||||
int index = enuId.indexOf("@protocolState="); |
||||
if (index == -1) index = enuId.indexOf("%40protocolState="); // 防止数据转义失败
|
||||
if (index != -1) { |
||||
enUnionId = enuId.substring(0, index); |
||||
protocolState = enuId.substring(index); |
||||
} |
||||
} |
||||
|
||||
log.info("[授权-解码] enUnionId={}, protocolState={}, state={}", enUnionId, protocolState, state); |
||||
|
||||
SnsOath2AccessToken snsToken = WxFactory.Base.OAuth().oauth2AccessToken(WeChatConfig.APP_ID, WeChatConfig.APP_SECRET, code); |
||||
if (snsToken != null) { |
||||
String openid = snsToken.getOpenid(); |
||||
String unionId = snsToken.getUnionid(); |
||||
if (unionId == null) { |
||||
log.info("[测试] enUnionId={}", enUnionId); |
||||
} |
||||
if (openid != null) { |
||||
HttpSession session = request.getSession(); |
||||
session.setMaxInactiveInterval(SESSION_MAX_INACTIVE_INTERVAL); |
||||
session.setAttribute("openid", openid); |
||||
|
||||
Cache<String, User> cache = WxCacheHelper.getUserCacheManager(); |
||||
if (WeChatConfig.isDevUser(openid) || !cache.containsKey(openid)) { |
||||
User user = new User(); |
||||
user.setUnionId(unionId); |
||||
user.setOpenid(openid); |
||||
user.setIsSnapShotUser(snsToken.getIsSnapShotUser()); |
||||
user.setPatientList(new PatientService().queryPatientList(openid, unionId, true)); |
||||
|
||||
if (isUserInfo) { |
||||
SnsUserInfo snsUser = WxFactory.Base.OAuth().snsUserInfo(snsToken.getAccessToken(), openid, "zh_CN", 3); |
||||
if (snsUser != null) { |
||||
user.setCountry(snsUser.getCountry()); |
||||
user.setAvatar(snsUser.getHeadImgUrl()); |
||||
user.setNickName(snsUser.getNickname_emoji()); |
||||
user.setProvince(snsUser.getProvince()); |
||||
user.setGenderByInt(snsUser.getSex()); |
||||
user.setCity(snsUser.getCity()); |
||||
user.setLanguage(snsUser.getLanguage()); |
||||
} |
||||
} |
||||
cache.put(openid, user); |
||||
} else { |
||||
if (isUserInfo) { |
||||
User user = cache.get(openid); |
||||
if (user != null && user.getAvatar() == null && user.getNickName() == null) { |
||||
SnsUserInfo snsUser = WxFactory.Base.OAuth().snsUserInfo(snsToken.getAccessToken(), openid, "zh_CN", 3); |
||||
if (snsUser != null) { |
||||
user.setAvatar(snsUser.getHeadImgUrl()); |
||||
user.setNickName(snsUser.getNickname_emoji()); |
||||
user.setGenderByInt(snsUser.getSex()); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
|
||||
if (WeChatConfig.HAS_HTTPS_BY_BASE_URL) { // 强制为https
|
||||
String httpsURL = URLHelper.URLToHttps(state); |
||||
state = httpsURL == null ? "" : httpsURL; |
||||
} |
||||
|
||||
if (state.contains(".html")) { // 网页授权配置
|
||||
return state; |
||||
} else { |
||||
String baseUrl = WeChatConfig.getBaseURL(); |
||||
if (baseUrl != null && state.contains(baseUrl)) { |
||||
return state; |
||||
} |
||||
} |
||||
} catch (Exception e) { |
||||
log.error("[微信][获取重定向链接异常]{}", e.getMessage()); |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
|
||||
public static Result isAuth(HttpServletRequest request, boolean isPayOAuth) throws Exception { |
||||
String token = request.getParameter("token"); // 前端缓存
|
||||
String state = request.getParameter("state"); |
||||
String isUserInfo = request.getParameter("isUserInfo"); |
||||
String protocolState = request.getParameter("protocolState"); |
||||
String enuId = ParamHelper.filterParamNull(request.getParameter("enuId"), ""); |
||||
String deState = URLDecoder.decode(Base64Helper.decode(state), "UTF-8"); |
||||
|
||||
if (WeChatConfig.IS_ENABLE_GMC && !WeChatConfig.IS_GMC_SERVER && !isPayOAuth) { // 开启医共体开关 & 不是医共体主服务器 & 不是支付授权
|
||||
try { // 请求转发
|
||||
String serverDomain = WeChatConfig.getDomain(false, false); |
||||
if (deState != null && serverDomain != null && !deState.contains(serverDomain)) { |
||||
return Result.error("授权域名不匹配"); |
||||
} |
||||
|
||||
RequestBody requestBody = new FormBody.Builder() |
||||
.add("token", token) |
||||
.add("state", state) |
||||
.add("isUserInfo", isUserInfo) |
||||
.add("enuId", enuId) |
||||
.add("protocolState", protocolState) |
||||
.build(); |
||||
log.info("[认证请求转发] URL:[{}]", WeChatConfig.getGMCAuthDomain(isHttpsWithProxy(request), true)); |
||||
String data = OkHttpHelper.post(WeChatConfig.getGMCAuthDomain(isHttpsWithProxy(request), true) + "wx_auth/is_auth", requestBody); |
||||
return Result.dataToResult(data, true); |
||||
} catch (Exception e) { |
||||
return Result.error(e); |
||||
} |
||||
} |
||||
|
||||
boolean isFindUserInfo = ("true".equals(isUserInfo)); |
||||
|
||||
AuthData authData = new AuthData(); |
||||
String cacheOpenid = authData.decodeToken(token, WeChatConfig.APP_ID); |
||||
if (cacheOpenid != null) { |
||||
log.info("[微信token认证] token={}, openid={}", token, cacheOpenid); |
||||
User user = WxCacheHelper.getCacheUser(cacheOpenid); |
||||
List<Patient> patients; |
||||
if (user == null) { |
||||
patients = new PatientService().queryPatientList(cacheOpenid, null, true); |
||||
Cache<String, User> cache = WxCacheHelper.getUserCacheManager(); |
||||
User addCache = new User(); |
||||
addCache.setOpenid(cacheOpenid); |
||||
addCache.setUnionId(authData.getUnionId()); |
||||
addCache.setAvatar(authData.getAvatar()); |
||||
addCache.setNickName(authData.getNickName()); |
||||
addCache.setPatientList(patients); |
||||
cache.put(cacheOpenid, addCache); |
||||
} else { |
||||
patients = user.getPatientList(); |
||||
} |
||||
|
||||
Map<String, Object> map = new HashMap<>(); |
||||
map.put("openid", cacheOpenid); |
||||
map.put("token", token); |
||||
map.put("enOpenId", AesWxHelper.encode(cacheOpenid, true)); |
||||
map.put("enUnionId", AesWxHelper.encode(authData.getUnionId(), true)); |
||||
map.put("date", new Date()); |
||||
map.put("avatar", authData.getAvatar()); |
||||
map.put("nickName", authData.getNickName()); |
||||
map.put("patients", CodeHelper.get28UUID() + Base64Helper.encode(URLEncoder.encode(JsonHelper.toJsonString(patients), "UTF-8"))); |
||||
map.put("enParams", AesMicroHelper.encode(cacheOpenid)); |
||||
return Result.success(map); |
||||
} |
||||
|
||||
HttpSession session = request.getSession(); |
||||
Object openid = session.getAttribute("openid"); |
||||
if (openid != null) { |
||||
log.info("[微信认证]openid={}", openid); |
||||
User user = WxCacheHelper.getCacheUser((String) openid); |
||||
if (user == null) { |
||||
return Result.success(getAuthUrl(request, state, isFindUserInfo, enuId, protocolState)); |
||||
} |
||||
|
||||
if (isFindUserInfo) { // 更换授权模式,需更新信息
|
||||
if (user.getNickName() == null || user.getAvatar() == null) { |
||||
return Result.success(getAuthUrl(request, state, true, enuId, protocolState)); |
||||
} |
||||
} |
||||
|
||||
Map<String, Object> map = new HashMap<>(); |
||||
map.put("openid", openid); |
||||
map.put("token", new AuthData().createToken(WeChatConfig.APP_ID, openid.toString(), user.getUnionId(), user.getAvatar(), user.getNickName())); |
||||
map.put("enOpenId", AesWxHelper.encode(openid.toString(), true)); |
||||
map.put("enUnionId", AesWxHelper.encode(user.getUnionId(), true)); |
||||
map.put("date", new Date()); |
||||
map.put("avatar", user.getAvatar()); |
||||
map.put("nickName", user.getNickName()); |
||||
map.put("patients", CodeHelper.get28UUID() + Base64Helper.encode(URLEncoder.encode(JsonHelper.toJsonString(user.getPatientList()), "UTF-8"))); |
||||
map.put("enParams", AesMicroHelper.encode(openid.toString())); |
||||
return Result.success(map); |
||||
} |
||||
|
||||
return Result.success(getAuthUrl(request, state, isFindUserInfo, enuId, protocolState)); |
||||
} |
||||
|
||||
private static final String OAUTH_URL = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=" + WeChatConfig.APP_ID + "&redirect_uri="; |
||||
|
||||
private static String getAuthUrl(HttpServletRequest request, String state, boolean isFindUserInfo, String enuId, String protocolState) { |
||||
// StringBuffer url = request.getRequestURL();
|
||||
// String baseUrl = url.delete(url.length() - request.getRequestURI().length(), url.length()).append(request.getServletContext().getContextPath()).append("/").toString();
|
||||
if (state == null) { |
||||
return null; |
||||
} |
||||
|
||||
String api = isFindUserInfo ? "u_auth" : "b_auth"; |
||||
String scope = isFindUserInfo ? "snsapi_userinfo" : "snsapi_base"; |
||||
|
||||
state = OAUTH_URL + WeChatConfig.getBaseURL(WeChatConfig.HAS_HTTPS_BY_BASE_URL || isHttpsWithProxy(request)) + |
||||
"wx_auth/" + api + |
||||
"?state=" + state + |
||||
"&response_type=code" + |
||||
"&scope=" + scope + "&forcePopup=true" + |
||||
"&enuId=" + (enuId == null ? "" : enuId) + |
||||
(protocolState == null ? "" : ("%40protocolState=" + protocolState)) + |
||||
"#wechat_redirect"; |
||||
return Base64Helper.encode(state); |
||||
} |
||||
|
||||
|
||||
// 是否为https请求
|
||||
public static boolean isHttpsWithProxy(HttpServletRequest request) { |
||||
// 优先检查代理头(适用于反向代理场景)
|
||||
String forwardedProto = request.getHeader("X-Forwarded-Proto"); |
||||
if (forwardedProto != null) { |
||||
return "https".equalsIgnoreCase(forwardedProto); |
||||
} |
||||
// 未经过代理,直接检查原生请求
|
||||
return request.isSecure(); |
||||
} |
||||
|
||||
|
||||
// // 医共体开启 & 不是支付授权
|
||||
// private static boolean isAuthGMC(boolean isPayOAuth) {
|
||||
// return WeChatConfig.IS_ENABLE_GMC && !isPayOAuth;
|
||||
// }
|
||||
|
||||
// private static String getOAuthURL(HttpServletRequest request, boolean isPayOAuth) {
|
||||
// boolean isHttps = WeChatConfig.HAS_HTTPS_BY_BASE_URL || isHttpsWithProxy(request);
|
||||
// return isAuthGMC(isPayOAuth)
|
||||
// ? "https://open.weixin.qq.com/connect/oauth2/authorize?appid=" + WeChatConfig.GMC_AUTH_APP_ID + "&redirect_uri=" + WeChatConfig.getGMCAuthDomain(isHttps, true)
|
||||
// : "https://open.weixin.qq.com/connect/oauth2/authorize?appid=" + WeChatConfig.APP_ID + "&redirect_uri=" + WeChatConfig.getBaseURL(isHttps);
|
||||
// }
|
||||
|
||||
// private static SnsOath2AccessToken getOath2AccessToken(String code, boolean isPayOAuth) {
|
||||
// if (isAuthGMC(isPayOAuth)) {
|
||||
// return WxFactory.Base.OAuth().oauth2AccessToken(WeChatConfig.GMC_AUTH_APP_ID, WeChatConfig.GMC_AUTH_APP_SECRET, code);
|
||||
// }
|
||||
// return WxFactory.Base.OAuth().oauth2AccessToken(WeChatConfig.APP_ID, WeChatConfig.APP_SECRET, code);
|
||||
// }
|
||||
|
||||
// // 获取重定向链接
|
||||
// private static String getAuthDomain(HttpServletRequest request, boolean isPayOAuth) {
|
||||
// if (isAuthGMC(isPayOAuth)) {
|
||||
// return WeChatConfig.getGMCAuthDomain(true, false); // 强制为https
|
||||
// }
|
||||
// return WeChatConfig.getDomain(WeChatConfig.HAS_HTTPS_BY_BASE_URL || isHttpsWithProxy(request)); // 配置中baseURL有"https"时优先级最高 ["@protocol=1"为https;"@protocol=0"为默认的http;]
|
||||
// }
|
||||
|
||||
} |
||||
package com.ynxbd.wx.wxfactory; |
||||
|
||||
import com.ynxbd.common.bean.Patient; |
||||
import com.ynxbd.common.bean.User; |
||||
import com.ynxbd.common.helper.common.*; |
||||
import com.ynxbd.common.helper.http.OkHttpHelper; |
||||
import com.ynxbd.common.result.Result; |
||||
import com.ynxbd.common.service.PatientService; |
||||
import com.ynxbd.wx.config.WeChatConfig; |
||||
import com.ynxbd.wx.wxfactory.base.auth.models.AuthData; |
||||
import com.ynxbd.wx.wxfactory.base.auth.models.SnsOath2AccessToken; |
||||
import com.ynxbd.wx.wxfactory.base.auth.models.SnsUserInfo; |
||||
import lombok.extern.slf4j.Slf4j; |
||||
import okhttp3.FormBody; |
||||
import okhttp3.RequestBody; |
||||
import org.apache.commons.lang3.ObjectUtils; |
||||
import org.ehcache.Cache; |
||||
|
||||
import javax.servlet.http.HttpServletRequest; |
||||
import javax.servlet.http.HttpServletResponse; |
||||
import javax.servlet.http.HttpSession; |
||||
import java.net.URLDecoder; |
||||
import java.net.URLEncoder; |
||||
import java.util.Date; |
||||
import java.util.HashMap; |
||||
import java.util.List; |
||||
import java.util.Map; |
||||
|
||||
@Slf4j |
||||
public class WxAuthHelper { |
||||
private static final int SESSION_MAX_INACTIVE_INTERVAL = 60 * 60; // session最大存活时间 1H
|
||||
|
||||
public static String auth(HttpServletRequest request, HttpServletResponse response, boolean isUserInfo) { |
||||
String code = request.getParameter("code"); |
||||
String state = request.getParameter("state"); // base64
|
||||
String enuId = request.getParameter("enuId"); |
||||
|
||||
log.info("[授权] code={}, state={}, enuId={}", code, state, enuId); |
||||
try { |
||||
state = state == null ? "" : URLDecoder.decode(Base64Helper.decode(state), "UTF-8"); |
||||
String enUnionId = null; |
||||
String protocolState = null; |
||||
|
||||
if (!ObjectUtils.isEmpty(enuId)) { |
||||
int index = enuId.indexOf("@protocolState="); |
||||
if (index == -1) index = enuId.indexOf("%40protocolState="); // 防止数据转义失败
|
||||
if (index != -1) { |
||||
enUnionId = enuId.substring(0, index); |
||||
protocolState = enuId.substring(index); |
||||
} |
||||
} |
||||
|
||||
log.info("[授权-解码] enUnionId={}, protocolState={}, state={}", enUnionId, protocolState, state); |
||||
|
||||
SnsOath2AccessToken snsToken = WxFactory.Base.OAuth().oauth2AccessToken(WeChatConfig.APP_ID, WeChatConfig.APP_SECRET, code); |
||||
if (snsToken != null) { |
||||
String openid = snsToken.getOpenid(); |
||||
String unionId = snsToken.getUnionid(); |
||||
if (unionId == null) { |
||||
log.info("[测试] enUnionId={}", enUnionId); |
||||
} |
||||
if (openid != null) { |
||||
HttpSession session = request.getSession(); |
||||
session.setMaxInactiveInterval(SESSION_MAX_INACTIVE_INTERVAL); |
||||
session.setAttribute("openid", openid); |
||||
|
||||
Cache<String, User> cache = WxCacheHelper.getUserCacheManager(); |
||||
if (WeChatConfig.isDevUser(openid) || !cache.containsKey(openid)) { |
||||
User user = new User(); |
||||
user.setUnionId(unionId); |
||||
user.setOpenid(openid); |
||||
user.setIsSnapShotUser(snsToken.getIsSnapShotUser()); |
||||
user.setPatientList(new PatientService().queryPatientList(openid, unionId, true)); |
||||
|
||||
if (isUserInfo) { |
||||
SnsUserInfo snsUser = WxFactory.Base.OAuth().snsUserInfo(snsToken.getAccessToken(), openid, "zh_CN", 3); |
||||
if (snsUser != null) { |
||||
user.setCountry(snsUser.getCountry()); |
||||
user.setAvatar(snsUser.getHeadImgUrl()); |
||||
user.setNickName(snsUser.getNickname_emoji()); |
||||
user.setProvince(snsUser.getProvince()); |
||||
user.setGenderByInt(snsUser.getSex()); |
||||
user.setCity(snsUser.getCity()); |
||||
user.setLanguage(snsUser.getLanguage()); |
||||
} |
||||
} |
||||
cache.put(openid, user); |
||||
} else { |
||||
if (isUserInfo) { |
||||
User user = cache.get(openid); |
||||
if (user != null && user.getAvatar() == null && user.getNickName() == null) { |
||||
SnsUserInfo snsUser = WxFactory.Base.OAuth().snsUserInfo(snsToken.getAccessToken(), openid, "zh_CN", 3); |
||||
if (snsUser != null) { |
||||
user.setAvatar(snsUser.getHeadImgUrl()); |
||||
user.setNickName(snsUser.getNickname_emoji()); |
||||
user.setGenderByInt(snsUser.getSex()); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
|
||||
if (WeChatConfig.HAS_HTTPS_BY_BASE_URL) { // 强制为https
|
||||
String httpsURL = URLHelper.URLToHttps(state); |
||||
state = httpsURL == null ? "" : httpsURL; |
||||
} |
||||
|
||||
if (state.contains(".html")) { // 网页授权配置
|
||||
return state; |
||||
} else { |
||||
String baseUrl = WeChatConfig.getBaseURL(); |
||||
if (baseUrl != null && state.contains(baseUrl)) { |
||||
return state; |
||||
} |
||||
} |
||||
} catch (Exception e) { |
||||
log.error("[微信][获取重定向链接异常]{}", e.getMessage()); |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
|
||||
public static Result isAuth(HttpServletRequest request, boolean isPayOAuth) throws Exception { |
||||
String token = request.getParameter("token"); // 前端缓存
|
||||
String state = request.getParameter("state"); |
||||
String isUserInfo = request.getParameter("isUserInfo"); |
||||
String protocolState = request.getParameter("protocolState"); |
||||
String enuId = ParamHelper.filterParamNull(request.getParameter("enuId"), ""); |
||||
String deState = URLDecoder.decode(Base64Helper.decode(state), "UTF-8"); |
||||
|
||||
if (WeChatConfig.IS_ENABLE_GMC && !WeChatConfig.IS_GMC_SERVER && !isPayOAuth) { // 开启医共体开关 & 不是医共体主服务器 & 不是支付授权
|
||||
try { // 请求转发
|
||||
String serverDomain = WeChatConfig.getDomain(false, false); |
||||
if (deState != null && serverDomain != null && !deState.contains(serverDomain)) { |
||||
return Result.error("授权域名不匹配"); |
||||
} |
||||
|
||||
RequestBody requestBody = new FormBody.Builder() |
||||
.add("token", token) |
||||
.add("state", state) |
||||
.add("isUserInfo", isUserInfo) |
||||
.add("enuId", enuId) |
||||
.add("protocolState", protocolState) |
||||
.build(); |
||||
log.info("[认证请求转发] URL:[{}]", WeChatConfig.getGMCAuthDomain(isHttpsWithProxy(request), true)); |
||||
String data = OkHttpHelper.post(WeChatConfig.getGMCAuthDomain(isHttpsWithProxy(request), true) + "wx_auth/is_auth", requestBody); |
||||
return Result.dataToResult(data, true); |
||||
} catch (Exception e) { |
||||
return Result.error(e); |
||||
} |
||||
} |
||||
|
||||
boolean isFindUserInfo = ("true".equals(isUserInfo)); |
||||
|
||||
AuthData authData = new AuthData(); |
||||
String cacheOpenid = authData.decodeToken(token, WeChatConfig.APP_ID); |
||||
if (cacheOpenid != null) { |
||||
log.info("[微信token认证] token={}, openid={}", token, cacheOpenid); |
||||
User user = WxCacheHelper.getCacheUser(cacheOpenid); |
||||
List<Patient> patients; |
||||
if (user == null) { |
||||
patients = new PatientService().queryPatientList(cacheOpenid, null, true); |
||||
Cache<String, User> cache = WxCacheHelper.getUserCacheManager(); |
||||
User addCache = new User(); |
||||
addCache.setOpenid(cacheOpenid); |
||||
addCache.setUnionId(authData.getUnionId()); |
||||
addCache.setAvatar(authData.getAvatar()); |
||||
addCache.setNickName(authData.getNickName()); |
||||
addCache.setPatientList(patients); |
||||
cache.put(cacheOpenid, addCache); |
||||
} else { |
||||
patients = user.getPatientList(); |
||||
} |
||||
|
||||
Map<String, Object> map = new HashMap<>(); |
||||
map.put("openid", cacheOpenid); |
||||
map.put("token", token); |
||||
map.put("enOpenId", AesWxHelper.encode(cacheOpenid, true)); |
||||
map.put("enUnionId", AesWxHelper.encode(authData.getUnionId(), true)); |
||||
map.put("date", new Date()); |
||||
map.put("avatar", authData.getAvatar()); |
||||
map.put("nickName", authData.getNickName()); |
||||
map.put("patients", CodeHelper.get28UUID() + Base64Helper.encode(URLEncoder.encode(JsonHelper.toJsonString(patients), "UTF-8"))); |
||||
map.put("enParams", AesMicroHelper.encode(cacheOpenid)); |
||||
return Result.success(map); |
||||
} |
||||
|
||||
HttpSession session = request.getSession(); |
||||
Object openid = session.getAttribute("openid"); |
||||
if (openid != null) { |
||||
log.info("[微信认证]openid={}", openid); |
||||
User user = WxCacheHelper.getCacheUser((String) openid); |
||||
if (user == null) { |
||||
return Result.success(getAuthUrl(request, state, isFindUserInfo, enuId, protocolState)); |
||||
} |
||||
|
||||
if (isFindUserInfo) { // 更换授权模式,需更新信息
|
||||
if (user.getNickName() == null || user.getAvatar() == null) { |
||||
return Result.success(getAuthUrl(request, state, true, enuId, protocolState)); |
||||
} |
||||
} |
||||
|
||||
Map<String, Object> map = new HashMap<>(); |
||||
map.put("openid", openid); |
||||
map.put("token", new AuthData().createToken(WeChatConfig.APP_ID, openid.toString(), user.getUnionId(), user.getAvatar(), user.getNickName())); |
||||
map.put("enOpenId", AesWxHelper.encode(openid.toString(), true)); |
||||
map.put("enUnionId", AesWxHelper.encode(user.getUnionId(), true)); |
||||
map.put("date", new Date()); |
||||
map.put("avatar", user.getAvatar()); |
||||
map.put("nickName", user.getNickName()); |
||||
map.put("patients", CodeHelper.get28UUID() + Base64Helper.encode(URLEncoder.encode(JsonHelper.toJsonString(user.getPatientList()), "UTF-8"))); |
||||
map.put("enParams", AesMicroHelper.encode(openid.toString())); |
||||
return Result.success(map); |
||||
} |
||||
|
||||
return Result.success(getAuthUrl(request, state, isFindUserInfo, enuId, protocolState)); |
||||
} |
||||
|
||||
private static final String OAUTH_URL = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=" + WeChatConfig.APP_ID + "&redirect_uri="; |
||||
|
||||
private static String getAuthUrl(HttpServletRequest request, String state, boolean isFindUserInfo, String enuId, String protocolState) { |
||||
// StringBuffer url = request.getRequestURL();
|
||||
// String baseUrl = url.delete(url.length() - request.getRequestURI().length(), url.length()).append(request.getServletContext().getContextPath()).append("/").toString();
|
||||
if (state == null) { |
||||
return null; |
||||
} |
||||
|
||||
String api = isFindUserInfo ? "u_auth" : "b_auth"; |
||||
String scope = isFindUserInfo ? "snsapi_userinfo" : "snsapi_base"; |
||||
|
||||
state = OAUTH_URL + WeChatConfig.getBaseURL(WeChatConfig.HAS_HTTPS_BY_BASE_URL || isHttpsWithProxy(request)) + |
||||
"wx_auth/" + api + |
||||
"?state=" + state + |
||||
"&response_type=code" + |
||||
"&scope=" + scope + "&forcePopup=true" + |
||||
"&enuId=" + (enuId == null ? "" : enuId) + |
||||
(protocolState == null ? "" : ("%40protocolState=" + protocolState)) + |
||||
"#wechat_redirect"; |
||||
return Base64Helper.encode(state); |
||||
} |
||||
|
||||
|
||||
// 是否为https请求
|
||||
public static boolean isHttpsWithProxy(HttpServletRequest request) { |
||||
// 优先检查代理头(适用于反向代理场景)
|
||||
String forwardedProto = request.getHeader("X-Forwarded-Proto"); |
||||
if (forwardedProto != null) { |
||||
return "https".equalsIgnoreCase(forwardedProto); |
||||
} |
||||
// 未经过代理,直接检查原生请求
|
||||
return request.isSecure(); |
||||
} |
||||
|
||||
|
||||
// // 医共体开启 & 不是支付授权
|
||||
// private static boolean isAuthGMC(boolean isPayOAuth) {
|
||||
// return WeChatConfig.IS_ENABLE_GMC && !isPayOAuth;
|
||||
// }
|
||||
|
||||
// private static String getOAuthURL(HttpServletRequest request, boolean isPayOAuth) {
|
||||
// boolean isHttps = WeChatConfig.HAS_HTTPS_BY_BASE_URL || isHttpsWithProxy(request);
|
||||
// return isAuthGMC(isPayOAuth)
|
||||
// ? "https://open.weixin.qq.com/connect/oauth2/authorize?appid=" + WeChatConfig.GMC_AUTH_APP_ID + "&redirect_uri=" + WeChatConfig.getGMCAuthDomain(isHttps, true)
|
||||
// : "https://open.weixin.qq.com/connect/oauth2/authorize?appid=" + WeChatConfig.APP_ID + "&redirect_uri=" + WeChatConfig.getBaseURL(isHttps);
|
||||
// }
|
||||
|
||||
// private static SnsOath2AccessToken getOath2AccessToken(String code, boolean isPayOAuth) {
|
||||
// if (isAuthGMC(isPayOAuth)) {
|
||||
// return WxFactory.Base.OAuth().oauth2AccessToken(WeChatConfig.GMC_AUTH_APP_ID, WeChatConfig.GMC_AUTH_APP_SECRET, code);
|
||||
// }
|
||||
// return WxFactory.Base.OAuth().oauth2AccessToken(WeChatConfig.APP_ID, WeChatConfig.APP_SECRET, code);
|
||||
// }
|
||||
|
||||
// // 获取重定向链接
|
||||
// private static String getAuthDomain(HttpServletRequest request, boolean isPayOAuth) {
|
||||
// if (isAuthGMC(isPayOAuth)) {
|
||||
// return WeChatConfig.getGMCAuthDomain(true, false); // 强制为https
|
||||
// }
|
||||
// return WeChatConfig.getDomain(WeChatConfig.HAS_HTTPS_BY_BASE_URL || isHttpsWithProxy(request)); // 配置中baseURL有"https"时优先级最高 ["@protocol=1"为https;"@protocol=0"为默认的http;]
|
||||
// }
|
||||
|
||||
} |
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,20 +1,20 @@ |
||||
package com.ynxbd.wx.wxfactory.bean; |
||||
|
||||
import lombok.Getter; |
||||
import lombok.NoArgsConstructor; |
||||
import lombok.Setter; |
||||
import lombok.ToString; |
||||
|
||||
import java.io.Serializable; |
||||
|
||||
@Getter |
||||
@Setter |
||||
@ToString |
||||
@NoArgsConstructor |
||||
public class AccessToken implements Serializable { |
||||
private static final long serialVersionUID = 36888886677781604L; |
||||
|
||||
private String accessToken; |
||||
// 创建时间
|
||||
private String createTime; |
||||
} |
||||
package com.ynxbd.wx.wxfactory.base.auth.models; |
||||
|
||||
import lombok.Getter; |
||||
import lombok.NoArgsConstructor; |
||||
import lombok.Setter; |
||||
import lombok.ToString; |
||||
|
||||
import java.io.Serializable; |
||||
|
||||
@Getter |
||||
@Setter |
||||
@ToString |
||||
@NoArgsConstructor |
||||
public class AccessToken implements Serializable { |
||||
private static final long serialVersionUID = 36888886677781604L; |
||||
|
||||
private String accessToken; |
||||
// 创建时间
|
||||
private String createTime; |
||||
} |
||||
@ -1,4 +1,4 @@ |
||||
package com.ynxbd.wx.wxfactory.bean.auth; |
||||
package com.ynxbd.wx.wxfactory.base.auth.models; |
||||
|
||||
import com.ynxbd.common.helper.common.DateHelper; |
||||
import com.ynxbd.common.helper.common.JsonHelper; |
||||
@ -1,4 +1,4 @@ |
||||
package com.ynxbd.wx.wxfactory.bean; |
||||
package com.ynxbd.wx.wxfactory.base.auth.models; |
||||
|
||||
import lombok.Getter; |
||||
import lombok.NoArgsConstructor; |
||||
@ -1,28 +1,28 @@ |
||||
package com.ynxbd.wx.wxfactory.bean; |
||||
|
||||
import lombok.Getter; |
||||
import lombok.NoArgsConstructor; |
||||
import lombok.Setter; |
||||
import lombok.ToString; |
||||
|
||||
@Getter |
||||
@Setter |
||||
@ToString |
||||
@NoArgsConstructor |
||||
public class SnsOath2AccessToken extends WxBaseResult { |
||||
private String accessToken; |
||||
private Integer expiresIn; |
||||
private String refreshToken; |
||||
private String openid; |
||||
private String scope; |
||||
private String unionid; |
||||
// 是否为快照页模式虚拟账号,只有当用户是快照页模式虚拟账号是返回,值为1
|
||||
private String isSnapShotUser; |
||||
|
||||
public Boolean getIsSnapShotUser() { |
||||
if (isSnapShotUser == null) { |
||||
return null; |
||||
} |
||||
return "1".equals(isSnapShotUser); |
||||
} |
||||
} |
||||
package com.ynxbd.wx.wxfactory.base.auth.models; |
||||
|
||||
import lombok.Getter; |
||||
import lombok.NoArgsConstructor; |
||||
import lombok.Setter; |
||||
import lombok.ToString; |
||||
|
||||
@Getter |
||||
@Setter |
||||
@ToString |
||||
@NoArgsConstructor |
||||
public class SnsOath2AccessToken extends WxBaseResult { |
||||
private String accessToken; |
||||
private Integer expiresIn; |
||||
private String refreshToken; |
||||
private String openid; |
||||
private String scope; |
||||
private String unionid; |
||||
// 是否为快照页模式虚拟账号,只有当用户是快照页模式虚拟账号是返回,值为1
|
||||
private String isSnapShotUser; |
||||
|
||||
public Boolean getIsSnapShotUser() { |
||||
if (isSnapShotUser == null) { |
||||
return null; |
||||
} |
||||
return "1".equals(isSnapShotUser); |
||||
} |
||||
} |
||||
@ -1,33 +1,33 @@ |
||||
package com.ynxbd.wx.wxfactory.bean; |
||||
|
||||
import lombok.Getter; |
||||
import lombok.NoArgsConstructor; |
||||
import lombok.Setter; |
||||
import lombok.ToString; |
||||
|
||||
import java.io.Serializable; |
||||
|
||||
@Getter |
||||
@Setter |
||||
@ToString(callSuper = true) |
||||
@NoArgsConstructor |
||||
public class SnsUserInfo extends WxBaseResult implements Serializable { |
||||
private static final long serialVersionUID = 20221025231300001L; |
||||
private Integer subscribe; |
||||
private String openid; |
||||
private String nickname; |
||||
private String nickname_emoji; |
||||
private Integer sex; |
||||
private String language; |
||||
private String city; |
||||
private String province; |
||||
private String country; |
||||
private String headImgUrl; |
||||
private Integer subscribe_time; |
||||
private String unionid; |
||||
private Integer groupid; |
||||
private String remark; |
||||
private String subscribe_scene; |
||||
private Integer qr_scene; |
||||
private String qr_scene_str; |
||||
} |
||||
package com.ynxbd.wx.wxfactory.base.auth.models; |
||||
|
||||
import lombok.Getter; |
||||
import lombok.NoArgsConstructor; |
||||
import lombok.Setter; |
||||
import lombok.ToString; |
||||
|
||||
import java.io.Serializable; |
||||
|
||||
@Getter |
||||
@Setter |
||||
@ToString(callSuper = true) |
||||
@NoArgsConstructor |
||||
public class SnsUserInfo extends WxBaseResult implements Serializable { |
||||
private static final long serialVersionUID = 20221025231300001L; |
||||
private Integer subscribe; |
||||
private String openid; |
||||
private String nickname; |
||||
private String nickname_emoji; |
||||
private Integer sex; |
||||
private String language; |
||||
private String city; |
||||
private String province; |
||||
private String country; |
||||
private String headImgUrl; |
||||
private Integer subscribe_time; |
||||
private String unionid; |
||||
private Integer groupid; |
||||
private String remark; |
||||
private String subscribe_scene; |
||||
private Integer qr_scene; |
||||
private String qr_scene_str; |
||||
} |
||||
@ -1,20 +1,20 @@ |
||||
package com.ynxbd.wx.wxfactory.bean; |
||||
|
||||
import lombok.Getter; |
||||
import lombok.NoArgsConstructor; |
||||
import lombok.Setter; |
||||
import lombok.ToString; |
||||
|
||||
@Getter |
||||
@Setter |
||||
@ToString(callSuper = true) |
||||
@NoArgsConstructor |
||||
public class WxBaseResult { |
||||
private final String SUCCESS_CODE = "0"; |
||||
private String errCode; |
||||
private String errMsg; |
||||
|
||||
public boolean isSuccess() { |
||||
return this.errCode == null || this.errCode.isEmpty() || SUCCESS_CODE.equals(this.errCode); |
||||
} |
||||
} |
||||
package com.ynxbd.wx.wxfactory.base.auth.models; |
||||
|
||||
import lombok.Getter; |
||||
import lombok.NoArgsConstructor; |
||||
import lombok.Setter; |
||||
import lombok.ToString; |
||||
|
||||
@Getter |
||||
@Setter |
||||
@ToString(callSuper = true) |
||||
@NoArgsConstructor |
||||
public class WxBaseResult { |
||||
private final String SUCCESS_CODE = "0"; |
||||
private String errCode; |
||||
private String errMsg; |
||||
|
||||
public boolean isSuccess() { |
||||
return this.errCode == null || this.errCode.isEmpty() || SUCCESS_CODE.equals(this.errCode); |
||||
} |
||||
} |
||||
@ -0,0 +1,68 @@ |
||||
package com.ynxbd.wx.wxfactory.base.passivemsg; |
||||
|
||||
import com.ynxbd.wx.wxfactory.base.passivemsg.models.XMLImageMsg; |
||||
import com.ynxbd.wx.wxfactory.base.passivemsg.models.XMLNewsMsg; |
||||
import com.ynxbd.wx.wxfactory.base.passivemsg.models.XMLTextMsg; |
||||
import lombok.NoArgsConstructor; |
||||
import lombok.extern.slf4j.Slf4j; |
||||
|
||||
import javax.servlet.http.HttpServletResponse; |
||||
import java.util.List; |
||||
|
||||
// 被动消息回复
|
||||
@Slf4j |
||||
@NoArgsConstructor |
||||
public class Client { |
||||
|
||||
/** |
||||
* 回复图片消息 |
||||
* |
||||
* @param response |
||||
* @param fromUserName |
||||
* @param toUserName |
||||
* @param mediaId |
||||
* @return |
||||
*/ |
||||
public boolean createXmlImageMsg(HttpServletResponse response, String fromUserName, String toUserName, String mediaId) { |
||||
return new XMLImageMsg(fromUserName, toUserName, mediaId).outputStreamWrite(response); |
||||
} |
||||
|
||||
/** |
||||
* 回复图文消息 |
||||
* |
||||
* @param response |
||||
* @param fromUserName |
||||
* @param toUserName |
||||
* @param article |
||||
* @return |
||||
*/ |
||||
public boolean createXmlNewsMsg(HttpServletResponse response, String fromUserName, String toUserName, XMLNewsMsg.Article article) { |
||||
return new XMLNewsMsg(fromUserName, toUserName, article).outputStreamWrite(response); |
||||
} |
||||
|
||||
/** |
||||
* 回复图文消息 |
||||
* |
||||
* @param response |
||||
* @param fromUserName |
||||
* @param toUserName |
||||
* @param articles |
||||
* @return |
||||
*/ |
||||
public boolean createXmlNewsMsg(HttpServletResponse response, String fromUserName, String toUserName, List<XMLNewsMsg.Article> articles) { |
||||
return new XMLNewsMsg(fromUserName, toUserName, articles).outputStreamWrite(response); |
||||
} |
||||
|
||||
/** |
||||
* 回复文本消息 |
||||
* |
||||
* @param response |
||||
* @param fromUserName |
||||
* @param toUserName |
||||
* @param content |
||||
* @return |
||||
*/ |
||||
public boolean createXmlTextMsg(HttpServletResponse response, String fromUserName, String toUserName, String content) { |
||||
return new XMLTextMsg(fromUserName, toUserName, content).outputStreamWrite(response); |
||||
} |
||||
} |
||||
@ -1,32 +1,32 @@ |
||||
package com.ynxbd.wx.wxfactory.bean.event; |
||||
|
||||
import lombok.Getter; |
||||
import lombok.NoArgsConstructor; |
||||
import lombok.Setter; |
||||
import lombok.ToString; |
||||
|
||||
@Getter |
||||
@Setter |
||||
@NoArgsConstructor |
||||
@ToString |
||||
public class WxEvent { |
||||
|
||||
private String toUserName; |
||||
|
||||
private String fromUserName; |
||||
|
||||
private Integer createTime; |
||||
|
||||
private String msgType; |
||||
|
||||
private String event; |
||||
|
||||
private String eventKey; |
||||
|
||||
private String content; |
||||
|
||||
private String url; |
||||
|
||||
private String msgId; |
||||
|
||||
} |
||||
package com.ynxbd.wx.wxfactory.base.passivemsg.event; |
||||
|
||||
import lombok.Getter; |
||||
import lombok.NoArgsConstructor; |
||||
import lombok.Setter; |
||||
import lombok.ToString; |
||||
|
||||
@Getter |
||||
@Setter |
||||
@NoArgsConstructor |
||||
@ToString |
||||
public class WxEvent { |
||||
|
||||
private String toUserName; |
||||
|
||||
private String fromUserName; |
||||
|
||||
private Integer createTime; |
||||
|
||||
private String msgType; |
||||
|
||||
private String event; |
||||
|
||||
private String eventKey; |
||||
|
||||
private String content; |
||||
|
||||
private String url; |
||||
|
||||
private String msgId; |
||||
|
||||
} |
||||
@ -1,134 +1,129 @@ |
||||
package com.ynxbd.wx.wxfactory.bean.event; |
||||
|
||||
import lombok.Getter; |
||||
import lombok.NoArgsConstructor; |
||||
import lombok.Setter; |
||||
import lombok.ToString; |
||||
import org.w3c.dom.Element; |
||||
import weixin.popular.bean.message.AroundBeacon; |
||||
import weixin.popular.bean.message.ChosenBeacon; |
||||
import weixin.popular.bean.message.CopyrightCheckResult; |
||||
|
||||
import java.util.List; |
||||
|
||||
@Getter |
||||
@Setter |
||||
@NoArgsConstructor |
||||
@ToString |
||||
public class WxEventMessage { |
||||
|
||||
private String toUserName; |
||||
|
||||
private String fromUserName; |
||||
|
||||
private Integer createTime; |
||||
|
||||
private String msgType; |
||||
|
||||
private String event; |
||||
|
||||
private String eventKey; |
||||
|
||||
private String msgId; |
||||
|
||||
private String content; |
||||
|
||||
private String picUrl; |
||||
|
||||
private String mediaId; |
||||
|
||||
private String format; |
||||
|
||||
private String recognition; |
||||
|
||||
private String thumbMediaId; |
||||
|
||||
private String location_X; |
||||
|
||||
private String location_Y; |
||||
|
||||
private String scale; |
||||
|
||||
private String label; |
||||
|
||||
private String title; |
||||
|
||||
private String description; |
||||
|
||||
private String url; |
||||
|
||||
private String ticket; |
||||
|
||||
private String latitude; |
||||
|
||||
private String longitude; |
||||
|
||||
private String precision; |
||||
|
||||
private String status; |
||||
|
||||
private Integer totalCount; |
||||
|
||||
private Integer filterCount; |
||||
|
||||
private Integer sentCount; |
||||
|
||||
private Integer errorCount; |
||||
|
||||
|
||||
private Integer expiredTime; |
||||
|
||||
private Integer failTime; |
||||
|
||||
private String failReason; |
||||
|
||||
private String uniqId; |
||||
|
||||
private String poiId; |
||||
|
||||
private String result; |
||||
|
||||
private String msg; |
||||
|
||||
private String lotteryId; |
||||
|
||||
private Integer money; |
||||
|
||||
private Integer bindTime; |
||||
|
||||
private Integer connectTime; |
||||
|
||||
private Integer expireTime; |
||||
|
||||
private String vendorId; |
||||
|
||||
private String shopId; |
||||
|
||||
private String deviceNo; |
||||
|
||||
private String keyStandard; |
||||
|
||||
private String keyStr; |
||||
|
||||
private String country; |
||||
|
||||
private String province; |
||||
|
||||
private String city; |
||||
|
||||
private Integer sex; |
||||
|
||||
private Integer scene; |
||||
|
||||
private String regionCode; |
||||
|
||||
private Integer reasonMsg; |
||||
|
||||
private ChosenBeacon chosenBeacon; |
||||
|
||||
private CopyrightCheckResult copyrightCheckResult; |
||||
|
||||
private List<AroundBeacon> aroundBeacons; |
||||
|
||||
private List<Element> otherElements; |
||||
} |
||||
package com.ynxbd.wx.wxfactory.base.passivemsg.event; |
||||
|
||||
import lombok.Getter; |
||||
import lombok.NoArgsConstructor; |
||||
import lombok.Setter; |
||||
import lombok.ToString; |
||||
|
||||
|
||||
@Getter |
||||
@Setter |
||||
@NoArgsConstructor |
||||
@ToString |
||||
public class WxEventMessage { |
||||
|
||||
private String toUserName; |
||||
|
||||
private String fromUserName; |
||||
|
||||
private Integer createTime; |
||||
|
||||
private String msgType; |
||||
|
||||
private String event; |
||||
|
||||
private String eventKey; |
||||
|
||||
private String msgId; |
||||
|
||||
private String content; |
||||
|
||||
private String picUrl; |
||||
|
||||
private String mediaId; |
||||
|
||||
private String format; |
||||
|
||||
private String recognition; |
||||
|
||||
private String thumbMediaId; |
||||
|
||||
private String location_X; |
||||
|
||||
private String location_Y; |
||||
|
||||
private String scale; |
||||
|
||||
private String label; |
||||
|
||||
private String title; |
||||
|
||||
private String description; |
||||
|
||||
private String url; |
||||
|
||||
private String ticket; |
||||
|
||||
private String latitude; |
||||
|
||||
private String longitude; |
||||
|
||||
private String precision; |
||||
|
||||
private String status; |
||||
|
||||
private Integer totalCount; |
||||
|
||||
private Integer filterCount; |
||||
|
||||
private Integer sentCount; |
||||
|
||||
private Integer errorCount; |
||||
|
||||
|
||||
private Integer expiredTime; |
||||
|
||||
private Integer failTime; |
||||
|
||||
private String failReason; |
||||
|
||||
private String uniqId; |
||||
|
||||
private String poiId; |
||||
|
||||
private String result; |
||||
|
||||
private String msg; |
||||
|
||||
private String lotteryId; |
||||
|
||||
private Integer money; |
||||
|
||||
private Integer bindTime; |
||||
|
||||
private Integer connectTime; |
||||
|
||||
private Integer expireTime; |
||||
|
||||
private String vendorId; |
||||
|
||||
private String shopId; |
||||
|
||||
private String deviceNo; |
||||
|
||||
private String keyStandard; |
||||
|
||||
private String keyStr; |
||||
|
||||
private String country; |
||||
|
||||
private String province; |
||||
|
||||
private String city; |
||||
|
||||
private Integer sex; |
||||
|
||||
private Integer scene; |
||||
|
||||
private String regionCode; |
||||
|
||||
private Integer reasonMsg; |
||||
|
||||
// private ChosenBeacon chosenBeacon;
|
||||
//
|
||||
// private CopyrightCheckResult copyrightCheckResult;
|
||||
//
|
||||
// private List<AroundBeacon> aroundBeacons;
|
||||
//
|
||||
// private List<Element> otherElements;
|
||||
} |
||||
@ -0,0 +1,93 @@ |
||||
package com.ynxbd.wx.wxfactory.base.passivemsg.models; |
||||
|
||||
import com.qq.weixin.mp.aes.AesException; |
||||
import com.qq.weixin.mp.aes.WXBizMsgCrypt; |
||||
import lombok.Getter; |
||||
import lombok.Setter; |
||||
import lombok.ToString; |
||||
import lombok.extern.slf4j.Slf4j; |
||||
|
||||
import javax.servlet.ServletOutputStream; |
||||
import javax.servlet.http.HttpServletResponse; |
||||
import java.io.IOException; |
||||
import java.io.OutputStream; |
||||
import java.io.Serializable; |
||||
import java.nio.charset.StandardCharsets; |
||||
import java.util.UUID; |
||||
|
||||
@Slf4j |
||||
@Getter |
||||
@Setter |
||||
@ToString |
||||
public abstract class XMLBaseMsg implements Serializable { |
||||
private static final long serialVersionUID = -20251201104500001L; |
||||
|
||||
private String toUserName; |
||||
private String fromUserName; |
||||
private String msgType; |
||||
|
||||
protected XMLBaseMsg(String toUserName, String fromUserName, String msgType) { |
||||
this.toUserName = toUserName; |
||||
this.fromUserName = fromUserName; |
||||
this.msgType = msgType; |
||||
} |
||||
|
||||
/** |
||||
* 非公共部分节点数据 |
||||
*/ |
||||
public abstract String subXMLContent(); |
||||
|
||||
public String toXML() { |
||||
StringBuilder sb = new StringBuilder(); |
||||
sb.append("<xml>"); |
||||
sb.append("<ToUserName><![CDATA[").append(this.toUserName).append("]]></ToUserName>"); |
||||
sb.append("<FromUserName><![CDATA[").append(this.fromUserName).append("]]></FromUserName>"); |
||||
sb.append("<CreateTime>").append(System.currentTimeMillis() / 1000L).append("</CreateTime>"); |
||||
sb.append("<MsgType><![CDATA[").append(this.msgType).append("]]></MsgType>"); |
||||
String subXml = this.subXMLContent(); |
||||
if (subXml != null) { |
||||
sb.append(subXml); |
||||
} |
||||
sb.append("</xml>"); |
||||
return sb.toString(); |
||||
} |
||||
|
||||
|
||||
public boolean outputStreamWrite(OutputStream outputStream) { |
||||
try { |
||||
outputStream.write(this.toXML().getBytes(StandardCharsets.UTF_8)); |
||||
outputStream.flush(); |
||||
return true; |
||||
} catch (IOException e) { |
||||
log.error("", e); |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
public boolean outputStreamWrite(OutputStream outputStream, WXBizMsgCrypt bizMsgCrypt) { |
||||
if (bizMsgCrypt != null) { |
||||
try { |
||||
String outputStr = bizMsgCrypt.encryptMsg(this.toXML(), System.currentTimeMillis() + "", UUID.randomUUID().toString()); |
||||
outputStream.write(outputStr.getBytes(StandardCharsets.UTF_8)); |
||||
outputStream.flush(); |
||||
return true; |
||||
} catch (IOException | AesException e) { |
||||
log.error("", e); |
||||
return false; |
||||
} |
||||
} else { |
||||
return this.outputStreamWrite(outputStream); |
||||
} |
||||
} |
||||
|
||||
public boolean outputStreamWrite(HttpServletResponse response) { |
||||
try (ServletOutputStream outputStream = response.getOutputStream()) { |
||||
outputStream.write(this.toXML().getBytes(StandardCharsets.UTF_8)); |
||||
outputStream.flush(); |
||||
return true; |
||||
} catch (IOException e) { |
||||
log.error("", e); |
||||
return false; |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,27 @@ |
||||
package com.ynxbd.wx.wxfactory.base.passivemsg.models; |
||||
|
||||
import lombok.Getter; |
||||
import lombok.Setter; |
||||
import lombok.ToString; |
||||
import lombok.extern.slf4j.Slf4j; |
||||
|
||||
// 回复图片消息
|
||||
|
||||
@Slf4j |
||||
@Getter |
||||
@Setter |
||||
@ToString(callSuper = true) |
||||
public class XMLImageMsg extends XMLBaseMsg { |
||||
private static final long serialVersionUID = -20251201104500002L; |
||||
private String mediaId; |
||||
|
||||
public XMLImageMsg(String toUserName, String fromUserName, String mediaId) { |
||||
super(toUserName, fromUserName, "image"); |
||||
this.mediaId = mediaId; |
||||
} |
||||
|
||||
public String subXMLContent() { |
||||
return "<Image><MediaId><![CDATA[" + this.mediaId + "]]></MediaId></Image>"; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,68 @@ |
||||
package com.ynxbd.wx.wxfactory.base.passivemsg.models; |
||||
|
||||
// 回复图文消息
|
||||
|
||||
import lombok.Getter; |
||||
import lombok.NoArgsConstructor; |
||||
import lombok.Setter; |
||||
import lombok.ToString; |
||||
import lombok.extern.slf4j.Slf4j; |
||||
|
||||
import java.util.Collections; |
||||
import java.util.List; |
||||
|
||||
@Slf4j |
||||
@Getter |
||||
@Setter |
||||
@ToString(callSuper = true) |
||||
public class XMLNewsMsg extends XMLBaseMsg { |
||||
private static final long serialVersionUID = -20251201104500003L; |
||||
private List<XMLNewsMsg.Article> articles; |
||||
|
||||
public XMLNewsMsg(String toUserName, String fromUserName, List<XMLNewsMsg.Article> articles) { |
||||
super(toUserName, fromUserName, "news"); |
||||
this.articles = articles; |
||||
} |
||||
|
||||
public XMLNewsMsg(String toUserName, String fromUserName, XMLNewsMsg.Article article) { |
||||
super(toUserName, fromUserName, "news"); |
||||
this.articles = Collections.singletonList(article); |
||||
} |
||||
|
||||
@Override |
||||
public String subXMLContent() { |
||||
StringBuilder sb = new StringBuilder(); |
||||
sb.append("<ArticleCount>").append(this.articles.size()).append("</ArticleCount>"); |
||||
sb.append("<Articles>"); |
||||
|
||||
for (XMLNewsMsg.Article item : this.articles) { |
||||
sb.append("<item>"); |
||||
sb.append("<Title><![CDATA[").append(item.title == null ? "" : item.title).append("]]></Title>"); |
||||
sb.append("<Description><![CDATA[").append(item.description == null ? "" : item.description).append("]]></Description>"); |
||||
sb.append("<PicUrl><![CDATA[").append(item.picUrl == null ? "" : item.picUrl).append("]]></PicUrl>"); |
||||
sb.append("<Url><![CDATA[").append(item.url == null ? "" : item.url).append("]]></Url>"); |
||||
sb.append("</item>"); |
||||
} |
||||
sb.append("</Articles>"); |
||||
return sb.toString(); |
||||
} |
||||
|
||||
|
||||
@Getter |
||||
@Setter |
||||
@ToString |
||||
@NoArgsConstructor |
||||
public static class Article { |
||||
private String title; |
||||
private String description; |
||||
private String url; |
||||
private String picUrl; |
||||
|
||||
public Article(String title, String description, String url, String picUrl) { |
||||
this.title = title; |
||||
this.description = description; |
||||
this.url = url; |
||||
this.picUrl = picUrl; |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,29 @@ |
||||
package com.ynxbd.wx.wxfactory.base.passivemsg.models; |
||||
|
||||
import lombok.Getter; |
||||
import lombok.Setter; |
||||
import lombok.ToString; |
||||
import lombok.extern.slf4j.Slf4j; |
||||
|
||||
@Slf4j |
||||
@Getter |
||||
@Setter |
||||
@ToString(callSuper = true) |
||||
public class XMLTextMsg extends XMLBaseMsg { |
||||
private static final long serialVersionUID = -20251201104500004L; |
||||
private String content; |
||||
|
||||
public XMLTextMsg(String toUserName, String fromUserName, String content) { |
||||
super(toUserName, fromUserName, "text"); |
||||
this.content = content; |
||||
} |
||||
|
||||
public String subXML() { |
||||
return "<Content><![CDATA[" + this.content + "]]></Content>"; |
||||
} |
||||
|
||||
@Override |
||||
public String subXMLContent() { |
||||
return "<Content><![CDATA[" + this.content + "]]></Content>"; |
||||
} |
||||
} |
||||
@ -1,65 +1,67 @@ |
||||
package com.ynxbd.wx.wxfactory.utils; |
||||
|
||||
import com.ynxbd.common.result.ResultEnum; |
||||
import com.ynxbd.common.result.ServiceException; |
||||
import lombok.extern.slf4j.Slf4j; |
||||
|
||||
import java.util.HashMap; |
||||
import java.util.Map; |
||||
import java.util.UUID; |
||||
|
||||
// 医保回调
|
||||
@Slf4j |
||||
public class MdRespHelper { |
||||
/** |
||||
* 失败响应 |
||||
*/ |
||||
public static String resp(ServiceException e, String mdPayKey) { |
||||
if (ResultEnum.PAY_NOTIFY_REPEAT.equals(e.getResultEnum())) { // 重复通知
|
||||
return respOk(mdPayKey); |
||||
} |
||||
return respFail(e.getMessage(), mdPayKey); |
||||
} |
||||
|
||||
/** |
||||
* 成功响应 |
||||
*/ |
||||
public static String respOk(String mdPayKey) { |
||||
log.info("[医保]回调通知成功"); |
||||
try { |
||||
Map<String, Object> map = new HashMap<>(); |
||||
map.put("return_code", "SUCCESS"); |
||||
map.put("result_code", "SUCCESS"); |
||||
map.put("return_msg", "OK"); |
||||
map.put("nonce_str", UUID.randomUUID().toString().replace("-", "")); |
||||
map.put("sign", WxSignHelper.generateSign(map, WxSignHelper.SIGN_TYPE_MD5, mdPayKey)); |
||||
return XmlHelper.mapToXml(map); |
||||
} catch (Exception e) { |
||||
e.printStackTrace(); |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
|
||||
/** |
||||
* 成功响应 |
||||
*/ |
||||
public static String respFail(String message, String mdPayKey) { |
||||
log.info("[医保]回调通知:{}", message); |
||||
if (message == null) { |
||||
message = "FAIL"; |
||||
} |
||||
try { |
||||
Map<String, Object> map = new HashMap<>(); |
||||
map.put("return_code", "FAIL"); |
||||
map.put("result_code", "FAIL"); |
||||
map.put("return_msg", message); |
||||
map.put("nonce_str", UUID.randomUUID().toString().replace("-", "")); |
||||
map.put("sign", WxSignHelper.generateSign(map, WxSignHelper.SIGN_TYPE_MD5, mdPayKey)); |
||||
return XmlHelper.mapToXml(map); |
||||
} catch (Exception e) { |
||||
e.printStackTrace(); |
||||
} |
||||
return null; |
||||
} |
||||
} |
||||
package com.ynxbd.wx.wxfactory.medical.models; |
||||
|
||||
import com.ynxbd.common.result.ResultEnum; |
||||
import com.ynxbd.common.result.ServiceException; |
||||
import com.ynxbd.wx.wxfactory.utils.WxSignHelper; |
||||
import com.ynxbd.wx.wxfactory.utils.XmlHelper; |
||||
import lombok.extern.slf4j.Slf4j; |
||||
|
||||
import java.util.HashMap; |
||||
import java.util.Map; |
||||
import java.util.UUID; |
||||
|
||||
// 医保回调
|
||||
@Slf4j |
||||
public class MedRespHelper { |
||||
/** |
||||
* 失败响应 |
||||
*/ |
||||
public static String resp(ServiceException e, String mdPayKey) { |
||||
if (ResultEnum.PAY_NOTIFY_REPEAT.equals(e.getResultEnum())) { // 重复通知
|
||||
return respOk(mdPayKey); |
||||
} |
||||
return respFail(e.getMessage(), mdPayKey); |
||||
} |
||||
|
||||
/** |
||||
* 成功响应 |
||||
*/ |
||||
public static String respOk(String mdPayKey) { |
||||
log.info("[医保]回调通知成功"); |
||||
try { |
||||
Map<String, Object> map = new HashMap<>(); |
||||
map.put("return_code", "SUCCESS"); |
||||
map.put("result_code", "SUCCESS"); |
||||
map.put("return_msg", "OK"); |
||||
map.put("nonce_str", UUID.randomUUID().toString().replace("-", "")); |
||||
map.put("sign", WxSignHelper.generateSign(map, WxSignHelper.SIGN_TYPE_MD5, mdPayKey)); |
||||
return XmlHelper.mapToXml(map); |
||||
} catch (Exception e) { |
||||
e.printStackTrace(); |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
|
||||
/** |
||||
* 成功响应 |
||||
*/ |
||||
public static String respFail(String message, String mdPayKey) { |
||||
log.info("[医保]回调通知:{}", message); |
||||
if (message == null) { |
||||
message = "FAIL"; |
||||
} |
||||
try { |
||||
Map<String, Object> map = new HashMap<>(); |
||||
map.put("return_code", "FAIL"); |
||||
map.put("result_code", "FAIL"); |
||||
map.put("return_msg", message); |
||||
map.put("nonce_str", UUID.randomUUID().toString().replace("-", "")); |
||||
map.put("sign", WxSignHelper.generateSign(map, WxSignHelper.SIGN_TYPE_MD5, mdPayKey)); |
||||
return XmlHelper.mapToXml(map); |
||||
} catch (Exception e) { |
||||
e.printStackTrace(); |
||||
} |
||||
return null; |
||||
} |
||||
} |
||||
@ -1,54 +1,54 @@ |
||||
package com.ynxbd.wx.wxfactory.bean; |
||||
|
||||
import lombok.Getter; |
||||
import lombok.NoArgsConstructor; |
||||
import lombok.Setter; |
||||
import lombok.ToString; |
||||
|
||||
import java.math.BigDecimal; |
||||
|
||||
@Getter |
||||
@Setter |
||||
@ToString |
||||
@NoArgsConstructor |
||||
public class MedicalInfo { |
||||
private BigDecimal acctFee; // 个人账户支付(医保账户)
|
||||
private BigDecimal cashFee; // 现金
|
||||
private BigDecimal medFeeSumAmt; // 医保金额
|
||||
private BigDecimal hifpFee; // 统筹支付金额
|
||||
private BigDecimal totalFee; // 订单总金额
|
||||
|
||||
private BigDecimal insuranceFee; // 个账 + 统筹支付金额
|
||||
|
||||
private String payOrdId; // 订单号
|
||||
private String payToken; // token
|
||||
private String payAuthNo; // 授权码
|
||||
private String chrgBchno; // 收费批次(去掉M作为发票号,用于退费)-医院订单号
|
||||
private String mdUserId; // 医保支付用户id
|
||||
private String mdTrtId; // 医保就诊号
|
||||
|
||||
/** |
||||
* 医保结算状态 |
||||
* <p> |
||||
* 0 : 已保存 |
||||
* 1 : 预结算完成 |
||||
* 2 : 结算中 |
||||
* 3 : 自费完成 |
||||
* 4 : 医保支付完成 |
||||
* 5 : 院内结算完成 |
||||
* 6 : 结算完成 |
||||
* 7 : 已退款 |
||||
* 8 : 已医保全部退款 |
||||
* 9 : 仅自费全部退款 |
||||
* 10 : 仅自费部分退款 |
||||
* 11 : 医保全部退自费部分退款 |
||||
* 12 : 已撤销 |
||||
* 13 : 医保已撤销 |
||||
* 14 : 异常 |
||||
* 15 : 结算失败 |
||||
* -1 : 状态未知 |
||||
*/ |
||||
private String statusCode; |
||||
// 医保结算说明
|
||||
private String status; |
||||
} |
||||
package com.ynxbd.wx.wxfactory.medical.models; |
||||
|
||||
import lombok.Getter; |
||||
import lombok.NoArgsConstructor; |
||||
import lombok.Setter; |
||||
import lombok.ToString; |
||||
|
||||
import java.math.BigDecimal; |
||||
|
||||
@Getter |
||||
@Setter |
||||
@ToString |
||||
@NoArgsConstructor |
||||
public class MedicalInfo { |
||||
private BigDecimal acctFee; // 个人账户支付(医保账户)
|
||||
private BigDecimal cashFee; // 现金
|
||||
private BigDecimal medFeeSumAmt; // 医保金额
|
||||
private BigDecimal hifpFee; // 统筹支付金额
|
||||
private BigDecimal totalFee; // 订单总金额
|
||||
|
||||
private BigDecimal insuranceFee; // 个账 + 统筹支付金额
|
||||
|
||||
private String payOrdId; // 订单号
|
||||
private String payToken; // token
|
||||
private String payAuthNo; // 授权码
|
||||
private String chrgBchno; // 收费批次(去掉M作为发票号,用于退费)-医院订单号
|
||||
private String mdUserId; // 医保支付用户id
|
||||
private String mdTrtId; // 医保就诊号
|
||||
|
||||
/** |
||||
* 医保结算状态 |
||||
* <p> |
||||
* 0 : 已保存 |
||||
* 1 : 预结算完成 |
||||
* 2 : 结算中 |
||||
* 3 : 自费完成 |
||||
* 4 : 医保支付完成 |
||||
* 5 : 院内结算完成 |
||||
* 6 : 结算完成 |
||||
* 7 : 已退款 |
||||
* 8 : 已医保全部退款 |
||||
* 9 : 仅自费全部退款 |
||||
* 10 : 仅自费部分退款 |
||||
* 11 : 医保全部退自费部分退款 |
||||
* 12 : 已撤销 |
||||
* 13 : 医保已撤销 |
||||
* 14 : 异常 |
||||
* 15 : 结算失败 |
||||
* -1 : 状态未知 |
||||
*/ |
||||
private String statusCode; |
||||
// 医保结算说明
|
||||
private String status; |
||||
} |
||||
@ -1,74 +1,74 @@ |
||||
package com.ynxbd.wx.wxfactory.bean; |
||||
|
||||
import lombok.Getter; |
||||
import lombok.NoArgsConstructor; |
||||
import lombok.Setter; |
||||
import lombok.ToString; |
||||
|
||||
import java.math.BigDecimal; |
||||
|
||||
// 医保回调通知
|
||||
|
||||
@Setter |
||||
@Getter |
||||
@ToString |
||||
@NoArgsConstructor |
||||
public class MedicalNotify { |
||||
private String sign; |
||||
|
||||
private String openid; |
||||
|
||||
private String medTransId; |
||||
|
||||
private String hospOutTradeNo; |
||||
|
||||
// 总金额(分)
|
||||
private BigDecimal totalFee; |
||||
// 支付现金(分)
|
||||
private BigDecimal cashFee; |
||||
// 医保-个人账户(分)
|
||||
private BigDecimal insuranceFee; |
||||
//
|
||||
private String serialNo; |
||||
//
|
||||
private String info; |
||||
|
||||
private String timeEnd; |
||||
private String cashOrderId; |
||||
// 类型标识
|
||||
private String attach; |
||||
// 分
|
||||
private BigDecimal insurance_self_fee; |
||||
private BigDecimal insurance_fund_fee; |
||||
private BigDecimal insurance_other_fee; |
||||
|
||||
private String payDate; |
||||
private String payTime; |
||||
|
||||
|
||||
// "<return_code><![CDATA[SUCCESS]]></return_code>\n" +
|
||||
// "<return_msg><![CDATA[SUCCESS]]></return_msg>\n" +
|
||||
// "<appid><![CDATA[wxd503671f502bd89d]]></appid>\n" +
|
||||
// "<mch_id><![CDATA[1288583001]]></mch_id>\n" +
|
||||
// "<openid><![CDATA[oeso-t5DIu2qpb0LJaKeJ06TRgzw]]></openid>\n" +
|
||||
// "<nonce_str><![CDATA[90690d6cf32c62ea]]></nonce_str>\n" +
|
||||
// "<med_trans_id><![CDATA[M22062955949633669]]></med_trans_id>\n" +
|
||||
// "<hosp_out_trade_no><![CDATA[WXa4820ed35506439d45d9d6d0635f]]></hosp_out_trade_no>\n" +
|
||||
// "<result_code><![CDATA[SUCCESS]]></result_code>\n" +
|
||||
// "<time_end><![CDATA[20220629142511]]></time_end>\n" +
|
||||
// "<pay_type>3</pay_type>\n" +
|
||||
// "<total_fee>597</total_fee>\n" +
|
||||
// "<cash_fee>297</cash_fee>\n" +
|
||||
// "<insurance_fee>300</insurance_fee>\n" +
|
||||
// "<response_content><![CDATA[{\"directUrl\":\"http://dzpz.ynylbz.cn/test/hicashier/#payResult?encData=c785ab466a6d13d2a3f3f84c47bcc80fef109d5bc5212327aeac8687555eef618b9b77d789eebfcfa5021cd36a43a3f17d71daabe194f29565d530567c756072091e895012e6c3dda1042eac5b08e5fb98801010896c6bc3891314aa46b230a30c59aa91f8dbaf2ae3a5d1ec700154db8f7e4381a9b72e3dee8747e6811bd8fd8c90ff1411c42fa49ff871c1370ad791dc030b4c2185a515b2902c293cd2fb4661acdbc8457f5a7cc7dd801885d1a94162b72ee49f305a260894e10c7488f3325b5fcd21afa639392afb322c68101df10489b710394321e549fde466c19d4f25d70440bd496c2ba695b798ae4e90642b\",\"feeSumamt\":5.97,\"fundPay\":0.00,\"medSetlFlag\":\"SUCC\",\"ownPayAmt\":2.97,\"psnAcctPay\":3.00,\"setlSn\":\"SET530100202206291425070000006\"}]]></response_content>\n" +
|
||||
// "<medical_card_id><![CDATA[]]></medical_card_id>\n" +
|
||||
// "<serial_no><![CDATA[WXa4820ed35506439d45d9d6d0635f]]></serial_no>\n" +
|
||||
// "<attach><![CDATA[recipe=1]]></attach>\n" +
|
||||
// "<cash_order_id><![CDATA[C22062955949633669]]></cash_order_id>\n" +
|
||||
// "<insurance_order_id><![CDATA[I22062955949633669]]></insurance_order_id>\n" +
|
||||
// "<sign><![CDATA[1FA8E91777A02ADDDCE2AB02D96B20A3]]></sign>\n" +
|
||||
// "<insurance_self_fee>300</insurance_self_fee>\n" +
|
||||
// "<insurance_fund_fee>0</insurance_fund_fee>\n" +
|
||||
// "<insurance_other_fee>0</insurance_other_fee>\n" +
|
||||
|
||||
} |
||||
package com.ynxbd.wx.wxfactory.medical.models; |
||||
|
||||
import lombok.Getter; |
||||
import lombok.NoArgsConstructor; |
||||
import lombok.Setter; |
||||
import lombok.ToString; |
||||
|
||||
import java.math.BigDecimal; |
||||
|
||||
// 医保回调通知
|
||||
|
||||
@Setter |
||||
@Getter |
||||
@ToString |
||||
@NoArgsConstructor |
||||
public class MedicalNotify { |
||||
private String sign; |
||||
|
||||
private String openid; |
||||
|
||||
private String medTransId; |
||||
|
||||
private String hospOutTradeNo; |
||||
|
||||
// 总金额(分)
|
||||
private BigDecimal totalFee; |
||||
// 支付现金(分)
|
||||
private BigDecimal cashFee; |
||||
// 医保-个人账户(分)
|
||||
private BigDecimal insuranceFee; |
||||
//
|
||||
private String serialNo; |
||||
//
|
||||
private String info; |
||||
|
||||
private String timeEnd; |
||||
private String cashOrderId; |
||||
// 类型标识
|
||||
private String attach; |
||||
// 分
|
||||
private BigDecimal insurance_self_fee; |
||||
private BigDecimal insurance_fund_fee; |
||||
private BigDecimal insurance_other_fee; |
||||
|
||||
private String payDate; |
||||
private String payTime; |
||||
|
||||
|
||||
// "<return_code><![CDATA[SUCCESS]]></return_code>\n" +
|
||||
// "<return_msg><![CDATA[SUCCESS]]></return_msg>\n" +
|
||||
// "<appid><![CDATA[wxd503671f502bd89d]]></appid>\n" +
|
||||
// "<mch_id><![CDATA[1288583001]]></mch_id>\n" +
|
||||
// "<openid><![CDATA[oeso-t5DIu2qpb0LJaKeJ06TRgzw]]></openid>\n" +
|
||||
// "<nonce_str><![CDATA[90690d6cf32c62ea]]></nonce_str>\n" +
|
||||
// "<med_trans_id><![CDATA[M22062955949633669]]></med_trans_id>\n" +
|
||||
// "<hosp_out_trade_no><![CDATA[WXa4820ed35506439d45d9d6d0635f]]></hosp_out_trade_no>\n" +
|
||||
// "<result_code><![CDATA[SUCCESS]]></result_code>\n" +
|
||||
// "<time_end><![CDATA[20220629142511]]></time_end>\n" +
|
||||
// "<pay_type>3</pay_type>\n" +
|
||||
// "<total_fee>597</total_fee>\n" +
|
||||
// "<cash_fee>297</cash_fee>\n" +
|
||||
// "<insurance_fee>300</insurance_fee>\n" +
|
||||
// "<response_content><![CDATA[{\"directUrl\":\"http://dzpz.ynylbz.cn/test/hicashier/#payResult?encData=c785ab466a6d13d2a3f3f84c47bcc80fef109d5bc5212327aeac8687555eef618b9b77d789eebfcfa5021cd36a43a3f17d71daabe194f29565d530567c756072091e895012e6c3dda1042eac5b08e5fb98801010896c6bc3891314aa46b230a30c59aa91f8dbaf2ae3a5d1ec700154db8f7e4381a9b72e3dee8747e6811bd8fd8c90ff1411c42fa49ff871c1370ad791dc030b4c2185a515b2902c293cd2fb4661acdbc8457f5a7cc7dd801885d1a94162b72ee49f305a260894e10c7488f3325b5fcd21afa639392afb322c68101df10489b710394321e549fde466c19d4f25d70440bd496c2ba695b798ae4e90642b\",\"feeSumamt\":5.97,\"fundPay\":0.00,\"medSetlFlag\":\"SUCC\",\"ownPayAmt\":2.97,\"psnAcctPay\":3.00,\"setlSn\":\"SET530100202206291425070000006\"}]]></response_content>\n" +
|
||||
// "<medical_card_id><![CDATA[]]></medical_card_id>\n" +
|
||||
// "<serial_no><![CDATA[WXa4820ed35506439d45d9d6d0635f]]></serial_no>\n" +
|
||||
// "<attach><![CDATA[recipe=1]]></attach>\n" +
|
||||
// "<cash_order_id><![CDATA[C22062955949633669]]></cash_order_id>\n" +
|
||||
// "<insurance_order_id><![CDATA[I22062955949633669]]></insurance_order_id>\n" +
|
||||
// "<sign><![CDATA[1FA8E91777A02ADDDCE2AB02D96B20A3]]></sign>\n" +
|
||||
// "<insurance_self_fee>300</insurance_self_fee>\n" +
|
||||
// "<insurance_fund_fee>0</insurance_fund_fee>\n" +
|
||||
// "<insurance_other_fee>0</insurance_other_fee>\n" +
|
||||
|
||||
} |
||||
@ -1,20 +1,20 @@ |
||||
package com.ynxbd.wx.wxfactory.bean; |
||||
|
||||
import com.ynxbd.wx.wxfactory.WxFactory; |
||||
import lombok.*; |
||||
|
||||
// 医保下单返回
|
||||
|
||||
@Getter |
||||
@Setter |
||||
@ToString(callSuper = true) |
||||
@NoArgsConstructor |
||||
public class MedicalPayOrder extends WxFactory.ResponseCheck.ResultBase { |
||||
// 诊疗单id 微信生成的医疗订单id,用于后续调用接口使用
|
||||
private String medTransId; |
||||
// 支付链接 下单后跳转到此url,用户完成支付,使用app支付时1.5 APP支付说明
|
||||
private String payUrl; |
||||
// 支付小程序 当使用医院小程序进行下单时,会返回此参数,医院小程序跳转至该支付小程序进行支付
|
||||
private String payAppId; |
||||
|
||||
} |
||||
package com.ynxbd.wx.wxfactory.medical.models; |
||||
|
||||
import com.ynxbd.wx.wxfactory.WxFactory; |
||||
import lombok.*; |
||||
|
||||
// 医保下单返回
|
||||
|
||||
@Getter |
||||
@Setter |
||||
@ToString(callSuper = true) |
||||
@NoArgsConstructor |
||||
public class MedicalPayOrder extends WxFactory.ResponseCheck.ResultBase { |
||||
// 诊疗单id 微信生成的医疗订单id,用于后续调用接口使用
|
||||
private String medTransId; |
||||
// 支付链接 下单后跳转到此url,用户完成支付,使用app支付时1.5 APP支付说明
|
||||
private String payUrl; |
||||
// 支付小程序 当使用医院小程序进行下单时,会返回此参数,医院小程序跳转至该支付小程序进行支付
|
||||
private String payAppId; |
||||
|
||||
} |
||||
@ -1,21 +1,21 @@ |
||||
package com.ynxbd.wx.wxfactory.bean; |
||||
|
||||
import com.ynxbd.wx.wxfactory.WxFactory; |
||||
import lombok.Getter; |
||||
import lombok.NoArgsConstructor; |
||||
import lombok.Setter; |
||||
import lombok.ToString; |
||||
|
||||
@Setter |
||||
@Getter |
||||
@ToString(callSuper = true) |
||||
@NoArgsConstructor |
||||
public class MedicalRefund extends WxFactory.ResponseCheck.ResultBase { |
||||
private String totalFee; |
||||
private String cashRefundFee; |
||||
private String insuranceRefundFee; |
||||
private String medRefundId; |
||||
private String cashRefundId; |
||||
private String insuranceRefundId; |
||||
private String sign; |
||||
} |
||||
package com.ynxbd.wx.wxfactory.medical.models; |
||||
|
||||
import com.ynxbd.wx.wxfactory.WxFactory; |
||||
import lombok.Getter; |
||||
import lombok.NoArgsConstructor; |
||||
import lombok.Setter; |
||||
import lombok.ToString; |
||||
|
||||
@Setter |
||||
@Getter |
||||
@ToString(callSuper = true) |
||||
@NoArgsConstructor |
||||
public class MedicalRefund extends WxFactory.ResponseCheck.ResultBase { |
||||
private String totalFee; |
||||
private String cashRefundFee; |
||||
private String insuranceRefundFee; |
||||
private String medRefundId; |
||||
private String cashRefundId; |
||||
private String insuranceRefundId; |
||||
private String sign; |
||||
} |
||||
@ -1,30 +1,30 @@ |
||||
package com.ynxbd.wx.wxfactory.bean; |
||||
|
||||
import com.ynxbd.wx.wxfactory.WxFactory; |
||||
import lombok.Getter; |
||||
import lombok.NoArgsConstructor; |
||||
import lombok.Setter; |
||||
import lombok.ToString; |
||||
|
||||
@Setter |
||||
@Getter |
||||
@ToString(callSuper = true) |
||||
@NoArgsConstructor |
||||
public class MedicalRefundInfo extends WxFactory.ResponseCheck.ResultBase { |
||||
private String appid; |
||||
private String mchId; |
||||
private String nonce_str; |
||||
private String med_trans_id; |
||||
private String med_refund_id; |
||||
private String hosp_out_trade_no; |
||||
private String hosp_out_refund_no; |
||||
private String insurance_refund_fee; |
||||
private String cash_refund_fee; |
||||
private String cash_refund_stauts; |
||||
private String insurance_refund_status; |
||||
private String med_refund_state; |
||||
private String refund_end_time; |
||||
private String response_content; |
||||
private String cash_refund_id; |
||||
private String insurance_refund_id; |
||||
} |
||||
package com.ynxbd.wx.wxfactory.medical.models; |
||||
|
||||
import com.ynxbd.wx.wxfactory.WxFactory; |
||||
import lombok.Getter; |
||||
import lombok.NoArgsConstructor; |
||||
import lombok.Setter; |
||||
import lombok.ToString; |
||||
|
||||
@Setter |
||||
@Getter |
||||
@ToString(callSuper = true) |
||||
@NoArgsConstructor |
||||
public class MedicalRefundInfo extends WxFactory.ResponseCheck.ResultBase { |
||||
private String appid; |
||||
private String mchId; |
||||
private String nonce_str; |
||||
private String med_trans_id; |
||||
private String med_refund_id; |
||||
private String hosp_out_trade_no; |
||||
private String hosp_out_refund_no; |
||||
private String insurance_refund_fee; |
||||
private String cash_refund_fee; |
||||
private String cash_refund_stauts; |
||||
private String insurance_refund_status; |
||||
private String med_refund_state; |
||||
private String refund_end_time; |
||||
private String response_content; |
||||
private String cash_refund_id; |
||||
private String insurance_refund_id; |
||||
} |
||||
@ -1,28 +1,28 @@ |
||||
package com.ynxbd.wx.wxfactory.bean; |
||||
|
||||
import lombok.Getter; |
||||
import lombok.NoArgsConstructor; |
||||
import lombok.Setter; |
||||
import lombok.ToString; |
||||
|
||||
@Getter |
||||
@Setter |
||||
@ToString |
||||
@NoArgsConstructor |
||||
public class MedicalUserInfo { |
||||
// 姓名
|
||||
private String userName; |
||||
// 授权码
|
||||
private String payAuthNo; |
||||
// 坐标
|
||||
private String longitude; |
||||
|
||||
private String latitude; |
||||
|
||||
private String cardNo; |
||||
|
||||
private boolean success; |
||||
|
||||
private String message; |
||||
|
||||
} |
||||
package com.ynxbd.wx.wxfactory.medical.models; |
||||
|
||||
import lombok.Getter; |
||||
import lombok.NoArgsConstructor; |
||||
import lombok.Setter; |
||||
import lombok.ToString; |
||||
|
||||
@Getter |
||||
@Setter |
||||
@ToString |
||||
@NoArgsConstructor |
||||
public class MedicalUserInfo { |
||||
// 姓名
|
||||
private String userName; |
||||
// 授权码
|
||||
private String payAuthNo; |
||||
// 坐标
|
||||
private String longitude; |
||||
|
||||
private String latitude; |
||||
|
||||
private String cardNo; |
||||
|
||||
private boolean success; |
||||
|
||||
private String message; |
||||
|
||||
} |
||||
@ -1,4 +1,4 @@ |
||||
package com.ynxbd.wx.wxfactory.bean; |
||||
package com.ynxbd.wx.wxfactory.medical.models; |
||||
|
||||
import com.ynxbd.wx.wxfactory.WxFactory; |
||||
import lombok.Getter; |
||||
@ -1,151 +1,90 @@ |
||||
package com.ynxbd.wx.wxfactory.menu.bean; |
||||
|
||||
public class WxMsgConst { |
||||
public static final String XML_MSG_TEXT = "text"; |
||||
public static final String XML_MSG_IMAGE = "image"; |
||||
public static final String XML_MSG_VOICE = "voice"; |
||||
public static final String XML_MSG_VIDEO = "video"; |
||||
public static final String XML_MSG_NEWS = "news"; |
||||
public static final String XML_MSG_MUSIC = "music"; |
||||
public static final String XML_MSG_LOCATION = "location"; |
||||
public static final String XML_MSG_LINK = "link"; |
||||
public static final String XML_MSG_EVENT = "event"; |
||||
public static final String XML_TRANSFER_CUSTOMER_SERVICE = "transfer_customer_service"; |
||||
public static final String CUSTOM_MSG_TEXT = "text"; |
||||
public static final String CUSTOM_MSG_IMAGE = "image"; |
||||
public static final String CUSTOM_MSG_VOICE = "voice"; |
||||
public static final String CUSTOM_MSG_VIDEO = "video"; |
||||
public static final String CUSTOM_MSG_MUSIC = "music"; |
||||
public static final String CUSTOM_MSG_NEWS = "news"; |
||||
public static final String CUSTOM_MSG_FILE = "file"; |
||||
public static final String CUSTOM_MSG_TRANSFER_CUSTOMER_SERVICE = "transfer_customer_service"; |
||||
public static final String CUSTOM_MSG_SAFE_NO = "0"; |
||||
public static final String CUSTOM_MSG_SAFE_YES = "1"; |
||||
public static final String MASS_MSG_MPNEWS = "mpnews"; |
||||
public static final String MASS_MSG_NEWS = "news"; |
||||
public static final String MASS_MSG_TEXT = "text"; |
||||
public static final String MASS_MSG_VOICE = "voice"; |
||||
public static final String MASS_MSG_IMAGE = "image"; |
||||
public static final String MASS_MSG_MPVIDEO = "mpvideo"; |
||||
public static final String MASS_MSG_VIDEO = "video"; |
||||
public static final String MASS_MSG_MUSIC = "music"; |
||||
public static final String EVT_SUBSCRIBE = "subscribe"; |
||||
public static final String EVT_UNSUBSCRIBE = "unsubscribe"; |
||||
public static final String EVT_SCAN = "SCAN"; |
||||
public static final String EVT_LOCATION = "LOCATION"; |
||||
public static final String EVT_CLICK = "CLICK"; |
||||
public static final String EVT_VIEW = "VIEW"; |
||||
public static final String EVT_MASS_SEND_JOB_FINISH = "MASSSENDJOBFINISH"; |
||||
public static final String EVT_SCANCODE_PUSH = "scancode_push"; |
||||
public static final String EVT_SCANCODE_WAITMSG = "scancode_waitmsg"; |
||||
public static final String EVT_PIC_SYSPHOTO = "pic_sysphoto"; |
||||
public static final String EVT_PIC_PHOTO_OR_ALBUM = "pic_photo_or_album"; |
||||
public static final String EVT_PIC_WEIXIN = "pic_weixin"; |
||||
public static final String EVT_LOCATION_SELECT = "location_select"; |
||||
public static final String EVT_TEMPLATESENDJOBFINISH = "TEMPLATESENDJOBFINISH"; |
||||
public static final String EVT_ENTER_AGENT = "enter_agent"; |
||||
public static final String EVT_QUALIFICATION_VERIFY_SUCCESS = "qualification_verify_success"; |
||||
public static final String EVT_QUALIFICATION_VERIFY_FAIL = "qualification_verify_fail"; |
||||
public static final String EVT_NAMING_VERIFY_SUCCESS = "naming_verify_success"; |
||||
public static final String EVT_NAMING_VERIFY_FAIL = "naming_verify_fail"; |
||||
public static final String EVT_ANNUAL_RENEW = "annual_renew"; |
||||
public static final String EVT_VERIFY_EXPIRED = "verify_expired"; |
||||
public static final String MEDIA_IMAGE = "image"; |
||||
public static final String MEDIA_VOICE = "voice"; |
||||
public static final String MEDIA_VIDEO = "video"; |
||||
public static final String MEDIA_THUMB = "thumb"; |
||||
public static final String MEDIA_FILE = "file"; |
||||
public static final String FILE_JPG = "jpeg"; |
||||
public static final String FILE_MP3 = "mp3"; |
||||
public static final String FILE_AMR = "amr"; |
||||
public static final String FILE_MP4 = "mp4"; |
||||
// public static final String XML_MSG_TEXT = "text";
|
||||
// public static final String XML_MSG_IMAGE = "image";
|
||||
// public static final String XML_MSG_VOICE = "voice";
|
||||
// public static final String XML_MSG_VIDEO = "video";
|
||||
// public static final String XML_MSG_NEWS = "news";
|
||||
// public static final String XML_MSG_MUSIC = "music";
|
||||
// public static final String XML_MSG_LOCATION = "location";
|
||||
// public static final String XML_MSG_LINK = "link";
|
||||
// public static final String XML_MSG_EVENT = "event";
|
||||
// public static final String XML_TRANSFER_CUSTOMER_SERVICE = "transfer_customer_service";
|
||||
// public static final String CUSTOM_MSG_TEXT = "text";
|
||||
// public static final String CUSTOM_MSG_IMAGE = "image";
|
||||
// public static final String CUSTOM_MSG_VOICE = "voice";
|
||||
// public static final String CUSTOM_MSG_VIDEO = "video";
|
||||
// public static final String CUSTOM_MSG_MUSIC = "music";
|
||||
// public static final String CUSTOM_MSG_NEWS = "news";
|
||||
// public static final String CUSTOM_MSG_FILE = "file";
|
||||
// public static final String CUSTOM_MSG_TRANSFER_CUSTOMER_SERVICE = "transfer_customer_service";
|
||||
// public static final String CUSTOM_MSG_SAFE_NO = "0";
|
||||
// public static final String CUSTOM_MSG_SAFE_YES = "1";
|
||||
// public static final String MASS_MSG_MPNEWS = "mpnews";
|
||||
// public static final String MASS_MSG_NEWS = "news";
|
||||
// public static final String MASS_MSG_TEXT = "text";
|
||||
// public static final String MASS_MSG_VOICE = "voice";
|
||||
// public static final String MASS_MSG_IMAGE = "image";
|
||||
// public static final String MASS_MSG_MPVIDEO = "mpvideo";
|
||||
// public static final String MASS_MSG_VIDEO = "video";
|
||||
// public static final String MASS_MSG_MUSIC = "music";
|
||||
// public static final String EVT_SUBSCRIBE = "subscribe";
|
||||
// public static final String EVT_UNSUBSCRIBE = "unsubscribe";
|
||||
// public static final String EVT_SCAN = "SCAN";
|
||||
// public static final String EVT_LOCATION = "LOCATION";
|
||||
// public static final String EVT_CLICK = "CLICK";
|
||||
// public static final String EVT_VIEW = "VIEW";
|
||||
// public static final String EVT_MASS_SEND_JOB_FINISH = "MASSSENDJOBFINISH";
|
||||
// public static final String EVT_SCANCODE_PUSH = "scancode_push";
|
||||
// public static final String EVT_SCANCODE_WAITMSG = "scancode_waitmsg";
|
||||
// public static final String EVT_PIC_SYSPHOTO = "pic_sysphoto";
|
||||
// public static final String EVT_PIC_PHOTO_OR_ALBUM = "pic_photo_or_album";
|
||||
// public static final String EVT_PIC_WEIXIN = "pic_weixin";
|
||||
// public static final String EVT_LOCATION_SELECT = "location_select";
|
||||
// public static final String EVT_TEMPLATESENDJOBFINISH = "TEMPLATESENDJOBFINISH";
|
||||
// public static final String EVT_ENTER_AGENT = "enter_agent";
|
||||
// public static final String EVT_QUALIFICATION_VERIFY_SUCCESS = "qualification_verify_success";
|
||||
// public static final String EVT_QUALIFICATION_VERIFY_FAIL = "qualification_verify_fail";
|
||||
// public static final String EVT_NAMING_VERIFY_SUCCESS = "naming_verify_success";
|
||||
// public static final String EVT_NAMING_VERIFY_FAIL = "naming_verify_fail";
|
||||
// public static final String EVT_ANNUAL_RENEW = "annual_renew";
|
||||
// public static final String EVT_VERIFY_EXPIRED = "verify_expired";
|
||||
// public static final String MEDIA_IMAGE = "image";
|
||||
// public static final String MEDIA_VOICE = "voice";
|
||||
// public static final String MEDIA_VIDEO = "video";
|
||||
// public static final String MEDIA_THUMB = "thumb";
|
||||
// public static final String MEDIA_FILE = "file";
|
||||
// public static final String FILE_JPG = "jpeg";
|
||||
// public static final String FILE_MP3 = "mp3";
|
||||
// public static final String FILE_AMR = "amr";
|
||||
// public static final String FILE_MP4 = "mp4";
|
||||
public static final String MENU_BUTTON_CLICK = "click"; |
||||
public static final String MENU_BUTTON_VIEW = "view"; |
||||
public static final String MENU_SCANCODE_PUSH = "scancode_push"; |
||||
public static final String MENU_SCANCODE_WAITMSG = "scancode_waitmsg"; |
||||
public static final String MENU_PIC_SYSPHOTO = "pic_sysphoto"; |
||||
public static final String MENU_PIC_PHOTO_OR_ALBUM = "pic_photo_or_album"; |
||||
public static final String MENU_PIC_WEIXIN = "pic_weixin"; |
||||
public static final String MENU_LOCATION_SELECT = "location_select"; |
||||
public static final String MENU_MEDIA_ID = "media_id"; |
||||
public static final String MENU_VIEW_LIMITED = "view_limited"; |
||||
public static final String QR_CODE_LIMIT_SCENE = "QR_LIMIT_SCENE"; |
||||
public static final String QR_CODE_LIMIT_STR_SCENE = "QR_LIMIT_STR_SCENE"; |
||||
public static final String OAUTH2_SCOPE_BASE = "snsapi_base"; |
||||
public static final String OAUTH2_SCOPE_USER_INFO = "snsapi_userinfo"; |
||||
public static final String LANG_CHINA = "zh_CN"; |
||||
public static final String LANG_CHINA_TAIWAN = "zh_TW"; |
||||
public static final String LANG_ENGLISH = "en"; |
||||
public static final String MATERIAL_NEWS = "news"; |
||||
public static final String MATERIAL_VOICE = "voice"; |
||||
public static final String MATERIAL_IMAGE = "image"; |
||||
public static final String MATERIAL_VIDEO = "video"; |
||||
public static final String SEND_ALL_NEWS = "mpnews"; |
||||
public static final String SEND_ALL_TEXT = "text"; |
||||
public static final String SEND_ALL_VOICE = "voice"; |
||||
public static final String SEND_ALL_IMAGE = "image"; |
||||
public static final String SEND_ALL_VIDEO = "mpvideo"; |
||||
public static final String URL_GET_ACCESSTOEKN = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET"; |
||||
public static final String URL_GET_WX_SERVICE_IP = "https://api.weixin.qq.com/cgi-bin/getcallbackip?access_token=ACCESS_TOKEN"; |
||||
public static final String URL_CREATE_MENU = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN"; |
||||
public static final String URL_CREATE_MENU_CONDITIONAL = "https://api.weixin.qq.com/cgi-bin/menu/addconditional?access_token=ACCESS_TOKEN"; |
||||
public static final String URL_DELETE_MENU = "https://api.weixin.qq.com/cgi-bin/menu/delete?access_token=ACCESS_TOKEN"; |
||||
public static final String URL_DELETE_MENU_CONDITIONAL = "https://api.weixin.qq.com/cgi-bin/menu/delconditional?access_token=ACCESS_TOKEN"; |
||||
public static final String URL_GET_MENU = "https://api.weixin.qq.com/cgi-bin/menu/get?access_token=ACCESS_TOKEN"; |
||||
public static final String URL_GET_CURRENT_MENU_INFO = "https://api.weixin.qq.com/cgi-bin/get_current_selfmenu_info?access_token=ACCESS_TOKEN"; |
||||
public static final String URL_TRYMATCH_MENU = "https://api.weixin.qq.com/cgi-bin/menu/trymatch?access_token=ACCESS_TOKEN"; |
||||
public static final String URL_UPLOAD_TEMP_MEDIA = "https://api.weixin.qq.com/cgi-bin/media/upload?access_token=ACCESS_TOKEN&type=TYPE"; |
||||
public static final String URL_DOWNLOAD_TEMP_MEDIA = "https://api.weixin.qq.com/cgi-bin/media/get?access_token=ACCESS_TOKEN&media_id=MEDIA_ID"; |
||||
public static final String URL_UPLOAD_MATERIAL_MEDIA = "https://api.weixin.qq.com/cgi-bin/material/add_material?access_token=ACCESS_TOKEN"; |
||||
public static final String URL_BATCHGET_MATERIAL_MEDIA_LIST = "https://api.weixin.qq.com/cgi-bin/material/batchget_material?access_token=ACCESS_TOKEN"; |
||||
public static final String URL_DOWNLOAD_MATERIAL_MEDIA = "https://api.weixin.qq.com/cgi-bin/material/get_material?access_token=ACCESS_TOKEN"; |
||||
public static final String URL_DELETE_MATERIAL_MEDIA = "https://api.weixin.qq.com/cgi-bin/material/del_material?access_token=ACCESS_TOKEN"; |
||||
public static final String URL_GET_MATERIAL_COUNT = "https://api.weixin.qq.com/cgi-bin/material/get_materialcount?access_token=ACCESS_TOKEN"; |
||||
public static final String URL_ADD_NEWS_MEDIA = "https://api.weixin.qq.com/cgi-bin/material/add_news?access_token=ACCESS_TOKEN"; |
||||
public static final String URL_IMAGE_DOMAIN_CHANGE = "https://api.weixin.qq.com/cgi-bin/media/uploadimg?access_token=ACCESS_TOKEN"; |
||||
public static final String URL_UPDATE_NEWS_MEDIA = "https://api.weixin.qq.com/cgi-bin/material/update_news?access_token=ACCESS_TOKEN"; |
||||
public static final String URL_CREATE_USER_TAG = "https://api.weixin.qq.com/cgi-bin/tags/create?access_token=ACCESS_TOKEN"; |
||||
public static final String URL_DELETE_USER_TAG = "https://api.weixin.qq.com/cgi-bin/tags/delete?access_token=ACCESS_TOKEN"; |
||||
public static final String URL_QUERY_ALL_USER_TAG = "https://api.weixin.qq.com/cgi-bin/tags/get?access_token=ACCESS_TOKEN"; |
||||
public static final String URL_QUERY_ALL_USER_UNDER_TAG = "https://api.weixin.qq.com/cgi-bin/user/tag/get?access_token=ACCESS_TOKEN"; |
||||
public static final String URL_UPDATE_USER_TAG_NAME = "https://api.weixin.qq.com/cgi-bin/tags/update?access_token=ACCESS_TOKEN"; |
||||
public static final String URL_BATCH_MOVING_USER_TAG = "https://api.weixin.qq.com/cgi-bin/tags/members/batchtagging?access_token=ACCESS_TOKEN"; |
||||
public static final String URL_BATCH_UN_TAG_USER_TAG = "https://api.weixin.qq.com/cgi-bin/tags/members/batchuntagging?access_token=ACCESS_TOKEN"; |
||||
public static final String URL_UPDATE_USER_REMARK = "https://api.weixin.qq.com/cgi-bin/user/info/updateremark?access_token=ACCESS_TOKEN"; |
||||
public static final String URL_GET_USER_INFO = "https://api.weixin.qq.com/cgi-bin/user/info?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN"; |
||||
public static final String URL_BATCH_GET_USER_INFO = "https://api.weixin.qq.com/cgi-bin/user/info/batchget?access_token=ACCESS_TOKEN"; |
||||
public static final String URL_BATCH_GET_USER_OPENID = "https://api.weixin.qq.com/cgi-bin/user/get?access_token=ACCESS_TOKEN&next_openid=NEXT_OPENID"; |
||||
public static final String URL_OAUTH2_GET_CODE = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect"; |
||||
public static final String URL_OAUTH2_GET_ACCESSTOKEN = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code"; |
||||
public static final String URL_OAUTH2_GET_REFRESH_ACCESSTOKEN = "https://api.weixin.qq.com/sns/oauth2/refresh_token?appid=APPID&grant_type=refresh_token&refresh_token=REFRESH_TOKEN"; |
||||
public static final String URL_OAUTH2_GET_USER_INFO = "https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN"; |
||||
public static final String URL_OAUTH2_CHECK_ACCESSTOKEN = "https://api.weixin.qq.com/sns/auth?access_token=ACCESS_TOKEN&openid=OPENID"; |
||||
public static final String URL_BATCH_ADD_USER_TO_BLACK_LISE = "https://api.weixin.qq.com/cgi-bin/tags/members/batchblacklist?access_token=ACCESS_TOKEN"; |
||||
public static final String URL_BATCH_REMOVE_USER_FROM_BLACK_LISE = "https://api.weixin.qq.com/cgi-bin/tags/members/batchunblacklist?access_token=ACCESS_TOKEN"; |
||||
public static final String URL_BATCH_GET_USERS_FROM_BLACK_LISE = "https://api.weixin.qq.com/cgi-bin/tags/members/getblacklist?access_token=ACCESS_TOKEN"; |
||||
public static final String URL_GET_QR_CODE = "https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=TOKEN"; |
||||
public static final String URL_DOWNLOAD_QR_CODE = "https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=TICKET"; |
||||
public static final String URL_LONGURL_TO_SHORTURL = "https://api.weixin.qq.com/cgi-bin/shorturl?access_token=ACCESS_TOKEN"; |
||||
public static final String URL_GET_JS_API_TICKET = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi"; |
||||
public static final String URL_TAG_SEND_ALL = "https://api.weixin.qq.com/cgi-bin/message/mass/sendall?access_token=ACCESS_TOKEN"; |
||||
public static final String URL_OPENID_SEND_ALL = "https://api.weixin.qq.com/cgi-bin/message/mass/send?access_token=ACCESS_TOKEN"; |
||||
public static final String URL_DELETE_SEND_ALL = "https://api.weixin.qq.com/cgi-bin/message/mass/delete?access_token=ACCESS_TOKEN"; |
||||
public static final String URL_PREVIEW_SEND_ALL = "https://api.weixin.qq.com/cgi-bin/message/mass/preview?access_token=ACCESS_TOKEN"; |
||||
public static final String URL_GET_STATUS_SEND_ALL = "https://api.weixin.qq.com/cgi-bin/message/mass/get?access_token=ACCESS_TOKEN"; |
||||
public static final String URL_PAY_UNIFIEORDER = "https://api.mch.weixin.qq.com/pay/unifiedorder"; |
||||
public static final String URL_TEMPLATE_SET_INDUSTRY = "https://api.weixin.qq.com/cgi-bin/template/api_set_industry?access_token=ACCESS_TOKEN"; |
||||
public static final String URL_TEMPLATE_GET_INDUSTRY = "https://api.weixin.qq.com/cgi-bin/template/get_industry?access_token=ACCESS_TOKEN"; |
||||
public static final String URL_TEMPLATE_GET_ID = "https://api.weixin.qq.com/cgi-bin/template/api_add_template?access_token=ACCESS_TOKEN"; |
||||
public static final String URL_TEMPLATE_GET_LIST = "https://api.weixin.qq.com/cgi-bin/template/get_all_private_template?access_token=ACCESS_TOKEN"; |
||||
public static final String URL_TEMPLATE_DELETE = "https://api.weixin.qq.com/cgi-bin/template/del_private_template?access_token=ACCESS_TOKEN"; |
||||
public static final String URL_TEMPLATE_SEND = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=ACCESS_TOKEN"; |
||||
public static final String URL_ADD_KF_ACCOUNT = "https://api.weixin.qq.com/customservice/kfaccount/add?access_token=ACCESS_TOKEN"; |
||||
public static final String URL_UPDATE_KF_ACCOUNT = "https://api.weixin.qq.com/customservice/kfaccount/update?access_token=ACCESS_TOKEN"; |
||||
public static final String URL_DELETE_KF_ACCOUNT = "https://api.weixin.qq.com/customservice/kfaccount/del?access_token=ACCESS_TOKEN"; |
||||
public static final String URL_UPDATE_KF_HEAD_IMAGE = "http://api.weixin.qq.com/customservice/kfaccount/uploadheadimg?access_token=ACCESS_TOKEN&kf_account=KFACCOUNT"; |
||||
public static final String URL_GET_ALL_KF_ACCOUNT = "https://api.weixin.qq.com/cgi-bin/customservice/getkflist?access_token=ACCESS_TOKEN"; |
||||
public static final String URL_KF_SEND_MESSAGE_TO_USER = "https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=ACCESS_TOKEN"; |
||||
public static final String URL_KF_SEND_TYPING_TO_USER = "https://api.weixin.qq.com/cgi-bin/message/custom/typing?access_token=ACCESS_TOKEN"; |
||||
// public static final String MENU_SCANCODE_PUSH = "scancode_push";
|
||||
// public static final String MENU_SCANCODE_WAITMSG = "scancode_waitmsg";
|
||||
// public static final String MENU_PIC_SYSPHOTO = "pic_sysphoto";
|
||||
// public static final String MENU_PIC_PHOTO_OR_ALBUM = "pic_photo_or_album";
|
||||
// public static final String MENU_PIC_WEIXIN = "pic_weixin";
|
||||
// public static final String MENU_LOCATION_SELECT = "location_select";
|
||||
// public static final String MENU_MEDIA_ID = "media_id";
|
||||
// public static final String MENU_VIEW_LIMITED = "view_limited";
|
||||
// public static final String QR_CODE_LIMIT_SCENE = "QR_LIMIT_SCENE";
|
||||
// public static final String QR_CODE_LIMIT_STR_SCENE = "QR_LIMIT_STR_SCENE";
|
||||
// public static final String OAUTH2_SCOPE_BASE = "snsapi_base";
|
||||
// public static final String OAUTH2_SCOPE_USER_INFO = "snsapi_userinfo";
|
||||
// public static final String LANG_CHINA = "zh_CN";
|
||||
// public static final String LANG_CHINA_TAIWAN = "zh_TW";
|
||||
// public static final String LANG_ENGLISH = "en";
|
||||
// public static final String MATERIAL_NEWS = "news";
|
||||
// public static final String MATERIAL_VOICE = "voice";
|
||||
// public static final String MATERIAL_IMAGE = "image";
|
||||
// public static final String MATERIAL_VIDEO = "video";
|
||||
// public static final String SEND_ALL_NEWS = "mpnews";
|
||||
// public static final String SEND_ALL_TEXT = "text";
|
||||
// public static final String SEND_ALL_VOICE = "voice";
|
||||
// public static final String SEND_ALL_IMAGE = "image";
|
||||
// public static final String SEND_ALL_VIDEO = "mpvideo";
|
||||
|
||||
|
||||
} |
||||
|
||||
@ -1,4 +1,4 @@ |
||||
package com.ynxbd.wx.wxfactory.bean; |
||||
package com.ynxbd.wx.wxfactory.payment; |
||||
|
||||
import lombok.ToString; |
||||
|
||||
@ -0,0 +1,912 @@ |
||||
package com.ynxbd.wx.wxfactory.payment; |
||||
|
||||
import com.google.gson.ExclusionStrategy; |
||||
import com.google.gson.FieldAttributes; |
||||
import com.google.gson.Gson; |
||||
import com.google.gson.GsonBuilder; |
||||
import com.google.gson.JsonElement; |
||||
import com.google.gson.JsonObject; |
||||
import com.google.gson.JsonSyntaxException; |
||||
import com.google.gson.annotations.Expose; |
||||
import com.google.gson.annotations.SerializedName; |
||||
|
||||
import java.util.List; |
||||
import java.util.Map.Entry; |
||||
|
||||
import okhttp3.Headers; |
||||
import okhttp3.Response; |
||||
import okio.BufferedSource; |
||||
|
||||
import javax.crypto.BadPaddingException; |
||||
import javax.crypto.Cipher; |
||||
import javax.crypto.IllegalBlockSizeException; |
||||
import javax.crypto.NoSuchPaddingException; |
||||
import javax.crypto.spec.GCMParameterSpec; |
||||
import javax.crypto.spec.SecretKeySpec; |
||||
import java.io.IOException; |
||||
import java.io.UncheckedIOException; |
||||
import java.io.UnsupportedEncodingException; |
||||
import java.net.URLEncoder; |
||||
import java.nio.charset.StandardCharsets; |
||||
import java.nio.file.Files; |
||||
import java.nio.file.Paths; |
||||
import java.security.InvalidAlgorithmParameterException; |
||||
import java.security.InvalidKeyException; |
||||
import java.security.KeyFactory; |
||||
import java.security.NoSuchAlgorithmException; |
||||
import java.security.PrivateKey; |
||||
import java.security.PublicKey; |
||||
import java.security.SecureRandom; |
||||
import java.security.Signature; |
||||
import java.security.SignatureException; |
||||
import java.security.spec.InvalidKeySpecException; |
||||
import java.security.spec.PKCS8EncodedKeySpec; |
||||
import java.security.spec.X509EncodedKeySpec; |
||||
import java.time.DateTimeException; |
||||
import java.time.Duration; |
||||
import java.time.Instant; |
||||
import java.util.Base64; |
||||
import java.util.HashMap; |
||||
import java.util.Map; |
||||
import java.util.Objects; |
||||
import java.security.MessageDigest; |
||||
import java.io.InputStream; |
||||
|
||||
import org.bouncycastle.crypto.digests.SM3Digest; |
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider; |
||||
|
||||
import java.security.Security; |
||||
|
||||
// 微信支付官方工具类
|
||||
|
||||
public class WXPayUtility { |
||||
|
||||
private static final Gson gson = new GsonBuilder() |
||||
.disableHtmlEscaping() |
||||
.addSerializationExclusionStrategy(new ExclusionStrategy() { |
||||
@Override |
||||
public boolean shouldSkipField(FieldAttributes fieldAttributes) { |
||||
final Expose expose = fieldAttributes.getAnnotation(Expose.class); |
||||
return expose != null && !expose.serialize(); |
||||
} |
||||
|
||||
@Override |
||||
public boolean shouldSkipClass(Class<?> aClass) { |
||||
return false; |
||||
} |
||||
}) |
||||
.addDeserializationExclusionStrategy(new ExclusionStrategy() { |
||||
@Override |
||||
public boolean shouldSkipField(FieldAttributes fieldAttributes) { |
||||
final Expose expose = fieldAttributes.getAnnotation(Expose.class); |
||||
return expose != null && !expose.deserialize(); |
||||
} |
||||
|
||||
@Override |
||||
public boolean shouldSkipClass(Class<?> aClass) { |
||||
return false; |
||||
} |
||||
}) |
||||
.create(); |
||||
private static final char[] SYMBOLS = |
||||
"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray(); |
||||
private static final SecureRandom random = new SecureRandom(); |
||||
|
||||
/** |
||||
* 将 Object 转换为 JSON 字符串 |
||||
*/ |
||||
public static String toJson(Object object) { |
||||
return gson.toJson(object); |
||||
} |
||||
|
||||
/** |
||||
* 将 JSON 字符串解析为特定类型的实例 |
||||
*/ |
||||
public static <T> T fromJson(String json, Class<T> classOfT) throws JsonSyntaxException { |
||||
return gson.fromJson(json, classOfT); |
||||
} |
||||
|
||||
/** |
||||
* 从公私钥文件路径中读取文件内容 |
||||
* |
||||
* @param keyPath 文件路径 |
||||
* @return 文件内容 |
||||
*/ |
||||
private static String readKeyStringFromPath(String keyPath) { |
||||
try { |
||||
return new String(Files.readAllBytes(Paths.get(keyPath)), StandardCharsets.UTF_8); |
||||
} catch (IOException e) { |
||||
throw new UncheckedIOException(e); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 读取 PKCS#8 格式的私钥字符串并加载为私钥对象 |
||||
* |
||||
* @param keyString 私钥文件内容,以 -----BEGIN PRIVATE KEY----- 开头 |
||||
* @return PrivateKey 对象 |
||||
*/ |
||||
public static PrivateKey loadPrivateKeyFromString(String keyString) { |
||||
try { |
||||
keyString = keyString.replace("-----BEGIN PRIVATE KEY-----", "") |
||||
.replace("-----END PRIVATE KEY-----", "") |
||||
.replaceAll("\\s+", ""); |
||||
return KeyFactory.getInstance("RSA").generatePrivate( |
||||
new PKCS8EncodedKeySpec(Base64.getDecoder().decode(keyString))); |
||||
} catch (NoSuchAlgorithmException e) { |
||||
throw new UnsupportedOperationException(e); |
||||
} catch (InvalidKeySpecException e) { |
||||
throw new IllegalArgumentException(e); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 从 PKCS#8 格式的私钥文件中加载私钥 |
||||
* |
||||
* @param keyPath 私钥文件路径 |
||||
* @return PrivateKey 对象 |
||||
*/ |
||||
public static PrivateKey loadPrivateKeyFromPath(String keyPath) { |
||||
return loadPrivateKeyFromString(readKeyStringFromPath(keyPath)); |
||||
} |
||||
|
||||
/** |
||||
* 读取 PKCS#8 格式的公钥字符串并加载为公钥对象 |
||||
* |
||||
* @param keyString 公钥文件内容,以 -----BEGIN PUBLIC KEY----- 开头 |
||||
* @return PublicKey 对象 |
||||
*/ |
||||
public static PublicKey loadPublicKeyFromString(String keyString) { |
||||
try { |
||||
keyString = keyString.replace("-----BEGIN PUBLIC KEY-----", "") |
||||
.replace("-----END PUBLIC KEY-----", "") |
||||
.replaceAll("\\s+", ""); |
||||
return KeyFactory.getInstance("RSA").generatePublic( |
||||
new X509EncodedKeySpec(Base64.getDecoder().decode(keyString))); |
||||
} catch (NoSuchAlgorithmException e) { |
||||
throw new UnsupportedOperationException(e); |
||||
} catch (InvalidKeySpecException e) { |
||||
throw new IllegalArgumentException(e); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 从 PKCS#8 格式的公钥文件中加载公钥 |
||||
* |
||||
* @param keyPath 公钥文件路径 |
||||
* @return PublicKey 对象 |
||||
*/ |
||||
public static PublicKey loadPublicKeyFromPath(String keyPath) { |
||||
return loadPublicKeyFromString(readKeyStringFromPath(keyPath)); |
||||
} |
||||
|
||||
/** |
||||
* 创建指定长度的随机字符串,字符集为[0-9a-zA-Z],可用于安全相关用途 |
||||
*/ |
||||
public static String createNonce(int length) { |
||||
char[] buf = new char[length]; |
||||
for (int i = 0; i < length; ++i) { |
||||
buf[i] = SYMBOLS[random.nextInt(SYMBOLS.length)]; |
||||
} |
||||
return new String(buf); |
||||
} |
||||
|
||||
/** |
||||
* 使用公钥按照 RSA_PKCS1_OAEP_PADDING 算法进行加密 |
||||
* |
||||
* @param publicKey 加密用公钥对象 |
||||
* @param plaintext 待加密明文 |
||||
* @return 加密后密文 |
||||
*/ |
||||
public static String encrypt(PublicKey publicKey, String plaintext) { |
||||
final String transformation = "RSA/ECB/OAEPWithSHA-1AndMGF1Padding"; |
||||
|
||||
try { |
||||
Cipher cipher = Cipher.getInstance(transformation); |
||||
cipher.init(Cipher.ENCRYPT_MODE, publicKey); |
||||
return Base64.getEncoder().encodeToString(cipher.doFinal(plaintext.getBytes(StandardCharsets.UTF_8))); |
||||
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) { |
||||
throw new IllegalArgumentException("The current Java environment does not support " + transformation, e); |
||||
} catch (InvalidKeyException e) { |
||||
throw new IllegalArgumentException("RSA encryption using an illegal publicKey", e); |
||||
} catch (BadPaddingException | IllegalBlockSizeException e) { |
||||
throw new IllegalArgumentException("Plaintext is too long", e); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 使用私钥按照 RSA_PKCS1_OAEP_PADDING 算法进行解密 |
||||
* |
||||
* @param privateKey 解密用私钥对象 |
||||
* @param ciphertext 待解密密文(Base64编码的字符串) |
||||
* @return 解密后明文 |
||||
*/ |
||||
public static String rsaOaepDecrypt(PrivateKey privateKey, String ciphertext) { |
||||
final String transformation = "RSA/ECB/OAEPWithSHA-1AndMGF1Padding"; |
||||
|
||||
try { |
||||
Cipher cipher = Cipher.getInstance(transformation); |
||||
cipher.init(Cipher.DECRYPT_MODE, privateKey); |
||||
byte[] decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(ciphertext)); |
||||
return new String(decryptedBytes, StandardCharsets.UTF_8); |
||||
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) { |
||||
throw new IllegalArgumentException("The current Java environment does not support " + transformation, e); |
||||
} catch (InvalidKeyException e) { |
||||
throw new IllegalArgumentException("RSA decryption using an illegal privateKey", e); |
||||
} catch (BadPaddingException | IllegalBlockSizeException e) { |
||||
throw new IllegalArgumentException("Ciphertext decryption failed", e); |
||||
} |
||||
} |
||||
|
||||
public static String aesAeadDecrypt(byte[] key, byte[] associatedData, byte[] nonce, |
||||
byte[] ciphertext) { |
||||
final String transformation = "AES/GCM/NoPadding"; |
||||
final String algorithm = "AES"; |
||||
final int tagLengthBit = 128; |
||||
|
||||
try { |
||||
Cipher cipher = Cipher.getInstance(transformation); |
||||
cipher.init( |
||||
Cipher.DECRYPT_MODE, |
||||
new SecretKeySpec(key, algorithm), |
||||
new GCMParameterSpec(tagLengthBit, nonce)); |
||||
if (associatedData != null) { |
||||
cipher.updateAAD(associatedData); |
||||
} |
||||
return new String(cipher.doFinal(ciphertext), StandardCharsets.UTF_8); |
||||
} catch (InvalidKeyException |
||||
| InvalidAlgorithmParameterException |
||||
| BadPaddingException |
||||
| IllegalBlockSizeException |
||||
| NoSuchAlgorithmException |
||||
| NoSuchPaddingException e) { |
||||
throw new IllegalArgumentException(String.format("AesAeadDecrypt with %s Failed", |
||||
transformation), e); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 使用私钥按照指定算法进行签名 |
||||
* |
||||
* @param message 待签名串 |
||||
* @param algorithm 签名算法,如 SHA256withRSA |
||||
* @param privateKey 签名用私钥对象 |
||||
* @return 签名结果 |
||||
*/ |
||||
public static String sign(String message, String algorithm, PrivateKey privateKey) { |
||||
byte[] sign; |
||||
try { |
||||
Signature signature = Signature.getInstance(algorithm); |
||||
signature.initSign(privateKey); |
||||
signature.update(message.getBytes(StandardCharsets.UTF_8)); |
||||
sign = signature.sign(); |
||||
} catch (NoSuchAlgorithmException e) { |
||||
throw new UnsupportedOperationException("The current Java environment does not support " + algorithm, e); |
||||
} catch (InvalidKeyException e) { |
||||
throw new IllegalArgumentException(algorithm + " signature uses an illegal privateKey.", e); |
||||
} catch (SignatureException e) { |
||||
throw new RuntimeException("An error occurred during the sign process.", e); |
||||
} |
||||
return Base64.getEncoder().encodeToString(sign); |
||||
} |
||||
|
||||
/** |
||||
* 使用公钥按照特定算法验证签名 |
||||
* |
||||
* @param message 待签名串 |
||||
* @param signature 待验证的签名内容 |
||||
* @param algorithm 签名算法,如:SHA256withRSA |
||||
* @param publicKey 验签用公钥对象 |
||||
* @return 签名验证是否通过 |
||||
*/ |
||||
public static boolean verify(String message, String signature, String algorithm, |
||||
PublicKey publicKey) { |
||||
try { |
||||
Signature sign = Signature.getInstance(algorithm); |
||||
sign.initVerify(publicKey); |
||||
sign.update(message.getBytes(StandardCharsets.UTF_8)); |
||||
return sign.verify(Base64.getDecoder().decode(signature)); |
||||
} catch (SignatureException e) { |
||||
return false; |
||||
} catch (InvalidKeyException e) { |
||||
throw new IllegalArgumentException("verify uses an illegal publickey.", e); |
||||
} catch (NoSuchAlgorithmException e) { |
||||
throw new UnsupportedOperationException("The current Java environment does not support" + algorithm, e); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 根据微信支付APIv3请求签名规则构造 Authorization 签名 |
||||
* |
||||
* @param mchid 商户号 |
||||
* @param certificateSerialNo 商户API证书序列号 |
||||
* @param privateKey 商户API证书私钥 |
||||
* @param method 请求接口的HTTP方法,请使用全大写表述,如 GET、POST、PUT、DELETE |
||||
* @param uri 请求接口的URL |
||||
* @param body 请求接口的Body |
||||
* @return 构造好的微信支付APIv3 Authorization 头 |
||||
*/ |
||||
public static String buildAuthorization(String mchid, String certificateSerialNo, |
||||
PrivateKey privateKey, |
||||
String method, String uri, String body) { |
||||
String nonce = createNonce(32); |
||||
long timestamp = Instant.now().getEpochSecond(); |
||||
|
||||
String message = String.format("%s\n%s\n%d\n%s\n%s\n", method, uri, timestamp, nonce, |
||||
body == null ? "" : body); |
||||
|
||||
String signature = sign(message, "SHA256withRSA", privateKey); |
||||
|
||||
return String.format( |
||||
"WECHATPAY2-SHA256-RSA2048 mchid=\"%s\",nonce_str=\"%s\",signature=\"%s\"," + |
||||
"timestamp=\"%d\",serial_no=\"%s\"", |
||||
mchid, nonce, signature, timestamp, certificateSerialNo); |
||||
} |
||||
|
||||
/** |
||||
* 计算输入流的哈希值 |
||||
* |
||||
* @param inputStream 输入流 |
||||
* @param algorithm 哈希算法名称,如 "SHA-256", "SHA-1" |
||||
* @return 哈希值的十六进制字符串 |
||||
*/ |
||||
private static String calculateHash(InputStream inputStream, String algorithm) { |
||||
try { |
||||
MessageDigest digest = MessageDigest.getInstance(algorithm); |
||||
byte[] buffer = new byte[8192]; |
||||
int bytesRead; |
||||
while ((bytesRead = inputStream.read(buffer)) != -1) { |
||||
digest.update(buffer, 0, bytesRead); |
||||
} |
||||
byte[] hashBytes = digest.digest(); |
||||
StringBuilder hexString = new StringBuilder(); |
||||
for (byte b : hashBytes) { |
||||
String hex = Integer.toHexString(0xff & b); |
||||
if (hex.length() == 1) { |
||||
hexString.append('0'); |
||||
} |
||||
hexString.append(hex); |
||||
} |
||||
return hexString.toString(); |
||||
} catch (NoSuchAlgorithmException e) { |
||||
throw new UnsupportedOperationException(algorithm + " algorithm not available", e); |
||||
} catch (IOException e) { |
||||
throw new RuntimeException("Error reading from input stream", e); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 计算输入流的 SHA256 哈希值 |
||||
* |
||||
* @param inputStream 输入流 |
||||
* @return SHA256 哈希值的十六进制字符串 |
||||
*/ |
||||
public static String sha256(InputStream inputStream) { |
||||
return calculateHash(inputStream, "SHA-256"); |
||||
} |
||||
|
||||
/** |
||||
* 计算输入流的 SHA1 哈希值 |
||||
* |
||||
* @param inputStream 输入流 |
||||
* @return SHA1 哈希值的十六进制字符串 |
||||
*/ |
||||
public static String sha1(InputStream inputStream) { |
||||
return calculateHash(inputStream, "SHA-1"); |
||||
} |
||||
|
||||
/** |
||||
* 计算输入流的 SM3 哈希值 |
||||
* |
||||
* @param inputStream 输入流 |
||||
* @return SM3 哈希值的十六进制字符串 |
||||
*/ |
||||
public static String sm3(InputStream inputStream) { |
||||
// 确保Bouncy Castle Provider已注册
|
||||
if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) { |
||||
Security.addProvider(new BouncyCastleProvider()); |
||||
} |
||||
|
||||
try { |
||||
SM3Digest digest = new SM3Digest(); |
||||
byte[] buffer = new byte[8192]; |
||||
int bytesRead; |
||||
while ((bytesRead = inputStream.read(buffer)) != -1) { |
||||
digest.update(buffer, 0, bytesRead); |
||||
} |
||||
byte[] hashBytes = new byte[digest.getDigestSize()]; |
||||
digest.doFinal(hashBytes, 0); |
||||
|
||||
StringBuilder hexString = new StringBuilder(); |
||||
for (byte b : hashBytes) { |
||||
String hex = Integer.toHexString(0xff & b); |
||||
if (hex.length() == 1) { |
||||
hexString.append('0'); |
||||
} |
||||
hexString.append(hex); |
||||
} |
||||
return hexString.toString(); |
||||
} catch (IOException e) { |
||||
throw new RuntimeException("Error reading from input stream", e); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 对参数进行 URL 编码 |
||||
* |
||||
* @param content 参数内容 |
||||
* @return 编码后的内容 |
||||
*/ |
||||
public static String urlEncode(String content) { |
||||
try { |
||||
return URLEncoder.encode(content, StandardCharsets.UTF_8.name()); |
||||
} catch (UnsupportedEncodingException e) { |
||||
throw new RuntimeException(e); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 对参数Map进行 URL 编码,生成 QueryString |
||||
* |
||||
* @param params Query参数Map |
||||
* @return QueryString |
||||
*/ |
||||
public static String urlEncode(Map<String, Object> params) { |
||||
if (params == null || params.isEmpty()) { |
||||
return ""; |
||||
} |
||||
|
||||
StringBuilder result = new StringBuilder(); |
||||
for (Entry<String, Object> entry : params.entrySet()) { |
||||
if (entry.getValue() == null) { |
||||
continue; |
||||
} |
||||
|
||||
String key = entry.getKey(); |
||||
Object value = entry.getValue(); |
||||
if (value instanceof List) { |
||||
List<?> list = (List<?>) entry.getValue(); |
||||
for (Object temp : list) { |
||||
appendParam(result, key, temp); |
||||
} |
||||
} else { |
||||
appendParam(result, key, value); |
||||
} |
||||
} |
||||
return result.toString(); |
||||
} |
||||
|
||||
/** |
||||
* 将键值对 放入返回结果 |
||||
* |
||||
* @param result 返回的query string |
||||
* @param key 属性 |
||||
* @param value 属性值 |
||||
*/ |
||||
private static void appendParam(StringBuilder result, String key, Object value) { |
||||
if (result.length() > 0) { |
||||
result.append("&"); |
||||
} |
||||
|
||||
String valueString; |
||||
// 如果是基本类型、字符串或枚举,直接转换;如果是对象,序列化为JSON
|
||||
if (value instanceof String || value instanceof Number || |
||||
value instanceof Boolean || value instanceof Enum) { |
||||
valueString = value.toString(); |
||||
} else { |
||||
valueString = toJson(value); |
||||
} |
||||
|
||||
result.append(key) |
||||
.append("=") |
||||
.append(urlEncode(valueString)); |
||||
} |
||||
|
||||
/** |
||||
* 从应答中提取 Body |
||||
* |
||||
* @param response HTTP 请求应答对象 |
||||
* @return 应答中的Body内容,Body为空时返回空字符串 |
||||
*/ |
||||
public static String extractBody(Response response) { |
||||
if (response.body() == null) { |
||||
return ""; |
||||
} |
||||
|
||||
try { |
||||
BufferedSource source = response.body().source(); |
||||
return source.readUtf8(); |
||||
} catch (IOException e) { |
||||
throw new RuntimeException(String.format("An error occurred during reading response body. " + |
||||
"Status: %d", response.code()), e); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 根据微信支付APIv3应答验签规则对应答签名进行验证,验证不通过时抛出异常 |
||||
* |
||||
* @param wechatpayPublicKeyId 微信支付公钥ID |
||||
* @param wechatpayPublicKey 微信支付公钥对象 |
||||
* @param headers 微信支付应答 Header 列表 |
||||
* @param body 微信支付应答 Body |
||||
*/ |
||||
public static void validateResponse(String wechatpayPublicKeyId, PublicKey wechatpayPublicKey, |
||||
Headers headers, |
||||
String body) { |
||||
String timestamp = headers.get("Wechatpay-Timestamp"); |
||||
String requestId = headers.get("Request-ID"); |
||||
try { |
||||
Instant responseTime = Instant.ofEpochSecond(Long.parseLong(timestamp)); |
||||
// 拒绝过期请求
|
||||
if (Duration.between(responseTime, Instant.now()).abs().toMinutes() >= 5) { |
||||
throw new IllegalArgumentException( |
||||
String.format("Validate response failed, timestamp[%s] is expired, request-id[%s]", |
||||
timestamp, requestId)); |
||||
} |
||||
} catch (DateTimeException | NumberFormatException e) { |
||||
throw new IllegalArgumentException( |
||||
String.format("Validate response failed, timestamp[%s] is invalid, request-id[%s]", |
||||
timestamp, requestId)); |
||||
} |
||||
String serialNumber = headers.get("Wechatpay-Serial"); |
||||
if (!Objects.equals(serialNumber, wechatpayPublicKeyId)) { |
||||
throw new IllegalArgumentException( |
||||
String.format("Validate response failed, Invalid Wechatpay-Serial, Local: %s, Remote: " + |
||||
"%s", wechatpayPublicKeyId, serialNumber)); |
||||
} |
||||
|
||||
String signature = headers.get("Wechatpay-Signature"); |
||||
String message = String.format("%s\n%s\n%s\n", timestamp, headers.get("Wechatpay-Nonce"), |
||||
body == null ? "" : body); |
||||
|
||||
boolean success = verify(message, signature, "SHA256withRSA", wechatpayPublicKey); |
||||
if (!success) { |
||||
throw new IllegalArgumentException( |
||||
String.format("Validate response failed,the WechatPay signature is incorrect.%n" |
||||
+ "Request-ID[%s]\tresponseHeader[%s]\tresponseBody[%.1024s]", |
||||
headers.get("Request-ID"), headers, body)); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 根据微信支付APIv3通知验签规则对通知签名进行验证,验证不通过时抛出异常 |
||||
* |
||||
* @param wechatpayPublicKeyId 微信支付公钥ID |
||||
* @param wechatpayPublicKey 微信支付公钥对象 |
||||
* @param headers 微信支付通知 Header 列表 |
||||
* @param body 微信支付通知 Body |
||||
*/ |
||||
public static void validateNotification(String wechatpayPublicKeyId, |
||||
PublicKey wechatpayPublicKey, Headers headers, |
||||
String body) { |
||||
String timestamp = headers.get("Wechatpay-Timestamp"); |
||||
try { |
||||
Instant responseTime = Instant.ofEpochSecond(Long.parseLong(timestamp)); |
||||
// 拒绝过期请求
|
||||
if (Duration.between(responseTime, Instant.now()).abs().toMinutes() >= 5) { |
||||
throw new IllegalArgumentException( |
||||
String.format("Validate notification failed, timestamp[%s] is expired", timestamp)); |
||||
} |
||||
} catch (DateTimeException | NumberFormatException e) { |
||||
throw new IllegalArgumentException( |
||||
String.format("Validate notification failed, timestamp[%s] is invalid", timestamp)); |
||||
} |
||||
String serialNumber = headers.get("Wechatpay-Serial"); |
||||
if (!Objects.equals(serialNumber, wechatpayPublicKeyId)) { |
||||
throw new IllegalArgumentException( |
||||
String.format("Validate notification failed, Invalid Wechatpay-Serial, Local: %s, " + |
||||
"Remote: %s", |
||||
wechatpayPublicKeyId, |
||||
serialNumber)); |
||||
} |
||||
|
||||
String signature = headers.get("Wechatpay-Signature"); |
||||
String message = String.format("%s\n%s\n%s\n", timestamp, headers.get("Wechatpay-Nonce"), |
||||
body == null ? "" : body); |
||||
|
||||
boolean success = verify(message, signature, "SHA256withRSA", wechatpayPublicKey); |
||||
if (!success) { |
||||
throw new IllegalArgumentException( |
||||
String.format("Validate notification failed, WechatPay signature is incorrect.\n" |
||||
+ "responseHeader[%s]\tresponseBody[%.1024s]", |
||||
headers, body)); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 对微信支付通知进行签名验证、解析,同时将业务数据解密。验签名失败、解析失败、解密失败时抛出异常 |
||||
* |
||||
* @param apiv3Key 商户的 APIv3 Key |
||||
* @param wechatpayPublicKeyId 微信支付公钥ID |
||||
* @param wechatpayPublicKey 微信支付公钥对象 |
||||
* @param headers 微信支付请求 Header 列表 |
||||
* @param body 微信支付请求 Body |
||||
* @return 解析后的通知内容,解密后的业务数据可以使用 Notification.getPlaintext() 访问 |
||||
*/ |
||||
public static Notification parseNotification(String apiv3Key, String wechatpayPublicKeyId, |
||||
PublicKey wechatpayPublicKey, Headers headers, |
||||
String body) { |
||||
validateNotification(wechatpayPublicKeyId, wechatpayPublicKey, headers, body); |
||||
Notification notification = gson.fromJson(body, Notification.class); |
||||
notification.decrypt(apiv3Key); |
||||
return notification; |
||||
} |
||||
|
||||
/** |
||||
* 微信支付API错误异常,发送HTTP请求成功,但返回状态码不是 2XX 时抛出本异常 |
||||
*/ |
||||
public static class ApiException extends RuntimeException { |
||||
private static final long serialVersionUID = 2261086748874802175L; |
||||
|
||||
private final int statusCode; |
||||
private final String body; |
||||
private final Headers headers; |
||||
private final String errorCode; |
||||
private final String errorMessage; |
||||
|
||||
public ApiException(int statusCode, String body, Headers headers) { |
||||
super(String.format("微信支付API访问失败,StatusCode: [%s], Body: [%s], Headers: [%s]", statusCode, |
||||
body, headers)); |
||||
this.statusCode = statusCode; |
||||
this.body = body; |
||||
this.headers = headers; |
||||
|
||||
if (body != null && !body.isEmpty()) { |
||||
JsonElement code; |
||||
JsonElement message; |
||||
|
||||
try { |
||||
JsonObject jsonObject = gson.fromJson(body, JsonObject.class); |
||||
code = jsonObject.get("code"); |
||||
message = jsonObject.get("message"); |
||||
} catch (JsonSyntaxException ignored) { |
||||
code = null; |
||||
message = null; |
||||
} |
||||
this.errorCode = code == null ? null : code.getAsString(); |
||||
this.errorMessage = message == null ? null : message.getAsString(); |
||||
} else { |
||||
this.errorCode = null; |
||||
this.errorMessage = null; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 获取 HTTP 应答状态码 |
||||
*/ |
||||
public int getStatusCode() { |
||||
return statusCode; |
||||
} |
||||
|
||||
/** |
||||
* 获取 HTTP 应答包体内容 |
||||
*/ |
||||
public String getBody() { |
||||
return body; |
||||
} |
||||
|
||||
/** |
||||
* 获取 HTTP 应答 Header |
||||
*/ |
||||
public Headers getHeaders() { |
||||
return headers; |
||||
} |
||||
|
||||
/** |
||||
* 获取 错误码 (错误应答中的 code 字段) |
||||
*/ |
||||
public String getErrorCode() { |
||||
return errorCode; |
||||
} |
||||
|
||||
/** |
||||
* 获取 错误消息 (错误应答中的 message 字段) |
||||
*/ |
||||
public String getErrorMessage() { |
||||
return errorMessage; |
||||
} |
||||
} |
||||
|
||||
public static class Notification { |
||||
@SerializedName("id") |
||||
private String id; |
||||
@SerializedName("create_time") |
||||
private String createTime; |
||||
@SerializedName("event_type") |
||||
private String eventType; |
||||
@SerializedName("resource_type") |
||||
private String resourceType; |
||||
@SerializedName("summary") |
||||
private String summary; |
||||
@SerializedName("resource") |
||||
private Resource resource; |
||||
private String plaintext; |
||||
|
||||
public String getId() { |
||||
return id; |
||||
} |
||||
|
||||
public String getCreateTime() { |
||||
return createTime; |
||||
} |
||||
|
||||
public String getEventType() { |
||||
return eventType; |
||||
} |
||||
|
||||
public String getResourceType() { |
||||
return resourceType; |
||||
} |
||||
|
||||
public String getSummary() { |
||||
return summary; |
||||
} |
||||
|
||||
public Resource getResource() { |
||||
return resource; |
||||
} |
||||
|
||||
/** |
||||
* 获取解密后的业务数据(JSON字符串,需要自行解析) |
||||
*/ |
||||
public String getPlaintext() { |
||||
return plaintext; |
||||
} |
||||
|
||||
private void validate() { |
||||
if (resource == null) { |
||||
throw new IllegalArgumentException("Missing required field `resource` in notification"); |
||||
} |
||||
resource.validate(); |
||||
} |
||||
|
||||
/** |
||||
* 使用 APIv3Key 对通知中的业务数据解密,解密结果可以通过 getPlainText 访问。 |
||||
* 外部拿到的 Notification 一定是解密过的,因此本方法没有设置为 public |
||||
* |
||||
* @param apiv3Key 商户APIv3 Key |
||||
*/ |
||||
private void decrypt(String apiv3Key) { |
||||
validate(); |
||||
|
||||
plaintext = aesAeadDecrypt( |
||||
apiv3Key.getBytes(StandardCharsets.UTF_8), |
||||
resource.associatedData.getBytes(StandardCharsets.UTF_8), |
||||
resource.nonce.getBytes(StandardCharsets.UTF_8), |
||||
Base64.getDecoder().decode(resource.ciphertext) |
||||
); |
||||
} |
||||
|
||||
public static class Resource { |
||||
@SerializedName("algorithm") |
||||
private String algorithm; |
||||
|
||||
@SerializedName("ciphertext") |
||||
private String ciphertext; |
||||
|
||||
@SerializedName("associated_data") |
||||
private String associatedData; |
||||
|
||||
@SerializedName("nonce") |
||||
private String nonce; |
||||
|
||||
@SerializedName("original_type") |
||||
private String originalType; |
||||
|
||||
public String getAlgorithm() { |
||||
return algorithm; |
||||
} |
||||
|
||||
public String getCiphertext() { |
||||
return ciphertext; |
||||
} |
||||
|
||||
public String getAssociatedData() { |
||||
return associatedData; |
||||
} |
||||
|
||||
public String getNonce() { |
||||
return nonce; |
||||
} |
||||
|
||||
public String getOriginalType() { |
||||
return originalType; |
||||
} |
||||
|
||||
private void validate() { |
||||
if (algorithm == null || algorithm.isEmpty()) { |
||||
throw new IllegalArgumentException("Missing required field `algorithm` in Notification" + |
||||
".Resource"); |
||||
} |
||||
if (!Objects.equals(algorithm, "AEAD_AES_256_GCM")) { |
||||
throw new IllegalArgumentException(String.format("Unsupported `algorithm`[%s] in " + |
||||
"Notification.Resource", algorithm)); |
||||
} |
||||
|
||||
if (ciphertext == null || ciphertext.isEmpty()) { |
||||
throw new IllegalArgumentException("Missing required field `ciphertext` in Notification" + |
||||
".Resource"); |
||||
} |
||||
|
||||
if (associatedData == null || associatedData.isEmpty()) { |
||||
throw new IllegalArgumentException("Missing required field `associatedData` in " + |
||||
"Notification.Resource"); |
||||
} |
||||
|
||||
if (nonce == null || nonce.isEmpty()) { |
||||
throw new IllegalArgumentException("Missing required field `nonce` in Notification" + |
||||
".Resource"); |
||||
} |
||||
|
||||
if (originalType == null || originalType.isEmpty()) { |
||||
throw new IllegalArgumentException("Missing required field `originalType` in " + |
||||
"Notification.Resource"); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 根据文件名获取对应的Content-Type |
||||
* |
||||
* @param fileName 文件名 |
||||
* @return Content-Type字符串 |
||||
*/ |
||||
public static String getContentTypeByFileName(String fileName) { |
||||
if (fileName == null || fileName.isEmpty()) { |
||||
return "application/octet-stream"; |
||||
} |
||||
|
||||
// 获取文件扩展名
|
||||
String extension = ""; |
||||
int lastDotIndex = fileName.lastIndexOf('.'); |
||||
if (lastDotIndex > 0 && lastDotIndex < fileName.length() - 1) { |
||||
extension = fileName.substring(lastDotIndex + 1).toLowerCase(); |
||||
} |
||||
|
||||
// 常见文件类型映射
|
||||
Map<String, String> contentTypeMap = new HashMap<>(); |
||||
// 图片类型
|
||||
contentTypeMap.put("png", "image/png"); |
||||
contentTypeMap.put("jpg", "image/jpeg"); |
||||
contentTypeMap.put("jpeg", "image/jpeg"); |
||||
contentTypeMap.put("gif", "image/gif"); |
||||
contentTypeMap.put("bmp", "image/bmp"); |
||||
contentTypeMap.put("webp", "image/webp"); |
||||
contentTypeMap.put("svg", "image/svg+xml"); |
||||
contentTypeMap.put("ico", "image/x-icon"); |
||||
|
||||
// 文档类型
|
||||
contentTypeMap.put("pdf", "application/pdf"); |
||||
contentTypeMap.put("doc", "application/msword"); |
||||
contentTypeMap.put("docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"); |
||||
contentTypeMap.put("xls", "application/vnd.ms-excel"); |
||||
contentTypeMap.put("xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); |
||||
contentTypeMap.put("ppt", "application/vnd.ms-powerpoint"); |
||||
contentTypeMap.put("pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation"); |
||||
|
||||
// 文本类型
|
||||
contentTypeMap.put("txt", "text/plain"); |
||||
contentTypeMap.put("html", "text/html"); |
||||
contentTypeMap.put("css", "text/css"); |
||||
contentTypeMap.put("js", "application/javascript"); |
||||
contentTypeMap.put("json", "application/json"); |
||||
contentTypeMap.put("xml", "application/xml"); |
||||
contentTypeMap.put("csv", "text/csv"); |
||||
|
||||
// 音视频类型
|
||||
contentTypeMap.put("mp3", "audio/mpeg"); |
||||
contentTypeMap.put("wav", "audio/wav"); |
||||
contentTypeMap.put("mp4", "video/mp4"); |
||||
contentTypeMap.put("avi", "video/x-msvideo"); |
||||
contentTypeMap.put("mov", "video/quicktime"); |
||||
|
||||
// 压缩文件类型
|
||||
contentTypeMap.put("zip", "application/zip"); |
||||
contentTypeMap.put("rar", "application/x-rar-compressed"); |
||||
contentTypeMap.put("7z", "application/x-7z-compressed"); |
||||
|
||||
|
||||
return contentTypeMap.getOrDefault(extension, "application/octet-stream"); |
||||
} |
||||
} |
||||
@ -1,188 +1,239 @@ |
||||
//package com.ynxbd.wx.wxfactory.payment.jsapi;
|
||||
//
|
||||
//import com.ynxbd.common.helper.common.ErrorHelper;
|
||||
//import com.ynxbd.common.helper.common.JsonHelper;
|
||||
//import com.ynxbd.common.result.JsonResult;
|
||||
//import com.ynxbd.wx.wxfactory.bean.MedicalPayOrder;
|
||||
//import com.ynxbd.wx.wxfactory.utils.WxRequestHelper;
|
||||
//import lombok.extern.slf4j.Slf4j;
|
||||
//
|
||||
//import java.math.BigDecimal;
|
||||
//import java.util.HashMap;
|
||||
//import java.util.Map;
|
||||
//
|
||||
//@Slf4j
|
||||
//public class Client {
|
||||
//
|
||||
// /**
|
||||
// * 微信JSAPI下单
|
||||
// *
|
||||
// * @param appId 应用ID
|
||||
// * @param mchId 直连商户号
|
||||
// * @param description 商品描述
|
||||
// * @param out_trade_no 商户订单号
|
||||
// * @param time_expire 交易结束时间(yyyy-MM-DDTHH:mm:ss+TIMEZONE 示例值:2018-06-08T10:34:56+08:00)
|
||||
// * @param notify_url 通知地址
|
||||
// * @param totalFee 订单金额(元)
|
||||
// * @param openid openid
|
||||
// * @param attach 附加数据
|
||||
// */
|
||||
// public MedicalPayOrder createOrder(String appId, String mchId, String description, String out_trade_no,
|
||||
// String time_expire, String attach, String notify_url, BigDecimal totalFee,
|
||||
// String openid) {
|
||||
//
|
||||
package com.ynxbd.wx.wxfactory.payment.jsapi; |
||||
|
||||
import com.google.gson.annotations.SerializedName; |
||||
import com.ynxbd.wx.wxfactory.payment.WXPayUtility; |
||||
import lombok.extern.slf4j.Slf4j; |
||||
import okhttp3.*; |
||||
|
||||
import java.io.IOException; |
||||
import java.io.UncheckedIOException; |
||||
import java.security.PrivateKey; |
||||
import java.security.PublicKey; |
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
|
||||
/** |
||||
* JSAPI下单 |
||||
*/ |
||||
|
||||
|
||||
@Slf4j |
||||
public class Client { |
||||
private static String HOST = "https://api.mch.weixin.qq.com"; |
||||
private static String METHOD = "POST"; |
||||
private static String PATH = "/v3/pay/transactions/jsapi"; |
||||
|
||||
|
||||
// JsapiPrepay client = new JsapiPrepay(
|
||||
// "19xxxxxxxx", // 商户号,是由微信支付系统生成并分配给每个商户的唯一标识符,商户号获取方式参考 https://pay.weixin.qq.com/doc/v3/merchant/4013070756
|
||||
// "1DDE55AD98Exxxxxxxxxx", // 商户API证书序列号,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013053053
|
||||
// "/path/to/apiclient_key.pem", // 商户API证书私钥文件路径,本地文件路径
|
||||
// "PUB_KEY_ID_xxxxxxxxxxxxx", // 微信支付公钥ID,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013038816
|
||||
// "/path/to/wxp_pub.pem" // 微信支付公钥文件路径,本地文件路径
|
||||
// );
|
||||
|
||||
// public DirectAPIv3JsapiPrepayResponse jsapiPrepay(String appId,
|
||||
// String mchId,
|
||||
// String certificateSerialNo,
|
||||
// PrivateKey privateKey,
|
||||
// String wechatPayPublicKeyId,
|
||||
// PublicKey wechatPayPublicKey) {
|
||||
// DirectAPIv3JsapiPrepayRequest request = new DirectAPIv3JsapiPrepayRequest();
|
||||
// request.appid = appId;
|
||||
// request.mchid = mchId;
|
||||
// request.description = "Image形象店-深圳腾大-QQ公仔";
|
||||
// request.outTradeNo = "1217752501201407033233368018";
|
||||
// request.timeExpire = "2018-06-08T10:34:56+08:00";
|
||||
// request.attach = "自定义数据说明";
|
||||
// request.notifyUrl = " https://www.weixin.qq.com/wxpay/pay.php";
|
||||
// request.goodsTag = "WXG";
|
||||
// request.supportFapiao = false;
|
||||
// request.amount = new CommonAmountInfo();
|
||||
// request.amount.total = 100L;
|
||||
// request.amount.currency = "CNY";
|
||||
// request.payer = new JsapiReqPayerInfo();
|
||||
// request.payer.openid = "oUpF8uMuAJO_M2pxb1Q9zNjWeS6o";
|
||||
// request.detail = new CouponInfo();
|
||||
// request.detail.costPrice = 608800L;
|
||||
// request.detail.invoiceId = "微信123";
|
||||
// request.detail.goodsDetail = new ArrayList<>();
|
||||
// {
|
||||
// GoodsDetail goodsDetailItem = new Client.GoodsDetail();
|
||||
// goodsDetailItem.merchantGoodsId = "1246464644";
|
||||
// goodsDetailItem.wechatpayGoodsId = "1001";
|
||||
// goodsDetailItem.goodsName = "iPhoneX 256G";
|
||||
// goodsDetailItem.quantity = 1L;
|
||||
// goodsDetailItem.unitPrice = 528800L;
|
||||
// request.detail.goodsDetail.add(goodsDetailItem);
|
||||
// }
|
||||
// ;
|
||||
// request.sceneInfo = new Client.CommonSceneInfo();
|
||||
// request.sceneInfo.payerClientIp = "14.23.150.211";
|
||||
// request.sceneInfo.deviceId = "013467007045764";
|
||||
// request.sceneInfo.storeInfo = new Client.StoreInfo();
|
||||
// request.sceneInfo.storeInfo.id = "0001";
|
||||
// request.sceneInfo.storeInfo.name = "腾讯大厦分店";
|
||||
// request.sceneInfo.storeInfo.areaCode = "440305";
|
||||
// request.sceneInfo.storeInfo.address = "广东省深圳市南山区科技中一道10000号";
|
||||
// request.settleInfo = new Client.SettleInfo();
|
||||
// request.settleInfo.profitSharing = false;
|
||||
// try {
|
||||
// JsonResult jsonResult = WxRequestHelper.postMdXml("https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi", params -> {
|
||||
// params.put("appid", appId);
|
||||
// params.put("mchid", mchId);
|
||||
// params.put("description", description);
|
||||
// params.put("out_trade_no", out_trade_no);
|
||||
// if (time_expire != null) {
|
||||
// params.put("time_expire", time_expire);
|
||||
// }
|
||||
// Map<String, Object> amountMap = new HashMap<>();
|
||||
// amountMap.put("total", totalFee.movePointRight(2)); // 分
|
||||
// params.put("amount", amountMap);
|
||||
//
|
||||
// params.put("notify_url", notify_url);
|
||||
// params.put("openid", openid);
|
||||
//
|
||||
// if (attach != null) {
|
||||
// params.put("attach", attach);
|
||||
// String uri = PATH;
|
||||
// String reqBody = WXPayUtility.toJson(request);
|
||||
//
|
||||
// Request.Builder reqBuilder = new Request.Builder().url(HOST + uri);
|
||||
// reqBuilder.addHeader("Accept", "application/json");
|
||||
// reqBuilder.addHeader("Wechatpay-Serial", wechatPayPublicKeyId);
|
||||
// reqBuilder.addHeader("Authorization", WXPayUtility.buildAuthorization(mchId, certificateSerialNo, privateKey, METHOD, uri, reqBody));
|
||||
// reqBuilder.addHeader("Content-Type", "application/json");
|
||||
// RequestBody requestBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), reqBody);
|
||||
// reqBuilder.method(METHOD, requestBody);
|
||||
// Request httpRequest = reqBuilder.build();
|
||||
//
|
||||
// // 发送HTTP请求
|
||||
// OkHttpClient client = new OkHttpClient.Builder().build();
|
||||
// try (Response httpResponse = client.newCall(httpRequest).execute()) {
|
||||
// String respBody = WXPayUtility.extractBody(httpResponse);
|
||||
// if (httpResponse.code() >= 200 && httpResponse.code() < 300) {
|
||||
// // 2XX 成功,验证应答签名
|
||||
// WXPayUtility.validateResponse(wechatPayPublicKeyId, wechatPayPublicKey, httpResponse.headers(), respBody);
|
||||
//
|
||||
// // 从HTTP应答报文构建返回数据
|
||||
// DirectAPIv3JsapiPrepayResponse response = WXPayUtility.fromJson(respBody, DirectAPIv3JsapiPrepayResponse.class);
|
||||
// // TODO: 请求成功,继续业务逻辑
|
||||
// System.out.println(response);
|
||||
// } else {
|
||||
// throw new WXPayUtility.ApiException(httpResponse.code(), respBody, httpResponse.headers());
|
||||
// }
|
||||
// });
|
||||
// log.info("[医保]下单返回:{}", JsonHelper.toJsonString(jsonResult));
|
||||
// if (!jsonResult.success()) {
|
||||
// return new MedicalPayOrder().createResult(jsonResult);
|
||||
// } catch (IOException e) {
|
||||
// throw new UncheckedIOException("Sending request to " + uri + " failed.", e);
|
||||
// }
|
||||
// return jsonResult.dataMapToBean(MedicalPayOrder.class);
|
||||
// } catch (Exception e) {
|
||||
// ErrorHelper.println(e);
|
||||
// } catch (WXPayUtility.ApiException e) {
|
||||
// // TODO: 请求失败,根据状态码执行不同的逻辑
|
||||
// e.printStackTrace();
|
||||
// }
|
||||
// return null;
|
||||
// }
|
||||
|
||||
|
||||
// public static class DirectAPIv3JsapiPrepayRequest {
|
||||
// @SerializedName("appid")
|
||||
// public String appid;
|
||||
//
|
||||
// @SerializedName("mchid")
|
||||
// public String mchid;
|
||||
//
|
||||
// /**
|
||||
// * 查询订单
|
||||
// *
|
||||
// * @param mchId 直连商户号
|
||||
// * @param outTradeNo 商品描述
|
||||
// * @param transId 微信支付订单号
|
||||
// */
|
||||
// public MedicalPayOrder queryOrder(String mchId, String outTradeNo, String transId) {
|
||||
// @SerializedName("description")
|
||||
// public String description;
|
||||
//
|
||||
// try {
|
||||
// JsonResult jsonResult = null;
|
||||
// @SerializedName("out_trade_no")
|
||||
// public String outTradeNo;
|
||||
//
|
||||
// if (outTradeNo != null) {
|
||||
// jsonResult = WxRequestHelper.postMdXml("https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/" + outTradeNo + "?" + mchId, params -> {
|
||||
// });
|
||||
// @SerializedName("time_expire")
|
||||
// public String timeExpire;
|
||||
//
|
||||
// } else {
|
||||
// if (transId != null) {
|
||||
// jsonResult = WxRequestHelper.postMdXml("https://api.mch.weixin.qq.com/v3/pay/transactions/id/" + transId + "?" + mchId, params -> {
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
// @SerializedName("attach")
|
||||
// public String attach;
|
||||
//
|
||||
// log.info("[医保]查询订单:{}", JsonHelper.toJsonString(jsonResult));
|
||||
// if (!jsonResult.success()) {
|
||||
// return new MedicalPayOrder().createResult(jsonResult);
|
||||
// }
|
||||
// return jsonResult.dataMapToBean(MedicalPayOrder.class);
|
||||
// } catch (Exception e) {
|
||||
// ErrorHelper.println(e);
|
||||
// }
|
||||
// return null;
|
||||
// @SerializedName("notify_url")
|
||||
// public String notifyUrl;
|
||||
//
|
||||
// @SerializedName("goods_tag")
|
||||
// public String goodsTag;
|
||||
//
|
||||
// @SerializedName("support_fapiao")
|
||||
// public Boolean supportFapiao;
|
||||
//
|
||||
// @SerializedName("amount")
|
||||
// public CommonAmountInfo amount;
|
||||
//
|
||||
// @SerializedName("payer")
|
||||
// public JsapiReqPayerInfo payer;
|
||||
//
|
||||
// @SerializedName("detail")
|
||||
// public CouponInfo detail;
|
||||
//
|
||||
// @SerializedName("scene_info")
|
||||
// public CommonSceneInfo sceneInfo;
|
||||
//
|
||||
// @SerializedName("settle_info")
|
||||
// public SettleInfo settleInfo;
|
||||
// }
|
||||
//
|
||||
// public static class DirectAPIv3JsapiPrepayResponse {
|
||||
// @SerializedName("prepay_id")
|
||||
// public String prepayId;
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 关闭订单
|
||||
// *
|
||||
// * @param mchId 直连商户号
|
||||
// * @param outTradeNo 商品描述
|
||||
// * @param transId 微信支付订单号
|
||||
// */
|
||||
// public MedicalPayOrder closeOrder(String mchId, String outTradeNo, String transId) {
|
||||
// public static class CommonAmountInfo {
|
||||
// @SerializedName("total")
|
||||
// public Long total;
|
||||
//
|
||||
// try {
|
||||
// JsonResult jsonResult = WxRequestHelper.postMdXml(" https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/" + outTradeNo + "/close", params -> {
|
||||
// params.put("mchId", mchId);
|
||||
// });
|
||||
// @SerializedName("currency")
|
||||
// public String currency;
|
||||
// }
|
||||
//
|
||||
// log.info("[医保]关闭订单返回:{}", JsonHelper.toJsonString(jsonResult));
|
||||
// if (!jsonResult.success()) {
|
||||
// return new MedicalPayOrder().createResult(jsonResult);
|
||||
// }
|
||||
// return jsonResult.dataMapToBean(MedicalPayOrder.class);
|
||||
// } catch (Exception e) {
|
||||
// ErrorHelper.println(e);
|
||||
// }
|
||||
// return null;
|
||||
// public static class JsapiReqPayerInfo {
|
||||
// @SerializedName("openid")
|
||||
// public String openid;
|
||||
// }
|
||||
//
|
||||
// public static class CouponInfo {
|
||||
// @SerializedName("cost_price")
|
||||
// public Long costPrice;
|
||||
//
|
||||
// /**
|
||||
// * 微信JSAPI下单
|
||||
// *
|
||||
// * @param outTradeNo 商品描述
|
||||
// * @param transId 微信支付订单号
|
||||
// */
|
||||
// public MedicalPayOrder refund(String outTradeNo, String transId, String outRefundNo, BigDecimal refundFee, BigDecimal totalFee, String reason, String notify_url) {
|
||||
// @SerializedName("invoice_id")
|
||||
// public String invoiceId;
|
||||
//
|
||||
// try {
|
||||
// JsonResult jsonResult = WxRequestHelper.postMdXml("https://api.mch.weixin.qq.com/v3/refund/domestic/refunds", params -> {
|
||||
// params.put("transaction_id", transId);
|
||||
// params.put("out_trade_no", outTradeNo);
|
||||
// params.put("out_refund_no", outRefundNo);
|
||||
//
|
||||
// Map<String, Object> amountMap = new HashMap<>();
|
||||
// amountMap.put("refund", refundFee.movePointRight(2)); // 分
|
||||
// amountMap.put("total", totalFee.movePointRight(2)); // 分
|
||||
// amountMap.put("currency", "CNY"); // 分
|
||||
// params.put("amount", amountMap);
|
||||
//
|
||||
// if (notify_url != null) {
|
||||
// params.put("notify_url", notify_url);
|
||||
// }
|
||||
// @SerializedName("goods_detail")
|
||||
// public List<GoodsDetail> goodsDetail;
|
||||
// }
|
||||
//
|
||||
// if (reason != null) {
|
||||
// params.put("reason", reason);
|
||||
// }
|
||||
// });
|
||||
// log.info("[微信]退费返回:{}", JsonHelper.toJsonString(jsonResult));
|
||||
// if (!jsonResult.success()) {
|
||||
// return new MedicalPayOrder().createResult(jsonResult);
|
||||
// }
|
||||
// return jsonResult.dataMapToBean(MedicalPayOrder.class);
|
||||
// } catch (Exception e) {
|
||||
// ErrorHelper.println(e);
|
||||
// }
|
||||
// return null;
|
||||
// public static class CommonSceneInfo {
|
||||
// @SerializedName("payer_client_ip")
|
||||
// public String payerClientIp;
|
||||
//
|
||||
// @SerializedName("device_id")
|
||||
// public String deviceId;
|
||||
//
|
||||
// @SerializedName("store_info")
|
||||
// public StoreInfo storeInfo;
|
||||
// }
|
||||
//
|
||||
// public static class SettleInfo {
|
||||
// @SerializedName("profit_sharing")
|
||||
// public Boolean profitSharing;
|
||||
// }
|
||||
//
|
||||
// public static class GoodsDetail {
|
||||
// @SerializedName("merchant_goods_id")
|
||||
// public String merchantGoodsId;
|
||||
//
|
||||
// /**
|
||||
// * 查询退费订单
|
||||
// *
|
||||
// * @param outRefundNo 退费订单号
|
||||
// */
|
||||
// public MedicalPayOrder queryRefund(String outRefundNo) {
|
||||
// @SerializedName("wechatpay_goods_id")
|
||||
// public String wechatpayGoodsId;
|
||||
//
|
||||
// try {
|
||||
// JsonResult jsonResult = WxRequestHelper.postMdXml("https://api.mch.weixin.qq.com/v3/refund/domestic/refunds/" + outRefundNo, params -> {
|
||||
// @SerializedName("goods_name")
|
||||
// public String goodsName;
|
||||
//
|
||||
// });
|
||||
// log.info("[微信]查询退费订单:{}", JsonHelper.toJsonString(jsonResult));
|
||||
// if (!jsonResult.success()) {
|
||||
// return new MedicalPayOrder().createResult(jsonResult);
|
||||
// }
|
||||
// return jsonResult.dataMapToBean(MedicalPayOrder.class);
|
||||
// } catch (Exception e) {
|
||||
// ErrorHelper.println(e);
|
||||
// }
|
||||
// return null;
|
||||
// @SerializedName("quantity")
|
||||
// public Long quantity;
|
||||
//
|
||||
// @SerializedName("unit_price")
|
||||
// public Long unitPrice;
|
||||
// }
|
||||
//
|
||||
// public static class StoreInfo {
|
||||
// @SerializedName("id")
|
||||
// public String id;
|
||||
//
|
||||
// @SerializedName("name")
|
||||
// public String name;
|
||||
//
|
||||
// @SerializedName("area_code")
|
||||
// public String areaCode;
|
||||
//
|
||||
// @SerializedName("address")
|
||||
// public String address;
|
||||
// }
|
||||
//}
|
||||
|
||||
} |
||||
|
||||
@ -1,4 +1,4 @@ |
||||
package com.ynxbd.wx.wxfactory.bean; |
||||
package com.ynxbd.wx.wxfactory.payment.jsapi.models; |
||||
|
||||
import lombok.Getter; |
||||
import lombok.NoArgsConstructor; |
||||
@ -1,40 +1,41 @@ |
||||
package com.ynxbd.wx.wxfactory.bean; |
||||
|
||||
import lombok.Getter; |
||||
import lombok.NoArgsConstructor; |
||||
import lombok.Setter; |
||||
import lombok.ToString; |
||||
|
||||
import java.math.BigDecimal; |
||||
|
||||
@Getter |
||||
@Setter |
||||
@ToString |
||||
@NoArgsConstructor |
||||
public class WxPayNotify { |
||||
private String openid; |
||||
private String outTradeNo; |
||||
private String tradeType; |
||||
private String bankType; |
||||
private String timeEnd; |
||||
private String attach; |
||||
private String transactionId; |
||||
private BigDecimal totalFee; |
||||
// --------------------------------------
|
||||
private String feeType; |
||||
private String cashFeeType; |
||||
private BigDecimal cashFee; |
||||
private BigDecimal settlementTotalFee; |
||||
private BigDecimal couponFee; |
||||
private Integer couponCount; |
||||
private String contractId; |
||||
private String tradeState; |
||||
// 微信支付分配的终端设备号
|
||||
private String deviceInfo; |
||||
private String isSubscribe; |
||||
|
||||
// 自定义参数-----------------------------
|
||||
private String payInfo; |
||||
private String payDate; |
||||
private String payTime; |
||||
} |
||||
package com.ynxbd.wx.wxfactory.payment.jsapi.models; |
||||
|
||||
import lombok.Getter; |
||||
import lombok.NoArgsConstructor; |
||||
import lombok.Setter; |
||||
import lombok.ToString; |
||||
|
||||
import java.math.BigDecimal; |
||||
|
||||
@Getter |
||||
@Setter |
||||
@ToString |
||||
@NoArgsConstructor |
||||
public class WxPayNotify { |
||||
private String openid; |
||||
private String productId; |
||||
private String outTradeNo; |
||||
private String tradeType; |
||||
private String bankType; |
||||
private String timeEnd; |
||||
private String attach; |
||||
private String transactionId; |
||||
private BigDecimal totalFee; |
||||
// --------------------------------------
|
||||
private String feeType; |
||||
private String cashFeeType; |
||||
private BigDecimal cashFee; |
||||
private BigDecimal settlementTotalFee; |
||||
private BigDecimal couponFee; |
||||
private Integer couponCount; |
||||
private String contractId; |
||||
private String tradeState; |
||||
// 微信支付分配的终端设备号
|
||||
private String deviceInfo; |
||||
private String isSubscribe; |
||||
|
||||
// 自定义参数-----------------------------
|
||||
private String payInfo; |
||||
private String payDate; |
||||
private String payTime; |
||||
} |
||||
@ -1,36 +1,36 @@ |
||||
package com.ynxbd.wx.wxfactory.base.refund; |
||||
|
||||
import com.ynxbd.wx.wxfactory.bean.refund.WxRefundQueryRoot; |
||||
import lombok.NoArgsConstructor; |
||||
import lombok.extern.slf4j.Slf4j; |
||||
import org.apache.http.client.methods.HttpUriRequest; |
||||
import org.apache.http.client.methods.RequestBuilder; |
||||
|
||||
import org.apache.http.entity.ContentType; |
||||
import org.apache.http.entity.StringEntity; |
||||
import org.apache.http.message.BasicHeader; |
||||
import weixin.popular.bean.paymch.Refundquery; |
||||
import weixin.popular.client.LocalHttpClient; |
||||
import weixin.popular.util.MapUtil; |
||||
import weixin.popular.util.SignatureUtil; |
||||
import weixin.popular.util.XMLConverUtil; |
||||
|
||||
import java.nio.charset.StandardCharsets; |
||||
import java.util.Map; |
||||
|
||||
@Slf4j |
||||
@NoArgsConstructor |
||||
public class Client { |
||||
|
||||
public WxRefundQueryRoot refundQuery(Refundquery refundquery, String mchKey) { |
||||
Map<String, String> map = MapUtil.objectToMap(refundquery); |
||||
String sign = SignatureUtil.generateSign(map, refundquery.getSign_type(), mchKey); |
||||
refundquery.setSign(sign); |
||||
String reqXml = XMLConverUtil.convertToXML(refundquery); |
||||
HttpUriRequest httpUriRequest = RequestBuilder.post() |
||||
.setHeader(new BasicHeader("Content-Type", ContentType.APPLICATION_XML.toString())).setUri("https://api.mch.weixin.qq.com/pay/refundquery").setEntity(new StringEntity(reqXml, StandardCharsets.UTF_8)).build(); |
||||
return LocalHttpClient.executeXmlResult(httpUriRequest, WxRefundQueryRoot.class, refundquery.getSign_type(), mchKey); |
||||
} |
||||
|
||||
|
||||
} |
||||
package com.ynxbd.wx.wxfactory.payment.refund; |
||||
|
||||
import com.ynxbd.wx.wxfactory.payment.refund.models.WxRefundQueryRoot; |
||||
import lombok.NoArgsConstructor; |
||||
import lombok.extern.slf4j.Slf4j; |
||||
import org.apache.http.client.methods.HttpUriRequest; |
||||
import org.apache.http.client.methods.RequestBuilder; |
||||
|
||||
import org.apache.http.entity.ContentType; |
||||
import org.apache.http.entity.StringEntity; |
||||
import org.apache.http.message.BasicHeader; |
||||
import weixin.popular.bean.paymch.Refundquery; |
||||
import weixin.popular.client.LocalHttpClient; |
||||
import weixin.popular.util.MapUtil; |
||||
import weixin.popular.util.SignatureUtil; |
||||
import weixin.popular.util.XMLConverUtil; |
||||
|
||||
import java.nio.charset.StandardCharsets; |
||||
import java.util.Map; |
||||
|
||||
@Slf4j |
||||
@NoArgsConstructor |
||||
public class Client { |
||||
|
||||
public WxRefundQueryRoot refundQuery(Refundquery refundquery, String mchKey) { |
||||
Map<String, String> map = MapUtil.objectToMap(refundquery); |
||||
String sign = SignatureUtil.generateSign(map, refundquery.getSign_type(), mchKey); |
||||
refundquery.setSign(sign); |
||||
String reqXml = XMLConverUtil.convertToXML(refundquery); |
||||
HttpUriRequest httpUriRequest = RequestBuilder.post() |
||||
.setHeader(new BasicHeader("Content-Type", ContentType.APPLICATION_XML.toString())).setUri("https://api.mch.weixin.qq.com/pay/refundquery").setEntity(new StringEntity(reqXml, StandardCharsets.UTF_8)).build(); |
||||
return LocalHttpClient.executeXmlResult(httpUriRequest, WxRefundQueryRoot.class, refundquery.getSign_type(), mchKey); |
||||
} |
||||
|
||||
|
||||
} |
||||
@ -0,0 +1,25 @@ |
||||
package com.ynxbd.wx.wxfactory.payment.refund.models; |
||||
|
||||
import lombok.Getter; |
||||
import lombok.NoArgsConstructor; |
||||
import lombok.Setter; |
||||
import lombok.ToString; |
||||
|
||||
@Getter |
||||
@Setter |
||||
@ToString |
||||
@NoArgsConstructor |
||||
public class RefundCoupon { |
||||
private String type; |
||||
private String id; |
||||
private Integer fee; |
||||
private Integer n; |
||||
|
||||
|
||||
public RefundCoupon(String type, String id, Integer fee, Integer n) { |
||||
this.type = type; |
||||
this.id = id; |
||||
this.fee = fee; |
||||
this.n = n; |
||||
} |
||||
} |
||||
@ -1,31 +1,30 @@ |
||||
package com.ynxbd.wx.wxfactory.bean.refund; |
||||
|
||||
import lombok.Getter; |
||||
import lombok.NoArgsConstructor; |
||||
import lombok.Setter; |
||||
import lombok.ToString; |
||||
import weixin.popular.bean.paymch.Coupon; |
||||
|
||||
import java.util.List; |
||||
|
||||
|
||||
@Getter |
||||
@Setter |
||||
@NoArgsConstructor |
||||
@ToString |
||||
public class WxRefundItem { |
||||
private String out_refund_no; |
||||
private String refund_id; |
||||
private String refund_channel; |
||||
private Integer refund_fee; |
||||
private Integer settlement_refund_fee; |
||||
private String coupon_type; |
||||
private Integer coupon_refund_fee; |
||||
private Integer coupon_refund_count; |
||||
private String coupon_refund; |
||||
private String refund_status; |
||||
private String refund_recv_accout; |
||||
private String refund_success_time; |
||||
private Integer n; |
||||
private List<Coupon> coupons; |
||||
} |
||||
package com.ynxbd.wx.wxfactory.payment.refund.models; |
||||
|
||||
import lombok.Getter; |
||||
import lombok.NoArgsConstructor; |
||||
import lombok.Setter; |
||||
import lombok.ToString; |
||||
|
||||
import java.util.List; |
||||
|
||||
|
||||
@Getter |
||||
@Setter |
||||
@NoArgsConstructor |
||||
@ToString |
||||
public class WxRefundItem { |
||||
private String out_refund_no; |
||||
private String refund_id; |
||||
private String refund_channel; |
||||
private Integer refund_fee; |
||||
private Integer settlement_refund_fee; |
||||
private String coupon_type; |
||||
private Integer coupon_refund_fee; |
||||
private Integer coupon_refund_count; |
||||
private String coupon_refund; |
||||
private String refund_status; |
||||
private String refund_recv_accout; |
||||
private String refund_success_time; |
||||
private Integer n; |
||||
private List<RefundCoupon> coupons; |
||||
} |
||||
@ -1,80 +1,79 @@ |
||||
package com.ynxbd.wx.wxfactory.bean.refund; |
||||
|
||||
import lombok.Getter; |
||||
import lombok.NoArgsConstructor; |
||||
import lombok.Setter; |
||||
import lombok.ToString; |
||||
import weixin.popular.bean.DynamicField; |
||||
import weixin.popular.bean.paymch.Coupon; |
||||
import weixin.popular.bean.paymch.MchBase; |
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType; |
||||
import javax.xml.bind.annotation.XmlAccessorType; |
||||
import javax.xml.bind.annotation.XmlRootElement; |
||||
import javax.xml.bind.annotation.XmlTransient; |
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
import java.util.Map; |
||||
|
||||
|
||||
@Getter |
||||
@Setter |
||||
@NoArgsConstructor |
||||
@ToString |
||||
@XmlRootElement(name = "xml") |
||||
@XmlAccessorType(XmlAccessType.FIELD) |
||||
public class WxRefundQueryRoot extends MchBase implements DynamicField { |
||||
private String device_info; |
||||
private String transaction_id; |
||||
private String out_trade_no; |
||||
private Integer total_fee; |
||||
private Integer settlement_total_fee; |
||||
private String fee_type; |
||||
private Integer cash_fee; |
||||
private Integer refund_count; |
||||
private String refund_account; |
||||
private Integer total_refund_count; |
||||
@XmlTransient |
||||
private List<WxRefundItem> items; |
||||
|
||||
public void buildDynamicField(Map<String, String> dataMap) { |
||||
if (dataMap != null) { |
||||
String refund_countStr = dataMap.get("refund_count"); |
||||
if (refund_countStr != null) { |
||||
List<WxRefundItem> list = new ArrayList<>(); |
||||
|
||||
WxRefundItem item; |
||||
List<Coupon> couponList; |
||||
for (int i = 0; i < Integer.parseInt(refund_countStr); ++i) { |
||||
item = new WxRefundItem(); |
||||
item.setOut_refund_no(dataMap.get("out_refund_no_" + i)); |
||||
item.setRefund_id(dataMap.get("refund_id_" + i)); |
||||
item.setRefund_channel(dataMap.get("refund_channel_" + i)); |
||||
item.setRefund_fee(dataMap.get("refund_fee_" + i) == null ? null : Integer.parseInt(dataMap.get("refund_fee_" + i))); |
||||
item.setSettlement_refund_fee(dataMap.get("settlement_refund_fee_" + i) == null ? null : Integer.parseInt(dataMap.get("settlement_refund_fee_" + i))); |
||||
item.setCoupon_type(dataMap.get("coupon_type_" + i)); |
||||
item.setCoupon_refund_fee(dataMap.get("coupon_refund_fee_" + i) == null ? null : Integer.parseInt(dataMap.get("coupon_refund_fee_" + i))); |
||||
item.setCoupon_refund_count(dataMap.get("coupon_refund_count_" + i) == null ? null : Integer.parseInt(dataMap.get("coupon_refund_count_" + i))); |
||||
item.setCoupon_refund(dataMap.get("coupon_refund_" + i)); |
||||
item.setRefund_status(dataMap.get("refund_status_" + i)); |
||||
item.setRefund_recv_accout(dataMap.get("refund_recv_accout_" + i)); |
||||
// 新增
|
||||
item.setRefund_success_time(dataMap.get("refund_success_time_" + i)); |
||||
item.setN(i); |
||||
if (item.getCoupon_refund_count() != null) { |
||||
couponList = new ArrayList<>(); |
||||
|
||||
for (int j = 0; j < item.getCoupon_refund_count(); ++j) { |
||||
Coupon coupon = new Coupon(null, dataMap.get("coupon_refund_id_" + i + "_" + j), dataMap.get("coupon_refund_fee_" + i + "_" + j) == null ? null : Integer.parseInt(dataMap.get("coupon_refund_fee_" + i + "_" + j)), j); |
||||
couponList.add(coupon); |
||||
} |
||||
item.setCoupons(couponList); |
||||
} |
||||
list.add(item); |
||||
} |
||||
this.items = list; |
||||
} |
||||
} |
||||
|
||||
} |
||||
} |
||||
package com.ynxbd.wx.wxfactory.payment.refund.models; |
||||
|
||||
import lombok.Getter; |
||||
import lombok.NoArgsConstructor; |
||||
import lombok.Setter; |
||||
import lombok.ToString; |
||||
import weixin.popular.bean.DynamicField; |
||||
import weixin.popular.bean.paymch.MchBase; |
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType; |
||||
import javax.xml.bind.annotation.XmlAccessorType; |
||||
import javax.xml.bind.annotation.XmlRootElement; |
||||
import javax.xml.bind.annotation.XmlTransient; |
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
import java.util.Map; |
||||
|
||||
|
||||
@Getter |
||||
@Setter |
||||
@NoArgsConstructor |
||||
@ToString |
||||
@XmlRootElement(name = "xml") |
||||
@XmlAccessorType(XmlAccessType.FIELD) |
||||
public class WxRefundQueryRoot extends MchBase implements DynamicField { |
||||
private String device_info; |
||||
private String transaction_id; |
||||
private String out_trade_no; |
||||
private Integer total_fee; |
||||
private Integer settlement_total_fee; |
||||
private String fee_type; |
||||
private Integer cash_fee; |
||||
private Integer refund_count; |
||||
private String refund_account; |
||||
private Integer total_refund_count; |
||||
@XmlTransient |
||||
private List<WxRefundItem> items; |
||||
|
||||
public void buildDynamicField(Map<String, String> dataMap) { |
||||
if (dataMap != null) { |
||||
String refund_countStr = dataMap.get("refund_count"); |
||||
if (refund_countStr != null) { |
||||
List<WxRefundItem> list = new ArrayList<>(); |
||||
|
||||
WxRefundItem item; |
||||
List<RefundCoupon> couponList; |
||||
for (int i = 0; i < Integer.parseInt(refund_countStr); ++i) { |
||||
item = new WxRefundItem(); |
||||
item.setOut_refund_no(dataMap.get("out_refund_no_" + i)); |
||||
item.setRefund_id(dataMap.get("refund_id_" + i)); |
||||
item.setRefund_channel(dataMap.get("refund_channel_" + i)); |
||||
item.setRefund_fee(dataMap.get("refund_fee_" + i) == null ? null : Integer.parseInt(dataMap.get("refund_fee_" + i))); |
||||
item.setSettlement_refund_fee(dataMap.get("settlement_refund_fee_" + i) == null ? null : Integer.parseInt(dataMap.get("settlement_refund_fee_" + i))); |
||||
item.setCoupon_type(dataMap.get("coupon_type_" + i)); |
||||
item.setCoupon_refund_fee(dataMap.get("coupon_refund_fee_" + i) == null ? null : Integer.parseInt(dataMap.get("coupon_refund_fee_" + i))); |
||||
item.setCoupon_refund_count(dataMap.get("coupon_refund_count_" + i) == null ? null : Integer.parseInt(dataMap.get("coupon_refund_count_" + i))); |
||||
item.setCoupon_refund(dataMap.get("coupon_refund_" + i)); |
||||
item.setRefund_status(dataMap.get("refund_status_" + i)); |
||||
item.setRefund_recv_accout(dataMap.get("refund_recv_accout_" + i)); |
||||
// 新增
|
||||
item.setRefund_success_time(dataMap.get("refund_success_time_" + i)); |
||||
item.setN(i); |
||||
if (item.getCoupon_refund_count() != null) { |
||||
couponList = new ArrayList<>(); |
||||
|
||||
for (int j = 0; j < item.getCoupon_refund_count(); ++j) { |
||||
RefundCoupon coupon = new RefundCoupon(null, dataMap.get("coupon_refund_id_" + i + "_" + j), dataMap.get("coupon_refund_fee_" + i + "_" + j) == null ? null : Integer.parseInt(dataMap.get("coupon_refund_fee_" + i + "_" + j)), j); |
||||
couponList.add(coupon); |
||||
} |
||||
item.setCoupons(couponList); |
||||
} |
||||
list.add(item); |
||||
} |
||||
this.items = list; |
||||
} |
||||
} |
||||
|
||||
} |
||||
} |
||||
@ -0,0 +1,51 @@ |
||||
package com.ynxbd.wx.wxfactory.utils; |
||||
|
||||
import com.vdurmont.emoji.EmojiParser; |
||||
|
||||
public class EmojiHelper { |
||||
public static String parseToHtmlHexadecimal(String emoji_str) { |
||||
return EmojiParser.parseToHtmlHexadecimal(emoji_str); |
||||
} |
||||
|
||||
public static String parseToHtmlTag(String emoji_str) { |
||||
if (emoji_str != null) { |
||||
String str = EmojiParser.parseToHtmlHexadecimal(emoji_str); |
||||
return htmlHexadecimalToHtmlTag(str); |
||||
} else { |
||||
return null; |
||||
} |
||||
} |
||||
|
||||
public static String parseToAliases(String emoji_str) { |
||||
return EmojiParser.parseToAliases(emoji_str); |
||||
} |
||||
|
||||
public static String parseToHtmlDecimal(String emoji_str) { |
||||
return EmojiParser.parseToHtmlDecimal(emoji_str); |
||||
} |
||||
|
||||
public static String removeAllEmojis(String emoji_str) { |
||||
return EmojiParser.removeAllEmojis(emoji_str); |
||||
} |
||||
|
||||
public static String htmlHexadecimalToHtmlTag(String emoji_str) { |
||||
return emoji_str != null ? emoji_str.replaceAll("&#x([^;]*);", "<span class='emoji emoji$1'></span>") : null; |
||||
} |
||||
|
||||
public static String parse(String emoji_str, int type) { |
||||
switch (type) { |
||||
case 1: |
||||
return parseToHtmlHexadecimal(emoji_str); |
||||
case 2: |
||||
return parseToHtmlTag(emoji_str); |
||||
case 3: |
||||
return parseToAliases(emoji_str); |
||||
case 4: |
||||
return parseToHtmlDecimal(emoji_str); |
||||
case 5: |
||||
return removeAllEmojis(emoji_str); |
||||
default: |
||||
return null; |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,8 @@ |
||||
package com.ynxbd.wx.wxfactory.utils; |
||||
|
||||
import lombok.extern.slf4j.Slf4j; |
||||
|
||||
@Slf4j |
||||
public class WechatPayHelper { |
||||
|
||||
} |
||||
@ -1,58 +1,60 @@ |
||||
package com.ynxbd.wx.wxfactory.utils; |
||||
|
||||
import com.ynxbd.wx.wxfactory.bean.event.WxEvent; |
||||
import lombok.extern.slf4j.Slf4j; |
||||
import org.apache.commons.codec.digest.DigestUtils; |
||||
|
||||
import javax.servlet.http.HttpServletRequest; |
||||
import java.util.Arrays; |
||||
|
||||
@Slf4j |
||||
public class WxEventHelper { |
||||
|
||||
/** |
||||
* 过滤内容可以直接返回 |
||||
* |
||||
* @param wxEvent 事件 |
||||
*/ |
||||
public static boolean filter(WxEvent wxEvent) { |
||||
String event = wxEvent.getEvent(); |
||||
if (event != null) { |
||||
event = event.toUpperCase(); |
||||
} |
||||
String msgType = wxEvent.getMsgType(); |
||||
if (msgType != null) { |
||||
msgType = msgType.toUpperCase(); |
||||
} |
||||
if ("VIEW".equals(event) && "EVENT".equals(msgType)) { // 点击菜单
|
||||
return true; |
||||
} |
||||
|
||||
if ("TEMPLATESENDJOBFINISH".equals(event)) { // 模板发送作业完成-->直接返回success
|
||||
log.warn("[公众号]模板消息推送 fromUserName={}, msgType={}, eventKey={}, event={}", wxEvent.getFromUserName(), msgType, wxEvent.getEventKey(), event); |
||||
return true; |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
|
||||
/** |
||||
* 消息验签 |
||||
* |
||||
* @param request request |
||||
* @param wxToken wxToken |
||||
*/ |
||||
public static boolean verifyEventSign(HttpServletRequest request, String wxToken) { |
||||
String nonce = request.getParameter("nonce"); |
||||
String timestamp = request.getParameter("timestamp"); |
||||
|
||||
String[] array = new String[]{wxToken, timestamp, nonce}; |
||||
Arrays.sort(array); |
||||
|
||||
StringBuilder sb = new StringBuilder(); |
||||
for (String s : array) { |
||||
sb.append(s); |
||||
} |
||||
return DigestUtils.sha1Hex(sb.toString()).equals(request.getParameter("signature")); |
||||
} |
||||
} |
||||
package com.ynxbd.wx.wxfactory.utils; |
||||
|
||||
import com.ynxbd.wx.wxfactory.base.passivemsg.event.WxEvent; |
||||
import lombok.extern.slf4j.Slf4j; |
||||
import org.apache.commons.codec.digest.DigestUtils; |
||||
|
||||
import javax.servlet.http.HttpServletRequest; |
||||
import java.util.Arrays; |
||||
|
||||
@Slf4j |
||||
public class WxEventHelper { |
||||
|
||||
private static final String TEMPLATE_SEND_JOB_FINISH = "TEMPLATESENDJOBFINISH"; |
||||
|
||||
/** |
||||
* 过滤内容可以直接返回 |
||||
* |
||||
* @param wxEvent 事件 |
||||
*/ |
||||
public static boolean filter(WxEvent wxEvent) { |
||||
String event = wxEvent.getEvent(); |
||||
if (event != null) { |
||||
event = event.toUpperCase(); |
||||
} |
||||
String msgType = wxEvent.getMsgType(); |
||||
if (msgType != null) { |
||||
msgType = msgType.toUpperCase(); |
||||
} |
||||
if ("VIEW".equals(event) && "EVENT".equals(msgType)) { // 点击菜单
|
||||
return true; |
||||
} |
||||
|
||||
if (TEMPLATE_SEND_JOB_FINISH.equals(event)) { // 模板发送作业完成-->直接返回success
|
||||
log.warn("[公众号]模板消息推送 fromUserName={}, msgType={}, eventKey={}, event={}", wxEvent.getFromUserName(), msgType, wxEvent.getEventKey(), event); |
||||
return true; |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
|
||||
/** |
||||
* 消息验签 |
||||
* |
||||
* @param request request |
||||
* @param wxToken wxToken |
||||
*/ |
||||
public static boolean verifyEventSign(HttpServletRequest request, String wxToken) { |
||||
String nonce = request.getParameter("nonce"); |
||||
String timestamp = request.getParameter("timestamp"); |
||||
|
||||
String[] array = new String[]{wxToken, timestamp, nonce}; |
||||
Arrays.sort(array); |
||||
|
||||
StringBuilder sb = new StringBuilder(); |
||||
for (String s : array) { |
||||
sb.append(s); |
||||
} |
||||
return DigestUtils.sha1Hex(sb.toString()).equals(request.getParameter("signature")); |
||||
} |
||||
} |
||||
|
||||
Loading…
Reference in new issue