好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

Java微信退款开发

一、下载证书并导入到系统

微信支付接口中,涉及资金回滚的接口会使用到商户证书,包括退款、撤销接口。商家在申请微信支付成功后,可以按照以下路径下载:微信商户平台(pay.weixin.qq.com)-->账户设置-->api安全-->证书下载。

下载的时候需要手机验证及登录密码。下载后找到apiclient_cert.p12这个证书,双击导入,导入的时候提示输入密码,这个密码就是商户id,且必须是在自己的商户平台下载的证书。否则会出现密码错误的提示:

导入正确的提示:

二、编写代码

首先初始化退款接口中的请求参数,如微信订单号transaction_id(和商户订单号只需要知道一个)、订单金额total_fee等;其次调用mobimessage中的refundresdata2xml方法解析成需要的类型;最后调用refundrequest类的httpsrequest方法触发请求。

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

/**

  * 处理退款请求

  * @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方法。

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

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数据之间的转换。

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

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方法加载证书到系统中,其中证书地址如下:

?

1

public static string certlocalpath = "/web-inf/cert/apiclient_cert.p12" ;

refundrequest类中httpsrequest方法调用微信接口,触发请求。

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

/**

  * 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;

  }

}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。

原文链接:https://blog.csdn.net/fenghuibian/article/details/52459699

查看更多关于Java微信退款开发的详细内容...

  阅读:13次