这是一篇关于微信支付V2版(包含JSAPI支付、Native支付和H5支付)、支付宝支付的文章,微信不知道什么时候更新了一个V3版,加密型更严格了。关于微信支付和支付宝商家的注册,本文就不说了,不了解的可以去某度一下,提前说一下微信支付必须是先注册服务号,微信支付和服务号进行绑定才能正常在网上支付。
完整项目地址: https://github.com/Q631124/qi-pay
这里先说一下微信支付,开发文档地址:https://pay.weixin.qq.com/wiki/doc/api/index.html,我们这里有一个支付页面:
// 去支付的js代码:confirm.js
function toPay() {
var gg=true;
if (gg) {
var payway =$('input[name=pay-way]:checked').val();
var phone =$('input[name=phone]').val();
var isPC =1;//默认为电脑支付
if ($('.pay-way input:checked').length <1) {
alert("请选择付款方式");
gg=false;
}else if (payway ==0) {
//电脑端微信支付
if(is_pc()){
$("#frm").attr("action","/page/payorder.do?pay_type=pc");//微信支付
gg=true;
}else{
if(is_weixin()){
//手机端微信支付
gg =false;
//appid为服务号中的APPID
window.location.href ="https://open.weixin.qq.com/connect/oauth2/authorize?appid=xxxxxxxxx&redirect_uri="+encodeURIComponent('http://127.0.0.1/pay/getWxCode.do?pay_type=wx&phone='+phone)+"&response_type=code&scope=snsapi_base&state=123#wechat_redirect";
}else{
//手机端h5支付
gg=true;
$("#frm").attr("action","/pay/getWxCode.do?pay_type=h5&code=1");
}
}
}else if (payway ==1) {
//支付宝支付判断是否在微信内部
if(is_weixin()){
gg =false;
alert("请点击右上角用手机浏览器打开该网页");
}else{
$("#frm").attr("action","/pay/alipay.do");
gg=true;
}
}
}
return gg;
}
//是否为电脑支付,是返回true
function is_pc() {
var userAgentInfo =navigator.userAgent;
var Agents = ["Android","iPhone",
"SymbianOS","Windows Phone",
"iPad","iPod"];
var flag =true;
for (var v =0;v
if (userAgentInfo.indexOf(Agents[v]) >0) {
flag =false;
break;
}
}
return flag;
}
//判断是否是微信环境
function is_weixin() {
var ua =navigator.userAgent.toLowerCase();
var isWeixin =ua.indexOf('micromessenger') != -1;
if (isWeixin) {
return true;
}else{
return false;
}
}
//PayAction.java
/**
* 获取微信支付链接
* @return
*/
@ResponseBody
@RequestMapping(value = {"/wechat"})
public Map getWeChat(User user, String code, String pay_type, HttpServletRequest request, HttpServletResponse response){
String code_url ="";
Map returnMap =new HashMap();
try {
String phone = user.getPhone();
//创建订单号
String out_trade_no = IDGeneratorUtil.getID("DD");
request.getSession().setAttribute("ORDER_NUM", out_trade_no);
request.getSession().setAttribute("phone", phone);
request.getSession().setAttribute("orderNum", out_trade_no);
String body = ClassItemUtil.getClassName(user.getClassItem());
int price = (int)(user.getPrice() *100);//价格,单位为分
String classItem = user.getClassItem();
String name = user.getName();
// 账号信息
String appid = PayConfigUtil.APP_ID;// appid
String mch_id = PayConfigUtil.MCH_ID;// 商业号
String key = PayConfigUtil.API_KEY;// key
String currTime = PayToolUtil.getCurrTime();
String timeStamp = PayToolUtil.getTimeStamp();
String nonce_str = UUID.randomUUID().toString().replace("-","").substring(0,16);
SortedMap packageParams =new TreeMap();
// 终端IP
String spbill_create_ip = getIpAddr(request);
// 回调接口
String notify_url = PayConfigUtil.NOTIFY_URL;
String trade_type ="";
String openId ="";
String scene_info ="";
if("pc".equals(pay_type)){
trade_type = PayConfigUtil.TRADE_TYPE_NATIVE;
packageParams.put("product_id",currTime);
}else if("wx".equals(pay_type)){
trade_type = PayConfigUtil.TRADE_TYPE_JSAPI;
//页面获取openId接口
String getopenid_url ="https://api.weixin.qq.com/sns/oauth2/access_token";
String param="appid="+appid+"&secret=xxxxxxxxx"+"&code="+code+"&grant_type=authorization_code";
//向微信服务器发送get请求获取openIdStr
String openIdStr = HttpRequest.sendGet(getopenid_url, param);
JSONObject json = JSONObject.parseObject(openIdStr);//转成Json格式
openId = json.getString("openid");//获取openId
packageParams.put("openid",openId);
}else if("h5".equals(pay_type)){
trade_type = PayConfigUtil.TRADE_TYPE_H5;
scene_info ="{\"h5_info\":{\"type\": \"Wap\",\"wap_url\": \"http://127.0.0.1/page/confirm.do\",\"wap_name\": \"水果大甩卖\"}}";
packageParams.put("scene_info",scene_info);
}
String params = out_trade_no +"," + phone +"," + name +"," + classItem +"," + user.getPrice();
String attach = java.net.URLEncoder.encode(params,"UTF-8");
packageParams.put("appid", appid);
packageParams.put("mch_id", mch_id);
packageParams.put("nonce_str", nonce_str);
packageParams.put("body", body);//(调整为自己的名称)
packageParams.put("out_trade_no", out_trade_no);
packageParams.put("total_fee", price);//价格的单位为分
packageParams.put("spbill_create_ip", spbill_create_ip);
packageParams.put("notify_url", notify_url);
packageParams.put("trade_type", trade_type);
packageParams.put("attach",attach);
packageParams.put("sign_type","MD5");
String sign = PayToolUtil.createSign("UTF-8", packageParams,key);
packageParams.put("sign", sign);
String requestXML = PayToolUtil.getRequestXml(packageParams);
String resXml = HttpUtil.postData(PayConfigUtil.UFDODER_URL, requestXML);
Map map = XMLUtil4jdom.doXMLParse(resXml);
String return_code = (String)map.get("return_code");
String return_msg = (String)map.get("return_msg");
String prepay_id ="";
String mweb_url ="";
if("SUCCESS".equals(return_code)){
code_url = (String) map.get("code_url");
if("wx".equals(pay_type)){
prepay_id = (String) map.get("prepay_id");
}else if("h5".equals(pay_type) &&"OK".equals(return_msg)){
mweb_url = (String) map.get("mweb_url");//调微信支付接口地址
String redirect_url ="http://127.0.0.1/";
String redirect_urlEncode = URLEncoder.encode(redirect_url,"utf-8");//对上面地址urlencode
mweb_url = mweb_url +"&redirect_url=" + redirect_urlEncode;
returnMap.put("mweb_url",mweb_url);
}
}else {
throw new Exception("返回失败");
}
if("wx".equals(pay_type)){
returnMap.put("appId",appid);
returnMap.put("timeStamp",timeStamp);
returnMap.put("nonceStr",nonce_str);
returnMap.put("package","prepay_id=" + prepay_id);
returnMap.put("signType","MD5");
SortedMap returnParams =new TreeMap();
returnParams.put("appId",appid);
returnParams.put("timeStamp",timeStamp);
returnParams.put("nonceStr",nonce_str);
returnParams.put("package","prepay_id=" + prepay_id);
returnParams.put("signType","MD5");
String paySign = PayToolUtil.createSign("UTF-8", returnParams,key);
returnMap.put("paySign",paySign);
}
returnMap.put("pay_type",pay_type);
returnMap.put("code_url",code_url);
returnMap.put("out_trade_no", out_trade_no);
}catch (Exception e){
e.printStackTrace();
}
return returnMap;
}
PC端支付,生成二维码扫码支付,请求/pay/wechat.do返回支付连接,使用/pay/createQRCode.do生成二维码
$.ajax({
url:'/pay/wechat.do',
type:'post',
data: {
phone:phone,
pay_type:pay_type,
name:name,
phone:phone,
price:price,
classItem:classItem
},
async:false,
dataType:'JSON',
success:function (data) {
if(data !=null){
var code_url = data.code_url;
if(code_url !=null){
$('#wx_pay_qrcode img').attr("src","/pay/createQRCode.do?code_url="+code_url);
var interval=setInterval(function(){
$.ajax({
url:'/pay/paystate.do',
type:'post',
data:{
out_trade_no: data.out_trade_no
},
success:function(dt){
if(dt=="ok"){
clearInterval(interval);
window.location.href ="http://127.0.0.1/page/paysuccess.do";
}
}
});
},3000);
}
}
}
});
这里需要导入jar包:Qrcode-1.0.jar,详情见文章末尾连接
@RequestMapping({"/createQRCode"})
public void createQRCode(String code_url,HttpServletResponse response) {
try {
Qrcode handler =new Qrcode();
handler.setQrcodeErrorCorrect('M');
handler.setQrcodeEncodeMode('B');
handler.setQrcodeVersion(7);
byte[] contentBytes = code_url.getBytes("UTF-8");
int imgSize =280;
BufferedImage bufImg =new BufferedImage(imgSize, imgSize,1);
Graphics2D gs = bufImg.createGraphics();
gs.setBackground(Color.WHITE);
gs.clearRect(0,0, imgSize, imgSize);
gs.setColor(Color.BLACK);
int pixoff =2;
if ((contentBytes.length >0) && (contentBytes.length <800)) {
boolean[][] codeOut = handler.calQrcode(contentBytes);
for (int i =0; i < codeOut.length; i++) {
for (int j =0; j < codeOut.length; j++) {
if (codeOut[j][i]) {
gs.fillRect(j *6 + pixoff, i *6 + pixoff,6,6);
}
}
}
}
gs.dispose();
bufImg.flush();
ImageIO.write(bufImg,"jpg", response.getOutputStream());
}catch (Exception e) {
e.printStackTrace();
}
}
微信内和手机H5页面请求/pay/getWxCode.do,到wxcode.jsp文件中调用js
$.ajax({
url:'/pay/wechat.do',
type:'post',
data: {
phone:phone,
pay_type:pay_type,
code:code,
name:name,
phone:phone,
price:price,
classItem:classItem
},
dataType:'JSON',
success:function (data) {
if(data !=null){
if(data.pay_type =="wx"){
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{
onBridgeReady();
}
}else if(data.pay_type =="h5"){
window.location.href = data.mweb_url;
}
function onBridgeReady() {
WeixinJSBridge.invoke(
'getBrandWCPayRequest', {
"appId": data.appId,//公众号名称,由商户传入
"timeStamp": data.timeStamp,//时间戳
"nonceStr": data.nonceStr,//随机串
"package": data.package,
"signType":"MD5",//微信签名方式:
"paySign": data.paySign //微信签名
},
function (res) {
if (res.err_msg =="get_brand_wcpay_request:ok") {
window.location.href ="/page/paysuccess.do";
}else{
alert("支付失败");
}
});
}
}
}
});
微信支付调用成功后都会返回通知页面,在页面上请求判断是否支付成功,在result_code返回SUCCESS是添加自己的业务逻辑
/**
* 微信支付成功返回
* @param request
* @param response
*/
@RequestMapping(value="/wxSuccess")
public void wxSuccess(HttpServletRequest request, HttpServletResponse response) {
String[] params =null;
BufferedReader in =null;
InputStream inputStream =null;
BufferedOutputStream out =null;
try{
//读取参数
StringBuffer sb =new StringBuffer();
inputStream = request.getInputStream();
String s ="";
in =new BufferedReader(new InputStreamReader(inputStream,"UTF-8"));
while ((s = in.readLine()) !=null){
sb.append(s);
}
in.close();
inputStream.close();
//解析xml成map
Map map =new HashMap();
map = XMLUtil4jdom.doXMLParse(sb.toString());
//过滤空 设置 TreeMap
SortedMap packageParams =new TreeMap();
Iterator it = map.keySet().iterator();
while (it.hasNext()) {
String parameter = (String) it.next();
String parameterValue = map.get(parameter);
String value ="";
if(null != parameterValue) {
value = parameterValue.trim();
}
packageParams.put(parameter, value);
}
// 账号信息
String key = PayConfigUtil.API_KEY;//key
//判断签名是否正确
if(PayToolUtil.isTenpaySign("UTF-8", packageParams,key)) {
String resXml ="";
if("SUCCESS".equals((String)packageParams.get("result_code"))){
String attach = (String)packageParams.get("attach");
if(!"".equals(attach) && attach !=null){
attach = java.net.URLDecoder.decode(attach,"UTF-8");
params = attach.split(",");
}
//自己的业务逻辑
//通知微信.异步确认成功.必写.不然会一直通知后台.八次之后就认为交易失败了.
resXml ="<xml>" +""
+"<return_msg><![CDATA[OK]]></return_msg>" +"</xml> ";
}else {
resXml ="<xml>" +""
+"<return_msg><![CDATA[报文为空]]></return_msg>" +"</xml> ";
}
out =new BufferedOutputStream(response.getOutputStream());
out.write(resXml.getBytes());
out.flush();
out.close();
}else{
logger.info("通知签名验证失败");
}
}catch (Exception e) {
e.printStackTrace();
}finally {
if(out !=null){
try {
out.close();
}catch (IOException e) {
e.printStackTrace();
}
}
if(inputStream !=null){
try {
inputStream.close();
}catch (IOException e) {
e.printStackTrace();
}
}
if(in !=null){
try {
in.close();
}catch (IOException e) {
e.printStackTrace();
}
}
}
}
以下为支付宝支付流程,支付宝支付相对于微信支付是简单的,支付宝在PC端会调用自己的支付页面,无需自己生成二维码,在手机端(微信内部除外)会自动调起支付宝客户端进行支付,支付宝开发文档地址:https://open.alipay.com/developmentDocument.htm
//支付宝支付,需要正确的参数才能调用成功
@RequestMapping(value={"/alipay"})
public void createQRCode(User user, HttpServletRequest request, HttpServletResponse response) {
try {
String orderNum = IDGeneratorUtil.getID("ZX");
request.getSession().setAttribute("ORDER_NUM", orderNum);//订单编号
request.getSession().setAttribute("phone", user.getPhone());
request.getSession().setAttribute("orderNum", orderNum);
String subject = user.getClassItem();
float price = user.getPrice();
String params = orderNum +"," + user.getPhone() +"," + user.getName() +"," + user.getClassItem() +"," + price;
//支付携带参数,会原样返回
String passback_params = java.net.URLEncoder.encode(params,"UTF-8");
AlipayClient alipayClient =new DefaultAlipayClient("https://openapi.alipay.com/gateway.do",
"xxxxxxxx",
"xxxxxxxxxxxxxxxxxxxxxx",
"json","utf-8",
"xxxxxxxxxxxxxxxxx",
"RSA2");
AlipayTradePagePayRequest alipayRequest =new AlipayTradePagePayRequest();
//通知地址
alipayRequest.setReturnUrl("http://127.0.0.1/");
//支付成功后调用地址,支付成功后的业务要写在该逻辑中
alipayRequest.setNotifyUrl("https://127.0.0.1/page/success.do");
alipayRequest.setBizContent("{ \"out_trade_no\":\"" + orderNum +"\"," +
" \"product_code\":\"FAST_INSTANT_TRADE_PAY\"," +" \"total_amount\":\"" + price +"\"," +
" \"subject\":\"" + subject +"\"," +" \"body\":\"" + subject +"\"," +
" \"passback_params\":\" " + passback_params +" \"," +
" \"extend_params\":{" +" \"sys_service_provider_id\":\"xxxxxxxx\"" +" }," +
" \"timeout_express\":\"30m\"" +" }");
String form = ((AlipayTradePagePayResponse)alipayClient.pageExecute(alipayRequest)).getBody();
response.setContentType("text/html;charset=utf-8");
response.getWriter().write(form);
response.getWriter().flush();
response.getWriter().close();
}catch (Exception e) {
e.printStackTrace();
}
}
支付宝接口文档中有两个参数:return_url、notify_url,区别是return_url为网页重定向通知,是用户付款成功后立刻返回的页面,主要是为了让用户看到自己已经支付成功了,支付宝不能保证其到达率,如果把支付成功的业务逻辑写到该页面下,不能保证业务逻辑正常执行;notify_url是支付宝服务器通知,在支付成功后支付宝进行的服务器请求,如果请求不成功,会在一定时间内陆续请求八次,提高了通知到达率,可以保证业务逻辑的执行。请求成功后必须以流的方式返回success或者fail,不返回支付宝会默认请求失败,会继续发起请求
/**
* 支付宝支付成功返回
* @param request
* @param response
*/
@RequestMapping("/success")
public void success(HttpServletRequest request, HttpServletResponse response) {
String passback_params = request.getParameter("passback_params");
String[] params =null;
try {
if(!"".equals(passback_params) && passback_params !=null){
passback_params = java.net.URLDecoder.decode(passback_params,"UTF-8");
params = passback_params.split(",");
}
addUser(params,request);
response.getWriter().print("success");
}catch (Exception e) {
e.printStackTrace();
try {
response.getWriter().print("fail");
}catch (IOException e1) {
e1.printStackTrace();
}
}
}
上面的内容还相对较乱,如果看的不是很明白,请访问github地址:https://github.com/Q631124/qi-pay里面有完整的代码逻辑。