微信开发退款申请_微信统一下单回调函数和微信申请退款回调函数可以写成一个吗 - CSDN
  • 之前有过微信开发的经验,但是第一次接触“微信退款“这一块的业务,查询了很多的博客资料以及走了很多的弯路。也发现“微信退款”分享的博客并不多。特地写了该博客,希望对你们有帮助。个人浅薄的见解 【稍微提...

    近日,在开发“微信申请退款”的功能。之前有过微信开发的经验,但是第一次接触“微信退款“这一块的业务,查询了很多的博客资料以及走了很多的弯路。也发现“微信退款”分享的博客并不多。特地写了该博客,希望对你们有帮助。个人浅薄的见解

    代码下载地址如下:
    链接:https://pan.baidu.com/s/1h6_ZSz5RbFARlY9yxkvj5w
    提取码:weft
    有任何关于微信开发的问题可以相互交流, 推荐QQ:2172931891 , 另外有微商城、微分销、微信小游戏等系统源码,有需要可以联系免费提供。



    一、微信退款Api


    https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_4

    二、开发准备
    (1)证书的准备:java开发需要用到:apiclient_cert.p12证书的,在微信公众号上下载–注意PKCS12证书 是从微信商户平台-》账户设置-》 API安全 中下载的 。
    这里写图片描述
    【何时用到证书?】与支付不一样,企业支付功能在发送post请求的时候,需要加载自己的一个证书之后,带着证书去请求退款才可以。这里使用到证书–很多人不知道证书在哪里使用
    (2)了解好数字签名 — 简单来解释,就是对自己要发送的数据进行加密处理、换句话说假如说你要传递A/B/C,就对这三者进行加密。初开发者的误区:不知道该加密什么数据、观看网上的博客胡乱进行签名,导致签名错误
    【温馨提示:】数字签名是一般开发人员容易遇到的错误,记住“你没遇到数字签名错误,都不好意思说自己做过微信退款支付订单查询等功能”。
    耐心解决就行
    (3)熟悉 从xml–》map,以及map—》xml。因为微信只接受xml数据,java写一个xml不简单,但是写map集合非常简单。而且返回的数据是xml格式。需要转化成开发熟知的map集合
    不懂的可以看以下这篇博客
    http://blog.csdn.net/xiaozhegaa/article/details/79127283

    三、退款API截图解释
    ~~ 接口说明 + 是否需要证书
    这里写图片描述
    数字签名说明 – 记住一句话:对要传递的数据进行加密,你要传递什么就要签名什么
    第一次开发遇到的坑就是:不了解数字签名、花费了自己很长的时间,特地出来强调一下
    证书说明
    这里写图片描述

    四、开发步骤如下
    1.拼凑所需要传递的参数 map集合
    2.根据要传递的参数生成自己的签名
    3.把签名放到map集合中【因为签名也要传递过去】
    4.将当前的map结合转化成xml格式
    5.发送请求到微信退款Api。发送请求是一个方法来的
    6.解析返回的xml数据===》map集合
    7.根据map中的result_code/return_code来判断是否成功与失败

    不得不再啰嗦一下。下面设计到签名、mapToXml转化、xmlToMap转化、发送请求到API。这些方法都可以在下面网址看到。这里我Xml转化成Map。我是习惯用一个Bean接收,也有范例的代码,大家模仿能力要强一点。这次贴出来给大家看看吧
    http://blog.csdn.net/xiaozhegaa/article/details/79127283

    /** 
    	* 解析退款申请 
    	* 解析的时候自动去掉CDMA 
    	* @param xml 
    	*/ 
    	@SuppressWarnings("unchecked") 
    	public static RefundResult getUnifiedorderResult(String xml){ 
    				RefundResult unifieorderResult = new RefundResult(); 
    			try { 
    					StringReader read = new StringReader(xml); 
    					// 创建新的输入源SAX 解析器将使用 InputSource 对象来确定如何读取 XML 输入 
    					InputSource source = new InputSource(read); 
    					// 创建一个新的SAXBuilder 
    					SAXBuilder sb = new SAXBuilder(); 
    					// 通过输入源构造一个Document 
    					Document doc; 
    					doc = (Document) sb.build(source); 
    				
    					Element root = doc.getRootElement();// 指向根节点 
    					List<Element> list = root.getChildren(); 
    				
    					if(list!=null&&list.size()>0){ 
    					for (Element element : list) { 
    						System.out.println("key是:"+element.getName()+",值是:"+element.getText()); 
    						if("return_code".equals(element.getName())){ 
    								unifieorderResult.setResult_code(element.getText()); 
    							} 
    					
    						if("return_msg".equals(element.getName())){ 
    							unifieorderResult.setReturn_msg(element.getText()); 
    							} 
    					
    						if("result_code".equals(element.getName())){ 
    							unifieorderResult.setResult_code(element.getText()); 
    							} 
    						
    						
    						if("out_refund_no".equals(element.getName())){ 
    							unifieorderResult.setOut_refund_no(element.getText()); 
    							} 
    						
    						if("refund_id".equals(element.getName())){ 
    							unifieorderResult.setRefund_id(element.getText()); 
    							} 
    						
    						if("refund_fee".equals(element.getName())){ 
    							unifieorderResult.setRefund_fee(element.getText()); 
    							} 
    						
    						if("coupon_refund_fee".equals(element.getName())){ 
    							unifieorderResult.setCoupon_refund_fee(element.getText()); 
    							} 
    						
    						if("total_fee".equals(element.getName())){ 
    							unifieorderResult.setTotal_fee(element.getText()); 
    							} 
    						
    						if("cash_fee".equals(element.getName())){ 
    							unifieorderResult.setCash_fee(element.getText()); 
    							} 
    						if("err_code_des".equals(element.getName())){ 
    							unifieorderResult.setErr_code_des(element.getText()); 
    							} 
    						}
    					}
    				
    
    			} catch (JDOMException e) { 
    			e.printStackTrace(); 
    			} catch (IOException e) { 
    			e.printStackTrace(); 
    			}catch (Exception e) { 
    			e.printStackTrace(); 
    			} 
    				
    			return unifieorderResult; 
    		} 
    
    
    @Test
    	public void test2(){
    		SortedMap<Object, Object> packageParams = new TreeMap<Object, Object>(); 
    		
    		packageParams.put("appid", wxconfig.AppID);
    		packageParams.put("mch_id", wxconfig.mch_id);
    		
    		String s = UUID.randomUUID().toString().replace("-", "");
    		packageParams.put("nonce_str",s);
    		
    		String s1 = UUID.randomUUID().toString().replace("-", "");
    		packageParams.put("out_refund_no",s1);
    		packageParams.put("out_trade_no","ozb5fjjxbwag1akdy0vm108makqhsdmx");
    	
    		packageParams.put("refund_fee","100");
    		packageParams.put("total_fee","100");
    		
    		packageParams.put("op_user_id",wxconfig.mch_id);
    		
    		String sign  = WeixinPayBack.createSign("utf-8",packageParams);
    		
    		packageParams.put("sign", sign);
    		
    		String reuqestXml = WXPayUtil.getRequestXml(packageParams);
    		 
    		System.out.println("-----------------"); 
    		System.out.println(reuqestXml);
    		System.out.println("-----------------"); 
    		//发送请求到后台了
    		String wxUrl = WeixinPayBack.getRefundURL; 
    		try {
    			String weixinPost = ClientCustomSSL.doRefund(wxUrl, reuqestXml).toString();
    			System.out.println(weixinPost);
    			
    			RefundResult refundResult = WeixinPayBack.getUnifiedorderResult(weixinPost);  
    			    
    		    String result =null;
    		    if("SUCCESS".equalsIgnoreCase(refundResult.getResult_code())){ 
    		    	result = "200"; 
    		    	System.out.println("==========处理退款成功=========="); 
    	    	}else{ 
    	    		result = refundResult.getReturn_msg(); 
    	    	} 
    		    
    		    System.out.println(result);
    		} catch (Exception e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}  
    		
    		
    		
    	}
    

    这里写图片描述
    五、结果演示
    这里写图片描述
    自此,我们就学会了“微信申请退款”的开发,希望对你们有帮助
    具体的、在调用改方法自己加上自己的业务逻辑就行了。希望对大家有帮助

    //TODO 9.0 操作支付表,把当前的支付的状态变成 退款状态   state 1 ---> 2
    //TODO 10 操作预约表,可以把当前的预约状态取消  已支付--->退款
    //TODO 11 操作用户表,如果是充值退款的话,把用户的现金 - 当前退款的money
    //TODO 12其他等等的操作,生成记录单号之类的
    
    展开全文
  • 今天要将微信的最后一个功能给做完了,这个功能就是申请退款。刚开始我先开了一下文档,发现它跟那个企业付款到零钱特别相似,然后就自己模仿企业付款到零钱把它给实现了。 二,解决方法 申请退款官方文档:...

    一,问题

    今天要将微信的最后一个功能给做完了,这个功能就是申请退款。刚开始我先看了一下文档,发现它跟那个企业付款到零钱特别相似,然后就自己模仿企业付款到零钱把它给实现了。

    二,解决方法

    申请退款官方文档:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_4

    2.1 下载商家证书,再将证书放到项目中

    ①下载商家证书

    在这里插入图片描述

    在这里插入图片描述

    ②将证书放到项目中

    在这里插入图片描述

    2.2 将官方的demo下载下来,将里面的工具类全都放到项目中

    官方demo下载地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=11_1

    在这里插入图片描述

    2.3 准备好8个必须的参数

    ①先拿到7个参数

    appid:公众账号ID
    mch_id:商户号
    nonce_str:随机字符串
    out_trade_no:商户订单号
    out_refund_no:商户退款单号
    total_fee:订单金额
    refund_fee:退款金额
    

    ②根据上面的7个参数,得到第8个参数

    sign:自己的签名。利用上面7个参数生成
    

    2.4 发送去微信,让微信执行退款操作

    2.5 源码

    ①RefundController

    import java.util.Map;
    import java.util.SortedMap;
    import java.util.TreeMap;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    import com.jc.util.wxPay.WXPayUtil;
    
    import controller.AuthUtil;
    import transfercontroller.CertHttpUtil;
    
    /**
     * @author: KOLO
     * @date:2018年12月29日 上午9:13:58
     * @version: 1.0 申请订单退款
     */
    @Controller
    @RequestMapping("/refund")
    public class RefundController {
    
    	/**
    	 * @Title: transfer
    	 * @Description: 申请订单退款
    	 * @param:
    	 * @return:
    	 */
    	@RequestMapping("/out")
    	public @ResponseBody String transfer(HttpServletRequest request, HttpServletResponse response) {
    		
    		// 1.0 拼凑微信退款需要的参数
    		String appid = AuthUtil.APPID; // 微信公众号的appid
    		String mch_id = AuthUtil.MCHID; // 商户号
    		String nonce_str = WXPayUtil.generateNonceStr(); // 生成随机数
    		String out_trade_no = "3cc7da01f4f54c3fbfbd0fd60b5559a6"; //商户订单号
    		String out_refund_no = "3cc7da01f4f54c3fbfbd0fd60b5559a6";//商户退款单号 
    		String total_fee = "1";//订单金额
    		String refund_fee = "1";//退款金额
    		 
    		 
    		// 2.0 生成map集合
    		SortedMap<String, String> packageParams = new TreeMap<String, String>();
    		packageParams.put("appid", appid); // 微信公众号的appid
    		packageParams.put("mch_id", mch_id); // 商务号
    		packageParams.put("nonce_str", nonce_str); // 随机生成后数字,保证安全性
    		packageParams.put("out_trade_no", out_trade_no); 
    		packageParams.put("out_refund_no", out_refund_no); 
    		packageParams.put("total_fee", total_fee); 
    		packageParams.put("refund_fee", refund_fee); 
    		
    		
    		try {
    			// 3.0 利用上面的参数,先去生成自己的签名
    			String sign = WXPayUtil.generateSignature(packageParams, AuthUtil.PATERNERKEY);
    
    			// 4.0 将签名再放回map中,它也是一个参数
    			packageParams.put("sign", sign);
    
    			// 5.0将当前的map结合转化成xml格式
    			String xml = WXPayUtil.mapToXml(packageParams);
    
    			// 6.0获取需要发送的url地址
    			String wxUrl = "https://api.mch.weixin.qq.com/secapi/pay/refund"; // 获取退款的api接口
    
    			System.out.println("发送前的xml为:" + xml);
    
    			// 7,向微信发送请求转账请求
    			String returnXml = CertHttpUtil.postData(wxUrl, xml, AuthUtil.MCHID, AuthUtil.CERTPATH);
    
    			System.out.println("返回的returnXml为:" + returnXml);
    
    			// 8,将微信返回的xml结果转成map格式
    			Map<String, String> returnMap = WXPayUtil.xmlToMap(returnXml);
    
    			if (returnMap.get("return_code").equals("SUCCESS")) {
    				// 退款成功
    				System.out.println("returnMap为:" + returnMap);
    
    			}
    
    			return returnXml;
    
    		} catch (Exception e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}
    		return "error";
    	}
    
    }
    

    ②CertHttpUtil类

    import java.io.File;
     import java.io.FileInputStream;
     import java.io.IOException;
    import java.io.InputStream;
    import java.security.KeyStore;
      
     import javax.net.ssl.SSLContext;
      
     import org.apache.http.HttpEntity;
     import org.apache.http.HttpResponse;
     import org.apache.http.client.config.RequestConfig;
     import org.apache.http.client.methods.HttpPost;
     import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
     import org.apache.http.conn.ssl.SSLContexts;
     import org.apache.http.entity.StringEntity;
     import org.apache.http.impl.client.CloseableHttpClient;
     import org.apache.http.impl.client.HttpClients;
     import org.apache.http.util.EntityUtils;
    import org.springframework.core.io.ClassPathResource;
    
    import controller.AuthUtil;
      
     /**
      * 获取微信apiclient_cert.p12证书
      *
      * @author zengwei
      * @email 15797630391@163.com
      * @date 2018年4月5日 下午4:49:36
      */ 
     public class CertHttpUtil {
      
         private static int socketTimeout = 10000;// 连接超时时间,默认10秒
         private static int connectTimeout = 30000;// 传输超时时间,默认30秒
         private static RequestConfig requestConfig;// 请求器的配置
         private static CloseableHttpClient httpClient;// HTTP请求器
      
         /**
          * 通过Https往API post xml数据
          *
          * @param url API地址
          * @param xmlObj 要提交的XML数据对象
         * @param mchId 商户ID
         * @param certPath 证书位置
          * @return
          */
         public static String postData(String url, String xmlObj, String mchId, String certPath) {
             // 加载证书
             try {
                 initCert(mchId, certPath);
             } catch (Exception e) {
                 e.printStackTrace();
             }
             String result = null;
             HttpPost httpPost = new HttpPost(url);
             // 得指明使用UTF-8编码,否则到API服务器XML的中文不能被成功识别
             StringEntity postEntity = new StringEntity(xmlObj, "UTF-8");
             httpPost.addHeader("Content-Type", "text/xml");
             httpPost.setEntity(postEntity);
             // 根据默认超时限制初始化requestConfig
             requestConfig = RequestConfig.custom().setSocketTimeout(socketTimeout).setConnectTimeout(connectTimeout).build();
             // 设置请求器的配置
             httpPost.setConfig(requestConfig);
             try {
                 HttpResponse response = null;
                 try {
                     response = httpClient.execute(httpPost);
                 } catch (IOException e) {
                     e.printStackTrace();
                 }
                 HttpEntity entity = response.getEntity();
                 try {
                     result = EntityUtils.toString(entity, "UTF-8");
                 } catch (IOException e) {
                     e.printStackTrace();
                 }
             } finally {
                 httpPost.abort();
             }
             return result;
         }
      
         /**
          * 加载证书
          *
          * @param mchId 商户ID
          * @param certPath 证书位置
          * @throws Exception
          */
         private static void initCert(String mchId, String certPath) throws Exception {
             // 证书密码,默认为商户ID
             String key = mchId;
             // 证书的路径
             String path = certPath;
             // 指定读取证书格式为PKCS12
             KeyStore keyStore = KeyStore.getInstance("PKCS12");
             // 读取本机存放的PKCS12证书文件  
             ClassPathResource cp = new ClassPathResource(AuthUtil.CERTPATH);
             InputStream instream = cp.getInputStream();
             try {
                 // 指定PKCS12的密码(商户ID)
                 keyStore.load(instream, key.toCharArray());
             } finally {
                 instream.close();
             }
             SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, key.toCharArray()).build();
             SSLConnectionSocketFactory sslsf =
                     new SSLConnectionSocketFactory(sslcontext, new String[] {"TLSv1"}, null,
                             SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
             httpClient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
         }
     }
    

    ③AuthUtil类

    import java.io.IOException;
    
    import javax.servlet.http.HttpServletRequest;
    
    import org.apache.http.HttpEntity;
    import org.apache.http.HttpResponse;
    import org.apache.http.client.ClientProtocolException;
    import org.apache.http.client.methods.HttpGet;
    import org.apache.http.impl.client.DefaultHttpClient;
    import org.apache.http.util.EntityUtils;
    
    import net.sf.json.JSONObject;
    public class AuthUtil {
    	public static final String APPID = "商家APPID";
    	public static final String APPSECRET = "商家APPSECRET";
    	public static final String MCHID = "商家ID";
    	public static final String PATERNERKEY = "商家密钥";
    	public static final String CERTPATH = "商家证书在项目的存放路径";
    	
    	public static JSONObject doGetJson(String url) throws ClientProtocolException, IOException {
    		JSONObject jsonObject = null;
    		// 首先初始化HttpClient对象
    		DefaultHttpClient client = new DefaultHttpClient();
    		// 通过get方式进行提交
    		HttpGet httpGet = new HttpGet(url);
    		// 通过HTTPclient的execute方法进行发送请求
    		HttpResponse response = client.execute(httpGet);
    		// 从response里面拿自己想要的结果
    		HttpEntity entity = response.getEntity();
    		if (entity != null) {
    			String result = EntityUtils.toString(entity, "UTF-8");
    			jsonObject = jsonObject.fromObject(result);
    		}
    		// 把链接释放掉
    		httpGet.releaseConnection();
    		return jsonObject;
    	}
    	
    	/**
    	 * @Title: getRequestIp
    	 * @Description: 获取用户的ip地址
    	 * @param:
    	 * @return:
    	 */
    	public static String getRequestIp(HttpServletRequest request) {
    		String ip = request.getHeader("x-forwarded-for");
    		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
    			ip = request.getHeader("Proxy-Client-IP");
    		}
    		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
    			ip = request.getHeader("WL-Proxy-Client-IP");
    		}
    		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
    			ip = request.getRemoteAddr();
    		}
    		if (ip.indexOf(",") != -1) {
    			String[] ips = ip.split(",");
    			ip = ips[0].trim();
    		}
    		return ip;
    	}
    }
    

    三,总结

    这几天将微信的功能都做完了。发现去调微信的接口的时候,基本上都是这样的流程:

    配置微信平台
    	||
    配置商家平台
    	||
    准备好必须的参数
    	||
    发送请求到微信
    	||
    获取返回的结果
    
    展开全文
  • 一、前言 这次的项目主要是关于微信公众号...二、微信申请退款接口微信退款接口文档:微信公众号退款申请接口开发文档退款申请流程:前端调用微信退款申请接口,退款申请需要双向的证书验证,登录微信商户平台(pay.w...

    一、前言

       这次的项目主要是关于微信公众号的一个开发,本人这次分配的模块是后台微信公众号的支付和退款,第一次接触微信公众的项目刚开始一脸懵逼,开发过程中遇到各种坑,所以想自己写一篇详细的关于微信公众号的开发,希望能对小伙伴们有所帮助!

    二、微信申请退款接口

    微信退款接口文档:微信公众号退款申请接口开发文档

    退款申请流程:前端调用微信退款申请接口,退款申请需要双向的证书验证,登录微信商户平台(pay.weixin.qq.com)-->账户中心-->账户设置-->API安全-->证书下载。具体安装请参考微信证书安装文档:商户证书安装指导。在微信退款申请接口调用时需要读取服务器安装的证书,然后才能想微信发送请求,否则请求回发送失败或者返回数据为空。如果退款接口中设置了退款结果通知的URL,那么在退款申请成功后会给设置的通知接口返回数据,当放回的结果为SUCCESS时,会携带一部分加密的数据,数据解密方式:

    解密步骤如下: 

    (1)对加密串A做base64解码,得到加密串B

    (2)对商户key做md5,得到32位小写key* ( key设置路径:微信商户平台(pay.weixin.qq.com)-->账户设置-->API安全-->密钥设置 )

    (3)用key*对加密串B做AES-256-ECB解密(PKCS7Padding)

    解密成功后可进行相应的业务处理,代码如下:

    申请退款接口:

    /**
    	 * 
    	* @Title: refund  
    	* @Description: 微信退款 
    	* @param @param request
    	* @param @param response
    	* @param @return
    	* @param @throws Exception     
    	* @return Map<String,String>    
    	* @throws
    	 */
    	@ResponseBody
    	@RequestMapping("/refund")
    	public JsPayResult refund(HttpServletRequest request, HttpServletResponse response) throws Exception {
    		
    		// 公众账号ID
    		String appid = Constants.APPID;
    		// 商户号
    		String mch_id = Constants.MCHID;
    		// 随机字符串
    		String nonce_str = CommonUtil.getRandomStr();
    		// 商户订单号
    		String out_trade_no = request.getParameter("out_trade_no");
    		// 商户退款单号,订单号是唯一的,加上订单号防止在高并发下退款单号不唯一
    		String out_refund_no = CommonUtil.getOrderIdByTime()+out_trade_no;
    		// 订单金额
    		String total_fee1 = CommonUtil.getMoney(request.getParameter("total_fee"));
    		String total_fee = CommonUtil.getMoney("0.01");
    		// 退款金额
    		String refund_fee1 = request.getParameter("refund_fee");
    		String refund_fee = CommonUtil.getMoney("0.01");
    		//退款结果通知url
    		String notify_url = Constants.REFOUND_NOTIFY_URL;
    		// 将请求参数封装至Map集合中
    		SortedMap<String, String> paramMap = new TreeMap<String, String>();
    		paramMap.put("appid", appid);
    		paramMap.put("mch_id", mch_id);
    		paramMap.put("nonce_str", nonce_str);
    		paramMap.put("out_trade_no", out_trade_no);
    		paramMap.put("out_refund_no", out_refund_no);
    		paramMap.put("total_fee", total_fee);
    		paramMap.put("refund_fee", refund_fee);
    		paramMap.put("notify_url", notify_url);
    		logger.info(paramMap);
    		// 签名
    		String sign = SignUtil.createSign(paramMap, Constants.PARTNER_KEY);
    		paramMap.put("sign", sign);
    		// 请求的xml数据
    		String requestXml = XMLUtil.map2Xml(paramMap, "xml");
    		
    	//1.指定读取证书格式为PKCS12
            KeyStore keyStore  = KeyStore.getInstance("PKCS12");
            //2.读取本机存放的PKCS12证书文件
            FileInputStream instream = new FileInputStream(new File(Constants.CERTIFICATE_PATH));
            try {
            	 //指定PKCS12的密码(商户ID)
                keyStore.load(instream, Constants.MCHID.toCharArray());
            } finally {
                instream.close();
            }
            //3.ssl双向验证发送http请求报文
            SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, Constants.MCHID.toCharArray()).build();
            SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext,new String[] { "TLSv1" },null,
                    SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
            CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
            //4.发送数据到微信的退款接口
            HttpPost httpost= HttpClientConnectionManager.getPostMethod(Constants.REFUND_URL);
            httpost.setEntity(new StringEntity(requestXml, "UTF-8"));
            HttpResponse weixinResponse = httpClient.execute(httpost);
            String resposeXmL = EntityUtils.toString(weixinResponse.getEntity(), "UTF-8");
            
    		//5.将返回的xml转换为map
    		Map<String, String> responseMap = XMLUtil.xml2Map(resposeXmL);
    		JsPayResult result = new JsPayResult();
    		if (Constants.RETURN_CODE.equals(responseMap.get("return_code"))) {
    			
    			result.setAppId(responseMap.get("appid"));
    			result.setMchId(responseMap.get("mch_id"));
    			result.setNonceStr(responseMap.get("nonce_str"));
    			// 微信订单号
    			result.setTransactionId(responseMap.get("transaction_id"));
    			// 商户订单号
    			result.setOutRradeNo(responseMap.get("out_trade_no"));
    			// 商户退款单号
    			result.setOutRefundNo(responseMap.get("out_refund_no"));
    			// 微信退款单号
    			result.setRefundId(responseMap.get("refund_id"));
    			// 退款金额
    			result.setSettlementRefundRee(responseMap.get("settlement_refund_fee"));
    			// 订单金额
    			result.setTotalFee(responseMap.get("total_fee"));
    			//申请退款金额
    			result.setRefundFee(responseMap.get("refund_fee"));
    			// 现金支付金额
    			result.setCashFee(responseMap.get("cash_fee"));
    			//退款状态
    			result.setRefundStatus(responseMap.get("refund_status"));
    			//退款成功时间
    			result.setSuccessTime(responseMap.get("success_time"));
    			//退款入账账户
    			result.setRefundRecvAccout(responseMap.get("refund_recv_accout"));
    			//退款资金来源
    			result.setRefundAccount(responseMap.get("refund_account"));
                //退款发起来源
    			result.setRefundRequestSource(responseMap.get("refund_request_source"));
    			result.setResultCode(Constants.RESULT_CODE_SUCCESS);
    			result.setMessage("退款成功!");
    			logger.info("*******退款申请**********"+"退款成功!");
    		}
    		else {
    			result.setResultCode(Constants.RESULT_CODE_FAIL);
    		    result.setMessage("退款失败!");
    		    logger.info("*******退款申请**********"+"退款失败!");
    			
    		}
    		return result;
    	}

    退款接口中涉及到的双向证书验证:

    //1.指定读取证书格式为PKCS12
            KeyStore keyStore  = KeyStore.getInstance("PKCS12");
            //2.读取本机存放的PKCS12证书文件
            FileInputStream instream = new FileInputStream(new File(Constants.CERTIFICATE_PATH));//读取证书的安装路径
            try {
            	 //指定PKCS12的密码(商户ID)
                keyStore.load(instream, Constants.MCHID.toCharArray());
            } finally {
                instream.close();
            }
            //3.ssl双向验证发送http请求报文
            SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, Constants.MCHID.toCharArray()).build();
            SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext,new String[] { "TLSv1" },null,
                    SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
            CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
            //4.发送数据到微信的退款接口
            HttpPost httpost= HttpClientConnectionManager.getPostMethod(Constants.REFUND_URL);
            httpost.setEntity(new StringEntity(requestXml, "UTF-8"));
            HttpResponse weixinResponse = httpClient.execute(httpost);
            String resposeXmL = EntityUtils.toString(weixinResponse.getEntity(), "UTF-8");

    三、退款结果通知接口

    /**
    	 * 
    	* @Title: refundNotify  
    	* @Description: 退款结果通知  
    	* @param @param request
    	* @param @param response
    	* @param @return
    	* @param @throws Exception     
    	* @return Map<String,String>    
    	* @throws
    	 */
    	@ResponseBody
    	@RequestMapping("/refundNotify")
    	public JsPayResult refundNotify(HttpServletRequest request, HttpServletResponse response) throws Exception {
    		// 将request请求中的数据转换为字符串
    		String reqpXml = CommonUtil.readRequestStr(request);
    		// 将返回串转换成 Map
    		Map<String, String> xmlToMap = XMLUtil.xml2Map(reqpXml);
    		// 返回给微信的结果
    		String respXml = "";
    		JsPayResult result = new JsPayResult();
    		// 在return_code为SUCCESS的时候有返回 req_info
    		if (Constants.RETURN_CODE.equals(xmlToMap.get("return_code"))) {
    			// 退款返回加密信息
    			String reqInfo = xmlToMap.get("req_info");
    			// 解密后的信息
    			String decodeReqInfo = AESUtil.decryptData(reqInfo);
    			// 将解密后的信息换成 Map
    			Map<String, String> reqInfoMap = XMLUtil.xml2Map(decodeReqInfo);
    
    			ResponseResult responseResult = refundRegistration(Constants.BRANCHCODE, reqInfoMap.get("out_trade_no"),
    					reqInfoMap.get("transaction_id"), "3300", DateUtil.getTradeTime(reqInfoMap.get("success_time")),
    					Constants.YYSOURCE);
    			if (Constants.RESULTCODE.equals(responseResult.getResultCode())) {
    				respXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>"
    						+ "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";
    				result.setMessage("退款成功!");
    			} else {
    				respXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"
    						+ "<return_msg><![CDATA[ERROR]]></return_msg>" + "</xml> ";
    				result.setMessage("退款失败!");
    			}
    			
    			logger.info("*******退款通知**********" + "退款成功!");
    		} else {
    			respXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>" + "<return_msg><![CDATA["
    					+ xmlToMap.get("return_code") + "]]></return_msg>" + "</xml> ";
    			result.setMessage("退款失败!");
    			logger.info("*******退款通知**********" + "退款失败!");
    		}
    
    		result.setResultCode(xmlToMap.get("return_code"));
    		BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
    		out.write(respXml.getBytes());
    		out.flush();
    		out.close();
    		return result;
    	}

    解密方式:

    public class AESUtil {
    
    	/**
    	 * 密钥算法
    	 */
    	private static final String ALGORITHM = "AES";
    	/**
    	 * 加解密算法/工作模式/填充方式
    	 */
    	private static final String ALGORITHM_MODE_PADDING = "AES/ECB/PKCS7Padding";
    	/**
    	 * 生成key(商户密钥)
    	 */
    	private static final String key = "qp2tMA7jIDLyRRhz83Ut2eVQh8qaI5PD";
    
    	/**
    	 * 对商户key做md5
    	 */
    	private static SecretKeySpec secretKey = new SecretKeySpec(MD5Util.MD5Encode(key, "UTF-8").toLowerCase().getBytes(),
    			ALGORITHM);
    
    	/**
    	 * AES加密
    	 * 
    	 * @param data
    	 * @return
    	 * @throws Exception
    	 */
    	public static String encryptData(String data) throws Exception {
    		Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
    		// 创建密码器
    		Cipher cipher = Cipher.getInstance(ALGORITHM_MODE_PADDING);
    		// 初始化
    		cipher.init(Cipher.ENCRYPT_MODE, secretKey);
    		return Base64Util.encode(cipher.doFinal(data.getBytes()));
    	}
    
    	/**
    	 * AES解密
    	 * 
    	 * @param base64Data
    	 * @return
    	 * @throws Exception
    	 */
    	public static String decryptData(String base64Data) throws Exception {
    		Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
    		// 创建密码器
    		Cipher cipher = Cipher.getInstance(ALGORITHM_MODE_PADDING);
    		// 初始化
    		cipher.init(Cipher.DECRYPT_MODE, secretKey);
    		return new String(cipher.doFinal(Base64Util.decode(base64Data)));
    	}
    
    	public static void main(String[] args) throws Exception {
    		// String A 为测试字符串,是微信返回过来的退款通知字符串
    		String A = "N0lJ0LWjHQlVOTEYdlRoVDbqWq+tRo9ZzvcwvIGHjFN6pYmHEX44W9l/jlyw8cwHueWk3m4hpldha73MgmCZJUu5LBZv27y/Vx2RkvHKCkiI5mV9pqQxiJmZTB1PN6s2wT3EVN1BFciGNzhozqQNIOyn/B9VOVKXkTeh1to8nI/UFVDexDE4ZyMBoB9oCQVcnkAuPaWqibHMU7i0iapB1UEMYJCgRKza9OGtvs0WbqIRgVVhtFxpxMhHmIaxzvH0JrdD/iOAYEV/NxUkye2HNJahatcYFBFQlbrTTBJ67MXZ6NzwFaOqqQYxZAKKEDrU++zu7hhX0lC5rZ5Uoyavn/sYTK3ZoCgAg/6O+S/f9sg+FoD8BrZmxC6tZwTfkDGsEO09m/JSTDxgU9ToCypyQt+bCxIFhLkGt8wKAtIEo0VOrfT9yMvHyBNLHtvNXj9gTQP+bMtpWAr0iMNgLwyXC2KY2FVxLmBEAnIIcXw7W15QItPcNVpQZceZooZwXn3QT6D4QDoHyg7ymHiAbtax0xHeYVGuGDB95E22q5C1Hh1a+7nyqkkJm1tzgJwyU+hhCw3Kw0Sj4JJVoLn6hFIBmVrDHf9x7j6VBULZ+39zk2upEudu/2TU1QVx96RCMW2O8EKXthPjzoOzZh1KeBsdkodrSn6gpBRNhIdbeimyAANVTyN+eeHThdx4tgEhodr9nVawFCSnD7jajowwaABFv/5AeWXSohfbbxAVrghNCjsfR4Grybr9fb6wB7hJ2yPZIKgdf8nGa9B7joKfZl2N7xIRawhGAVR3RRC2ajBEiabqaNhBCvhHPzR75oXViUL5OzVSyYznvrE1JEIgtGSN0rI/hUBIxhnTEv/X9C3NWiYRWoLMt29vJbQlk9hgQHVnTpH00khjXe8tdtMIkY7FUJmIsZH8D0jDMAvDj00Zl6r5z7FsyzRR+0xNsiyj8BPAxmSLqyrvXtgYx91N8I16TsgEBPJACL7tHkUr+kjQNXNzRp32mJFkB7/ZNQaXH8cm5aUAFk9eCuQAD4GqQxoYHOs/L7q2WMdajxPPxQSO6JU=";
    		String B = AESUtil.decryptData(A);
    		System.out.println(B);
    	}
    
    }

    有兴趣的朋友可以关注下本人的微信公众号:“JAVA菜鸟程序猿”

     

     

     

    展开全文
  • 接上一篇Java微信支付-统一下单API,本篇在上篇文章的基础上讲述微信支付-申请退款API 下文中所需配置、类都在以请查看以上链接内容。 话不多说,直接上码 package com.kkcode.kkclass.wechat.pay.service; ...

    接上一篇Java微信支付-统一下单API,本篇在上篇文章的基础上讲述微信支付-申请退款API
    下文中所需配置、类都在以请查看以上链接内容。

    话不多说,直接上码

    /**
     * 微信支付接口
     * 
     * @create: 2019-10-10 15:40
     * @author: Sun
     */
    public interface WxPayService {
    
        /**
         * 申请退款
         *
         * @param uid        用户id
         * @param orderNo    商户订单号
         * @param refundNo   商户退款单号
         * @param totalFee   订单金额
         * @param refundFee  退款金额
         * @param refundDesc 退款描述
         * @return return_code为SUCCESS的时候,将响应结果,存放到map中返回,否则throw WxPayException
         */
        Map<String, String> refund(String uid, String orderNo, String refundNo, String totalFee,
                                   String refundFee, String refundDesc) throws WxPayException;
    }
    
    
    /**
     * @author: Sun
     * @create: 2019-10-10 17:54
     * @version: v1.0
     */
    @Service
    @Slf4j
    public class WxPayServiceImpl implements WxPayService {
    
    	@Autowired
        private MyWxPayConfig myWxPayConfig;
    
    	/**
         * 退款 异步通知地址
         */
        @Value("${wxconfig.refund.notify.url}")
        private String refundAsyncNotifyUrl;
    
    	
    	@Override
        public Map<String, String> refund(String uid, String orderNo, String refundNo,
                                          String totalFee, String refundFee, String refundDesc) throws WxPayException {
            log.info("[refund] [入参] [uid:{}] [orderNo:{}] [refundNo:{}] [totalFee:{}] [refundFee:{}] [refundDesc:{}] ", uid, orderNo, refundNo, totalFee, refundFee, refundDesc);
    
            WXPay wxPay = new WXPay(myWxPayConfig);
            Map<String, String> paramMap = new HashMap<>();
            paramMap.put("out_trade_no", orderNo);
            paramMap.put("out_refund_no", refundNo);
            paramMap.put("total_fee", totalFee);
            paramMap.put("refund_fee", refundFee);
            paramMap.put("refund_desc", refundDesc);
            paramMap.put("notify_url", refundAsyncNotifyUrl);
    
            Map<String, String> resultMap = null;
            try {
                resultMap = wxPay.refund(paramMap);
                log.info("[refund] [退款API响应参数] [uid:{}] [out_trade_no:{}] [resultMap:{}]", uid, orderNo, resultMap);
            } catch (Exception e) {
                log.error("[refund] [uid:{}] [out_trade_no:{}]  [调用微信SDK过程中出现异常:{}]", uid, orderNo, ExceptionUtils.getStackTrace(e));
                throw new WxPayException(4506, "调用退款API异常,请重新尝试!");
            }
    
            return resultMap;
        }
    }
    
    展开全文
  • JAVA微信支付退款接口demo 应用场景 当交易发生之后一段时间内,由于买家或者卖家的原因需要退款时,卖家可以通过退款接口将支付款退还给买家,微信支付将在收到退款请求并且验证成功之后,按照退款规则将支付款按...
  • 一、接口链接:... 二、请求字段 三、注意事项 (1)需要证书: ...四、代码开发 .../** * 微信退款 * @throws Exception */ public static String refundFunction(Map&...

    一、接口链接:https://api.mch.weixin.qq.com/secapi/pay/refund

    二、请求字段

    三、注意事项

    (1)需要证书:

    (2)证书来源:

    (3)证书获取

    四、代码开发

    /**
    * 微信退款
    * @throws Exception
    */
    public static String refundFunction(Map<String, Object> map) throws Exception {
    //这里的map主要有两个参数(商户号mchId和订单金额totalFee)
    String result = "";//这里用于返回处理返回结果
    //xml转换为map,这里用于方便自己后面取出打印结果
    XmlToMap xmlToMap = new XmlToMap();
      //这里是自己封装的一些配置文件,大家可以跳过这一步,下面用到这里会和大家说明
    MyConfig config = null;
    try {
    //加载配置
    config = new MyConfig();
    } catch (Exception e) {
    e.printStackTrace();
    }

    //获取商户订单号和订单金额
    String mchId = map.get("mchId").toString();
    //获取订单金额(退款金额默认全部)
      //这里是自己做的一些格式的转换,有点笨拙,希望不影响大家的思路
    String a = map.get("totalFee")+"";
    String b = Double.valueOf(a) + "";
    int lastindex = b.indexOf(".");
    b = b.substring(0 , lastindex);
    int c = Integer.parseInt(b);
    // String d = c + "";
    // System.out.println("refundMoney--------->"+refundMoney);
    String totalFee = c + "";
    //获取微信订单号
    // String transactionId = map.get("transactionId").toString();

    SortedMap<Object,Object> parameters = new TreeMap<Object,Object>();
    parameters.put("appid", config.getAppID());//appid
    parameters.put("mch_id", config.getMchID());//商户号
    parameters.put("nonce_str", CreateNoncestr());//随机数
    // parameters.put("transaction_id", transactionId);//微信支付单号
    parameters.put("out_trade_no", mchId);//商户订单号
    parameters.put("out_refund_no", CreateNoncestr());//我们自己设定的退款申请号,约束为UK
    parameters.put("total_fee", totalFee) ;//订单金额 单位为分!!!这里稍微注意一下
    parameters.put("refund_fee", totalFee);//退款金额 单位为分!!!
    parameters.put("op_user_id", config.getMchID());//操作人员,默认为商户账号
    String sign = createSign("utf-8", parameters);
    System.out.println("sign---->"+sign);//签名
    parameters.put("sign", sign);

     //xml和map之间的转换
    String reuqestXml = getRequestXml(parameters);
    Map<String, Object> xmlMap = XmlToMap.xmlStr2Map(reuqestXml);

    KeyStore keyStore = KeyStore.getInstance("PKCS12");
    FileInputStream instream = new FileInputStream(new File(config.certPath));//放商户证书的路径
    try {
    keyStore.load(instream, config.getMchID().toCharArray());//商户号
    } finally {
    instream.close();
    }

     //这里导包注意一下,可能会冲突
    SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, config.getMchID().toCharArray()).build();//商户号
    SSLConnectionSocketFactory 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(config.refund_url);//退款接口

    System.out.println("executing request" + httpPost.getRequestLine());
    StringEntity reqEntity = new StringEntity(reuqestXml);
    // 设置类型
    reqEntity.setContentType("application/x-www-form-urlencoded");
    httpPost.setEntity(reqEntity);
    CloseableHttpResponse response = httpclient.execute(httpPost);
    try {
    HttpEntity entity = response.getEntity();

    System.out.println("----------------------------------------");
    System.out.println(response.getStatusLine());
    if (entity != null) {
    System.out.println("Response content length: " + entity.getContentLength());
    BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(entity.getContent(),"UTF-8"));
    System.out.println("bufferedReader.readLine()--->"+bufferedReader.readLine());
    String text;
    //这里是自己做的输出结果,方便查看错误原因
    while ((text = bufferedReader.readLine()) != null) {
    System.out.println(text);
    if(!text.startsWith("</xml>")) {
    text = "<xml>"+text+"</xml>";
    Map<String, Object> xmlResultMap = XmlToMap.xmlStr2Map(text);
    if(xmlResultMap.get("result_code") != null) {
    result = xmlResultMap.get("result_code")+"";
    }
    }
    }
    }
    EntityUtils.consume(entity);
    } finally {
    response.close();
    }
    } finally {
    httpclient.close();
    }
    return result;
    }

     

    转载于:https://www.cnblogs.com/qqzhulu/p/10311189.html

    展开全文
  • 准备工作:获取商户证书(java开发使用的证书文件apiclient_cert.p12)微信支付接口中,涉及资金回滚的接口会使用到商户证书,包括退款、撤销接口。商家在申请微信支付成功后,收到的相应邮件后,可以按照指引下载...
  • Java微信退款开发

    2019-04-04 15:20:25
    微信支付接口中,涉及资金回滚的接口会使用到商户证书,包括退款、撤销接口。商家在申请微信支付成功后,可以按照以下路径下载:微信商户平台(pay.weixin.qq.com)–>账户设置–>API安全–>证书下载。 ...
  • 微信支付退款-java版 首先,博主也是小菜鸟,撰写这篇文章的目的是让更多像我一样的走的路不再那么弯曲,引领初学者更好入门。...1、用户通过 APP 应用(微信公众号系统)提交退款申请给到商户; 2、商户核实 APP 用户...
  • 微信JSAPI支付,微信APP支付,微信退款,支付宝手机网站支付,支付宝APP支付,支付宝退款,我都放到个人公众号:JAVA大贼船。觉得个人以后开发会用到的可以关注一下哦!少走点弯路… 官方文档 APP申请退款和JSAPI申请...
  • 本文是【浅析微信支付】系列文章的第八篇,主要讲解商户如何处理微信申请退款退款回调、查询退款接口,其中有一些坑的地方,会着重强调。 浅析微信支付系列已经更新七篇了哟~,没有看过的朋友们可以看一下哦。 ...
  • 但近日我在调微信退款接口,发现有许多坑,和大家分享一下。希望对大家有帮助。     ① 要是在测试的时候,网页提示 curl 58 说明 证书的路径出现问题(这里要填物理路径,也就是绝对路径) ② 网页提示curl 52...
  • 前两天刚刚完成公司申请退款接口,来这里总结下自己遇到的那些坑,希望能帮助让大家少走些弯路,一路绿灯 微信官方API文档 业务很简单,就是把微信那边需要的参数封装到map里,一起传过去就好 需要准备的东西 还有...
  • 退款证书: 微信支付Key 后台操作 代码 操作: 一.开发前提: 下载demo: 这里使用的是官方的demo 沙箱准备: 沙箱可以在模拟环境中进行测试调用,你最好需要这个先调好api,然后再放到真实环境中. 上图的...
  • 前面已经讲过微信APP支付的统一下单、支付结果通知的接口开发,现在我们讲述一下申请退款的流程开发。 官方的API地址:https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_4&amp;index=6 1、应用...
  • 这阵子由于项目需要,需要对公众号的点餐程序添加微信支付与退款功能!想起大二上学期第一次做微信支付时的绝望,现在还记忆犹新。 这一次做微信支付相关的业务,是基于框架来做。如果有做过微信支付的同学,可能你...
  • java微信退款接口

    2019-08-20 16:23:33
    原文链接:... 微信公众号退款开发 博主是小菜鸟,这篇文章仅是自己开发的随笔记录,不足博友可以指出来,一起进步 1、【微信支付】公众号支付开发者文档链接地址 ... 调用微信退款接口,需要发...
  • 微信支付之退款

    2016-06-16 11:13:45
    下面介绍下微信支付退款功能的开发步骤: 一、下载证书并导入到系统 微信退款是需要证书的,这个证书不是官方demo中的证书,而是需要自己在微信商户平台中的api安全栏下载的证书,在官方的证书使用实例的一个...
  • 微信支付和退款的demo

    2020-07-27 23:32:32
    微信支付以及微信免密退款的demo 和开发文档说明.......
  • 微信退款开发 --Java

    2018-06-25 17:50:43
    一、下载证书并导入到系统 微信支付接口中,涉及资金回滚的接口会使用到商户证书,包括退款、撤销接口。商家在申请微信支付成功后,可以按照以下路径下载:微信商户平台(pay.weixin.qq.com)--&gt;账户设置--&...
1 2 3 4 5 ... 20
收藏数 1,689
精华内容 675
关键字:

微信开发退款申请