请求过程: 小程序端,先提交微信支付请求,服务端获取请求。然后再向微信发生支付请求,微信获取服务端提交的支付请求。根据api文档里的notify_url(设置接收反馈结果的路径)返回给服务端。服务器接收到微信端的结果之后,再把相应参数返回给小程序,小程序端再请调取微信支付接口,生成订单,最后客户完成支付。然后通知微信支付成功,整个过程就是这样,下面贴出详细的代码; 小程序微信支付api文档 :https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=9_1
//首先服务端得有个接口,来接收 小程序端来 提交支付请求 /** * 微信付款接口 * * @param params * @return */ @RequestMapping(value = "addWaybillTest", produces ="application/json;charset=UTF-8")
@ResponseBod public Map addWaybillTest(HttpServletRequest request,HttpServletResponse response,@RequestBody MlApiEntity mlApiEntity) { Map<String, Object> resultMap=new HashMap(); try { //得到一个时间戳 SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss"); String str = sdf.format(new Date());
String body="MLSystem"+"矿泉水2瓶测试";
String total_fee="1";
String notify_url="获取微信支付通知接口URL,自定义" ;
String openId=WxCommon.getOpenId(mlApiEntity.getCode());
//先微信支付
resultMap =WxCommon.wexinPlace( body, "220", total_fee, request, notify_url, openId);
} catch (Exception e) {
e.printStackTrace();
loger.info("checkLogin=============error====" + e);
resultMap.put("result", "系统服务器繁忙,请稍候!");
e.printStackTrace();
}
return resultMap;
}
//微信支付工具类 public class WxCommon {
private static String appid="";
private static String secret="";
private static String mch_id="";
private static String key="";
//获取oppenid
public static String getOpenId(String code){
Map<String, String>params=new HashMap();
params.put("appid", appid);
params.put("secret",secret );
params.put("js_code", code);
params.put("grant_type", "authorization_code");
String result=HttpXmlClient.post("https://api.weixin.qq.com/sns/jscode2session", params);
JSONObject jsonObj = JSONObject.fromObject(result);
if(jsonObj.has("openid")){
String openid=jsonObj.getString("openid");
if("".equals(openid)){
return "invalidCode";
}else{
return openid;
}
}else{
return "invalidCode";
}
}
//统一下单
public static Map wexinPlace( String body,String waybillNo,String total_fee,HttpServletRequest request,String notify_url,String account){ Map<String, String> paramMap=new HashMap(); paramMap.put("appid", appid); paramMap.put("mch_id", mch_id); paramMap.put("device_info", "WEB"); paramMap.put("nonce_str", GeneratePwd.genRandomNum(32)); paramMap.put("sign_type","MD5"); paramMap.put("body",body); paramMap.put("out_trade_no",waybillNo); paramMap.put("total_fee",total_fee); paramMap.put("spbill_create_ip",getIpAddr(request)); paramMap.put("notify_url",notify_url); paramMap.put("trade_type","JSAPI"); paramMap.put("openid",account); paramMap.put("sign", createSign(paramMap)); // 把参数转换成XML数据格式 String xmlWeChat = assembParamToXml(paramMap); String resXml = HttpUtil.post("https://api.mch.weixin.qq.com/pay/unifiedorder", xmlWeChat); Map<String, String> resMap = null; if (!StrUtil.isBlank(resXml)) { try { resMap = parseXMLToMap(resXml); } catch (IOException e) { e.printStackTrace(); } catch (JDOMException e) { e.printStackTrace(); } } Map<String, String> map = new HashMap(); if (resMap == null) { map.put("status", "false"); return map; } String return_code = resMap.get("return_code"); if (return_code.equalsIgnoreCase("FAIL")) { map.put("message", resMap.get("return_msg")); map.put("status", "false"); return map; } else if (return_code.equalsIgnoreCase("SUCCESS")) { String err_code = resMap.get("err_code"); if (!StrUtil.isBlank(err_code)) { map.put("status", "false"); map.put("message", resMap.get("err_code_des")); } else if (resMap.get("result_code").equalsIgnoreCase("SUCCESS")) { map.put("appId",appid); // 时间戳 当前的时间 需要转换成秒 map.put("timeStamp", String.valueOf(System.currentTimeMillis() / 1000)); // 随机字符串 不长于32位 map.put("nonceStr", GeneratePwd.genRandomNum(32)); // 订单详情扩展字符串 统一下单接口返回的prepay_id参数值,提交格式如:prepay_id=*** map.put("package", "prepay_id=" + resMap.get("prepay_id")); // 签名方式 签名算法,暂支持MD5 map.put("signType", "MD5"); // 签名 map.put("paySign", createSign(map)); map.remove("appId"); map.put("status", "true"); }else { map.put("status", "false"); } } return map;
}
/**
* 微信支付签名sign
*/
public static String createSign(Map<String, String> param) {
//签名步骤一:按字典排序参数
List list = new ArrayList(param.keySet());
Object[] ary = list.toArray();
Arrays.sort(ary);
list = Arrays.asList(ary);
String str = "";
for (int i = 0; i < list.size(); i++) {
str += list.get(i) + "=" + param.get(list.get(i) + "") + "&";
}
//签名步骤二:加上key
str += "key=" + key;
System.err.println(str);
//步骤三:加密并大写
str = MD5Util.md5(str).toUpperCase();
return str;
}
/**
* 获取访问者IP
* <p>
* 在一般情况下使用Request.getRemoteAddr()即可,但是经过nginx等反向代理软件后,这个方法会失效。
* <p>
* 本方法先从Header中获取X-Real-IP,如果不存在再从X-Forwarded-For获得第一个IP(用,分割),
* 如果还不存在则调用Request .getRemoteAddr()。
*
* @param request
* @return
*/
public static String getIpAddr(HttpServletRequest request) {
String ip = request.getHeader("X-Real-IP");
if (!StrUtil.isBlank(ip) && !"unknown".equalsIgnoreCase(ip)) {
if (ip.contains("../") || ip.contains("..\\")) {
return "";
}
return ip;
}
ip = request.getHeader("X-Forwarded-For");
if (!StrUtil.isBlank(ip) && !"unknown".equalsIgnoreCase(ip)) {
// 多次反向代理后会有多个IP值,第一个为真实IP。
int index = ip.indexOf(',');
if (index != -1) {
ip = ip.substring(0, index);
}
if (ip.contains("../") || ip.contains("..\\")) {
return "";
}
return ip;
} else {
ip = request.getRemoteAddr();
if (ip.contains("../") || ip.contains("..\\")) {
return "";
}
if (ip.equals("0:0:0:0:0:0:0:1")) {
ip = "127.0.0.1";
}
return ip;
}
}
// 通知微信正确接收
public static void noticeWeChatSuccess(String weiXinPayUrl) {
Map<String, String> parames = new HashMap<String, String>();
parames.put("return_code", "SUCCESS");
parames.put("return_msg", "OK");
// 将参数转成xml格式
String xmlWeChat = assembParamToXml(parames);
try {
if (!StrUtil.isBlank(weiXinPayUrl)) {
String s = HttpUtil.post(weiXinPayUrl, xmlWeChat);
}
} catch (Exception e) {
e.printStackTrace();
}
}
// 通知微信错误
public static void noticeWeChatFAIL(String weiXinPayUrl) {
Map<String, String> parames = new HashMap<String, String>();
parames.put("return_code", "FAIL");
parames.put("return_msg", "校验错误");
// 将参数转成xml格式
String xmlWeChat = assembParamToXml(parames);
try {
if (!StrUtil.isBlank(weiXinPayUrl)) {
String s = HttpUtil.post(weiXinPayUrl, xmlWeChat);
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 解析xml,返回第一级元素键值对。如果第一级元素有子节点,则此节点的值是子节点的xml数据。
*/
public static Map parseXMLToMap(String strxml) throws IOException, JDOMException {
strxml = strxml.replaceFirst("encoding=\".*\"", "encoding=\"UTF-8\"");
if (null == strxml || "".equals(strxml)) {
return null;
}
Map m = new HashMap();
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;
}
/**
* 获取子结点的xml
*
* @param children
* @return String
*/
private 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();
}
/**
* 将需要传递给微信的参数转成xml格式
*
* @param parameters
* @return
*/
private static String assembParamToXml(Map<String, String> parameters) {
StringBuffer sb = new StringBuffer();
sb.append("<xml>");
Set<String> es = parameters.keySet();
List<Object> list = new ArrayList<Object>(es);
Object[] ary = list.toArray();
Arrays.sort(ary);
list = Arrays.asList(ary);
Iterator<Object> it = list.iterator();
while (it.hasNext()) {
String key = (String) it.next();
String val = (String) parameters.get(key);
if ("attach".equalsIgnoreCase(key) || "body".equalsIgnoreCase(key) || "sign".equalsIgnoreCase(key)) { sb.append("<" + key + ">" + "<![CDATA[" + val + "]]></" + key + ">"); } else { sb.append("<" + key + ">" + val + "</" + key + ">"); } } sb.append("</xml>"); return sb.toString(); } /** * 处理xml请求信息 */ public static String getWeiXinResponse(HttpServletRequest request) { BufferedReader bis = null; String result = ""; try { bis = new BufferedReader(new InputStreamReader(request.getInputStream())); String line = null; while ((line = bis.readLine()) != null) { result += line; } } catch (Exception e) { e.printStackTrace(); } finally { if (bis != null) { try { bis.close(); } catch (IOException e) { e.printStackTrace(); } } } return result; }
}
/** *接收微信支付结果通知 成功付款 则新增订单 * * @param params * @return */ @RequestMapping(value = "wxPayResult", produces ="application/json;charset=UTF-8")
@ResponseBody public void wxPayResult(HttpServletRequest request,HttpServletResponse response)throws Exception { String resXml=WxCommon.getWeiXinResponse(request);//字节流 转换成 字符流 Map<String, String> resMap = null;
if (!StrUtil.isBlank(resXml)) {
try {
resMap = WxCommon.parseXMLToMap(resXml);
} catch (IOException e) {
e.printStackTrace();
} catch (JDOMException e) {
e.printStackTrace();
}
}
Map<String, String> map = new HashMap();
if (resMap != null) {
String return_code = resMap.get("return_code");
if(return_code.equals("FAIL")){
String return_msg="";
return_msg=resMap.get("return_msg");
loger.info("===========================微信支付接口调用失败,返回结果=====================" +return_msg);
}else if(return_code.equals("SUCCESS")){
String result_code=resMap.get("result_code");
if(result_code.equals("SUCCESS")){
String sign=resMap.get("sign");
resMap.remove("sign");
String result=WxCommon.createSign(resMap);
if(sign.equals(result)){
String out_trade_no=resMap.get("out_trade_no");
WxCommon.noticeWeChatSuccess("https://api.mch.weixin.qq.com/pay/unifiedorder");//支付成功后我们要通知微信
loger.info("===========================微信支付成====================="); } }else{
loger.info("===========================微信支付失=====================" ); }
}else if(result_code.equals("FAIL")){
String err_code=resMap.get("err_code");
if(!StrUtil.isBlank(err_code)){
loger.info("===========================微信支付失败:错误返回的信息描述:====================="+err_code );
String err_code_des=resMap.get("err_code_des");
if(!StrUtil.isBlank(err_code_des)){
loger.info("===========================微信支付失败:错误返回的信息描述:====================="+err_code_des );
}
}else{
}
}
}
}
}