玩转小程序支付之退款

完成小程序支付和保存支付通知内容之后,接下来就是退款啦

官方文档:https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=9_4

注意:

调用API时需要使用证书,所以我们需要下载证书,并放在服务器里某个位置

/*
* 密钥证书文件的存放路径
*/
public static final String KEY_PATH = "/wwwroot/web/wechat/apiclient_cert.p12";

 代码如下:

----SpringBoot 的Controller

/**
	   * 申请退款
	   * @return
	   */
	  @RequestMapping(value = "/refund", method = RequestMethod.GET)
	  @Transactional
	  public @ResponseBody Map<String, Object> refund(String id,String user) {
	        Map<String,Object> result = new HashMap<String,Object>();
	        String currTime = PayUtils.getCurrTime();
	     	String strTime = currTime.substring(8, currTime.length());
	     	String strRandom = PayUtils.buildRandom(4) + "";
	     	String nonceStr = strTime + strRandom;
	     	String outRefundNo = "wx@re@"+PayUtils.getTimeStamp();
	     	String outTradeNo = "";
	     	
	     	ProfPayLog payLog = wxappOrderService.findByPayLogId(Long.valueOf(id));
	     	DecimalFormat df = new DecimalFormat("######0"); 
	     	String fee = String.valueOf(df.format((Double.valueOf(payLog.getTotalFee())*100)));
	     	SortedMap<String, String> packageParams = new TreeMap<String, String>();
	     	packageParams.put("appid", appId);
	     	packageParams.put("mch_id", mchId);//微信支付分配的商户号
	     	packageParams.put("nonce_str", nonceStr);//随机字符串,不长于32位
	     	packageParams.put("op_user_id", mchId);//操作员帐号, 默认为商户号
	     	//out_refund_no只能含有数字、字母和字符_-|*@
	     	packageParams.put("out_refund_no", outRefundNo);//商户系统内部的退款单号,商户系统内部唯一,同一退款单号多次请求只退一笔
	     	packageParams.put("out_trade_no", outTradeNo);//商户侧传给微信的订单号32位
	     	packageParams.put("refund_fee", fee);
	     	packageParams.put("total_fee", fee);
	     	packageParams.put("transaction_id", payLog.getTransactionId());//微信生成的订单号,在支付通知中有返回
	     	String sign = PayUtils.createSign(packageParams,key);
	     	
	     	String refundUrl = "https://api.mch.weixin.qq.com/secapi/pay/refund";
	     	String xmlParam="<xml>"+
	     			"<appid>"+appId+"</appid>"+
	     			"<mch_id>"+mchId+"</mch_id>"+
	     			"<nonce_str>"+nonceStr+"</nonce_str>"+
	     			"<op_user_id>"+mchId+"</op_user_id>"+
	     			"<out_refund_no>"+outRefundNo+"</out_refund_no>"+
	     			"<out_trade_no>"+outTradeNo+"</out_trade_no>"+
	     			"<refund_fee>"+fee+"</refund_fee>"+
	     			"<total_fee>"+fee+"</total_fee>"+
	     			"<transaction_id>"+payLog.getTransactionId()+"</transaction_id>"+
	     			"<sign>"+sign+"</sign>"+
	     			"</xml>";
	     	String resultStr = PayUtils.post(refundUrl, xmlParam);
	     	//解析结果
	     	try {
				Map map =  PayUtils.doXMLParse(resultStr);
				String returnCode = map.get("return_code").toString();
		        if(returnCode.equals("SUCCESS")){
		        	String resultCode = map.get("result_code").toString();
		        	if(resultCode.equals("SUCCESS")){
		        		ProfPayLog profPayLog = new ProfPayLog();
		        		profPayLog.setCreatedAt(new Date());
		        		profPayLog.setSource(payLog.getSource());
	        			profPayLog.setTotalFee(payLog.getTotalFee());
	        			profPayLog.setTradeNo(payLog.getTradeNo());
	        			profPayLog.setTransactionId(map.get("refund_id").toString());
	        			profPayLog.setUserId(user);
	        			profPayLog.setType(ProfPayLog.Type.Refund);
	        			profPayLog = wxappOrderService.save(profPayLog);
		        		result.put("status", "success");
		        	}else{
		        		result.put("status", "fail");
		        	}
		        }else{
		        	result.put("status", "fail");
		        }
			} catch (Exception e) {
				e.printStackTrace();
				result.put("status", "fail");
			}
	     	return result;
	  }

  

----PayUtils  

public static String post(String url, String xmlParam){
		StringBuilder sb = new StringBuilder();
		 try {
			    KeyStore keyStore  = KeyStore.getInstance("PKCS12");
		        FileInputStream instream = new FileInputStream(new File(KEY_PATH));
		        try {
		            keyStore.load(instream, "商户id".toCharArray());
		        } finally {
		            instream.close();
		        }
		 
		        // 证书
		        SSLContext sslcontext = SSLContexts.custom()
		                .loadKeyMaterial(keyStore, "商户id".toCharArray())
		                .build();
		        // 只允许TLSv1协议
		        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
		                sslcontext,
		                new String[] { "TLSv1" },
		                null,
		                SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
		        //创建基于证书的httpClient,后面要用到
		        CloseableHttpClient client = HttpClients.custom()
		                .setSSLSocketFactory(sslsf)
		                .build();
		        
             HttpPost httpPost = new HttpPost(url);//退款接口
             StringEntity  reqEntity  = new StringEntity(xmlParam);
             // 设置类型 
             reqEntity.setContentType("application/x-www-form-urlencoded"); 
             httpPost.setEntity(reqEntity);
             CloseableHttpResponse response = client.execute(httpPost);
             try {
                 HttpEntity entity = response.getEntity();
                 System.out.println(response.getStatusLine());
                 if (entity != null) {
                     BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(entity.getContent(),"UTF-8"));
                     String text="";
                     while ((text = bufferedReader.readLine()) != null) {
                         sb.append(text);
                     }
                 }
                 EntityUtils.consume(entity);
             } catch(Exception e){
            	 e.printStackTrace();
             }finally {
                 try {
					response.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
             }
         } catch (Exception e) {
				e.printStackTrace();
		}

  注意:商户id那个地方填前面用到的mch_id,因为证书是微信认证过的商户独有的。

原文地址:https://www.cnblogs.com/tonyccc/p/7070062.html