*新闻详情页*/>
在开发微信小程序支付的功能前 我们先熟悉下微信小程序支付的业务流程图
不熟悉流程的建议还是仔细阅读微信官方的开发者文档。
一 准备工作
事先需要申请企业版小程序 并开通“微信支付” 即商户功能 。并获取一下参数
appid ******** //小程序appid mchid ******** //小程序绑定商户id key ***************** //商户后台设置的key
并在商户后天设置开发者选项 主要是设置回调域名。
二 Java后台代码编写
Controller层代码
RestController RequestMapping(value /payment/ ) public class PaymentController { private static Logger logger LoggerFactory.getLogger(PaymentController.class); Value( ${hcc.wx.domain} ) private String orderDomain; Autowired private PaymentService paymentService; * p 统一下单入口 /p * param request * param response * throws Exception ResponseBody RequestMapping(value toPay , method RequestMethod.POST, produces { application/json;charset UTF-8 }) public JSONObject toPay(HttpServletRequest request) throws Exception { String requestStr RequestStr.getRequestStr(request); if (StringUtils.isEmpty(requestStr)) { throw new ParamException(); JSONObject jsonObj JSONObject.parseObject(requestStr); if(StringUtils.isEmpty(jsonObj.getString( orderNo )) || StringUtils.isEmpty(jsonObj.getString( openId ))){ throw new ParamException(); OrderInfo orderInfo .....//此处写获取订单信息方法 if(orderInfo null){ return AjaxUtil.renderFailMsg( 订单不存在 }else if(orderInfo.getPayAmount() null || orderInfo.getPayAmount() 0){ return AjaxUtil.renderFailMsg( 订单有误 请确认 }else if(orderInfo.getOrderStatus() ! 1){//1待付款 String msg orderInfo.getOrderStatus() 1 ? 此订单已支付 : 订单未提交 请确认 return AjaxUtil.renderFailMsg(msg); }else{ logger.info( 【小程序支付服务】请求订单编号:[ orderInfo.getOrderNo() ] Map String, String resMap paymentService.xcxPayment( orderInfo.getOrderNo(),orderInfo.getPayAmount(),jsonObj.getString( openId if( SUCCESS .equals(resMap.get( returnCode )) OK .equals(resMap.get( returnMsg ))){ //统一下单成功 resMap.remove( returnCode resMap.remove( returnMsg logger.info( 【小程序支付服务】支付下单成功 return AjaxUtil.renderSuccessMsg(resMap); }else{ logger.info( 【小程序支付服务】支付下单失败 原因: resMap.get( returnMsg return AjaxUtil.renderFailMsg(resMap.get( returnMsg * p 回调Api /p * param request * param response * throws Exception RequestMapping(value xcxNotify ) public void xcxNotify(HttpServletRequest request,HttpServletResponse response) throws Exception { InputStream inputStream request.getInputStream(); //获取请求输入流 ByteArrayOutputStream outputStream new ByteArrayOutputStream(); byte[] buffer new byte[1024]; int len while ((len inputStream.read(buffer))! -1){ outputStream.write(buffer,0,len); outputStream.close(); inputStream.close(); Map String,Object map BeanToMap.getMapFromXML(new String(outputStream.toByteArray(), utf-8 logger.info( 【小程序支付回调】 回调数据 \n map); String resXml String returnCode (String) map.get( return_code if ( SUCCESS .equalsIgnoreCase(returnCode)) { String returnmsg (String) map.get( result_code if( SUCCESS .equals(returnmsg)){ //更新数据 int result paymentService.xcxNotify(map); if(result 0){ //支付成功 resXml xml return_code ![CDATA[SUCCESS]] /return_code return_msg ![CDATA[OK]] /return_msg /xml }else{ resXml xml return_code ![CDATA[FAIL]] /return_code return_msg ![CDATA[报文为空] /return_msg /xml logger.info( 支付失败: resXml); }else{ resXml xml return_code ![CDATA[FAIL]] /return_code return_msg ![CDATA[报文为空] /return_msg /xml logger.info( 【订单支付失败】 logger.info( 【小程序支付回调响应】 响应内容 \n resXml); response.getWriter().print(resXml); }
Service接口层代码 部分代码
/** * p 支付接口层 /p * author att * date 2018年5月27日 * since jdk1.8 * version 1.0 public interface PaymentService { Map String,String xcxPayment(String orderNo, double money,String openId) throws Exception; int xcxNotify(Map String,Object map) throws Exception; }
Service接口实现 部分代码
Service(value paymentService ) public class PaymentServiceImpl implements PaymentService{ private static Logger LOGGER LoggerFactory.getLogger(PaymentServiceImpl.class); Value( ${spring.profiles.active} ) private String PROJECT_ENV; Value( ${hcc.wx.domain} ) private String orderDomain; Autowired private PaymentRecordMapper paymentRecordMapper; Autowired private PaymentNotifyMapper paymentNotifyMapper; Override public Map String, String xcxPayment(String orderNum, double money,String openId) throws Exception { LOGGER.info( 【小程序支付】 统一下单开始, 订单编号 orderNum); SortedMap String, String resultMap new TreeMap String, String //生成支付金额 开发环境处理支付金额数到0.01、0.02、0.03元 double payAmount PayUtil.getPayAmountByEnv(PROJECT_ENV, money); //添加或更新支付记录(参数跟进自己业务需求添加) int flag this.addOrUpdatePaymentRecord(orderNum, payAmount,.....); if(flag 0){ resultMap.put( returnCode , FAIL resultMap.put( returnMsg , 此订单已支付 LOGGER.info( 【小程序支付】 此订单已支付 }else if(flag 0){ resultMap.put( returnCode , FAIL resultMap.put( returnMsg , 支付记录生成或更新失败 LOGGER.info( 【小程序支付】 支付记录生成或更新失败 }else{ Map String,String resMap this.xcxUnifieldOrder(orderNum, PayConfig.TRADE_TYPE_JSAPI, payAmount,openId); if(PayConstant.SUCCESS.equals(resMap.get( return_code )) PayConstant.SUCCESS.equals(resMap.get( result_code ))){ resultMap.put( appId , PayConfig.XCX_APP_ID); resultMap.put( timeStamp , PayUtil.getCurrentTimeStamp()); resultMap.put( nonceStr , PayUtil.makeUUID(32)); resultMap.put( package , prepay_id resMap.get( prepay_id resultMap.put( signType , MD5 resultMap.put( sign , PayUtil.createSign(resultMap,PayConfig.XCX_KEY)); resultMap.put( returnCode , SUCCESS resultMap.put( returnMsg , OK LOGGER.info( 【小程序支付】统一下单成功 返回参数: resultMap); }else{ resultMap.put( returnCode , resMap.get( return_code resultMap.put( returnMsg , resMap.get( return_msg LOGGER.info( 【小程序支付】统一下单失败 失败原因: resMap.get( return_msg return resultMap; * 小程序支付统一下单 private Map String,String xcxUnifieldOrder(String orderNum,String tradeType, double payAmount,String openid) throws Exception{ //封装参数 SortedMap String,String paramMap new TreeMap String,String paramMap.put( appid , PayConfig.XCX_APP_ID); paramMap.put( mch_id , PayConfig.XCX_MCH_ID); paramMap.put( nonce_str , PayUtil.makeUUID(32)); paramMap.put( body , BaseConstants.PLATFORM_COMPANY_NAME); paramMap.put( out_trade_no , orderNum); paramMap.put( total_fee , PayUtil.moneyToIntegerStr(payAmount)); paramMap.put( spbill_create_ip , PayUtil.getLocalIp()); paramMap.put( notify_url , this.getNotifyUrl()); paramMap.put( trade_type , tradeType); paramMap.put( openid ,openid); paramMap.put( sign , PayUtil.createSign(paramMap,PayConfig.XCX_KEY)); //转换为xml String xmlData PayUtil.mapToXml(paramMap); //请求微信后台 获取预支付ID String resXml HttpUtils.postData(PayConfig.WX_PAY_UNIFIED_ORDER, xmlData); LOGGER.info( 【小程序支付】 统一下单响应 \n resXml); return PayUtil.xmlStrToMap(resXml); private String getNotifyUrl(){ //服务域名 return PayConfig.PRO_SERVER_DOMAIN /wxapp/payment/xcxNotify * 添加或更新支付记录 Override public int addOrUpdatePaymentRecord(String orderNo, double payAmount,......) throws Exception{ //写自己的添加或更新支付记录的业务代码 return 0; Override Transactional(readOnly false,rollbackFor {Exception.class}) public int xcxNotify(Map String,Object map) throws Exception{ int flag //支付订单编号 String orderNo (String)map.get( out_trade_no //检验是否需要再次回调刷新数据 //TODO 微信后台回调 刷新订单支付状态等相关业务 return flag; }
PayUtil工具类
/** * Function: 支付工具类 br/ * date: br/ * author att * version 1.0 * since JDK1.8 * see public class PayUtil { static Logger log LogManager.getLogger(PayUtil.class.getName()); * 获取当前机器的ip * return String public static String getLocalIp(){ InetAddress ia null; String localip null; try { ia ia.getLocalHost(); localip ia.getHostAddress(); } catch (Exception e) { e.printStackTrace(); return localip; * Map转换为 Xml * param data * return Xml * throws Exception public static String mapToXml(SortedMap String, String map) throws Exception { DocumentBuilderFactory documentBuilderFactory DocumentBuilderFactory.newInstance(); //防止XXE攻击 documentBuilderFactory.setXIncludeAware(false); documentBuilderFactory.setExpandEntityReferences(false); DocumentBuilder documentBuilder documentBuilderFactory.newDocumentBuilder(); org.w3c.dom.Document document documentBuilder.newDocument(); org.w3c.dom.Element root document.createElement( xml document.appendChild(root); for (String key: map.keySet()) { String value map.get(key); if (value null) { value value value.trim(); org.w3c.dom.Element filed document.createElement(key); filed.appendChild(document.createTextNode(value)); root.appendChild(filed); TransformerFactory tf TransformerFactory.newInstance(); Transformer transformer tf.newTransformer(); DOMSource source new DOMSource(document); transformer.setOutputProperty(OutputKeys.ENCODING, UTF-8 transformer.setOutputProperty(OutputKeys.INDENT, yes StringWriter writer new StringWriter(); StreamResult result new StreamResult(writer); transformer.transform(source, result); String output writer.getBuffer().toString(); try { writer.close(); catch (Exception ex) { return output;
public static String createSign(SortedMap String,String parameters,String key){ StringBuffer sb new StringBuffer(); Set es parameters.entrySet(); Iterator ? it es.iterator(); while(it.hasNext()) { Map.Entry entry (Map.Entry)it.next(); String k (String)entry.getKey(); if(entry.getValue() ! null || ! .equals(entry.getValue())) { String v String.valueOf(entry.getValue()); if (null ! v ! .equals(v) ! sign .equals(k) ! key .equals(k)) { sb.append(k v sb.append( key key); String sign MD5Util.MD5Encode(sb.toString(), UTF-8 ).toUpperCase(); return sign;
public static Map String, Object getMapFromXML(String strXML) throws Exception { try { Map String, Object data new HashMap String, Object DocumentBuilderFactory documentBuilderFactory DocumentBuilderFactory.newInstance(); //防止XXE攻击 documentBuilderFactory.setXIncludeAware(false); documentBuilderFactory.setExpandEntityReferences(false); DocumentBuilder documentBuilder documentBuilderFactory.newDocumentBuilder(); InputStream stream new ByteArrayInputStream(strXML.getBytes( UTF-8 org.w3c.dom.Document doc documentBuilder.parse(stream); doc.getDocumentElement().normalize(); NodeList nodeList doc.getDocumentElement().getChildNodes(); for (int idx idx nodeList.getLength(); idx) { Node node nodeList.item(idx); if (node.getNodeType() Node.ELEMENT_NODE) { org.w3c.dom.Element element (org.w3c.dom.Element) node; data.put(element.getNodeName(), element.getTextContent()); try { stream.close(); } catch (Exception ex) { ex.printStackTrace(); return data; } catch (Exception ex) { throw ex; * 生成随机数 * return public static String makeUUID(int len) { return UUID.randomUUID().toString().replaceAll( - , ).substring(0, len); * 获取当前的Timestamp * return public static String getCurrentTimeStamp() { return Long.toString(System.currentTimeMillis()/1000); * 获取当前的时间 * return public static long getCurrentTimestampMs() { return System.currentTimeMillis(); * 生成订单号 * return public static String generateOrderNo() { SimpleDateFormat sdf new SimpleDateFormat( yyMMdd return sdf.format(new Date()) makeUUID(16); * 获取当前工程url * param request * return public static String getCurrentUrl(HttpServletRequest request){ return request.getScheme() :// request.getServerName() : request.getServerPort() request.getContextPath(); * Xml字符串转换为Map * param xmlStr * return public static Map String,String xmlStrToMap(String xmlStr){ Map String,String map new HashMap String,String Document doc; try { doc DocumentHelper.parseText(xmlStr); Element root doc.getRootElement(); List children root.elements(); if(children ! null children.size() 0) { for(int i i children.size(); i ) { Element child (Element)children.get(i); map.put(child.getName(), child.getTextTrim()); } catch (DocumentException e) { e.printStackTrace(); return map; public static String getSceneInfo(String wapUrl,String name){ Map String,Map String,String map new HashMap String, Map String,String (); if(!StringUtils.isEmpty(wapUrl) !StringUtils.isEmpty(name)){ /*{ h5_info : { type : Wap , wap_url : https://pay.qq.com , wap_name : 腾讯充值 }}*/ Map String,String childmap new TreeMap String, String childmap.put( type , Wap childmap.put( wap_url ,wapUrl); childmap.put( wap_name , name); map.put( h5_info , childmap); return JSON.toJSONString(map); return null;
public static String moneyToIntegerStr(Double money){ BigDecimal decimal new BigDecimal(money); int amount decimal.multiply(new BigDecimal(100)) .setScale(0, BigDecimal.ROUND_HALF_UP).intValue(); return String.valueOf(amount); /** * 除去数组中的空值和签名参数 * param sArray 签名参数组 * return 去掉空值与签名参数后的新签名参数组 public static Map String, String paraFilter(Map String, String sArray) { Map String, String result new HashMap String, String if (sArray null || sArray.size() 0) { return result; for (String key : sArray.keySet()) { String value sArray.get(key); if (value null || value.equals( ) || key.equalsIgnoreCase( sign ) || key.equalsIgnoreCase( sign_type )) { continue; result.put(key, value); return result; /** * 把数组所有元素排序 并按照“参数 参数值”的模式用“ ”字符拼接成字符串 * param params 需要排序并参与字符拼接的参数组 * return 拼接后字符串 public static String createLinkString(Map String, String params) { List String keys new ArrayList String (params.keySet()); Collections.sort(keys); String prestr for (int i i keys.size(); i ) { String key keys.get(i); String value params.get(key); if (i keys.size() - 1) {//拼接时 不包括最后一个 字符 prestr prestr key value; } else { prestr prestr key value return prestr; * 根据不同环境生成支付金额 * param env * param money * param payType * return public static double getPayAmountByEnv(String env,Double money){ double pay_money 0.01; //测试环境 if(BaseConstants.PLATFORM_ENV_DEV.equals(env)){ if(money 10000){ pay_money 0.03; }else if(money 1000){ pay_money 0.02; }else{ pay_money 0.01; return pay_money; }else{ //生成环境 return money; }
支付配置类
/** * Function: 支付配置 br/ * date: br/ * author att * version 1.0 * since JDK1.8 public class PayConfig { //微信支付类型 //NATIVE--原生支付 //JSAPI--公众号支付-小程序支付 //MWEB--H5支付 //APP -- app支付 public static final String TRADE_TYPE_NATIVE NATIVE public static final String TRADE_TYPE_JSAPI JSAPI public static final String TRADE_TYPE_MWEB MWEB public static final String TRADE_TYPE_APP APP //小程序支付参数 public static String XCX_APP_ID; public static String XCX_MCH_ID; public static String XCX_KEY; //微信支付API public static final String WX_PAY_UNIFIED_ORDER https://api.mch.weixin.qq.com/pay/unifiedorder //参数 static{ Properties properties new Properties(); try { properties.load(PayConstant.class.getClassLoader().getResourceAsStream( payment_config.properties //xcx XCX_APP_ID (String) properties.get( xcx.pay.appid XCX_MCH_ID (String) properties.get( xcx.pay.mchid XCX_KEY (String) properties.get( xcx.pay.key } catch (Exception e) { e.printStackTrace(); }
Properties配置
##config xcx.pay.appid wx********** xcx.pay.mchid ***** xcx.pay.key **********
三 小程序端 获取统一下单返回参数发起支付
在小程序端 发起支付请求到 Java后台的统一下单接口返回prepay_id等参数 然后封装调起微信的js方法 wx.requestPayment(OBJECT) 具体参考文档
测试一把
本代码仅仅为项目中抽取的内容来写技术文章 如果想获取更完整内容或支持 请关注以下公众号 然后进入 关于我 联系我 联系本人。
Copyright © 2002-2020 学生个人网页制作代码_如何做网页或网站_网页游戏制作_dw网页设计作业_动态网页 版权所有 (网站地图) 粤ICP备10235580号