首先我们要明确目标,我们点击微信支付官网,我们主要聚焦于这三种支付方式,其中jspai与app主要与uniapp开发微信小程序与app对接,而native主要与网页端扫码支付对接
建议导入这个jar,里面一些提供map和xml互转以及生成签名的函数,使用非常方便。
<dependency> <groupid>com.github.wxpay</groupid> <artifactid>wxpay-sdk</artifactid> <version>0.0.3</version></dependency>
ps:下面代码涉及到的wxpayutil下面的函数都是这个工具包里来的,所以不要再问这个函数代码在哪
商户号与支付结果通知回调地址
回调url配置途径:
微信商户平台(pay.weixin.qq.com)–>产品中心–>开发配置–>支付配置
既然涉及到维信小程序和app,那么它们的app_id是必须的
统一下单、查单、退款的api地址,去官方文档找即可,我这里直接写好了
/** * 统一下单接口 */public static final string unified_order_url = "/d/file/titlepic/unifiedorder; /** * 查询订单接口 */public static final string order_query_url = "/d/file/titlepic/orderquery; /** * 订单退款接口 */public static final string order_refund_url = "https://api.mch.weixin.qq.com/capi/pay/refund";
api密钥
用于签名算法
获取途径
微信商户平台(pay.weixin.qq.com)–>账户中心–>账户设置–>api安全–>设置api密钥
这种支付的使用场景,我们这里是对接uniapp开发的微信小程序
我们仔细阅读文档并提取信息,其实访问接口的很多参数都不是必填的,我们尽量去关注必填参数
归纳如下:
appid微信小程序app_idmch_id商户号nonce_str随机字符串,可以调用wxpayutil下generatenoncestr方法生成sign
可以调用wxpayutil下generatesignature方法生成,这个方法需写给儿子的话要用到准备工作中的商户api密钥(app_key)来加密。
appid微信小程序app_idmch_id商户号nonce_str随机字符串,可以调用wxpayutil下generatenoncestr方法生成sign可以调用wxpayutil下genera尘封tesignature方法生成,这个方法需要用到准备工作中的商户api密钥(app_key)来加密。
body商品描述,建议软件名字+产品操作,例如天天爱消除-游戏充值out_trade_no我们自己生成的订单号,保证同一商号下唯一即可total_fee金额,注意单位是分spbill_create_ip用户客户端ipnotify_url支付结果通知回调地址trade_type交易类型,我们这里填jsapiopenid因为是jsapi方式,所以必传,openid其实很好获取,参考地址如何获取openid但是下完单之后,怎么样才能让前端调用微信支付呢?这里需要查看uniapp官方文档微信小程序支付
我们发现其实主要是拿到预支付id prepay_id以及签名。
代码实现:
public map<string, string> unifiedorderbyjsp(order order, string clientip) throws exception { long begin = system.currenttimemillis(); //使用map封装微信支付需要的固定参数 map<string, string> m = new hashmap<>(); //设置支付参数 m.put("appid", orderutils.wx_app_id); m.put("mch_id", orderutils.mch_id); m.put("nonce_str", wxpayutil.generatenoncestr()); //商品描述 例如:天天爱消除-游戏充值 m.put("body", "换芯易-" + order.getgoodsmodel() + "购买"); //订单号 m.put("out_trade_no", order.getordersn()); m.put("total_fee", order.getactualprice().multiply(new bigdecimal("100")).longvalue() + ""); m.put("spbill_create_ip", clientip); m.put("notify_url", orderutils.notify_url); m.put("trade_type", "jsapi"); //trade_type=jsapi时(即jsapi支付),必须要openid urthreedao urthreedao = new urthreedaoimpl(); urthree urthree = urthreedao.queryopenid(order.geturid()); if (urthree == null) { return null; } m.put("openid", urthree.getthreeopenid()); //生成签名 m.put("sign", wxpayutil.generatesignature(m, orderutils.app_key)); //发送请求,请求路径:unified_order_url = "/d/file/titlepic/unifiedorder" httpclient client = new httpclient(orderutils.unified_order_url); //设置xml格式的参数,要把map转为xml client.txmlparam(wxpayutil.maptoxml(m)); //设置支持https client.thttps(true); //执行请求发送 client.post(); //xml转为map接受结果 map<string, string> respon = wxpayutil.xmltomap(client.getcontent()); long end = system.currenttimemillis(); system.out.println("请求/d/file/titlepic/" + (end - begin) + "ms"); system.out.println("请求结果:" + json.tojsonstring(respon)); if ("success".equals(respon.get("return_code")) && "success".equals(respon.get("result_code"))) { map<string, string> param = new hashmap<>(); //返回结果格式参照/d/file/titlepic/payment //随机字符串 //时间戳,但是单位为s,不是毫秒 param.put("timestamp", string.valueof(system.currenttimemillis() / 1000)); param.put("noncestr", wxpayutil.generatenoncestr()); param.put("package", "prepay_id=" + respon.get("prepay_id")); param.put("signtype", "md5"); param.put("paysign", wxpayutil.generatesignature(param, orderutils.app_key)); return param; } //为空代表下单失败 return null; }
主要应用场景是pc端扫码支付。
必填参数归纳如下:
appid微信小程序app_idmch_id商户号nonce_str随机字符串,可以调用wxpayutil下generatenoncestr方法生成sign可以调用wxpayutil下generatesignature方法生成,这个方法需要用到准备工作中的商户api密钥(app_key)来加密。
body商品描述,建议软件名字+产品操作,例如天天爱消除-游戏充值out_trade_no我们自己生成的订单号,保证同一商号下唯一即可total_fee金额,注意单位是分spbill_create_ip用户客户端ipnotify_url支付结果通知回调地址trade_type交易类型,我们这里填nativeproduct_idtrade_type=native时,此参数必传。此参数为二维码中包含的商品id,我们可以自行定义。代码如下:
public map<string, string> unifiedorderbynative(order order, string clientip) throws exception { long begin = system.currenttimemillis(); //使用map封装微信支付需要的固定参数 map<string, string> m = new hashmap<>(); //1、设置支付参数 m.put("appid", orderutils.wx_app_id); m.put("mch_id", orderutils.mch_id); m.put("nonce_str", wxpayutil.generatenoncestr()); //商品描述 例如:天天爱消除-游戏充值 m.put("body", "换芯易-" + order.getgoodsmodel() + "购买"); //订单号 m.put("out_trade_no", order.getordersn()); m.put("total_fee", order.getactualprice().multiply(new bigdecimal("100")).longvalue() + ""); m.put("spbill_create_ip", clientip); m.put("notify_url", orderutils.notify_url); m.put("trade_type", "native"); //trade_type=native时,此参数必传 m.put("product_id", order.getid()); //生成签名 m.put("sign", wxpayutil.generatesignature(m, orderutils.app_key)); //发送请求,请求路径:unified_order_url = "/d/file/titlepic/unifiedorder" httpclient client = new httpclient(orderutils.unified_order_url); //设置xml格式的参数,要把map转为xml client.txmlparam(wxpayutil.maptoxml(m)); //设置支持https client.thttps(true); //执行请求发送 client.post(); //xml转为map接受结果 map<string, string> respon = wxpayutil.xmltomap(client.getcontent()); long end = system.currenttimemillis(); system.out.println("请求/d/file/titlepic/" + (end - begin) + "ms"); system.out.println("请求结果:" + json.tojsonstring(respon)); if ("success".equals(respon.get("return_code")) && "success".equals(respon.get("result_code"))) { map<string, string> param = new hashmap<>(); //二维码地址 param.put("code_url", respon.get("code_url")); param.put("order_sn", order.getordersn()); param.put("order_id", order.getid()); param.put("total_fee", order.getactualprice().multiply(new bigdecimal("100")).longvalue() + ""); return param; } //为空代表下单失败 return null; }
上面代码中最重要的返回结果就是code_url。即生成的支付二维码地址,然后用微信扫码并付款即可。
应用场景是app端调用微信支付,对接uniapp开发的app
归纳如下:
appidapp的app_idmch_id商户号nonce_str随机字符串,可以调用wxpayutil下generatenoncestr方法生成sign可以调用wxpayutil下generatesignature方法生成,这个方法需要用到准备工作中的商户api密钥(app_key)来加密。
body商品描述,建议软件名字+产品操作,例如天天爱消除-游戏充值out_trade_no我们自己生成的订单号,保证同一商号下唯一即可total_fee金额,注意单位是分spbill_create_ip用户客户端ipnotify_url支付结果通知回调地址trade_type交易类型,我们这里填app但是下完单之后,怎么样才能让前端调用微信支付呢?这里需要查看uniapp官方文档微信小程序支付:
我们可以看到常规的比如appid、商户号需要,充要的是预支付id prepayid和签名。
代码实现如下:
public map<string, string> unifiedorderbyapp(order order, string clientip) throws exception { long begin = system.currenttimemillis(); //使用map封装微信支付需要的固定参数 map<string, string> m = new hashmap<>(); //1、设置支付参数 m.put("appid", orderutils.app_app_id); m.put("mch_id", orderutils.mch_id); m.put("nonce_str", wxpayutil.generatenoncestr()); //商品描述 例如:天天爱消除-游戏充值 m.put("body", "换芯易-" + order.getgoodsmodel() + "购买"); //订单号 m.put("out_trade_no", order.getordersn()); m.put("total_fee", order.getactualprice().multiply(new bigdecimal("100")).longvalue() + ""); m.put("spbill_create_ip", clientip); m.put("notify_url", orderutils.notify_url); m.put("trade_type", "app"); //生成签名 m.put("sign", wxpayutil.generatesignature(m, orderutils.app_key)); //发送请求,请求路径:unified_order_url = "/d/file/titlepic/unifiedorder" httpclient client = new httpclient(orderutils.unified_order_url); //设置xml格式的参数,要把map转为xml client.txmlparam(wxpayutil.maptoxml(m)); //设置支持https client.thttps(true); //执行请求发送 client.post(); //xml转为map接受结果 map<string, string> respon = wxpayutil.xmltomap(client.getcontent()); long end = system.currenttimemillis(); system.out.println("请求/d/file/titlepic/" + (end - begin) + "ms"); system.out.println("请求结果:" + json.tojsonstring(respon)); if ("success".equals(respon.get("return_code")) && "success".equals(respon.get("result_code"))) { map<string, string> param = new hashmap<>(); param.put("appid", orderutils.app_app_id); //随机字符串 param.put("noncestr", wxpayutil.generatenoncestr()); //固定值 param.put("package", "sign=wxpay"); param.put("partnerid", orderutils.mch_id); param.put("prepayid", respon.get("prepay_id")); //时间戳,但是单位为s,不是毫秒 param.put("timestamp", string.valueof(system.currenttimemillis() / 1000)); //param.put("package", respon.get("sign")); param.put("sign", wxpayutil.generatesignature(param, orderutils.app_key)); return param; } //为空代表下单失败 return null; }
按照uniapp官方要求的格式封装返回结果给前端。
对于上面三种支付方式,他们的返回结果我都没有细细分析,因为官方文档写得很详细,我们非常需要关注的也就预支付id这个返回结果。所以贴一下官方文档,有兴趣可以仔细看看(重要的我会标注)
共有
其中app和jsapi得着重关注预支付id
native扫码支付主要关注code_url
我们实际业务中,过于依赖微信支付的回调结果来判断订单状态显然是不太合适的,所以自己去查单这种操作十分常见。查单需要的必填参数归纳如下:
appidapp_idmch_id商户号nonce_str随机字符串,可以调用wxpayutil下generatenoncestr方法生成sign可以调用wxpayutil下generatesignature方法生成,这个方法需要用到准备工作中的商户api密钥(app_key)来加密。
out_trade_no我们自己生成的订单号,保证同一商号下唯一即可,这个订单号需要和我们当时调用统一下单传过去的单号一致ps:这里的out_trade_no可以用transaction_id代替,但是一般我们查单的时候可能还没有transaction_id,所以只能用我们自己程序业务订单号out_trade_no去查,因为transaction_id需要调查单接口才能得到,也就是说如果我们以后”二次查单”可以用这个参数。
代码实现入下:
public map<string, string> que反问句的特点ryorderstatus(string ordersn) throws exception { long begintime = system.currenttimemillis(); //1、封装参数 map<string, string> m = new hashmap<>(); m.put("appid", orderutils.app_app_id); m.put("mch_id", orderutils.mch_id); m.put("out_trade_no", ordersn); m.put("nonce_str", wxpayutil.generatenoncestr()); //生成签名 m.put("sign", wxpayutil.generatesignature(m, orderutils.app_key)); //发送请求,请求路径:unified_order_url = "/d/file/titlepic/orderquery" httpclient client = new httpclient(orderutils.order_query_url); client.txmlparam(wxpayutil.maptoxml(m)); client.thttps(true); client.post(); //3、返回第三方的数据 map<string, string> result = wxpayutil.xmltomap(client.getcontent()); long endtime = system.currenttimemillis(); system.out.println("请求/d/file/titlepic/" + (endtime - begintime) + "ms"); system.out.println("请求结果:" + json.tojsonstring(result)); return result; }
返回结果参数部分如下,重要的标注起来:
一般情况下主要是利用trade_state来判断用户是否支付成功从而更新订单或记录交易成功物流等业务操作。
map<string, string> map = orderdao.queryorderstatus(ordersn);if (map == null) { return apirespon.createapirespon(apirespon.http_state_400_error_10001, "微信支付出错");} //微信if ("success".equals(map.get("trade_state"))) { //更新订单或记录交易成功物流等业务操作}
言简意赅,就是方便(rnm,退名著读书笔记钱)
重要参数
appidapp_idmch_id商户号nonce_str随机字符串,可以调用wxpayutil下generatenoncestr方法生成sign可以调用wxpayutil下generatesignature方法生成,这个方法需要用到准备工作中的商户api密钥(app_key)来加密。
out_trade_no我们自己生成的订单号,保证同一商号下唯一即可,这个订单号需要和我们当时调用统一下单传过去的单号一致transaction_id和上面的out_trade_no二选一,官方推荐transaction_idout_refund_no其实和统一下单的时候的订单号原理差不多,我们自己随机生成一个,保证同一商户系统下唯一即可total_fee要退款的订单的金额refund_fee实际要退款的金额代码实现如下:
public map<string, string> refundorder(refundtrace refundtrace, order order) throws exception { long begin = system.currenttimemillis(); //使用map封装微信支付需要的固定参数 map<string, string> m = new hashmap<>(); //1、设置支付参数 m.put("appid", orderutils.wx_app_id); m.put("mch_id", orderutils.mch_id); m.put("nonce_str", wxpayutil.generatenoncestr()); //微信支付订单号 m.put("transaction_id", order.getpayid()); //商户退款单号 m.put("out_refund_no", refundtrace.getrefundsn()); //订单金额 m.put("total_fee", order.getactualprice().multiply(new bigdecimal("100")).longvalue() + ""); //退款金额 即实际退款金额 m.put("refund_fee", refundtrace.getrefundamount().multiply(new bigdecimal("100")).longvalue() + ""); //退款原因 m.put("refund_desc", refundtrace.getrefundreason()); m.put("模仿圣诞老人写给孩子的信notify_url", orderutils.notify_url); //生成签名 m.put("sign", wxpayutil.generatesignature(m, orderutils.app_key)); 发送请求,请求路径:order_refund_url = "https://api.mch.weixin.qq.com/capi/pay/refund" string content = httprequestutils.refundrequest(wxpayutil.maptoxml(m)); //xml转为map接受结果 map<string, string> respon = wxpayutil.xmltomap(content); long end = system.currenttimemillis(); system.out.println("请求https://api.mch.weixin.qq.com/capi/pay/refund耗时:" + (end - begin) + "ms"); system.out.println("请求结果:" + json.tojsonstring(respon)); if ("success".equals(respon.get("return_code")) && "success".equals(respon.get("result_code"))) { map<string, string> param = new hashmap<>(); param.put("refund_id", respon.get("refund_id")); param.put("refund_fee", respon.get("refund_fee")); return param; } //为空代表退款失败 return null; }
这里注意一下!!!!因为操作涉及到把商户方的钱转回到买方的操作,所以安全系数比较高,调用退款api官方要求证书
证书申请途径如下:
微信商户平台(pay.weixin.qq.com)–>账户中心–>账户设置–>api安全–>申请api证书
申请到证书,需要放到项目下:
那么我们这里的http请求就不能用之前的了,需要配置该证书
上面代码中使用的refundrequest函数细节如下:
public static string refundrequest(string order) throws exception { try { keystore clientstore = keystore.getinstance("pkcs12"); // 读取项目存放的pkcs12证书文件 fileinputstream instream = new fileinputstream("apiclient_cert.p12"); try { // 指定pkcs12的密码(商户id) clientstore.load(instream, orderutils.mch_id.tochararray()); } finally { instream.clo(); } sslcontext sslcontext = sslcontexts.custom().loadkeymaterial(clientstore, orderutils.mch_id.tochararray()).build(); // 指定tls版本 sslconnectionsocketfactory sslsf = new sslconnectionsocketfactory(sslcontext, new string[]{"tlsv1"}, null, sslconnectionsocketfactory.getdefaulthostnameverifier()); // 设置httpclient的sslsocketfactory cloablehttpclient httpclient = httpclients.custom().tsslsocketfactory(sslsf).build(); try { httppost httpost = new httppost(orderutils.order_refund_url); // 设置响应头信息// httpost.addheader("connection", "keep-alive");// httpost.addheader("accept", "*/*");// httpost.addheader("content-type", content_type_form.tostring());// httpost.addheader("x-requested-with", "xmlhttprequest");// httpost.addheader("cache-control", "max-age=0");// httpost.addheader("ur-agent", default_ur_agent); httpost.tentity(new stringentity(order, "utf-8")); cloablehttprespon respon = httpclient.execute(httpost); try { httpentity entity = respon.getentity(); string jsonstr = entityutils.tostring(respon.getentity(), "utf-8"); entityutils.consume(entity); return jsonstr; } finally { respon.clo(); } } finally { httpclient.clo(); } } catch (exception e) { throw new runtimeexception(e); } }
我所使用的httpclient工具类源码,我这里使用主要用于发送带xml参数的post请求
public class httpclient { private string url; private map<string, string> param; private int statuscode; private string content; private string xmlparam; private boolean ishttps; public boolean ishttps() { return ishttps; } public void thttps(boolean ishttps) { this.ishttps = ishttps; } public string getxmlparam() { return xmlparam; } public void txmlparam(string xmlparam) { this.xmlparam = xmlparam; } public httpclient(string url, map<string, string> param) { this.url = url; this.param = param; } public httpclient(string url) { this.url = url; } public void tparameter(map<string, string> map) { param = map; } public void addparameter(string key, string value) { if (param == null) { param = new hashmap<string, string>(); } param.put(key, value); } public void post() throws clientprotocolexception, ioexception { httppost http = new httppost(url); tentity(http); execute(http); } public void put() throws clientprotocolexception, ioexception { httpput http = new httpput(url); tentity(http); execute(http); } public void get() throws clientprotocolexception, ioexception { if (param != null) { stringbuilder url = new stringbuilder(this.url); boolean isfirst = true; for (string key : param.keyt()) { if (isfirst) { url.append("?"); isfirst = fal; } el { url.append("&"); } url.append(key).append("=").append(param.get(key)); } system.out.println("请求的url:" + url.tostring()); this.url = url.tostring(); } httpget http = new httpget(url); execute(http); } /** * t http post,put param */ private void tentity(httpentityenclosingrequestba http) { if (param != null) { list<namevaluepair> nvps = new linkedlist<namevaluepair>(); for (string key : param.keyt()) nvps.add(new basicnamevaluepair(key, param.get(key))); // 参数 http.tentity(new urlencodedformentity(nvps, consts.utf_8)); // 设置参数 } if (xmlparam != null) { http.tentity(new stringentity(xmlparam, consts.utf_8)); } } private void execute(httpurirequest http) throws clientprotocolexception, ioexception { cloablehttpclient httpclient = null; try { if (ishttps) { sslcontext sslcontext = new sslcontextbuilder() .loadtrustmaterial(null, new truststrategy() { // 信任所有 @override public boolean istrusted(x509certificate[] chain, string authtype) throws certificateexception { return true; } }).build(); sslconnectionsocketfactory sslsf = new sslconnectionsocketfactory( sslcontext); httpclient = httpclients.custom().tsslsocketfactory(sslsf) .build(); } el { httpclient = httpclients.createdefault(); } cloablehttprespon respon = httpclient.execute(http); try { if (respon != null) { if (respon.getstatusline() != null) statuscode = respon.getstatusline().getstatuscode(); httpentity entity = respon.getentity(); // 响应内容 content = entityutils.tostring(entity, consts.utf_8); } } finally { respon.clo(); } } catch (exception e) { e.printstacktrace(); } finally { httpclient.clo(); } } public int getstatuscode() { return statuscode; } public string getcontent() throws parexception, ioexception { return content; } }
到此这篇关于java后端对接微信支付(小程序、app、pc端扫码)包含查单退款的文章就介绍到这了,更多相关java 对接微信支付内容请搜索www.887551.com以前的文章或继续浏览下面的相关文章希望大家以后多多支持www.887551.com!
本文发布于:2023-04-04 03:25:56,感谢您对本站的认可!
本文链接:https://www.wtabcd.cn/fanwen/zuowen/50cbde1671e63910ec891cfad2d60e59.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文word下载地址:Java后端对接微信支付(小程序、APP、PC端扫码)包含查单退款.doc
本文 PDF 下载地址:Java后端对接微信支付(小程序、APP、PC端扫码)包含查单退款.pdf
留言与评论(共有 0 条评论) |