微信支付和支付宝支付开发案列
以下基础方法可以参考支付宝支付开放平台(https://doc.open.alipay.com/docs/doc.htm?treeId=204&articleId=105051&docType=1)
和微信支付平台相关文档(https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_3)
//===========================【weixinPay】==========2017.01.09=======by.Tommy==================
String timeMillis = String.valueOf(System.currentTimeMillis() / 1000);
String randomString = PayCommonUtil.getRandomString(32);
public static String wxnotify = "/commodityOrderbase/weixinpayNotify.do";
public static String alipayNotifyUrl = "/commodityOrderbase/alipayNotify.do";
/**
* @throws UnsupportedEncodingException
*
* @Title: weixinPrePay
* @Description: 调用微信支付系统 ---统一下单API()
* API地址:https://pay.weixin.QQ.com/wiki/doc/api/jsapi.php?chapter=9_1
* 看文档,主要流程就是把20个左右的参数封装为xml格式发送到微信给的接口地址,然后就可以获取到返回的内容了,如果成功里面就有支付所需要的预支付ID
* @param @param trade_no
* @param @param totalAmount
* @param @param description
* @param @param openid
* @param @param sym
* @param @param request
* @param @return 参数
* @return Map 返回类型
* @throws
*/
@SuppressWarnings("unchecked")
public Map weixinPrePay(String trade_no,String totalAmount,
String description, String openid,String sym, HttpServletRequest request) throws UnsupportedEncodingException {
SortedMap parameterMap = new TreeMap();
parameterMap.put("appid", PayCommonUtil.APPID); //应用ID
parameterMap.put("mch_id", PayCommonUtil.MCH_ID); //商户号
parameterMap.put("nonce_str", randomString);
/**
* 商品描述 商品描述交易字段格式根据不同的应用场景按照以下格式: APP——需传入应用市场上的APP名字-实际商品名称,天天爱消除-游戏充值。
*/
parameterMap.put("body", description);
//商户系统内部的订单号,32个字符内、可包含字母, 其他说明见商户订单号
parameterMap.put("out_trade_no", trade_no);
//货币类型
parameterMap.put("fee_type", "CNY");
//订单总金额 支付金额单位为【分】,参数值不能带小数。
parameterMap.put("total_fee", totalAmount);
//用户端实际ip
parameterMap.put("spbill_create_ip", request.getRemoteAddr());
/**
* 通知地址 接收微信支付异步通知回调地址,通知url必须为直接可访问的url,不能携带参数。
*/
//parameterMap.put("notify_url", sym + wxnotify);
String basePath = request.getScheme() + "://"
+ request.getServerName() + ":" + request.getServerPort()+ request.getContextPath();
parameterMap.put("notify_url", basePath + wxnotify);
//交易类型 支付类型
parameterMap.put("trade_type", "APP");
//trade_type为JSAPI是 openid为必填项
//parameterMap.put("openid", openid);
//生成签名
String sign = PayCommonUtil.createSign("UTF-8", parameterMap);
parameterMap.put("sign", sign);
String requestXML = PayCommonUtil.getRequestXml(parameterMap);
//发起HTTP POST 请求 并获得微信支付系统返回结果
String result = PayCommonUtil.httpsRequest("https://api.mch.weixin.qq.com/pay/unifiedorder", "POST",requestXML);
Map map = null;
try {
map = PayCommonUtil.doXMLParse(result);
log.info("【微信APP支付】请求微信统一下单接口结果:"+result);
} catch (JDOMException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return map;
}
/**
* @throws Exception
*
* @Title: weixinRefund
* @Description: 微信退款
* @param @param trade_no 商户侧传给微信的订单号
* @param @param out_refund_no 商户系统内部的退款单号,商户系统内部唯一,同一退款单号多次请求只退一笔
* @param @param totalAmount
* @param @param openid
* @param @param sym
* @param @param request
* @param @return
* @param @throws UnsupportedEncodingException 参数
* @return Map 返回类型
* @throws
*/
@SuppressWarnings("unchecked")
public Map weixinRefund(String trade_no,String out_refund_no,String totalAmount
,HttpServletRequest request) throws Exception {
SortedMap parameterMap = new TreeMap();
parameterMap.put("appid", PayCommonUtil.APPID); //应用ID
parameterMap.put("mch_id", PayCommonUtil.MCH_ID); //商户号
parameterMap.put("nonce_str", randomString);
parameterMap.put("out_trade_no", trade_no);
parameterMap.put("out_refund_no", out_refund_no);
//订单总金额 支付金额单位为【分】,参数值不能带小数。
parameterMap.put("total_fee", totalAmount);
//订单退款总金额 支付金额单位为【分】,参数值不能带小数。
parameterMap.put("refund_fee", totalAmount);
//操作员帐号, 默认为商户号
parameterMap.put("op_user_id", PayCommonUtil.MCH_ID);
//生成签名
String sign = PayCommonUtil.createSign("UTF-8", parameterMap);
parameterMap.put("sign", sign);
String requestXML = PayCommonUtil.getRequestXml(parameterMap);
//发起HTTP POST 请求 并获得微信支付系统返回结果
String result = PayCommonUtil.httpsRequest2("https://api.mch.weixin.qq.com/secapi/pay/refund", "POST",requestXML);
Map map = null;
try {
map = PayCommonUtil.doXMLParse(result);
log.info("【微信APP支付】提交微信退款业务结果:"+result);
} catch (JDOMException e) {
log.info("【微信APP支付】提交微信退款业务结果:"+result);
} catch (IOException e) {
log.info("【微信APP支付】提交微信退款业务结果:"+result);
}
return map;
}
/**
*
* @Title: weixinpayNotify
* @Description: 接收微信支付异步通知回调地址,通知url必须为直接可访问的url,不能携带参数。
* @param @param request
* @param @param response
* @param @return
* @param @throws UserExecuteFailedException
* @param @throws IOException
* @param @throws JDOMException 参数
* @return String 返回类型
* @throws
*/
@RequestMapping(value = "weixinpayNotify.do")
public String weixinpayNotify(HttpServletRequest request, HttpServletResponse response)
throws UserExecuteFailedException, IOException, JDOMException {
// /////////////////////////////////////////////////
log.info("=【微信APP支付】异步通知=");
InputStream inStream = request.getInputStream();
ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len = 0;
while ((len = inStream.read(buffer)) != -1) {
outSteam.write(buffer, 0, len);
}
String resultxml = new String(outSteam.toByteArray(), "UTF-8");
Map params = PayCommonUtil.doXMLParse(resultxml);
outSteam.close();
inStream.close();
if (!PayCommonUtil.isTenpaySign(params)) {
log.info("【微信APP支付】异步通知结果:fail");
return "fail";
}else{
// ------------------------------
// 处理业务开始
// ------------------------------
// 此处处理订单状态,结合自己的订单数据完成订单状态的更新
// ------------------------------
String out_trade_no = params.get("out_trade_no"); //商户系统的订单号,与请求一致。
String transaction_id = params.get("transaction_id"); //商户系统的订单号,与请求一致。
CommodityOrderBase cob = orderBaseService.queryByOrderNum(out_trade_no);
if(cob != null){
//避免付款后立马取消了订单,异步通知还未处理,仍将取消状态改为待发货的bug
if(cob.getOrderStatus() == 0 ){
cob.setOrderStatus(1); //订单状态:0 待付款 1待发货 2已发货 3已完成 4 已取消
cob.setPayTime(DateUtils.formatNowDate()); //支付时间
cob.setTransactionId(transaction_id); //微信支付订单号
cob.setCheckCancel(1); //是否可以退款:1可以2订单处理中
orderBaseService.updateCommodityOrderBase(cob);
}
}
// 处理业务完毕
// ------------------------------
log.info("【微信APP支付】异步通知结果:"+"订单号为:"+out_trade_no);
return "success";
}
// /////////////////////////////////////////////////
}
//======================【alipay】=======================================================
/**
* @throws AlipayApiException
*
* @Title: aliPay
* @Description: 【支付宝APP支付】下订单
* @param body 对一笔交易的具体描述信息。如果是多种商品,请将商品描述字符串累加传给body。 示例:Iphone6 16G
* @param subject 商品的标题/交易标题/订单标题/订单关键字等。 示例:大乐透
* @param out_trade_no 商户网站唯一订单号
* @param total_amount 商户网站唯一订单号订单总金额,单位为元,精确到小数点后两位,取值范围[0.01,100000000] 示例:9.00
* @param request
* @param @return
* @param @throws UnsupportedEncodingException 参数
* @return Map 返回类型
* @throws
*/
@SuppressWarnings("unchecked")
public String aliPay(String body, String subject, String out_trade_no, String total_amount
, HttpServletRequest request) throws UnsupportedEncodingException, AlipayApiException {
log.info("【支付宝APP支付】下订单开始==========-》");
//1.公共参数=========->
Map param = new HashMap();
param.put("app_id", AliPayUtil.APPID);
param.put("method", "alipay.trade.app.pay");
param.put("format", AliPayUtil.FORMAT);
param.put("charset",AliPayUtil.CHARSET);
param.put("sign_type", AliPayUtil.SIGN_TYPE);
//发送请求的时间,格式"yyyy-MM-dd HH:mm:ss"
param.put("timestamp", DateUtils.formatNowDate());
param.put("version", "1.0");
String notify_url = request.getScheme() + "://"
+ request.getServerName() + ":" + request.getServerPort()+ request.getContextPath()
+alipayNotifyUrl;
param.put("notify_url", notify_url);
//2.业务参数=========->
Map pcont = new HashMap();
pcont.put("body", body);
pcont.put("subject", subject);
pcont.put("out_trade_no", out_trade_no);
/**
* 该笔订单允许的最晚付款时间,逾期将关闭交易。
* 取值范围:1m~15d。m-分钟,h-小时,d-天,1c-当天(1c-当天的情况下,无论交易何时创建,都在0点关闭)。
* 该参数数值不接受小数点, 如 1.5h,可转换为 90m。
*/
pcont.put("timeout_express", "90m");
pcont.put("total_amount", total_amount);
pcont.put("seller_id", AliPayUtil.PARTNER_ID);
//销售产品码,商家和支付宝签约的产品码,为固定值QUICK_MSECURITY_PAY
pcont.put("product_code", "QUICK_MSECURITY_PAY");
//请求参数按照key=value&key=value方式拼接的未签名原始字符串:
JSONObject bizcontentJson= JSONObject.fromObject(pcont);
//业务请求参数的集合,最大长度不限,除公共参数外所有请求参数都必须放在这个参数中传递,具体参照各产品快速接入文档
param.put("biz_content", bizcontentJson.toString()); // 业务请求参数 不需要对json字符串转义
/**
* 方式一==============================================================================
*/
param.put("sign", AliPayUtil.getSign(param, AliPayUtil.APP_PRIVATE_KEY)); // 业务请求参数
String orderStr = AliPayUtil.getSignEncodeUrl(param, true);
log.info("【支付宝APP支付】请求参数为:"+orderStr);
return orderStr;
/**
* 方式二==============================================================================
*/
/* //3.对未签名原始字符串进行签名=========->
String rsaSign = AlipaySignature.rsaSign(param, AliPayUtil.APP_PRIVATE_KEY, "UTF-8");
Map map4 = new HashMap();
map4.put("app_id", AliPayUtil.APPID);
map4.put("method", "alipay.trade.app.pay");
map4.put("format", AliPayUtil.FORMAT);
map4.put("charset", AliPayUtil.CHARSET);
map4.put("sign_type", AliPayUtil.SIGN_TYPE);
map4.put("timestamp", URLEncoder.encode(DateUtils.formatNowDate(),"UTF-8"));
map4.put("version", "1.0");
map4.put("notify_url", URLEncoder.encode(notify_url,"UTF-8"));
//4.最后对请求字符串的所有一级value(biz_content作为一个value)进行encode,编码格式按请求串中的charset为准,没传charset按UTF-8处理,获得最终的请求字符串:
map4.put("biz_content", URLEncoder.encode(bizcontentJson.toString(), "UTF-8"));
Map par = AlipayCore.paraFilter(map4); //除去数组中的空值和签名参数
String json4 = AlipayCore.createLinkString(map4); //拼接后的字符串
json4 = json4 + "&sign=" + URLEncoder.encode(rsaSign, "UTF-8");
log.info("【支付宝APP支付】请求参数为:"+json4.toString());
return json4.toString(); */
}
/**
*
* @Title: alipayNotify
* @Description: TODO 支付宝APP支付异步通知
* 对于APP支付产生的交易,支付宝会根据原始支付API中传入的异步通知地址notify_url,通过POST请求的形式将支付结果作为参数通知到商户系统。
* 参考链接:
* http://blog.csdn.net/fengshizty/article/details/53215196
* https://doc.open.alipay.com/docs/api.htm?spm=a219a.7395905.0.0.sS4WFQ&docType=4&apiId=759
* https://doc.open.alipay.com/docs/doc.htm?spm=a219a.7629140.0.0.y9tReT&treeId=193&articleId=105303&docType=1
* @param @param request
* @param @throws Exception 参数
* @return void 返回类型
* @throws
*/
@ResponseBody
@RequestMapping(value = "/alipayNotify.do",method={RequestMethod.POST})
public String alipayNotify( HttpServletRequest request ){
/**
* 获取到返回的所有参数 先判断是否交易成功trade_status 再做签名校验
* 1、商户需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号
* 2、判断total_amount是否确实为该订单的实际金额(即商户订单创建时的金额)
* 3、校验通知中的seller_id(或者seller_email) 是否为out_trade_no这笔单据的对应的操作方(有的时候,一个商户可能有多个seller_id/seller_email)
* 4、验证app_id是否为该商户本身。上述1、2、3、4有任何一个验证不通过,则表明本次通知是异常通知,务必忽略。在上述验证通过后商户必须根据支付宝不同类型的业务通知,
* 正确的进行不同的业务处理,并且过滤重复的通知结果数据。在支付宝的业务通知中,只有交易通知状态为TRADE_SUCCESS或TRADE_FINISHED时,支付宝才会认定为买家付款成功。
*
* 交易状态: WAIT_BUYER_PAY 交易创建,等待买家付款 TRADE_CLOSED 未付款交易超时关闭,或支付完成后全额退款
* TRADE_SUCCESS 交易支付成功 TRADE_FINISHED 交易结束,不可退款
*/
String trade_status = request.getParameter("trade_status");
String out_trade_no = request.getParameter("out_trade_no");
String trade_no = request.getParameter("trade_no"); //支付宝交易凭证号
log.info("【支付宝APP支付】支付异步通知结果:"+trade_status+" ====-》订单号为:"+out_trade_no);
if ("TRADE_SUCCESS".equals(trade_status)) {
Enumeration> pNames = request.getParameterNames();
Map param = new HashMap();
try {
//将异步通知中收到的待验证所有参数都存放到map中
while (pNames.hasMoreElements()) {
String pName = (String) pNames.nextElement();
param.put(pName, request.getParameter(pName));
}
//log.info("【支付宝APP支付】异步通知返回参数--param:"+param);
boolean signVerified = AlipaySignature.rsaCheckV1(param, AliPayUtil.ALIPAY_PUBLIC_KEY,
AliPayUtil.CHARSET);
log.info("【支付宝APP支付】校验签名是否正确--signVerified:"+signVerified);
//调用SDK验证签名
if (signVerified) {
// TODO 验签成功后
// 按照支付结果异步通知中的描述,对支付结果中的业务内容进行1\2\3\4二次校验,校验成功后在response中返回success,校验失败返回failure
CommodityOrderBase cob = orderBaseService.queryByOrderNum(out_trade_no);
if(cob != null){
//避免付款后立马取消了订单,异步通知还未处理,仍将取消状态改为待发货的bug
if(cob.getOrderStatus() == 0){
cob.setOrderStatus(1); //订单状态:0 待付款 1待发货 2已发货 3已完成 4 已取消
cob.setPayTime(DateUtils.formatNowDate()); //支付时间
cob.setAlipayTradeNo(trade_no); //支付宝交易凭证号
cob.setCheckCancel(1); //是否可以退款:1可以2订单处理中
orderBaseService.updateCommodityOrderBase(cob);
}
}
log.info("【支付宝APP支付】订单支付成功:" + JSON.toJSONString(param));
return "success";
} else {
// TODO 验签失败则记录异常日志,并在response中返回failure.
log.info("【支付宝APP支付】订单支付失败:" + JSON.toJSONString(param));
return "failure";
}
} catch (Exception e) {
log.info("【支付宝APP支付】订单支付抛出异常:" + e.getMessage());
return "failure";
}
}else{
return "failure";
}
}
/**
*
* @Title: alipayRefund
* @Description: TODO【阿里APP支付】申请退款
* @param trade_no 支付宝交易号,和商户订单号不能同时为空
* @param out_trade_no 订单支付时传入的商户订单号,不能和 trade_no同时为空。
* @param refund_amount 需要退款的金额,该金额不能大于订单金额,单位为元,支持两位小数
* @param request
* @param @return
* @param @throws Exception 参数
* @return Map 返回类型
* @throws
*/
public Map alipayRefund(String alipay_trade_no,String out_trade_no,String refund_amount
,HttpServletRequest request) throws Exception {
// 统一收单交易退款接口
AlipayTradeRefundRequest alipayRequest = new AlipayTradeRefundRequest();
//只需要传入业务参数
Map param = new HashMap<>();
param.put("trade_no", alipay_trade_no);
param.put("out_trade_no", out_trade_no);
param.put("refund_amount", refund_amount);
param.put("refund_reason", "正常退款");//退款的原因说明
alipayRequest.setBizContent(JSON.toJSONString(param)); // 请求参数的集合 ,不需要对json字符串转义
//返回支付宝退款信息
Map restmap = new HashMap<>();
try {
AlipayTradeRefundResponse alipayResponse = AlipayAPIClientFactory.getAlipayClient().execute(alipayRequest);
if (alipayResponse.isSuccess()) {
/**
* 调用成功,则处理业务逻辑
* code:
* 10000 接口调用成功
* 20000 服务不可用
* 查看地址:https://doc.open.alipay.com/docs/doc.htm?treeId=291&articleId=105806&docType=1
*/
if ("10000".equals(alipayResponse.getCode())) {
restmap.put("out_trade_no", alipayResponse.getOutTradeNo());
restmap.put("trade_no", alipayResponse.getTradeNo());
restmap.put("buyer_logon_id", alipayResponse.getBuyerLogonId());// 用户的登录id
restmap.put("gmt_refund_pay", DateUtils.formatWithDefaultPattern(alipayResponse.getGmtRefundPay())); //退款支付时间
restmap.put("buyer_user_id", alipayResponse.getBuyerUserId());// 买家在支付宝的用户id
restmap.put("return_code", "SUCCESS");
restmap.put("return_msg", "SUCCESS");
log.info("【支付宝APP支付】订单退款结果:退款成功");
} else {
log.info("【支付宝APP支付】订单退款失败:" + alipayResponse.getMsg() + ":" + alipayResponse.getSubMsg());
restmap.put("return_code", "FAIL");
restmap.put("return_msg", "支付宝申请退款失败原因:"+alipayResponse.getMsg()+" 错误码查看地址:https://doc.open.alipay.com/docs/doc.htm?treeId=291&articleId=105806&docType=1");
}
}
} catch (AlipayApiException e) {
restmap.put("return_code", "FAIL");
log.info("【支付宝APP支付】订单退款失败:"+e.getErrMsg());
}
return restmap;
}
//1 微信支付 2支付宝支付
if(payway.equals(1)){
log.info("=【微信APP支付】进入微信支付统一下单接口=");
//微信支付 总金额单位为 分
String weixinTotalAmount = ""+ (int)(StringUtil.getDouble(totalAmount)*100);
Map map = weixinPrePay(ob.getOrderNum(),weixinTotalAmount, description,"", "", request);
String return_code = map.get("return_code");
String result_code = map.get("result_code"); //业务结果 参考链接:https://pay.weixin.qq.com/wiki/doc/api/wap.php?chapter=9_1
String return_msg = map.get("return_msg");
if(return_code.equalsIgnoreCase("SUCCESS") && result_code.equalsIgnoreCase("SUCCESS")){
//返回给APP端相关参数,用于调用微信支付
SortedMap finalpackage = new TreeMap();
finalpackage.put("appid", PayCommonUtil.APPID);
finalpackage.put("partnerid", PayCommonUtil.MCH_ID);
finalpackage.put("prepayid", map.get("prepay_id"));
finalpackage.put("package", "Sign=WXPay");
finalpackage.put("noncestr", PayCommonUtil.getRandomString(32));
finalpackage.put("timestamp", String.valueOf(System.currentTimeMillis() / 1000));
String sign = PayCommonUtil.createSign("UTF-8", finalpackage);
finalpackage.put("sign", sign);
finalpackage.put("packages", "Sign=WXPay"); //用于Android避免关键字 ,意义与上package同等
maps.putAll(finalpackage);
//修改订单状态
ob.setOrderStatus(0); //订单状态:0 待支付 1待发货 2已发货 3已完成 4 已取消
ob.setPayway(1);
ob.setCheckCancel(2); //是否可以退款:1可以2订单处理中
orderBaseService.updateCommodityOrderBase(ob);
}else{
errorCode = 222; //下单失败
log.info("【微信APP支付】请求统一下单接口失败,订单号为:"+ob.getOrderNum()+" 原因为:"+return_msg);
}
}else if(payway.equals(2)){
//支付宝 总金额单位为 元 保留小数点后2位
String alipaySign = aliPay(description, "快递费", ob.getOrderNum(), totalAmount, request);
maps.put("alipaySign", alipaySign);
//修改订单支付类型
ob.setPayway(2);
ob.setCheckCancel(2); //是否可以退款:1可以2订单处理中
orderBaseService.updateCommodityOrderBase(ob);
}
int payway = cob.getPayway(); //支付方式:1 微信 2 支付宝
if(payway == 1){
log.info("【微信APP支付】开始调用微信申请退款接口,订单号为:"+cob.getOrderNum());
Double totalfee = cob.getTotalFee();
String totalAmount = ""+(int)(totalfee*100);
Map map = weixinRefund(orderNum, outRefundNo, totalAmount, request);
String return_code = map.get("return_code"); //SUCCESS/FAIL
String return_msg = map.get("return_msg"); //返回信息,如非空,为错误原因 签名失败 参数格式校验错误
String result_code = map.get("result_code"); //SUCCESS/FAIL SUCCESS退款申请接收成功,结果通过退款查询接口查询 FAIL 提交业务失败
String err_code = map.get("err_code");
if(!StringUtil.nullOrBlank(return_code) && return_code.equalsIgnoreCase("SUCCESS") && result_code.equalsIgnoreCase("SUCCESS")){
log.info("【微信APP支付】原路退款至微信成功,商户订单号为:"+cob.getOrderNum());
//更改订单为【已取消】
orderBaseService.delOrderBase(orderNum, userId,diamond,outRefundNo);
cob.setOrderStatus(4);
cob.setCancelTime(DateUtils.formatNowDate());
cob.setOutRefundNo(outRefundNo);
orderBaseService.updateCommodityOrderBase(cob);
}else{
errorCode = 223; //微信申请退款失败
log.info("【微信APP支付】原路退款至微信失败,商户订单号为:"+cob.getOrderNum()+"原因为:"+return_msg+" 错误码:"+err_code);
log.info("错误码参见:https://pay.weixin.qq.com/wiki/doc/api/wap.php?chapter=9_4");
}
}else if(payway == 2){
log.info("【支付宝APP支付】原路退款至支付宝,商户订单号为:"+cob.getOrderNum());
Map map = alipayRefund(cob.getAlipayTradeNo(), cob.getOrderNum(), ""+cob.getTotalFee(), request);
String return_code = map.get("return_code");
String return_msg = map.get("return_msg");
String gmt_refund_pay = map.get("gmt_refund_pay");
if(!StringUtil.nullOrBlank(return_code) && return_code.equals("SUCCESS")){
log.info("【支付宝APP支付】申请原路退款至支付宝成功,商户订单号为:"+cob.getOrderNum()+" 退款支付时间为:"+gmt_refund_pay);
//更改订单为【已取消】
orderBaseService.delOrderBase(orderNum, userId,diamond,outRefundNo);
cob.setOrderStatus(4);
cob.setCancelTime(DateUtils.formatNowDate());
cob.setOutRefundNo(outRefundNo);
orderBaseService.updateCommodityOrderBase(cob);
}else{
errorCode = 224; //支付宝申请退款失败
log.info("【支付宝APP支付】申请原路退款至支付宝失败,商户订单号为:"+cob.getOrderNum()+"原因为:"+return_msg);
}
}else{
//更改订单为【已取消】
orderBaseService.delOrderBase(orderNum, userId,diamond,outRefundNo);
}
PayCommonUtil.java
package com.skin.pay.weixin;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.ConnectException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.KeyStore;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import javax.net.ssl.SSLContext;
import org.apache.http.Consts;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContexts;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.apache.log4j.Logger;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.input.SAXBuilder;
import com.skin.util.MD5Util;
/**
*
* @ClassName: PayCommonUtil
* @Description: 微信支付帮助类
* 参考链接:http://www.cnblogs.com/007sx/p/5811137.html https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_1
* @author by.Tommy
* @date 2017年1月9日
*
*/
public class PayCommonUtil {
//微信支付参数配置
public static String API_KEY="*****";
public static String APPID="****";
public static String MCH_ID="****";
protected static Logger log = Logger.getLogger(PayCommonUtil.class);
/**
*
* @Title: getRandomString
* @Description: 随机字符串生成
* @param @param length 生成字符串的长度
* @param @return 参数
* @return String 返回类型
* @throws
*/
public static String getRandomString(int length) {
String base = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
Random random = new Random();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < length; i++) {
int number = random.nextInt(base.length());
sb.append(base.charAt(number));
}
return sb.toString();
}
/**
*
* @Title: getRequestXml
* @Description: 请求xml组装
* @param @param parameters
* @param @return 参数
* @return String 返回类型
* @throws
*/
public static String getRequestXml(SortedMap parameters){
StringBuffer sb = new StringBuffer();
sb.append("");
Set es = parameters.entrySet();
Iterator it = es.iterator();
while(it.hasNext()) {
Map.Entry entry = (Map.Entry)it.next();
String key = (String)entry.getKey();
String value = (String)entry.getValue();
if ("attach".equalsIgnoreCase(key)||"body".equalsIgnoreCase(key)||"sign".equalsIgnoreCase(key)) {
//sb.append("<"+key+">"+""+key+">");
sb.append("<"+key+">"+value+""+key+">");
}else {
sb.append("<"+key+">"+value+""+key+">");
}
}
sb.append(" ");
return sb.toString();
}
/**
*
* @Title: createSign
* @Description: 生成签名
* @param characterEncoding
* @param parameters
* @param @return 参数
* @return String 返回类型
* @throws
*/
public static String createSign(String characterEncoding,SortedMap 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=" + API_KEY);
String sign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase();
return sign;
}
/**
* 验证回调签名
* @param packageParams
* @param key
* @param charset
* @return
*/
public static boolean isTenpaySign(Map map) {
String charset = "UTF-8";
String signFromAPIResponse = map.get("sign");
if (signFromAPIResponse == null || signFromAPIResponse.equals("")) {
log.info("==API返回的数据签名数据不存在,有可能被第三方篡改!!!==");
return false;
}
//System.out.println("服务器回包里面的签名是:" + signFromAPIResponse);
//过滤空 设置 TreeMap
SortedMap packageParams = new TreeMap<>();
for (String parameter : map.keySet()) {
String parameterValue = map.get(parameter);
String v = "";
if (null != parameterValue) {
v = parameterValue.trim();
}
packageParams.put(parameter, v);
}
StringBuffer sb = new StringBuffer();
Set es = packageParams.entrySet();
Iterator it = es.iterator();
while(it.hasNext()) {
Map.Entry entry = (Map.Entry)it.next();
String k = (String)entry.getKey();
String v = (String)entry.getValue();
if(!"sign".equals(k) && null != v && !"".equals(v)) {
sb.append(k + "=" + v + "&");
}
}
sb.append("key=" + API_KEY);
//将API返回的数据根据用签名算法进行计算新的签名,用来跟API返回的签名进行比较
//算出签名
String resultSign = "";
String tobesign = sb.toString();
if (null == charset || "".equals(charset)) {
//resultSign = MD5Util.MD5Encode(tobesign,characterEncoding).toUpperCase();
resultSign = MD5Util.MD5Encode(tobesign,"").toUpperCase();
} else {
try {
//resultSign = MD5Util.MD5Encode(tobesign,characterEncoding).toUpperCase();
resultSign = MD5Util.MD5Encode(tobesign,"").toUpperCase();
} catch (Exception e) {
//resultSign = MD5Util.MD5Encode(tobesign,characterEncoding).toUpperCase();
resultSign = MD5Util.MD5Encode(tobesign,"").toUpperCase();
}
}
String tenpaySign = ((String)packageParams.get("sign")).toUpperCase();
return tenpaySign.equals(resultSign);
}
/**
* 发起HTTP请求方法
*/
public static String httpsRequest(String requestUrl, String requestMethod, String outputStr) {
try {
URL url = new URL(requestUrl);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setDoOutput(true);
conn.setDoInput(true);
conn.setUseCaches(false);
// 设置请求方式(GET/POST)
conn.setRequestMethod(requestMethod);
conn.setRequestProperty("content-type", "application/x-www-form-urlencoded");
// 当outputStr不为null时向输出流写数据
if (null != outputStr) {
OutputStream outputStream = conn.getOutputStream();
// 注意编码格式
outputStream.write(outputStr.getBytes("UTF-8"));
outputStream.close();
}
// 从输入流读取返回内容
InputStream inputStream = conn.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String str = null;
StringBuffer buffer = new StringBuffer();
while ((str = bufferedReader.readLine()) != null) {
buffer.append(str);
}
// 释放资源
bufferedReader.close();
inputStreamReader.close();
inputStream.close();
inputStream = null;
conn.disconnect();
return buffer.toString();
} catch (ConnectException ce) {
log.info("https连接超时:{}"+ ce);
} catch (Exception e) {
log.info("https请求异常:{}"+ e);
}
return null;
}
/**
* 退款的请求方法
*
* 当交易发生之后一段时间内,由于买家或者卖家的原因需要退款时,卖家可以通过退款接口将支付款退还给买家,微信支付将在收到退款请求并且验证成功之后,按照退款规则将支付款按原路退到买家帐号上。
* 注意:
* 1、交易时间超过一年的订单无法提交退款;
* 2、微信支付退款支持单笔交易分多次退款,多次退款需要提交原支付订单的商户订单号和设置不同的退款单号。一笔退款失败后重新提交,要采用原来的退款单号。总退款金额不能超过用户实际支付金额。
* 3、退款有一定延时,用零钱支付的退款20分钟内到账,银行卡支付的退款3个工作日后重新查询退款状态。
* 接口链接:https://api.mch.weixin.qq.com/secapi/pay/refund
*/
public static String httpsRequest2(String requestUrl, String requestMethod, String outputXml) throws Exception {
/*----1.读取证书文件,这一段是直接从微信支付平台提供的demo中copy的,所以一般不需要修改---- */
KeyStore keyStore = KeyStore.getInstance("PKCS12");
StringBuilder res = new StringBuilder("");
FileInputStream instream = new FileInputStream(new File("/home/weixin/apiclient_cert.p12"));
try {
keyStore.load(instream, MCH_ID.toCharArray());
} finally {
instream.close();
}
// Trust own CA and all self-signed certs
SSLContext sslcontext = SSLContexts.custom()
.loadKeyMaterial(keyStore, MCH_ID.toCharArray())
.build();
// Allow TLSv1 protocol only
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
sslcontext,
new String[] { "TLSv1" },
null,
SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
try {
/*----2.发送数据到微信的退款接口---- */
HttpPost httpost = new HttpPost(requestUrl);
httpost.addHeader("Connection", "keep-alive");
httpost.addHeader("Accept", "*/*");
httpost.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
httpost.addHeader("Host", "api.mch.weixin.qq.com");
httpost.addHeader("X-Requested-With", "XMLHttpRequest");
httpost.addHeader("Cache-Control", "max-age=0");
httpost.addHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0) ");
StringEntity stringEntity = new StringEntity(outputXml ,Consts.UTF_8);
httpost.setEntity(stringEntity);
CloseableHttpResponse response = httpclient.execute(httpost);
try {
HttpEntity entity = response.getEntity();
if (entity != null) {
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(entity.getContent()));
String text = "";
res.append(text);
while ((text = bufferedReader.readLine()) != null) {
res.append(text);
//System.out.println(text);
}
}
EntityUtils.consume(entity);
} finally {
response.close();
}
} finally {
httpclient.close();
}
return res.toString();
}
/**
*
* @Title: doXMLParse
* @Description: xml解析
* @param strxml
* @param @return
* @param @throws JDOMException
* @param @throws IOException 参数
* @return Map 返回类型
* @throws
*/
public static Map doXMLParse(String strxml) throws JDOMException, IOException {
strxml = strxml.replaceFirst("encoding=\".*\"", "encoding=\"UTF-8\"");
if(null == strxml || "".equals(strxml)) {
return null;
}
Map m = new HashMap();
InputStream in = new ByteArrayInputStream(strxml.getBytes("UTF-8"));
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 = getChildrenText(children);
}
m.put(k, v);
}
//关闭流
in.close();
return m;
}
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(getChildrenText(list));
}
sb.append(value);
sb.append("" + name + ">");
}
}
return sb.toString();
}
}
AliPayUtil.java
package com.skin.pay.alipay;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.apache.log4j.Logger;
/**
* 参考文档: https://doc.open.alipay.com/docs/api.htm?spm=a219a.7395905.0.0.Ewddf6&docType=4&apiId=757
* @ClassName: AliPayUtil
* @Description: 阿里支付宝支付帮助类
* @author by.Tommy
* @date 2017年1月11日
*
*/
public class AliPayUtil {
protected static Logger log = Logger.getLogger(AliPayUtil.class);
//=============阿里支付宝支付参数配置 ===================================
//支付宝网关(固定)
public static String ALIPAY_GATEWAY="https://openapi.alipay.com/gateway.do";
//APPID即创建应用后生成
public static String APPID="*****";
//开发者应用私钥,由开发者自己生成
public static String APP_PRIVATE_KEY="******";
//参数返回格式,只支持json
public static String FORMAT="json";
//请求和签名使用的字符编码格式,支持GBK和UTF-8
public static String CHARSET="utf-8";
//应用公钥, 由支付宝生成
//public static String ALIPAY_PUBLIC_KEY="*******";
//支付宝公钥,由支付宝生成
public static String ALIPAY_PUBLIC_KEY="*********";
//商户生成签名字符串所使用的签名算法类型,目前支持RSA2和RSA,推荐使用RSA2
public static String SIGN_TYPE="RSA";
//合作伙伴身份(PID) 查看地址:https://openhome.alipay.com/platform/keyManage.htm?keyType=partner
public static String PARTNER_ID="*******";
private static final String ALGORITHM = "RSA";
private static final String SIGN_ALGORITHMS = "SHA1WithRSA";
private static final String DEFAULT_CHARSET = "UTF-8";
public static String sign(String content, String privateKey) {
try {
PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(
Base64.decode(privateKey));
KeyFactory keyf = KeyFactory.getInstance(ALGORITHM);
PrivateKey priKey = keyf.generatePrivate(priPKCS8);
java.security.Signature signature = java.security.Signature
.getInstance(SIGN_ALGORITHMS);
signature.initSign(priKey);
signature.update(content.getBytes(DEFAULT_CHARSET));
byte[] signed = signature.sign();
return new String(Base64.encode(signed));
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 对支付参数信息进行签名
* @param map 待签名授权信息
* @return
*/
public static String getSign(Map map, String rsaKey) {
List keys = new ArrayList(map.keySet());
// key排序
Collections.sort(keys);
StringBuilder authInfo = new StringBuilder();
boolean first = true;
for (String key : keys) {
if (first) {
first = false;
} else {
authInfo.append("&");
}
authInfo.append(key).append("=").append(map.get(key));
}
return sign(authInfo.toString(), rsaKey);
}
/**
* 返回签名编码拼接url
* @param params
* @param isEncode
* @return
*/
public static String getSignEncodeUrl(Map map, boolean isEncode) {
String sign = map.get("sign");
String encodedSign = "";
if (CollectionUtil.isNotEmpty(map)) {
map.remove("sign");
List keys = new ArrayList(map.keySet());
// key排序
Collections.sort(keys);
StringBuilder authInfo = new StringBuilder();
boolean first = true;// 是否第一个
for (String key: keys) {
if (first) {
first = false;
} else {
authInfo.append("&");
}
authInfo.append(key).append("=");
if (isEncode) {
try {
authInfo.append(URLEncoder.encode(map.get(key), CHARSET));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
} else {
authInfo.append(map.get(key));
}
}
try {
encodedSign = authInfo.toString() + "&sign=" + URLEncoder.encode(sign, CHARSET);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
return encodedSign.replaceAll("\\+", "%20");
}
}
CollectionUtil.java
package com.skin.pay.alipay;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
public class CollectionUtil {
private CollectionUtil() {
super();
}
// 判断一个集合是否为空
public static boolean isEmpty(Collection col) {
if (col == null || col.isEmpty()) {
return true;
}
return false;
}
// 判断一个集合是否不为空
public static boolean isNotEmpty(Collection col) {
return !isEmpty(col);
}
// 判断Map是否为空
public static boolean isEmpty(Map map) {
if (map == null || map.isEmpty()) {
return true;
}
return false;
}
// 判断Map是否不为空为空
public static boolean isNotEmpty(Map map) {
return !isEmpty(map);
}
// 去除list中的重复数据
public static List removeRepeat(List list) {
if (isEmpty(list)) {
return list;
}
List result = new ArrayList();
for (T e : list) {
if (!result.contains(e)) {
result.add(e);
}
}
return result;
}
// 将集合转换为String数组
public static String[] toArray(List list) {
if (isEmpty(list)) {
return null;
}
String[] result = new String[list.size()];
for (int i = 0; i < list.size(); i++) {
result[i] = String.valueOf(list.get(i));
}
return result;
}
}