本文主要是介绍服务端微信支付(APP和JSAPI支付)相关问题,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
服务端微信支付相关问题
JSAPI开发文档
jsapi开发文档链接
app开发文档链接
统一下单参数
在开发前需要先申请商户平台申请商户号和APP支付(在开放平台申请账号)、JSAPI(在公众平台申请账号)
在开发过程中先后集成了APP支付和公众号支付(JSAPI支付)此两种支付方式统一下单的参数不同,详见下方:
共同参数:
mch_id:商户号;nonce_str:随机字符串;body:商品描述;out_trade_no:商户订单号;total_fee:订单总金额,单位为分;spbill_create_ip:终端IP;notify_url:回调地址(此地址为微信支付成功后会回调请求该地址,商户方再此处理支付成功后的业务逻辑处理)
不同参数:
appid:APP支付(开放平台ID)、JSAPI支付(公众号ID);
trade_type:APP支付(APP)、JSAPI支付(JSAPI);
openid:JSAPI支付必传此参数;
注意事项
1.当支付方式为jsapi时统一下单接口参数返回前端务必注意部分参数的大小写
如appId I是大写,signType T为大写,timeStamp S是大写,nonceStr S是大写,否则前端JS调微信SDK时会一直报验签失败
2.支付方式为jsapi时统一下单请求微信后台的参数必须有openId,每个关注过公众号用户的Openid是唯一的
3.微信授权信息调微信后台接口时需要将获取的access_token和jsapiTicket放至缓存,因为每天请求此接口有次数限制,有效期为两个小时
4.统一下单中生成签名前需要在微信商户平台中设备Key密钥
统一下单JAVA代码如下
@Override@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)public Map<Object,Object> saveOrder(String total, String loginUserId, String billsId,String feeType,String openId) {try {Map<Object, Object> map = new HashMap<>();//根据openId是否为空进行判断是app支付还是公众号支付if(StringUtils.isNotEmpty(openId)){map.put("appid", WECHAT_OFFICIAL_APPID);map.put("trade_type", "JSAPI");map.put("openid", openId);}else{map.put("appid", APPID);map.put("trade_type", TRADE_TYPE);}map.put("mch_id", MCHID);map.put("nonce_str", RandomUtil.getRandomStringByLength(32));map.put("body", "家半径-微信支付");map.put("out_trade_no", RandomUtil.getRandomStringByLength(32));//处理金额去除小数点DecimalFormat decimalFormat = new DecimalFormat("0");decimalFormat.setRoundingMode(RoundingMode.HALF_UP);BigDecimal big = new BigDecimal(total).multiply(new BigDecimal("100"));map.put("total_fee", Integer.parseInt(decimalFormat.format(big)));map.put("spbill_create_ip", RandomUtil.getHostIp());if(billsId==""){map.put("notify_url", SHOW_NOTIFY_URL);}else{map.put("notify_url", NOTIFY_URL);}//根据算法生成签名String sign = Md5EncryptUtil.getWechatSign(map, KEY_APPSECRET);map.put("sign", sign);//将参数转换成xmlString paramXML = XMLParser.converterPayPalm(map);//通过HTTPS请求调用微信的统一下单请求String responseXML = HttpProtocolUtil.httpPost(PREPAYURL, paramXML);Map<Object,Object> returnData = new HashMap<>();//检验API返回的数据里面的签名是否合法,避免数据在传输的过程中被第三方篡改.Boolean bo = Md5EncryptUtil.checkWechatResponseSign(responseXML,KEY_APPSECRET);if (!bo) {returnData.put("status",1);return returnData;}//将返回数据XML转为Map格式Map<Object, Object> responseMap =XMLParser.xmlString2Map(responseXML);PaymentTradePO paymentTradePO;if (responseMap.get("return_code").toString().contains("SUCCESS") && responseMap.get("result_code").toString().contains("SUCCESS")) {paymentTradePO = new PaymentTradePO();paymentTradePO.setTradeId(map.get("out_trade_no").toString());paymentTradePO.setPayType(PaymentTypeEnum.WECHAT_PAY.getType());paymentTradePO.setPrice(new BigDecimal(total));paymentTradePO.setUserId(loginUserId);paymentTradePO.setSuccessReturn(PaySuccessReturnEnum.IS_NOT_RETURN.getType());paymentTradePO.setTradeStatus(PayStatusEnum.WAIT_PAY.getType());paymentTradePO.setCreateTime(new Date());paymentTradePO.setStatus(DataStatusEnum.NORMAL.getType());paymentTradePO.setFeeType(feeType);if(StringUtils.isNotEmpty(billsId)){paymentTradePO.setFeeId(billsId);}//统一下单成功后保存商户后台系统的订单表iWechatPayDao.save(SqlUtil.durableData(paymentTradePO, PlatformConstants.TABLE_SAVE));//处理返回APP端的参数及生成signMap<Object,Object> resultMap = new HashMap<>();if(StringUtils.isNotEmpty(openId)){resultMap.put("appId", WECHAT_OFFICIAL_APPID);resultMap.put("signType", "MD5");resultMap.put("package",String.format("%s%s","prepay_id=",responseMap.get("prepay_id").toString()));resultMap.put("timeStamp", DateUtil.getSecondTimestamp(new Date()));resultMap.put("nonceStr",RandomUtil.getRandomStringByLength(32));}else{resultMap.put("appid", APPID);resultMap.put("partnerid",MCHID);resultMap.put("prepayid",responseMap.get("prepay_id").toString());resultMap.put("package",PACKAGE);resultMap.put("timestamp", DateUtil.getSecondTimestamp(new Date()));resultMap.put("noncestr",RandomUtil.getRandomStringByLength(32));}//根据算法生成签名String returnSign = Md5EncryptUtil.getWechatSign(resultMap, KEY_APPSECRET);if(StringUtils.isNotEmpty(openId)){resultMap.put("paySign",returnSign);}else{resultMap.put("sign",returnSign);}//status 0代表统一下单成功 1代表微信系统后台返回sign不合法 2代表与微信后台交互其它原因resultMap.put("status",0);resultMap.put("tradeId",map.get("out_trade_no").toString());return resultMap;}if(responseMap.get("return_msg")!=null){Map<Object,Object> returnMsg = new HashMap<>();returnMsg.put("return_msg",responseMap.get("return_msg"));returnMsg.put("status",2);return returnMsg;}return null;} catch (Exception e) {throw new DataAccessException("【App_微信支付】统一下单失败", e);}}
生成签名的方法
public static String getWechatSign(Map<Object,Object> map,String key){ArrayList<String> list = new ArrayList<String>();for(Map.Entry<Object,Object> entry:map.entrySet()){if(entry.getValue()!=""){list.add(entry.getKey() + "=" + entry.getValue() + "&");}}int size = list.size();String [] arrayToSort = list.toArray(new String[size]);Arrays.sort(arrayToSort, String.CASE_INSENSITIVE_ORDER);StringBuilder sb = new StringBuilder();for(int i = 0; i < size; i ++) {sb.append(arrayToSort[i]);}String result = sb.toString();result += "key=" + key;LOG.error("sign Before MD5:" + result);result = md5(result).toUpperCase();LOG.error("sign Result:" + result);return result;}
验签工具类
public static boolean checkWechatResponseSign(String responseString,String key) throws DocumentException {
// Map<Object,Object> map = XMLParser.getMapFromXML(responseString, "xml");Map<Object, Object> map = XMLParser.xmlString2Map(responseString);String signFromAPIResponse = map.get("sign")==null ? null:map.get("sign").toString();if(signFromAPIResponse == null || signFromAPIResponse.equals("")){LOG.error("API返回的数据签名数据不存在,有可能被第三方篡改!!!");return false;}LOG.error("服务器回包里面的签名是:" + signFromAPIResponse);//清掉返回数据对象里面的Sign数据map.put("sign","");//将API返回的数据根据用签名算法进行计算新的签名,用来跟API返回的签名进行比较String signForAPIResponse = getWechatSign(map, key);if(!signForAPIResponse.equals(signFromAPIResponse)){//签名验不过,表示这个API返回的数据有可能已经被篡改了LOG.error("API返回的数据签名验证不通过,有可能被第三方篡改!!!");return false;}LOG.error("恭喜,API返回的数据签名验证通过!!!");return true;}
将map参数转换成XML工具类
public static String converterPayPalm(Map<Object, Object> dataMap) {StringBuilder strBuilder = new StringBuilder();try {strBuilder.append("<xml>");Set<Object> objSet = dataMap.keySet();for (Object key : objSet) {if (key == null) {continue;}//strBuilder.append("\n");strBuilder.append("<").append(key.toString()).append(">");Object value = dataMap.get(key);strBuilder.append(coverter(value).trim());strBuilder.append("</").append(key.toString()).append(">");}strBuilder.append("</xml>");} catch (Exception e) {LOG.error("MAP转换XML异常:" + e);}return strBuilder.toString();}
将XML转为MAP
public static Map<Object,Object> xmlString2Map(String xmlStr){Map<Object,Object> map = new HashMap<Object,Object>();Document doc;try {doc = DocumentHelper.parseText(xmlStr);Element el = doc.getRootElement();map = recGetXmlElementValue(el,map);} catch (DocumentException e) {e.printStackTrace();}return map;}
JSAPI支付获取openId
public MessageVO getOpenId(String code){Map<String, Object> map = new HashMap<>();String status = "1";String msg = "ok";String requestUrl = WECHAT_OFFICIAL_REQUEST_URL +"?appid="+WECHAT_OFFICIAL_APPID+"&secret="+WECHAT_OFFICIAL_SECRET+"&code="+code+"&grant_type=authorization_code";try {if(StringUtils.isBlank(code)){status = "0";//失败状态msg = "code为空";}else {System.out.println(requestUrl);// 发起GET请求获取凭证JSONObject jsonObject = HttpProtocolUtil.httpsRequest(requestUrl, "GET", null);if (jsonObject != null) {try {map.put("openid", jsonObject.getString("openid"));} catch (JSONException e) {// 获取token失败status = "0";msg = "code无效";}}else {status = "0";msg = "code无效";}}map.put("status", status);map.put("msg", msg);} catch (Exception e) {throw new DataAccessException("【微信公众号_停车场】获取openId失败",e);}return new MessageVO(BaseErrorCodeEnum.SUCCESS.getErrorCode(),map);}
JSAPI获取微信授权信息
public MessageVO getJsapiTicket(String url) throws Exception {url = URLDecoder.decode(url, "UTF-8");Map<String, String> map = new HashMap<>();String requestUrl = "https://api.weixin.qq.com/cgi-bin/token"+"?appid="+WECHAT_OFFICIAL_APPID+"&secret="+WECHAT_OFFICIAL_SECRET+"&grant_type=client_credential";try {String ticket = "";//先从缓存中取access_token和ticket其有效期为两个小时,如取不到再调接口获取ticket = redisService.get(RedisConstant.WECHAT_OFFICIAL_TICKET);//url = "http://develop.wx.jia-r.com/?code=0018eRlg2PT5xI0oGfmg2DZElg28eRlZ&state=";if(StringUtils.isEmpty(ticket)){//发起GET请求获取凭证JSONObject jsonObject = HttpProtocolUtil.httpsRequest(requestUrl, "GET", null);if (jsonObject != null) {String accessToken = jsonObject.getString("access_token");if(StringUtils.isNotEmpty(accessToken)){redisService.set(RedisConstant.WECHAT_OFFICIAL_ACCESS_TOKEN,RedisConstant.WECHAT_OFFICIAL_TIME,accessToken);}String requestSecondUrl = "https://api.weixin.qq.com/cgi-bin/ticket/getticket"+"?access_token="+accessToken+"&type=JSAPI";JSONObject jsonObjectSecond = HttpProtocolUtil.httpsRequest(requestSecondUrl, "GET", null);if(jsonObjectSecond != null){ticket = jsonObjectSecond.getString("ticket");if(StringUtils.isNotEmpty(ticket)){redisService.set(RedisConstant.WECHAT_OFFICIAL_TICKET,RedisConstant.WECHAT_OFFICIAL_TIME,ticket);}}}}map = Sha1EncryptUtil.getSign(ticket, url);} catch (Exception e) {throw new DataAccessException("【微信公众号_停车场】获取ticket失败",e);}return new MessageVO(BaseErrorCodeEnum.SUCCESS.getErrorCode(),map);}
微信支付成功回调代码
@ResponseBody@RequestMapping("/payResultNotice")public String payResultNotice(HttpServletRequest request) {try {System.out.print("微信支付回调获取数据开始");LOG.info("微信支付回调获取数据开始");LOG.error("【微信支付回调获取数据开始】");String inputLine;String notityXml = "";try {while ((inputLine = request.getReader().readLine()) != null) {notityXml += inputLine;}request.getReader().close();} catch (Exception e) {LOG.info("xml获取失败:" + e);throw new Exception(e);}System.out.println("接收到的报文:" + notityXml);String projectId = "";AppPayParkFeeNoticeVO payParkFeeNoticeVO = wechatPayServiceClient.payResultNotice(notityXml);return payParkFeeNoticeVO.getWechatReturnNotice();} catch (Exception e) {throw new DataAccessException("【App_微信支付】API返回的数据异常", e);}}
这篇关于服务端微信支付(APP和JSAPI支付)相关问题的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!