支付dome微信公众号支付zip包
微信测试号获取方法:https://blog.csdn.net/Lc_2018/article/details/88353526
微信支付流程:
详细请去参考微信公众号支付API文档
https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1
代码如下:
package com.example.wxpay.api;
import com.example.wxpay.sdk.WXPayUtil;
import com.example.wxpay.utils.HttpCertClientUtils;
import com.example.wxpay.utils.HttpClientUtils;
import org.springframework.util.StringUtils;
import java.util.HashMap;
import java.util.Map;
/**
* @Author: Administrator
* @Date: 2019/12/23 16:03
*/
public class WXPayUtils {
/**
* 微信统一下单接口
* @param notify_url 回调地址
* @param out_trade_no 商户订单号
* @param total_fee 订单总金额
* @param ip IP
* @param body 商品内容描述
* @param openid 微信用户 OPENID
* @return
* @throws Exception
*/
public Map<String, String> wxPay(String notify_url, String out_trade_no, String total_fee, String ip, String body, String openid) throws Exception {
Map<String, String> mp = new HashMap();
mp.put("appid", WXPayConfig.getAppID()); // 公众账号ID
mp.put("mch_id", WXPayConfig.getMchID()); // 商户号
mp.put("nonce_str", WXPayUtil.generateNonceStr()); // 随机字符串
mp.put("body", body); // 商品描述
mp.put("out_trade_no", out_trade_no); // 商户订单号
mp.put("total_fee", total_fee); // 订单总金额, 单位为分
mp.put("spbill_create_ip", ip); // 终端IP
mp.put("notify_url", notify_url); // 异步接收微信支付结果通知的回调地址, 通知url必须为外网可访问的url, 不能携带参数
mp.put("openid",openid); //用户唯一标识
mp.put("trade_type", "JSAPI"); // 交易类型(JSAPI公众号支付/NATIVE扫码支付/APP支付)
mp.put("product_id", out_trade_no); // trade_type=NATIVE时(即扫码支付), 此参数必传. 此参数为二维码中包含的商品ID, 商户自行定义。
String sign = WXPayUtil.generateSignature(mp, WXPayConfig.getKey());
StringBuilder sb = new StringBuilder();
sb.append("" );
sb.append("" + mp.get("appid") + "");
sb.append(" + mp.get("mch_id") + "]]> ");
sb.append(" + mp.get("nonce_str") + "]]> ");
sb.append(" + mp.get("body") + "]]>");
sb.append(" + mp.get("out_trade_no") + "]]> ");
sb.append("" + mp.get("total_fee") + "");
sb.append(" + mp.get("spbill_create_ip") + "]]> ");
sb.append(" + mp.get("notify_url") + "]]> ");
sb.append("+mp.get("openid")+"]]> ");
sb.append(" + mp.get("trade_type") + "]]> ");
sb.append(" + mp.get("product_id") + "]]> ");
sb.append(" + sign + "]]> ");
sb.append("");
String url = "https://api.mch.weixin.qq.com/sandboxnew/pay/unifiedorder";
String str = HttpClientUtils.post(url, sb.toString());
Map<String, String> retmap = new HashMap();
try {
retmap = WXPayUtil.xmlToMap(str);
} catch (Exception e) {
e.printStackTrace();
}
return retmap;
}
/**
* 微信查询
* @param out_trade_no 商户订单号
* @return
* @throws Exception
*/
public Map<String, String> wxOrderQuery(String out_trade_no) throws Exception {
Map<String, String> mp = new HashMap<>();
mp.put("appid", WXPayConfig.getAppID()); // 公众账号ID
mp.put("mch_id", WXPayConfig.getMchID()); // 商户号
// mp.put("transaction_id", transaction_id); // 微信订单号 (2 S 1)
mp.put("out_trade_no", out_trade_no); // 商户订单号 (2 S 1)
mp.put("nonce_str", WXPayUtil.generateNonceStr()); // 随机字符串
String sign = WXPayUtil.generateSignature(mp, WXPayConfig.getKey());
StringBuilder sb = new StringBuilder();
sb.append("" );
sb.append("" + mp.get("appid") + "");
sb.append(" + mp.get("mch_id") + "]]> ");
sb.append(" + mp.get("out_trade_no") + "]]> ");
sb.append("" + mp.get("nonce_str") + "");
sb.append(" + sign + "]]> ");
sb.append("");
String url = "https://api.mch.weixin.qq.com/pay/orderquery";
String str = HttpClientUtils.post(url, sb.toString());
Map<String, String> retmap = new HashMap<>();
try {
retmap = WXPayUtil.xmlToMap(str);
} catch (Exception e) {
e.printStackTrace();
}
return retmap;
}
// 订单关闭
public Map<String, String> wxPayClose(String out_trade_no) throws Exception {
Map<String, String> mp = new HashMap<>();
mp.put("appid", WXPayConfig.getAppID()); // 公众账号ID
mp.put("mch_id", WXPayConfig.getMchID()); // 商户号
mp.put("out_trade_no", out_trade_no); // 商户订单号 (2 S 1)
mp.put("nonce_str", WXPayUtil.generateNonceStr()); // 随机字符串
String sign = WXPayUtil.generateSignature(mp, WXPayConfig.getKey());
StringBuilder sb = new StringBuilder();
sb.append("" );
sb.append("" + mp.get("appid") + "");
sb.append(" + mp.get("mch_id") + "]]> ");
sb.append("" + mp.get("nonce_str") + "");
sb.append(" + mp.get("out_trade_no") + "]]> ");
sb.append(" + sign + "]]> ");
sb.append("");
String url = "https://api.mch.weixin.qq.com/pay/closeorder";
String str = HttpClientUtils.post(url, sb.toString());
Map<String, String> retmap = new HashMap<>();
try {
retmap = WXPayUtil.xmlToMap(str);
} catch (Exception e) {
e.printStackTrace();
}
return retmap;
}
/**
* 查询退款
* @param out_trade_no 商户订单号
* @return
* @throws Exception
*/
public Map<String, String> wxRefundQuery(String out_trade_no) throws Exception {
Map<String, String> mp = new HashMap<>();
mp.put("appid", WXPayConfig.getAppID()); // 公众账号ID
mp.put("mch_id", WXPayConfig.getMchID()); // 商户号
mp.put("nonce_str", WXPayUtil.generateNonceStr()); // 随机字符串
// mp.put("transaction_id", transaction_id); // 微信订单号 (4 S 1)
// mp.put("out_refund_no", out_refund_no); // 商户退款单号 (4 S 1)
// mp.put("refund_id", refund_id); // 微信退款单号 (4 S 1)
mp.put("out_trade_no", out_trade_no); // 商户订单号 (4 S 1)
String sign = WXPayUtil.generateSignature(mp, WXPayConfig.getKey());
StringBuilder sb = new StringBuilder();
sb.append("" );
sb.append("" + mp.get("appid") + "");
sb.append(" + mp.get("mch_id") + "]]> ");
sb.append("" + mp.get("nonce_str") + "");
sb.append(" + mp.get("out_trade_no") + "]]> ");
sb.append(" + sign + "]]> ");
sb.append("");
String url = "https://api.mch.weixin.qq.com/pay/refundquery";
String str = HttpClientUtils.post(url, sb.toString());
Map<String, String> retmap = new HashMap<>();
try {
retmap = WXPayUtil.xmlToMap(str);
} catch (Exception e) {
e.printStackTrace();
}
return retmap;
}
//h5pay
}
package com.example.wxpay.api;
import com.example.wxpay.sdk.WXPayConstants;
import com.example.wxpay.sdk.WXPayUtil;
import net.sf.json.JSONObject;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
/**
* @Author: Administrator
* @Date: 2019/12/23 15:50
* 创建一个WXPayConfig类,用于配置微信支付环境
*/
public class WXPayConfig {
/**
* 获取 App ID
*
* @return App ID
*/
public static String getAppID() {
return "xx";
}
/**
* 获取 Mch ID (安装证书的商户ID)
*
* @return Mch ID
*/
public static String getMchID() {
return "xx";
}
/**
* 获取 API 密钥
*
* @return API密钥
*/
public static String getKey() {
return "xx";
}
public static final String APP_ID = "xx"; //填写开发者的APPID
public static final String APP_SECRET = "xx"; //填写开发者的APPSECRET
public static JSONObject doGetJson(String url) throws Exception, IOException {
JSONObject jsonObject=null;
//初始化httpClient
DefaultHttpClient client=new DefaultHttpClient();
//用Get方式进行提交
HttpGet httpGet=new HttpGet(url);
//发送请求
HttpResponse response= client.execute(httpGet);
//获取数据
HttpEntity entity=response.getEntity();
//格式转换
if (entity!=null) {
String result= EntityUtils.toString(entity,"UTF-8");
jsonObject=JSONObject.fromObject(result);
}
//释放链接
httpGet.releaseConnection();
return jsonObject;
}
/**
* 微信证书
* @return
*/
public static String getCert() {
String separator = System.getProperty("file.separator");
return WXPayConfig.getClassPath() + separator + "static" + separator + "cert" + separator + "apiclient_cert.p12";
}
/**
* 获取系统路径
* @return
*/
public static String getClassPath() {
String path = Thread.currentThread().getContextClassLoader().getResource("").toString();
String temp = path.replaceFirst("file:/", "");
String separator = System.getProperty("file.separator");
String resultPath = temp.replaceAll("/", separator + separator);
return resultPath;
}
}
package com.example.wxpay.controller;
import com.example.wxpay.api.WXPayConfig;
import com.example.wxpay.api.WXPayUtils;
import com.example.wxpay.sdk.WXPayConstants;
import com.example.wxpay.sdk.WXPayUtil;
import com.example.wxpay.utils.HttpUtil;
import com.example.wxpay.utils.ResponseUtils;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.*;
/**
* @Author: Administrator
* @Date: 2020/1/5 13:32
*/
@Controller
@RequestMapping("/weixin")
public class WXPayController {
@Value("${pays.http}")
private String payHttp;
private final String callBack = "/weixin/callBack";
/**
* @Description: 微信公众号登录授权
* @auther: Sakura
* @date: 2019/3/8 9:46
* @param: [request, response]
* @return: java.lang.String
*/
@GetMapping(value = "/login")
public void login(HttpServletRequest request, HttpServletResponse response) throws IOException {
// 回调地址,该域名需要公众号验证,
String backUrl = "http://2q85475y51.zicp.vip/weixin/wx";
// 向用户申请授权
String url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=" + WXPayConfig.APP_ID
+ "&redirect_uri=" +URLEncoder.encode(backUrl)
+ "&response_type=code"
+ "&scope=snsapi_userinfo"
+ "&state=STATE#wechat_redirect";
// String url ="https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx5c659a84e995c4e2 &redirect_uri=http://2q85475y51.zicp.vip/wx/wx &response_type=code&scope=snsapi_login&state=NIMASIL#wechat_redirect";
response.sendRedirect(url);
}
@GetMapping("/wx")
public String pay(HttpServletRequest request,ModelMap model) throws IOException {
String code = request.getParameter("code");
System.out.println(code);
List<Object> list = accessToken(code);
String openId=list.get(1).toString();
System.out.println(openId);
model.put("openId",openId);
return "/pay";
}
// 统一下单
@PostMapping("/wx")
public String paycode(ModelMap map, String orderNo, String body, Integer price,String openId) throws Exception {
WXPayUtils wxPayUtils = new WXPayUtils();
String url = payHttp + callBack;
System.out.println(url);
// http://2q85475y51.zicp.vip/weixin/callback
System.out.println(orderNo);
Map<String, String> rs = wxPayUtils.wxPay(url,orderNo, String.valueOf(price),"192.168.101.3", body,openId);
String timeStamp=String.valueOf(System.currentTimeMillis() / 1000);
// map.put("prepay_id", rs.toString());
map.put("appId",rs.get("appid"));
map.put("prepay_id",rs.get("prepay_id"));
map.put("nonceStr",rs.get("nonce_str"));
map.put("paySign",rs.get("sign"));
map.put("timeStamp",timeStamp);
return "paycode2" ;
}
//
/**
* 通过微信用户的code换取网页授权access_token
* @return
* @throws IOException
* @throws
*/
public List<Object> accessToken(String code) throws IOException {
List<Object> list = new ArrayList<Object>();
String url="https://api.weixin.qq.com/sns/oauth2/access_token?appid="+WXPayConfig.APP_ID
+ "&secret="+WXPayConfig.APP_SECRET
+ "&code="+code
+ "&grant_type=authorization_code";
HttpClient client = new DefaultHttpClient();
HttpPost post = new HttpPost(url);
HttpResponse res = client.execute(post);
if (res.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
HttpEntity entity = res.getEntity();
String str = EntityUtils.toString(entity, "utf-8");
ObjectMapper mapper=new ObjectMapper();
Map<String,Object> jsonOb=mapper.readValue(str, Map.class);
list.add(jsonOb.get("access_token"));
list.add(jsonOb.get("openid"));
}
return list;
}
/**
* 支付结果通知
* @return
* @throws Exception
*/
@PostMapping("/callBack")
public void notifyOrder(HttpServletRequest request, HttpServletResponse response) throws Exception {
// 获取request返回的值
String retText = ResponseUtils.getRequestBody(request);
Map<String, String> map = WXPayUtil.xmlToMap(retText);
// 检查签名信息
boolean sign = WXPayUtil.isSignatureValid(map, WXPayConfig.getKey());
if (sign) {
String wxouttradeno = map.get("out_trade_no");
String wxappid = map.get("appid");
String wxmchid = map.get("mch_id");
String wxbanktype = map.get("bank_type");
String wxcashfee = map.get("cash_fee");
String wxtotalfee = map.get("total_fee");
String wxissubscribe = map.get("is_subscribe");
String wxopenid = map.get("openid");
String wxtimeend = map.get("time_end");
String wxtradetype = map.get("trade_type");
String wxtransactionid = map.get("transaction_id");
// 写入数据库
System.out.println("===========支付回调===========");
System.out.println("商户订单号:" + wxouttradeno);
System.out.println("APPID:" + wxappid);
System.out.println("商户ID:" + wxmchid);
System.out.println("付款银行:" + wxbanktype);
System.out.println("现金支付金额:" + wxcashfee);
System.out.println("订单金额:" + wxtotalfee);
System.out.println("是否关注公众账号:" + wxissubscribe);
System.out.println("用户标识:" + wxopenid);
System.out.println("支付完成时间:" + wxtimeend);
System.out.println("交易类型:" + wxtradetype); // JSAPI、NATIVE、APP
System.out.println("微信支付订单号:" + wxtransactionid);
// 响应完成到微信
StringBuilder sb = new StringBuilder();
sb.append("" );
sb.append(" ");
sb.append(" ");
sb.append("");
ResponseUtils.response(response, sb.toString());
}
}
//用来获取测试号的密钥
@RequestMapping("/pay/pay")
@ResponseBody
public static String GetSignKey() throws Exception {
String nonce_str = WXPayUtil.generateNonceStr();//生成随机字符
Map<String, String> param = new HashMap<String, String>();
param.put("mch_id", WXPayConfig.getMchID());//需要真实商户号
param.put("nonce_str", nonce_str);//随机字符
String sign = WXPayUtil.generateSignature(param,WXPayConfig.getKey(), WXPayConstants.SignType.MD5);//通过SDK生成签名其中API_KEY为商户对应的真实密钥
param.put("sign", sign);
String xml = WXPayUtil.mapToXml(param);//将map转换为xml格式
String url = "https://api.mch.weixin.qq.com/sandboxnew/pay/getsignkey";//沙箱密钥获取api
String SignKey = HttpUtil.postData(url, xml);//
System.out.println("signkey+"+SignKey);
Map<String, String> param1 = new HashMap<String, String>();
param1 = WXPayUtil.xmlToMap(SignKey);
String key = param1.get("sandbox_signkey");
System.out.println(key);
return key;
}
// h5pay
}
package com.example.wxpay.sdk;
import sun.net.www.http.HttpClient;
/**
* 常量
*/
public class WXPayConstants {
public enum SignType {
MD5, HMACSHA256
}
public static final String DOMAIN_API = "api.mch.weixin.qq.com";
public static final String DOMAIN_API2 = "api2.mch.weixin.qq.com";
public static final String DOMAIN_APIHK = "apihk.mch.weixin.qq.com";
public static final String DOMAIN_APIUS = "apius.mch.weixin.qq.com";
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";
public static final String WXPAYSDK_VERSION = "WXPaySDK/3.0.9";
public static final String USER_AGENT = WXPAYSDK_VERSION +
" (" + System.getProperty("os.arch") + " " + System.getProperty("os.name") + " " + System.getProperty("os.version") +
") Java/" + System.getProperty("java.version") + " HttpClient/" + HttpClient.class.getPackage().getImplementationVersion();
public static final String MICROPAY_URL_SUFFIX = "/pay/micropay";
public static final String UNIFIEDORDER_URL_SUFFIX = "/pay/unifiedorder";
public static final String ORDERQUERY_URL_SUFFIX = "/pay/orderquery";
public static final String REVERSE_URL_SUFFIX = "/secapi/pay/reverse";
public static final String CLOSEORDER_URL_SUFFIX = "/pay/closeorder";
public static final String REFUND_URL_SUFFIX = "/secapi/pay/refund";
public static final String REFUNDQUERY_URL_SUFFIX = "/pay/refundquery";
public static final String DOWNLOADBILL_URL_SUFFIX = "/pay/downloadbill";
public static final String REPORT_URL_SUFFIX = "/payitil/report";
public static final String SHORTURL_URL_SUFFIX = "/tools/shorturl";
public static final String AUTHCODETOOPENID_URL_SUFFIX = "/tools/authcodetoopenid";
// sandbox
public static final String SANDBOX_MICROPAY_URL_SUFFIX = "/sandboxnew/pay/micropay";
public static final String SANDBOX_UNIFIEDORDER_URL_SUFFIX = "/sandboxnew/pay/unifiedorder";
public static final String SANDBOX_ORDERQUERY_URL_SUFFIX = "/sandboxnew/pay/orderquery";
public static final String SANDBOX_REVERSE_URL_SUFFIX = "/sandboxnew/secapi/pay/reverse";
public static final String SANDBOX_CLOSEORDER_URL_SUFFIX = "/sandboxnew/pay/closeorder";
public static final String SANDBOX_REFUND_URL_SUFFIX = "/sandboxnew/secapi/pay/refund";
public static final String SANDBOX_REFUNDQUERY_URL_SUFFIX = "/sandboxnew/pay/refundquery";
public static final String SANDBOX_DOWNLOADBILL_URL_SUFFIX = "/sandboxnew/pay/downloadbill";
public static final String SANDBOX_REPORT_URL_SUFFIX = "/sandboxnew/payitil/report";
public static final String SANDBOX_SHORTURL_URL_SUFFIX = "/sandboxnew/tools/shorturl";
public static final String SANDBOX_AUTHCODETOOPENID_URL_SUFFIX = "/sandboxnew/tools/authcodetoopenid";
}
package com.example.wxpay.sdk;
import com.example.wxpay.sdk.WXPayConstants.SignType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.parsers.DocumentBuilder;
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 java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.StringWriter;
import java.security.MessageDigest;
import java.security.SecureRandom;
import java.util.*;
public class WXPayUtil {
private static final String SYMBOLS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
private static final Random RANDOM = new SecureRandom();
/**
* XML格式字符串转换为Map
*
* @param strXML XML字符串
* @return XML数据转换后的Map
* @throws Exception
*/
public static Map<String, String> xmlToMap(String strXML) throws Exception {
try {
Map<String, String> data = new HashMap<String, String>();
DocumentBuilder documentBuilder = WXPayXmlUtil.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<String, String> data) throws Exception {
org.w3c.dom.Document document = WXPayXmlUtil.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<String, String> 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<String, String> 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<String, String> 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<String, String> 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<String, String> 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<String, String> 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<String, String> data, String key, SignType signType) throws Exception {
Set<String> 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 {
throw new Exception(String.format("Invalid sign_type: %s", signType));
}
}
/**
* 获取随机字符串 Nonce Str
*
* @return String 随机字符串
*/
public static String generateNonceStr() {
char[] nonceChars = new char[32];
for (int index = 0; index < nonceChars.length; ++index) {
nonceChars[index] = SYMBOLS.charAt(RANDOM.nextInt(SYMBOLS.length()));
}
return new String(nonceChars);
}
/**
* 生成 MD5
*
* @param data 待处理数据
* @return MD5结果
*/
public static String MD5(String data) throws Exception {
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();
}
}
package com.example.wxpay.sdk;
import org.w3c.dom.Document;
import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
/**
* 2018/7/3
*/
public final class WXPayXmlUtil {
public static DocumentBuilder newDocumentBuilder() throws ParserConfigurationException {
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
documentBuilderFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
documentBuilderFactory.setFeature("http://xml.org/sax/features/external-general-entities", false);
documentBuilderFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
documentBuilderFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
documentBuilderFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
documentBuilderFactory.setXIncludeAware(false);
documentBuilderFactory.setExpandEntityReferences(false);
return documentBuilderFactory.newDocumentBuilder();
}
public static Document newDocument() throws ParserConfigurationException {
return newDocumentBuilder().newDocument();
}
}
package com.example.wxpay.utils;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Project Name: com.weixin.pay.utils
* File Name: HttpClientUtils
* Date: 2018-11-30 21:45
* Copyright (c) 2018, [email protected] All Rights Reserved.
*
* @author Tobin
*/
public class HttpClientUtils {
//编码格式。发送编码格式统一用UTF-8
private static final String ENCODING = "UTF-8";
private final static Logger logger = LoggerFactory.getLogger(HttpClientUtils.class);
/**
* Post JOSN
* @param url
* @param json
* @return
*/
public static String post(String url, String json) {
logger.info("Post URL=" + url);
logger.info("Post Data=" + json);
StringBuffer requestText = new StringBuffer();
CloseableHttpResponse response = null;
CloseableHttpClient client = null;
HttpPost httpPost = new HttpPost(url);
StringEntity entityParams = null;
try {
entityParams = new StringEntity(json, "utf-8");
httpPost.setEntity(entityParams);
httpPost.setHeader("Content-Type", "type/json;charset=ISO-8859-1");
client = HttpClients.createDefault();
response = client.execute(httpPost);
byte[] x = EntityUtils.toByteArray(response.getEntity());
requestText.append(new String(x, "utf-8"));
} catch (Exception e) {
logger.error(e.getMessage(), e);
} finally {
logger.info("Request=" + requestText.toString());
}
return requestText.toString();
}
/**
* 基于HttpClient 4.5的通用GET方法
*
* @param url 提交的URL
* @return 提交响应
*/
public static String get(String url) throws RuntimeException {
CloseableHttpClient client = HttpClients.createDefault();
String responseText = "";
CloseableHttpResponse response = null;
try {
HttpGet get = new HttpGet(url);
response = client.execute(get);
HttpEntity entity = response.getEntity();
if (entity != null) {
responseText = EntityUtils.toString(entity, ENCODING);
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
try {
if (response != null)
response.close();
} catch (Exception e) {
e.printStackTrace();
}
}
return responseText;
}
}
package com.example.wxpay.utils;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
//发起post请求,并且获取服务器返回
public class HttpUtil {
private final static int CONNECT_TIMEOUT = 5000;//
private final static String DEFAULT_ENCODING="UTF-8";
public static String postData(String urlStr,String data) {
return postData(urlStr, data,null);
}
public static String postData(String urlStr,String data,String contentType) {
BufferedReader reader = null;
try {
URL url = new URL(urlStr);
// URLConnection coon =url.openConnection();
HttpURLConnection coon = (HttpURLConnection) url.openConnection();
coon.setRequestMethod("POST");
coon.setDoOutput(true);//为true后可使用.getOutputStrean
coon.setConnectTimeout(CONNECT_TIMEOUT);//设置连接主机超时(ms)
coon.setReadTimeout(CONNECT_TIMEOUT);//设置从主机读取数据超时(ms)
if(contentType!=null) {
coon.setRequestProperty("content_type", contentType);
}
OutputStreamWriter writer = new OutputStreamWriter(coon.getOutputStream(),DEFAULT_ENCODING);//将写入的字符编码成字节后写入一个字节流
if(data==null) {
data="";
}
writer.write(data);
writer.flush();
writer.close();
reader = new BufferedReader(new InputStreamReader(coon.getInputStream(), DEFAULT_ENCODING));//缓冲字符输入流
StringBuilder sb = new StringBuilder();
String line = null;
while((line = reader.readLine())!=null) {
sb.append(line);
sb.append("\r\n");
}
return sb.toString();
}catch (IOException e) {
//logger.error("Error connecting to " + urlStr + ": " + e.getMessage());
e.printStackTrace();
} finally {
try {
if (reader != null)
reader.close();
} catch (IOException e) {
}
}
return null;
}
}
package com.example.wxpay.utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
/**
* Project Name: com.weixin.pay.utils
* File Name: ResponseUtils
* Date: 2018-11-30 21:47
* Copyright (c) 2018, [email protected] All Rights Reserved.
*
* @author Tobin
*/
public class ResponseUtils {
private final static Logger logger = LoggerFactory.getLogger(ResponseUtils.class);
/**
* Response
* @param response
* @param responseMsg
*/
public static void response(HttpServletResponse response, String responseMsg) {
if (responseMsg == null)
return;
try {
byte[] msgs = responseMsg.getBytes("UTF-8");
response.setContentType("text/html;charset=UTF-8");
response.setHeader("Cache-Control", "no-store, max-age=0, no-cache, must-revalidate");
response.addHeader("Cache-Control", "post-check=0, pre-check=0");
response.setHeader("Pragma", "no-cache");
response.setDateHeader("Expires", 0);
response.setContentLength(msgs.length);
response.getOutputStream().write(msgs);
response.getOutputStream().flush();
response.getOutputStream().close();
} catch (Exception e) {
e.printStackTrace();
}
}
public static String getRequestBody(HttpServletRequest request) {
String retText = null;
BufferedReader reader = null;
try {
reader = new BufferedReader(new InputStreamReader(request.getInputStream(), "ISO-8859-1"));
String tmp = null;
StringBuffer htmlRet = new StringBuffer();
while ((tmp = reader.readLine()) != null) {
htmlRet.append(tmp).append("\n");
}
retText = new String(htmlRet.toString().trim().getBytes("ISO-8859-1"), "UTF-8");
} catch (Exception e) {
e.printStackTrace();
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return retText;
}
}
<!DOCTYPE html>
<html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf8">
<title>微信支付</title>
</head>
<body>
<h2>微信订单支付</h2>
<form action="http://2q85475y51.zicp.vip/weixin/wx" method="post">
<div>
<label>商户单号</label>
<input name="orderNo" value=""/>
</div>
<div>
<label>商品描述</label>
<input name="body" value=""/>
</div>
<div>
<label>openId</label>
<input name="openId" th:value="${openId}"/>
</div>
<div>
<label>商品价格</label>
<input name="price" value=""/>
<span style="color:red">在生产环境中,商品价格必须从数据库中读取,而不是从页面上传入参数给后台处理</span>
</div>
<input type="submit"/>
</form>
</body>
</html>
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>wxpay</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>wxpay</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!-- HTTP Client -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.5</version>
</dependency>
<!-- 微信退款解密 -->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.47</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j</artifactId>
<version>2.12.1</version>
</dependency>
<dependency>
<groupId>net.sf.json-lib</groupId>
<artifactId>json-lib</artifactId>
<version>2.4</version>
</dependency>
<!-- https://mvnrepository.com/artifact/net.sf.json-lib/json-lib -->
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
server:
port: 8080
pays:
http://2q85475y51.zicp.vip/
jquery-3.3.1.js
jquery-qrcode-0.14.0.js
:备注
1.js的话自己去官网去找js官网
2.外网的话可以下载花生壳或者ngrok如果用ngrok的话注意测试后台上的域名是否一致
3.公众号因为只能在微信上使用,因此不能用postman来调试,可以去微信官网下载一个微信开发者工具来进行调试
4.如果报出scope错误可以去微信测试后台里面找到网页账号设置域名(不要http);
5.支付的价格正式的话要求数据库里查,测试的话要101,201这样的价格
6.支付账号和商户号是真实的,这个你需要花费300元去微信那里开通
7.如果还是运行不通,请在评论区说出来,如果运行的通点个赞呗.
要是显示有回调的东西说明你成功了,