微信线下门店二维码扫码支付和退款

来源:互联网 发布:购买汇天奇分析家软件 编辑:程序博客网 时间:2024/04/30 01:59


查看微信官网开发接口

微信线下门店扫码支付开发

流程:生成一个预付单-》生成二维码》付款交易成功(微信端接收到钱已付款,这时需要告诉商户系统我已收到钱,发送异步通知给商户系统,一般会发送多次有时间间隔。

商户系统的异步通知方法:处理自己系统的业务,一般修改交易流水状态,发送微信客服消息等等。

最后发送个“SUCCESS”内容的xml给微信端,这时微信端就不会再异步请求了。


1.生成预付订单
2.js生成二维码
3.回调通知方法  

4.微信退款



1.jsp页面的生成二维码

//*************start*******wxPayByQr********************************function wxQrClick(){var total_amount=$("#paymentAmount").val();var orderId=$("#myorderId").val();//checkvar paySize=$(".payMoneyC").length;var ptypeNum=$(".ptype:checked").length;var paySum=0;if(ptypeNum<=0){layer.msg("支付方式至少选择一个!");return;}if(ptypeNum!=1){layer.msg("支付方式只能是微信扫码支付!");return;}var companyId=$("#companyId").val();var subCompanyId=$("#subCompanyId").val();if(subCompanyId==null||subCompanyId=='null'||subCompanyId==undefined){subCompanyId="";}$.ajax({url : '<%=basePath%>/payPrepareByQr.action',async : false,type : "post",dataType : "json",data:{"orderAmount":total_amount,"orderId":orderId},success : function(result) {if(result.code=='SUCCESS'){$("#wxQrBtn").hide();$("#outputWXQr").show();<span style="color:#ff0000;">jQuery('#outputWXQr').qrcode({width:200,height:200,text:result.code_url});</span>window.setInterval(finshWXQrPay, 8000); }else{layer.msg("微信二维码生成出错!");}}});}function finshWXQrPay(){ var orderId=$("#myorderId").val();$.ajax({url : '<%=basePath%>/aliPay!notifyFinshedByWXQr.action?orderId='+orderId,async : false,type : "post",dataType : "json",data:$('#finishForm').serialize(),success : function(result) { if(result.code!='0'){ layer.msg("微信扫码支付交易成功,订单3秒后即将关闭!"); window.setTimeout(function(){ //关闭弹出窗之前,跳转到其他页面    parent.window.location.href="<%=path%>/"+result.redirectUrl;    closeLayerDialog(); },3000);  }}});}//*************end*******wxPayByQr********************************


2.二维码调用的方法

/** * 微信二维码扫码支付生成预支付交易单,并返回交易会话的二维码链接code_url * @return * @throws JDOMException * @throws IOException * @throws NumberFormatException * @throws SQLException */public String payPrepareByQr() throws JDOMException, IOException, NumberFormatException, SQLException {Order order = orderService.getOrderById(Integer.parseInt(orderId));CompanyPay cp = orderService.getCompanyPay(order.getCompanyId());HttpServletRequest request = ServletActionContext.getRequest();HttpServletResponse response = ServletActionContext.getResponse();PrintWriter out = null;if(order.getDealSts()==5||order.getDealSts()==7){out.print("0");return null;}SortedMap<Object, Object> parameters = new TreeMap<Object, Object>();userwxId = (userwxId==null||"".equals(userwxId))?order.getOrderPersonWXId():userwxId;if (!StringUtils.isNotBlank(userwxId)) {userwxId="";}if (!StringUtils.isNotBlank(ticketId)) {ticketId="";}parameters.put("appid", cp.getAppId());parameters.put("mch_id", cp.getMchId());parameters.put("nonce_str", PayCommonUtil.CreateNoncestr());// 随机字符串,不长于32位parameters.put("body", order.getIsTakeOut()==0?"堂食订单":"外卖订单");//商品描述String tradeNo = getTradeNo();parameters.put("out_trade_no", tradeNo);//商户系统内部的订单号,32个字符内、可包含字母float amount_f = Float.parseFloat(orderAmount);Long amount = (long)(amount_f*100);parameters.put("total_fee", amount.toString());//订单总金额,单位为分,不能带小数点parameters.put("spbill_create_ip", request.getRemoteAddr());//订单生成的机器IP,第一个参数订单编号,第二个参数交易金额,第四个参数优惠劵编号parameters.put("notify_url", ConfigUtil.WXQR_NOTIFY_URL+"?orderId="+orderId+","+orderAmount+","+tradeNo+","+ticketId);//接收微信支付成功通知parameters.put("trade_type", "NATIVE");//JSAPI、NATIVE、APPString sign = PayCommonUtil.createSign("UTF-8", parameters,cp.getApiKey());parameters.put("sign", sign);String requestXML = PayCommonUtil.getRequestXml(parameters);System.out.println("requestXML_------------------->>>>>:"+requestXML);String result = CommonUtil.httpsRequest(ConfigUtil.UNIFIED_ORDER_URL,"POST", requestXML);Map<String, String> map = XMLUtil.doXMLParse(result);SortedMap<Object, Object> params = new TreeMap<Object, Object>();String return_code=(String)map.get("return_code");String result_code=(String)map.get("result_code");String json = "";if("SUCCESS".equals(return_code)&&"SUCCESS".equals(result_code)){params.put("code", map.get("return_code"));params.put("appId", cp.getAppId());params.put("prepay_id", map.get("prepay_id"));params.put("trade_type", map.get("trade_type"));params.put("code_url", map.get("code_url"));//trade_type为NATIVE是有返回,可将该参数值生成二维码展示出来进行扫码支付json = JSONObject.fromObject(params).toString();//生成交易流水,等回调后再改变状态WXPayLog vo = new WXPayLog();vo.setOutTradeNo(tradeNo);vo.setOrderId(orderId);vo.setTotalFee(amount_f);vo.setPayOpenId(order.getOrderPersonWXId());vo.setAppId(parameters.get("appid").toString());vo.setStatus(0);vo.setTradeDate(new Date());vo.setTradeType("NATIVE");orderService.insertWXPaylog(vo);}else{params.put("code", map.get("return_code"));params.put("msg", map.get("return_msg"));json = JSONObject.fromObject(params).toString();}System.out.println("微信扫码支付返回信息:="+json);ResponseWriteUtil.writeHTML(json);return null;}

3.回调的方法

/** * 微信扫码支付异步通知回调方法 * @return * @throws Exception */public String paySuccessByWXQr() throws Exception {HttpServletRequest request = ServletActionContext.getRequest();HttpServletResponse response = ServletActionContext.getResponse();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();System.out.println("~~~~~~~~~~~~~~~~付款成功~~~~~~~~~~~~~~~~~~`");List<String> tktIds = new ArrayList<String>();//tktIds.add(ticketId);//orderService.payNotify(orderId,tktIds,orderAmount);String result = new String(outSteam.toByteArray(), "utf-8");// 获取微信调用我们notify_url的返回信息Map<Object, Object> map = XMLUtil.doXMLParse(result);String paras = "";String userwxId="";if (map != null) {for (Object keyValue : map.keySet()) {//System.out.println("[" + keyValue + "=" + map.get(keyValue)//+ "]");if ("orderId".equals(keyValue)) {paras = map.get(keyValue).toString();}if("openid".equals(keyValue)){userwxId=map.get(keyValue).toString();}}}String[] para = paras.split(",");String orderId=para[0];String orderAmount=para[1];String tradeNo=para[2];String ticketId="";if(para.length>3){ticketId=para[3];if(ticketId!=null && !"".equals(ticketId)){tktIds.add(ticketId);}}System.out.println("params="+orderId+","+orderAmount+","+tradeNo+","+userwxId +","+ticketId);//判断付款是否成功,已成功则不再记录付款信息if(orderService.ifOrderPaid(Integer.parseInt(orderId))>0){response.getWriter().write(PayCommonUtil.setXML("SUCCESS", ""));return null;}//更新交易流水状态orderService.updateWXPayLogStatus(tradeNo);// 增加付款信息Order o = orderService.getOrderById(Integer.parseInt(orderId));List<SysUser> us = sysUserService.fetchSysUserByOpenId(userwxId, o.getCompanyId());SysUser u = (us!=null && us.size()>0)?us.get(0):null;if(o.getIsTakeOut()==0){//堂吃orderService.payNotify(orderId,tktIds,orderAmount,0);}else if(o.getIsTakeOut()==1){//外卖o.setPaymentAmount(Float.parseFloat(orderAmount));o.setPaymentType(EnumUtil.PAYMENT_TYPE.weixin.getCode());//配送员结束订单,赞为支付方式为现金或刷卡两种方式o.setPaymentTime(new Date());o.setDealSts(EnumUtil.ORDER_dealSts.paid.getCode());o.setFinishTime(new Date());o.setOrderRemark("用户微信支付");List<OrderPlus> ops = new ArrayList<OrderPlus>();OrderPlus op = new OrderPlus();op.setOrderId(o.getId());op.setOrderAmount(o.getAmount());op.setPaymentAmount(o.getPaymentAmount());//op.setPaymentTime(new Date());op.setActUser(u==null?"":u.getId());op.setPaymentType(EnumUtil.PAYMENT_TYPE.weixin.getCode());op.setNotes("用户微信支付:支付金额为"+o.getPaymentAmount());ops.add(op);orderService.taeoutOrderPaymet4admin(o, ops,tktIds);}orderService.updateOrderItemActualPrice(o.getId());System.out.println("~~~~~~~~~~~~~~~~业务处理完成~~~~~~~~~~~~~~~~~~`");//--------------------------消息发送成功-----------------------------------------endif (map.get("result_code").toString().equalsIgnoreCase("SUCCESS")) {response.getWriter().write(PayCommonUtil.setXML("SUCCESS", "")); // 告诉微信服务器,我收到信息了,不要在调用回调action了System.out.println("-------------"+ PayCommonUtil.setXML("SUCCESS", ""));}return null;}


XMLUtil.java
import java.io.ByteArrayInputStream;import java.io.IOException;import java.io.InputStream;import java.util.HashMap;import java.util.Iterator;import java.util.List;import java.util.Map;import org.jdom.Document;import org.jdom.Element;import org.jdom.JDOMException;import org.jdom.input.SAXBuilder;public class XMLUtil {/** * 解析xml,返回第一级元素键值对。如果第一级元素有子节点,则此节点的值是子节点的xml数据。 * @param strxml * @return * @throws JDOMException * @throws IOException */public static Map doXMLParse(String strxml) throws JDOMException, IOException {strxml = strxml.replaceFirst("encoding=\".*\"", "encoding=\"UTF-8\"");if(null == strxml || "".equals(strxml)) {return null;}Map m = new HashMap();InputStream in = new ByteArrayInputStream(strxml.getBytes("UTF-8"));SAXBuilder builder = new SAXBuilder();Document doc = builder.build(in);Element root = doc.getRootElement();List list = root.getChildren();Iterator it = list.iterator();while(it.hasNext()) {Element e = (Element) it.next();String k = e.getName();String v = "";List children = e.getChildren();if(children.isEmpty()) {v = e.getTextNormalize();} else {v = XMLUtil.getChildrenText(children);}m.put(k, v);}//关闭流in.close();return m;}/** * 获取子结点的xml * @param children * @return String */public static String getChildrenText(List children) {StringBuffer sb = new StringBuffer();if(!children.isEmpty()) {Iterator it = children.iterator();while(it.hasNext()) {Element e = (Element) it.next();String name = e.getName();String value = e.getTextNormalize();List list = e.getChildren();sb.append("<" + name + ">");if(!list.isEmpty()) {sb.append(XMLUtil.getChildrenText(list));}sb.append(value);sb.append("</" + name + ">");}}return sb.toString();}}

4.微信退款:首先需要有微信退款的证书,放到商户系统的目录下,然后调用微信退款接口

/** * 微信--申请退款 *  * @param orderId *            订单编号 * @param refundMoney *            退款金额 * @param totalFee *            订单总金额 * @param outRefundNo *            商户退款单号 * @return */public Map<String, Object> payRefund(String orderId, float refundMoney,float totalFee, String outRefundNo) {Map<String, Object> resultMap = new HashMap<String, Object>();try {Order order = orderService.getOrderById(Integer.parseInt(orderId));CompanyPay cp = orderService.getCompanyPay(order.getCompanyId());String tradeNo = orderRefundService.selectOutTradeNoByOrderId(Integer.parseInt(orderId));SortedMap<Object, Object> parameters = new TreeMap<Object, Object>();parameters.put("appid", cp.getAppId());// 微信分配的公众账号IDparameters.put("mch_id", cp.getMchId());// 微信支付分配的商户号parameters.put("nonce_str", PayCommonUtil.CreateNoncestr());// 随机字符串,不长于32位// CHSGOFBZJ520150918162021614parameters.put("out_trade_no", tradeNo);// 商户系统内部的订单号,32个字符内、可包含字母parameters.put("out_refund_no", outRefundNo);// 商户系统内部的退款单号,商户系统内部唯一,同一退款单号多次请求只退一笔Long refundMoney_f = (long) (refundMoney * 100);Long totalFee_f = (long) (totalFee * 100);parameters.put("total_fee", totalFee_f.toString());// 订单总金额,单位为分,不能带小数点parameters.put("refund_fee", refundMoney_f.toString());// 退款总金额,单位为分,不能带小数点parameters.put("op_user_id", cp.getMchId());// 操作员帐号, 默认为商户号String sign = PayCommonUtil.createSign("UTF-8", parameters,cp.getApiKey());parameters.put("sign", sign);String requestXML = PayCommonUtil.getRequestXml(parameters);System.out.println("requestXML_------------------->>>>>:"+ requestXML);// String result = CommonUtil.httpsRequest(ConfigUtil.REFUND_URL,// "POST", requestXML);Map<String, String> map = clientCustomSSLCall(ConfigUtil.REFUND_URL, order.getCompanyId(), cp.getMchId(),requestXML);// log.debug("result:=" + result);// Map<String, String> map = XMLUtil.doXMLParse(result);if ("FAIL".equals(map.get("return_code"))) {resultMap.put("result_flag", "fail");resultMap.put("return_code", map.get("return_code"));resultMap.put("return_msg", map.get("return_msg"));} else if ("SUCCESS".equals(map.get("return_code"))) {if ("SUCCESS".equals(map.get("result_code"))) {resultMap.put("result_flag", "success");// SUCCESS退款申请接收成功,结果通过退款查询接口查询resultMap.put("return_code", map.get("return_code"));resultMap.put("return_msg", map.get("return_msg"));resultMap.put("err_code", map.get("err_code"));resultMap.put("err_code_des", map.get("err_code_des"));} else if ("FAIL".equals(map.get("result_code"))) {resultMap.put("result_flag", "fail");// FAIL 提交业务失败resultMap.put("err_code", map.get("err_code"));resultMap.put("err_code_des", map.get("err_code_des"));}}} catch (Exception e) {System.out.println("payRefund Exception:" + e.getMessage());}return resultMap;}/** * 自定义SSL双向证书验证 *  * @param url * @param mchId * @param arrayToXml * @return * @throws Exception */public Map<String, String> clientCustomSSLCall(String url,String companyId, String mchId, String arrayToXml) throws Exception {Map<String, String> doXMLtoMap = new HashMap<String, String>();KeyStore keyStore = KeyStore.getInstance("PKCS12");String cAPath = ServletActionContext.getServletContext().getRealPath("/WEB-INF/ca/" + companyId + "/apiclient_cert.p12");// System.out.println("capath:=" + cAPath);FileInputStream instream = new FileInputStream(new File(cAPath));try {keyStore.load(instream, mchId.toCharArray());} finally {instream.close();}// Trust own CA and all self-signed certsSSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, mchId.toCharArray()).build();// Allow TLSv1 protocol onlySSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[] { "TLSv1" }, null,SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();try {HttpPost httpPost = new HttpPost(url);httpPost.setEntity(new StringEntity(arrayToXml, "UTF-8"));CloseableHttpResponse response = httpclient.execute(httpPost);String jsonStr = EntityUtils.toString(response.getEntity(), "UTF-8");doXMLtoMap = XMLUtil.doXMLParse(jsonStr);log.debug("result jsonStr:=" + jsonStr);response.close();} finally {httpclient.close();}return doXMLtoMap;}/** * 微信退款证书是否存在 *  * @return */public String isWxCAExist() {String companyId = getCompanyInfo().getCompanyId();String cAPath = ServletActionContext.getServletContext().getRealPath("/WEB-INF/ca/" + companyId + "/apiclient_cert.p12");File f = new File(cAPath);if (f.exists()) {ResponseWriteUtil.writeHTML("{\"isExist\":\"1\"}");// 存在} else {ResponseWriteUtil.writeHTML("{\"isExist\":\"0\"}");// 不存在}return null;}

PayCommonUtil.java

import java.io.UnsupportedEncodingException;import java.util.Iterator;import java.util.Map;import java.util.Random;import java.util.Set;import java.util.SortedMap;import org.apache.log4j.Logger;public class PayCommonUtil {private static Logger log = Logger.getLogger(PayCommonUtil.class);public static String CreateNoncestr(int length) {String chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";String res = "";for (int i = 0; i < length; i++) {Random rd = new Random();res += chars.indexOf(rd.nextInt(chars.length() - 1));}return res;}public static String CreateNoncestr() {String chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";String res = "";for (int i = 0; i < 16; i++) {Random rd = new Random();res += chars.charAt(rd.nextInt(chars.length() - 1));}return res;}/** * @Description:sign签名 * @param characterEncoding 编码格式 * @param parameters 请求参数 * @return * @throws UnsupportedEncodingException  */public static String createSign(String characterEncoding,SortedMap<Object,Object> parameters,String apiKey) throws UnsupportedEncodingException{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();Object v = entry.getValue();if(null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) {sb.append(k + "=" + v + "&");}}//sb.append("key=" + ConfigUtil.API_KEY);sb.append("key=" + apiKey);System.out.println("createSign-----befor_md5_sign:"+sb.toString());String sign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase();return sign;}public static String createSign4pay(String characterEncoding,SortedMap<Object,Object> parameters,String apiKey) throws UnsupportedEncodingException{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();Object v = entry.getValue();if(null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) {sb.append(k + "=" + v + "&");}}sb.append("key=" + apiKey);//sb.append("key=" + ConfigUtil.APP_SECRECT);System.out.println("befor_md5_sign:"+sb.toString());String sign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase();return sign;}/** * @Description:将请求参数转换为xml格式的string * @param parameters  请求参数 * @return * @throws UnsupportedEncodingException  */public static String getRequestXml(SortedMap<Object,Object> parameters) throws UnsupportedEncodingException{StringBuffer sb = new StringBuffer();sb.append("<xml>");Set es = parameters.entrySet();Iterator it = es.iterator();while(it.hasNext()) {Map.Entry entry = (Map.Entry)it.next();String k = (String)entry.getKey();String v = (String)entry.getValue();if ("attach".equalsIgnoreCase(k)||"body".equalsIgnoreCase(k)||"sign".equalsIgnoreCase(k)) {sb.append("<"+k+">"+"<![CDATA["+v+"]]></"+k+">");}else {sb.append("<"+k+">"+v+"</"+k+">");}}sb.append("</xml>");return sb.toString();}/** * @Description:返回给微信的参数 * @param return_code 返回编码 * @param return_msg  返回信息 * @return */public static 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>";}}



0 0
原创粉丝点击