服务端微信支付(APP和JSAPI支付)相关问题

2024-01-21 11:18

本文主要是介绍服务端微信支付(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支付)相关问题的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/629338

相关文章

线上Java OOM问题定位与解决方案超详细解析

《线上JavaOOM问题定位与解决方案超详细解析》OOM是JVM抛出的错误,表示内存分配失败,:本文主要介绍线上JavaOOM问题定位与解决方案的相关资料,文中通过代码介绍的非常详细,需要的朋... 目录一、OOM问题核心认知1.1 OOM定义与技术定位1.2 OOM常见类型及技术特征二、OOM问题定位工具

Vue3绑定props默认值问题

《Vue3绑定props默认值问题》使用Vue3的defineProps配合TypeScript的interface定义props类型,并通过withDefaults设置默认值,使组件能安全访问传入的... 目录前言步骤步骤1:使用 defineProps 定义 Props步骤2:设置默认值总结前言使用T

Web服务器-Nginx-高并发问题

《Web服务器-Nginx-高并发问题》Nginx通过事件驱动、I/O多路复用和异步非阻塞技术高效处理高并发,结合动静分离和限流策略,提升性能与稳定性... 目录前言一、架构1. 原生多进程架构2. 事件驱动模型3. IO多路复用4. 异步非阻塞 I/O5. Nginx高并发配置实战二、动静分离1. 职责2

解决升级JDK报错:module java.base does not“opens java.lang.reflect“to unnamed module问题

《解决升级JDK报错:modulejava.basedoesnot“opensjava.lang.reflect“tounnamedmodule问题》SpringBoot启动错误源于Jav... 目录问题描述原因分析解决方案总结问题描述启动sprintboot时报以下错误原因分析编程异js常是由Ja

MySQL 表空却 ibd 文件过大的问题及解决方法

《MySQL表空却ibd文件过大的问题及解决方法》本文给大家介绍MySQL表空却ibd文件过大的问题及解决方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考... 目录一、问题背景:表空却 “吃满” 磁盘的怪事二、问题复现:一步步编程还原异常场景1. 准备测试源表与数据

解决Nginx启动报错Job for nginx.service failed because the control process exited with error code问题

《解决Nginx启动报错Jobfornginx.servicefailedbecausethecontrolprocessexitedwitherrorcode问题》Nginx启... 目录一、报错如下二、解决原因三、解决方式总结一、报错如下Job for nginx.service failed bec

SysMain服务可以关吗? 解决SysMain服务导致的高CPU使用率问题

《SysMain服务可以关吗?解决SysMain服务导致的高CPU使用率问题》SysMain服务是超级预读取,该服务会记录您打开应用程序的模式,并预先将它们加载到内存中以节省时间,但它可能占用大量... 在使用电脑的过程中,CPU使用率居高不下是许多用户都遇到过的问题,其中名为SysMain的服务往往是罪魁

MySQ中出现幻读问题的解决过程

《MySQ中出现幻读问题的解决过程》文章解析MySQLInnoDB通过MVCC与间隙锁机制在可重复读隔离级别下解决幻读,确保事务一致性,同时指出性能影响及乐观锁等替代方案,帮助开发者优化数据库应用... 目录一、幻读的准确定义与核心特征幻读 vs 不可重复读二、mysql隔离级别深度解析各隔离级别的实现差异

C++ vector越界问题的完整解决方案

《C++vector越界问题的完整解决方案》在C++开发中,std::vector作为最常用的动态数组容器,其便捷性与性能优势使其成为处理可变长度数据的首选,然而,数组越界访问始终是威胁程序稳定性的... 目录引言一、vector越界的底层原理与危害1.1 越界访问的本质原因1.2 越界访问的实际危害二、基

Python多线程应用中的卡死问题优化方案指南

《Python多线程应用中的卡死问题优化方案指南》在利用Python语言开发某查询软件时,遇到了点击搜索按钮后软件卡死的问题,本文将简单分析一下出现的原因以及对应的优化方案,希望对大家有所帮助... 目录问题描述优化方案1. 网络请求优化2. 多线程架构优化3. 全局异常处理4. 配置管理优化优化效果1.