Java微信退款开发
程序员文章站
2024-03-07 08:18:20
一、下载证书并导入到系统
微信支付接口中,涉及资金回滚的接口会使用到商户证书,包括退款、撤销接口。商家在申请微信支付成功后,可以按照以下路径下载:微信商户平台(pay.w...
一、下载证书并导入到系统
微信支付接口中,涉及资金回滚的接口会使用到商户证书,包括退款、撤销接口。商家在申请微信支付成功后,可以按照以下路径下载:微信商户平台(pay.weixin.qq.com)-->账户设置-->api安全-->证书下载。
下载的时候需要手机验证及登录密码。下载后找到apiclient_cert.p12这个证书,双击导入,导入的时候提示输入密码,这个密码就是商户id,且必须是在自己的商户平台下载的证书。否则会出现密码错误的提示:
导入正确的提示:
二、编写代码
首先初始化退款接口中的请求参数,如微信订单号transaction_id(和商户订单号只需要知道一个)、订单金额total_fee等;其次调用mobimessage中的refundresdata2xml方法解析成需要的类型;最后调用refundrequest类的httpsrequest方法触发请求。
/** * 处理退款请求 * @param request * @return * @throws exception */ @requestmapping("/refund") @responsebody public jsonapi refund(httpservletrequest request) throws exception { //获得当前目录 string path = request.getsession().getservletcontext().getrealpath("/"); logutils.trace(path); date now = new date(); simpledateformat dateformat = new simpledateformat("yyyymmddhhmmss");//可以方便地修改日期格式 string outrefundno = "no" + dateformat.format( now ); //获得退款的传入参数 string transactionid = "4008202001201609012791655620"; string outtradeno = "20160901141024"; integer totalfee = 1; integer refundfee = totalfee; refundreqdata refundreqdata = new refundreqdata(transactionid,outtradeno,outrefundno,totalfee,refundfee); string info = mobimessage.refundreqdata2xml(refundreqdata).replaceall("__", "_"); logutils.trace(info); try { refundrequest refundrequest = new refundrequest(); string result = refundrequest.httpsrequest(wxconfigure.refund_api, info, path); logutils.trace(result); map<string, string> getmap = mobimessage.parsexml(new string(result.tostring().getbytes(), "utf-8")); if("success".equals(getmap.get("return_code")) && "success".equals(getmap.get("return_msg"))){ return new jsonapi(); }else{ //返回错误描述 return new jsonapi(getmap.get("err_code_des")); } }catch(exception e){ e.printstacktrace(); return new jsonapi(); } }
初始化退款接口需要的数据,隐藏了get和set方法。
public class refundreqdata { //每个字段具体的意思请查看api文档 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 int total_fee = 0; private int refund_fee = 0; private string op_user_id = ""; /** * 请求退款服务 * @param transactionid 是微信系统为每一笔支付交易分配的订单号,通过这个订单号可以标识这笔交易,它由支付订单api支付成功时返回的数据里面获取到。建议优先使用 * @param outtradeno 商户系统内部的订单号,transaction_id 、out_trade_no 二选一,如果同时存在优先级:transaction_id>out_trade_no * @param outrefundno 商户系统内部的退款单号,商户系统内部唯一,同一退款单号多次请求只退一笔 * @param totalfee 订单总金额,单位为分 * @param refundfee 退款总金额,单位为分 */ public refundreqdata(string transactionid,string outtradeno,string outrefundno,int totalfee,int refundfee){ //微信分配的公众号id(开通公众号之后可以获取到) setappid(wxconfigure.appid); //微信支付分配的商户号id(开通公众号的微信支付功能之后可以获取到) setmch_id(wxconfigure.mch_id); //transaction_id是微信系统为每一笔支付交易分配的订单号,通过这个订单号可以标识这笔交易,它由支付订单api支付成功时返回的数据里面获取到。 settransaction_id(transactionid); //商户系统自己生成的唯一的订单号 setout_trade_no(outtradeno); setout_refund_no(outrefundno); settotal_fee(totalfee); setrefund_fee(refundfee); setop_user_id(wxconfigure.mch_id); //随机字符串,不长于32 位 setnonce_str(stringutil.generaterandomstring(16)); //根据api给的签名规则进行签名 sortedmap<object, object> parameters = new treemap<object, object>(); parameters.put("appid", appid); parameters.put("mch_id", mch_id); parameters.put("nonce_str", nonce_str); parameters.put("transaction_id", transaction_id); parameters.put("out_trade_no", out_trade_no); parameters.put("out_refund_no", out_refund_no); parameters.put("total_fee", total_fee); parameters.put("refund_fee", refund_fee); parameters.put("op_user_id", op_user_id); string sign = dictionarysort.createsign(parameters); setsign(sign); //把签名数据设置到sign这个属性中 }
mobimessage实现json数据类型和xml数据之间的转换。
public class mobimessage { public static map<string,string> xml2map(httpservletrequest request) throws ioexception, documentexception { map<string,string> map = new hashmap<string, string>(); saxreader reader = new saxreader(); inputstream inputstream = request.getinputstream(); document document = reader.read(inputstream); element root = document.getrootelement(); list<element> list = root.elements(); for(element e:list){ map.put(e.getname(), e.gettext()); } inputstream.close(); return map; } //订单转换成xml public static string jsapireqdata2xml(jsapireqdata jsapireqdata){ /*xstream xstream = new xstream(); xstream.alias("xml",productinfo.getclass()); return xstream.toxml(productinfo);*/ mobimessage.xstream.alias("xml",jsapireqdata.getclass()); return mobimessage.xstream.toxml(jsapireqdata); } public static string refundreqdata2xml(refundreqdata refundreqdata){ /*xstream xstream = new xstream(); xstream.alias("xml",productinfo.getclass()); return xstream.toxml(productinfo);*/ mobimessage.xstream.alias("xml",refundreqdata.getclass()); return mobimessage.xstream.toxml(refundreqdata); } public static string class2xml(object object){ return ""; } public static map<string, string> parsexml(string xml) throws exception { map<string, string> map = new hashmap<string, string>(); document document = documenthelper.parsetext(xml); element root = document.getrootelement(); list<element> elementlist = root.elements(); for (element e : elementlist) map.put(e.getname(), e.gettext()); return map; } //扩展xstream,使其支持cdata块 private static xstream xstream = new xstream(new xppdriver() { public hierarchicalstreamwriter createwriter(writer out) { return new prettyprintwriter(out) { // 对所有xml节点的转换都增加cdata标记 boolean cdata = true; //@suppresswarnings("unchecked") public void startnode(string name, class clazz) { super.startnode(name, clazz); } protected void writetext(quickwriter writer, string text) { if (cdata) { writer.write("<![cdata["); writer.write(text); writer.write("]]>"); } else { writer.write(text); } } }; } }); }
refundrequest类中initcert方法加载证书到系统中,其中证书地址如下:
public static string certlocalpath = "/web-inf/cert/apiclient_cert.p12";
refundrequest类中httpsrequest方法调用微信接口,触发请求。
/** * user: rizenguo * date: 2014/10/29 * time: 14:36 */ public class refundrequest { //连接超时时间,默认10秒 private int sockettimeout = 10000; //传输超时时间,默认30秒 private int connecttimeout = 30000; //请求器的配置 private requestconfig requestconfig; //http请求器 private closeablehttpclient httpclient; /** * 加载证书 * @param path * @throws ioexception * @throws keystoreexception * @throws unrecoverablekeyexception * @throws nosuchalgorithmexception * @throws keymanagementexception */ private void initcert(string path) throws ioexception, keystoreexception, unrecoverablekeyexception, nosuchalgorithmexception, keymanagementexception { //拼接证书的路径 path = path + wxconfigure.certlocalpath; keystore keystore = keystore.getinstance("pkcs12"); //加载本地的证书进行https加密传输 fileinputstream instream = new fileinputstream(new file(path)); try { keystore.load(instream, wxconfigure.mch_id.tochararray()); //加载证书密码,默认为商户id } catch (certificateexception e) { e.printstacktrace(); } catch (nosuchalgorithmexception e) { e.printstacktrace(); } finally { instream.close(); } // trust own ca and all self-signed certs sslcontext sslcontext = sslcontexts.custom() .loadkeymaterial(keystore, wxconfigure.mch_id.tochararray()) //加载证书密码,默认为商户id .build(); // allow tlsv1 protocol only sslconnectionsocketfactory sslsf = new sslconnectionsocketfactory( sslcontext, new string[]{"tlsv1"}, null, sslconnectionsocketfactory.browser_compatible_hostname_verifier); httpclient = httpclients.custom() .setsslsocketfactory(sslsf) .build(); //根据默认超时限制初始化requestconfig requestconfig = requestconfig.custom().setsockettimeout(sockettimeout).setconnecttimeout(connecttimeout).build(); } /** * 通过https往api post xml数据 * @param url api地址 * @param xmlobj 要提交的xml数据对象 * @param path 当前目录,用于加载证书 * @return * @throws ioexception * @throws keystoreexception * @throws unrecoverablekeyexception * @throws nosuchalgorithmexception * @throws keymanagementexception */ public string httpsrequest(string url, string xmlobj, string path) throws ioexception, keystoreexception, unrecoverablekeyexception, nosuchalgorithmexception, keymanagementexception { //加载证书 initcert(path); 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); //设置请求器的配置 httppost.setconfig(requestconfig); try { httpresponse response = httpclient.execute(httppost); httpentity entity = response.getentity(); result = entityutils.tostring(entity, "utf-8"); } catch (connectionpooltimeoutexception e) { logutils.trace("http get throw connectionpooltimeoutexception(wait time out)"); } catch (connecttimeoutexception e) { logutils.trace("http get throw connecttimeoutexception"); } catch (sockettimeoutexception e) { logutils.trace("http get throw sockettimeoutexception"); } catch (exception e) { logutils.trace("http get throw exception"); } finally { httppost.abort(); } return result; } }
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
下一篇: 缓冲流_转换流_序列化流_打印流