博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
微信小程序支付+Java后台实现(完整版)
阅读量:4288 次
发布时间:2019-05-27

本文共 17047 字,大约阅读时间需要 56 分钟。

在开发微信小程序支付的功能前,我们先熟悉下微信小程序支付的业务流程图:

å°ç¨åºæ¯ä»æ¶åºå¾

不熟悉流程的建议还是仔细阅读微信官方的。

一,准备工作

事先需要申请企业版小程序,并开通“微信支付”(即商户功能)。并获取一下参数:

appid=********  //小程序appidmchid=********  //小程序绑定商户idkey=*****************  //商户后台设置的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;     /**	 * 

统一下单入口

* * @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
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")); } } } /** *

回调Api

* * @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 = 0; while ((len=inputStream.read(buffer))!=-1){ outputStream.write(buffer,0,len); } outputStream.close(); inputStream.close(); Map
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 = "
" + "
" + "
"+"
"; } }else{ resXml = "
" + "
" + "
" + "
"; logger.info("支付失败:"+resXml); } }else{ resXml = "" + "" + "
" + "
"; logger.info("【订单支付失败】"); } logger.info("【小程序支付回调响应】 响应内容:\n"+resXml); response.getWriter().print(resXml); }}]]>

Service接口层代码(部分代码)

/*** 

支付接口层

* * @author att * @date 2018年5月27日* @since jdk1.8* @version 1.0 */public interface PaymentService { Map
xcxPayment(String orderNo, double money,String openId) throws Exception; int xcxNotify(Map
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
xcxPayment(String orderNum, double money,String openId) throws Exception { LOGGER.info("【小程序支付】 统一下单开始, 订单编号="+orderNum); SortedMap
resultMap = new TreeMap
(); //生成支付金额,开发环境处理支付金额数到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
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
xcxUnifieldOrder(String orderNum,String tradeType, double payAmount,String openid) throws Exception{ //封装参数 SortedMap
paramMap = new TreeMap
(); 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
map) throws Exception{ int flag = 0; //支付订单编号 String orderNo = (String)map.get("out_trade_no"); //检验是否需要再次回调刷新数据 //TODO 微信后台回调,刷新订单支付状态等相关业务 return flag; }

PayUtil工具类:

/** * Function: 支付工具类 
* date: 2018-01-18
* * @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
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; } /** * 创建签名Sign * * @param key * @param parameters * @return */ public static String createSign(SortedMap
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; } /** * XML转换为Map * * @param strXML * @return Map * @throws Exception */ public static Map
getMapFromXML(String strXML) throws Exception { try { Map
data = new HashMap
(); 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 = 0; 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
xmlStrToMap(String xmlStr){ Map
map = new HashMap
(); Document doc; try { doc = DocumentHelper.parseText(xmlStr); Element root = doc.getRootElement(); List children = root.elements(); if(children != null && children.size() > 0) { for(int i = 0; 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
> map = new HashMap
>(); if(!StringUtils.isEmpty(wapUrl) && !StringUtils.isEmpty(name)){ /*{"h5_info": {"type":"Wap","wap_url": "https://pay.qq.com","wap_name": "腾讯充值"}}*/ Map
childmap = new TreeMap
(); 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; } /** * 转换金额型到整型 * @param money * @return */ 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
paraFilter(Map
sArray) { Map
result = new HashMap
(); 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
params) { List
keys = new ArrayList
(params.keySet()); Collections.sort(keys); String prestr = ""; for (int i = 0; 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: 支付配置 
* date: 2018-01-18
* * @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配置:

##configxcx.pay.appid=wx**********xcx.pay.mchid=*****xcx.pay.key=**********

三,小程序端(获取统一下单返回参数发起支付)

在小程序端,发起支付请求到,Java后台的统一下单接口返回prepay_id等参数,然后封装调起微信的js方法:wx.requestPayment(OBJECT),具体参考文档:

测试一把:

本代码仅仅为项目中抽取的内容来写技术文章,如果想获取更完整内容或支持,请关注以下公众号,然后进入:关于我 >>> 联系我  联系本人。

转载地址:http://ojlgi.baihongyu.com/

你可能感兴趣的文章
解读:阿里文娱搜索算法实践与思考
查看>>
基于位置的点击模型
查看>>
链表操作算法题合集
查看>>
Crackme3 破解教程
查看>>
奖学金评比系统(数据库系统设计版)
查看>>
HTTP Live Streaming直播
查看>>
rtmp+fms rtmp 视频发布环境
查看>>
最简单的基于librtmp的示例:发布(FLV通过RTMP发布)
查看>>
Windows/Linux下引用jar包,并用javac/java编译运行
查看>>
HttpClient使用详解
查看>>
HttpClient详解(一)
查看>>
httpclient 请求http数据,json转map
查看>>
git 常用命令
查看>>
用递归方法建立二叉树
查看>>
用递归方法对二叉树进行先序、中序和后序遍历
查看>>
翻转二叉树
查看>>
逆序链表
查看>>
epoll 使用详解
查看>>
stl 中 set容器用法
查看>>
有序数组求交集
查看>>