好得很程序员自学网

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

java实现微信App支付服务端

微信 app支付服务端的实现方法,供大家参考,具体内容如下

引言

主要实现app支付统一下单、异步通知、调起支付接口、支付订单查询、申请退款、查询退款功能;封装了https对发起退款的证书校验、签名、xml解析等。

支付流程

具体支付流程参考[微信app]文档, 文档地址

app支付: app端点击下单—-服务端生成订单,并调起[统一下单],返回app支付所需参数—–app端[调起支付接口[,发起支付—-微信服务器端调用服务端回调地址—–服务端按照[支付结果通知],处理支付结果

app查询: 调起[查询订单]

app退款: 发起退款请求,调用[申请退款],发起退款,需双向证书验证

app退款查询: 调起[查询退款]

支付代码实现

代码实现签名、证书校验、http和https封装等,项目结构如下:

支付代码

包含支付、支付查询、异步通知、退款申请、退款查询

?

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

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

268

269

270

271

272

273

274

275

276

277

278

279

280

281

282

283

284

285

286

287

288

289

290

291

292

293

294

295

296

297

298

299

300

301

302

303

304

305

306

307

308

309

310

311

312

313

314

315

316

317

318

319

320

321

322

323

324

325

326

327

328

329

330

331

332

333

334

335

336

337

338

339

340

341

342

343

344

345

package org.andy.wxpay.controller;

 

import java.math.bigdecimal;

import java.util.hashmap;

import java.util.map;

 

import javax.servlet.servletinputstream;

import javax.servlet.http.httpservletrequest;

import javax.servlet.http.httpservletresponse;

 

import org.andy.wxpay.model.jsonresult;

import org.andy.wxpay.model.responsedata;

import org.andy.wxpay.utils.collectionutil;

import org.andy.wxpay.utils.configutil;

import org.andy.wxpay.utils.fileutil;

import org.andy.wxpay.utils.httputils;

import org.andy.wxpay.utils.payutil;

import org.andy.wxpay.utils.serializerfeatureutil;

import org.andy.wxpay.utils.stringutil;

import org.andy.wxpay.utils.webutil;

import org.andy.wxpay.utils.xmlutil;

import org.apache.log4j.logger;

import org.springframework.stereotype.controller;

import org.springframework.web.bind.annotation.requestmapping;

import org.springframework.web.bind.annotation.requestmethod;

import org.springframework.web.bind.annotation.requestparam;

 

import com.alibaba.fastjson.json;

 

/**

  * 创建时间:2016年11月2日 下午4:16:32

  *

  * @author andy

  * @version 2.2

  */

@controller

@requestmapping ( "/order" )

public class paycontroller {

 

  private static final logger log = logger.getlogger(paycontroller. class );

 

  private static final string order_pay = "https://api.mch.weixin.qq.com/pay/unifiedorder" ; // 统一下单

 

  private static final string order_pay_query = "https://api.mch.weixin.qq.com/pay/orderquery" ; // 支付订单查询

 

  private static final string order_refund = "https://api.mch.weixin.qq.com/secapi/pay/refund" ; // 申请退款

 

  private static final string order_refund_query = "https://api.mch.weixin.qq.com/pay/refundquery" ; // 申请退款

 

  private static final string app_id = configutil.getproperty( "wx.appid" );

 

  private static final string mch_id = configutil.getproperty( "wx.mchid" );

 

  private static final string api_secret = configutil.getproperty( "wx.api.secret" );

 

  /**

  * 支付下订单

  *

  * @param request

  * @param response

  * @param cashnum

  * 支付金额

  * @param mercid

  * 商品id

  * @param callback

  */

  @requestmapping (value = "/pay" , method = requestmethod.post)

  public void orderpay(httpservletrequest request, httpservletresponse response,

  @requestparam (required = false , defaultvalue = "0" ) double cashnum, string mercid, string callback) {

  log.info( "[/order/pay]" );

  if (! "001" .equals(mercid)) {

  webutil.response(response, webutil.packjsonp(callback, json

  .tojsonstring( new jsonresult(- 1 , "商品不存在" , new responsedata()), serializerfeatureutil.features)));

  }

 

  map<string, string> restmap = null ;

  boolean flag = true ; // 是否订单创建成功

  try {

  string total_fee = bigdecimal.valueof(cashnum).multiply(bigdecimal.valueof( 100 ))

  .setscale( 0 , bigdecimal.round_half_up).tostring();

  map<string, string> parm = new hashmap<string, string>();

  parm.put( "appid" , app_id);

  parm.put( "mch_id" , mch_id);

  parm.put( "device_info" , "web" );

  parm.put( "nonce_str" , payutil.getnoncestr());

  parm.put( "body" , "测试付费" );

  parm.put( "attach" , "andy" );

  parm.put( "out_trade_no" , payutil.gettradeno());

  parm.put( "total_fee" , total_fee);

  parm.put( "spbill_create_ip" , payutil.getremoteaddrip(request));

  parm.put( "notify_url" , "https://www.andy.org/wxpay/order/pay/notify.shtml" );

  parm.put( "trade_type" , "app" );

  parm.put( "sign" , payutil.getsign(parm, api_secret));

 

  string restxml = httputils.post(order_pay, xmlutil.xmlformat(parm, false ));

  restmap = xmlutil.xmlparse(restxml);

  } catch (exception e) {

  log.error(e.getmessage(), e);

  }

 

  map<string, string> paymap = new hashmap<string, string>();

  if (collectionutil.isnotempty(restmap) && "success" .equals(restmap.get( "result_code" ))) {

  paymap.put( "appid" , app_id);

  paymap.put( "partnerid" , mch_id);

  paymap.put( "prepayid" , restmap.get( "prepay_id" ));

  paymap.put( "package" , "sign=wxpay" );

  paymap.put( "noncestr" , payutil.getnoncestr());

  paymap.put( "timestamp" , payutil.paytimestamp());

  try {

  paymap.put( "sign" , payutil.getsign(paymap, api_secret));

  } catch (exception e) {

  flag = false ;

  }

  }

 

  if (flag) {

  webutil.response(response,

  webutil.packjsonp(callback,

  json.tojsonstring( new jsonresult( 1 , "订单获取成功" , new responsedata( null , paymap)),

   serializerfeatureutil.features)));

  } else {

  if (collectionutil.isnotempty(restmap)) {

  log.info( "订单创建失败:" + restmap.get( "err_code" ) + ":" + restmap.get( "err_code_des" ));

  }

  webutil.response(response, webutil.packjsonp(callback, json

  .tojsonstring( new jsonresult(- 1 , "订单获取失败" , new responsedata()), serializerfeatureutil.features)));

  }

  }

 

 

  /**

  * 查询支付结果

  *

  * @param request

  * @param response

  * @param tradeid 微信交易订单号

  * @param tradeno 商品订单号

  * @param callback

  */

  @requestmapping (value = "/pay/query" , method = requestmethod.post)

  public void orderpayquery(httpservletrequest request, httpservletresponse response, string tradeid, string tradeno,

  string callback) {

  log.info( "[/order/pay/query]" );

  if (stringutil.isempty(tradeno) && stringutil.isempty(tradeid)) {

  webutil.response(response, webutil.packjsonp(callback, json

  .tojsonstring( new jsonresult(- 1 , "订单号不能为空" , new responsedata()), serializerfeatureutil.features)));

  }

 

  map<string, string> restmap = null ;

  try {

  map<string, string> parm = new hashmap<string, string>();

  parm.put( "appid" , app_id);

  parm.put( "mch_id" , mch_id);

  parm.put( "transaction_id" , tradeid);

  parm.put( "out_trade_no" , tradeno);

  parm.put( "nonce_str" , payutil.getnoncestr());

  parm.put( "sign" , payutil.getsign(parm, api_secret));

 

  string restxml = httputils.post(order_pay_query, xmlutil.xmlformat(parm, false ));

  restmap = xmlutil.xmlparse(restxml);

  } catch (exception e) {

  log.error(e.getmessage(), e);

  }

 

  if (collectionutil.isnotempty(restmap) && "success" .equals(restmap.get( "result_code" ))) {

  // 订单查询成功 处理业务逻辑

  log.info( "订单查询:订单" + restmap.get( "out_trade_no" ) + "支付成功" );

  webutil.response(response, webutil.packjsonp(callback, json

  .tojsonstring( new jsonresult( 1 , "订单支付成功" , new responsedata()), serializerfeatureutil.features)));

  } else {

  if (collectionutil.isnotempty(restmap)) {

  log.info( "订单支付失败:" + restmap.get( "err_code" ) + ":" + restmap.get( "err_code_des" ));

  }

  webutil.response(response, webutil.packjsonp(callback, json

  .tojsonstring( new jsonresult(- 1 , "订单支付失败" , new responsedata()), serializerfeatureutil.features)));

  }

  }

 

 

  /**

  * 订单支付微信服务器异步通知

  *

  * @param request

  * @param response

  */

  @requestmapping ( "/pay/notify" )

  public void orderpaynotify(httpservletrequest request, httpservletresponse response) {

  log.info( "[/order/pay/notify]" );

  response.setcharacterencoding( "utf-8" );

  response.setcontenttype( "text/xml" );

  try {

  servletinputstream in = request.getinputstream();

  string resxml = fileutil.readinputstream2string(in);

  map<string, string> restmap = xmlutil.xmlparse(resxml);

  log.info( "支付结果通知:" + restmap);

  if ( "success" .equals(restmap.get( "result_code" ))) {

  // 订单支付成功 业务处理

  string out_trade_no = restmap.get( "out_trade_no" ); // 商户订单号

  // 通过商户订单判断是否该订单已经处理 如果处理跳过 如果未处理先校验sign签名 再进行订单业务相关的处理

 

  string sing = restmap.get( "sign" ); // 返回的签名

  restmap.remove( "sign" );

  string signnow = payutil.getsign(restmap, api_secret);

  if (signnow.equals(sing)) {

  // 进行业务处理

  log.info( "订单支付通知: 支付成功,订单号" + out_trade_no);

 

  // 处理成功后相应给响应xml

  map<string, string> respmap = new hashmap<>();

  respmap = new hashmap<string, string>();

  respmap.put( "return_code" , "success" ); //相应给微信服务器

  respmap.put( "return_msg" , "ok" );

  string resxml = xmlutil.xmlformat(restmap, true );

  response.getwriter().write(resxml);

  } else {

  log.info( "订单支付通知:签名错误" );

  }

  } else {

  log.info( "订单支付通知:支付失败," + restmap.get( "err_code" ) + ":" + restmap.get( "err_code_des" ));

  }

  } catch (exception e) {

  log.error(e.getmessage(), e);

  }

  }

 

  /**

  * 订单退款 需要双向证书验证

  *

  * @param request

  * @param response

  * @param tradeno 微信订单号

  * @param orderno 商家订单号

  * @param callback

  */

  @requestmapping (value = "/pay/refund" , method = requestmethod.post)

  public void orderpayrefund(httpservletrequest request, httpservletresponse response, string tradeno, string orderno,

  string callback) {

  log.info( "[/pay/refund]" );

  if (stringutil.isempty(tradeno) && stringutil.isempty(orderno)) {

  webutil.response(response, webutil.packjsonp(callback, json

  .tojsonstring( new jsonresult(- 1 , "订单号不能为空" , new responsedata()), serializerfeatureutil.features)));

  }

 

  map<string, string> restmap = null ;

  try {

  map<string, string> parm = new hashmap<string, string>();

  parm.put( "appid" , app_id);

  parm.put( "mch_id" , mch_id);

  parm.put( "nonce_str" , payutil.getnoncestr());

  parm.put( "transaction_id" , tradeno);

  parm.put( "out_trade_no" , orderno); //订单号

  parm.put( "out_refund_no" , payutil.getrefundno()); //退款单号

  parm.put( "total_fee" , "10" ); // 订单总金额 从业务逻辑获取

  parm.put( "refund_fee" , "10" ); // 退款金额

  parm.put( "op_user_id" , mch_id);

  parm.put( "refund_account" , "refund_source_recharge_funds" ); //退款方式

  parm.put( "sign" , payutil.getsign(parm, api_secret));

 

  //string restxml = httputils.posts(order_refund, xmlutil.xmlformat(parm, false));

  string restxml = httputils.posts(order_refund, xmlutil.xmlformat(parm, false ));

  restmap = xmlutil.xmlparse(restxml);

  } catch (exception e) {

  log.error(e.getmessage(), e);

  }

 

  map<string, string> refundmap = new hashmap<>();

  if (collectionutil.isnotempty(restmap) && "success" .equals(restmap.get( "result_code" ))) {

  refundmap.put( "transaction_id" , restmap.get( "transaction_id" ));

  refundmap.put( "out_trade_no" , restmap.get( "out_trade_no" ));

  refundmap.put( "refund_id" , restmap.get( "refund_id" ));

  refundmap.put( "out_refund_no" , restmap.get( "out_refund_no" ));

  log.info( "订单退款:订单" + restmap.get( "out_trade_no" ) + "退款成功,商户退款单号" + restmap.get( "out_refund_no" ) + ",微信退款单号"

  + restmap.get( "refund_id" ));

  webutil.response(response,

  webutil.packjsonp(callback,

  json.tojsonstring( new jsonresult( 1 , "订单获取成功" , new responsedata( null , refundmap)),

   serializerfeatureutil.features)));

  } else {

  if (collectionutil.isnotempty(restmap)) {

  log.info( "订单退款失败:" + restmap.get( "err_code" ) + ":" + restmap.get( "err_code_des" ));

  }

  webutil.response(response, webutil.packjsonp(callback, json

  .tojsonstring( new jsonresult(- 1 , "订单退款失败" , new responsedata()), serializerfeatureutil.features)));

  }

  }

 

  /**

  * 订单退款查询

  * @param request

  * @param response

  * @param tradeid 微信订单号

  * @param tradeno 商户订单号

  * @param refundid 微信退款号

  * @param refundno 商家退款号

  * @param callback

  */

  @requestmapping (value = "/pay/refund/query" , method = requestmethod.post)

  public void orderpayrefundquery(httpservletrequest request, httpservletresponse response, string refundid,

  string refundno, string tradeid, string tradeno, string callback) {

  log.info( "[/pay/refund/query]" );

  if (stringutil.isempty(tradeid) && stringutil.isempty(tradeno)

  && stringutil.isempty(refundno) && stringutil.isempty(refundid)) {

  webutil.response(response, webutil.packjsonp(callback, json

  .tojsonstring( new jsonresult(- 1 , "退单号或订单号不能为空" , new responsedata()), serializerfeatureutil.features)));

  }

 

  map<string, string> restmap = null ;

  try {

  map<string, string> parm = new hashmap<string, string>();

  parm.put( "appid" , app_id);

  parm.put( "mch_id" , mch_id);

  parm.put( "transaction_id" , tradeid);

  parm.put( "out_trade_no" , tradeno);

  parm.put( "refund_id" , refundid);

  parm.put( "out_refund_no" , refundno);

  parm.put( "nonce_str" , payutil.getnoncestr());

  parm.put( "sign" , payutil.getsign(parm, api_secret));

 

  string restxml = httputils.post(order_refund_query, xmlutil.xmlformat(parm, false ));

  restmap = xmlutil.xmlparse(restxml);

  } catch (exception e) {

  log.error(e.getmessage(), e);

  }

 

  map<string, string> refundmap = new hashmap<>();

  if (collectionutil.isnotempty(restmap) && "success" .equals(restmap.get( "result_code" )) && "success" .equals(restmap.get( "result_code" ))) {

  // 订单退款查询成功 处理业务逻辑

  log.info( "退款订单查询:订单" + restmap.get( "out_trade_no" ) + "退款成功,退款状态" + restmap.get( "refund_status_0" ));

  refundmap.put( "transaction_id" , restmap.get( "transaction_id" ));

  refundmap.put( "out_trade_no" , restmap.get( "out_trade_no" ));

  refundmap.put( "refund_id" , restmap.get( "refund_id_0" ));

  refundmap.put( "refund_no" , restmap.get( "out_refund_no_0" ));

  refundmap.put( "refund_status" , restmap.get( "refund_status_0" ));

  webutil.response(response, webutil.packjsonp(callback, json

  .tojsonstring( new jsonresult( 1 , "订单退款成功" , new responsedata( null , refundmap)), serializerfeatureutil.features)));

  } else {

  if (collectionutil.isnotempty(restmap)) {

  log.info( "订单退款失败:" + restmap.get( "err_code" ) + ":" + restmap.get( "err_code_des" ));

  }

  webutil.response(response, webutil.packjsonp(callback, json

  .tojsonstring( new jsonresult(- 1 , "订单退款失败" , new responsedata()), serializerfeatureutil.features)));

  }

  }

 

}

微信支付接口参数含义具体参考微信app支付文档。

微信支付工具类

包含签名、订单号、退单号、随机串、服务器ip地址、客户端ip地址等方法。

 

?

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

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

package org.andy.wxpay.utils;

 

import java.io.unsupportedencodingexception;

import java.net.urlencoder;

import java.util.arrays;

import java.util.date;

import java.util.map;

import java.util.set;

 

import javax.servlet.http.httpservletrequest;

 

/**

  * 创建时间:2016年11月2日 下午7:12:44

  *

  * @author andy

  * @version 2.2

  */

 

public class payutil {

 

  /**

  * 生成订单号

  *

  * @return

  */

  public static string gettradeno() {

  // 自增8位数 00000001

  return "tno" + datetimeutil.formatdate( new date(), datetimeutil.time_stamp_pattern) + "00000001" ;

  }

 

  /**

  * 退款单号

  *

  * @return

  */

  public static string getrefundno() {

  // 自增8位数 00000001

  return "rno" + datetimeutil.formatdate( new date(), datetimeutil.time_stamp_pattern) + "00000001" ;

  }

 

  /**

  * 退款单号

  *

  * @return

  */

  public static string gettransferno() {

  // 自增8位数 00000001

  return "tno" + datetimeutil.formatdate( new date(), datetimeutil.time_stamp_pattern) + "00000001" ;

  }

 

  /**

  * 返回客户端ip

  *

  * @param request

  * @return

  */

  public static string getremoteaddrip(httpservletrequest request) {

  string ip = request.getheader( "x-forwarded-for" );

  if (stringutil.isnotempty(ip) && ! "unknown" .equalsignorecase(ip)) {

  // 多次反向代理后会有多个ip值,第一个ip才是真实ip

  int index = ip.indexof( "," );

  if (index != - 1 ) {

  return ip.substring( 0 , index);

  } else {

  return ip;

  }

  }

  ip = request.getheader( "x-real-ip" );

  if (stringutil.isnotempty(ip) && ! "unknown" .equalsignorecase(ip)) {

  return ip;

  }

  return request.getremoteaddr();

  }

 

  /**

  * 获取服务器的ip地址

  *

  * @param request

  * @return

  */

  public static string getlocalip(httpservletrequest request) {

  return request.getlocaladdr();

  }

 

  public static string getsign(map<string, string> params, string paternerkey) throws unsupportedencodingexception {

  return md5utils.getmd5(createsign(params, false ) + "&key=" + paternerkey).touppercase();

  }

 

  /**

  * 构造签名

  *

  * @param params

  * @param encode

  * @return

  * @throws unsupportedencodingexception

  */

  public static string createsign(map<string, string> params, boolean encode) throws unsupportedencodingexception {

  set<string> keysset = params.keyset();

  object[] keys = keysset.toarray();

  arrays.sort(keys);

  stringbuffer temp = new stringbuffer();

  boolean first = true ;

  for (object key : keys) {

  if (key == null || stringutil.isempty(params.get(key))) // 参数为空不参与签名

  continue ;

  if (first) {

  first = false ;

  } else {

  temp.append( "&" );

  }

  temp.append(key).append( "=" );

  object value = params.get(key);

  string valuestr = "" ;

  if ( null != value) {

  valuestr = value.tostring();

  }

  if (encode) {

  temp.append(urlencoder.encode(valuestr, "utf-8" ));

  } else {

  temp.append(valuestr);

  }

  }

  return temp.tostring();

  }

 

  /**

  * 创建支付随机字符串

  * @return

  */

  public static string getnoncestr(){

  return randomutil.randomstring(randomutil.letter_number_char, 32 );

  }

 

  /**

  * 支付时间戳

  * @return

  */

  public static string paytimestamp() {

  return long .tostring(system.currenttimemillis() / 1000 );

  }

}

其他所需工具类参考项目源码

支付结果

app支付测试完成

源代码地址: 微信app支付

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

原文链接:https://blog.csdn.net/fengshizty/article/details/53199356

查看更多关于java实现微信App支付服务端的详细内容...

  阅读:9次