最近项目中有遇到需要微信支付的,大大小小的还是有些问题 在这写下来方便下次用到提醒自己吧。
我用的是ssm框架+maven+Tomcat8下面贴代码,中途让我困惑的错的地方我也会标记出来的:
/**
*
* @param openid 这是通过微信小程序里边通过code获取的一个参数
* @param price 价格在微信里边要求是证书 所以你可以选择在前端处理了传过来或者在后端处理
* @param title 订单标题怎么样都可以吧 但是生成xml的时候要注意编码格式
* @throws Exception
*/
@ResponseBody
@RequestMapping(value="pay/payMoneny",produces = "application/json; charset=utf-8")
public String payMoneny(String openid,double price,String title,HttpServletRequest req,HttpServletResponse resp) throws Exception{
JSONObject json = new JSONObject();
price=price*100;
int a=(int)price;
//得到小程序传过来的价格,注意这里的价格必须为整数,1代表1分,所以传过来的值必须*100;
System.out.println(a);
//订单标题
System.out.println(title);
//时间戳
String times = System.currentTimeMillis() + "";
SortedMap<Object, Object> packageParams = new TreeMap<Object, Object>();
packageParams.put("appid", 小程序开发平台中的appid);
packageParams.put("mch_id", 这个是商家号怎么获取看开发文档是有的因为我是负责写后台的这都是别人给我的);
packageParams.put("nonce_str", times);//时间戳
packageParams.put("body", title);//支付主体
packageParams.put("out_trade_no", 如果后续需要写代码的话可以把你的订单号拼接在字符串中);//编号
packageParams.put("total_fee", a);//价格
packageParams.put("notify_url", 回调地址);//支付返回地址,(虽然网上都是这么写的 但是不知道为什么我的回调函数进不去 我的解决方法:在前端支付完之后让前端的再掉了一个处理业务逻辑的方法)
packageParams.put("trade_type", "JSAPI") ;//这个api有,固定的
packageParams.put("openid", openid);//openid
//获取sign
String sign = PayCommonUtil.createSign("UTF-8", packageParams, "跟下边秘钥的一样");//最后这个是自己设置的32位密钥appkey
packageParams.put("sign", sign);
//转成XML
String reqXML = PayCommonUtil.getRequestXml(packageParams);
System.out.println(reqXML);
//得到含有prepay_id的XML
String resXml = HttpUtil.postData("https://api.mch.weixin.qq.com/pay/unifiedorder", reqXML);
System.out.println(resXml);
//解析XML存入Map
Map map = XMLUtil.doXMLParse(resXml);
System.out.println(map);
// String return_code = (String) map.get("return_code");
//得到prepay_id
String prepay_id = (String) map.get("prepay_id");
SortedMap<Object, Object> packageP = new TreeMap<Object, Object>();
packageP.put("appId", appid);//!!!注意,这里是appId,上面是appid。
packageP.put("nonceStr", times);//时间戳
packageP.put("package", "prepay_id=" + prepay_id);//必须把package写成 "prepay_id="+prepay_id这种形式
packageP.put("signType", "MD5");//paySign加密
packageP.put("timeStamp", (System.currentTimeMillis() / 1000) + "");
//得到paySign
String paySign = PayCommonUtil.createSign("UTF-8", packageP, "32位的秘钥好像是在微信开发平台设置的appkey");
packageP.put("paySign", paySign);
String returnCode = (String) map.get("return_code");
//将packageP数据返回给小程序
json.accumulate("data", packageP);
json.accumulate("code", 200);
json.accumulate("msg", "成功!");
return json.toString();
}
注意:一定要注意那些参数的名称,我在写提现就是商家像用户转钱的时候就被那个小细节困扰了很久。
下面就是一些需要的帮助类:先是生成签名
/**
* @author
* @Description:sign签名
* @param characterEncoding
* 编码格式
* @param parameters
* 请求参数
* @return
*/
public static String createSign(String characterEncoding, SortedMap<Object, Object> packageParams, String API_KEY) {
StringBuffer sb = new StringBuffer();
Set es = packageParams.entrySet();
Iterator it = es.iterator();
while (it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
String k = entry.getKey().toString();
String v = entry.getValue().toString();
if (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) {
sb.append(k + "=" + v + "&");
}
}
sb.append("key=" + API_KEY);
String sign = MD5.MD5Encode(sb.toString(), characterEncoding).toUpperCase();
return sign;
}
下面是转换成xml的帮助类:当然也可以写在一起的我就是写在一个方法里边的
/**
* @author
* @Description:将请求参数转换为xml格式的string
* @param parameters
* 请求参数
* @return
*/
public static String getRequestXml(SortedMap<Object, Object> parameters) {
StringBuffer sb = new StringBuffer();
sb.append("<xml>");
Set es = parameters.entrySet();
Iterator it = es.iterator();
while (it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
String k = entry.getKey().toString();
String v = entry.getValue().toString();
if ("attach".equalsIgnoreCase(k) || "body".equalsIgnoreCase(k) || "sign".equalsIgnoreCase(k)) {
sb.append("<" + k + ">" + v + "</" + k + ">");
} else {
sb.append("<" + k + ">" + v + "</" + k + ">");
}
}
sb.append("</xml>");
return sb.toString();
}
下面这个类是发起请求的:
public class HttpUtil {
private final static int CONNECT_TIMEOUT = 5000; // in milliseconds
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 conn = url.openConnection();
conn.setDoOutput(true);
conn.setConnectTimeout(CONNECT_TIMEOUT);
conn.setReadTimeout(CONNECT_TIMEOUT);
if(contentType != null)
conn.setRequestProperty("content-type", contentType);
OutputStreamWriter writer = new OutputStreamWriter(conn.getOutputStream(), DEFAULT_ENCODING);
if(data == null)
data = "";
writer.write(data);
writer.flush();
writer.close();
reader = new BufferedReader(new InputStreamReader(conn.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());
} finally {
try {
if (reader != null)
reader.close();
} catch (IOException e) {
}
}
return null;
}
public static CloseableHttpResponse Post(String url, String outputEntity, boolean isLoadCert) throws Exception {
HttpPost httpPost = new HttpPost(url);
// 得指明使用UTF-8编码,否则到API服务器XML的中文不能被成功识别
httpPost.addHeader("Content-Type", "text/xml");
httpPost.setEntity(new StringEntity(outputEntity, "UTF-8"));
if (isLoadCert) {
// 加载含有证书的http请求
return HttpClients.custom().setSSLSocketFactory(CertUtil.initCert()).build().execute(httpPost);
} else {
return HttpClients.custom().build().execute(httpPost);
}
}
}
其实网上代码都差不多吧,都是要自己测试了才行的,一般发出来的都是自己测试成功了,但是有可能写的时间久了复制出来的代码不完整,有问题再沟通吧。