import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.dom4j.DocumentException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import net.sf.json.JSONObject;
import springcloud.producer.dao.BaseDAO;
import springcloud.producer.service.PayService;
import springcloud.producer.utils.HttpRequest;
import springcloud.producer.utils.MathUtil;
import springcloud.producer.utils.Redis;
import springcloud.producer.utils.RedisConstant;
import springcloud.producer.utils.UtilState;
import springcloud.producer.utils.Utils;
import springcloud.producer.utils.wechat.PayConfigUtil;
import springcloud.producer.utils.wechat.WxPayUtil;
@Service("payService")
public class PayServiceImpl implements PayService{
Logger log = Logger.getLogger(this.getClass());
@Autowired
private BaseDAO<?> baseDAO;
//预支付
@Override
public String miniProgramPay(String fee, String code, Integer companyId,String payType) throws Exception {
// 获取openid
String openId = HttpRequest.sendGet("https://api.weixin.qq.com/sns/jscode2session?appid=" + PayConfigUtil.APP_ID
+ "&secret=" + PayConfigUtil.APP_SECRET + "&js_code=" + code + "&grant_type=authorization_code", code);
JSONObject json = JSONObject.fromObject(openId);
openId = json.getString("openid");
if (StringUtils.isBlank(openId))
throw new Exception("code错误");
// 商户号
String mchId = PayConfigUtil.MCH_ID;
// 支付密钥
String key = "&key=" + PayConfigUtil.API_KEY;
// 交易类型
String tradeType = "JSAPI";
// 随机字符串
String nonceStr = WxPayUtil.getNonceStr();
// 小程序id
String appid = PayConfigUtil.APP_ID;
// 商品订单号(保持唯一性)
String outTradeNo = mchId + WxPayUtil.getNonceStr();
// 支付金额
String totalFee = WxPayUtil.getMoney(fee);
// 发起支付设备ip
String spbillCreateIp = PayConfigUtil.CREATE_IP;
// 商品描述
String body = "小程序充值";
// 附加数据,商户携带的订单的自定义数据 (原样返回到通知中,这类我们需要系统中订单的id 方便对订单进行处理)
// 我们后面需要键值对的形式,所以先装入map
Map<String, String> sParaTemp = new HashMap<String, String>();
sParaTemp.put("appid", appid);
sParaTemp.put("attach", payType);
sParaTemp.put("body", body);
sParaTemp.put("mch_id", mchId);
sParaTemp.put("nonce_str", nonceStr);
sParaTemp.put("notify_url", PayConfigUtil.NOTIFY_URL);
sParaTemp.put("openid", openId);
sParaTemp.put("out_trade_no", outTradeNo);
sParaTemp.put("spbill_create_ip", spbillCreateIp);
sParaTemp.put("total_fee", totalFee);
sParaTemp.put("trade_type", tradeType);
// 去掉空值 跟 签名参数(空值不参与签名,所以需要去掉)
Map<String, String> map = WxPayUtil.paraFilter(sParaTemp);
// 按照 参数=参数值&参数2=参数值2 这样的形式拼接(拼接需要按照ASCII码升序排列)
String mapStr = WxPayUtil.createLinkString(map);
// MD5运算生成签名
String sign = WxPayUtil.sign(mapStr, key, "utf-8").toUpperCase();
sParaTemp.put("sign", sign);
String xml = "<xml>" + "<appid>" + appid + "</appid>" + "<attach>" + payType + "</attach>" + "<body>" + body
+ "</body>" + "<mch_id>" + mchId + "</mch_id>" + "<nonce_str>" + nonceStr + "</nonce_str>"
+ "<notify_url>" + PayConfigUtil.NOTIFY_URL + "</notify_url>" + "<openid>" + openId + "</openid>"
+ "<out_trade_no>" + outTradeNo + "</out_trade_no>" + "<spbill_create_ip>" + spbillCreateIp
+ "</spbill_create_ip>" + "<total_fee>" + totalFee + "</total_fee>" + "<trade_type>" + tradeType
+ "</trade_type>" + "<sign>" + sign + "</sign>" + "</xml>";
// 统一下单url,生成预付id
String url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
String result = WxPayUtil.httpRequest(url, "POST", xml);
String timeStamp = String.valueOf(System.currentTimeMillis() / 1000);
// 得到预支付id
String prepay_id = "";
try {
prepay_id = WxPayUtil.getPayNo(result);
} catch (DocumentException e) {
e.printStackTrace();
}
String packages = "prepay_id=" + prepay_id;
String nonceStr1 = WxPayUtil.getNonceStr();
// 开始第二次签名
String mapStr1 = "appId=" + appid + "&nonceStr=" + nonceStr1 + "&package=prepay_id=" + prepay_id
+ "&signType=MD5&timeStamp=" + timeStamp;
String paySign = WxPayUtil.sign(mapStr1, key, "utf-8").toUpperCase();
Map<String, String> resultMap = new HashMap<>();
resultMap.put("timeStamp", timeStamp);
resultMap.put("nonceStr", nonceStr1);
resultMap.put("package", packages);
resultMap.put("paySign", paySign);
resultMap.put("fee", fee);
resultMap.put("companyId", companyId + "");
Redis.getRedis().addHashMapToJedis(RedisConstant.R_OPENID + outTradeNo, resultMap, RedisConstant.R_OPENID_T,
true);
return Utils.jsonUtilCode(UtilState.SUCCESS, resultMap);
}
}
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import org.apache.commons.codec.digest.DigestUtils;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
public class WxPayUtil {
/**
* 获取随机字符串 (采用截取8位当前日期数 + 4位随机整数)
* @return
*/
public static String getNonceStr() {
//获得当前日期
Date now = new Date();
SimpleDateFormat outFormat = new SimpleDateFormat("yyyyMMddHHmmss");
String currTime = outFormat.format(now);
//截取8位
String strTime = currTime.substring(8, currTime.length());
//得到4位随机整数
int num = 1;
double random = Math.random();
if (random < 0.1) {
random = random + 0.1;
}
for (int i = 0; i < 4; i++) {
num = num * 10;
}
num = (int)random * num;
return strTime + num;
}
/**
* 把数组所有元素排序,并按照“参数=参数值”的模式用“&”字符拼接成字符串
* @param params 需要排序并参与字符拼接的参数组
* @return 拼接后字符串
*/
public static String createLinkString(Map<String, String> params) {
List<String> keys = new ArrayList<String>(params.keySet());
Collections.sort(keys);
String prestr = "";
for (int i = 0; i < keys.size(); i++) {
String key = keys.get(i);
String value = params.get(key);
if (i == keys.size() - 1) {// 拼接时,不包括最后一个&字符
prestr = prestr + key + "=" + value;
} else {
prestr = prestr + key + "=" + value + "&";
}
}
return prestr;
}
/**
* 除去数组中的空值和签名参数
* @param sArray 签名参数组
* @return 去掉空值与签名参数后的新签名参数组
*/
public static Map<String, String> paraFilter(Map<String, String> sArray) {
Map<String, String> result = new HashMap<String, String>();
if (sArray == null || sArray.size() <= 0) {
return result;
}
for (String key : sArray.keySet()) {
String value = sArray.get(key);
if (value == null || value.equals("") || key.equalsIgnoreCase("sign")
|| key.equalsIgnoreCase("sign_type")) {
continue;
}
result.put(key, value);
}
return result;
}
/**
* MD5 加密,转为指定类型
* @param text
* @param key
* @param input_charset
* @return
*/
public static String sign(String text, String key, String input_charset) {
text = text + key;
return DigestUtils.md5Hex(getContentBytes(text, input_charset));
}
public static byte[] getContentBytes(String content, String charset) {
if (charset == null || "".equals(charset)) {
return content.getBytes();
}
try {
return content.getBytes(charset);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("MD5签名过程中出现错误,指定的编码集不对,您目前指定的编码集是:" + charset);
}
}
/**
* 元转换成分
* @param money
* @return
*/
public static String getMoney(String amount) {
if(amount==null){
return "";
}
// 金额转化为分为单位
String currency = amount.replaceAll("\\$|\\¥|\\,", ""); //处理包含, ¥ 或者$的金额
int index = currency.indexOf(".");
int length = currency.length();
Long amLong = 0l;
if(index == -1){
amLong = Long.valueOf(currency+"00");
}else if(length - index >= 3){
amLong = Long.valueOf((currency.substring(0, index+3)).replace(".", ""));
}else if(length - index == 2){
amLong = Long.valueOf((currency.substring(0, index+2)).replace(".", "")+0);
}else{
amLong = Long.valueOf((currency.substring(0, index+1)).replace(".", "")+"00");
}
return amLong.toString();
}
/**
* 解析xml得到 prepay_id 预支付id
* @param result
* @return
* @throws DocumentException
*/
public static String getPayNo(String result) throws DocumentException{
Map<String, String> map = new HashMap<String, String>();
InputStream in = new ByteArrayInputStream(result.getBytes());
SAXReader read = new SAXReader();
Document doc = read.read(in);
//得到xml根元素
Element root = doc.getRootElement();
//遍历 得到根元素的所有子节点
@SuppressWarnings("unchecked")
List<Element> list =root.elements();
for(Element element:list){
//装进map
map.put(element.getName(), element.getText());
}
//返回码
String return_code = map.get("return_code");
//返回信息
String result_code = map.get("result_code");
//预支付id
String prepay_id = "";
//return_code 和result_code 都为SUCCESS 的时候返回 预支付id
if(return_code.equals("SUCCESS")&&result_code.equals("SUCCESS")){
prepay_id = map.get("prepay_id");
}
return prepay_id;
}
/**
* 解析 回调时的xml装进map 返回
* @param result
* @return
* @throws DocumentException
*/
public static Map<String, String> getNotifyUrl(String result) throws DocumentException{
Map<String, String> map = new HashMap<String, String>();
InputStream in = new ByteArrayInputStream(result.getBytes());
SAXReader read = new SAXReader();
Document doc = read.read(in);
//得到xml根元素
Element root = doc.getRootElement();
//遍历 得到根元素的所有子节点
@SuppressWarnings("unchecked")
List<Element> list =root.elements();
for(Element element:list){
//装进map
map.put(element.getName().toString(), element.getText().toString());
}
return map;
}
/**
* 验证签名,判断是否是从微信发过来
* 验证方法:接收微信服务器回调我们url的时候传递的xml中的参数 然后再次加密,看是否与传递过来的sign签名相同
* @param map
* @return
*/
public static boolean verifyWeixinNotify(Map<String, String> map,String key) {
//根据微信服务端传来的各项参数 进行再一次加密后 与传过来的 sign 签名对比
String mapStr = createLinkString(map);
String signOwn = WxPayUtil.sign(mapStr, key, "utf-8").toUpperCase(); //根据微信端参数进行加密的签名
String signWx = map.get("sign"); //微信端传过来的签名
if(signOwn.equals(signWx)){
//如果两个签名一致,验证成功
return true;
}
return false;
}
/**
*
* @param requestUrl请求地址
* @param requestMethod请求方法
* @param outputStr参数
*/
public static String httpRequest(String requestUrl,String requestMethod,String outputStr){
// 创建SSLContext
StringBuffer buffer=null;
try{
URL url = new URL(requestUrl);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod(requestMethod);
conn.setDoOutput(true);
conn.setDoInput(true);
conn.connect();
//往服务器端写内容
if(null !=outputStr){
OutputStream os=conn.getOutputStream();
os.write(outputStr.getBytes("utf-8"));
os.close();
}
// 读取服务器端返回的内容
InputStream is = conn.getInputStream();
InputStreamReader isr = new InputStreamReader(is, "utf-8");
BufferedReader br = new BufferedReader(isr);
buffer = new StringBuffer();
String line = null;
while ((line = br.readLine()) != null) {
buffer.append(line);
}
}catch(Exception e){
e.printStackTrace();
}
return buffer.toString();
}
}
public class PayConfigUtil {
//初始化
public final static String APP_ID = ""; //公众账号appid(改为自己实际的)
public final static String APP_SECRET = "";
public final static String MCH_ID = ""; //商户号(改为自己实际的)
public final static String API_KEY = ""; //(改为自己实际的)key设置路径:微信商户平台(pay.weixin.qq.com)-->账户设置-->API安全-->密钥设置
//有关url
public final static String UFDODER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder";
public final static String NOTIFY_URL = ""; //微信支付回调接口,就是微信那边收到(改为自己实际的)
//企业向个人账号付款的URL
public final static String SEND_EED_PACK_URL = "https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers";
public final static String CREATE_IP = "127.0.0.1";//发起支付ip(改为自己实际的)
}
//支付成功回调
@Override
public void miniProgramNotify(HttpServletRequest request, HttpServletResponse response) throws Exception {
BufferedReader br = new BufferedReader(new InputStreamReader((ServletInputStream) request.getInputStream()));
String line = null;
StringBuilder sb = new StringBuilder();
while ((line = br.readLine()) != null) {
sb.append(line);
}
// sb为微信返回的xml
String notityXml = sb.toString();
String resXml = "";
log.info("接收到的报文:" + notityXml);
Map<String, String> map = WxPayUtil.getNotifyUrl(notityXml);
String returnCode = (String) map.get("return_code");
String outTradeNo = map.get("out_trade_no") + "";
String totalFee = map.get("total_fee") + "";
String attach = map.get("attach") + "";
if ("SUCCESS".equals(returnCode)) {
// 验证签名是否正确
if (returnCode.equals("SUCCESS")) {
/** 此处添加自己的业务逻辑代码start **/
}
} else {
resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"
+ "<return_msg><![CDATA[报文为空]]></return_msg>" + "</xml> ";
}
resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>" + "<return_msg><![CDATA[报文为空]]></return_msg>"
+ "</xml> ";
log.info("微信支付回调数据结束");
BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
out.write(resXml.getBytes());
out.flush();
out.close();
}