微信退款开发 微信如何申请退款

上一章咱们介绍了微信支付整个流程,这章就趁热打铁地整理下微信退款(JSAPI支付)相关的知识 。
前提:从微信公众号那边获取appid,mchid,paternerKey三个参数备用 。
1、微信申请退款的实现应用场景:
当交易发生之后一段时间内,由于买家或者卖家的原因需要退款时,卖家可以通过退款接口将支付款退还给买家,微信支付将在收到退款请求并且验证成功之后,按照退款规则将支付款按原路退到买家帐号上 。
注意:
【微信退款开发 微信如何申请退款】1、交易时间超过一年的订单无法提交退款
2、微信支付退款支持单笔交易分多次退款,多次退款需要提交原支付订单的商户订单号和设置不同的退款单号 。申请退款总金额不能超过订单金额 。一笔退款失败后重新提交,请不要更换退款单号,请使用原商户退款单号
3、请求频率限制:150qps,即每秒钟正常的申请退款请求次数不超过150次
错误或无效请求频率限制:6qps,即每秒钟异常或错误的退款申请请求不超过6次
4、每个支付订单的部分退款次数不能超过50次
5、如果同一个用户有多笔退款,建议分不同批次进行退款,避免并发退款导致退款失败
是否需要证书:
java开发申请退款需要要用到双向证书(apiclient_cert.p12证书) 。登录微信商户平台(https://pay.weixin.qq.com/)如下图所示操作

微信退款开发 微信如何申请退款

文章插图

申请退款接口调用:
@GET@Path(value = "https://www.goobye.net/refundOrder")@Produces(MediaType.APPLICATION_JSON)public Response refundOrder(@QueryParam("outTradeNo") String outTradeNo){Map result = new HashMap();try{// 1、获取参数,再进行申请退款Map paramMap = new HashMap();paramMap.put("appid", appid); //公众账号IDparamMap.put("mch_id", mchid); //商户号paramMap.put("nonce_str",WXPayUtil.generateNonceStr());//随机字符串paramMap.put("out_refund_no", WXPayUtil.generateNonceStr());//商户退款单号paramMap.put("out_trade_no", outTradeNo);//商户订单号paramMap.put("total_fee", price+"");//订单金额paramMap.put("refund_fee", price+"");//退款金额String sign = WXPayUtil.generateSignature(paramMap, paternerKey);paramMap.put("sign", sign); //签名String requestXmlString = WXPayUtil.mapToXml(paramMap);//转为xml字符串String refundUrl = "https://api.mch.weixin.qq.com/secapi/pay/refund";//微信申请退款接口// 2、判断微信是否退款成功HttpResponse resp = WeChatUtil.httpPost(refundUrl, requestXmlString);WeChatOrderResponse response = null;if (resp.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {String entityStr = EntityUtils.toString(resp.getEntity(), "UTF-8");response = (WeChatOrderResponse) WeChatUtil.xmlToPojo(entityStr,WeChatOrderResponse.class);}if("FAIL".equals(response.getReturn_code())){throw new RuntimeException("return_code为fail, " + response.getReturn_msg());}if("FAIL".equals(response.getResult_code())){throw new RuntimeException("result_code为fail, " +response.getErr_code_des());}//3、微信退款成功后的操作流程......result.put("code", SUCCESS);}catch(Exception e){result.put("code", ERROR);result.put("msg", e.getMessage());}return Response.ok(result).build();}
WeChatOrderResponse类的参数与微信的申请退款接口的返回结果一致,下面是该类的具体实现:
@XmlRootElement(name = "xml")public class WeChatOrderResponse {private String return_code;private String return_msg;// 以下字段在return_code为SUCCESS的时候有返回private String result_code;private String err_code;private String err_code_des;private String appid;private String mch_id;private String nonce_str;private String sign;private String transaction_id;private String out_trade_no;private String out_refund_no;private String refund_id;private String refund_fee;private String settlement_refund_fee;private String total_fee;private String settlement_total_fee;private String fee_type;private String cash_fee;private String cash_fee_type;private String cash_refund_fee;private String coupon_type_$n;private String coupon_refund_fee;private String coupon_refund_fee_$n;private String coupon_refund_count;private String coupon_refund_id_$n;// 以下字段在return_code 和result_code都为SUCCESS的时候有返回private String trade_type;private String prepay_id;private String code_url;private String openid;//参数的get/set方法......}
WeChatUtil工具类(亲测有效)的具体结构如下:
public class WeChatUtil {/*** Post请求+证书* @param url微信申请退款接口链接* @param entity微信申请退款接口的参数*/public static HttpResponse httpPost(String url, String entity) {//注意PKCS12证书 是从微信商户平台->账号设置->API安全 中下载的KeyStore keyStore = KeyStore.getInstance("PKCS12");//加载本地的证书进行https加密传输,keystorePath是证书的绝对路径FileInputStream instream = new FileInputStream(new File(keystorePath));try{//设置证书密码,keystorePassword:下载证书时的密码,默认密码是你的mchidkeyStore.load(instream, keystorePassword.toCharArray());} finally {instream.close();}//java 主动信任证书//keystorePassword:下载证书时的密码,默认密码是你的mchidSSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, keystorePassword.toCharArray()).build();SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[] { "TLSv1" }, null,SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);//CloseableHttpClient 加载证书来访问https网站CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();try {// 设置响应头信息,发送post请求HttpPost httpPost = new HttpPost(url);RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(5000).setConnectTimeout(5000).setConnectionRequestTimeout(5000).build();httpPost.setConfig(requestConfig);httpPost.setEntity(new StringEntity(entity, "UTF-8"));httpPost.setHeader("Accept", "*/*");httpPost.setHeader("Content-type", "application/xml");HttpResponse resp = httpClient.execute(httpPost);return resp;} catch (Exception e) {e.printStackTrace();return null;}}/*** 字符串转为对象* @param xmljson字符串* @param clazz*/public static Object xmlToPojo(String xml, Class clazz){try {JAXBContext context = JAXBContext.newInstance(clazz);Unmarshaller umMarshaller = context.createUnmarshaller();Object pojo = umMarshaller.unmarshal(new ByteArrayInputStream(xml.getBytes("utf-8")));}catch (Exception e ){e.printStackTrace();}}}