前言
[蚂蚁金服对接平台](https://open.alipay.com/platform/manageHome.htm)
首先登陆蚂蚁金服官方对接平台,这里用沙箱测试
按照官方提示,设置以下配置。
开发部分
依赖包pom
<!-- https://mvnrepository.com/artifact/com.alipay.sdk/alipay-sdk-java -->
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java</artifactId>
<version>3.4.27.ALL</version>
</dependency>
目录层级
配置类AlipayConfig:
package com.yima.core.module.rent.pay.alipay;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
public class AlipayConfig {
/**
* 应用号
*/
public static String APP_ID = "你的沙箱中appId";
/**
* 商户的私钥
*/
public static String APP_PRIVATE_KEY = "你的私钥";
/**
* 编码
*/
public static String CHARSET = "UTF-8";
/**
* 支付宝公钥
*/
public static String ALIPAY_PUBLIC_KEY = "你的公钥,和私钥配对加解密使用";
/**
* 支付宝网关地址
*/
private static String GATEWAY = "https://openapi.alipay.com/gateway.do";
/**
* 成功付款回调
*/
public static String PAY_NOTIFY = "你的回调地址";
/**
* 支付成功回调
*/
public static String REFUND_NOTIFY = "你的回调地址";
/**
* 前台通知地址
*/
public static String RETURN_URL = "你的回调地址";
/**
* 参数类型
*/
public static String PARAM_TYPE = "json";
/**
* 成功标识
*/
public static final String SUCCESS_REQUEST = "TRADE_SUCCESS";
/**
* 交易关闭回调(当该笔订单全部退款完毕,则交易关闭)
*/
public static final String TRADE_CLOSED = "TRADE_CLOSED";
/**
* 收款方账号
*/
public static final String SELLER_ID = "注册沙箱的支付宝账号";
/**
* 支付宝请求客户端入口
*/
private volatile static AlipayClient alipayClient = null;
/**
* 不可实例化
*/
private AlipayConfig(){};
/**
* 双重锁单例
* @return 支付宝请求客户端实例
*/
public static AlipayClient getInstance(){
if (alipayClient == null){
synchronized (AlipayConfig.class){
if (alipayClient == null){
alipayClient = new DefaultAlipayClient(GATEWAY,APP_ID,APP_PRIVATE_KEY,PARAM_TYPE,CHARSET,ALIPAY_PUBLIC_KEY);
}
}
}
return alipayClient;
}
}
调用类AlipayTrade
package com.yima.core.module.rent.pay.alipay;
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.yima.core.module.rent.pay.util.SignUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.http.HttpServletRequest;
import java.util.Map;
public class AlipayTrade {
private Logger logger = LoggerFactory.getLogger(AlipayTrade.class);
/**
* 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); //调用SDK验证签名
}
logger.debug("支付宝验签结果"+isVerify);
return isVerify;
}
}
工具类SignUtil
package com.yima.core.module.rent.pay.util;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import javax.servlet.http.HttpServletRequest;
/**
* @author Yifei Kuang
* @date 2019年3月11日17:04:35
*/
public class SignUtil {
/**
* 连接Map键值对
*
* @param map
* Map
* @param prefix
* 前缀
* @param suffix
* 后缀
* @param separator
* 连接符
* @param ignoreEmptyValue
* 忽略空值
* @param ignoreKeys
* 忽略Key
* @return 字符串
*/
public static String joinKeyValue(Map<String, Object> map, String prefix, String suffix, String separator,
boolean ignoreEmptyValue, String... ignoreKeys) {
List<String> list = new ArrayList<String>();
if (map != null) {
for (Map.Entry<String, Object> entry : map.entrySet()) {
String key = entry.getKey();
String value = String.valueOf(entry.getValue());
if (StringUtils.isNotEmpty(key) && !ArrayUtils.contains(ignoreKeys, key)
&& (!ignoreEmptyValue || StringUtils.isNotEmpty(value))) {
list.add(key + "=" + (value != null ? value : ""));
}
}
}
return (prefix != null ? prefix : "") + StringUtils.join(list, separator) + (suffix != null ? suffix : "");
}
/**
* 把request请求参数转换为Map<String,String>
* @param request 该请求
* @return Map<String,String>格式的参数
*/
public static Map<String,String> request2Map(HttpServletRequest request){
Enumeration<String> names = request.getParameterNames();
Map<String, String> resData = new HashMap<String, String>();
while (names.hasMoreElements()) {
String name = names.nextElement();
resData.put(name, request.getParameter(name));
}
return resData;
}
/**
* Bean转map
* @param bean 要转的bean
* @return 返回一个TreeMap
*/
public static TreeMap<String, String> bean2TreeMap(Object bean) {
TreeMap<String, String> requestMap = new TreeMap<String, String>();
Class<?> cls = bean.getClass();
Field[] fields = cls.getDeclaredFields();
try {
for (int i = 0; i < fields.length; i++) {
String key = fields[i].getName();
fields[i].setAccessible(true);
Object value = fields[i].get(bean);
if ("sign".equals(key) || value == null || StringUtils.isEmpty(value.toString())) {
continue;
}
requestMap.put(key, value.toString());
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return requestMap;
}
}