微信支付java
直接上代码:
1、支付配置PayCommonUtil
import com.legendshop.payment.tenpay.util.MD5Util;
import com.legendshop.util.AppUtils; import javax.servlet.http.HttpServletRequest;
import java.util.*; public class PayCommonUtil { /**
* @Date: 2018/6/6 11:08
* @Descript: 因为公司app端与网页端商户号不一样,所以这里需要做一些判断
*/ /* 微信公众号 */
public static final String appid_wap = "your appid";
public static final String MCHID_wap = "商户号"; /**
* 微信原生
* appid 查看方法:登录微信商户平台(https://pay.weixin.qq.com/index.php/core/home/login) --> 营销中心 --> 支付后配置
*/
public static final String appid = "your appid";
public static final String MCHID = "商户号";
public static final String notify_url = "你的通知接口地址";
/**
* 两个商户号的key又是一样的,这里就共用一个变量。
* key 查看方法:登录微信商户平台 --> 账户中心 --> API安全 --> API密钥
*/
public static final String key = "your key"; /**
* 创建微信交易对象
*/
public static SortedMap<Object, Object> getWXPrePayID(String tradeType, String openid) {
SortedMap<Object, Object> parameters = new TreeMap<Object, Object>(); Boolean isWap = false;
Boolean isWx = false;
if (AppUtils.isNotBlank(tradeType)) {
if (tradeType.equals("JSAPI")) {
//微信浏览器内执行
isWx = true;
isWap = true;
}
if (tradeType.equals("MWEB")) {
//H5
isWap = true;
}
} parameters.put("appid", isWap ? appid_wap : appid);
parameters.put("mch_id", isWap ? MCHID_wap : MCHID);
parameters.put("nonce_str", createNoncestr());
parameters.put("fee_type", "CNY");
parameters.put("notify_url", notify_url);
parameters.put("trade_type", AppUtils.isBlank(tradeType) ? "APP" : tradeType);
if (isWx) {
//微信浏览器内执行需要openid
parameters.put("openid", openid);
}
return parameters;
} /**
* 再次签名(仅APP支付需要)
*/
public static SortedMap<Object, Object> startWXPay(Map map) {
try { SortedMap<Object, Object> parameterMap = new TreeMap<Object, Object>();
parameterMap.put("appid", appid);
parameterMap.put("partnerid", MCHID);
parameterMap.put("prepayid", map.get("prepay_id"));
parameterMap.put("package", "Sign=WXPay");
parameterMap.put("noncestr", createNoncestr());
// 10位
parameterMap.put("timestamp",
Long.parseLong(String.valueOf(System.currentTimeMillis()).toString().substring(0, 10)));
String sign = PayCommonUtil.createSign("UTF-8", parameterMap);
parameterMap.put("sign", sign);
return parameterMap;
} catch (Exception e) {
e.printStackTrace();
}
return null;
} /*
* 获取真实IP
* */
public static String getRemortIP(HttpServletRequest request) {
if (request.getHeader("x-forwarded-for") == null) {
return request.getRemoteAddr();
}
return request.getHeader("x-forwarded-for");
} /**
* 创建随机数
*
* @param length
* @return
*/
public static String createNoncestr() {
String chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
String res = "";
for (int i = 0; i < 16; i++) {
Random rd = new Random();
res += chars.charAt(rd.nextInt(chars.length() - 1));
}
return res;
} /**
* @param characterEncoding 编码格式
* @param parameters 请求参数
* @return
* @Description:创建sign签名
*/
public static String createSign(String characterEncoding, SortedMap<Object, Object> parameters) {
StringBuffer sb = new StringBuffer();
Set es = parameters.entrySet();
Iterator it = es.iterator();
while (it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
String k = (String) entry.getKey();
Object v = entry.getValue();
if (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) {
sb.append(k + "=" + v + "&");
}
}
sb.append("key=" + key);
String sign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase();
return sign;
} }
工具类MD5Util
public class MD5Util { private static String byteArrayToHexString(byte b[]) {
StringBuffer resultSb = new StringBuffer();
for (int i = 0; i < b.length; i++)
resultSb.append(byteToHexString(b[i])); return resultSb.toString();
} private static String byteToHexString(byte b) {
int n = b;
if (n < 0)
n += 256;
int d1 = n / 16;
int d2 = n % 16;
return hexDigits[d1] + hexDigits[d2];
} public static String MD5Encode(String origin, String charsetname) {
String resultString = null;
try {
resultString = new String(origin);
MessageDigest md = MessageDigest.getInstance("MD5");
if (charsetname == null || "".equals(charsetname))
resultString = byteArrayToHexString(md.digest(resultString
.getBytes()));
else
resultString = byteArrayToHexString(md.digest(resultString
.getBytes(charsetname)));
} catch (Exception exception) {
}
return resultString;
} private static final String hexDigits[] = { "0", "1", "2", "3", "4", "5",
"6", "7", "8", "9", "a", "b", "c", "d", "e", "f" }; }
2、接下来是调用,这里只贴核心部分代码
/**
* body:订单标题
* out_trade_no:结算单流水号,一般用商城订单号就可以了,但是这样会有个问题就是在不同平台(微信浏览器内、微信外的浏览器、APP等)分别提交订单或者订单价格的改变等因素都会出现“201 订单重复“的问题,可根据实际业务需求做相应的处理
* fee:最小是1,表示1分钱
* tradeType:JSAPI(微信浏览器支付)、MWEB(微信以外的浏览器)、APP(原生支付)
* openid:当tradeType = JSAPI,该属性是必须的
* redirectUrl:当tradeType = MWEB,表示支付成功后的跳转地址,一般是跳转到支付成功的页面
*/ private Map getWXPay(String body,String out_trade_no,String fee,String tradeType,String openid,String redirectUrl) throws Exception{ SortedMap<Object, Object> parameters = PayCommonUtil.getWXPrePayID(tradeType,openid); // 获取预付单,此处已做封装,需要工具类 // String body = "雅量商品支付";
//
// String out_trade_no = PayCommonUtil.getDateStr();
//
// String fee = "1"; parameters.put("body", AppUtils.isBlank(body) ? "雅量商品支付" : body); //获取ip要注意反向代理的ip获取配置
parameters.put("spbill_create_ip", PayCommonUtil.getRemortIP(request));
parameters.put("out_trade_no", out_trade_no);
parameters.put("total_fee", fee); /**
* 签名(签名规则:https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=4_3,)
* 注意:使用SortedMap已符合”参数名ASCII码从小到大排序(字典序)“
*/
String sign = PayCommonUtil.createSign("UTF-8", parameters);
parameters.put("sign", sign); // 封装请求参数结束
String requestXML = PayCommonUtil.getRequestXml(parameters); // 获取xml结果 /**
* 调用统一下单接口(APP、微信内支付、H5都是使用该接口)
*
* 区别:
* APP支付:统一下单 --> 二次签名 --> 返回数据给客户端调起支付
* 微信内支付:统一下单 --> 返回数据给客户端调起支付
* H5支付:统一下单 --> 返回链接给客户端,客户端进行跳转
*/
String result = PayCommonUtil.httpsRequest("https://api.mch.weixin.qq.com/pay/unifiedorder", "POST",
requestXML); Map<String, String> map = XMLUtil.doXMLParse(result); Map allData = new HashedMap();
Map runRs = new HashedMap();
String rsCode = map.get("result_code").toString();
runRs.put("result_code", rsCode);
if(rsCode.equals("FAIL")){
runRs.put("err_code_des",map.get("err_code_des"));
}
allData.put("runRs",runRs); if(AppUtils.isNotBlank(tradeType) && (tradeType.equals("JSAPI") || tradeType.equals("MWEB"))){ SortedMap<Object, Object> signData = new TreeMap<Object, Object>(); signData.put("appId",map.get("appid")); signData.put("timeStamp",
Long.parseLong(String.valueOf(System.currentTimeMillis()).toString().substring(0, 10))); signData.put("nonceStr",map.get("nonce_str")); signData.put("package","prepay_id=" + map.get("prepay_id")); signData.put("signType","MD5"); String paySign = PayCommonUtil.createSign("UTF-8", signData); signData.put("paySign",paySign); if(tradeType.equals("MWEB")){ //跳转链接
signData.put("mwebUrl",map.get("mweb_url") + "&redirect_url=" + redirectUrl);
} allData.put("data",signData); }else{
SortedMap<Object, Object> parMap = PayCommonUtil.startWXPay(map); allData.put("data",parMap);
} return allData; }
工具类XMLUtil
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map; import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder; /**
* xml工具类
* @author miklchen
*
*/
public class XMLUtil { /**
* 解析xml,返回第一级元素键值对。如果第一级元素有子节点,则此节点的值是子节点的xml数据。
* @param strxml
* @return
* @throws JDOMException
* @throws IOException
*/
public static Map doXMLParse(String strxml) throws JDOMException, IOException {
if(null == strxml || "".equals(strxml)) {
return null;
} Map m = new HashMap();
InputStream in = HttpClientUtil.String2Inputstream(strxml);
SAXBuilder builder = new SAXBuilder();
Document doc = builder.build(in);
Element root = doc.getRootElement();
List list = root.getChildren();
Iterator it = list.iterator();
while(it.hasNext()) {
Element e = (Element) it.next();
String k = e.getName();
String v = "";
List children = e.getChildren();
if(children.isEmpty()) {
v = e.getTextNormalize();
} else {
v = XMLUtil.getChildrenText(children);
} m.put(k, v);
} //关闭流
in.close(); return m;
} /**
* 获取子结点的xml
* @param children
* @return String
*/
public static String getChildrenText(List children) {
StringBuffer sb = new StringBuffer();
if(!children.isEmpty()) {
Iterator it = children.iterator();
while(it.hasNext()) {
Element e = (Element) it.next();
String name = e.getName();
String value = e.getTextNormalize();
List list = e.getChildren();
sb.append("<" + name + ">");
if(!list.isEmpty()) {
sb.append(XMLUtil.getChildrenText(list));
}
sb.append(value);
sb.append("</" + name + ">");
}
} return sb.toString();
} /**
* 获取xml编码字符集
* @param strxml
* @return
* @throws IOException
* @throws JDOMException
*/
public static String getXMLEncoding(String strxml) throws JDOMException, IOException {
InputStream in = HttpClientUtil.String2Inputstream(strxml);
SAXBuilder builder = new SAXBuilder();
Document doc = builder.build(in);
in.close();
return (String)doc.getProperty("encoding");
} }
千万要注意参数的大小写问题,一个接口一个样
3、接收通知
支付成功后不是由客户端通知服务器的,这样无法保证正确性,而是应该由微信服务器来通知我们的项目服务器,并且要做签名验证保证正确性、一致性。
通知接口关键代码
@RequestMapping(value = "/notify", method = RequestMethod.POST)
public void notify(HttpServletRequest request,HttpServletResponse response) throws Exception {
String resXml = "";
/** 支付成功后,微信回调返回的信息 */
Map<String, String> map=null;
try {
map = WeiXinUtil.parseXml(request);
} catch (XmlPullParserException e1) {
e1.printStackTrace();
} catch (IOException e1) {
e1.printStackTrace();
} try {
// SortedMap<Object,Object> parameters = new TreeMap<Object,Object>();
// parameters.put("appid", map.get("appid"));
// parameters.put("attach",map.get("attach"));
// parameters.put("bank_type",map.get("bank_type"));
// parameters.put("cash_fee", map.get("cash_fee"));
// parameters.put("fee_type", map.get("fee_type"));
// parameters.put("is_subscribe", map.get("is_subscribe"));
// parameters.put("mch_id", map.get("mch_id"));
// parameters.put("nonce_str", map.get("nonce_str"));
// parameters.put("openid", map.get("openid"));
// parameters.put("out_trade_no", map.get("out_trade_no"));
// parameters.put("result_code", map.get("result_code"));
// parameters.put("return_code", map.get("return_code"));
// parameters.put("time_end", map.get("time_end"));
// parameters.put("total_fee", map.get("total_fee"));
// parameters.put("trade_type", map.get("trade_type"));
// parameters.put("transaction_id", map.get("transaction_id"));
// 用于验签
SortedMap<Object, Object> parameters = new TreeMap<Object, Object>();
for (Object keyValue : map.keySet()) {
//** 输出返回的订单支付信息 *//*
if (!"sign".equals(keyValue)) {
parameters.put(keyValue, map.get(keyValue));
}
} String out_trade_no=map.get("out_trade_no");
String openid=map.get("openid");
boolean config=true; RequestHandler reqHandler = new RequestHandler(request,response);
String checkSign = reqHandler.createSign(parameters); //签名验证
if(
AppUtils.isBlank(out_trade_no) ||
AppUtils.isBlank(openid) ||
map.get("result_code").toString().equalsIgnoreCase("FAIL") ||
!checkSign.equals(map.get("sign"))
){
resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"
+ "<return_msg><![CDATA[报文有误]]></return_msg>" + "</xml> ";
config=false;
} if(config){ //根据out_trade_no查找订单,代码根据自己的业务进行修改
SubSettlement subSettlement= subSettlementService.getSubSettlementBySn(out_trade_no);
if(AppUtils.isBlank(subSettlement)){
resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"
+ "<return_msg><![CDATA[订单找不到]]></return_msg>" + "</xml> ";
config=false;
} if(config){ // ***************
//校验返回的订单金额是否与商户侧的订单金额一致
String total_fee=map.get("total_fee");
String OrderTotal=String.valueOf(new BigDecimal(Arith.sub(subSettlement.getCashAmount(), subSettlement.getPdAmount())).setScale(2,BigDecimal.ROUND_HALF_UP).multiply(new BigDecimal(100)).intValue()); if(total_fee.equals(OrderTotal)){ //微信返回的金额与数据库的金额不一致性 //业务逻辑,一般是改变订单状态、发送订单通知等等 // 支付成功,返回success,微信服务器就不会再重复发送该通知
resXml = "<xml>"+ "<return_code><![CDATA[SUCCESS]]></return_code>"+ "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> "; }else{
resXml = "<xml>"
+ "<return_code><![CDATA[FAIL]]></return_code>"
+ "<return_msg><![CDATA[回调失败]]></return_msg>"
+ "</xml> ";
} } } }catch (Exception e) {
e.printStackTrace();
resXml = "<xml>"
+ "<return_code><![CDATA[FAIL]]></return_code>"
+ "<return_msg><![CDATA[支付失败]]></return_msg>"
+ "</xml> ";
}
BufferedOutputStream out = new BufferedOutputStream(
response.getOutputStream());
out.write(resXml.getBytes());
out.flush();
out.close(); }
条码支付很简单,下载demo和证书下来配置一下就可以直接运行
微信条码支付demo:https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=11_1
微信签名要求:https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=4_3
微信支付开发文档:https://pay.weixin.qq.com/wiki/doc/api/index.html
转载请注明博客出处:http://www.cnblogs.com/cjh-notes/
微信支付java的更多相关文章
- 微信支付java版V3验证数据合法性
[TOC] 1. 微信支付java版V3验证数据合法性 概要:使用微信支付接口时,微信会返回或回调给商户XML数据,开发者需要验证微信返回的数据是否合法. 特别提醒:商户系统对于支付结果通知的内容一定 ...
- app微信支付-java服务端接口 支付-查询-退款
个人不怎么看得懂微信的文档,看了很多前辈的写法,终于调通了,在这里做一下记录. 首先来定义各种处理类(微信支付不需要特殊jar包,很多处理需要自己封装,当然也可以自己写完打个jar包) 参数要用jdo ...
- APP微信支付Java后台的实现(springmvc)
第一次做微信支付,阅读完开发文档后,下了个官方demo,摸索了好久,期间也出现了好多问题,终于是实现生成预支付订单以及支付成功后接收微信服务器通知,不多说了,直接上代码: 一.工具类 Constant ...
- 微信支付(java版本)_支付结果通知
应用场景: 支付完成后,微信会把相关支付结果和用户信息发送给商户,商户需要接收处理,并返回应答. 对后台通知交互时,如果微信收到商户的应答不是成功或超时,微信认为通知失败,微信会通过一定的策略定期重新 ...
- 微信支付java开发
微信公众平台 (此处只讲pay) 微信商户平台,公众号的后台管理工具,包含公众号的商户信息,公众号支付,扫码支付,刷卡支付 1.商户信息包含商户号,和此公众平台关联的商户号,需登录商户平台设置商户秘钥 ...
- 微信小程序-统一下单、微信支付(Java后台)
1.首先分享 微信统一下单接口: https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1 微信接口 签名 对比网址: https: ...
- 微信支付(java版本)_统一下单
最近工作接触到微信支付,刚开始解决微信支付很神秘,接触之后发现并没有那么神秘,就是有很多坑,在开发的时候需要注意,整理出来: 1.准备工作 首先需要登录微信支付公众平台阅读接口文档,地址:https: ...
- 小程序微信支付java
https://blog.csdn.net/qq_33452819/article/details/70314204#
- 转-Android微信支付
http://blog.fangjie.info/android微信支付/ Android微信支付 2014-08-09 一.使用微信官方的提供的demo里的appid等 1.微信接口上手指南:(从“ ...
随机推荐
- Qt——事件
1.常见事件 [1]鼠标事件 (1)坐标 x(),y(), 相对windows globalX() globalY() (2)获得点击 button() [2]键盘事件 [3]定时器事件 timerI ...
- 四则运算_EX
在原有四则运算基础上,除整数以外要支持真分数运算(验证正确性) 一次出的题避免相互重复 可定制出题数目 #include <stdio.h>#include <stdlib.h> ...
- MapWinGIS使用
.net语言中使用MapWinGIS.ocx http://www.cnblogs.com/kekec/archive/2011/03/30/1999709.html 基于MapWinGis的开发探索 ...
- 天津Uber优步司机奖励政策(12月28日到12月29日)
滴快车单单2.5倍,注册地址:http://www.udache.com/ 如何注册Uber司机(全国版最新最详细注册流程)/月入2万/不用抢单:http://www.cnblogs.com/mfry ...
- Linux下安装Nginx并实现socket代理
nginx可以使用各平台的默认包来安装,本文是介绍使用源码编译安装,包括具体的编译参数信息. 正式开始前,编译环境gcc g++ 开发库之类的需要提前装好,这里默认你已经装好. ububtu平台编译环 ...
- 如何用istio实现应用的灰度发布
Istio为用户提供基于微服务的流量治理能力.Istio允许用户按照标准制定一套流量分发规则,并且无侵入的下发到实例中,平滑稳定的实现灰度发布功能. 基于华为云的Istio服务网格技术,使得灰度发布全 ...
- 怎样安装Android Studio
在浏览器地址栏输入 http://www.android-studio.org/ 打开Android Studio中文社区, 下载安装包: 这里需要注意的是SDK的目录, 我没有选择默认的目录, 而是 ...
- Maxscript-获取选中文件
Maxscript - 获取选中文件 使用 .Net 的方法弹出窗口选择文件,并范围所有选中文件的路径“” Fn Fun_GetFilePaths strTitle strFilter = ( dia ...
- node事件循环
Node.js 是单进程单线程应用程序,但是通过事件和回调支持并发,所以性能非常高. Node.js 的每一个 API 都是异步的,并作为一个独立线程运行,使用异步函数调用,并处理并发. Node.j ...
- 数据库Mysql的学习(五)-运算符与函数
,store,store,store,store FROM bookinfo;//加减乘除取余 //余额大于200 //余额不等于200 SELECT * FROM readerinfo WHERE ...