SpringBoot对接支付宝支付API(沙箱环境)


支付宝开放平台:https://open.alipay.com/

支付流程介绍

该图描述了消费者和商户【开发者】服务器和支付宝服务间的请求流程,可以看到用户是通过商户的服务器进行发送支付请求,再由消费者输入相关用户登录信息和支付信息【该流程商户服务器无法干预和监听】,用户和支付宝方的结果会由支付宝服务器通知回商户服务器,商户服务器可以编写对应的逻辑去处理。

获取相关支付秘钥信息

1、首先登陆支付宝开放平台,然后进入管理中心

登录后点击该服务,进入后可以查看到属于自己的沙箱测试账号和一些公钥私钥、商户appid等信息

支付宝开放平台还提供了一些工具和DEMO:

页面拉到中间可以下载示例(这里的手机网站支付下载下来是jsp的)。

支付宝API文档:https://opendocs.alipay.com/apis

电脑网站支付

1、引入相关依赖



    com.alipay.sdk
    alipay-sdk-java
    4.17.5.ALL


    com.alipay.sdk
    alipay-easysdk
    2.1.2




    org.apache.commons
    commons-lang3
    3.3.2

2、在resources目录下创建static文件夹,存在静态html、js、css等等文件

error_url.html




    
    支付宝支付接口


支付失败

notify_url.html




    
    支付宝支付接口测试


通知页面

return_url.html




    
    支付宝支付接口测试


支付成功

3、相关Java代码

1)常量:包括支付宝公私钥、商户appid等

public final class PayConstants {

    // 商户appid
    public static final String APP_ID = "20003304";
    // 私钥 pkcs8格式的
    public static final String RSA_PRIVATE_KEY = "MIIEvQIBADANBgkqhStMpac+XQVRjF7JeckeOuGvD8a3ouA20NLXTa++UBbCzu9JSl/UAFGPqLTccvQwJuG+2362ExHdx+sJm//3X3dvYxCMlRKzOkbHf66ikkhe//tKJrGSkWVJaZ/t96q1l0ueaYFuMJBhefbEPypAwap+XGk4BHBuYi7pkM9y2ErmtRCwrRh1IAu2rsk+ZlUtwu5RZpNS4lxFbMzHp+a8SasxtBf9fCRrycGKS92PHZCcPGvQS0ozYYwJOLUTbjtnDol/3m9mG0PVjIXaLDCgSaEb8l6ZMasCoLIzNf2F76PQ2wuqQSBXw1AgMBAAECggEBALBNnbAlgVp8RUcTKDxgmQ5JwrFJ4SD7SxyIHuVti8Ci+gdU+ld0NX/8LSucNELX3aV5ahlzWMxUq4LWaq4zPrf+pDc40ZBEYhuPQNe3ETJ3jT2A9W4GytyIWCDU/eFyjSETfzlefRkf9TRxBk4Ymawe7i8cWHmFQZHM6ZbTSDWqWU1xv7YZsQsPtNHitALxkMyWLgT0L7F/r8K3fUhg3KvSHiKRrfzzB0xE/kng4dvif8cPTa6TWFutPoBodVhzqh+OCJVwK0vxk4boxIx6e8cyzu+BV3naOCp+jYThGym3HPst2JSSO8JuwXSfn29UFPuPyTiddD/plifTDTnqeKECgYEA3pPFQ3AMg01a2aVHt2G/i6zzuj6Uci4L9E40XOUXAOwVQBm7TZKE5iePFzYvtc8in+q3hNckuGl9pn8REHribnYtVkTUGv0P5rv6LPZ1bs7j+zNDihyWC9WCSsOgyMcUVs9kx70ByJpZaUgKYXVGnRXqfFxDbC73KN/M74emVbkCgYEAzyozdl1F8JTSSyxEtapyBOEmicqlQybH0Bogm3aNd/myWeEbCkC7h+2DztgqCbmQXj1A16rv2M7JnEbicCqBzF2KgeEQzvki+1l7DE20XTZ26k/W5P+ITgmOWtRzll57tl4Nrxy0Qa3UIpi2aaLP8m1Vt1wNm6jQW/L59+6HGF0CgYAlpZ7vCivDpbjhKViZBvqMGIPdE6quKWWyO+wVblZWvJfXkMKim07JUxWb2nl1agS0QdIqsO0wF/+9wq5TOEXGY0zF07yAmB8xasglBMbWKD5dhc09thoXBFUWakJu02V4TXoBYtHDaYAIE7xVwa1g8o0H3d81lDgP/NLADRSwWQKBgFcb0pnRrIf1u9s6GQYeTmU42wCTghwTYW9inRVhqyioiNL8MWeFQpVsTMEb6ck/CrJlpS9KRCHxCZs5vxsU9JEfQA9OuMeewJPZ00CgJ9gN/8CUo7KUtBbEM+SDcrrmrbRIJg7czfrUAeAuQH14pWG1kZZREbPDLyGKsIsM9CdNAoGAHrka7bPx2LfEgqRVebJlDs0ZpKitlW0d9W3N8o/ZK0xRPiWuhHAUCti2TFaTSrf50bmuXDOAzcQ0N/eoJFw5DFW66tpVsvb68DFG/JdGwsBuMn8uqsyBY0BOAMsuPKFalmWmJwYkIgFxqmybb5zEe/2oa3bGU3tt1Y0YXPfprs4=";
    // 服务器异步通知页面路径 需http://或者https://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问
    public static final String NOTIFY_URL = "http://127.0.0.1:9999/notify_url.html";
    // 页面跳转同步通知页面路径 需http://或者https://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问 商户可以自定义同步跳转地址
    public static final String RETURN_URL = "http://127.0.0.1:9999/return_url.html";
    // 请求网关地址,沙箱环境网关比正式环境网关多了 "dev" 。https://openapi.alipay.com/gateway.do
    public static final String GATEWAY_URL = "https://openapi.alipaydev.com/gateway.do";
    // 编码
    public static final String CHARSET = "UTF-8";
    // 返回格式
    public static final String FORMAT = "json";
    // 支付宝公钥,而不是应用公钥
    public static final String ALIPAY_PUBLIC_KEY = "MIIBIjANBgkqhkiG9w0BAQEFAAOC4d/lq7M9igfQpgfh+DHsSYN93d5R17v0Ir8Vm32e+1wguQj8yF8v+/9U43z5iu/7iFqzkPK9u/wMHsRIyaICqbL+wfwRXnoJQR+9QCr76jnZ7p2rlSIRbKXXrlhYHdpsyh3hMHlknaGVorFfdBD5X3Z5M+U6+Mx0SY2MgrKU0U9094txU4rZGB3fL6tORwjA561Q5NPcHfPTAxhRv8agXGYhr6sb9VlEoOkXo4sRc26M3r3B17btPIN6rlV5AJQRuTPUcMv/OirhMBf4wIDAQAB";
    // 日志记录目录
    public static String log_path = "D:/tmp/logs/";
    // RSA2
    public static final String SIGN_TYPE = "RSA2";

}

2)LogFile.java

public class LogFile {
	 /** 
     * 写日志,方便测试(看网站需求,也可以改成把记录存入数据库)
     * @param sWord 要写入日志里的文本内容
     */
    public static void logResult(String sWord) {
        FileWriter writer = null;
        try {
            writer = new FileWriter(PayConstants.log_path + "alipay_log_" + System.currentTimeMillis()+".txt");
            writer.write(sWord);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (writer != null) {
                try {
                    writer.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }   
}

3)测试验证AlipayPcController.java

/**
 * 电脑网站支付
 */
@Controller
@RequestMapping("/api/pc")
public class AlipayPcController {

    /**
     * alipay.trade.page.pay(统一收单下单并支付页面接口)
     */
    @RequestMapping("/pcTradePay")
    public void pcTradePay(HttpServletRequest servletRequest, HttpServletResponse servletResponse) throws AlipayApiException, IOException {
        //=================== 1、获取alipay客户端
        AlipayClient alipayClient = getAlipayClient();

        //=================== 2、请求参数
        AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
        //服务器异步通知页面路径
        request.setNotifyUrl(PayConstants.NOTIFY_URL);
        //页面跳转同步通知页面路径
        request.setReturnUrl(PayConstants.RETURN_URL);
        JSONObject bizContent = new JSONObject();
        String outTradeNo = getTradeNo();
        System.out.println("生成的out_trade_no:" + outTradeNo);
        LogFile.logResult("/api/pc/pcTradePay生成的out_trade_no:" + outTradeNo);
        bizContent.put("out_trade_no", outTradeNo);
        bizContent.put("total_amount", 0.01);
        bizContent.put("subject", "测试商品");//订单标题。注意:不可使用特殊字符,如 /,=,& 等。
        bizContent.put("product_code", "FAST_INSTANT_TRADE_PAY");
        //bizContent.put("time_expire", "2022-08-01 22:00:00");

        //// 商品明细信息,按需传入
        //JSONArray goodsDetail = new JSONArray();
        //JSONObject goods1 = new JSONObject();
        //goods1.put("goods_id", "goodsNo1");
        //goods1.put("goods_name", "子商品1");
        //goods1.put("quantity", 1);
        //goods1.put("price", 0.01);
        //goodsDetail.add(goods1);
        //bizContent.put("goods_detail", goodsDetail);

        //// 扩展信息,按需传入
        //JSONObject extendParams = new JSONObject();
        //extendParams.put("sys_service_provider_id", "2088511833207846");
        //bizContent.put("extend_params", extendParams);
        request.setBizContent(bizContent.toString());

        //=================== 3、响应内容
        AlipayTradePagePayResponse response = alipayClient.pageExecute(request);
        if (response.isSuccess()) {
            System.out.println("支付调用成功");
        } else {
            System.out.println("支付调用失败");
        }
        //响应为表单格式,可嵌入页面,具体以返回的结果为准
        String form = response.getBody();
        servletResponse.setContentType("text/html;charset=" + PayConstants.CHARSET);
        servletResponse.getWriter().write(form);//直接将完整的表单html输出到页面
        servletResponse.getWriter().flush();
        servletResponse.getWriter().close();
    }

    /**
     * alipay.trade.query(统一收单线下交易查询)
     */
    @RequestMapping("/pcTradeQuery")
    public void pcTradeQuery(HttpServletRequest servletRequest, HttpServletResponse servletResponse, @RequestParam("outTradeNo") String outTradeNo) throws AlipayApiException, IOException {
        //=================== 1、获取alipay客户端
        AlipayClient alipayClient = getAlipayClient();

        //=================== 2、请求参数
        AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
        JSONObject bizContent = new JSONObject();
        //bizContent.put("out_trade_no", "20220501120853429");
        bizContent.put("out_trade_no", outTradeNo);
        //bizContent.put("trade_no", "2014112611001004680073956707");
        request.setBizContent(bizContent.toString());

        //=================== 3、响应内容
        AlipayTradeQueryResponse response = alipayClient.execute(request);
        if (response.isSuccess()) {
            System.out.println("交易查询调用成功");
        } else {
            System.out.println("交易查询调用失败");
        }
        //响应为json格式
        String body = response.getBody();
        servletResponse.setContentType("application/json;charset=" + PayConstants.CHARSET);
        servletResponse.getWriter().write(body);
        servletResponse.getWriter().flush();
        servletResponse.getWriter().close();
    }

    /**
     * alipay.trade.refund(统一收单交易退款接口)
     */
    @RequestMapping("/pcTradeRefund")
    public void pcTradeRefund(HttpServletRequest servletRequest, HttpServletResponse servletResponse, @RequestParam("tradeNo") String tradeNo) throws AlipayApiException, IOException {
        //=================== 1、获取alipay客户端
        AlipayClient alipayClient = getAlipayClient();

        //=================== 2、请求参数
        AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
        JSONObject bizContent = new JSONObject();
        //bizContent.put("trade_no", "2022050122001421280506656470");
        bizContent.put("trade_no", tradeNo);
        bizContent.put("refund_amount", 0.01);
        bizContent.put("out_request_no", "HZ01RF001");

        //// 返回参数选项,按需传入
        //JSONArray queryOptions = new JSONArray();
        //queryOptions.add("refund_detail_item_list");
        //bizContent.put("query_options", queryOptions);

        request.setBizContent(bizContent.toString());

        //=================== 3、响应内容
        AlipayTradeRefundResponse response = alipayClient.execute(request);
        if (response.isSuccess()) {
            System.out.println("退款调用成功");
        } else {
            System.out.println("退款调用失败");
        }

        //响应为json格式
        String body = response.getBody();
        servletResponse.setContentType("application/json;charset=" + PayConstants.CHARSET);
        servletResponse.getWriter().write(body);
        servletResponse.getWriter().flush();
        servletResponse.getWriter().close();
    }

    /**
     * alipay.trade.fastpay.refund.query(统一收单交易退款查询)
     */
    @RequestMapping("/pcRefundQuery")
    public void pcRefundQuery(HttpServletRequest servletRequest, HttpServletResponse servletResponse, @RequestParam("tradeNo") String tradeNo) throws AlipayApiException, IOException {
        //=================== 1、获取alipay客户端
        AlipayClient alipayClient = getAlipayClient();

        //=================== 2、请求参数
        AlipayTradeFastpayRefundQueryRequest request = new AlipayTradeFastpayRefundQueryRequest();
        JSONObject bizContent = new JSONObject();
        //bizContent.put("trade_no", "2022050122001421280506656470");
        bizContent.put("trade_no", tradeNo);
        bizContent.put("out_request_no", "HZ01RF001");

        //// 返回参数选项,按需传入
        //JSONArray queryOptions = new JSONArray();
        //queryOptions.add("refund_detail_item_list");
        //bizContent.put("query_options", queryOptions);
        request.setBizContent(bizContent.toString());

        //=================== 3、响应内容
        AlipayTradeFastpayRefundQueryResponse response = alipayClient.execute(request);
        if (response.isSuccess()) {
            System.out.println("调用成功");
        } else {
            System.out.println("调用失败");
        }

        //响应为json格式
        if ("REFUND_SUCCESS".equals(response.getRefundStatus())) {
            System.out.println("退款成功");
        }
        servletResponse.setContentType("application/json;charset=" + PayConstants.CHARSET);
        servletResponse.getWriter().write(response.getBody());
        servletResponse.getWriter().flush();
        servletResponse.getWriter().close();
    }

    /**
     * alipay.trade.close(统一收单交易关闭接口)
     */
    @RequestMapping("/closePcTrade")
    public void closePcTrade(HttpServletRequest servletRequest, HttpServletResponse servletResponse, @RequestParam("tradeNo") String tradeNo) throws AlipayApiException, IOException {
        //=================== 1、获取alipay客户端
        AlipayClient alipayClient = getAlipayClient();

        //=================== 2、请求参数
        AlipayTradeCloseRequest request = new AlipayTradeCloseRequest();
        JSONObject bizContent = new JSONObject();
        //bizContent.put("trade_no", "2022050122001421280506656471");
        bizContent.put("trade_no", tradeNo);
        request.setBizContent(bizContent.toString());

        //=================== 3、响应内容
        AlipayTradeCloseResponse response = alipayClient.execute(request);
        if(response.isSuccess()){
            System.out.println("调用成功");
        } else {
            System.out.println("调用失败");
        }

        //响应为json格式
        String body = response.getBody();
        servletResponse.setContentType("application/json;charset=" + PayConstants.CHARSET);
        servletResponse.getWriter().write(body);
        servletResponse.getWriter().flush();
        servletResponse.getWriter().close();

    }

    //alipay客户端配置(重要,必须保证正确)
    private AlipayClient getAlipayClient() throws AlipayApiException {
        AlipayConfig alipayConfig = new AlipayConfig();
        //支付网关, 默认是正式环境:https://openapi.alipay.com/gateway.do,此处使用沙箱环境测试
        alipayConfig.setServerUrl(PayConstants.GATEWAY_URL);
        //商户appid
        alipayConfig.setAppId(PayConstants.APP_ID);
        //私钥 pkcs8格式的
        alipayConfig.setPrivateKey(PayConstants.RSA_PRIVATE_KEY);
        //返回格式
        alipayConfig.setFormat(PayConstants.FORMAT);
        //编码
        alipayConfig.setCharset(PayConstants.CHARSET);
        //支付宝公钥,而不是应用公钥
        alipayConfig.setAlipayPublicKey(PayConstants.ALIPAY_PUBLIC_KEY);
        //RSA2
        alipayConfig.setSignType(PayConstants.SIGN_TYPE);
        return new DefaultAlipayClient(alipayConfig);
    }

    //生成交易订单号
    private String getTradeNo() {
        //时间(精确到毫秒)
        DateTimeFormatter ofPattern = DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS");
        String localDate = LocalDateTime.now().format(ofPattern);
        //随机数
        //String randomNumeric = RandomStringUtils.randomNumeric(8);
        //return localDate + randomNumeric;
        return localDate;
    }
}

扫码支付

扫码支付,指用户打开支付宝钱包中的“扫一扫”功能,扫描商家展示在某收银场景下的二维码并进行支付的模式。该模式适用于线下实体店支付、面对面支付等场景。

业务流程:

使用步骤:

  1. 收银员在商家收银系统操作生成支付宝订单,并生成二维码;
  2. 用户登录支付宝钱包,点击首页“付款-扫码付”或直接点击“扫一扫”,进入扫一扫界面;
  3. 用户扫收银员提供的二维码,核对金额,确认支付;
  4. 用户付款后商家收银系统会拿到支付成功或者失败的结果。

产品特点:

  • 用户仅出示手机扫码即可完成付款,方便快捷;
  • 资金实时到账,无现金流压力。

基本上我们开发常用的也就是一个生成二维码的预创建订单接口,一个取消,一个退款,一个支付成功后的回调接口(有些接口是通用接口,我们上面电脑网站支付已经演示,这里就不再演示)

1、引入相关的依赖



    com.alipay.sdk
    alipay-sdk-java
    4.17.5.ALL


    com.alipay.sdk
    alipay-easysdk
    2.1.2




    com.google.zxing
    core
    3.3.0


    com.google.zxing
    javase
    3.3.0



    org.apache.commons
    commons-lang3
    3.3.2

2、二维码生成工具类

/**
 * 二维码生成
 */
public class QRCodeGeneratorUtil {

    //二维码图片路径
    public static final String QR_CODE_IMAGE_PATH = "D:/tmp/images/";

    /**
     * 生成二维码
     *
     * @param text     文本或请求链接
     * @param width    二维码图片宽度
     * @param height   二维码图片高度
     * @param filePath 二维码存放位置(包括文件名)
     * @throws WriterException
     * @throws IOException
     */
    public static void generateQRCodeImage(String text, int width, int height, String filePath) throws WriterException, IOException {
        QRCodeWriter qrCodeWriter = new QRCodeWriter();
        BitMatrix bitMatrix = qrCodeWriter.encode(text, BarcodeFormat.QR_CODE, width, height);
        Path path = FileSystems.getDefault().getPath(filePath);
        MatrixToImageWriter.writeToPath(bitMatrix, "PNG", path);
    }
}

3、测试验证

/**
 * 当面付-扫码支付
 */
@Controller
@RequestMapping("/api/scanCode")
public class AlipayScanCodeController {


    /**
     * alipay.trade.precreate(统一收单线下交易预创建)
     * 收银员通过收银台或商户后台调用支付宝接口,生成二维码后,展示给用户,由用户扫描二维码完成订单支付。
     */
    @RequestMapping("/preCreateScanCodeTrade")
    public void preCreateScanCodeTrade(HttpServletRequest servletRequest, HttpServletResponse servletResponse) throws AlipayApiException, IOException, WriterException {
        //=================== 1、获取alipay客户端
        AlipayClient alipayClient = getAlipayClient();

        //=================== 2、创建订单请求参数
        AlipayTradePrecreateRequest request = new AlipayTradePrecreateRequest();
        request.setNotifyUrl(PayConstants.NOTIFY_URL);
        JSONObject bizContent = new JSONObject();
        String outTradeNo = getTradeNo();
        LogFile.logResult("preCreateScanCodeTrade outTradeNo = " + outTradeNo);
        bizContent.put("out_trade_no", outTradeNo);
        //bizContent.put("out_trade_no", "20210817010101003");
        bizContent.put("total_amount", 0.01);
        bizContent.put("subject", "测试商品");

        //// 商品明细信息,按需传入
        //JSONArray goodsDetail = new JSONArray();
        //JSONObject goods1 = new JSONObject();
        //goods1.put("goods_id", "goodsNo1");
        //goods1.put("goods_name", "子商品1");
        //goods1.put("quantity", 1);
        //goods1.put("price", 0.01);
        //goodsDetail.add(goods1);
        //bizContent.put("goods_detail", goodsDetail);

        //// 扩展信息,按需传入
        //JSONObject extendParams = new JSONObject();
        //extendParams.put("sys_service_provider_id", "2088511833207846");
        //bizContent.put("extend_params", extendParams);

        //// 结算信息,按需传入
        //JSONObject settleInfo = new JSONObject();
        //JSONArray settleDetailInfos = new JSONArray();
        //JSONObject settleDetail = new JSONObject();
        //settleDetail.put("trans_in_type", "defaultSettle");
        //settleDetail.put("amount", 0.01);
        //settleDetailInfos.add(settleDetail);
        //settleInfo.put("settle_detail_infos", settleDetailInfos);
        //bizContent.put("settle_info", settleInfo);

        //// 二级商户信息,按需传入
        //JSONObject subMerchant = new JSONObject();
        //subMerchant.put("merchant_id", "2088000603999128");
        //bizContent.put("sub_merchant", subMerchant);

        //// 业务参数信息,按需传入
        //JSONObject businessParams = new JSONObject();
        //businessParams.put("busi_params_key", "busiParamsValue");
        //bizContent.put("business_params", businessParams);

        //// 营销信息,按需传入
        //JSONObject promoParams = new JSONObject();
        //promoParams.put("promo_params_key", "promoParamsValue");
        //bizContent.put("promo_params", promoParams);

        request.setBizContent(bizContent.toString());

        //=================== 3、响应内容
        AlipayTradePrecreateResponse response = alipayClient.execute(request);
        if (response.isSuccess()) {
            System.out.println("调用成功");
        } else {
            System.out.println("调用失败");
        }
        //拿到二维码的链接,使用工具生成二维码在页面展示即可
        System.out.println("二维码扫描的链接qrCode = " + response.getQrCode());
        String imagePath = QRCodeGeneratorUtil.QR_CODE_IMAGE_PATH + outTradeNo + "code.png";
        QRCodeGeneratorUtil.generateQRCodeImage(response.getQrCode(), 350, 350, imagePath);

        //响应为json格式,输出到浏览器看看
        servletResponse.setContentType("application/json;charset=" + PayConstants.CHARSET);
        servletResponse.getWriter().write(response.getBody());
        servletResponse.getWriter().flush();
        servletResponse.getWriter().close();
    }


    /**
     * alipay.trade.cancel(统一收单交易撤销接口)
     * 支付交易返回失败或支付系统超时,调用该接口撤销交易。
     * 如果此订单用户支付失败,支付宝系统会将此订单关闭;如果用户支付成功,支付宝系统会将此订单资金退还给用户。
     * 注意:只有发生支付系统超时或者支付结果未知时可调用撤销,其他正常支付的单如需实现相同功能只能调用申请退款API。
     * 提交支付交易后调用【查询订单API】,没有明确的支付结果再调用【撤销订单API】。
     */
    @RequestMapping("/cancelTrade")
    public void cancelTrade(HttpServletRequest servletRequest, HttpServletResponse servletResponse, @RequestParam("outTradeNo") String outTradeNo) throws AlipayApiException, IOException {
        //=================== 1、获取alipay客户端
        AlipayClient alipayClient = getAlipayClient();

        //=================== 3、请求参数
        AlipayTradeCancelRequest request = new AlipayTradeCancelRequest();
        JSONObject bizContent = new JSONObject();
        bizContent.put("out_trade_no", outTradeNo);
        //bizContent.put("out_trade_no", "20150320010101001");
        request.setBizContent(bizContent.toString());

        //=================== 3、响应内容
        AlipayTradeCancelResponse response = alipayClient.execute(request);
        if (response.isSuccess()) {
            System.out.println("调用成功");
        } else {
            System.out.println("调用失败");
        }
        //响应为json格式,输出到浏览器看看
        servletResponse.setContentType("application/json;charset=" + PayConstants.CHARSET);
        servletResponse.getWriter().write(response.getBody());
        servletResponse.getWriter().flush();
        servletResponse.getWriter().close();
    }

    //alipay客户端配置(重要,必须保证正确)
    private AlipayClient getAlipayClient() throws AlipayApiException {
        AlipayConfig alipayConfig = new AlipayConfig();
        //支付网关, 默认是正式环境:https://openapi.alipay.com/gateway.do,此处使用沙箱环境测试
        alipayConfig.setServerUrl(PayConstants.GATEWAY_URL);
        //商户appid
        alipayConfig.setAppId(PayConstants.APP_ID);
        //私钥 pkcs8格式的
        alipayConfig.setPrivateKey(PayConstants.RSA_PRIVATE_KEY);
        //返回格式
        alipayConfig.setFormat(PayConstants.FORMAT);
        //编码
        alipayConfig.setCharset(PayConstants.CHARSET);
        //支付宝公钥,而不是应用公钥
        alipayConfig.setAlipayPublicKey(PayConstants.ALIPAY_PUBLIC_KEY);
        //RSA2
        alipayConfig.setSignType(PayConstants.SIGN_TYPE);
        return new DefaultAlipayClient(alipayConfig);
    }

    //生成交易订单号
    private String getTradeNo() {
        //时间(精确到毫秒)
        DateTimeFormatter ofPattern = DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS");
        String localDate = LocalDateTime.now().format(ofPattern);
        //随机数
        //String randomNumeric = RandomStringUtils.randomNumeric(8);
        //return localDate + randomNumeric;
        return localDate;
    }
}