微信 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支付服务端的详细内容...