java实现微信App支付服务端
程序员文章站
2024-02-26 11:18:46
微信app支付服务端的实现方法,供大家参考,具体内容如下
引言
主要实现app支付统一下单、异步通知、调起支付接口、支付订单查询、申请退款、查询退款功能;封装了http...
微信app支付服务端的实现方法,供大家参考,具体内容如下
引言
主要实现app支付统一下单、异步通知、调起支付接口、支付订单查询、申请退款、查询退款功能;封装了https对发起退款的证书校验、签名、xml解析等。
支付流程
具体支付流程参考“微信app”文档,
app支付:app端点击下单—-服务端生成订单,并调起“统一下单”,返回app支付所需参数—–app端“调起支付接口“,发起支付—-微信服务器端调用服务端回调地址—–服务端按照“支付结果通知”,处理支付结果
app查询:调起“查询订单”
app退款:发起退款请求,调用“申请退款”,发起退款,需双向证书验证
app退款查询:调起“查询退款”
支付代码实现
代码实现签名、证书校验、http和https封装等,项目结构如下:
支付代码
包含支付、支付查询、异步通知、退款申请、退款查询
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地址等方法。
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支付
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
上一篇: Java热门笔试试题整理
下一篇: java实现微信企业付款到个人