pom依赖:
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.6.1</version>
</dependency>
微信小程序js:
wx.request({
//url:微信小程序js获取价格后将价格和openId传入后台路径
url: '*********',
method: 'post',
header: { 'conten-type': 'application/x-www-from-urlencoded' },
success: function (res) {
var payEncodeStr = res.data;
//调起支付API
wx.requestPayment({
'timeStamp': payEncodeStr.timeStamp,
'nonceStr': payEncodeStr.nonceStr,
'package': payEncodeStr.package,
'signType': payEncodeStr.signType,
'paySign': payEncodeStr.paySign,
'success': function (res) {
//起调成功后操作
*****************
},
'fail': function (res) {
},
'complete': function (res) {
}
})
}
},
fail: function (res) {
}
})
java后台:
微信小程序发起支付需要的常量:
public interface Constants {
//小程序APPID
public static final String APPID = "*********";
//微信支付的商户id
public static final String MCH_ID = "*********";
//微信支付的商户密钥
public static final String PARTNERKEY = "**********";
//支付成功后的服务器回调url
public static final String NOTIFY_URL = "***********";
//签名方式
public static final String SIGNTYPE = "MD5";
//交易类型
public static final String TRADETYPE = "JSAPI";
//微信统一下单接口地址
public static final String PAY_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder";
}
业务代码:
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.net.HttpURLConnection;
import java.net.InetAddress;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Random;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.springframework.util.DigestUtils;
public class ProgramPayMent {
/**
* 支付起调(小程序调用js后进入此方法)
*/
public Map<String, String> programMent(String openId, String price) {
// 返回给移动端需要的参数
Map<String, String> response = new HashMap<String, String>();
try {
// 商户订单号
String transactionId = "根据自己的生成规则生成";
// 生成的随机字符串
String nonce_str = getRandomString(32);
// 商品名称
String body = "********";
// 本机的ip地址
String spbill_create_ip = getLocalIp();
String money = getMoney(price);// 支付金额,单位:分,这边需要转成字符串类型,否则后面的签名会失败
Map<String, String> packageParams = new HashMap<String, String>();
// 小程序ID,微信分配的小程序ID
packageParams.put("appid", Constants.APPID);
// 商户号,微信支付分配的商户号
packageParams.put("mch_id", Constants.MCH_ID);
// 随机字符串,长度要求在32位以内。
packageParams.put("nonce_str", nonce_str);
// 商品简单描述
packageParams.put("body", body);
// 商户系统内部订单号,要求32个字符内,只能是数字、大小写字母_-|*且在同一个商户号下唯一
packageParams.put("out_trade_no", transactionId);// 商户订单号
// 订单总金额,单位为分
packageParams.put("total_fee", money);
// 终端IP,支持IPV4和IPV6两种格式的IP地址。调用微信支付API的机器IP
packageParams.put("spbill_create_ip", spbill_create_ip);
// 异步接收微信支付结果通知的回调地址,通知url必须为外网可访问的url,不能携带参数。
packageParams.put("notify_url", Constants.NOTIFY_URL);
// 交易类型(JSAPI--JSAPI支付(或小程序支付)、NATIVE--Native支付、APP--app支付,MWEB--H5支付,不同trade_type决定了调起支付的方式,请根据支付产品正确上传)
packageParams.put("trade_type", Constants.TRADETYPE);
// 用户标识,trade_type=JSAPI,此参数必传,用户在商户appid下的唯一标识。
packageParams.put("openid", openId);
// 除去数组中的空值和签名参数
packageParams = paraFilter(packageParams);
String prestr = createLinkString(packageParams); // 把数组所有元素,按照“参数=参数值”的模式用“&”字符拼接成字符串
// MD5运算生成签名,这里是第一次签名,用于调用统一下单接口,key为商户平台设置的密钥key
String mysign = sign(prestr, Constants.PARTNERKEY, "utf-8").toUpperCase();
// 拼接统一下单接口使用的xml数据,要将上一步生成的签名一起拼接进去
String xml = "<xml version='1.0' encoding='gbk'>" + "<appid>" + Constants.APPID + "</appid>"
+ "<body><![CDATA[" + body + "]]></body>" + "<mch_id>" + Constants.MCH_ID + "</mch_id>"
+ "<nonce_str>" + nonce_str + "</nonce_str>" + "<notify_url>" + Constants.NOTIFY_URL
+ "</notify_url>" + "<openid>" + openId + "</openid>" + "<out_trade_no>" + transactionId
+ "</out_trade_no>" + "<spbill_create_ip>" + spbill_create_ip + "</spbill_create_ip>"
+ "<total_fee>" + money + "</total_fee>" + "<trade_type>" + Constants.TRADETYPE + "</trade_type>"
+ "<sign>" + mysign + "</sign>" + "</xml>";
System.out.println(xml);
// 调用统一下单接口,并接受返回的结果
String result = httpRequest(Constants.PAY_URL, "POST", xml);
// 解析结果,将结果存在map中
Map map = doXMLToMap(result);
String return_code = (String) map.get("return_code");
if (return_code == "SUCCESS" || return_code.equals(return_code)) {
// 业务结果
String prepay_id = (String) map.get("prepay_id");// 返回的预付单信息
response.put("nonceStr", nonce_str);
response.put("package", "prepay_id=" + prepay_id);
Long timeStamp = System.currentTimeMillis() / 1000;
response.put("timeStamp", timeStamp + "");// 这边要将返回的时间戳转化成字符串,不然小程序端调用wx.requestPayment方法会报签名错误
String stringSignTemp = "appId=" + Constants.APPID + "&nonceStr=" + nonce_str + "&package=prepay_id="
+ prepay_id + "&signType=" + Constants.SIGNTYPE + "&timeStamp=" + timeStamp;
// 再次签名,这个签名用于小程序端调用wx.requesetPayment方法
String paySign = sign(stringSignTemp, Constants.PARTNERKEY, "utf-8").toUpperCase();
response.put("paySign", paySign);
response.put("appid", Constants.APPID);
}
} catch (Exception e) {
e.printStackTrace();
}
return response;
}
/**
* 支付回调(常量中 NOTIFY_URL 的值为该路径)
*/
public void callback(HttpServletRequest request, HttpServletResponse response) {
try {
InputStream inputStream = request.getInputStream();
// 获取请求输入流
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len = 0;
while ((len = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, len);
}
outputStream.close();
inputStream.close();
// 回调数据
Map<String, String> map = getMapFromXML(new String(outputStream.toByteArray(), "utf-8"));
String resXml = "";
String returnCode = map.get("return_code");
if ("SUCCESS".equalsIgnoreCase(returnCode)) {
String returnmsg = map.get("result_code");
if ("SUCCESS".equals(returnmsg)) {
/**
*
* 业务代码
*
*/
// 支付成功
resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>"
+ "<return_msg><![CDATA[OK]]></return_msg>" + "</xml>";
} else {
resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"
+ "<return_msg><![CDATA[报文为空]></return_msg>" + "</xml>";
System.out.println("支付失败:" + resXml);
}
} else {
resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"
+ "<return_msg><![CDATA[报文为空]></return_msg>" + "</xml>";
System.out.println("【订单支付失败】");
}
System.out.println("响应内容:" + resXml);
response.getWriter().print(resXml);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 生成随机字符串
*/
public static String getRandomString(int length) {
String str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
Random random = new Random();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < length; i++) {
int number = random.nextInt(62);
sb.append(str.charAt(number));
}
return sb.toString();
}
/**
* 获取本机IP
*
* @return
*/
public static String getLocalIp() {
InetAddress ia = null;
String localip = null;
try {
ia = ia.getLocalHost();
localip = ia.getHostAddress();
} catch (Exception e) {
e.printStackTrace();
}
return localip;
}
/**
* 将金额转换为分
*/
public static String getMoney(String mon) {
String finalmoney = "";
Double db_money = Double.parseDouble(mon.toString().replace(" ", ""));
String result;
double d2 = 100.0;
BigDecimal bd1 = new BigDecimal(Double.toString(db_money));
BigDecimal bd2 = new BigDecimal(Double.toString(d2));
result = bd1.multiply(bd2).doubleValue() + ""; // 乘
finalmoney = result.substring(0, result.indexOf("."));
return finalmoney;
}
/**
* 除去数组中的空值和签名参数
*
* @param sArray
* 签名参数组
* @return 去掉空值与签名参数后的新签名参数组
*/
public 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;
}
/**
* 把数组所有元素排序,并按照“参数=参数值”的模式用“&”字符拼接成字符串
*
* @param params
* 需要排序并参与字符拼接的参数组
* @return 拼接后字符串
*/
public 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;
}
/**
* sign
*/
public String sign(String content, String key, String input_charset) {
content = content + "&key=" + key;
byte[] str = {};
if (input_charset == null || "".equals(input_charset)) {
str = content.getBytes();
}
try {
str = content.getBytes(input_charset);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("MD5签名过程中出现错误,指定的编码集不对,您目前指定的编码集是:" + input_charset);
}
return DigestUtils.md5DigestAsHex(str);
}
/**
* httpRequest请求
*/
public 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);
}
br.close();
} catch (Exception e) {
e.printStackTrace();
}
return buffer.toString();
}
/**
* 解析xml,取出其中的result_code,和prepay_id
*/
public HashMap<String, String> doXMLToMap(String results) {
HashMap<String, String> result = new HashMap<String, String>();
String return_code = results.substring(results.indexOf("<return_code><![CDATA[") + 22,
results.indexOf("]]></return_code>"));
result.put("return_code", return_code);
if (return_code.equals("SUCCESS")) {
String prepay_id = results.substring(results.indexOf("<prepay_id><![CDATA[") + 20,
results.indexOf("]]></prepay_id>"));
result.put("prepay_id", prepay_id);
}
return result;
}
/**
* 将xml字符串转换成map
*/
public static Map<String, String> getMapFromXML(String strXML) throws Exception {
Document doc = DocumentHelper.parseText(strXML);
Map<String, String> map = new HashMap<String, String>();
if (doc == null) {
return map;
}
Element root = doc.getRootElement();
for (Iterator iterator = root.elementIterator(); iterator.hasNext();) {
Element e = (Element) iterator.next();
List list = e.elements();
map.put(e.getName(), e.getText());
}
return map;
}
}
我把所有业务代码放一起的可以根据自己需求分层~~~~~~~~