支付宝与微信二码合一,扫码支付(学习笔记)
1:页面生成的二维码:
(页面生成的二维码(扫描二维码会向服务端的判断扫码类型的接口发送带参数请求!)
( 带上订单号和商品图片参数 例如: 生成二维码参数地址
localhost:8080/common/qrcode?order=XX&imgurl=xx
)
2: 后端接收参数判断是什么发起的支付方式:
- 判断是什么发起请求的工具类(AgentUtils.java):
<!-- User Agent依赖 -->
<dependency>
<groupId>eu.bitwalker</groupId>
<artifactId>UserAgentUtils</artifactId>
<version>1.20</version>
</dependency>
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.StringUtils;
import eu.bitwalker.useragentutils.Browser;
import eu.bitwalker.useragentutils.DeviceType;
import eu.bitwalker.useragentutils.OperatingSystem;
import eu.bitwalker.useragentutils.UserAgent;
/**
* 客户端识别工具
*/
public class AgentUtils {
/**
* 获取客户端为 微信/支付宝
*/
public static String getUserAgentWap(HttpServletRequest request) {
String agent = ((HttpServletRequest) request).getHeader("user-agent");
if (StringUtils.isNotBlank(agent)) {
agent = agent.toLowerCase();
if (agent.indexOf("micromessenger") >= 0) {
return "weixin";
} else if (agent.indexOf("alipayclient") >= 0) {
return "alipay";
} else {
return "other";
}
}
return "unknown";
}
}
- 控制层:
/**
* 扫码支付URL
*/
@Controller
@RequestMapping("/common")
public class CommonsController extends BaseController{
@RequestMapping(value="/qrcode",method=RequestMethod.GET)
public String scan(HttpServletRequest request,RedirectAttributes attr) {
String order = request.getParameter("order");
String imgurl = request.getParameter("imgurl");
String agent = AgentUtils.getUserAgentWap(request);
if ("weixin".equals(agent)) {
//重定向发送微信服务端请求
return "redirect:/weixin/getcode?imgurl=" + imgurl + "&order=" + order + "";
} else if ("alipay".equals(agent)) {
//重定向发起服务端支付宝(确定下单的接口/pay/confirmpay)
return "redirect:/pay/confirmpay?imgurl=" + imgurl + "&order=" + order + "";
}
return null;
}
支付宝手机网站支付方式
: (建议先看一次官网文档)
要先去进行配置应用获取
应用App的ID(APP_ID)
,应用公钥(ALI_PUBLIC_KEY)
,私钥(APP_PRIVATE_KEY)
和支付宝公钥
4个参数
控制层Controller
@Controller
@RequestMapping("/pay")
public class PayController {
// 服务器异步通知页面api路径 需http://或者https://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问
public static String notify_url = "http://商户绑定域名/alipay.wap.pay/notify_url";
// 跳转页面,买家支付成功后跳转的页面,仅当买家支付成功后跳转一次 需http://或者https://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问 商户可以自定义同步跳转地址
public static String return_url = "http://商户绑定域名/alipay.wap.pay/return_url";
/**
* (重定向目标接口, 二选一,可以选择直接进行请求支付接口/orderpay进行支付)
* 先进到商品详情页面,让用户知道买的是什么,由用户点确定发起调用支付宝支付接口
* 确认支付界面 (使用的是更好的用户体验)
*/
@RequestMapping(value = "/confirmpay", method = RequestMethod.GET)
public String ConfirmPay(HttpServletRequest req, HttpServletResponse res,Model model) {
String imgurl = req.getParameter("imgurl");
String order = req.getParameter("order");
model.addAttribute("imgurl", imgurl);
model.addAttribute("order", order);
//返回confirmspays.jsp商品页面
return "confirmspays";
}
下单支付前的商品详情页面.jsp
:
<%@ page contentType="text/html;charset=UTF-8"%>
<%@ include file="/WEB-INF/views/include/taglib.jsp"%>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<%--jQuery--%>
<script src="${ctxStatic}/common/js/jquery-2.1.3.min.js"></script>
<title>支付确认</title>
<style type="text/css">
body, html {
height: 100%;
}
body{
padding: 0;
margin: 0;
font-size: 1.2rem;
}
.btn_style{
width: 90%;
margin-top: 50px;
margin-left: 5%;
height: 50px;
background-color: #805f45;
color: white;
font-size: 16px;
}
</style>
<div id="tz"></div>
<script type="text/javascript">
function submitData() {
$.ajax({
url : "${ctx}/pay/orderpay",
data : {
"urlSmallPicture": $("#imgurl").val(),
"billNumber": $("#order").val()
},
type : 'post',
dataType : 'json',
success : function(data) {
if(data.return_code == 'success'){
//把响应的确定支付表单显示到div中
$("#tz").html(data.return_data);
}
},
error : function() {
console.log('request error!');
}
});
}
</script>
</head>
<body>
<input type="hidden" name="imgurl" id="imgurl" value="${imgurl}">
<input type="hidden" name="order" id="order" value="${order}">
<div style="width: 100%;margin-top: 100px;text-align: center;">
<img src="${imgurl}" style="width: 200px;">
</div>
<div>
<button class="btn btn_style" id="btnSubmit" οnclick="submitData();">确 认</button>
</div>
</body>
</html>
支付宝下单支付接口:
/**
* 订单统一下单支付
* @param req
* @param resp
* @return
*/
@RequestMapping(value="/orderpay", method = RequestMethod.POST)
@ResponseBody
public Map<String, Object> orderpay(HttpServletRequest req, HttpServletResponse resp) throws IOException, ParseException, AlipayApiException{
Map<String, Object> map = new HashMap<String, Object>();
String order = req.getParameter("order");
//判断商户是否存在该订单和获取订单价格什么的(自行操作)
IdmOrder idmOrder = (IdmOrder)缓存Redis.getIdmOrderByBillNo(order);
......
//创建阿里客户端对象
AlipayClient alipayClient = new DefaultAlipayClient("https://openapi.alipay.com/gateway.do", "App的ID", "应用私钥","json", "字符集(UTF-8)", "应用公钥", "(签名类型例如:RSA2)");
//创建交易信息模型对象
AlipayTradeWapPayModel precreateModel = new AlipayTradeWapPayModel();
//商户订单号,需要保证不重复
precreateModel.setOutTradeNo(order);
//订单金额
precreateModel.setTotalAmount(money);
//交易主题
precreateModel.setSubject("Ada");
//商品名称
precreateModel.setBody("订单对象.get()");
//对一笔交易的具体描述信息。如果是多种商品,请将商品描述字符串累加传给body
precreateModel.setSellerId("出售商品");
//销售产品码,商家和支付宝签约的产品码,该产品请填写固定值:QUICK_WAP_WAY
precreateModel.setProductCode("QUICK_WAP_WAY");
//设置支付宝交易超时 取值范围:1m~15d。m-分钟,h-小时,d-天,1c-当天(1c-当天的情况下,无论交易何时创建,都在0点关闭)
precreateModel.setTimeoutExpress("5m");
//创建阿里请求对象
AlipayTradeWapPayRequest alipayRequest = new AlipayTradeWapPayRequest();
//业务请求参数的集合
alipayRequest.setBizModel(precreateModel);
//设置后台异步通知的地址,在手机端支付后支付宝会通知后台(成功或者失败),手机端的真实支付结果依赖于此地址
alipayRequest.setNotifyUrl(notify_url);
//支付成功后的跳转页面,由于前台回跳的不可靠性,前台回跳只能作为商户支付结果页的入口,最终支付结果必须以异步通知或查询接口返回为准,不能依赖前台回跳
alipayRequest.setReturnUrl(return_url);
//返回响应的输入密码完成支付的表单给商品详情页面
map.put("return_code", "success");
map.put("return_data", alipayClient.pageExecute(request).getBody());
return map;
//下面是直接拉起支付页面 没有到商品详情页面
// String form="";
// try {
// //进行执行
// form = alipayClient.pageExecute(alipayRequest).getBody(); //调用SDK生成表单
// } catch (AlipayApiException e) {
// e.printStackTrace();
// }
// httpResponse.setContentType("text/html;charset=UTF-8");
// httpResponse.getWriter().write(form);//直接将完整的表单html输出到页面
// httpResponse.getWriter().flush();
// httpResponse.getWriter().close();
}
支付宝NotifyUrl
支付异步通知和ReturnUrl
支付成功回跳:
(只能回调公司绑定好的公网能访问的域名 例如: https:www.baidu.com/pay/alipaynotify
)
/**
* 支付宝支付状态结果异步通知()
* @param req
* @param resp
* @return (并返回成功或者失败给支付宝通知接口,不然支付宝会一直异步通知)
*/
@RequestMapping(value = "/alipaynotify", method = RequestMethod.POST)
@ResponseBody
public String AlipayNotify(HttpServletRequest req, HttpServletResponse resp) throws IOException, AlipayApiException {
//将异步通知中所有参数都经过工具类处理后存放到map中
Map<String, String> params = PayUtil.parseParams(req.getParameterMap());
// 验证通知合法性
boolean verify_result = AlipaySignature.rsaCheckV1(paramsMap, "应用公匙", "字符集(UTF-8)", "(签名类型例如:RSA2)");
// 验证签名
if(! verify_result){
logger.warn("---支付宝异步通知->签名验证失败");
return PayUtil.generatePayErrorReplyParams();
}
//查看状态是否是交易成功状态
if ("TRADE_SUCCESS".equals(params.get("trade_status").toString())) {
//查询该订单是否存在.....
IdmOrder idmOrder = (IdmOrder)缓存Redis.getIdmOrderByBillNo(order);
if (order != null) {
//1、商户需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号,
if ( ! "支付接口返回订单号".equals(数据库订单号)) {
logger.info("支付宝异步通知->订单号与商户号订单不一致!" + out_trade_no);
return PayUtil.generatePayErrorReplyParams();
}
//2、校验返回的订单金额是否与商户侧的订单金额一致(查询商户后台的订单金额进行比较)
if (order.getOrderAmount() != Double.parseDouble(total_amount)) {
logger.info("支付宝异步通知->订单中的交易金额和返回的金额不一致!" + order.getOrderAmount());
return PayUtil.generatePayErrorReplyParams();
}
//3、校验通知中的seller_id(或者seller_email) 是否为out_trade_no这笔单据的对应的操作方
if (!xxxx.seller_id.equals(seller_id)) {
logger.info("支付宝异步通知->收款支付宝账户号不一致!" + seller_id);
return PayUtil.generatePayErrorReplyParams();
}
// 4、验证app_id是否为该商户本身
if (!app_id.equals(XXXX.ALI_APP_ID)) {
logger.info("---支付宝异步通知->商户号不一致!" + app_id);
return PayUtil.generatePayErrorReplyParams();
}
//更改商品订单状态....
//把商品订单存储进数据库 (然后删除缓存的数据)
//查商品库存.........
//减商品库存........
//极光通知App客户端(支付结果通知)
JpushService.pullMessages(别名,"PAY_RESULT","success","支付结果通知");
//返回成功状态码给支付宝接口
return PayUtil.generatePaySuccessReplyParams();
}
logger.info("支付宝异步通知->支付结果返回的不是成功的状态码,请详查!");
return PayUtil.generatePayErrorReplyParams();
}
/**
* 支付宝成功支付后页面跳转 ReturnUrl
*/
@RequestMapping(value = "/AlipaySuccess", method = RequestMethod.GET)
public String AlipaySuccess(HttpServletRequest req, HttpServletResponse res) {
//获取支付宝GET过来反馈信息
Map<String, String> params = PayUtil.parseParams(req.getParameterMap());
if (PayUtil.checkedParams(params)) {
//验证成功返回成功页面,或者成功状态给前端来处理
return "paySuccess";
}
return "";
}
//--------------------------交易查询接口如果需要在官网自行查询-----------------------------------
微信方式
: (建议先看一次官网文档)
要先去进行配置应用获取应用App的ID(appid)
, 应用秘钥(appsecret)
, 商户ID(mch_id)
和API密钥(Key)
4个参数
控制层用户授权获取code
/**
* 微信支付
*/
@Controller
@RequestMapping("/weixin")
public class WeiXinsPayController {
//公网能访问的公司域名加上项目支付接口地址
private static String WX_REDIRECT_URL ="https://******.com/weixin"
/**
*微信网页授权-用户授权获取code
*openid是微信用户在公众号appid下的唯一用户标识(appid不同,则获取到的openid就不同),可用于永久标记一个用户,同时也是微信JSAPI支付的必传参数
*/
@RequestMapping(value="/getcode")
public String getcode(Model model,HttpServletRequest req){
//购买商品生成的订单号方便后面调用顺便传递
String order = req.getParameter("order");
//拼接获取code的链接 ---redirect_uri参数:授权后重定向的回调链接地址 (state是重定向会带上的参数) 最好使用restful风格传递参数 (参数是网站链接要使用state传递)
String url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid="+AppID()+
"&redirect_uri="+WX_REDIRECT_URL+"/getopenid/" + order +"&response_type=code&scope=snsapi_base&state="+urlSmallPicture+"#wechat_redirect";
model.addAttribute("authorize", url);
//返回网站链接数据使用跳板页,进行访问
return "authorize";
}
}
跳板页.jsp
跳转url
:
<%@ page contentType="text/html;charset=UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html
<html lang="zh-CN">
<head>
<title></title>
<script type="text/javascript">
window.location.href = "${authorize}";
</script>
</head>
<body>
</body>
</html>
控制层获取openid
:
/**
* 获取openid就够了
* @param req
* @param model
* @param order 重定向链接带的参数使用restful风格接收
* @param code 响应参数
* @param state 响应参数
* @return
*/
@RequestMapping("/getopenid/{order}")
public String wx(HttpServletRequest req, Model model, @PathVariable("order") String order, @RequestParam("state") String state, @RequestParam("code")String code ){
Map<String, Object> params = new HashMap<String, Object>();
params.put("appid", AppID);
params.put("secret","应用密匙");
//填写第一步获取的code参数
params.put("code", code);
//固定写法
params.put("grant_type", "authorization_code");
//使用发送http请求的工具类发送http. get() 方式的请求获取响应参数
String openidJson = HttpUtil.get("https://api.weixin.qq.com/sns/oauth2/access_token", params);
//转换成map类型 K-V
Map<String, Object> parseObject = JSON.parseObject(openidJson);
//获得openid:
String openId = parseObject.get("openid").toString();
model.addAttribute("openid", openId);
//商品订单
model.addAttribute("order", order);
//订单号图片地址
model.addAttribute("imgurl", state);
//封装数据返回页面显示
return "confirmspay";
}
商品详情页面和调起微信前端脚本发起下单支付 .jsp页面
:
<%@ page contentType="text/html;charset=UTF-8" %>
<%@ include file="/WEB-INF/views/include/taglib.jsp" %>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<%--jQuery--%>
<script src="${ctxStatic}/common/js/jquery-2.1.3.min.js"></script>
<%--微信支付脚本--%>
<script src="http://res.wx.qq.com/open/js/jweixin-1.0.0.js" type="text/javascript"></script>
<title>支付确认</title>
<style type="text/css">
body, html {
height: 100%;
}
body {
padding: 0;
margin: 0;
font-size: 1.2rem;
}
.btn_style {
width: 90%;
margin-top: 50px;
margin-left: 5%;
height: 50px;
background-color: #805f45;
color: white;
font-size: 16px;
}
</style>
<script type="text/javascript">
function submitData() {
//确定支付调用统一下单接口
$.ajax({
url: "/weixin/paying",
data: {
"openid": $("#openid").val(),
"billNumber": $("#order").val()
},
type: 'post',
dataType: 'json',
success: function (data) {
if (data.return_code == 'success') {
var appId = data.appId;
var timeStamp = data.timeStamp;
var nonceStr = data.nonceStr;
var package = data.package;
var signType = data.signType;
var paySign = data.paySign;
WeixinJSBridge
.invoke(
'getBrandWCPayRequest',
{
"appId": appId, //公众号名称,由商户传入 obj.appid
"timeStamp": timeStamp, //时间戳,自1970年以来的秒数
"nonceStr": nonceStr, //随机串 obj.nonce_str
"package": package, //订单详情扩展字符串
"signType": signType, //微信签名方式:
"paySign": paySign //微信签名
},
function (res) {
if (res.err_msg == 'get_brand_wcpay_request:ok') {
//支付成功,可以做跳转到支付成功的提示页面(或者请求后端)
window.location.href = "${pageContext.request.contextPath}/weixin/payingSuccess";
// alert(JSON.stringify(res));
} else if (res.err_msg == 'get_brand_wcpay_request:cancel') {
//支付取消
alert("支付取消");
} else if (res.err_msg == 'get_brand_wcpay_request:fail') {
//支付失败
alert("支付失败");
// alert(JSON.stringify(res));
}
});
if (typeof WeixinJSBridge == "undefined") {
if (document.addEventListener) {
document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
} else if (document.attachEvent) {
document.attachEvent('WeixinJSBridgeReady', onBridgeReady);
document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
}
}
} else if (data.return_code == 'fail') {
//提示订单已经支付
alert(data.return_data);
}
},
error: function () {
console.log('request error!');
}
});
}
</script>
</head>
<body>
<%--隐藏域提交数据用--%>
<input type="hidden" name="order" id="order" value="${order}">
<input type="hidden" name="openid" id="openid" value="${openid}">
<div style="width: 100%; margin-top: 100px; text-align: center;">
<img src="${imgurl}" style="width: 200px;">
</div>
<div>
<button class="btn btn_style" id="btnSubmit" οnclick="submitData();">确
认
</button>
</div>
</body>
</html>
控制层:微信统一下单:
/**
* 统一下单
* @param req
* @return
* @throws Exception
*/
@RequestMapping(value="/paying",method=RequestMethod.POST)
@ResponseBody
public Map<String, Object> paying(HttpServletRequest req) throws Exception{
//获取订单号和openid参数
String order = req.getParameter("order");
String openid = req.getParameter("openid");
//创建线程安全的Map
Map<String, Object> maps = new ConcurrentHashMap<>();
//根据订单判断商户是否存在该订单
IdmOrder idmOrder = (IdmOrder)缓存Redis.getIdmOrderByBillNo(order);
if (idmOrder == null) {
logger.info("商户端->该订单不存在!");
maps.put("result_Msg", 错误信息);
return maps;
}......
Map<String, String> reqData = new HashMap<>();
//随机字符串 必填-每次请求都要是新的随机字符串
reqData.put("nonce_str", WXPayUtil.generateNonceStr());//WXPayUtil.generateNonceStr()
//商品描述 必填
reqData.put("body", ##);//"Ada"
//商户号 必填
reqData.put("mch_id", 商户ID(mch_id));
//商品订单号 必填
reqData.put("out_trade_no", order);
//支付金额(将元转换为分) 必填
reqData.put("total_fee", PayUtil.changeY2F(###));
//终端IP 不知道怎么写可以写本地127.0.1 必填
reqData.put("spbill_create_ip", "127.0.1");//客户端主机
//异步回调通知地址通知url必须为外网可访问的url,不能携带参数 必填
reqData.put("notify_url", ###); //"https://*****.com/wxpay/notifying"
//交易类型JSAPI 必填
reqData.put("trade_type", "JSAPI");
//之前获取的openid 必填
reqData.put("openid", openid);
//商品id
reqData.put("product_id", 按需);
//创建微信下单对象 (注入微信下单配置类)
WXPay wxpay = new WXPay(new MyWXPayConfig());
//执行下单 统一下单接口unifiedOrder
Map<String, String> responseparams = wxpay.unifiedOrder(reqData);
//预支付id
String prepay_id = "prepay_id=;
if ("SUCCESS".equals(responseparams.return_code)) {
String packages= prepay_id + (String) responseparams.get("prepay_id");
//App的ID
maps.put("appId", ##);
//时间戳
maps.put("timeStamp", timeStamp);
//随机字符串
maps.put("nonceStr", #);
//订单详情扩展字符串package是关键字加个S处理
maps.put("package", packages);
//签名方式 默认为MD5,支持HMAC-SHA256和MD5。注意此处需与统一下单的签名类型一致
maps.put("signType", #);
//注:这里采用 HMACSHA256进行签名,因为预下单支付那里默认采用HMACSHA256,要保持一致
String paySign = WXPayUtil.generateSignature(maps,"API密钥(Key)",SignType.HMACSHA256);
maps.put("return_code", "success");
maps.put("paySign", paySign);
}
}else {
maps.put("return_code", "error");
maps.put("return_data", "null");
}
return maps;
}
控制层:微信异步通知回调notify_url
:
/**
* 异步回调通知接口 (并返回成功或者失败给微信通知接口,不然微信会一直异步通知)
* @param request
*/
@ResponseBody
@RequestMapping("/notifying")
public String notifying(HttpServletRequest request, HttpServletResponse response) throws Exception{
// 获取请求数据,将请求转成xml
String xmlData = null;
try {
xmlData = PayUtil.copyToString(request.getInputStream(), Charset.forName("utf-8"));
} catch (IOException e) {
logger.info("微信公众号支付异步通知>>数据无法转成xml异常!");
//返回支付失败状态给微信
return PayUtil.generatePayErrorReplyXML();
}
// 将得到xml转为map,验证签名是否成功
Map<String, String> xmlToMapData = WXPayUtil.xmlToMap(xmlData);
logger.info("得到的xmlToMapData:"+xmlToMapData);
//获取签名sign
String sign = xmlToMapData.get("sign");
//重新生成新sign
String newsign = WXPayUtil.generateSignature(xmlToMapData, API密钥(Key),SignType.HMACSHA256);
// 验证签名验证是否成功
if(! sign.equals(newsign)) {
logger.info("微信公众号支付异步通知>>验证签名失败!");
return PayUtil.generatePayErrorReplyXML();
}
//查询return_code是否是支付成功了success
if (xmlToMapData.get("return_code").equals("SUCCESS")) {
//支付成功之后的逻辑
// 商户订单号
String out_trade_no = xmlToMapData.get("out_trade_no").toString();
// 微信支付订单号
String thrid_trade_no = xmlToMapData.get("transaction_id").toString();
// 订单金额
String total_fee = xmlToMapData.get("total_fee").toString();
//查询该订单是否存在
IdmOrder order = (IdmOrder)缓存Redis.getIdmOrderByBillNo(out_trade_no);
if (order != null) {
// 1、商户需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号,
if (!out_trade_no.equals(order.getBillNo())) {
logger.info("微信公众号支付异步通知->订单号与商户号订单不一致!" + out_trade_no);
return PayUtil.generatePayErrorReplyXML();
}
//2、校验返回的订单金额是否与商户侧的订单金额一致(查询商户后台的订单金额进行比较)
if (Integer.parseInt(PayUtil.changeY2F(order.getOrderAmount().toString())) != Integer.parseInt(total_fee)) {
logger.info("微信公众号支付异步通知->订单中的交易金额和返回的金额不一致!" + order.getOrderAmount());
return PayUtil.generatePayErrorReplyXML();
}
//更改商品订单状态...自行操作
//把商品订单存储进数据库 (然后删除缓存的数据)
//查商品库存...自行操作
//减商品库存...自行操作
//通知客户端APP(支付结果通知)
JpushService.pullMessages(idmItem.getSn(),"PAY_RESULT","success","支付结果通知"); //别名
}
//返回成功状态码给微信端
return PayUtil.generatePaySuccessReplyXML();
}
logger.info("微信公众号支付异步通知->支付结果返回的不是成功的状态码,请详查!");
return PayUtil.generatePayErrorReplyXML();
}
微信下单配置类:
import java.io.ByteArrayInputStream;
import java.io.InputStream;
/**
* 扫码支付参数配置,可用于支付和退款的参数
*/
public class MyWXPayConfig extends WXPayConfig {
private byte[] certData;
@Override
public String getAppID() {
return "应用App的ID";
}
public String getAPIKey(){
return "API密钥Key";
}
@Override
public String getMchID() {
return "商户ID";
}
@Override
public String getKey() {
return "应用秘钥(appsecret)";
}
@Override
public InputStream getCertStream() {
ByteArrayInputStream certBis = new ByteArrayInputStream(this.certData);
return certBis;
}
@Override
public int getHttpConnectTimeoutMs() {
// TODO Auto-generated method stub
return 10*1000;
}
@Override
public int getHttpReadTimeoutMs() {
// TODO Auto-generated method stub
return 20*1000;
}
@Override
public IWXPayDomain getWXPayDomain() {
return new MyWXPayDomain();
}
}
支付宝和微信封装的响应状态和解析支付接口返回的响应参数工具类:
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.internal.util.AlipaySignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
public class PayUtil {
protected static Logger logger = LoggerFactory.getLogger(PayUtil.class);
public static final int BUFFER_SIZE = 4096;
/**
* 将元为单位的转换为分 替换小数点,支持以逗号区分的金额
* @param amount
* @return
*/
public static String changeY2F(String amount) {
String currency = amount.replaceAll("\\$|\\¥|\\,", ""); // 处理包含, ¥
// 或者$的金额
int index = currency.indexOf(".");
int length = currency.length();
Long amLong = 0l;
if (index == -1) {
amLong = Long.valueOf(currency + "00");
} else if (length - index >= 3) {
amLong = Long.valueOf((currency.substring(0, index + 3)).replace(".", ""));
} else if (length - index == 2) {
amLong = Long.valueOf((currency.substring(0, index + 2)).replace(".", "") + 0);
} else {
amLong = Long.valueOf((currency.substring(0, index + 1)).replace(".", "") + "00");
}
return amLong.toString();
}
/**
* 支付结果成功时,发给微信支付的参数
* @return
*/
public static String generatePaySuccessReplyXML() {
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append("<xml>").append("<return_code><![CDATA[SUCCESS]]></return_code>")
.append("<return_msg><![CDATA[OK]]></return_msg>").append("</xml>");
return stringBuffer.toString();
}
/**
* 支付结果失败时,发给微信支付的参数
* @return
*/
public static String generatePayErrorReplyXML() {
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append("<xml>").append("<return_code><![CDATA[FAIL]]></return_code>")
.append("<return_msg><![CDATA[ERROR]]></return_msg>").append("</xml>");
return stringBuffer.toString();
}
/**
* 支付宝结果成功时,发给支付宝的参数
* @return
*/
public static String generatePaySuccessReplyParams() {
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append("success");
return stringBuffer.toString();
}
/**
* 支付宝结果失败时,发给支付宝的参数
* @return
*/
public static String generatePayErrorReplyParams() {
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append("failure");
return stringBuffer.toString();
}
/**
* 解析alipay异步通知的参数
* @param requestParams 样例:
* {
* "gmt_create": ["2017-07-14 14:38:54"],
* "charset": ["utf-8"]
* ...
* }
*/
public static Map<String, String> parseParams(Map<?, ?> requestParams){
Map<String,String> params = new HashMap<String,String>();
for (Iterator<?> iter = requestParams.keySet().iterator(); iter.hasNext();) {
String name = (String) iter.next();
String[] values = (String[]) requestParams.get(name);
String valueStr = "";
for (int i = 0; i < values.length; i++) {
valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ",";
}
params.put(name, valueStr);
}
return params;
}
/**
* 验证支付宝参数合法性
* 使用支付宝2.0SDK自带的验证方法进行验证 AlipaySignature.rsaCheckV1
* @param params
* @return
*/
public static boolean checkedParams(Map<String, String> params){
try {
//切记alipaypublickey是支付宝的公钥,请去open.alipay.com对应应用下查看。
return AlipaySignature.rsaCheckV1(params, AliPayConfig.ALI_PUBLIC_KEY, AliPayConfig.ALI_CHARSET, AliPayConfig.ALI_SIGN_TYPE);
} catch (AlipayApiException e) {
// TODO Auto-generated catch block
logger.error("---支付宝API异常>>");
e.printStackTrace();
}
return false;
}
/**
* 根据支付宝对应费率,计算用户提现费用
* 支付宝费率0.15%,费用区间:2-25,即不低于2元,上到25封顶
* @param amount
* @return
*/
public static double cunWithdrawCost(double amount){
double coust = BigDecimalUtil.mul(amount, 0.0015);
if(coust <= 2 ){
return 2;
}
if(coust >= 25){
return 25;
}
return coust;
}
public static String getAtt(HttpServletRequest request, String key){
return (String)request.getSession().getAttribute(key);
}
/**
* 将元为单位的转换为分 (乘100)
*
* @param amount
* @return
*/
public static String changeY2F(Long amount) {
return BigDecimal.valueOf(amount).multiply(new BigDecimal(100)).toString();
}
/**
* 将流转换为xml
* @param in
* @param charset
* @throws IOException
*/
public static String copyToString(InputStream in, Charset charset) throws IOException {
StringBuilder out = new StringBuilder();
InputStreamReader reader = new InputStreamReader(in, charset);
char[] buffer = new char[BUFFER_SIZE];
int bytesRead = -1;
while ((bytesRead = reader.read(buffer)) != -1) {
out.append(buffer, 0, bytesRead);
}
return out.toString();
}
}
支付(支付宝或者微信)依赖:
<!-- 支付宝3.4.49.ALL -->
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java</artifactId>
<version>3.7.4.ALL</version>
</dependency>
<!-- 微信ALL -->
<dependency>
<groupId>com.github.wxpay</groupId>
<artifactId>wxpay-sdk</artifactId>
<version>3.0.9</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.3</version>
</dependency>
<!-- 日记 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.21</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.7.21</version>
</dependency>
扩展: 微信下单调用http
工具类发送下单接口的方式
/**
* 微信下单
* @param req
* @return
* @throws Exception
*/
@RequestMapping(value="/paying",method=RequestMethod.POST)
@ResponseBody
public Map<String, String> paying(HttpServletRequest req) throws Exception{
String billNumber = req.getParameter("billNumber");
String openid = req.getParameter("openid");
//判断商户是否存在该订单..............
Map<String, String> reqData = new HashMap<>();
//商户APPid
reqData.put("appid", "商户Appid");
//随机字符串 必填-每次请求都要是新的随机字符串
reqData.put("nonce_str", WXPayUtil.generateNonceStr());//WXPayUtil.generateNonceStr()
//商品描述 必填
reqData.put("body", ##);//"咖啡"
//商户号 必填
reqData.put("mch_id", 商户ID(mch_id));
//商品订单号 必填
reqData.put("out_trade_no", order);
//支付金额(将元转换为分) 必填
reqData.put("total_fee", PayUtil.changeY2F(###));
//终端IP 不知道怎么写可以写本地127.0.1 必填
reqData.put("spbill_create_ip", "127.0.0.1");//客户端主机
//异步回调通知地址通知url必须为外网可访问的url,不能携带参数 必填
reqData.put("notify_url", ###); //"https://www.baidu.com/wxpay/notifying"
//交易类型JSAPI 必填
reqData.put("trade_type", "JSAPI");
//之前获取的openid 必填
reqData.put("openid", openid);
//商品id
reqData.put("product_id", 按需);
//签名加密方式,如果不是使用默认MD5就需要指定
reqData.put("sign_type", "HMAC-SHA256");
//生成HMAC-SHA256加密方式签名的Xml,通过httpClient发送请求得到数据
String xmlParam = WXPayUtil.generateSignedXml(reqData, "API密钥(paternerKey)",SignType.HMACSHA256);
//使用http工具类发送post请求,工具类自行找!
HttpClient httpClient=new HttpClient("https://api.mch.weixin.qq.com/pay/unifiedorder");
httpClient.setHttps(true);
httpClient.setXmlParam(xmlParam);
httpClient.post();
//获取内容
String content = httpClient.getContent();
//转换内容为map
Map<String, String> refund = WXPayUtil.xmlToMap(content);
Map<String,String> maps=new HashMap<>();
//成功下单的话 包装前端支付需要的参数
if ("SUCCESS".equals(refund.get("return_code").toString())
) {
String timeStamp=String.valueOf(WXPayUtil.getCurrentTimestamp());
String appIds=myWXPayConfig.getAppID();
String nonceStrs=UUID.randomUUID().toString().replaceAll("-", "").toUpperCase();
String prepay_id=refund.get("prepay_id").toString();
String packages="prepay_id="+prepay_id;
String signType=SignType.MD5.toString();
//App的ID
maps.put("appId", ##);
//时间戳
maps.put("timeStamp", timeStamp);
//随机字符串
maps.put("nonceStr", #);
//订单详情扩展字符串package是关键字加个S处理
maps.put("package", packages);
//签名方式 默认为MD5,支持HMAC-SHA256和MD5。注意此处需与统一下单的签名类型一致
maps.put("signType", #);
/**
* 注:这里采用 HMACSHA256进行签名,
* 因为预下单支付那里默认采用HMACSHA256,要保持一致
*/
String paySign = WXPayUtil.generateSignature(maps,"API密钥(Key)",SignType.HMACSHA256);
logger.info("paySign:"+paySign);
maps.put("return_code", "success");
maps.put("paySign", paySign);
return maps;