java微信公众号支付示例详解

网友投稿 256 2023-02-10

java微信公众号支付示例详解

开始之前,先准备好:appid、商家号、商户密匙。

工具类:

MD5Util.java

package com.yiexpress.core.utils.wechat;

import java.security.MessageDigest;

/**

* MD5工具类

*/

public class MD5Util {

public final static String MD5(String s) {

char hexDigits[]={'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};

try {

byte[] btInput = s.getBytes();

MessageDigest mdInst = MessageDigest.getInstance("MD5");

mdInst.update(btInput);

byte[] md = mdInst.digest();

int j = md.length;

char str[] = new char[j * 2];

int k = 0;

for (int i = 0; i < j; i++) {

byte byte0 = md[i];

str[k++] = hexDigits[byte0 >>> 4 & 0xf];

str[k++] = hexDigits[byte0 & 0xf];

}

String md5Str = new String(str);

return md5Str;

} catch (Exception e) {

e.printStackTrace();

return null;

}

}

}

SapUtils.java

package com.yiexpress.core.utils;

import java.lang.reflect.*;

import java.util.List;

import java.io.IOException;

import java.io.StringWriter;

import org.dom4j.Document;

import org.dom4j.DocumentHelper;

import org.dom4j.Element;

import org.dom4j.io.OutputFormat;

import org.dom4j.io.XMLWriter;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.util.StringUtils;

public class SapUtils {

private static final Logger logger = LoggerFactory.getLogger(SapUtils.class);

/** 根据反射对javabean转成xml文件的格式

* 以类名为第一标签,所有属性作为第二节点,并放入对应的值,如果属性为空 就不放入该熟悉

* @param dto 传入的对象

* @param operationName 操作名称

* @return

*/

public static String formatToXml(Object dto,String operationName){

logger.info("解析当前类{}为指定的xml文档格式的数据",dto.getClass().getName());

logger.info("当前的同步方法是,{}",operationName);

String result = null;

Field fields[]=dto.getClass().getDeclaredFields();//dto 是实体类名称

//DocumentHelper提供了创建Document对象的方法

Document document = DocumentHelper.createDocument();

//添加节点信息

String className=dto.getClass().getName();

// 操作的名称

Element rootElement = document.addElement(operationName);

try {

Field.setAccessible(fields, true);

for (int i = 0; i < fields.length; i++) {

//添加节点信息

if(!StringUtils.isEmpty(fields[i].get(dto))){

Class> type = fields[i].getType();

// 如果是list

if(type == List.class){

String listName = fields[i].getName();

createElement(rootElement, fields[i].get(dto),listName);

}

else{

Element element = rootElement.addElement(fields[i].getName());

element.setText((String) fields[i].get(dto));

}

}

}

// 设置XML文档格式

OutputFormat outputFormat = OutputFormat.createPrettyPrint();

// 设置XML编码方式,即是用指定的编码方式保存XML文档到字符串(String),这里也可以指定为GBK或是ISO8859-1

outputFormat.setEncoding("UTF-8");

// outputFormat.setSuppressDeclaration(true); //是否生产xml头

outputFormat.setIndent(true); //设置是否缩进

outputFormat.setIndent(" "); //以四个空格方式实现缩进

outputFormat.setNewlines(true); //设置是否换行

StringWriter stringWriter =null;

// Writer fileWriter =null;

// xmlWriter是用来把XML文档写入字符串的(工具)

XMLWriter xmlWriter = null;

try {

// stringWriter字符串是用来保存XML文档的

stringWriter = new StringWriter();

// fileWriter = new FileWriter("D:\\modu11le.xml");

// xmlWriter是用来把XML文档写入字符串的(工具)

xmlWriter = new XMLWriter(stringWriter, outputFormat);

// 把创建好的XML文档写入字符串

xmlWriter.write(document);

//fileWriter.write(stringWriter.toString());

result=stringWriter.toString();

} catch (IOException e) {

logger.error("写入数据失败");

throw new RuntimeException("写入数据失败"+e);

}finally{

try {

if(xmlWriter!=null){

xmlWriter.flush();

xmlWriter.close();

}

/* if(fileWriter!=null){

fileWriter.flush();

fileWriter.close();

}*/

} catch (IOException e) {

logger.error("关闭输出流出错");

throw new RuntimeException("关闭输出流出错"+e);

}

}

} catch (Exception e) {

logger.error("添加xml的节点失败"+e);

}

logger.error("转换xml结束");

return result;

}

/**

* 添加类中的list

* @param element

* @param object

* @param name

* @return

* @throws IllegalArgumentException

* @throws IllegalAccessException

*/

public static Element createElement(Element element ,Object object,String name ) throws IllegalArgumentException, IllegalAccessException{

Element nameElement = element.addElement(name);

List info = (List)object;

// 添加row的标签

Element rowElement = nameElement.addElement("row");

// 添加 对象的熟悉

Field fields[]=info.get(j).getClass().getDeclaredFields();//dto 是实体类名称

Field.setAccessible(fields, true);

for (int i = 0; i < fields.length; i++) {

//添加节点信息

if(!StringUtils.isEmpty(fields[i].get(info.get(j)))){

Element childElement = rowElement.addElement(fields[i].getName());

childElement.setText((String) fields[i].get(info.get(j)));

}

}

}

return element;

}

}

UnifiedOrderRequest.java

package com.yiexpress.core.utils.wechat;

public class UnifiedOrderRequest {

private String aphttp://pid;// 公众账号ID

private String mch_id;//商户号

private String device_info; //设备号 否

private String nonce_str;//随机字符串

private String sign;//签名

private String sign_type;//签名类型

private String body;//商品描述

private String detail;//商品详情

private String attach;//附加数据

private String out_trade_no;//商户订单号

private String fee_type;//标价币种

private String total_fee;//标价金额

private String spbill_create_ip;//终端IP

private String time_start;//交易起始时间

private String time_expire;//交易结束时间

private String goods_tag;//订单优惠标记

private String notify_url;//通知地址

private String trade_type;//交易类型

private String product_id;//商品ID

private String limit_pay;//指定支付方式

private String openid;//用户标识

public String getAppid() {

return appid;

}

public void setAppid(String appid) {

this.appid = appid;

}

public String getMch_id() {

return mch_id;

}

public void setMch_id(String mch_id) {

this.mch_id = mch_id;

}

public String getDevice_info() {

return device_info;

}

public void setDevice_info(String device_info) {

this.device_info = device_info;

}

public String getNonce_str() {

return nonce_str;

}

public void setNonce_str(String nonce_str) {

this.nonce_str = nonce_str;

}

public String getSign() {

return sign;

}

public void setSign(String sign) {

this.sign = sign;

}

public String getSign_type() {

return sign_type;

}

public void setSign_type(String sign_type) {

this.sign_type = sign_type;

}

public String getBody() {

return body;

}

public void setBody(String body) {

this.body = body;

}

public String getDetail() {

return detail;

}

public void setDetail(String detail) {

this.detail = detail;

}

public String getAttach() {

return attach;

}

public void setAttach(String attach) {

this.attach = attach;

}

public String getOut_trade_no() {

return out_trade_no;

}

public void setOut_trade_no(String out_trade_no) {

this.out_trade_no = out_trade_no;

}

public String getFee_type() {

return fee_type;

}

public void setFee_type(String fee_type) {

this.fee_type = fee_type;

}

public String getTotal_fee() {

return total_fee;

}

public void setTotal_fee(String total_fee) {

this.total_fee = total_fee;

}

public String getSpbill_create_ip() {

return spbill_create_ip;

}

public void setSpbill_create_ip(String spbill_create_ip) {

this.spbill_create_ip = spbill_create_ip;

}

public String getTime_start() {

return time_start;

}

public void setTime_start(String time_start) {

this.time_start = time_start;

}

public String getTime_expire() {

return time_expire;

}

public void setTime_expire(String time_expire) {

this.time_expire = time_expire;

}

public String getGoods_tag() {

return goods_tag;

}

public void setGoods_tag(String goods_tag) {

this.goods_tag = goods_tag;

}

public String getNotify_url() {

return notify_url;

}

public void setNotify_url(String notify_url) {

this.notify_url = notify_url;

}

public String getTrade_type() {

return trade_type;

}

public void setTrade_type(String trade_type) {

this.trade_type = trade_type;

}

public String getProduct_id() {

return product_id;

}

public void setProduct_id(String product_id) {

this.product_id = product_id;

}

public String getLimit_pay() {

return limit_pay;

}

public void setLimit_pay(String limit_pay) {

this.limit_pay = limit_pay;

}

public String getOpenid() {

return openid;

}

public void setOpenid(String openid) {

this.openid = openid;

}

}

UnifiedOrderRespose.java

package com.yiexpress.core.utils.wechat;

public class UnifiedOrderRespose {

private String return_code; //返回状态码

private String return_msg; //返回信息

private String appid; //公众账号ID

private String mch_id; //商户号

private String device_info; //设备号

private String nonce_str; //随机字符串

private String sign; //签名

private String result_code; //业务结果

private String err_code; //错误代码

private String err_code_des; //错误代码描述

private String trade_type; //交易类型

private String prepay_id; //预支付交易会话标识

private String code_url; //二维码链接

public String getReturn_code() {

return return_code;

}

public void setReturn_code(String return_code) {

this.return_code = return_code;

}

public String getReturn_msg() {

return return_msg;

}

public void setReturn_msg(String return_msg) {

this.return_msg = return_msg;

}

public String getAppid() {

return appid;

}

public void setAppid(String appid) {

this.appid = appid;

}

public String getMch_id() {

return mch_id;

}

public void setMch_id(String mch_id) {

this.mch_id = mch_id;

}

public String getDevice_info() {

return device_info;

}

public void setDevice_info(String device_info) {

this.device_info = device_info;

}

public String getNonce_str() {

return nonce_str;

}

public void setNonce_str(String nonce_str) {

this.nonce_str = nonce_str;

}

public String getSign() {

return sign;

}

public void setSign(String sign) {

this.sign = sign;

}

public String getResult_code() {

return result_code;

}

public void setResult_code(String result_code) {

this.result_code = result_code;

}

public String getErr_code() {

return err_code;

}

public void setErr_code(String err_code) {

this.err_code = err_code;

}

public String getErr_code_des() {

return err_code_des;

}

public void setErr_code_des(String err_code_des) {

this.err_code_des = err_code_des;

}

public String getTrade_type() {

return trade_type;

}

public void setTrade_type(String trade_type) {

this.trade_type = trade_type;

}

public String getPrepay_id() {

return prepay_id;

}

public void setPrepay_id(String prepay_id) {

this.prepay_id = prepay_id;

}

public String getCode_url() {

return code_url;

}

public void setCode_url(String code_url) {

this.code_url = code_url;

}

}

WXPayConstants.java

package com.yiexpress.core.utils.wechat;

public class WXPayConstants {

public enum SignType {

MD5, HMACSHA256

}

public static final String FAIL = "FAIL";

public static final String SUCCESS = "SUCCESS";

public static final String HMACSHA256 = "HMAC-SHA256";

public static final String MD5 = "MD5";

public static final String FIELD_SIGN = "sign";

public static final String FIELD_SIGN_TYPE = "sign_type";

}

WXPayUtil.java

package com.yiexpress.core.utils.wechat;

import java.io.BufferedOutputStream;

import java.io.BufferedReader;

import java.io.ByteArrayInputStream;

import java.io.InputStream;

import java.io.InputStreamReader;

import java.io.StringWriter;

import java.io.Writer;

import java.net.HttpURLConnection;

import java.net.URL;

import java.util.*;

import java.security.MessageDigest;

import org.w3c.dom.Node;

import org.w3c.dom.NodeList;

import com.yiexpress.core.utils.SapUtils;

import com.yiexpress.core.utils.XmlUtil;

import com.yiexpress.core.utils.wechat.WXPayConstants.SignType;

import javax.crypto.Mac;

import javax.crypto.spec.SecretKeySpec;

import javax.xml.parsers.DocumentBuilder;

import javax.xml.parsers.DocumentBuilderFactory;

import javax.xml.transform.OutputKeys;

import javax.xml.transform.Transformer;

import javax.xml.transform.TransformerFactory;

import javax.xml.transform.dom.DOMSource;

import javax.xml.transform.stream.StreamResult;

import org.jdom2.Document;

import org.jdom2.Element;

import org.jdom2.input.SAXBuilder;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

/**

* 支付工具类

*/

public class WXPayUtil {

private static Logger log = LoggerFactory.getLogger(WXPayUtil.class);

/**

* 生成订单对象信息

* @param orderId 订单号

* @param appId 微信appId

* @param mch_id 微信分配的商户ID

* @param body 支付介绍主体

* @param price 支付价格(放大100倍)

* @param spbill_create_ip 终端IP

* @param notify_url 异步直接结果通知接口地址

* @param noncestr

* @return

*/

public static Map createOrderInfo(Map requestMap,String shopKey) {

//生成订单对象

UnifiedOrderRequest unifiedOrderRequest = new UnifiedOrderRequest();

unifiedOrderRequest.setAppid(requestMap.get("appId"));//公众账号ID

unifiedOrderRequest.setBody(requestMap.get("body"));//商品描述

unifiedOrderRequest.setMch_id(requestMap.get("mch_id"));//商户号

unifiedOrderRequest.setNonce_str(requestMap.get("noncestr"));//随机字符串

unifiedOrderRequest.setNotify_url(requestMap.get("notify_url"));//通知地址

unifiedOrderRequest.setOpenid(requestMap.get("userWeixinOpenId"));

unifiedOrderRequest.setDetail(requestMap.get("detail"));//详情

unifiedOrderRequest.setOut_trade_no(requestMap.get("out_trade_no"));//商户订单号

unifiedOrderRequest.setSpbill_create_ip(requestMap.get("spbill_create_ip"));//终端IP

unifiedOrderRequest.setTotal_fee(requestMap.get("payMoney")); //金额需要扩大100倍:1代表支付时是0.01

SortedMap packageParams = new TreeMap();

packageParams.put("appid", unifiedOrderRequest.getAppid());

packageParams.put("bodZHJgTSy", unifiedOrderRequest.getBody());

packageParams.put("mch_id", unifiedOrderRequest.getMch_id());

packageParams.put("nonce_str", unifiedOrderRequest.getNonce_str());

packageParams.put("notify_url", unifiedOrderRequest.getNotify_url());

packageParams.put("openid", unifiedOrderRequest.getOpenid());

packageParams.put("detail", unifiedOrderRequest.getDetail());

packageParams.put("out_trade_no", unifiedOrderRequest.getOut_trade_no());

packageParams.put("spbill_create_ip", unifiedOrderRequest.getSpbill_create_ip());

packageParams.put("total_fee", unifiedOrderRequest.getTotal_fee());

packageParams.put("trade_type", unifiedOrderRequest.getTrade_type());

try {

unifiedOrderRequest.setSign(generateSignature(packageParams,shopKey));//签名

} catch (Exception e) {

e.printStackTrace();

}

//将订单对象转为xml格式

String orderstr=SapUtils.formatToXml(unifiedOrderRequest,"xml").replace("","");

log.debug("封装好的统一下单请求数据:"+orderstr.replace("__", "_"));

Map responseMap = new HashMap();

responseMap.put("orderInfo_toString", orderstr.replace("__", "_"));

responseMap.put("unifiedOrderRequest",unifiedOrderRequest);

return responseMap;

}

public static void main(String[] args) {

// UnifiedOrderRequest ut=new UnifiedOrderRequest();

// ut.setAppid("wx1234156789");

// ut.setBody("内容body");

// ut.setMch_id("商户号");

// ut.setNonce_str("随机字符串");

// ut.setNotify_url("回调地址");

// ut.setOpenid("openid");

// ut.setDetail("详情");

// ut.setOut_trade_no("订单号");

// ut.setSpbill_create_ip("终端IP");

// ut.setTotal_fee("金额");

// ut.setTrade_type("调用类型JSAPI");

// System.out.println("---"+SapUtils.formatToXml(ut,"xml")+"---");

// UnifiedOrderRequest unifiedOrderRequest = new UnifiedOrderRequest();

// unifiedOrderRequest.setAppid("dsfsdf");//公众账号ID

// unifiedOrderRequest.setBody("sdfsdf");//商品描述

// unifiedOrderRequest.setMch_id("sdfsd");//商户号

// unifiedOrderRequest.setNonce_str("dfsd");//随机字符串

// unifiedOrderRequest.setNotify_url("sdfdsf");//通知地址

// unifiedOrderRequest.setOpenid("sdfsdf");

//

//

// System.out.println("---"+SapUtils.formatToXml(unifiedOrderRequest,"xml").replace("","")+"---");

// String str="dsfsdfsdfsddfsdsdfsdfsdfdsfJSAPIsdfsdf";

// UnifiedOrderRequest s=SapUtils.getBeanByxml(str,UnifiedOrderRequest.class);

// System.out.println(s.getAppid()+"---"+s.getMch_id()+"--"+s.getFee_type());

}

/**

* 生成签名

* @param appid_value

* @param mch_id_value

* @param productId

* @param nonce_str_value

* @param trade_type

* @param notify_url

* @param spbill_create_ip

* @param total_fee

* @param out_trade_no

* @return

*/

private static String createSign(UnifiedOrderRequest unifiedOrderRequest,String shopKey) {

//根据规则创建可排序的map集合

SortedMap packageParams = new TreeMap();

packageParams.put("appid", unifiedOrderRequest.getAppid());

packageParams.put("body", unifiedOrderRequest.getBody());

packageParams.put("mch_id", unifiedOrderRequest.getMch_id());

packageParams.put("nonce_str", unifiedOrderRequest.getNonce_str());

packageParams.put("notify_url", unifiedOrderRequest.getNotify_url());

packageParams.put("out_trade_no", unifiedOrderRequest.getOut_trade_no());

packageParams.put("spbill_create_ip", unifiedOrderRequest.getSpbill_create_ip());

packageParams.put("trade_type", unifiedOrderRequest.getTrade_type());

packageParams.put("total_fee", unifiedOrderRequest.getTotal_fee());

StringBuffer sb = new StringBuffer();

Set es = packageParams.entrySet();//字典序

Iterator it = es.iterator();

while (it.hasNext()) {

Map.Entry entry = (Map.Entry) it.next();

String k = (String) entry.getKey();

String v = (String) entry.getValue();

//为空不参与签名、参数名区分大小写

if (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) {

sb.append(k + "=" + v + "&");

}

}

//第二步拼接key,key设置路径:微信商户平台(pay.weixin.qq.com)-->账户设置-->API安全-->密钥设置

sb.append("key="+shopKey);

String sign = MD5Util.MD5(sb.toString()).toUpperCase();//MD5加密

log.error("方式一生成的签名="+sign);

return sign;

}

//xml解析

public static SortedMap doXMLParseWithSorted(String strxml) throws Exception {

strxml = strxml.replaceFirst("encoding=\".*\"", "encoding=\"UTF-8\"");

if(null == strxml || "".equals(strxml)) {

return null;

}

SortedMap m = new TreeMap();

InputStream in = new ByteArrayInputStream(strxml.getBytes("UTF-8"));

SAXBuilder builder = new SAXBuilder();

Document doc = builder.build(in);

Element root = doc.getRootElement();

List list = root.getChildren();

Iterator it = list.iterator();

while(it.hasNext()) {

Element e = (Element) it.next();

String k = e.getName();

String v = "";

List children = e.getChildren();

if(children.isEmpty()) {

v = e.getTextNormalize();

} else {

v = getChildrenText(children);

}

m.put(k, v);

}

//关闭流

in.close();

return m;

}

public static String getChildrenText(List children) {

StringBuffer sb = new StringBuffer();

if(!children.isEmpty()) {

Iterator it = children.iterator();

while(it.hasNext()) {

Element e = (Element) it.next();

String name = e.getName();

String value = e.getTextNormalize();

List list = e.getChildren();

sb.append("<" + name + ">");

if(!list.isEmpty()) {

sb.append(getChildrenText(list));

}

sb.append(value);

sb.append("" + name + ">");

}

}

return sb.toString();

}

/**

* 调统一下单API

* @param orderInfo

* @return

*/

public static UnifiedOrderRespose httpOrder(String orderInfo,int index) {

//统一下单接口地址 自动适应 1中国境内 2东南亚 3其他

String[] urlList={"https://api.mch.weixin.qq.com/pay/unifiedorder","https://apihk.mch.weixin.qq.com/pay/unifiedorder"

,"https://apius.mch.weixin.qq.com/pay/unifiedorder "};

//String url = "https://api.mch.weixin.qq.com/pay/unifiedorder";

try {

HttpURLConnection conn = (HttpURLConnection) new URL(urlList[index]).openConnection();

//加入数据

conn.setRequestMethod("POST");

conn.setDoOutput(true);

BufferedOutputStream buffOutStr = new BufferedOutputStream(conn.getOutputStream());

buffOutStr.write(orderInfo.getBytes("UTF-8"));

buffOutStr.flush();

buffOutStr.close();

//获取输入流

BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"));

String line = null;

StringBuffer sb = new StringBuffer();

while((line = reader.readLine())!= null){

sb.append(line);

}

//xml转对象

UnifiedOrderRespose unifiedOrderRespose =XmlUtil.getBeanByxml(sb.toString(),UnifiedOrderRespose.class);

return unifiedOrderRespose;

} catch (Exception e) {

e.printStackTrace();

}

return null;

}

/**

* XML格式字符串转换为Map

*

* @param strXML XML字符串

* @return XML数据转换后的Map

* @throws Exception

*/

public static Map xmlToMap(String strXML) throws Exception {

try {

Map data = new HashMap();

DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();

DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();

InputStream stream = new ByteArrayInputStream(strXML.getBytes("UTF-8"));

org.w3c.dom.Document doc = documentBuilder.parse(stream);

doc.getDocumentElement().normalize();

NodeList nodeList = doc.getDocumentElement().getChildNodes();

for (int idx = 0; idx < nodeList.getLength(); ++idx) {

Node node = nodeList.item(idx);

if (node.getNodeType() == Node.ELEMENT_NODE) {

org.w3c.dom.Element element = (org.w3c.dom.Element) node;

data.put(element.getNodeName(), element.getTextContent());

}

}

try {

stream.close();

} catch (Exception ex) {

// do nothing

}

return data;

} catch (Exception ex) {

WXPayUtil.getLogger().warn("Invalid XML, can not convert to map. Error message: {}. XML content: {}", ex.getMessage(), strXML);

throw ex;

}

}

/**

* 将Map转换为XML格式的字符串

*

* @param data Map类型数据

* @return XML格式的字符串

* @throws Exception

*/

public static String mapToXml(Map data) throws Exception {

DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();

DocumentBuilder documentBuilder= documentBuilderFactory.newDocumentBuilder();

org.w3c.dom.Document document = documentBuilder.newDocument();

org.w3c.dom.Element root = document.createElement("xml");

document.appendChild(root);

for (String key: data.keySet()) {

String value = data.get(key);

if (value == null) {

value = "";

}

value = value.trim();

org.w3c.dom.Element filed = document.createElement(key);

filed.appendChild(document.createTextNode(value));

root.appendChild(filed);

}

TransformerFactory tf = TransformerFactory.newInstance();

Transformer transformer = tf.newTransformer();

DOMSource source = new DOMSource(document);

transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");

transformer.setOutputProperty(OutputKeys.INDENT, "yes");

StringWriter writer = new StringWriter();

StreamResult result = new StreamResult(writer);

transformer.transform(source, result);

String output = writer.getBuffer().toString(); //.replaceAll("\n|\r", "");

try {

writer.close();

}

catch (Exception ex) {

}

return output;

}

/**

* 生成带有 sign 的 XML 格式字符串

*

* @param data Map类型数据

* @param key API密钥

* @return 含有sign字段的XML

*/

public static String generateSignedXml(final Map data, String key) throws Exception {

return generateSignedXml(data, key, SignType.MD5);

}

/**

* 生成带有 sign 的 XML 格式字符串

*

* @param data Map类型数据

* @param key API密钥

* @param signType 签名类型

* @return 含有sign字段的XML

*/

public static String generateSignedXml(final Map data, String key, SignType signType) throws Exception {

String sign = generateSignature(data, key, signType);

data.put(WXPayConstants.FIELD_SIGN, sign);

return mapToXml(data);

}

/**

* 判断签名是否正确

*

* @param xmlStr XML格式数据

* @param key API密钥

* @return 签名是否正确

* @throws Exception

*/

public static boolean isSignatureValid(String xmlStr, String key) throws Exception {

Map data = xmlToMap(xmlStr);

if (!data.containsKey(WXPayConstants.FIELD_SIGN) ) {

return false;

}

String sign = data.get(WXPayConstants.FIELD_SIGN);

return generateSignature(data, key).equals(sign);

}

/**

* 判断签名是否正确,必须包含sign字段,否则返回false。使用MD5签名。

*

* @param data Map类型数据

* @param key API密钥

* @return 签名是否正确

* @throws Exception

*/

public static boolean isSignatureValid(Map data, String key) throws Exception {

return isSignatureValid(data, key, SignType.MD5);

}

/**

* 判断签名是否正确,必须包含sign字段,否则返回false。

*

* @param data Map类型数据

* @param key API密钥

* @param signType 签名方式

* @return 签名是否正确

* @throws Exception

*/

public static boolean isSignatureValid(Map data, String key, SignType signType) throws Exception {

if (!data.containsKey(WXPayConstants.FIELD_SIGN) ) {

return false;

}

String sign = data.get(WXPayConstants.FIELD_SIGN);

return generateSignature(data, key, signType).equals(sign);

}

/**

* 生成签名

*

* @param data 待签名数据

* @param key API密钥

* @return 签名

*/

public static String generateSignature(final Map data, String key) throws Exception {

return generateSignature(data, key, SignType.MD5);

}

/**

* 生成签名. 注意,若含有sign_type字段,必须和signType参数保持一致。

*

* @param data 待签名数据

* @param key API密钥

* @param signType 签名方式

* @return 签名

*/

public static String generateSignature(final Map data, String key, SignType signType) throws Exception {

Set keySet = data.keySet();

String[] keyArray = keySet.toArray(new String[keySet.size()]);

Arrays.sort(keyArray);

StringBuilder sb = new StringBuilder();

for (String k : keyArray) {

if (k.equals(WXPayConstants.FIELD_SIGN)) {

continue;

}

if (data.get(k).trim().length() > 0) // 参数值为空,则不参与签名

sb.append(k).append("=").append(data.get(k).trim()).append("&");

}

sb.append("key=").append(key);

if (SignType.MD5.equals(signType)) {

return MD5(sb.toString()).toUpperCase();

}

else if (SignType.HMACSHA256.equals(signType)) {

return HMACSHA256(sb.toString(), key);

}

else {

log.error("获取签名失败,失败原因:"+String.format("Invalid sign_type: %s", signType));

throw new Exception(String.format("Invalid sign_type: %s", signType));

}

}

/**

* 获取随机字符串 Nonce Str

* @return String 随机字符串

*/

public static String generateNonceStr() {

return UUID.randomUUID().toString().replaceAll("-", "").substring(0, 32);

}

/**

* Map转xml数据

*/

public static String GetMapToXML(Map param){

StringBuffer sb = new StringBuffer();

sb.append("");

for (Map.Entry entry : param.entrySet()) {

sb.append("<"+ entry.getKey() +">");

sb.append(entry.getValue());

sb.append(""+ entry.getKey() +">");

}

sb.append("");

return sb.toString();

}

/**

* 生成 MD5

* @param data 待处理数据

* @return MD5结果

*/

public static String MD5(String data) throws Exception {

java.security.MessageDigest md = MessageDigest.getInstance("MD5");

byte[] array = md.digest(data.getBytes("UTF-8"));

StringBuilder sb = new StringBuilder();

for (byte item : array) {

sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));

}

return sb.toString().toUpperCase();

}

/**

* 生成 HMACSHA256

* @param data 待处理数据

* @param key 密钥

* @return 加密结果

* @throws Exception

*/

public static String HMACSHA256(String data, String key) throws Exception {

Mac sha256_HMAC = Mac.getInstance("HmacSHA256");

SecretKeySpec secret_key = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA256");

sha256_HMAC.init(secret_key);

byte[] array = sha256_HMAC.doFinal(data.getBytes("UTF-8"));

StringBuilder sb = new StringBuilder();

for (byte item : array) {

sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));

}

return sb.toString().toUpperCase();

}

/**

* 日志

* @return

*/

public static Logger getLogger() {

Logger logger = LoggerFactory.getLogger("wxpay java sdk");

return logger;

}

/**

* 获取当前时间戳,单位秒

* @return

*/

public static long getCurrentTimestamp() {

return System.currentTimeMillis()/1000;

}

/**

* 获取当前时间戳,单位毫秒

* @return

*/

public static long getCurrentTimestampMs() {

return System.currentTimeMillis();

}

/**

* 生成 uuid, 即用来标识一笔单,也用做 nonce_str

* @return

*/

public static String generateUUID() {

return UUID.randomUUID().toString().replaceAll("-", "").substring(0, 32);

}

/**

* 支付签名

* @param timestamp

* @param noncestr

* @param packages

* @return

* @throws UnsupportedEncodingException

*/

public static String paySign(String timestamp, String noncestr,String packages,String appId){

Map paras = new HashMap();

paras.put("appid", appId);

paras.put("timestamp", timestamp);

paras.put("noncestr", noncestr);

paras.put("package", packages);

paras.put("signType", "MD5");

StringBuffer sb = new StringBuffer();

Set es = paras.entrySet();//字典序

Iterator it = es.iterator();

while (it.hasNext()) {

Map.Entry entry = (Map.Entry) it.next();

String k = (String) entry.getKey();

String v = (String) entry.getValue();

//为空不参与签名、参数名区分大小写

if (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) {

sb.append(k + "=" + v + "&");

}

}

String sign = MD5Util.MD5(sb.toString()).toUpperCase();//MD5加密

return sign;

}

}

XmlUtil.java

package com.yiexpress.core.utils;

import java.io.StringReader;

import java.lang.reflect.Field;

import java.util.Date;

import org.dom4j.Document;

import org.dom4j.Element;

import org.dom4j.io.SAXReader;

import org.xml.sax.InputSource;

public class XmlUtil{

/**

* json 数据转换对象

*

* @param Element

* 要转换的Element数据

* @param pojo

* 要转换的目标对象类型

* @return 转换的目标对象

* @throws Exception

* 转换失败

*/

@SuppressWarnings("rawtypes")

public static Object fromXmlToBean(Element rootElt, Class pojo) throws Exception{

// 首先得到pojo所定义的字段

Field[] fields = pojo.getDeclaredFields();

// 根据传入的Class动态生成pojo对象

Object obj = pojo.newInstance();

for (Field field : fields)

{

// 设置字段可访问(必须,否则报错)

field.setAccessible(true);

// 得到字段的属性名

String name = field.getName();

// 这一段的作用是如果字段在Element中不存在会抛出异常,如果出异常,则跳过。

try

{

rootElt.elementTextTrim(name);

}

catch (Exception ex)

{

continue;

}

if (rootElt.elementTextTrim(name) != null && !"".equals(rootElt.elementTextTrim(name)))

{

// 根据字段的类型将值转化为相应的类型,并设置到生成的对象中。

if (field.getType().equals(Long.class) || field.getType().equals(long.class))

{

field.set(obj, Long.parseLong(rootElt.elementTextTrim(name)));

}

else if (field.getType().equals(String.class))

{

field.set(obj, rootElt.elementTextTrim(name));

}

else if (field.getType().equals(Double.class) || field.getType().equals(double.class))

{

field.set(obj, Double.parseDouble(rootElt.elementTextTrim(name)));

}

else if (field.getType().equals(Integer.class) || field.getType().equals(int.class))

{

field.set(obj, Integer.parseInt(rootElt.elementTextTrim(name)));

}

else if (field.getType().equals(java.util.Date.class))

{

field.set(obj, Date.parse(rootElt.elementTextTrim(name)));

}

else

{

continue;

}

}

}

return obj;

}

/**

* 把xml格式转化为指定对象

*

* @param xml

* @return

*/

@SuppressWarnings("unchecked")

public static T getBeanByxml(String xml, Class valueType) {

T person = null;

InputSource in = new InputSource(new StringReader(xml));

in.setEncoding("UTF-8");

SAXReader reader = new SAXReader();

Document document;

try {

document = reader.read(in);

Element root = document.getRootElement();

person = (T) XmlUtil.fromXmlToBean(root, valueType);

} catch (Exception e) {

// TODO Auto-generated catch block

e.printStackTrace();

System.out.println("数据解析错误");

}

return person;

}

}

获取预支付ID和签名的controller

package com.yiexpress.jerry.controller.ewe.wechat;

import java.util.HashMap;

import java.util.Map;

import java.util.SortedMap;

import java.util.TreeMap;

import javax.servlet.http.HttpServletRequest;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.stereotype.Controller;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RequestParam;

import org.springframework.web.bind.annotation.ResponseBody;

import com.yiexpress.core.utils.wechat.UnifiedOrderRequest;

import com.yiexpress.core.utils.wechat.UnifiedOrderRespose;

import com.yiexpress.core.utils.wechat.WXPayUtil;

/**

* 微信支付controller

*/

@Controller

@RequestMapping(value = "/wxpay")

public class WXPayController{

private static final Logger LOGGER = LoggerFactory.getLogger(WXPayController.class);

private String appId="公总号 appid";//公总号 appid

private String mchId="商家号";//商家号

private String apiKey="商户密匙";//商户密匙

/**

* 获取终端IP

* @param request

* @return

*/

public static String getIpAddr(HttpServletRequest request) {

String ip = request.getHeader( " x-forwarded-for " );

if (ip == null || ip.length() == 0 || " unknown " .equalsIgnoreCase(ip)) {

ip = request.getHeader( " Proxy-Client-IP " );

}

if (ip == null || ip.length() == 0 || " unknown " .equalsIgnoreCase(ip)) {

ip = request.getHeader( " WL-Proxy-Client-IP " );

}

if (ip == null || ip.length() == 0 || " unknown " .equalsIgnoreCase(ip)) {

ip = request.getRemoteAddr();

}

return ip;

}

/**

* 支付初始化 返回预支付ID、签名等信息

* @param payMoney

* @return map

* result -1

*/

@RequestMapping("/toPayInit")

@ResponseBody

public Map toPay(HttpServletRequest request,@RequestParam(value="payMoney",required=true)float payMoney,@RequestParam(value="openId",required=true) String openId,@RequestParam(value="orderId",required=true)String orderId){

Map map = new HashMap<>();

//订单号 目前生产的随机数 后面放入指定系统唯一的单号

//判断单号是否存在

String noncestr = WXPayUtil.generateNonceStr();

Map requestMap = new HashMap();

requestMap.put("appId",appId);

requestMap.put("userWeixinOpenId",openId);

//之前使用ETCA单号作为商户订单号,现在改为自动生成的账单号 2018-10-25 Peter

//requestMap.put("out_trade_no",auShipmentBrief.getShipmentReference());

requestMap.put("out_trade_no","订单号");

requestMap.put("mch_id",mchId);

//计算金额 微信支付的金额的单位是分,例如:实际支付1.23元,传入参数就是123

int money=0;

try {

money=(int)(payMoney*100);

} catch (Exception e) {

map.put("result",-1);

map.put("msg","金额格式不正确");

return map;

}

requestMap.put("payMoney",money+"");

requestMap.put("spbill_create_ip", getIpAddr(request));

requestMap.put("notify_url","回调地址");

requestMap.put("noncestr", noncestr);

requestMap.put("body","微信下单账单支付");

requestMap.put("detail","散客下单账单支付");

Map requestInfo = WXPayUtil.createOrderInfo(requestMap,apiKey);

String orderInfo_toString = (String) requestInfo.get("orderInfo_toString");

LOGGER.debug("request 请求字符串:"+orderInfo_toString);

//判断返回码

UnifiedOrderRespose orderResponse = WXPayUtil.httpOrder(orderInfo_toString,0);// 调用统一下单接口

//判断超时的情况

if(orderResponse==null || orderResponse.getReturn_code()==null || ("SUCCESS".equals(orderResponse.getReturn_code()) && (orderResponse.getErr_code()==null || "SYSTEMERROR".equals(orderResponse.getErr_code())))){

orderResponse = WXPayUtil.httpOrder(orderInfo_toString,1);

if(orderResponse==null || orderResponse.getReturn_code()==null || ("SUCCESS".equals(orderResponse.getReturn_code()) && (orderResponse.getErr_code()==null || "SYSTEMERROR".equals(orderResponse.getErr_code())))){

orderResponse = WXPayUtil.httpOrder(orderInfo_toString,2);

}

}

LOGGER.debug("response 返回字段:==》{}",orderResponse);

//根据微信文档return_code 和result_code都为SUCCESS的时候才会返回code_url

if(null!=orderResponse && "SUCCESS".equals(orderResponse.getReturn_code()) && "SUCCESS".equals(orderResponse.getResult_code())){

String timestamp = String.valueOf(WXPayUtil.getCurrentTimestamp());

map.put("timestamp",timestamp);

map.put("noncestr",noncestr);

UnifiedOrderRequest unifiedOrderRequest = (UnifiedOrderRequest) requestInfo.get("unifiedOrderRequest");

map.put("unifiedOrderRequest",unifiedOrderRequest);

SortedMap packageParams = new TreeMap();

packageParams.put("appId",appId);

packageParams.put("signType","MD5");

packageParams.put("nonceStr", noncestr);

packageParams.put("timeStamp", timestamp);

String packages = "prepay_id="+orderResponse.getPrepay_id();

packageParams.put("package",packages);

String sign = null;

try {

//生成签名

sign = WXPayUtil.generateSignature(packageParams,apiKey);

} catch (Exception e) {

map.put("result",-1);

map.put("msg","支付签名信息异常");

e.printStackTrace();

}

if(sign!=null && !"".equals(sign)){

LOGGER.debug("------------支付签名:"+sign+"-------------------");

map.put("paySign",sign);

map.put("result",1);

map.put("appId",appId);

}else{

map.put("result",-1);

map.put("msg","支付签名信息异常");

}

map.put("prepay_id",orderResponse.getPrepay_id());

return map;

}else{ //不成功

if(orderResponse!=null){

String text = "调用微信支付出错,返回状态码:"+orderResponse.getReturn_code()+",返回信息:"+orderResponse.getReturn_msg();

if(orderResponse.getErr_code()!=null && !"".equals(orderResponse.getErr_code())){

text = text +",错误码:"+orderResponse.getErr_code()+",错误描述:"+orderResponse.getErr_code_des();

}

LOGGER.error(text);

}else{

LOGGER.error("返回值 orderResponse对象为空");

}

map.put("result",-1);

map.put("msg","支付环境异常,请稍后再试");

return map;

}

}

}

jsp代码

定义微信支付成功回调接口APIAupostController.java

package com.yiexpress.api.controller.ewe.aupost;

import java.util.HashMap;

import java.util.Map;

import javax.annotation.Resource;

import javax.servlet.ServletInputStream;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import org.apache.commons.collections.MapUtils;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.beans.factory.annotation.Value;

import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;

import org.springframework.stereotype.Controller;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.ResponseBody;

import com.yiexpress.core.utils.wechat.WXPayUtil;

@Controller

@RequestMapping("/api")

public class APIAupostController {

private static final Logger LOGGER=LoggerFactory.getLogger(APIAupostController.class);

@Resource(name = "jacksonBean")

private MappingJackson2HttpMessageConverter jackson;

private String apiKey="商户密匙";//商户密匙

/**

* 异步回调接口

* @param request

* @param response

* @throws Exception

*/

@RequestMapping(value="/paymentNotice",produces="text/html;charset=utf-8")

@ResponseBody

public String WeixinParentNotifyPage(HttpServletRequest request,HttpServletResponse response) throws Exception{

ServletInputStream instream = request.getInputStream();

StringBuffer sb = new StringBuffer();

int len = -1;

byte[] buffer = new byte[1024];

while((len = instream.read(buffer)) != -1){

sb.append(new String(buffer,0,len));

}

instream.close();

Map map = WXPayUtil.xmlToMap(sb.toString());//接受微信的回调的通知参数

Map return_data = new HashMap();

//判断签名是否正确

if(WXPayUtil.isSignatureValid(map,apiKey)){

if(map.get("return_code").toString().equals("FAIL")){

return_data.put("return_code", "FAIL");

return_data.put("return_msg", map.get("return_msg"));

}else {

String return_code=MapUtils.getString(map,"return_code");

String result_code=MapUtils.getString(map,"result_code");

if(return_code!=null && "SUCCESS".equals(return_code) && result_code!=null && "SUCCESS".equals(result_code)){

String out_trade_no =MapUtils.getString(map,"out_trade_no");//系统订单号

//支付成功,可以自定义新逻辑

}

}

}else{

return_data.put("return_code", "FAIL");

return_data.put("return_msg", "签名错误");

}

String xml = WXPayUtil.GetMapToXML(return_data);

LOGGER.error("支付通知回调结果:"+xml);

return xml;

}

}

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:java导出csv格式文件的方法
下一篇:物流开放平台api接口(物流API)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~