做了很多次都没整理,总以为脑子记住了,可当下次用的时候又跟个脑瘫孩儿一样,今天就记录一下部分代码。需要帮助加QQ 1137586868
首先解释一下微信的签名制作方式:这里可能让小白最头疼的了,统一下单和支付时要的签名制作方式也都不一样。我这里只打个比喻让你理解怎么制作,具体的代码里已经有了。
比如说你要给统一下单接口发送请求参数 A B C D E F(其中F代表的你签名),ABCDE都是固定的参数,如appId,时间戳等, 那F怎么得来呢? 其实就是 A+B+C+D+E 以后,在通过MD5编译,形成的结果就是很长的字符串,在把字符串转为大写就做成了你的签名F。
总的来说,其实就3步。
1发起统一下单接口
统一下单接口是由java来搞得,但 通常情况下,由用户在小程序点击支付按钮触发,所以需要在小程序部分先发起请求,把自己业务的数据传到后台,在进行统一下单。
// 立即购买
justBuy: function () {
var that = this
wx.request({
url: app.globalData.PROJECTPATH + '/task/saveSalarGoods.htm',
data: {
userId: app.globalData.userId,//自己的用户ID
openid: app.globalData.openid,//小程序用户的ID,
goodsId: that.data.result.id,//自己商品ID
name: that.data.addressArr[0].name,//自己的
phone: that.data.addressArr[0].phone,//自己的
address: that.data.addressArr[0].address,//自己的
addressDetail: that.data.addressArr[0].addressDetail//自己的
}, success: function (res) {
//统一订单接口执行完成后,由小程序发起支付,调用wx.requestPayment方法,这个方法在第二步介绍
}
}
})
}
下面是java的统一下单代码
/**
* 购买商品统一下单接口
* @throws UnsupportedEncodingException
*/
@RequestMapping("saveSalarGoods.htm")
public void salasGoods(String name,String phone,String address,String addressDetail,String openid,String goodsId,String userId,HttpServletRequest request,HttpServletResponse response) throws UnsupportedEncodingException{
if (StringUtils.isBlank(userId)) {
printStrAjax(response, HttpUtil.getClientResult(-1, "传递参数错误", null));
return;
}
YpSales ypgoods = ypSalesService.selectById(goodsId);
if (ypgoods.getGoods_status() == 2 || ypgoods.getGoods_status() == 3 ) {
printStrAjax(response, HttpUtil.getClientResult(-1, "该商品已出售,请刷新页面获取最新数据", null));
return;
}
ypgoods.setAddress_user(new String(name.getBytes("ISO-8859-1"), "UTF-8"));
ypgoods.setAddress_phone(phone);
ypgoods.setAddress(new String(address.getBytes("ISO-8859-1"), "UTF-8"));
ypgoods.setAddress_detail(new String(addressDetail.getBytes("ISO-8859-1"), "UTF-8"));
ypSalesService.saveSales(ypgoods);
String amount = String.valueOf(ypgoods.getGoods_money()*Constants.MOENY_PRPORTION);
String payId = payService.savePay(goodsId, userId, Integer.parseInt(amount), 4);
//上面的都是我的业务代码,下面开始进行统一下单接口前得准备工作
//创建hashmap(用户获得签名)
SortedMap<String, String> paraMap = new TreeMap<String, String>();
//设置body变量 (支付成功显示在微信支付 商品详情中)
String body = "LHGS-PAY-GOODS";
//设置随机字符串
String nonceStr = PayUtil.createCode(32);
//设置请求参数(小程序ID)
paraMap.put("appid", Constants.appID);
//设置请求参数(商户号)
paraMap.put("mch_id", Constants.MCH_ID);
//设置请求参数(随机字符串)
paraMap.put("nonce_str", nonceStr);
//设置请求参数(商品描述)
paraMap.put("body", PayUtil.urlEncodeUTF8(body));
//设置请求参数(商户订单号)
paraMap.put("out_trade_no", payId);
//设置请求参数(总金额)
paraMap.put("total_fee", amount);
//设置请求参数(终端IP)
paraMap.put("spbill_create_ip", "127.0.0.1");
//设置请求参数(回调通知地址)
paraMap.put("notify_url", Constants.SALES_PAY_PATH);
//设置请求参数(交易类型)
paraMap.put("trade_type", "JSAPI");
//设置请求参数(openid)(在接口文档中 该参数 是否必填项 但是一定要注意 如果交易类型设置成'JSAPI'则必须传入openid)
paraMap.put("openid", openid);
//自定义参数(我放的流水ID,用于回调的时候修改状态)
paraMap.put("device_info", payId);
String stringA = PayUtil.createLinkString(paraMap);
//第二步,在stringA最后拼接上key得到stringSignTemp字符串,并对stringSignTemp进行MD5运算,再将得到的字符串所有字符转换为大写,得到sign值signValue。(签名)
String sign = PayUtil.sign(stringA, Constants.key, "utf-8").toUpperCase();
//将参数 编写XML格式
paraMap.put("sign",sign);
String s = null;
s = PayUtil.GetMapToXML(paraMap);
Map map = null;
try {
map = PayUtil.doXMLParse(PayUtil.httpRequest(Constants.UNIFIED_ORDER_PATH,s));
} catch (Exception e) {
printStrAjax(response, HttpUtil.getClientResult(-1, "统一下单接口执行失败", map));
return;
}
if (!map.get("return_code").equals("SUCCESS")) {
printStrAjax(response, HttpUtil.getClientResult(-1, "统一下单接口执行失败", map));
return;
}
/**
* 统一下单接口执行成功,即map中返回值return_code=SUCCESS时。
* 制作支付的请求参数,和签名,
*/
SortedMap<String, String> paraMapPay = new TreeMap<String, String>();
String timeStampPay = Long.toString(System.currentTimeMillis() / 1000);
String nonceStrPay = PayUtil.createCode(32);
String packagePay = (String) map.get("prepay_id");//请求统一下单成功时,获取这个参数,调起支付时要传递
String signTypePay ="MD5";
paraMapPay.put("timeStamp", timeStampPay);
paraMapPay.put("nonceStr", nonceStrPay);
paraMapPay.put("package", "prepay_id="+packagePay);
paraMapPay.put("signType", signTypePay);
paraMapPay.put("appId", Constants.appID);
String payString = PayUtil.createLinkString(paraMapPay);
String signPay = PayUtil.sign(payString, Constants.key, "utf-8").toUpperCase();
String paySignPay = signPay;
//这是传递给小程序的支付所需要的参数。
Map<String, String> mapPay = new HashMap<String, String>();
mapPay.put("timeStampPay", timeStampPay);
mapPay.put("nonceStrPay", nonceStrPay);
mapPay.put("packagePay", "prepay_id="+packagePay);//传递时=号不要忘记
mapPay.put("signTypePay", signTypePay);
mapPay.put("paySignPay", paySignPay);
printStrAjax(response, HttpUtil.getClientResult(1, "统一下单接口执行成功", mapPay));
}
以上代码,已经完成统一下单和支付前得准备工作。
2进行支付
当统一下单返回成功时,并且已经把调起支付的请求参数准备好了,那么直接调用wx.requestPayment。改方法可以写在统一下单请求的返回里面,参考第一步。
切记下面的参数都是JAVA后台制作好传递过来的。
wx.requestPayment(
{
'timeStamp': res.data.result.timeStampPay,
'nonceStr': res.data.result.nonceStrPay,
'package': res.data.result.packagePay,
'signType': 'MD5',
'paySign': res.data.result.paySignPay,
'success': function (res) {
console.info("微信支付成功");
wx.redirectTo({//这里的成功不代表真的成功,微信文档上说的,具体要看支付回调通知,在java代码里。
url: "../status/status?desc=支付成功&price=" + that.data.myMoney + "元&status=success&title=支付成功"
})
},
'fail': function (res) {
console.info("掉起支付失败");
wx.redirectTo({
url: "../status/status?desc=支付失败&status=fail&title=支付失败"
})
},
'complete': function (res) {
console.info("");
}
})
3支付回调
上面支付成功后,就比较简单了,微信会给你发很多次支付是否成功的请求,你必须得给人家回一个“知道了”,
他才停止。 这个请求需要你在微信小程序里面配置,并且在java代码中,支付的时候也要传递。
这里需要注意的是自己的业务,因为微信发很多次回调,当你接收了以后,你首先更改你订单的支付状态,其次加减库存,所以,在加减库存等操作的时候,可不要随着微信给你请求次数不断的叠加,一定要判断哦。
* 销售商品金额支付后的回调函数
* @param code
* @param request
* @param response
*/
@RequestMapping("salesnotify.htm")
public void salesnotify(HttpServletRequest request,HttpServletResponse response){
try{
logger.info("info已经入支付成功返回接口--销售");
BufferedReader br = new BufferedReader(new InputStreamReader((ServletInputStream)request.getInputStream()));
String line = null;
StringBuilder sb = new StringBuilder();
while((line = br.readLine()) != null){
sb.append(line);
}
br.close();
//sb为微信返回的xml
String notityXml = sb.toString();
String resXml = "";
System.out.println("---------------接收到的报文------------:" + notityXml);
Map map = PayUtil.doXMLParse(notityXml);
System.out.println("---------------转换MAP正常------------:");
String returnCode = (String) map.get("return_code");
if("SUCCESS".equals(returnCode)){
logger.info("---------------支付成功已进入------------");
//验证签名是否正确
Map<String, String> validParams = PayUtil.paraFilter(map); //回调验签时需要去除sign和空值参数
String validStr = PayUtil.createLinkString(validParams);//把数组所有元素,按照“参数=参数值”的模式用“&”字符拼接成字符串
String sign = PayUtil.sign(validStr, Constants.key, "utf-8").toUpperCase();//拼装生成服务器端验证的签名
// 因为微信回调会有八次之多,所以当第一次回调成功了,那么我们就不再执行逻辑了
System.out.println("---------------组装签名已经完成------------:");
//根据微信官网的介绍,此处不仅对回调的参数进行验签,还需要对返回的金额与系统订单的金额进行比对等
if(sign.equals(map.get("sign"))){
System.out.println("---------------签名正常------------");
/**此处添加自己的业务逻辑代码start**/
String payId = (String) map.get("device_info");//获取我的自定义刺激参数
if (StringUtils.isNotBlank(payId)) {
ypSalesService.updateNotify(payId);
}
/**此处添加自己的业务逻辑代码end**/
//通知微信服务器已经支付成功
resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>"
+ "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";
} else {
System.out.println("---------------微信支付回调失败!签名不一致------------");
}
}else{
System.out.println("---------------支付失败------------");
resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"
+ "<return_msg><![CDATA[报文为空]]></return_msg>" + "</xml> ";
}
System.out.println(resXml);
System.out.println("微信支付回调数据结束");
BufferedOutputStream out = new BufferedOutputStream(
response.getOutputStream());
out.write(resXml.getBytes());
out.flush();
out.close();
logger.info("---------------已反馈给微信------------");
} catch (Exception e) {
logger.info("---------------进入异常------------");
}
}
直接复制使用的话,很多方法找不到,可以加我Q 1137586868
后面再发一篇退款的代码