1、引入相关依赖 pom.xml
<!--ali支付--> <dependency> <groupId>com.alipay.sdk</groupId> <artifactId>alipay-sdk-java</artifactId> <version>4.9.79.ALL</version> </dependency> <dependency> <groupId>commons-configuration</groupId> <artifactId>commons-configuration</artifactId> <version>1.10</version> </dependency> <dependency> <groupId>com.google.zxing</groupId> <artifactId>core</artifactId> <version>3.2.1</version> </dependency> <!--ali支付end-->
2、配置文件 application.yml
[注意] 证书文件必须放到服务器上,放到项目里的证书,在win环境下可以根据路径找到,但打成jar包后就找不到项目里的证书文件了,解决办法是判断服务器环境,根据不同环境拼接证书路径
证书文件目录结构如下,在Linux服务器上部署时,将pay文件夹和jar包放到同一个文件夹下
# 支付 pay: alipay: gatewayUrl: https://openapi.alipay.com/gateway.do appid: ***** appPrivateKey: alipayPublicKey: appCertPath: /pay/alipay/cert/appCertPublicKey****.crt alipayCertPath: /pay/alipay/cert/alipayCertPublicKey_RSA2.crt alipayRootCertPath: /pay/alipay/cert/alipayRootCert.crt returnUrl: **** notifyUrl: ****
3、读取配置文件
import lombok.Data; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.context.properties.ConfigurationProperties; import javax.annotation.PostConstruct; @Data @Slf4j @ConfigurationProperties(prefix = "pay.alipay") public class AlipayProperties { /** * 支付宝gatewayUrl */ private String gatewayUrl; /** * 商户应用id */ private String appid; /** * RSA私钥,用于对商户请求报文加签 */ private String appPrivateKey; /** * 支付宝RSA公钥,用于验签支付宝应答 */ private String alipayPublicKey; /** * 签名类型 */ private String signType = "RSA2"; /** * 格式 */ private String formate = "json"; /** * 编码 */ private String charset = "UTF-8"; /** * 同步地址 */ private String returnUrl; /** * 异步地址 */ private String notifyUrl; /** * 应用公钥证书 */ private String appCertPath; /** * 支付宝公钥证书 */ private String alipayCertPath; /** * 支付宝根证书 */ private String alipayRootCertPath; /** * 最大查询次数 */ private static int maxQueryRetry = 5; /** * 查询间隔(毫秒) */ private static long queryDuration = 5000; /** * 最大撤销次数 */ private static int maxCancelRetry = 3; /** * 撤销间隔(毫秒) */ private static long cancelDuration = 3000; private AlipayProperties() { } /** * PostContruct是spring框架的注解,在方法上加该注解会在项目启动的时候执行该方法,也可以理解为在spring容器初始化的时候执行该方法。 */ @PostConstruct public void init() { log.info(description()); } public String description() { StringBuilder sb = new StringBuilder("\n支付宝Configs{"); sb.append("支付宝网关: ").append(gatewayUrl).append("\n"); sb.append(", appid: ").append(appid).append("\n"); sb.append(", 商户RSA私钥: ").append(getKeyDescription(appPrivateKey)).append("\n"); sb.append(", 应用公钥证书: ").append(appCertPath).append("\n"); sb.append(", 支付宝公钥证书: ").append(alipayCertPath).append("\n"); sb.append(", 支付宝根证书: ").append(alipayRootCertPath).append("\n"); sb.append(", 支付宝RSA公钥: ").append(getKeyDescription(alipayPublicKey)).append("\n"); sb.append(", 签名类型: ").append(signType).append("\n"); sb.append(", 查询重试次数: ").append(maxQueryRetry).append("\n"); sb.append(", 查询间隔(毫秒): ").append(queryDuration).append("\n"); sb.append(", 撤销尝试次数: ").append(maxCancelRetry).append("\n"); sb.append(", 撤销重试间隔(毫秒): ").append(cancelDuration).append("\n"); sb.append("}"); return sb.toString(); } private String getKeyDescription(String key) { int showLength = 6; if (StringUtils.isNotEmpty(key) && key.length() > showLength) { return new StringBuilder(key.substring(0, showLength)).append("******") .append(key.substring(key.length() - showLength)).toString(); } return null; } }
4、在启动时初始化支付宝客户端
import com.alipay.api.AlipayApiException; import com.alipay.api.CertAlipayRequest; import com.alipay.api.DefaultAlipayClient; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.util.ResourceUtils; import java.io.IOException; @Configuration @Slf4j @EnableConfigurationProperties(AlipayProperties.class) public class AlipayConfiguration { @Autowired private AlipayProperties properties; // /** // * 支付宝接口加签模式为公钥时使用 // * // * @return // */ // @Bean // public AlipayClient alipayClient() { // return new DefaultAlipayClient(properties.getGatewayUrl(), // properties.getAppid(), // properties.getAppPrivateKey(), // properties.getFormate(), // properties.getCharset(), // properties.getAlipayPublicKey(), // properties.getSignType()); // } /** * 支付宝接口加签模式为公钥证书时使用 * * @return */ @Bean public DefaultAlipayClient alipayClient() throws AlipayApiException, IOException { log.info("---------创建支付宝客户端--------"); //判断系统环境 String osName = System.getProperty("os.name"); log.info("-------系统环境--------" + osName); String serverpath = null; if (osName.startsWith("Windows")) { // windows serverpath = ResourceUtils.getURL("classpath:").getPath(); serverpath += "static/app"; log.info("-------------win文件路径----------" + serverpath); // } else if (osName.startsWith("Mac OS")) { // // 苹果 } else { // unix or linux serverpath = System.getProperty("user.dir"); log.info("-------------unix or linux文件路径----------" + serverpath); } CertAlipayRequest certAlipayRequest = new CertAlipayRequest(); certAlipayRequest.setServerUrl(properties.getGatewayUrl()); certAlipayRequest.setAppId(properties.getAppid()); certAlipayRequest.setFormat(properties.getFormate()); certAlipayRequest.setPrivateKey(properties.getAppPrivateKey()); certAlipayRequest.setCharset(properties.getCharset()); certAlipayRequest.setSignType(properties.getSignType()); certAlipayRequest.setCertPath(serverpath + properties.getAppCertPath()); certAlipayRequest.setAlipayPublicCertPath(serverpath + properties.getAlipayCertPath()); certAlipayRequest.setRootCertPath(serverpath + properties.getAlipayRootCertPath()); return new DefaultAlipayClient(certAlipayRequest); } }
5、APP支付
import cn.hutool.core.date.DateUtil; import com.alipay.api.AlipayApiException; import com.alipay.api.AlipayClient; import com.alipay.api.domain.AlipayFundTransPayModel; import com.alipay.api.domain.AlipayTradeAppPayModel; import com.alipay.api.domain.AlipayTradeQueryModel; import com.alipay.api.domain.Participant; import com.alipay.api.internal.util.AlipaySignature; import com.alipay.api.request.AlipayFundTransUniTransferRequest; import com.alipay.api.request.AlipayTradeAppPayRequest; import com.alipay.api.request.AlipayTradeQueryRequest; import com.alipay.api.response.AlipayFundTransUniTransferResponse; import com.alipay.api.response.AlipayTradeAppPayResponse; import com.alipay.api.response.AlipayTradeQueryResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.UUID; /** * app支付 */ @Slf4j @Controller @RequestMapping("/app/alipay/app") public class AlipayAppPayController { @Autowired private AlipayProperties alipayProperties; @Autowired private AlipayClient alipayClient; @Autowired private AlipayProperties aliPayProperties; /** * 去支付 * <p> * * @param response * @throws Exception */ @RequestMapping("/createOrder") public void createOrder(HttpServletRequest request, HttpServletResponse response, @RequestBody Map<String, String> mapParams) throws AlipayApiException { BaseResault resPo = new BaseResault(); // 订单模型 String productCode = "QUICK_MSECURITY_PAY"; AlipayTradeAppPayModel model = new AlipayTradeAppPayModel(); Order bean = orderService.getOrderByNum(mapParams.get("orderNum")); model.setOutTradeNo(bean.getOrderNum()); log.info("----订单编号----" + model.getOutTradeNo()); model.setSubject(bean.getShopName()); log.info("----subject----" + model.getSubject()); model.setTotalAmount(bean.getTotalMoney().toString()); log.info("----金额----" + model.getTotalAmount()); model.setTimeoutExpress("30m"); model.setProductCode(productCode); AlipayTradeAppPayRequest appPayRequest = new AlipayTradeAppPayRequest(); appPayRequest.setNotifyUrl(‘服务器域名’ + alipayProperties.getNotifyUrl()); log.info("----notifyUrl----" + appPayRequest.getNotifyUrl()); appPayRequest.setBizModel(model); //实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称:alipay.trade.app.pay AlipayTradeAppPayResponse ali_response = alipayClient.sdkExecute(appPayRequest); //这里和使用公钥加签支付使用的方法不同 //就是orderString 可以直接给客户端请求,无需再做处理。 log.info("---------支付宝返回---------{}", ali_response.getBody()); resPo.setObj(ali_response.getBody()); resPo.setCode(ConstantApp.AppTokenValidResult.SUCCESS.getCode()); resPo.setMessage(ConstantApp.AppTokenValidResult.SUCCESS.getMessage()); ResponseJsonUtils.json(response, resPo); }
}
6、查询订单
/** * 订单查询(最主要用于查询订单的支付状态) * * @param orderNo 商户订单号 * @return */ @GetMapping("/query") public void query(HttpServletRequest request, HttpServletResponse response, String orderNo) throws AlipayApiException { BaseResault resPo = new BaseResault(); AlipayTradeQueryRequest aliRequest = new AlipayTradeQueryRequest(); AlipayTradeQueryModel bizModel = new AlipayTradeQueryModel(); bizModel.setTradeNo(orderNo); aliRequest.setBizModel(bizModel); AlipayTradeQueryResponse aliResponse = alipayClient.certificateExecute(aliRequest); if (aliResponse.isSuccess()) { log.info("查询支付宝支付状态-----调用成功{}", aliResponse.getBody()); if (aliResponse.getTradeStatus().equals("TRADE_SUCCESS") || aliResponse.getTradeStatus().equals("TRADE_FINISHED")) { //订单编号 String orderNum = aliResponse.getOutTradeNo(); // 处理业务 resPo.setObj(aliResponse.getBody()); resPo.setCode(ConstantApp.AppTokenValidResult.SUCCESS.getCode()); resPo.setMessage(ConstantApp.AppTokenValidResult.SUCCESS.getMessage()); ResponseJsonUtils.json(response, resPo); return; } else { log.error("支付宝订单" + orderNo + "交易失败,交易状态:" + aliResponse.getTradeStatus()); resPo.setObj(aliResponse.getBody()); resPo.setCode(ConstantApp.AppTokenValidResult.SYSTEM_ERROR.getCode()); resPo.setMessage(ConstantApp.AppTokenValidResult.SYSTEM_ERROR.getMessage()); ResponseJsonUtils.json(response, resPo); return; } } else { log.info("查询支付宝支付状态------调用失败{}", aliResponse.getBody()); resPo.setObj(aliResponse.getBody()); resPo.setCode(ConstantApp.AppTokenValidResult.SYSTEM_ERROR.getCode()); resPo.setMessage(ConstantApp.AppTokenValidResult.SYSTEM_ERROR.getMessage()); ResponseJsonUtils.json(response, resPo); return; } }
7、转账给用户
/** * alipay.fund.trans.uni.transfer(单笔转账接口) * <p>文档地址 * https://docs.open.alipay.com/api_28/alipay.fund.trans.uni.transfer/ * * @param response * @throws Exception */ @RequestMapping("/alipayToUser") public void alipayToUser(HttpServletRequest request, HttpServletResponse response) throws AlipayApiException { BaseResault resPo = new BaseResault(); //转账金额 不能少于0.1元 String money = request.getParameter("money"); //支付宝账号,支持邮箱、电话号码 String phone = request.getParameter("phone"); //支付宝账户名称 String name = request.getParameter("name"); // 订单模型 AlipayFundTransPayModel model = new AlipayFundTransPayModel(); //商户端的唯一订单号 String orderNum = UUID.randomUUID().toString().replace("-", ""); log.info("============支付宝转账给用户-订单号:" + orderNum + "=============="); model.setOutBizNo(orderNum); //订单总金额,单位为元,精确到小数点后两位 model.setTransAmount(money); //业务产品码, //收发现金红包固定为:STD_RED_PACKET; //单笔无密转账到支付宝账户固定为:TRANS_ACCOUNT_NO_PWD; //单笔无密转账到银行卡固定为:TRANS_BANKCARD_NO_PWD model.setProductCode("TRANS_ACCOUNT_NO_PWD"); //收款方信息 Participant participant = new Participant(); //参与方的唯一标识 participant.setIdentity(phone); //参与方的标识类型,目前支持如下类型: //1、ALIPAY_USER_ID 支付宝的会员ID //2、ALIPAY_LOGON_ID:支付宝登录号,支持邮箱和手机号格式 participant.setIdentityType("ALIPAY_LOGON_ID"); //参与方真实姓名,如果非空,将校验收款支付宝账号姓名一致性。当identity_type=ALIPAY_LOGON_ID时,本字段必填。 participant.setName(name); model.setPayeeInfo(participant); //业务备注 model.setRemark("转账测试"); model.setBizScene("DIRECT_TRANSFER"); AlipayFundTransUniTransferRequest appPayRequest = new AlipayFundTransUniTransferRequest(); appPayRequest.setBizModel(model); //实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称:alipay.fund.trans.uni.transfer(单笔转账接口) AlipayFundTransUniTransferResponse ali_response = alipayClient.certificateExecute(appPayRequest); if (ali_response.isSuccess()) { log.info("========支付宝转账给用户-调用成功=======" + ali_response.getBody()); resPo.setObj(ali_response.getBody()); resPo.setCode(ConstantApp.AppTokenValidResult.SUCCESS.getCode()); resPo.setMessage(ConstantApp.AppTokenValidResult.SUCCESS.getMessage()); ResponseJsonUtils.json(response, resPo); } else { log.info("========支付宝转账给用户-调用失败=======" + ali_response.getBody()); resPo.setObj(ali_response.getBody()); resPo.setCode(ConstantApp.AppTokenValidResult.SYSTEM_ERROR.getCode()); resPo.setMessage(ConstantApp.AppTokenValidResult.SYSTEM_ERROR.getMessage()); ResponseJsonUtils.json(response, resPo); } }
8、异步通知
/** * 证书支付异步通知 * https://docs.open.alipay.com/194/103296 */ @RequestMapping("/notify") public String notify(HttpServletRequest request) { log.info("-----------支付异步通知----------------"); Map requestParams = request.getParameterMap(); log.info(">>>支付宝回调参数:" + requestParams); Map<String, String> params = new HashMap<>(); 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] + ","; } //乱码解决,这段代码在出现乱码时使用。 //valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8"); params.put(name, valueStr); } log.info(">>>支付宝回调参数解析:" + params); try { String alipayCertPath = System.getProperty("user.dir") + aliPayProperties.getAlipayCertPath(); //切记alipaypublickey是支付宝的公钥,请去open.alipay.com对应应用下查看。 boolean flag = AlipaySignature.rsaCertCheckV1( params, alipayCertPath, aliPayProperties.getCharset(), aliPayProperties.getSignType()); if (flag) { log.info(">>>支付宝回调签名认证成功"); //商户订单号 String out_trade_no = params.get("out_trade_no"); //交易状态 String trade_status = params.get("trade_status"); //交易金额 String amount = params.get("total_amount"); //商户app_id String app_id = params.get("app_id"); if ("TRADE_SUCCESS".equals(trade_status) || "TRADE_FINISHED".equals(trade_status)) { /** * 自己的业务处理,一定要判断是否已经处理过订单 */ log.info("-----------^v^支付成功^v^------------"); } } else { log.error("没有处理支付宝回调业务,支付宝交易状态:{},params:{}", trade_status, params); } } else { log.info("支付宝回调签名认证失败,signVerified=false, params:{}", params); return "failure"; } } catch (Exception e) { log.error(e.getMessage(), e); log.info("支付宝回调签名认证失败,signVerified=false, params:{}", params); return "failure"; } return "success"; }
9、退款
/** * 同一订单多次退款 * * @param orderNo 商户订单号 * @return */ @Override public String multipleRefunds(String orderNo, String refundAmount, String refundReason) throws AlipayApiException { log.info("------------支付宝退款开始--------------"); AlipayTradeRefundRequest alipayRequest = new AlipayTradeRefundRequest(); AlipayTradeRefundModel model = new AlipayTradeRefundModel(); // 商户订单号 model.setOutTradeNo(orderNo); // 退款金额 单位元 model.setRefundAmount(refundAmount); // 退款原因 model.setRefundReason(refundReason); // 退款订单号(同一个订单可以分多次部分退款,当分多次时必传) model.setOutRequestNo(UUID.randomUUID().toString()); log.info("------退款参数-----{}-----{}-----{}------{}", orderNo, refundAmount, refundReason, model.getOutRequestNo()); alipayRequest.setBizModel(model); AlipayTradeRefundResponse alipayResponse = alipayClient.certificateExecute(alipayRequest); log.info("--------支付宝退款结果---------" + alipayResponse.getBody() + "---------订单号--" + orderNo); return alipayResponse.getBody(); }