注意:开发支付功能前,需要登录微信小程序后台开通微信支付功能
第一步:获取支付参数
获取支付时所需openId参考此篇博文js调用,获取参数
wx.login({
success: res => {
// 发送 res.code 到后台换取 openId, sessionKey, unionId
wx.request({
url: that.data.url + '/mini_Weixin_getOpenId.action',
data:{"code":res.code},
method:'GET',
header: {
'content-type': 'application/json'
},
success: function (res) {
openId = res.data;
wx.request({
url: that.data.url + '/mini_WeixinPay_getPayParameter.action',
data:{"openId":openId,"totalAmount":payData.payAmount,"orderId":payData.orderId},
method:'GET',
header: {
'content-type': 'application/json'
},
success: function (res) {
that.goPay(res); //调用微信支付
}
})
}
})
}
})
后端代码
/**
* 小程序统一下单接口,并返回相关参数
*/
public String getPayParameter(){
try {
String notify_url = "支付成功回调地址";
reqIp = getIpAddress();
JSONObject json = new JSONObject();
String totalFeeStr = "";//原始的支付金额,单位元
Float totalFee = 0.0f;
//随机数
//String nonce_str = "1add1a30ac87aa2db72f57a2375d8fec";
String nonce_str = UUID.randomUUID().toString().replaceAll("-", "");
//商品描述
String body = "测试";
//商户订单号
String out_trade_no = orderId;
//总金额
Integer total_fee = 0;//转化成单位,元--分
String time_start =DateUtil.formatDateTime();
double totalAmount_all = 0;
logger.info("/ns/toPay=reqIP==="+reqIp);
totalAmount_all = Arith.add(totalAmount_all, Double.valueOf(totalAmount));
totalFeeStr = Double.toString(totalAmount_all);
totalFee = new Float(totalAmount_all);
total_fee = Math.round(totalFee*100);//转化成单位,元--分
logger.info("/ns/toPay=totalFeeStr==="+totalFeeStr);
logger.info("/ns/toPay=totalFee==="+totalFee);
logger.info("/ns/toPay=total_fee==="+total_fee);
logger.info("/ns/toPay=body==="+body);
logger.info("/ns/toPay=time_start==="+time_start);
//获取统一下单需要的openid
logger.info("weixToPay.openId:===@==" + openId);
try {
if(openId==null||"".equals(openId)){
logger.info("获取统一下单需要的openid失败!");
return json.toString();
}
} catch (Exception e) {
e.printStackTrace();
return null;
}
//获取sign
SortedMap<String, String> packageParams = new TreeMap<String, String>();
packageParams.put("appid", "小程序appId");
packageParams.put("mch_id","商户号");
packageParams.put("nonce_str", nonce_str);
packageParams.put("body", body);
packageParams.put("out_trade_no", out_trade_no);
packageParams.put("total_fee", total_fee+"");
packageParams.put("spbill_create_ip", reqIp);
packageParams.put("notify_url", notify_url);
packageParams.put("trade_type", "JSAPI");
packageParams.put("openid", openId);
RequestHandler reqHandler = new RequestHandler(this.getRequest(), this.getResponse());
reqHandler.init("小程序appId", "小程序appsecret", "商户密钥");
String sign = reqHandler.createSign(packageParams);
//System.out.println("sign:===@=="+sign);
String xml="<xml>"+
"<appid>"+小程序appId+"</appid>"+
"<mch_id>"+商户号+"</mch_id>"+
"<nonce_str>"+nonce_str+"</nonce_str>"+
"<sign>"+sign+"</sign>"+
"<body><![CDATA["+body+"]]></body>"+
"<out_trade_no>"+out_trade_no+"</out_trade_no>"+
"<total_fee>"+total_fee+"</total_fee>"+
"<spbill_create_ip>"+reqIp+"</spbill_create_ip>"+ //终端ip
"<notify_url>"+notify_url+"</notify_url>"+
"<trade_type>JSAPI</trade_type>"+
"<openid>"+openId+"</openid>"+
"</xml>";
logger.info("xml:===@=="+xml);
//获取openId后调用统一支付接口https://api.mch.weixin.qq.com/pay/unifiedorder
//获取预支付prepay_id
String createOrderURL = "https://api.mch.weixin.qq.com/pay/unifiedorder";
String prepay_id="";
try {
prepay_id = MiniUtil.getPayNo(createOrderURL, xml);
logger.info("获取预支付prepay_id:===@==" + prepay_id);
if(prepay_id.equals("")){
json.put("orderId", orderId);
json.put("orderName", body);
json.put("totalFee", totalFeeStr);
json.put("createTime", time_start);
json.put("ErrorMsg", "统一支付接口获取预支付订单出错");
return json.toString();
}
} catch (Exception e1) {
e1.printStackTrace();
return null;
}
SortedMap<String, String> finalpackage = new TreeMap<String, String>();
String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
String packages = "prepay_id="+prepay_id;
finalpackage.put("appId", "小程序appId");
finalpackage.put("timeStamp", timestamp);
finalpackage.put("nonceStr", nonce_str);
finalpackage.put("package", packages);
finalpackage.put("signType", "MD5");
String finalsign = reqHandler.createSign(finalpackage);
json.put("appid", "小程序appId");
json.put("timeStamp", timestamp);
json.put("nonceStr", nonce_str);
json.put("createTime", time_start);
json.put("package", packages);
json.put("paySign", finalsign);
json.put("orderId", orderId);
json.put("payPrice", total_fee); //支付金额,单位分
json.put("totalFeeStr", totalFeeStr); //原始的支付金额,单位元
return json.toString();
} catch (NumberFormatException e) {
e.printStackTrace();
logger.info(e);
return null;
}
}
运算方法
/**
* 提供精确的加法运算。
* @param v1 被加数
* @param v2 加数
* @return 两个参数的和
*/
public static double add(double v1,double v2){
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
return b1.add(b2).doubleValue();
}
获取支付时所需Ip地址
public String getIpAddress() {
HttpServletRequest request = this.getRequest();
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.getHeader("HTTP_CLIENT_IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
return ip;
}
RequestHandler 工具类
public class RequestHandler {
/** Token获取网关地址地址 */
private String tokenUrl;
/** 预支付网关url地址 */
private String gateUrl;
/** 查询支付通知网关URL */
private String notifyUrl;
/** 商户参数 */
private String appid;
private String appkey;
private String partnerkey;
private String appsecret;
private String key;
/** 请求的参数 */
private SortedMap parameters;
/** Token */
private String Token;
private String charset;
/** debug信息 */
private String debugInfo;
private String last_errcode;
private HttpServletRequest request;
private HttpServletResponse response;
/**
* 初始构造函数。
*
* @return
*/
public RequestHandler(HttpServletRequest request,
HttpServletResponse response) {
this.last_errcode = "0";
this.request = request;
this.response = response;
//this.charset = "GBK";
this.charset = "UTF-8";
this.parameters = new TreeMap();
// 验证notify支付订单网关
notifyUrl = "https://gw.tenpay.com/gateway/simpleverifynotifyid.xml";
}
/**
* 初始化函数。
*/
public void init(String app_id, String app_secret, String partner_key) {
this.last_errcode = "0";
this.Token = "token_";
this.debugInfo = "";
this.appid = app_id;
this.partnerkey = partner_key;
this.appsecret = app_secret;
this.key = partner_key;
}
public void init() {
}
/**
* 创建md5摘要,规则是:按参数名称a-z排序,遇到空值的参数不参加签名。
*/
public String createSign(SortedMap<String, String> packageParams) {
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 + "&");
}
}
sb.append("key=" + this.getKey());
String sign = MD5Util.MD5Encode(sb.toString(), this.charset)
.toUpperCase();
System.out.println("packge签名:" + sign);
return sign;
}
}
签名工具类
public class MD5Util {
private static String byteArrayToHexString(byte b[]) {
StringBuffer resultSb = new StringBuffer();
for (int i = 0; i < b.length; i++)
resultSb.append(byteToHexString(b[i]));
return resultSb.toString();
}
private static String byteToHexString(byte b) {
int n = b;
if (n < 0)
n += 256;
int d1 = n / 16;
int d2 = n % 16;
return hexDigits[d1] + hexDigits[d2];
}
public static String MD5Encode(String origin, String charsetname) {
String resultString = null;
try {
resultString = new String(origin);
MessageDigest md = MessageDigest.getInstance("MD5");
if (charsetname == null || "".equals(charsetname))
resultString = byteArrayToHexString(md.digest(resultString
.getBytes()));
else
resultString = byteArrayToHexString(md.digest(resultString
.getBytes(charsetname)));
} catch (Exception exception) {
}
return resultString;
}
private static final String hexDigits[] = { "0", "1", "2", "3", "4", "5",
"6", "7", "8", "9", "a", "b", "c", "d", "e", "f" };
}
获取预支付订单工具类
public class MiniUtil {
/**
* 模拟浏览器post提交
*
* @param url
* @return
*/
public static HttpPost getPostMethod(String url) {
HttpPost pmethod = new HttpPost(url); // 设置响应头信息
pmethod.addHeader("Connection", "keep-alive");
pmethod.addHeader("Accept", "*/*");
pmethod.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
pmethod.addHeader("Host", "api.mch.weixin.qq.com");
pmethod.addHeader("X-Requested-With", "XMLHttpRequest");
pmethod.addHeader("Cache-Control", "max-age=0");
pmethod.addHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0) ");
return pmethod;
}
/**
* 解析xml,返回第一级元素键值对。如果第一级元素有子节点,则此节点的值是子节点的xml数据。
* @param strxml
* @throws JDOMException
* @throws IOException
* @return Map
*/
public static Map doXMLParse(String strxml) throws Exception {
if (null == strxml || "".equals(strxml)) {
return null;
}
Map m = new HashMap();
InputStream in = new ByteArrayInputStream(strxml.getBytes());
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
*/
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();
}
//获取预支付订单
public static String getPayNo(String url, String xmlParam) {
DefaultHttpClient client = new DefaultHttpClient();
client.getParams().setParameter(ClientPNames.ALLOW_CIRCULAR_REDIRECTS,true);
HttpPost httpost = getPostMethod(url);
String prepay_id = "";
try {
try {
httpost.setEntity(new StringEntity(xmlParam, "UTF-8"));
HttpResponse response = client.execute(httpost);
String jsonStr = EntityUtils.toString(response.getEntity(), "UTF-8");
Map<String, Object> dataMap = new HashMap<String, Object>();
logger.info("预支付下单结果:====@==="+jsonStr);
if (jsonStr.indexOf("FAIL") != -1) {
return prepay_id;
}
Map map = doXMLParse(jsonStr);
String return_code = (String) map.get("return_code");
prepay_id = (String) map.get("prepay_id");
} catch (Exception e) {
e.printStackTrace();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if(!httpost.isAborted()){
httpost.abort();
}
client.getConnectionManager().shutdown();
}
return prepay_id;
}
}
第二步:小程序调起微信支付,完成支付
//微信支付
goPay(payData) {
wx.requestPayment({
timeStamp: payData.data.timeStamp,
nonceStr: payData.data.nonceStr,
package: payData.data.package,
signType: 'MD5',
paySign: payData.data.paySign,
success(res) {
console.log("支付成功", res)
//你可以在这里支付成功以后,再跳会webview页,并把支付成功状态传回去
wx.reLaunch({
url: '/pages/paySuccess/paySuccess',
});
},
fail(res) {
console.log("支付失败", res);
wx.reLaunch({
url: '/pages/payFail/payFail',
});
}
})
}