近期项目要求打通支付宝支付和微信支付,所以趟了一趟接入支付的浑水:
支付宝接入流程
微信支付流程
分析完之后就对照官方的文档开始行动了
一:支付宝支付
- 打开支付宝
- 选择商家平台
- 选择手机网站支付如下操作
https://b.alipay.com/signing/productDetail.htm?productId=I1011000290000001001
选择手机网站支付,填写资料
填写后等待审核通过
然后进入开放平台->开发者中心->网页&移动应用,
然后创建应用
然后点击进入查看详情
然后添加手机网站支付功能
然后点击应用信息访问进行开发配置
在配置上图的回调地址和密钥,接下来就是开发了
这里有官方的demo
https://docs.open.alipay.com/270/106291/
二 、微信支付
接下来介绍微信支付,微信支付没有官方的demo
比较蛋疼,踩过很多的坑
支付宝支付相对接入比较容易,微信支付需要分微信内打开和微信外打开,微信内走公众号支付,微信外走H5支付,刚开始接入的时候发现H5链接在微信内打开支付不了才知道微信内强制走的是公众号支付,这一点不知道可能让你的功能在提测的时候不通过,或者延期
先到微信公众平台
申请开通公众号支付和H5支付
然后配置
添加公众号授权,然后就是接入开发了
微信或者支付宝下单/支付回调
PayController.java
package com.lebo.pay.controller;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.TimeUnit;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.alibaba.fastjson.JSON;
import com.lebo.pay.controller.BaseController;
import com.lebo.pay.entity.OpenIdClass;
import com.lebo.pay.entity.Order;
import com.lebo.pay.entity.WechatUnifiedOrder;
import com.lebo.pay.service.AlipayTradeService;
import com.lebo.pay.service.WechatTradeService;
import com.lebo.pay.service.impl.OrderService;
import com.lebo.util.PageData;
import com.lebo.util.RedisUtil;
import com.lebo.util.ResultUtil;
import com.lebo.util.StatusEnum;
import com.lebo.util.StringUtil;
import com.lebo.util.UIDUtil;
import com.lebo.util.XmlUtil;
/**
*
* @ClassName: PayController
* @author chenrui
* @QQ: 914221084
* @date: 2018年6月12日 下午8:46:18
*/
@Controller
@RequestMapping("/pay")
public class PayController extends BaseController{
private static Logger logger = Logger.getLogger(PayController.class);
@Autowired
private AlipayTradeService alipayTradeService;
@Autowired
private WechatTradeService wechatTradeService;
@Autowired
private OrderService orderService;
/*
* 微信或者支付宝支付下单
*/
@RequestMapping(value="/unifiedOrder")
@ResponseBody
public Object unifiedOrder(Order order,Model model){
ResultUtil reUtil = new ResultUtil(StatusEnum.SUCCESS.getCode(), StatusEnum.SUCCESS.getMessage());
String outTradeNo = order.getOutTradeNo();
if(StringUtils.isBlank(outTradeNo)){
reUtil.setCodeAndMessage(StatusEnum.ORDERNO_IS_CANNOT_NULL.getCode(), StatusEnum.ORDERNO_IS_CANNOT_NULL.getMessage());
return reUtil;
}
if("P".equals(RedisUtil.getStr(outTradeNo+"status"))){
reUtil.setCodeAndMessage(StatusEnum.ORDER_HAVE_PAY.getCode(), StatusEnum.ORDER_HAVE_PAY.getMessage());
return reUtil;
}
order.setStatus("w");
if("w".equals(RedisUtil.getStr(outTradeNo+"status"))){
String orderStr = (String) RedisUtil.getStr("order"+outTradeNo);
if(StringUtils.isNoneBlank(orderStr)){
TreeMap<String,String> prepareH5Pay = (TreeMap<String, String>) JSON.parseObject(orderStr, TreeMap.class);
reUtil.setData(prepareH5Pay);
return reUtil;
}
}
if(StringUtils.isNotBlank(order.getIp())){
if(StringUtil.isInnerIp(order.getIp())){
reUtil.setCodeAndMessage(StatusEnum.IP_IS_WRONG.getCode(), StatusEnum.IP_IS_WRONG.getMessage());
}
}
if(RedisUtil.tryLock(outTradeNo,15)){
try {
if(StringUtils.isBlank(order.getPayType())){
order.setPayType("weChat");
}
order.setPayId(UIDUtil.getUUID());
orderService.insertOrder(order);
RedisUtil.putObj("order"+outTradeNo,JSON.toJSONString(order),(int) TimeUnit.DAYS.toSeconds(10));
} catch (Exception e1) {
reUtil.setCodeAndMessage(StatusEnum.PREPARORDER_FAIL.getCode(), StatusEnum.PREPARORDER_FAIL.getMessage());
logger.error("支付失败:params"+JSON.toJSONString(order)+","+e1.getMessage());
return reUtil;
}
if("weChat".equals(order.getPayType())){
WechatUnifiedOrder wechatOrder = new WechatUnifiedOrder();
String isWxbrowser = (String) getRequest().getParameter("isWxbrowser");
if("true".equals(isWxbrowser)){
String code = (String) getRequest().getParameter("code");
logger.info("支付失败:code="+code+"isWxbrowser="+isWxbrowser);
wechatOrder.setTrade_type("JSAPI");
try {
OpenIdClass openData = wechatTradeService.getOpenId(code);
if(openData==null){
reUtil.setCodeAndMessage(StatusEnum.PREPARORDER_FAIL.getCode(), StatusEnum.PREPARORDER_FAIL.getMessage());
return reUtil;
}
wechatOrder.setOpenid(openData.getOpenid());
} catch (Exception e) {
reUtil.setCodeAndMessage(StatusEnum.PREPARORDER_FAIL.getCode(), StatusEnum.PREPARORDER_FAIL.getMessage());
logger.error("获取openid失败:params"+JSON.toJSONString(order)+","+e.getMessage());
return reUtil;
}
}
wechatOrder.setBody(order.getBody());
wechatOrder.setDetail(order.getDetail());
wechatOrder.setGoods_tag(order.getGoodsTag());
wechatOrder.setOut_trade_no(outTradeNo);
wechatOrder.setFee_type("CNY");
wechatOrder.setTotal_fee((int)(order.getTotalAmount()*100));
if(StringUtils.isBlank(order.getIp())){
wechatOrder.setSpbill_create_ip("119.139.197.202");
}else{
wechatOrder.setSpbill_create_ip(order.getIp());
}
wechatOrder.setTime_start(System.currentTimeMillis()+"");
wechatOrder.setLimit_pay("cera");
wechatOrder.setTime_expire(order.getTimeExpire());
//TODO 生成预付单
try {
TreeMap<String,String> data = wechatTradeService.unifiedOrderRequest(wechatOrder);
if(data!=null) {
reUtil.setData(data);
}else {
reUtil.setCodeAndMessage(StatusEnum.PREPARORDER_FAIL.getCode(), StatusEnum.PREPARORDER_FAIL.getMessage());
}
} catch (Exception e) {
reUtil.setCodeAndMessage(StatusEnum.PREPARORDER_EXCEPTION.getCode(), StatusEnum.PREPARORDER_EXCEPTION.getMessage());
logger.error("支付失败:params"+JSON.toJSONString(order)+","+e.getMessage());
}
}else{
try {
Map<String,String> paraMap = new HashMap<String,String>();
paraMap.put("out_trade_no",outTradeNo);
paraMap.put("total_amount",String.valueOf(order.getTotalAmount()));
paraMap.put("subject",order.getBody());
paraMap.put("body",order.getDetail());
paraMap.put("product_code","QUICK_WAP_PAY");
String resultStr = alipayTradeService.TradeWapPayRequest(paraMap);
if(resultStr!=null) {
reUtil.setData(resultStr);
}else {
reUtil.setCodeAndMessage(StatusEnum.PREPARORDER_FAIL.getCode(), StatusEnum.PREPARORDER_FAIL.getMessage());
}
} catch (Exception e) {
reUtil.setCodeAndMessage(StatusEnum.PREPARORDER_EXCEPTION.getCode(), StatusEnum.PREPARORDER_EXCEPTION.getMessage());
logger.error("支付失败:params"+JSON.toJSONString(order)+","+e.getMessage());
}
}
}else{
reUtil.setCodeAndMessage(StatusEnum.PREPARORDER_IS_PRODUCTING.getCode(), StatusEnum.PREPARORDER_IS_PRODUCTING.getMessage());
}
logger.info("接口結果:json:"+JSON.toJSONString(reUtil));
return reUtil;
}
/*
* 微信支付成功回调
*/
@RequestMapping(value="/wechatNotify")
@ResponseBody
public Object wechatNotify(HttpServletRequest request){
PageData pd = new PageData(request);
Map<String,String> return_data = new HashMap<String,String>();
try {
logger.info("access to wechatNotify");
if(wechatTradeService.verifyNotify(request)) {
return_data.put("return_code", "SUCCESS");
return_data.put("return_msg", "OK");
}else {
return_data.put("return_code", "FAIL");
return_data.put("return_msg", "return_code不正确");
}
} catch (Exception e) {
return_data.put("return_code", "FAIL");
return_data.put("return_msg", "支付回调异常");
logger.error("支付失败:params"+JSON.toJSONString(pd)+","+e.getMessage());
}
return XmlUtil.GetMapToXML(return_data);
}
/*
* 支付宝支付成功回调
*/
@RequestMapping(value="/alipayNotify")
@ResponseBody
public Object alipayNotify(HttpServletRequest request){
PageData pd = new PageData(request);
logger.info("access to alipayNotify");
String result = "success";
try {
if(alipayTradeService.verifyNotify(request)) {
logger.info("支付成功:params"+JSON.toJSONString(pd));
}else {
result = "fail";
logger.error("支付失败:params"+JSON.toJSONString(pd)+",支付回调失败");
}
} catch (Exception e) {
result = "fail";
logger.error("支付失败:params"+JSON.toJSONString(pd)+","+e.getMessage());
}
return result;
}
/*
* 获取公众号相关的用户信息
*/
@RequestMapping(value="/getOpenid")
@ResponseBody
public Object getOpenid(HttpServletRequest request){
PageData pd = new PageData(request);
ResultUtil reUtil = new ResultUtil(StatusEnum.SUCCESS.getCode(), StatusEnum.SUCCESS.getMessage());
Map<String, Object> data = new HashMap<String, Object>();
logger.info("access to getOpenid");
String url = request.getRequestURI();
logger.info("request url : " + url);
Enumeration<String> em = request.getParameterNames();
while (em.hasMoreElements()) {
String nameParam = em.nextElement();
String valueParam = request.getParameter(nameParam);
data.put(nameParam, valueParam);
logger.info(nameParam + " = " + valueParam);
}
String result = "success";
try {
if(StringUtils.isBlank(pd.getString("code"))){
reUtil.setCodeAndMessage(StatusEnum.VALIDATION_FAIL.getCode(), StatusEnum.VALIDATION_FAIL.getMessage());
return reUtil;
}
OpenIdClass openData = wechatTradeService.getOpenId(pd.getString("code"));
if(openData!=null) {
data.put("openid", openData.getOpenid());
data.put("access_token", openData.getAccess_token());
data.put("refresh_token", openData.getRefresh_token());
data.put("scope", openData.getScope());
data.put("unionid", openData.getUnionid());
reUtil.setData(data);
}else {
reUtil.setCodeAndMessage(StatusEnum.PREPARORDER_FAIL.getCode(), StatusEnum.PREPARORDER_FAIL.getMessage());
}
} catch (Exception e) {
reUtil.setCodeAndMessage(StatusEnum.PREPARORDER_EXCEPTION.getCode(), StatusEnum.PREPARORDER_EXCEPTION.getMessage());
logger.error("获取openId失败:params"+JSON.toJSONString(pd)+","+e.getMessage());
}
return result;
}
/*
* 获取订单状态
*/
@RequestMapping(value="/getOrderStatus")
@ResponseBody
public Object getOrderStatus(HttpServletRequest request){
PageData pd = new PageData(request);
ResultUtil reUtil = new ResultUtil(StatusEnum.SUCCESS.getCode(), StatusEnum.SUCCESS.getMessage());
Map<String, Object> data = new HashMap<String, Object>();
String outTradeNo = pd.getString("outTradeNo");
if(StringUtils.isBlank(pd.getString("outTradeNo"))){
reUtil.setCodeAndMessage(StatusEnum.VALIDATION_FAIL.getCode(), StatusEnum.VALIDATION_FAIL.getMessage());
return reUtil;
}
data.put("status", RedisUtil.getStr(outTradeNo+"status"));
reUtil.setData(data);
return reUtil;
}
}
微信支付service
WechatTradeServiceImpl .java
package com.lebo.pay.service.impl;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
import com.lebo.pay.config.WechatConfig;
import com.lebo.pay.entity.AccessToken;
import com.lebo.pay.entity.OpenIdClass;
import com.lebo.pay.entity.Order;
import com.lebo.pay.entity.WechatPayNotify;
import com.lebo.pay.entity.WechatRefund;
import com.lebo.pay.entity.WechatRefundQuery;
import com.lebo.pay.entity.WechatUnifiedOrder;
import com.lebo.pay.entity.WxTicket;
import com.lebo.pay.service.WechatTradeService;
import com.lebo.util.HttpUtil;
import com.lebo.util.RedisUtil;
import com.lebo.util.SignUtil;
import com.lebo.util.WebUtils;
import com.lebo.util.XmlUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.TimeUnit;
import javax.servlet.http.HttpServletRequest;
/**
* 微信交易
* @author chenrui
* @date 2018/06/11
*/
@Service
public class WechatTradeServiceImpl implements WechatTradeService{
private static Logger logger = LoggerFactory.getLogger(WechatTradeServiceImpl.class);
@Autowired
private OrderService orderService;
/**
* 微信统一下单
* @param unifiedOrder 要下单的内容
* @return 返回H5下单请求需要内容
*/
public TreeMap<String,String> unifiedOrderRequest(WechatUnifiedOrder unifiedOrder){
WechatUnifiedOrder.Response response = WechatConfig.getInstance().unifiedOrder(unifiedOrder);
if (response.getResult_code().equals(WechatConfig.SUCCESS_REQUEST)){
TreeMap<String,String> prepareH5Pay = new TreeMap<String, String>();
prepareH5Pay.put("appId", WechatConfig.APP_ID);
prepareH5Pay.put("nonceStr", WechatConfig.getInstance().nonce_str(16));
if("JSAPI".equals(unifiedOrder.getTrade_type())){
prepareH5Pay.put("package", "prepay_id="+response.getPrepay_id());
}else{
prepareH5Pay.put("partnerId", WechatConfig.MCH_ID);
prepareH5Pay.put("package", "Sign=WXPay");
prepareH5Pay.put("prepayId",response.getPrepay_id());
}
prepareH5Pay.put("signType", "MD5");
prepareH5Pay.put("timeStamp", String.valueOf(System.currentTimeMillis() / 1000));
prepareH5Pay.put("sign", WechatConfig.getInstance().sign(prepareH5Pay));
prepareH5Pay.put("mweburl",response.getMweb_url());
RedisUtil.putObj("order"+unifiedOrder.getOut_trade_no(), JSON.toJSONString(prepareH5Pay), 120);
RedisUtil.putObj(unifiedOrder.getOut_trade_no()+"status", "w", 120);
return prepareH5Pay;
}
return null;
}
/**
* 微信退款请求
* @param refund 退款请求参数
* @return 返回参数(同步接口,直接返回),只有return_code和result_code都成功则退款成功
*/
public WechatRefund.Response refundRequest(WechatRefund refund){
WechatRefund.Response response = WechatConfig.getInstance().refund(refund);
return response;
}
/**
* 微信退款查询请求
* @param refund 退款请求参数
* @return 返回参数(同步接口,直接返回),只有return_code和result_code都成功则查询成功
*/
public WechatRefundQuery.Response refundQueryRequest(WechatRefundQuery refund){
WechatRefundQuery.Response response = WechatConfig.getInstance().refundQuery(refund);
return response;
}
/**
* 微信回调验签
* @param request 回调请求
* @return true成功
*/
@SuppressWarnings("unchecked")
public boolean verifyNotify(HttpServletRequest request){
try {
InputStream inputStream = request.getInputStream();
WechatPayNotify notice = XmlUtil.xmlToBean(inputStream, WechatPayNotify.class);
//更新预付单状态
if (notice == null) return false;
logger.debug("微信回调参数:"+ JSON.toJSONString(notice));
String sign = WechatConfig.getInstance().sign(SignUtil.bean2TreeMap(notice));
boolean ischeck = sign.equals(notice.getSign());
if(ischeck){
RedisUtil.putObj(notice.getOut_trade_no()+"status", "P",(int) TimeUnit.DAYS.toSeconds(10));
Order order = new Order();
order.setOutTradeNo(notice.getOut_trade_no());
order.setStatus("p");
try {
orderService.updateOrder(order);
order = (Order) RedisUtil.getObj("order"+notice.getOut_trade_no());
if(order==null){
order =orderService.getOrder(notice.getOut_trade_no());
}
Map<String,Object> parameterMap = new HashMap<String,Object>();
parameterMap.put("outTradeNo", notice.getOut_trade_no());
parameterMap.put("userId",order.getUserId());
parameterMap.put("payType","weChat");
String result = WebUtils.post(order.getNotifyUrl(), parameterMap);
Map<String,String> resultMap = (Map<String, String>) JSON.parseObject(result, Map.class);
logger.info(order.getNotifyUrl()+"接口結果:json:"+JSON.toJSONString(result));
if("00".equals(resultMap.get("code"))){
ischeck = true;
}else{
ischeck = false;
}
} catch (Exception e) {
logger.error("订单更新失败:"+ JSON.toJSONString(notice));
ischeck = false;
}
logger.debug("微信验签结果:"+ischeck);
}
return ischeck;
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
// 获取access_token的接口地址(GET) 限200(次/天)
public final static String access_token_url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";
public final static String ticket_url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=wx_card";
public final static String auth_url = "https://api.weixin.qq.com/card/invoice/getauthurl?access_token=ACCESS_TOKEN";
/**
* 获取access_token
*
* @param appid 凭证
* @param appsecret 密钥
* @return
*/
public static AccessToken getAccessToken() {
AccessToken accessToken = null;
String requestUrl = ticket_url.replace("ACCESS_TOKEN", WechatConfig.APP_ID).replace("APPSECRET", WechatConfig.APP_SECRET);
String jsonStr = WebUtils.get(requestUrl, null) ;
// 如果请求成功
if (null != jsonStr) {
try {
JSONObject jsonObject = JSONObject.parseObject(jsonStr);
accessToken = new AccessToken();
accessToken.setToken(jsonObject.getString("access_token"));
accessToken.setExpiresIn(jsonObject.getIntValue("expires_in"));
} catch (JSONException e) {
accessToken = null;
// 获取token失败
logger.error("获取token失败 errcode:{} errmsg:{}",jsonStr);
}
}
return accessToken;
}
public static WxTicket getTickect(AccessToken accessToken) {
WxTicket wxTicket = null;
if(accessToken==null){
logger.error("获取wxTicket失败 ");
return wxTicket;
}
String requestUrl = ticket_url.replace("ACCESS_TOKEN", accessToken.getToken());
String jsonStr = WebUtils.get(requestUrl, null) ;
// 如果请求成功
if (null != jsonStr) {
try {
JSONObject jsonObject = JSONObject.parseObject(jsonStr);
wxTicket = new WxTicket();
wxTicket.setTicket(jsonObject.getString("ticket"));
wxTicket.setExpiresIn(jsonObject.getIntValue("expires_in"));
} catch (JSONException e) {
wxTicket = null;
// 获取token失败
logger.error("获取token失败 errcode:{} errmsg:{}",jsonStr);
}
}
return wxTicket;
}
public String getAuth(Order order) {
String auth = null;
AccessToken accessToken = getAccessToken();
WxTicket wxTicket = getTickect(accessToken) ;
if(wxTicket==null){
logger.error("获取wxTicket失败 ");
return auth;
}
String requestUrl = auth_url.replace("ACCESS_TOKEN", accessToken.getToken());
Map<String, Object> params = new HashMap<String, Object>();
params.put("s_pappid", "");//开票平台在微信的标识号,商户需要找开票平台提供
params.put("order_id", order.getOutTradeNo());//订单id,在商户内单笔开票请求的唯一识别号,
params.put("money", (int)(order.getTotalAmount()*100));//订单金额,以分为单位
params.put("timestamp", String.valueOf(System.currentTimeMillis() /1000));//时间戳
params.put("source", "wap");//开票来源,app:app开票,web:微信h5开票,wxa:小程序开发票,wap:普通网页开票
params.put("redirect_url", "");//授权成功后跳转页面。本字段只有在source为H5的时候需要填写,引导用户在微信中进行下一步流程。app开票因为从外部app拉起微信授权页,授权完成后自动回到原来的app,故无需填写。
params.put("ticket", wxTicket.getTicket());//从上一环节中获取
params.put("type", "1");//授权类型,0:开票授权,1:填写字段开票授权,2:领票授权
String jsonStr = WebUtils.post(requestUrl, params);
// 如果请求成功
if (null != jsonStr) {
try {
JSONObject jsonObject = JSONObject.parseObject(jsonStr);
auth = jsonObject.getString("auth_url");
} catch (JSONException e) {
wxTicket = null;
// 获取token失败
logger.error("获取auth失败 errcode:{} errmsg:{}",jsonStr);
}
}
return auth;
}
@Override
public OpenIdClass getOpenId(String code) throws Exception {
logger.info("************************getOpenId*********************");
logger.info("+++++++++++" + code + "++++++++++");
if (code != null) {
// 狗日的微信又是必须要https请求,千万不要搞错了
String url = "https://api.weixin.qq.com/sns/oauth2/access_token?"
+ "appid="+WechatConfig.APP_ID
+ "&secret="+WechatConfig.OFFICAL_ACCOUNT_SECRET + "&code="
+ code + "&grant_type=authorization_code";
logger.info("url="+url);
String returnData = HttpUtil.loadJSON(url);
logger.info("returnData="+returnData + "################");
OpenIdClass openIdClass = JSON.parseObject(returnData,OpenIdClass.class);
if (openIdClass.getOpenid() != null) {
return openIdClass;
}
}
return null;
}
}
支付宝支付service
AlipayTradeServiceImpl.java
package com.lebo.pay.service.impl;
import com.alibaba.fastjson.JSON;
import com.alipay.api.AlipayApiException;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradeRefundRequest;
import com.alipay.api.request.AlipayTradeWapPayRequest;
import com.alipay.api.response.AlipayTradeRefundResponse;
import com.lebo.pay.config.AliPayConfig;
import com.lebo.pay.entity.Order;
import com.lebo.pay.service.AlipayTradeService;
import com.lebo.util.RedisUtil;
import com.lebo.util.SignUtil;
import com.lebo.util.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.TimeUnit;
import javax.servlet.http.HttpServletRequest;
/**
* 支付宝交易类
* @author chenrui
* @date 2018/06/11
*/
@Service
public class AlipayTradeServiceImpl implements AlipayTradeService{
private Logger logger = LoggerFactory.getLogger(AlipayTradeServiceImpl.class);
@Autowired
private OrderService orderService;
/**
* web支付下单并支付(web支付在安卓中是可以直接唤醒支付宝APP的)
* url https://doc.open.alipay.com/doc2/detail.htm?treeId=203&articleId=105463&docType=1#s1
* @return web支付的表单
*/
public String TradeWapPayRequest(Map<String, String> sParaTemp){
AlipayTradeWapPayRequest alipayRequest = new AlipayTradeWapPayRequest();
alipayRequest.setReturnUrl(AliPayConfig.RETURN_URL);
alipayRequest.setNotifyUrl(AliPayConfig.PAY_NOTIFY);
// 待请求参数数组
sParaTemp.put("seller_id",AliPayConfig.SELLER_ID);
alipayRequest.setBizContent(JSON.toJSONString(sParaTemp));
String form = "";
try {
form = AliPayConfig.getInstance().pageExecute(alipayRequest).getBody();
} catch (AlipayApiException e) {
logger.error("支付宝构造表单失败",e);
}
logger.debug("支付宝支付表单构造:"+form);
return form;
}
/**
* 申请退款
* @param sParaTemp 退款参数
* @return true成功,回调中处理
* 备注:https://doc.open.alipay.com/docs/api.htm?spm=a219a.7629065.0.0.3RjsEZ&apiId=759&docType=4
*/
public boolean tradeRefundRequest(Map<String, ?> sParaTemp) throws AlipayApiException {
AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
request.setReturnUrl(AliPayConfig.RETURN_URL);
request.setNotifyUrl(AliPayConfig.REFUND_NOTIFY);
// 待请求参数数组
request.setBizContent(JSON.toJSONString(sParaTemp));
AlipayTradeRefundResponse response = AliPayConfig.getInstance().execute(request);
logger.debug("支付宝退货结果:"+response.isSuccess());
return response.isSuccess();
}
/**
* 支付宝回调验签
* @param request 回调请求
* @return true成功
* 备注:验签成功后,按照支付结果异步通知中的描述(二次验签接口,貌似称为历史接口了)
*/
public boolean verifyNotify(HttpServletRequest request) throws AlipayApiException {
Map<String,String> paranMap = SignUtil.request2Map(request);
logger.debug("支付宝回调参数:"+paranMap.toString());
boolean isVerify = false;
if (AliPayConfig.SUCCESS_REQUEST.equals(paranMap.get("trade_status")) || AliPayConfig.TRADE_CLOSED.equals(paranMap.get("trade_status"))) {
isVerify = AlipaySignature.rsaCheckV1(paranMap, AliPayConfig.ALIPAY_PUBLIC_KEY, AliPayConfig.CHARSET,AliPayConfig.SIGN_TYPE); //调用SDK验证签名
if(isVerify){
String orderNo = paranMap.get("out_trade_no");
RedisUtil.putObj(orderNo+"status", "P",(int) TimeUnit.DAYS.toSeconds(10));
Order order = new Order();
order.setOutTradeNo(orderNo);
order.setStatus("p");
try {
orderService.updateOrder(order);
order = (Order) RedisUtil.getObj("order"+orderNo);
if(order==null){
order =orderService.getOrder(orderNo);
}
Map<String,Object> parameterMap = new HashMap<String,Object>();
parameterMap.put("outTradeNo", orderNo);
parameterMap.put("userId",order.getUserId());
parameterMap.put("payType","alipay");
String result = WebUtils.post(order.getNotifyUrl(), parameterMap);
logger.info(order.getNotifyUrl()+"接口結果:json:"+JSON.toJSONString(result));
Map<String,String> resultMap = (Map<String, String>) JSON.parseObject(result, Map.class);
if("00".equals(resultMap.get("code"))){
isVerify =true;
}else{
isVerify =false;
}
}catch(Exception e){
logger.error("verifyNotify error:"+e.getMessage());
isVerify =false;
}
}
}
logger.debug("支付宝验签结果"+isVerify);
return isVerify;
}
}
支付宝下单后生产预付单,接口会生成form数据,通过js自动提交(<form> ...submit</form>)
微信支付分为H5支付和公众号支付
H5支付在微信内是不能支付支付成功的,微信会强制在微信内用微信公众号支付,这样就少不了走公众号支付那一套,这里严重吐槽一下,
进入前页面后要判断是在微信内还是为微信外,微信内就要用
http://open.weixin.qq.com/connect/oauth2/authorize拿取授权码,因为公众号支付需要openId,而openId需要通过授权code获得,拿到授权code就可以调用jssdk支付了,在微信外走H5支付为调用接口生成预付单后微信会生成一个字段mweburl,这个是一个url,通过这个地址就可以唤起微信支付了,但是要传入refer如果你直接点击能想办法传过去也是可以的,我这里采用的方法是在页面内绑定点击事件,它传过去的时候就会解析到refer了,不传的话会报商家配置参数有误,
然后贴上html代码,供大家参考
wxpayDemo2.html(微信支付(融合H5支付、公众号支付))
<!DOCTYPE html>
<html>
<head>
<title>微信手机网站支付接口页面demo</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<style>
*{
margin:0;
padding:0;
}
ul,ol{
list-style:none;
}
body{
font-family: "Helvetica Neue",Helvetica,Arial,"Lucida Grande",sans-serif;
}
.hidden{
display:none;
}
.new-btn-login-sp{
padding: 1px;
display: inline-block;
width: 75%;
}
.new-btn-login {
background-color: #02aaf1;
color: #FFFFFF;
font-weight: bold;
border: none;
width: 100%;
height: 30px;
border-radius: 5px;
font-size: 16px;
}
#main{
width:100%;
margin:0 auto;
font-size:14px;
}
.red-star{
color:#f00;
width:10px;
display:inline-block;
}
.null-star{
color:#fff;
}
.content{
margin-top:5px;
}
.content dt{
width:100px;
display:inline-block;
float: left;
margin-left: 20px;
color: #666;
font-size: 13px;
margin-top: 8px;
}
.content dd{
margin-left:120px;
margin-bottom:5px;
}
.content dd input {
width: 85%;
height: 28px;
border: 0;
-webkit-border-radius: 0;
-webkit-appearance: none;
}
#foot{
margin-top:10px;
position: absolute;
bottom: 15px;
width: 100%;
}
.foot-ul{
width: 100%;
}
.foot-ul li {
width: 100%;
text-align:center;
color: #666;
}
.note-help {
color: #999999;
font-size: 12px;
line-height: 130%;
margin-top: 5px;
width: 100%;
display: block;
}
#btn-dd{
margin: 20px;
text-align: center;
}
.foot-ul{
width: 100%;
}
.one_line{
display: block;
height: 1px;
border: 0;
border-top: 1px solid #eeeeee;
width: 100%;
margin-left: 20px;
}
.am-header {
display: -webkit-box;
display: -ms-flexbox;
display: box;
width: 100%;
position: relative;
padding: 7px 0;
-webkit-box-sizing: border-box;
-ms-box-sizing: border-box;
box-sizing: border-box;
background: #1D222D;
height: 50px;
text-align: center;
-webkit-box-pack: center;
-ms-flex-pack: center;
box-pack: center;
-webkit-box-align: center;
-ms-flex-align: center;
box-align: center;
}
.am-header h1 {
-webkit-box-flex: 1;
-ms-flex: 1;
box-flex: 1;
line-height: 18px;
text-align: center;
font-size: 18px;
font-weight: 300;
color: #fff;
}
</style>
</head>
<script src="./js/jquery-1.9.1.min.js" type="text/javascript"></script>
<script src="./js/qrcode.min.js" type="text/javascript"></script>
<div style="display: none;margin-top: 20px;">
<a id="getBrandWCPayRequest" href="#" style="font-size:40px;">H5非微信app支付</a>移动端,非微信app使用<br>
</div>
<div id="qrcode" style="display: none;margin-top: 20px;"><a id="getBrandWCPayRequest" href="#" style="font-size:40px;">H5非微信app支付</a>移动端,非微信app使用<br>
</div>
<script>
var appId = "";
var timeStamp = "";
var nonceStr = "";
var pg = "";
var signType = "";
var paySign = "";
var openid = "";
var code = "";
function getQueryString(name) {
var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)", "i");
var r = window.location.search.substr(1).match(reg);
if (r != null) return unescape(r[2]);
return null;
}
function getOpenId(){
var isWxbrowser = isWeiXin();
if(isWxbrowser){
code = getQueryString("code")
if(code==null||code==undefined||code==""){
//获取openid
window.location.href="http://open.weixin.qq.com/connect/oauth2/authorize?appid=xxxx&redirect_uri=http://xxx/payDemo/wxpayDemo2.html&response_type=code&scope=snsapi_base&state=wxpay#wechat_redirect";
}
}
}
$(function(){
getOpenId();
})
function createWxH5Pay(){
var isWxbrowser = isWeiXin();
var data = "body="+$("#body1").val()+"&detail="+$("#detail").val()+"&outTradeNo="+$("#outTradeNo").val()+"&totalAmount="+$("#totalAmount").val()+"&payType="+$("#payType").val()+"&isWxbrowser="+isWxbrowser+"&code="+code;
var outTradeNo = $("#outTradeNo").val();
if(outTradeNo==null||outTradeNo==undefined||outTradeNo==""){
//处理
alert("商户订单号不能为空");
return;
}
var body1 = $("#body1").val();
if(body1==null||body1==undefined||body1==""){
//处理
alert("订单名称不能为空");
return;
}
var totalAmount = $("#totalAmount").val();
if(totalAmount==null||totalAmount==undefined||totalAmount==""){
//处理
alert("付款金额不能为空");
return;
}
var detail = $("#detail").val();
if(detail==null||detail==undefined||detail==""){
//处理
alert("商品描述不能为空");
return;
}
$.ajax({
url: '/payDemo/pay/unifiedOrder.do',
type: 'post',
cache:false,
async:false,
data: data,
dataType: 'JSON',
timeout: 5000,
success: function(data){
console.log("data="+data.prepayid);
if(data!=null){
if(data.code!="00")alert(data.code+data.message);
appId = data.data.appId;
timeStamp = data.data.timeStamp;
nonceStr = data.data.nonceStr;
pg = data.data.package;
signType = "MD5";
paySign = data.data.sign;
if(!isWeiXin()){
$("#getBrandWCPayRequest").attr("href",data.data.mweburl);
document.getElementById("getBrandWCPayRequest").click();
}else{
pay();
}
}
}
});
}
function isWeiXin(){
var ua = window.navigator.userAgent.toLowerCase();
if(ua.match(/MicroMessenger/i) == 'micromessenger'){
return true;
}else{
return false;
}
}
//开始支付
function onBridgeReady(){
console.log("-------------------------------->>>>:")
WeixinJSBridge.invoke(
'getBrandWCPayRequest', {
"appId" : appId, //公众号名称,由商户传入
"timeStamp": timeStamp+"", //时间戳,自1970年以来的秒数
"nonceStr" : nonceStr, //随机串
"package" : pg,
"signType" : signType, //微信签名方式:
"paySign" : paySign //微信签名
},
function(res){
if(res.err_msg == "get_brand_wcpay_request:ok" ) {
alert("支付成功"); // 使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回 ok,但并不保证它绝对可靠。
}else if (res.err_msg == "get_brand_wcpay_request:cancel") {
alert("支付过程中用户取消");
}else{
//支付失败
// alert("appId="+appId+",timestamp="+timeStamp+",nonceStr="+nonceStr+",package="+pg+",signType="+signType+",paySign="+paySign);
alert(res.err_msg)
}
}
);
}
//唤起微信支付
function pay(){
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();
}
}
</script>
<body text=#000000 bgColor="#ffffff" leftMargin=0 topMargin=4>
<header class="am-header">
<h1>微信手机网站支付接口快速通道</h1>
</header>
<div id="main">
<div id="body" style="clear:left">
<dl class="content">
<dt>商户订单号(*):</dt>
<dd>
<input id="outTradeNo" name="outTradeNo" />
</dd>
<hr class="one_line">
<dt>订单名称(*):</dt>
<dd>
<input id="body1" name="body1" />
</dd>
<hr class="one_line">
<dt>付款金额(*):</dt>
<dd>
<input id="totalAmount" name="totalAmount" />
</dd>
<hr class="one_line"/>
<dt>商品描述(*):</dt>
<dd>
<input id="detail" name="detail" />
</dd>
<dt></dt>
<dd>
<input type="hidden" id="payType" name="payType" value="weChat" />
</dd>
<hr class="one_line">
<dt></dt>
<dd id="btn-dd">
<span class="new-btn-login-sp">
<button class="new-btn-login" onclick='createWxH5Pay(this);' style="text-align:center;">确 认</button>
</span>
<span class="note-help">如果您点击“确认”按钮,即表示您同意该次的执行操作。</span>
</dd>
</dl>
</div>
<div id="foot">
<ul class="foot-ul">
<li>
深圳市xxxxx公司版权所有 2015-2018
</li>
</ul>
</div>
</div>
</body>
</html>
alipayDemo.html(支付宝支付)
<!DOCTYPE html>
<html>
<head>
<title>支付宝手机网站支付接口</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<style>
*{
margin:0;
padding:0;
}
ul,ol{
list-style:none;
}
body{
font-family: "Helvetica Neue",Helvetica,Arial,"Lucida Grande",sans-serif;
}
.hidden{
display:none;
}
.new-btn-login-sp{
padding: 1px;
display: inline-block;
width: 75%;
}
.new-btn-login {
background-color: #02aaf1;
color: #FFFFFF;
font-weight: bold;
border: none;
width: 100%;
height: 30px;
border-radius: 5px;
font-size: 16px;
}
#main{
width:100%;
margin:0 auto;
font-size:14px;
}
.red-star{
color:#f00;
width:10px;
display:inline-block;
}
.null-star{
color:#fff;
}
.content{
margin-top:5px;
}
.content dt{
width:100px;
display:inline-block;
float: left;
margin-left: 20px;
color: #666;
font-size: 13px;
margin-top: 8px;
}
.content dd{
margin-left:120px;
margin-bottom:5px;
}
.content dd input {
width: 85%;
height: 28px;
border: 0;
-webkit-border-radius: 0;
-webkit-appearance: none;
}
#foot{
margin-top:10px;
position: absolute;
bottom: 15px;
width: 100%;
}
.foot-ul{
width: 100%;
}
.foot-ul li {
width: 100%;
text-align:center;
color: #666;
}
.note-help {
color: #999999;
font-size: 12px;
line-height: 130%;
margin-top: 5px;
width: 100%;
display: block;
}
#btn-dd{
margin: 20px;
text-align: center;
}
.foot-ul{
width: 100%;
}
.one_line{
display: block;
height: 1px;
border: 0;
border-top: 1px solid #eeeeee;
width: 100%;
margin-left: 20px;
}
.am-header {
display: -webkit-box;
display: -ms-flexbox;
display: box;
width: 100%;
position: relative;
padding: 7px 0;
-webkit-box-sizing: border-box;
-ms-box-sizing: border-box;
box-sizing: border-box;
background: #1D222D;
height: 50px;
text-align: center;
-webkit-box-pack: center;
-ms-flex-pack: center;
box-pack: center;
-webkit-box-align: center;
-ms-flex-align: center;
box-align: center;
}
.am-header h1 {
-webkit-box-flex: 1;
-ms-flex: 1;
box-flex: 1;
line-height: 18px;
text-align: center;
font-size: 18px;
font-weight: 300;
color: #fff;
}
</style>
</head>
<script src="./js/jquery-1.9.1.min.js" type="text/javascript"></script>
<script>
function createAliH5Pay(){
var data = "body="+$("#body1").val()+"&detail="+$("#detail").val()+"&outTradeNo="+$("#outTradeNo").val()+"&totalAmount="+$("#totalAmount").val()+"&payType="+$("#payType").val();
var outTradeNo = $("#outTradeNo").val();
if(outTradeNo==null||outTradeNo==undefined||outTradeNo==""){
//处理
alert("商户订单号不能为空");
return;
}
var body1 = $("#body1").val();
if(body1==null||body1==undefined||body1==""){
//处理
alert("订单名称不能为空");
return;
}
var totalAmount = $("#totalAmount").val();
if(totalAmount==null||totalAmount==undefined||totalAmount==""){
//处理
alert("付款金额不能为空");
return;
}
var detail = $("#detail").val();
if(detail==null||detail==undefined||detail==""){
//处理
alert("商品描述不能为空");
return;
}
$.ajax({
url: '/payDemo/pay/unifiedOrder.do',
type: 'post',
cache:false,
async:false,
data: data,
dataType: 'JSON',
timeout: 5000,
success: function(data){
console.log("data="+data.data);
if(data!=null){
if(data.code!="00")alert("["+data.code+"]"+data.message);
$("body").html(data.data);
}
}
});
}
</script>
<body text=#000000 bgColor="#ffffff" leftMargin=0 topMargin=4>
<header class="am-header">
<h1>支付宝手机网站支付接口快速通道(接口名:alipay.trade.wap.pay)</h1>
</header>
<div id="main">
<div id="body" style="clear:left">
<dl class="content">
<dt>商户订单号(*):</dt>
<dd>
<input id="outTradeNo" name="outTradeNo" />
</dd>
<hr class="one_line">
<dt>订单名称(*):</dt>
<dd>
<input id="body1" name="body" />
</dd>
<hr class="one_line">
<dt>付款金额(*):</dt>
<dd>
<input id="totalAmount" name="totalAmount" />
</dd>
<hr class="one_line"/>
<dt>商品描述(*):</dt>
<dd>
<input id="detail" name="detail" />
</dd>
<dt></dt>
<dd>
<input type="hidden" id="payType" name="payType" value="aliPay" />
</dd>
<hr class="one_line">
<dt></dt>
<dd id="btn-dd">
<span class="new-btn-login-sp">
<button class="new-btn-login" type="button" onclick='createAliH5Pay(this);' style="text-align:center;">确 认</button>
</span>
<span class="note-help">如果您点击“确认”按钮,即表示您同意该次的执行操作。</span>
</dd>
</dl>
</div>
<div id="foot">
<ul class="foot-ul">
<li>
深圳市xxxxx公司版权所有 2015-2018
</li>
</ul>
</div>
</div>
</body>
</html>