对接支付宝(当面付-扫码支付,电脑网站支付),已通过测试

1.去蚂蚁金服开放平台下载demo,拿到相关jar
在这里插入图片描述
2.pom.xml文件

    <!-- 加载jar-->
<!-- alipay-->
<dependency>
    <groupId>commons-codec</groupId>
    <artifactId>commons-codec</artifactId>
    <version>1.10</version>
    <scope>system</scope>
    <systemPath>${project.basedir}/src/main/webapp/WEB-INF/lib/commons-codec-1.10.jar</systemPath>
</dependency>
<dependency>
    <groupId>commons-configuration</groupId>
    <artifactId>commons-configuration</artifactId>
    <version>1.10</version>
    <scope>system</scope>
    <systemPath>${project.basedir}/src/main/webapp/WEB-INF/lib/commons-configuration-1.10.jar</systemPath>
</dependency>
<dependency>
    <groupId>commons-lang</groupId>
    <artifactId>commons-lang</artifactId>
    <version>2.6</version>
    <scope>system</scope>
    <systemPath>${project.basedir}/src/main/webapp/WEB-INF/lib/commons-lang-2.6.jar</systemPath>
</dependency>
<dependency>
    <groupId>commons-logging</groupId>
    <artifactId>commons-logging</artifactId>
    <version>1.1.1</version>
    <scope>system</scope>
    <systemPath>${project.basedir}/src/main/webapp/WEB-INF/lib/commons-logging-1.1.1.jar</systemPath>
</dependency>

<dependency>
    <groupId>com.aliyun</groupId>
    <artifactId>alipay-sdk-java-3.3.0.jar</artifactId>
    <version>3.3.0</version>
    <scope>system</scope>
    <systemPath>${project.basedir}/src/main/webapp/WEB-INF/lib/alipay-sdk-java-3.3.0.jar</systemPath>
</dependency>
<dependency>
    <groupId>com.aliyun</groupId>
    <artifactId>alipay-sdk-java-3.3.0-source.jar</artifactId>
    <version>3.3.0</version>
    <scope>system</scope>
    <systemPath>${project.basedir}/src/main/webapp/WEB-INF/lib/alipay-sdk-java-3.3.0-source.jar</systemPath>
</dependency>
<dependency>
    <groupId>com.aliyun</groupId>
    <artifactId>alipay-trade-sdk-20161215.jar</artifactId>
    <version>3.3.0</version>
    <scope>system</scope>
    <systemPath>${project.basedir}/src/main/webapp/WEB-INF/lib/alipay-trade-sdk-20161215.jar</systemPath>
</dependency>
<dependency>
    <groupId>com.aliyun</groupId>
    <artifactId>alipay-trade-sdk-20161215-source.jar</artifactId>
    <version>3.3.0</version>
    <scope>system</scope>
    <systemPath>${project.basedir}/src/main/webapp/WEB-INF/lib/alipay-trade-sdk-20161215-source.jar</systemPath>
</dependency>
<dependency>
    <groupId>org.hamcrest</groupId>
    <artifactId>hamcrest-core</artifactId>
    <version>1.3</version>
    <scope>system</scope>
    <systemPath>${project.basedir}/src/main/webapp/WEB-INF/lib/hamcrest-core-1.3.jar</systemPath>
</dependency>

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <configuration>
                <includeSystemScope>true</includeSystemScope>
            </configuration>
        </plugin>

        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <configuration>
                <source>1.8</source>
                <target>1.8</target>
                <encoding>UTF-8</encoding>
                <compilerArguments>
                    <extdirs>
                        ${project.basedir}/src/main/webapp/WEB-INF/lib
                    </extdirs>
                </compilerArguments>
            </configuration>
        </plugin>
    </plugins>
</build>

3.Alipay-config.properties

# 支付宝网关名
open_api_domain = 
# 商户UID
pid = 
# appId
appid = 

# RSA私钥、公钥和支付宝公钥(在蚂蚁金服开放平台下载公私钥生成工具生成并配置在账户里)
private_key =
public_key = 

#SHA1withRsa对应支付宝公钥
#alipay_public_key = MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDDI6d306Q8fIfCOaTXyiUeJHkrIvYISRcc73s3vF1ZT7XN8RNPwJxo8pWaJMmvyTn9N4HQ632qJBVHf8sxHi/fEsraprwCtzvzQETrNRwVxLO5jVmRGi60j8Ue1efIlzPXV9je9mkjzOmdssymZkh2QhUrCmZYI/FCEa3/cNMW0QIDAQAB

#SHA256withRsa对应支付宝公钥(在账户里面查看)
alipay_public_key = 

# 签名类型: RSA->SHA1withRsa,RSA2->SHA256withRsa
sign_type = RSA2
# 当面付最大查询次数和查询间隔(毫秒)
max_query_retry = 50
query_duration = 3000

# 当面付最大撤销次数和撤销间隔(毫秒)
max_cancel_retry = 3
cancel_duration = 2000

# 交易保障线程第一次调度延迟和调度间隔(秒)
heartbeat_delay = 5
heartbeat_duration = 900

#支付超时时间 m-分钟,h-小时,d-天,1c-当天(1c-当天的情况下,无论交易何时创建,都在0点关闭)
#该参数数值不接受小数点, 如 1.5h,可转换为 90m。
timeout_express=120m
#编码
charset = UTF-8

#返回格式
format = json

# 以下地址必须公网可以访问,否则无法通知。我用的内网穿透工具
# 电脑网站支付异步通知地址
page_notify_URL= http://qq1334713380.xicp.net:36576/xboot/pay/aliPayNotify/aliPay_page_notify

# 电脑网站支付同步通知地址(电脑网站支付成功以后跳转页面)
return_URL= http://qq1334713380.xicp.net:36576/xboot/pay/aliPayReturn.html

# 当面付-扫码支付 异步通知地址
precreate_notify= http://qq1334713380.xicp.net:36576/xboot/pay/aliPayNotify/aliPay_precreate_notify

4.AlipayConfig.java

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * @ClassName: PropConfig
 * @Description: TODO(配置文件设置)
 * @author 君泓 junying.wjy
 * @date 2018年7月17日 下午4:23:20
 *
 */
public final class AlipayConfig {

   public static final String ALIPAY_DEMO = "alipay";
    public static final String ALIPAY_DEMO_VERSION = "alipay_JAVA_20180907104657";

   /**
    * 配置文件加载
    */
   private static Properties prop;

   /**
    * 配置文件名称
    */
   public static String CONFIG_FILE = "Alipay-Config.properties";

   /**
    * 配置文件相对路径
    */
   public static String ALIPAY_CONFIG_PATH = File.separator + "etc" + File.separator + CONFIG_FILE;

   /**
    * 项目路径
    */
   public static String PROJECT_PATH = "";

   private static Log logger = LogFactory.getLog(AlipayConfig.class);

   /**
    * 初始化配置值
    */
   public static void initPropertis() {
      prop = new Properties();
      try {
         synchronized (prop) {
            InputStream inputStream = AlipayConfig.class.getClassLoader().getResourceAsStream(CONFIG_FILE);
            prop.load(inputStream);
            inputStream.close();
         }
      } catch (IOException e) {
         logger.error("日志 =============》: 配置文件Alipay-Config.properties找不到");
         e.printStackTrace();
      }
   }

   /**
    * 获取配置文件信息
    * 
    * @return
    */
   public static Properties getProperties() {
      if (prop == null){
         initPropertis();}
      return prop;
   }


   /**
    * 配置信息写入配置文件
    */
   public static void writeConfig() {
       System.out.println("ALIPAY_CONFIG_PATH:"+ALIPAY_CONFIG_PATH);
      File file = new File(ALIPAY_CONFIG_PATH);
      if (file.exists()) {
         file.setReadable(true);
         file.setWritable(true);
      }
      String lineText = null;
      BufferedReader bufferedReader = null;
      BufferedWriter bw = null;
      StringBuffer stringBuffer = new StringBuffer();
      try {
         bufferedReader = new BufferedReader(new FileReader(ALIPAY_CONFIG_PATH));
         while ((lineText = bufferedReader.readLine()) != null) {

            if (lineText.startsWith("APP_ID")) {
               lineText = "APP_ID = " + prop.getProperty("APP_ID");
            } else if (lineText.startsWith("RSA2_PRIVATE_KEY")) {
               lineText = "RSA2_PRIVATE_KEY = " + prop.getProperty("RSA2_PRIVATE_KEY");
            } else if (lineText.startsWith("RSA2_PUBLIC_KEY")) {
               lineText = "RSA2_PUBLIC_KEY = " + prop.getProperty("RSA2_PUBLIC_KEY");
            } else if (lineText.startsWith("ALIPAY_RSA2_PUBLIC_KEY")) {
               lineText = "ALIPAY_RSA2_PUBLIC_KEY = " + prop.getProperty("ALIPAY_RSA2_PUBLIC_KEY");
            } else if (lineText.startsWith("NOTIFY_URL")) {
               lineText = "NOTIFY_URL = " + prop.getProperty("NOTIFY_URL");
            } else if (lineText.startsWith("RETURN_URL")) {
               lineText = "RETURN_URL = " + prop.getProperty("RETURN_URL");
            } else if (lineText.startsWith("SANDBOX_BUYER_EMAIL")) {
               lineText = "SANDBOX_BUYER_EMAIL = " + prop.getProperty("SANDBOX_BUYER_EMAIL");
            } else if (lineText.startsWith("SANBOX_BUYER_LOGON_PWD")) {
               lineText = "SANBOX_BUYER_LOGON_PWD = " + prop.getProperty("SANBOX_BUYER_LOGON_PWD");
            } else if (lineText.startsWith("SANBOX_BUYER_PAY_PWD")) {
               lineText = "SANBOX_BUYER_PAY_PWD = " + prop.getProperty("SANBOX_BUYER_PAY_PWD");
            } else if (lineText.startsWith("SANDBOX_SELLER_ID")) {
               lineText = "SANDBOX_SELLER_ID = " + prop.getProperty("SANDBOX_SELLER_ID");
            } else if (lineText.startsWith("SANDBOX_SELLER_EMAIL")) {
               lineText = "SANDBOX_SELLER_EMAIL = " + prop.getProperty("SANDBOX_SELLER_EMAIL");
            } else if (lineText.startsWith("SANDBOX_SELLER_LOGON_PWD")) {
               lineText = "SANDBOX_SELLER_LOGON_PWD = " + prop.getProperty("SANDBOX_SELLER_LOGON_PWD");
            } else if (lineText.startsWith("ALIPAY_GATEWAY_URL")) {
               lineText = "ALIPAY_GATEWAY_URL = " + prop.getProperty("ALIPAY_GATEWAY_URL");
            } else if (lineText.startsWith("CHARSET")) {
               lineText = "CHARSET = " + prop.getProperty("CHARSET");
            } else if (lineText.startsWith("FORMAT")) {
               lineText = "FORMAT = " + prop.getProperty("FORMAT");
            } else if (lineText.startsWith("SIGNTYPE")) {
               lineText = "SIGNTYPE = " + prop.getProperty("SIGNTYPE");
            }

            stringBuffer.append(lineText).append("\r\n");
         }
         bufferedReader.close();
         bw = new BufferedWriter(new FileWriter(ALIPAY_CONFIG_PATH));
         bw.write(stringBuffer.toString());
         bw.close();
      } catch (Exception e) {
         e.printStackTrace();
      }
   }
   public final static class Status {

      /**
       * 上传成功
       */
      public final static String UPLOAD_SUCCESS = "UPLOAD_SUCCESS";

      /**
       * 上传失败
       */
      public final static String UPLOAD_FAILED = "UPLOAD_FAILED";

      /**
       * 同样的公钥
       */
      public final static String NOT_COVER = "NOT_COVER";

   }
}
5.DefaultAlipayClientFactory.java
import java.util.Properties;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;

/**
 * @ClassName: DefaultAlipayClientFactory
 * @Description: TODO(公共请求参数拼接类)
 * @author 君泓 junying.wjy
 * @date 2018年7月23日 下午4:38:38
 *
 */
public class DefaultAlipayClientFactory {

   private static AlipayClient alipayClient = null;

   /**
    * 封装公共请求参数
    * 
    * @return AlipayClient
    */
   public static AlipayClient getAlipayClient() {

   if (alipayClient != null) {
         return alipayClient;
      }

      Properties prop = AlipayConfig.getProperties();

      // 网关
      String URL = prop.getProperty("open_api_domain");
      // 商户APP_ID
      String APP_ID = prop.getProperty("appid");
      // 商户RSA 私钥
      String APP_PRIVATE_KEY = prop.getProperty("private_key");
      // 请求方式 json
      String FORMAT = prop.getProperty("FORMAT");
      // 编码格式,目前只支持UTF-8
      String CHARSET = prop.getProperty("CHARSET");
      // 支付宝公钥
      String ALIPAY_PUBLIC_KEY = prop.getProperty("alipay_public_key");
      // 签名方式
      String SIGN_TYPE = prop.getProperty("sign_type");

      return new DefaultAlipayClient(URL, APP_ID, APP_PRIVATE_KEY, FORMAT, CHARSET, ALIPAY_PUBLIC_KEY, SIGN_TYPE);
   }
}

6.spring boot的启动类中添加:

@Primary
@Bean
public AlipayTradeService alipayTradeService() {
    AlipayTradeService tradeService = new AlipayTradeServiceImpl.ClientBuilder().build();
    return tradeService;
}

7.AlipayVo.java

@Data
public class AlipayVo implements Serializable {

    private static final long serialVersionUID = 1L;
    /**
     * 订单名称
     */
    private String subject;
    /**
     * 商户网站唯一订单号
     */
    private String out_trade_no;
    /**
     * 该笔订单允许的最晚付款时间
     */
    private String timeout_express;
    /**
     * 付款金额
     */
    private String total_amount;
    /**
     * 销售产品码,与支付宝签约的产品码名称
     */
    private String product_code;

}

7.AliPayServiceImpl.java(提供支付服务)

import cn.hutool.core.util.StrUtil;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.domain.TradeFundBill;
import com.alipay.api.request.AlipayTradePagePayRequest;
import com.alipay.api.request.AlipayTradeQueryRequest;
import com.alipay.api.request.AlipayTradeRefundRequest;
import com.alipay.api.response.*;
import com.alipay.demo.trade.config.Configs;
import com.alipay.demo.trade.model.ExtendParams;
import com.alipay.demo.trade.model.builder.AlipayTradePrecreateRequestBuilder;
import com.alipay.demo.trade.model.result.AlipayF2FPrecreateResult;
import com.alipay.demo.trade.service.AlipayTradeService;
import com.google.gson.Gson;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import java.util.*;

@SuppressWarnings("ALL")
@Service
public class AliPayServiceImpl implements IAliPayService {

    private static Logger log = LoggerFactory.getLogger(AliPayServiceImpl.class);

    /**
     * 支付宝当面付2.0服务
     */
    @Autowired
    private AlipayTradeService tradeService;

    @Autowired
    private OrderDao orderDao;

    @Autowired
    private IPayOrderFlowService payOrderFlowService;

    @Autowired
    private PayOrderFlowDao payOrderFlowDao;
    @Autowired
    private OrderService orderService;

    @Autowired
    private UserDao userDao;

    @Autowired
    private OrderRefundService orderRefundService;

    @Autowired
    private DictDataDao dictDataDao;

    @Autowired
    private DictDao dictDao;

    private Properties prop = AlipayConfig.getProperties();

    static {
        /** 一定要在创建AlipayTradeService之前调用Configs.init()设置默认参数
         *  Configs会读取classpath下的zfbinfo.properties文件配置信息,如果找不到该文件则确认该文件是否在classpath目录
         */
        Configs.init("Alipay-Config.properties");
    }

    /**
     * 当面付生成支付二维码
     *
     * @param outTradeNo
     * @return
     */
    @Override
    public Map createNative(String outTradeNo) {
        // (必填) 商户网站订单系统中唯一订单号,64个字符以内,只能包含字母、数字、下划线,
        // 需保证商户系统端不能重复,建议通过数据库sequence生成,
        //订单号合法性校验
        Order order = PayCommonUtil.checkOutTradeNo(outTradeNo, true);

        // (必填) 订单标题,粗略描述用户的支付目的。如“xxx品牌xxx门店当面付扫码消费”
        String subject = "";

        // (必填) 订单总金额,单位为元,不能超过1亿元
        // 如果同时传入了【打折金额】,【不可打折金额】,【订单总金额】三者,则必须满足如下条件:【订单总金额】=【打折金额】+【不可打折金额】
        String totalAmount = order.getPrice() + "";

        // 卖家支付宝账号ID,用于支持一个签约账号下支持打款到不同的收款账号,(打款到sellerId对应的支付宝账号)
        // 如果该字段为空,则默认为与支付宝签约的商户的PID,也就是appid对应的PID
        String sellerId = "";

        // (必填) 商户门店编号,通过门店号和商家后台可以配置精准到门店的折扣信息,详询支付宝技术支持
        String storeId = "lvmajiayonggong";

        // 业务扩展参数,目前可添加由支付宝分配的系统商编号(通过setSysServiceProviderId方法),详情请咨询支付宝技术支持
        ExtendParams extendParams = new ExtendParams();
        extendParams.setSysServiceProviderId(Configs.getPid());

        // 支付超时,定义为120分钟
        String timeoutExpress = prop.getProperty("timeout_express");

        // 创建扫码支付请求builder,设置请求参数
        AlipayTradePrecreateRequestBuilder builder = new AlipayTradePrecreateRequestBuilder()
                .setSubject(subject).setTotalAmount(totalAmount).setOutTradeNo(outTradeNo)
                .setSellerId(sellerId).setExtendParams(extendParams).setStoreId(storeId)
                .setTimeoutExpress(timeoutExpress)
                //支付宝服务器主动通知商户服务器里指定的页面http路径
                .setNotifyUrl(prop.getProperty("precreate_notify"));

        AlipayF2FPrecreateResult result = tradeService.tradePrecreate(builder);
        switch (result.getTradeStatus()) {
            case SUCCESS:
                AlipayTradePrecreateResponse response = result.getResponse();
                Map responseMap = new HashMap(16);
                //二维码生成链接
                responseMap.put("codeUrl", response.getQrCode());
                //订单号
                responseMap.put("outTradeNo", outTradeNo);
                //金额
                responseMap.put("totalFee", order.getPrice());
                return responseMap;
            case FAILED:
                log.error("支付宝预下单失败!!!");
                ExceptionCast.cast(ResultEnum.ALI_PAY_TRADE_STATUS_FAILED);
                break;
            case UNKNOWN:
                log.error("系统异常,预下单状态未知!!!");
                ExceptionCast.cast(ResultEnum.ALI_PAY_TRADE_STATUS_UNKNOWN);
                break;
            default:
                log.error("不支持的交易状态,交易返回异常!!!");
                ExceptionCast.cast(ResultEnum.ALI_PAY_TRADE_STATUS_DEFAULT);
                break;
        }
        return null;
    }

    /**
     * 公共查询订单
     *
     * @param outTradeNo
     * @return
     */
    @Override
    public Result tradeQuery(String outTradeNo) {
        //订单号合法性校验
        Order order = PayCommonUtil.checkOutTradeNo(outTradeNo, true);
        // 创建查询请求builder,设置请求参数
        AlipayClient alipayClient = DefaultAlipayClientFactory.getAlipayClient();
        AlipayTradeQueryRequest alipayRequest = new AlipayTradeQueryRequest();
        alipayRequest.setBizContent("{\"out_trade_no\":\"" + outTradeNo + "\"}");
        //增加一个定时器,防止无限轮询下去
        int i = 0;
        try {
            //轮询
            while (true) {
                i++;

                AlipayTradeQueryResponse response = null;

                response = alipayClient.execute(alipayRequest);

                if ("TRADE_SUCCESS".equals(response.getTradeStatus())) {
                    //更新订单状态
                    order.setPayTime(new Date());
                    order.setPayType(OrderStatusName.ALI_PAY);
                    orderDao.save(order);
                    orderService.updateStatusBypay(order.getId());
                    return ResultUtil.success("支付成功");
                } else if ("TRADE_CLOSED".equals(response.getTradeStatus())) {
                    return ResultUtil.error("交易超时关闭或支付完成后全额退款");
                } else if ("UNKNOWN".equals(response.getTradeStatus())) {
                    return ResultUtil.error("系统异常,订单支付状态未知!!!");
                }
                try {
                    Thread.sleep(Integer.parseInt(prop.getProperty("query_duration")));
                } catch (InterruptedException e) {
                    e.printStackTrace();

                }
                //防止无限的轮询下去,增加一个计数器
                if (i >= 20) {
                    return ResultUtil.error("二维码超时");
                }
            }
        } catch (AlipayApiException e) {
            e.printStackTrace();
        }
        return ResultUtil.error("支付错误");
    }

    /**
     * 支付宝PC网站支付
     *
     * @param outTradeNo
     * @return
     */
    @Override
    public Result createPagePay(String outTradeNo) {
        //订单号合法性校验
        Order order = PayCommonUtil.checkOutTradeNo(outTradeNo, true);
        //如果不使用数据对象,就需要自己去拼接字符串。详情参考官网demo
        AlipayVo vo = new AlipayVo();
        //订单号
        vo.setOut_trade_no(outTradeNo);
        //价格
        vo.setTotal_amount(order.getPrice() + "");
        //标题
        vo.setSubject("");
        //这个是固定的
        vo.setProduct_code("FAST_INSTANT_TRADE_PAY");
        //超时时间
        vo.setTimeout_express(prop.getProperty("timeout_express"));
        //转json
        String json = new Gson().toJson(vo);
        log.info("json: {}", json);

        //公共请求参数
        AlipayClient alipayClient = DefaultAlipayClientFactory.getAlipayClient();
        // 设置请求参数
        AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest();
        alipayRequest.setReturnUrl(prop.getProperty("page_notify_URL"));
        alipayRequest.setNotifyUrl(prop.getProperty("return_URL"));
        alipayRequest.setBizContent(json);
        AlipayTradePagePayResponse response = null;
        try {
            response = alipayClient.pageExecute(alipayRequest);
        } catch (AlipayApiException e) {
            e.printStackTrace();
            if (e.getCause() instanceof java.security.spec.InvalidKeySpecException) {
                return ResultUtil.error("商户私钥格式不正确");
            }
        }
        log.info("result: {}", response.getBody());
        if (response.isSuccess()) {
            return ResultUtil.data(response, "请求成功");
        } else {
            return ResultUtil.error("支付宝预下单失败!!!");
        }

    }

    /**
     * 电脑网站和当面付的退款
     *
     * @param outTradeNo   订单号
     * @param refundAmount 退款金额
     * @return
     */
    @Override
    public Result tradePageRefund(OrderRefund refund) {
        //公共请求参数
        AlipayClient alipayClient = DefaultAlipayClientFactory.getAlipayClient();

        //设置请求参数
        AlipayTradeRefundRequest alipayRequest = new AlipayTradeRefundRequest();

        Random random = new Random();
        int i = random.nextInt();

        // (必填) 退款原因,可以说明用户退款原因,方便为商家后台提供统计
        String refundReason = "正常退款";
        //标识一次退款请求,同一笔交易多次退款需要保证唯一,如需部分退款,则此参数必传
        String outRequestNo = i + "";
        alipayRequest.setBizContent("{\"out_trade_no\":\"" + refund.getOrderId().getOrderUUID() + "\","
                + "\"refund_amount\":\"" + refund.getRufundPrice() + "\","
                + "\"refund_reason\":\"" + refundReason + "\","
                + "\"out_request_no\":\"" + outRequestNo + "\"}");

        AlipayTradeRefundResponse response = null;
        try {
            response = alipayClient.execute(alipayRequest);
            if ("Y".equals(response.getFundChange())) {
                //退款成功
                /**
                * 在这里处理你的业务逻辑
                */
            }
        } catch (AlipayApiException e) {
            e.printStackTrace();
            ExceptionCast.cast(ResultEnum.INVALID_PARAM);
        }
        this.createPayOrderFlow(response, 1);
        return ResultUtil.error("退款失败");
    }

    /**
     * 将request中的参数转换成Map
     * @param request
     * @return
     */
    public static Map<String, String> convertRequestParamsToMap(HttpServletRequest request) {
        Map<String, String> retMap = new HashMap<String, String>();

        Set<Map.Entry<String, String[]>> entrySet = request.getParameterMap().entrySet();

        for (Map.Entry<String, String[]> entry : entrySet) {
            String name = entry.getKey();
            String[] values = entry.getValue();
            int valLen = values.length;

            if (valLen == 1) {
                retMap.put(name, values[0]);
            } else if (valLen > 1) {
                StringBuilder sb = new StringBuilder();
                for (String val : values) {
                    sb.append(",").append(val);
                }
                retMap.put(name, sb.toString().substring(1));
            } else {
                retMap.put(name, "");
            }
        }

        return retMap;
    }
}

8.AliPayNotifyController.java(支付成功后的异步通知)

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.demo.trade.config.Configs;
import io.swagger.annotations.Api;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.*;

@Slf4j
@RestController
@Api(description = "支付宝支付异步回调接口")
@RequestMapping("/xboot/pay/aliPayNotify")
public class AliPayNotifyController {

    @Autowired
    private OrderService orderService;

    private static Logger logger = LoggerFactory.getLogger(AliPayNotifyController.class);


 //构建线程池
private static final ThreadPoolExecutor EXECUTOR_SERVICE = new ThreadPoolExecutor(5,8,0,TimeUnit.MILLISECONDS,new ArrayBlockingQueue<>(10),
        new BasicThreadFactory.Builder().namingPattern("schedule-pool-%d").daemon(true).build(),new ThreadPoolExecutor.CallerRunsPolicy());


    @Autowired
    private IPayOrderFlowService payOrderFlowService;

    @Autowired
    private UserDao userDao;

    @Autowired
    private OrderDao orderDao;

    static {

        Configs.init("Alipay-Config.properties");// 支付宝配置
    }

    /**
     * 扫码支付成功异步通知接口
     * <pre>
     * 第一步:验证签名,签名通过后进行第二步
     * 第二步:按一下步骤进行验证
     * 1、商户需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号,
     * 2、判断total_amount是否确实为该订单的实际金额(即商户订单创建时的金额),
     * 3、校验通知中的seller_id(或者seller_email) 是否为out_trade_no这笔单据的对应的操作方(有的时候,一个商户可能有多个seller_id/seller_email),
     * 4、验证app_id是否为该商户本身。上述1、2、3、4有任何一个验证不通过,则表明本次通知是异常通知,务必忽略。
     * 在上述验证通过后商户必须根据支付宝不同类型的业务通知,正确的进行不同的业务处理,并且过滤重复的通知结果数据。
     * 在支付宝的业务通知中,只有交易通知状态为TRADE_SUCCESS或TRADE_FINISHED时,支付宝才会认定为买家付款成功。
     * </pre>
     *
     * @param
     * @return
     */
    @PostMapping("aliPay_precreate_notify")
    public void aliPayPrecreateNotify(HttpServletRequest request, HttpServletResponse response) {
        // 将异步通知中收到的待验证所有参数都存放到map中
        Map<String, String> params = AliPayServiceImpl.convertRequestParamsToMap(request);
        System.out.println(params);
        String paramsJson = JSON.toJSONString(params);
        logger.info("支付宝回调,{}", paramsJson);
        ServletOutputStream outputStream = null;
        try {

            // 调用SDK验证签名
            boolean signVerified = AlipaySignature.rsaCheckV1(params, Configs.getAlipayPublicKey(),
                    "UTF-8", Configs.getSignType());
            if (signVerified) {
                logger.info("支付宝回调签名认证成功");
                // 按照支付结果异步通知中的描述,对支付结果中的业务内容进行1\2\3\4二次校验,校验成功后在response中返回success,校验失败返回failure
                this.check(params);
                // 另起线程处理业务
                EXECUTOR_SERVICE .execute(() -> {
                    //接收响应参数的实体
                    AliPayNotifyParam param = buildAlipayNotifyParam(params);
                    String trade_status = param.getTradeStatus();
                    // 支付成功
                    if (AliPayTradeStatus.TRADE_SUCCESS.equals(trade_status)
                            || AliPayTradeStatus.TRADE_FINISHED.equals(trade_status)) {
                        try {
                             // 在这里处理支付成功逻辑
                         } catch (Exception e) {
                            logger.error("支付宝回调业务处理报错,params:" + paramsJson, e);
                        }
                    } else {
                        logger.error("没有处理支付宝回调业务,支付宝交易状态:{},params:{}", trade_status, paramsJson);
                    }

                });
                // 如果签名验证正确,立即返回success,后续业务另起线程单独处理
                // 业务处理失败,可查看日志进行补偿,跟支付宝已经没多大关系。
                outputStream = response.getOutputStream();
                outputStream.println("success");
                return;
            } else {
                logger.info("支付宝回调签名认证失败,signVerified=false, paramsJson:{}", paramsJson);
                outputStream.println("failure");
            }
        } catch (AlipayApiException e) {
            logger.error("支付宝回调签名认证失败,paramsJson:{},errorMsg:{}", paramsJson, e.getMessage());
            ExceptionCast.cast(ResultEnum.INVALID_PARAM);

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                outputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * pc网站支付异步通知接口
     *
     * @return
     * @throws AlipayApiException
     */
    @PostMapping("/aliPay_page_notify")
    public void aliPayPageNotify(HttpServletRequest request, HttpServletResponse response) {
        Map<String, String> params = AliPayServiceImpl.convertRequestParamsToMap(request);
        logger.info("notify params: {}", JSONObject.toJSON(params));

        // 验证签名
        boolean signVerified = false;
        try {
            signVerified = AlipaySignature.rsaCheckV1(params, Configs.getAlipayPublicKey(), "UTF-8", Configs.getSignType());

            logger.info("notify signVerified: {}", signVerified);
            if (signVerified) {
                Order order = this.check(params);
                // 另起线程处理业务
                EXECUTOR_SERVICE .execute(() -> {
                    AliPayNotifyParam param = buildAlipayNotifyParam(params);
                    String tradeStatus = param.getTradeStatus();
                    // 支付成功
                    if (AliPayTradeStatus.TRADE_SUCCESS.equals(tradeStatus)
                            || AliPayTradeStatus.TRADE_FINISHED.equals(tradeStatus)) {
                        //在这里处理你的业务逻辑
                        //判断该笔订单是否在商户网站中已经做过处理
            			//如果没有做过处理,根据订单号(out_trade_no)在商户网站的订单系统中查到该笔订单的详细,并执行商户的业务程序
                       //如果有做过处理,不执行商户的业务程序
			
            			//注意:
                			//付款完成后,支付宝系统发送该交易状态通知
                    } else {
                        logger.error("没有处理支付宝回调业务,支付宝交易状态:{},params:{}", params.get("trade_status"), JSONObject.toJSON(params));
                    }

                });
                // 如果签名验证正确,立即返回success,后续业务另起线程单独处理
                // 业务处理失败,可查看日志进行补偿,跟支付宝已经没多大关系。
                response.getOutputStream().println("success");
            } else {
                logger.info("验证失败,不去更新状态");
                response.getOutputStream().println("failure");
            }
        } catch (AlipayApiException e) {
            ExceptionCast.cast(ResultEnum.INVALID_PARAM);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    private AliPayNotifyParam buildAlipayNotifyParam(Map<String, String> params) {
        String json = JSON.toJSONString(params);
        return JSON.parseObject(json, AliPayNotifyParam.class);
    }

    /**
     * 1、商户需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号,
     * 2、判断total_amount是否确实为该订单的实际金额(即商户订单创建时的金额),
     * 3、校验通知中的seller_id(或者seller_email)是否为out_trade_no这笔单据的对应的操作方(有的时候,一个商户可能有多个seller_id/seller_email),
     * 4、验证app_id是否为该商户本身。上述1、2、3、4有任何一个验证不通过,则表明本次通知是异常通知,务必忽略。
     * 在上述验证通过后商户必须根据支付宝不同类型的业务通知,正确的进行不同的业务处理,并且过滤重复的通知结果数据。
     * 在支付宝的业务通知中,只有交易通知状态为TRADE_SUCCESS或TRADE_FINISHED时,支付宝才会认定为买家付款成功。
     *
     * @param params
     * @throws AlipayApiException
     */
    private Order check(Map<String, String> params) throws AlipayApiException {
        String outTradeNo = params.get("out_trade_no");

        // 1、商户需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号,
        Order order = orderService.findByOrderUUID(outTradeNo);
        if (order == null) {
            throw new AlipayApiException("out_trade_no错误");
        }

        // 2、判断total_amount是否确实为该订单的实际金额(即商户订单创建时的金额),
        BigDecimal bigDecimal = new BigDecimal(params.get("total_amount"));
        if (order.getPrice().compareTo(bigDecimal) != 0) {
            throw new AlipayApiException("error total_amount");
        }

        // 3、校验通知中的seller_id(或者seller_email)是否为out_trade_no这笔单据的对应的操作方(有的时候,一个商户可能有多个seller_id/seller_email),
        // 第三步可根据实际情况省略

        // 4、验证app_id是否为该商户本身。
        if (!params.get("app_id").equals(Configs.getAppid())) {
            throw new AlipayApiException("app_id不一致");
        }

        return order;
    }

}

9.AliPayNotifyParam.java

/**
* 支付宝支付结果异步响应实体
*/
@Data
public class AliPayNotifyParam {

    private String appId;
    private String tradeNo; // 支付宝交易凭证号
    private String outTradeNo; // 原支付请求的商户订单号
    private String outBizNo; // 商户业务ID,主要是退款通知中返回退款申请的流水号
    private String buyerId; // 买家支付宝账号对应的支付宝唯一用户号。以2088开头的纯16位数字
    private String buyerLogonId; // 买家支付宝账号
    private String sellerId; // 卖家支付宝用户号
    private String sellerEmail; // 卖家支付宝账号
    private String tradeStatus; // 交易目前所处的状态,见交易状态说明
    private BigDecimal totalAmount; // 本次交易支付的订单金额
    private BigDecimal receiptAmount; // 商家在交易中实际收到的款项
    private BigDecimal buyerPayAmount; // 用户在交易中支付的金额
    private BigDecimal refundFee; // 退款通知中,返回总退款金额,单位为元,支持两位小数
    private String subject; // 商品的标题/交易标题/订单标题/订单关键字等
    private String body; // 该订单的备注、描述、明细等。对应请求时的body参数,原样通知回来
    private Date gmtCreate; // 该笔交易创建的时间。格式为yyyy-MM-dd HH:mm:ss
    private Date gmtPayment; // 该笔交易的买家付款时间。格式为yyyy-MM-dd HH:mm:ss
    private Date gmtRefund; // 该笔交易的退款时间。格式为yyyy-MM-dd HH:mm:ss.S
    private Date gmtClose; // 该笔交易结束时间。格式为yyyy-MM-dd HH:mm:ss
    private String fundBillList; // 支付成功的各个渠道金额信息,array
    private String passbackParams; // 公共回传参数,如果请求时传递了该参数,则返回给商户时会在异步通知时将该参数原样返回。

}

ps:蚂蚁金服开放平台SDK标准版现以官方的名义上传至 Maven 中央仓库,对接的商户有用到mvn项目的可以使用,maven地址:

   <dependency>
        <groupId>com.alipay.sdk</groupId>
        <artifactId>alipay-sdk-java</artifactId>
        <version>3.3.49.ALL</version>
    </dependency>
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值