第一步,小程序部分功能调用,小程序页面的Wxml类似于html
<button bindtap="payFor" type="primary" size="mini">支付按钮</button>
payFor:function(){
var openid=app.globalData.open;
console.log(openid);
wx.request({
url:'https://域名地址/puJin/Pay/wxPrePay.do',
method:'GET',
data:{
openid:openid
},
header:{
'content-type':'application/json'
},
success:function(res){
console.log(res.data);
var nonceStr=res.data.msg.nonceStr;
var packageStr=res.data.msg.package;
var paySign=res.data.msg.paySign;
var timeStamp=res.data.msg.timeStamp;
console.log(nonceStr);
console.log(packageStr);
console.log(paySign);
console.log(timeStamp);
wx.requestPayment(
{
'timeStamp': timeStamp,
'nonceStr': nonceStr,
'package': packageStr,
'signType': 'MD5',
'paySign': paySign,
'success':function(res){
console.log(res);
},
'fail':function(res){
console.log(res);
},
'complete':function(res){
console.log(res);
}
})
},
fail:function(res){
console.log("--------fail--------");
},
complete: function (res) {
wx.hideNavigationBarLoading(); //完成停止加载图标
wx.stopPullDownRefresh();
}
})
}
第二,获取openID,调用后端接口得到nonceStr,package,paySign,timeStamp需要给后台传递openid,小程序APP.js设置全局变量,这里var tha=this;一定要这样写,具体原因可以留言问我
App({
globalData:{
open:''
},
onLaunch: function () {
var that=this;
wx.login({
success: function(res) {
// console.log(res.code);
if (res.code) {
//发起网络请求
var code=res.code;
wx.request({
url: 'https://域名地址或IP地址/puJin/Pay/getOpenid.do',
method:'GET',
data:{
code:code
},
header: {
'content-type':'application/json'
},
success: (res) =>{
var openid = res.data.openid //返回openid
console.log('openid为' + openid);
that.globalData.open = openid;
if (that.testDataCallback){
that.testDataCallback(res.testData);
}
}
})
} else {
console.log('获取用户登录态失败!' + res.errMsg)
}
}
});
}
})
第三,传递code,从后台获取openid ,前端不能获取,因为这是微信的一个坑,前端虽然也能获取到,但是微信的域名正式上线不能通过,而且小程序里面也配置不了微信的域名
@RequestMapping("/getOpenid")
@ResponseBody//不添加 前台接收不到后台返回的数据
public Map<String,Object> getOpenid(HttpServletRequest request,HttpServletResponse response,String orderNumber,ModelMap mmp){
String code=request.getParameter("code");
System.out.println("code="+code);
String u1="https://api.weixin.qq.com/sns/jscode2session?appid=wx9c5861fd987d9ec3&secret=d42093a70df1175f88a090dfc46a4b83&js_code="+code+"&grant_type=authorization_code";
String str1=InterFaceDeal.interfaceUtil(u1);
JSONObject js=JSONObject.parseObject(str1);
String openid=(String) js.get("openid");
System.out.println("openid="+openid);
Map<String, Object> map=new HashMap<String, Object>();
map.put("openid", openid);
map.put("session_key", js.get("session_key"));
return map;
}
第四,请求前面小程序页面请求的接口 url:'https://域名地址或者本地测试地址/puJin/Pay/wxPrePay.do',
/**
* 微信统一下单接口
* @param request
* @param response
* @throws Exception
*/
@RequestMapping("/wxPrePay")
@ResponseBody//不添加 前台接收不到后台返回的数据
public Map<String,Object> wxPrePay(HttpServletRequest request,HttpServletResponse response,String openid) throws Exception{
// response.setContentType("text/html;charset=utf-8");
// response.setHeader("Access-Control-Allow-Origin", "*");
// // 星号表示所有的异域请求都可以接受,
// response.setHeader("Access-Control-Allow-Methods", "GET,POST");
String orderNumber=CreateOrderNumber.getOrderIdByTime();
System.out.println("openid="+openid);
Map<String, Object> map = new HashMap<>();
//正式环境
String notify_url= "https://zmrcm.cn/puJin/Pay/notify_url.do";
// Order payOrder = orderService.selectByorderNumber(orderNumber);
Map<String,String> parameters = new HashMap<String,String>();
parameters.put("appid", "小程序appid");
parameters.put("mch_id", "商户号"); //商户号
String nonce_str=WXPayUtil.generateNonceStr();
parameters.put("nonce_str", nonce_str); //随机字符串
parameters.put("body","微信小程序支付"); //订单详情
parameters.put("out_trade_no", orderNumber); //订单id
parameters.put("fee_type", "CNY"); //标价币种
// String openid=(String) openidMap.get("openid");
parameters.put("openid",openid); // 微信公众号统一标示openid
//String total_fee=String.format("%.2f",Double.parseDouble(payOrder.getActual_pay()));
// String total_fee=new BigDecimal(100).multiply(new BigDecimal(payOrder.getActual_pay())).setScale(0,BigDecimal.ROUND_DOWN).toString();
parameters.put("total_fee","100"); //支付金额 以分为单位
parameters.put("spbill_create_ip",getIpAddr(request));//下单ip
parameters.put("notify_url", notify_url); //下边那个方法地址 支付成功回调路径
parameters.put("trade_type", "JSAPI"); // 固定填写
//设置签名
String paysign=WXPayUtil.generateSignature(parameters, "自己的(API密钥)API_KEY");
parameters.put("sign", paysign);
//封装请求参数结束
String requestXML = WXPayUtil.mapToXml(parameters);
//调用统一下单接口
String result = PayCommonUtil.httpsRequest(ConfigUtil.UNIFIED_ORDER_URL, "POST", requestXML);
//String unifiedorder_url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
//String result = HttpRequest.sendPost(unifiedorder_url, requestXML);//发送post请求"统一下单接口"返回预支付id:prepay_id
// System.out.println(result);
try {
// Map<String, String> resultMap=WXPay.unifiedOrder(parameters);
/**统一下单接口返回正常的prepay_id,再按签名规范重新生成签名后,将数据传出。参与签名的字段名为appId,partnerId,prepayId,nonceStr,timeStamp,package。注意:package的值格式为Sign=WXPay**/
Map<String, String> resultMap = WXPayUtil.xmlToMap(result);
System.out.println("resultMap="+resultMap);
Map<String,String> parameterMap2 = new HashMap<String,String>();
parameterMap2.put("appId", "小程序appid");
parameterMap2.put("package", "prepay_id="+resultMap.get("prepay_id").toString().trim());//预付款id
parameterMap2.put("nonceStr",nonce_str);//随机字符串
parameterMap2.put("signType", WXPayConstants.MD5);//MD5加密 非必须
//生成的时间戳是13位,ios必须是10位
String timestamp=WXPayUtil.getCurrentTimestamp()+"";
parameterMap2.put("timeStamp",timestamp);
String sign2 = WXPayUtil.generateSignature(parameterMap2,"自己的(API密钥)API_KEY");
parameterMap2.put("paySign", sign2);
map.put("code","200");
map.put("msg",parameterMap2);
} catch (JDOMException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return map;
}
第六,
/**
* 财付通通知
* @param request
* @param response
* @throws Exception
*/
@RequestMapping("/notify_url")
public void ten_notify_url(HttpServletRequest request,HttpServletResponse response) throws Exception{
//---------------------------------------------------------
//财付通支付通知(后台通知)示例,商户按照此文档进行开发即可
//---------------------------------------------------------
//log.info("财付通支付通知开始");
String result = getRequestString(request);
Map parse = XMLUtil.doXMLParse(result);
String out_trade_no = parse.get("out_trade_no").toString();
String result_code = parse.get("result_code").toString();
String transaction_id = parse.get("transaction_id").toString();
// 微信支付订单号 transaction_id
// log.info("商户订单号:"+out_trade_no+"......交易状态:"+result_code);
System.out.println("===============================");
System.out.println("商户订单号:"+out_trade_no+"......交易状态:"+result_code);
System.out.println("===============================");
if (parse.get("result_code").toString().equalsIgnoreCase("SUCCESS")) { // 交易支付成功的执行相关业务逻辑
// Order orderInfo = orderService.selectByorderNumber(out_trade_no);
// orderInfo.setOrder_title("微信付款");
// orderInfo.setStatus("1");
// orderInfo.setPayway("0");
// int i=orderService.updateByPrimaryKeySelective(orderInfo);
response.getWriter().write(setXML("SUCCESS", "")); //告诉微信服务器,我收到信息了,不要在调用回调action了
} else {
// Order orderInfo = orderService.selectByorderNumber(out_trade_no);
// orderInfo.setOrder_title("未付款,交易关闭!");
// int i=orderService.updateByPrimaryKeySelective(orderInfo);
}
}
private String setXML(String return_code, String return_msg) {
return "<xml><return_code><![CDATA[" + return_code + "]]></return_code>"+
"<return_msg><![CDATA[" + return_msg+ "]]></return_msg></xml>";
}
private String getRequestString(HttpServletRequest request) throws IOException{
InputStream inStream = request.getInputStream();
ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len = 0;
while ((len = inStream.read(buffer)) != -1) {
outSteam.write(buffer, 0, len);
}
outSteam.close();
inStream.close();
return new String(outSteam.toByteArray(),"utf-8");//获取微信调用我们notify_url的返回信息
}
public String getIpAddr(HttpServletRequest request) {
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.getRemoteAddr();
}
return ip;
}
第七,工具包
* @return String 随机字符串
*/
public static String generateNonceStr() {
char[] nonceChars = new char[32];
for (int index = 0; index < nonceChars.length; ++index) {
nonceChars[index] = SYMBOLS.charAt(RANDOM.nextInt(SYMBOLS.length()));
}
return new String(nonceChars);
}
/**
* 生成签名
*
* @param data 待签名数据
* @param key API密钥
* @return 签名
*/
public static String generateSignature(final Map<String, String> data, String key) throws Exception {
return generateSignature(data, key, SignType.MD5);
}
/**
* 生成签名. 注意,若含有sign_type字段,必须和signType参数保持一致。
*
* @param data 待签名数据
* @param key API密钥
* @param signType 签名方式
* @return 签名
*/
public static String generateSignature(final Map<String, String> data, String key, SignType signType) throws Exception {
Set<String> keySet = data.keySet();
String[] keyArray = keySet.toArray(new String[keySet.size()]);
Arrays.sort(keyArray);
StringBuilder sb = new StringBuilder();
for (String k : keyArray) {
if (k.equals(WXPayConstants.FIELD_SIGN)) {
continue;
}
if (data.get(k).trim().length() > 0) // 参数值为空,则不参与签名
sb.append(k).append("=").append(data.get(k).trim()).append("&");
}
sb.append("key=").append(key);
if (SignType.MD5.equals(signType)) {
return MD5(sb.toString()).toUpperCase();
}
else if (SignType.HMACSHA256.equals(signType)) {
return HMACSHA256(sb.toString(), key);
}
else {
throw new Exception(String.format("Invalid sign_type: %s", signType));
}
}
/**
* 获取当前时间戳,单位秒
* @return
*/
public static long getCurrentTimestamp() {
return System.currentTimeMillis()/1000;
}
/**
* 获取当前时间戳,单位毫秒
* @return
*/
public static long getCurrentTimestampMs() {
return System.currentTimeMillis();
}