支付宝扫码支付,回调和退款(沙箱环境配置和完整代码)


支付宝扫码支付


最近重构项目时,负责了支付模块,微信扫码支付(NATIVE)和 支付宝扫码支付,也是第一次接触,虽然根据官方文档和一些博客写出来了,但是遇到的问题却很多,走了很多弯路,浪费了很多精力和时间,抽出时间来记录一下,以后难免还是会用到。

微信扫码支付传送门

支付宝支付需要沙箱测试,需要配置沙箱环境。

沙箱配置方式和遇到的坑

支付宝官方文档 介绍的很详细,接下来直接上代码,文档和代码结合着思路会更清晰:
在这里插入图片描述
获取支付宝二维码支付:

private Map<String, Object> getPayCodeByAliPay(PayCodeRequest payCodeRequest) {
    log.info("获取支付宝二维码请求*******");
    Map<String, Object> resultMap = new HashMap<>(16);
    String currentUserID;
    String orderID;
    BizContentByAliPay bizContent;
    AlipayMakeContent makeCode;
    try {
        orderID = payCodeRequest.getOrderID();
        currentUserID = payCodeRequest.getCurrentUserID();

        SystemConfig queryOneSystemConfig = orderPayService.queryOneSystemConfig("cm2_aliPayAccount");
        bizContent = new BizContentByAliPay();
        bizContent.setOut_trade_no(CommonUtils.genPayId());
        bizContent.setSubject(payCodeRequest.getProductName());
        String payAmount = payCodeRequest.getPayAmount().replace(",", "");
        bizContent.setTotal_amount(new BigDecimal(payAmount));
        bizContent.setBody(payCodeRequest.getValidityPeriod());

        PayRecord queryOrderPayRecordByOrderID = orderPayService.queryPayRecordByOrderID(orderID);
        if (null != queryOrderPayRecordByOrderID && OrderPayStatusEnum.PAID.getValue() == queryOrderPayRecordByOrderID.getPayStatus()) {
            log.info("支付的类型:" + queryOrderPayRecordByOrderID.getPayType());
            log.debug("订单已存在支付过记录");
            resultMap.put("code", -20003);
            resultMap.put("desc", "订单已支付,无法再次操作");
            return resultMap;
        }

        makeCode = AliPayUtil.makeCode(queryOneSystemConfig.getConfigValue(), bizContent);

        if (null == makeCode || null == makeCode.getAlipay_trade_precreate_response()) {
            resultMap.put("code", -20001);
            resultMap.put("desc", "获取支付宝支付二维码失败");
            payLogService.createPayLog(currentUserID, new Gson().toJson(bizContent), orderID, new Gson().toJson(makeCode), 1);
            return resultMap;
        }
        AliPayMakeResponse alipay_trade_precreate_response = makeCode.getAlipay_trade_precreate_response();

        String qr_code = alipay_trade_precreate_response.getQr_code();

        //生成二维码
        String base64Code = QRCodeUtil.getBase64Code(qr_code);
        log.info("生成的二维码:", base64Code);
        /**
         * 有记录,且非已支付
         */
        if (null != queryOrderPayRecordByOrderID) {
            queryOrderPayRecordByOrderID.setOrderID(payCodeRequest.getOrderID());
            queryOrderPayRecordByOrderID.setPayType(OrderPayTypeEnum.ALIPAY.getValue());
            queryOrderPayRecordByOrderID.setSign(makeCode.getSign());
            queryOrderPayRecordByOrderID.setEditor(payCodeRequest.getCurrentUserID());
            queryOrderPayRecordByOrderID.setEditTime(new Date());
            queryOrderPayRecordByOrderID.setPayStatus(OrderPayStatusEnum.UNPAID.getValue());
            queryOrderPayRecordByOrderID.setQr_code(qr_code);
            queryOrderPayRecordByOrderID.setOut_trade_no(bizContent.getOut_trade_no());
            long longValue = (new BigDecimal(payAmount).multiply(new BigDecimal("100"))).longValue();
            queryOrderPayRecordByOrderID.setPayAmount(longValue);
            orderPayService.updateOrderPayRecord(queryOrderPayRecordByOrderID);
        } else {
            PayRecord payRecord = new PayRecord();
            payRecord.setOrderID(payCodeRequest.getOrderID());
            payRecord.setPayType(OrderPayTypeEnum.ALIPAY.getValue());
            payRecord.setSign(makeCode.getSign());
            payRecord.setOperator(payCodeRequest.getCurrentUserID());
            payRecord.setCreateTime(new Date());
            payRecord.setPayStatus(OrderPayStatusEnum.UNPAID.getValue());
            long longValue = (new BigDecimal(payAmount).multiply(new BigDecimal("100"))).longValue();
            payRecord.setPayAmount(longValue);
            payRecord.setOut_trade_no(bizContent.getOut_trade_no());
            payRecord.setQr_code(qr_code);
            orderPayService.createOrderPayRecord(payRecord);
        }
        resultMap.put("code", 0);
        resultMap.put("desc", "获取成功");
        resultMap.put("data", base64Code);
        resultMap.put("out_trade_no", bizContent.getOut_trade_no());
        return resultMap;
    } catch (Exception e) {
        log.error("获取支付宝二维码处理异常*******", e);
        resultMap.put("code", -20001);
        resultMap.put("desc", "获取支付宝支付二维码失败");
        return resultMap;
    }

支付宝支付工具类

public class AliPayUtil {
    private static String FORMAT = "json";
    private static String CHARSET = "UTF-8";

    /**
     * @name 阿里获取二维码接口
     * @throws AlipayApiException
     * @Param out_trade_no 商户订单号,64个字符以内、只能包含字母、数字、下划线;需保证在商户端不重复
     * @Param total_amount 订单总金额,单位为元,精确到小数点后两位,取值范围[0.01,100000000] 如果同时传入了【打折金额】,【不可打折金额】,
     * 【订单总金额】三者,则必须满足如下条件:【订单总金额】=【打折金额】+【不可打折金额】
     * @Param subject 订单标题
     * @Param store_id 商户门店编号
     * @Param timeout_express 该笔订单允许的最晚付款时间,逾期将关闭交易。
     * 取值范围:1m~15d。m-分钟,h-小时,d-天,1c-当天(1c-当天的情况下,无论交易何时创建,都在0点关闭)。 该参数数值不接受小数点, 如 1.5h,可转换为 90m。
     */
    public static AlipayMakeContent makeCode(String aliPayAccountData, BizContentByAliPay bizContent) {
        log.info("获取支付宝支付二维码********");
        log.info("aliPayAccountData:" + aliPayAccountData);
        log.info("bizContent:" + new Gson().toJson(bizContent));
        AlipayMakeContent payMakeContent = null;
        try {
            AliPayAccount aliPayAccount = new Gson().fromJson(aliPayAccountData, AliPayAccount.class);
            String URL = aliPayAccount.getApplicationUrl();
            String app_id = aliPayAccount.getApp_id();
            String alipay_public_key = aliPayAccount.getAlipay_public_key();
            String app_private_key = aliPayAccount.getApp_private_key();
            String sign_type = aliPayAccount.getSign_type();
            String timeout_express = aliPayAccount.getTimeout_express();
            AlipayClient alipayClient = new DefaultAlipayClient(URL, app_id, app_private_key, FORMAT, CHARSET, alipay_public_key, sign_type);
            AlipayTradePrecreateRequest request = new AlipayTradePrecreateRequest();//创建API对应的request类

            bizContent.setTimeout_express(timeout_express);
            if (null != aliPayAccount.getNotifyUrl() && !"".equals(aliPayAccount.getNotifyUrl())) {
                request.setNotifyUrl(aliPayAccount.getNotifyUrl());
            }
            if (null != aliPayAccount.getReturnUrl() && !"".equals(aliPayAccount.getReturnUrl())) {
                request.setReturnUrl(aliPayAccount.getReturnUrl());
            }

            //把订单信息转换为json对象的字符串
            request.setBizContent(new Gson().toJson(bizContent));
            AlipayTradePrecreateResponse response = alipayClient.execute(request);
            payMakeContent = new Gson().fromJson(response.getBody(), AlipayMakeContent.class);
            return payMakeContent;
        } catch (Exception e) {
            log.error("调用支付宝生产二维码异常:", e);
            return null;
        }

    }


    /**
     * 获取支付宝二维码
     * @param aliPayAccountData 支付宝账号json
     * @param bizContent 业务参数
     * @return
     */
    public static Map<String, Object> orderPayByAliPay(String aliPayAccountData, BizContentByAliPay bizContent) {
        log.info("获取支付二维码body********");
        log.info("aliPayAccountData:" + aliPayAccountData);
        log.info("bizContent:" + new Gson().toJson(bizContent));
        Map<String, Object> resultMap = new HashMap<String, Object>();
        resultMap.put("code", 0);
        try {
            AliPayAccount aliPayAccount = new Gson().fromJson(aliPayAccountData, AliPayAccount.class);
            String URL = aliPayAccount.getApplicationUrl();
            String app_id = aliPayAccount.getApp_id();
            String alipay_public_key = aliPayAccount.getAlipay_public_key();
            String app_private_key = aliPayAccount.getApp_private_key();
            String sign_type = aliPayAccount.getSign_type();
            String notifyUrl = aliPayAccount.getNotifyUrl();

            AlipayClient alipayClient = new DefaultAlipayClient(URL, app_id, app_private_key, FORMAT, CHARSET, alipay_public_key, sign_type);

            AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest();//创建API对应的request
            /* alipayRequest.setReturnUrl(returnUrl);*/
            alipayRequest.setNotifyUrl(notifyUrl);//在公共参数中设置回跳和通知地址
          /* bizContent.setOut_trade_no(BaseUtil.genPayID());
           bizContent.setTotal_amount(new BigDecimal("799.90"));
           bizContent.setSubject("赛门铁克DV证书");
           bizContent.setBody("1年期证书");*/
            alipayRequest.setBizContent(new Gson().toJson(bizContent));
            AlipayTradePagePayResponse pageExecute = alipayClient.pageExecute(alipayRequest); //调用SDK生成表单
            if (pageExecute.isSuccess()) {
                String body = pageExecute.getBody();
                resultMap.put("data", body);
                return resultMap;
            }
            log.error("未成功获取支付二维码:", new Gson().toJson(pageExecute));
            resultMap.put("code", -20001);
            resultMap.put("desc", "获取支付宝支付二维码失败");
            return resultMap;
        } catch (Exception e) {
            log.error("获取支付二维码body异常:", e);
            resultMap.put("code", -20001);
            resultMap.put("desc", "获取支付宝支付二维码失败");
            return resultMap;
        }
    }

    /**
     * 查看支付交易信息
     * @param aliPayAccountData
     * @param query
     * @return
     */
    public static AliPayQueryContent queryPay(String aliPayAccountData, BizContentQueryByAliPay query) {
        log.info("查询支付宝交易信息********");
        log.info("aliPayAccountData:" + aliPayAccountData);
        log.info("query:" + new Gson().toJson(query));

        AliPayQueryContent payContent = new AliPayQueryContent();
        try {
            AliPayAccount aliPayAccount = new Gson().fromJson(aliPayAccountData, AliPayAccount.class);
            String URL = aliPayAccount.getApplicationUrl();
            String app_id = aliPayAccount.getApp_id();
            String alipay_public_key = aliPayAccount.getAlipay_public_key();
            String app_private_key = aliPayAccount.getApp_private_key();
            String sign_type = aliPayAccount.getSign_type();

            AlipayClient alipayClient = new DefaultAlipayClient(URL, app_id, app_private_key, FORMAT, CHARSET, alipay_public_key, sign_type);
            AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
            request.setBizContent(new Gson().toJson(query));
            AlipayTradeQueryResponse response = alipayClient.execute(request);
            log.info("查询结果:" + response.getBody());
            payContent = new Gson().fromJson(response.getBody(), AliPayQueryContent.class);
            return payContent;
        } catch (Exception e) {
            log.error("查询支付宝交易信息异常:", e);
            return null;
        }
    }

    /**
     * 退款操作:
     * 退款成功注意事项。
     全额退款情况:trade_status= TRADE_CLOSED,而refund_status=REFUND_SUCCESS;
     非全额退款情况:trade_status= TRADE_SUCCESS,而refund_status=REFUND_SUCCESS
     * @param aliPayAccountData
     * @param refund
     * @return
     */
    public static TradeResponseContent refund(String aliPayAccountData, BizContentRefundByAliPay refund) {
        log.info("支付宝进行退款操作********");
        log.info("aliPayAccountData:" + aliPayAccountData);
        log.info("query:" + new Gson().toJson(refund));
        TradeResponseContent tradeContent = null;
        try {
            AliPayAccount aliPayAccount = new Gson().fromJson(aliPayAccountData, AliPayAccount.class);
            String URL = aliPayAccount.getApplicationUrl();
            String app_id = aliPayAccount.getApp_id();
            String alipay_public_key = aliPayAccount.getAlipay_public_key();
            String app_private_key = aliPayAccount.getApp_private_key();
            String sign_type = aliPayAccount.getSign_type();

            AlipayClient alipayClient = new DefaultAlipayClient(URL, app_id, app_private_key, FORMAT, CHARSET, alipay_public_key, sign_type);
            //设置请求参数
            AlipayTradeRefundRequest alipayRequest = new AlipayTradeRefundRequest();
            alipayRequest.setBizContent(new Gson().toJson(refund));
            AlipayTradeRefundResponse execute = alipayClient.execute(alipayRequest);
            tradeContent = new Gson().fromJson(execute.getBody(), TradeResponseContent.class);
            return tradeContent;
        } catch (Exception e) {
            log.error("支付宝退款异常:", e);
            return null;
        }

    }

    /**
     * 查询退款信息
     * @param aliPayAccountData
     * @param tradeRefundQuery
     * @return
     */
    public static TradeResponseContent refundQuery(String aliPayAccountData, TradeRefundQuery tradeRefundQuery) {
        log.info("查询支付宝退款详情操作********");
        log.info("aliPayAccountData:" + aliPayAccountData);
        log.info("tradeRefundQuery:" + new Gson().toJson(tradeRefundQuery));
        TradeResponseContent tradeContent = null;
        try {
            AliPayAccount aliPayAccount = new Gson().fromJson(aliPayAccountData, AliPayAccount.class);
            String URL = aliPayAccount.getApplicationUrl();
            String app_id = aliPayAccount.getApp_id();
            String alipay_public_key = aliPayAccount.getAlipay_public_key();
            String app_private_key = aliPayAccount.getApp_private_key();
            String sign_type = aliPayAccount.getSign_type();

            AlipayClient alipayClient = new DefaultAlipayClient(URL, app_id, app_private_key, FORMAT, CHARSET, alipay_public_key, sign_type);
            //设置请求参数
            AlipayTradeFastpayRefundQueryRequest alipayRequest = new AlipayTradeFastpayRefundQueryRequest();

            alipayRequest.setBizContent(new Gson().toJson(tradeRefundQuery));
            //请求
            String body = alipayClient.execute(alipayRequest).getBody();
            //输出
            tradeContent = new Gson().fromJson(body, TradeResponseContent.class);
            return tradeContent;
        } catch (AlipayApiException e) {
            log.error("查询支付宝退款异常:", e);
            return null;
        }


    }

   

    private static void query(String value) {
        BizContentQueryByAliPay query = new BizContentQueryByAliPay();
        query.setOut_trade_no("1909251da8f845545b6588");
        AliPayQueryContent queryPay = queryPay(value, query);
        System.err.println(new Gson().toJson(queryPay));
    }

    private static void pay(String value) throws Exception {
        BizContentByAliPay bizContent = new BizContentByAliPay();
        bizContent.setOut_trade_no("1909251da8f845545b6588");
        bizContent.setTotal_amount(new BigDecimal("0.01"));
        bizContent.setSubject("Symantec EV强制型");
        bizContent.setBody("1年期证书");

        AliPayAccount aliPayAccount = new Gson().fromJson(value, AliPayAccount.class);
        String URL = aliPayAccount.getApplicationUrl();
        String app_id = aliPayAccount.getApp_id();
        String alipay_public_key = aliPayAccount.getAlipay_public_key();
        String app_private_key = aliPayAccount.getApp_private_key();
        String sign_type = aliPayAccount.getSign_type();
        String timeout_express = aliPayAccount.getTimeout_express();
        AlipayClient alipayClient = new DefaultAlipayClient(URL, app_id, app_private_key, FORMAT, CHARSET, alipay_public_key, sign_type);
        AlipayTradePrecreateRequest request = new AlipayTradePrecreateRequest();//创建API对应的request类

        bizContent.setTimeout_express(timeout_express);
        request.setNotifyUrl(aliPayAccount.getNotifyUrl());

        //把订单信息转换为json对象的字符串
        request.setBizContent(new Gson().toJson(bizContent));
        AlipayTradePrecreateResponse response = alipayClient.execute(request);
        AlipayMakeContent payMakeContent = new Gson().fromJson(response.getBody(), AlipayMakeContent.class);
        QRCodeUtil.encode(payMakeContent.getAlipay_trade_precreate_response().getQr_code(), "D:/" + bizContent.getOut_trade_no() + ".jpg");
        System.err.println(new Gson().toJson(payMakeContent));
    }

    public static void main1(String[] args) throws AlipayApiException {
        String value = "{\"applicationUrl\":\"https://openapi.alipaydev.com/gateway.do\",\"app_id\":\"2016082000290858\",\"app_private_key\":\"MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCZ1RBO92Rl+JwlM8471+rlctOSeKblBz15k1RkGrbv8+dUzvnvBENylG811eIEqEhfWefsIMeqPnbAG3wpeDbyRDgmFWiZZmBGcO2UKAM2+tDI1AfEgDxs2sKeasdigVqzccXJsr5fRfB8+BnLjEceaaXutegQA61Bm45/bhXT8TErFgJWJX5ucHrIVGDX/xqCiuZ5AjKLM0bhSG2cmy7E5bnhtCzMWt9QcM9g3lMSp1745Op7Yw3S+LonEE564GEnR4jUuWMVfEE+dQHuiJ4RKY/n3vzJHVdt5BTj3CR5+J2kHWuHBw4/kuZxPtdAA+aPZYkv05XdPdRfIifr/cxDAgMBAAECggEBAIvFFS5KhYRJIMaEjYu+IQUdEbLxwOIzc920yWazByh6OePKszhCbW38BBesH9IPfMJ2E81t8zxxyL+/uU6MlZAF/+ZKaWGHrU4TaSnGOTWJKo5uaChYQ468LTx21HZiBaEpbIZYy7QQ/fLuEbHp4Ox3HXcI/LMgRBlqV0zdHyQTgYYMFZLOg49vKloNeC+DEqoFhBFskdc2nit0B7WvXTk5qZfdRTcLPHLz2Ih1rJrFz8niwLz5exBGG2jWFIsbhQ/hHFPmiaPYvYX0sCAZz5jxUlgFQ2DRAXrrPW9Qy0j83V9yn2jFBBGf5Ff5PcDb5OgaJppPo9pjw7y3/xzZ/zECgYEA7ARBC2QPh1PdRRUd1MIftN9Wi9q/W+rA1jp9OpCY6t1+CskajqNGLA5z402oHLqq+gOtf7lsZkrZVrlEmnJbojblWejrx7GbZHTLJXrPKxkOCaqPzqPdEaFfqvdVjB8xqJd0JdGxVLAEMWJYavWmqpTpmkdTwJHkoJ5nOKP7D1cCgYEApttvK3ySOBUEcWS6SnDqZvcvKzPe0zSc0v22zIvyxpAGOetRNMKPbWLrJsiYEOeEYlhjVb6Ovh7qjXiSWskSGkytjlDh1ReigEHnDjmKEr/5JqvRSzL7Jh03+ypmGWZ2NUlmee3TEZI5HnU5B2nnlV4MibqZnnq8YCCI4CT8EvUCgYB+aITh8fSzOYKwhm/UzSgoOtpiy4VkWKZx1x/NZzbPzFO+uRlFPkPOQutk2enpgT0j7SZW21woqXwgi9gnlIpppldOwYbPDNw6gKdxaoZdAe1X1gdx2KgF0lWn/xLNmsOBFaU6wEXnIuMMBC1xFCLxCBLZ88vyvvEh6dVFK+bCvwKBgQCTVZ3zazr9tGlI62Wnx87i/o8y+cle+80ibY/VTBoihRhKWmRJTycwsrDrfSLxgk0ePefw1aPbcxcvtnCdI4ENofl6eib/b7A4yniI0fv+lG8fW4YifbDQteoxqP2gnh3w7ZBER+EhWdl/8Ihb6ILqHLK7hv6QttXRl8jlCyNeZQKBgDz4HhUkc1Mig1bD8mgkQPkMiptd6/jidLoRJ/ExoR2Ctab4ixPGEYuPNfTqpK7wdto70z0i/HmtwWY3e3doeMYeF9PiNjDiqElx5266G8r3lFIbMKGj849h41u0k50swshAx3MboJv3y0N+oZ97u3E9a7FA5s319GCf9jE618xv\",\"alipay_public_key\":\"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvSpfv47tdtCJCZXhbEUtqoofSQLRCFLYCG6U8Q7wiDytFGVyI1Dby3M6bvHjREjb7imoox4Lrsg66lbnTLeEuetAnukU0GhAM/1zzVBnV6iTDy5fq4tEDVCWCY6+GLn6YNgOC+RVPrko5mYs+GIexWhT+QWvNcrewBYjdWWEY2PeoKf5ZgSkgfZELZcEPOWDaPzdn95J1U90cwakLL360C/E9s0h/mkRjxMSgQVfQef1JmNSpqyc+5uT3VJp4PcqQX1/RpawJG1B0NBaxmrAYJDdA1F7VgUzNIdLW6z+8IvQXG+7NuAayNgaelgdfkz+l3hebaMKrMxMKl2pNiqkpwIDAQAB\n\",\"sign_type\":\"RSA2\",\"returnUrl\":\"http://daguoge.vaiwan.com/callback/return\",\"notifyUrl\":\"http://daguoge.vaiwan.com/callback/notify\"}";
        value = "{\"applicationUrl\":\"https://openapi.alipaydev.com/gateway.do\",\"app_id\":\"2016101400684128\",\"app_private_key\":\"MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCQsOVWbMEmpCk78dmXFAPM1tofr3OMLExNHKmZ9ysbtffnMAaa5LgLaeAZRaMOgFJuhO25MYAONKPrUfNMTeeqNIR2xf0C5faYSyE2ludrL84BlpufH6Wh6XE+gniagpytndtN7Qa/ZDQ+/jKLWL95NnYPXZiWuQWsrLDbKn10d1BmBF3klfnG0RZR7l7wXg8QvrrT6neU4nS/xqEtR+F/HRgGApq2w5BTINlob2IML7C0YOJiZUAhYBwpa6Tz2s9aafkCE1Xh02uJ5uliur0QFNPgrAKmtfbHJnNv3W45geRRvwq83EYSBsjJttVEKkhr/YpKGPdnnEcpwn7gXIILAgMBAAECggEALDwooRd2pZ2ijZUO9MVFtCYrYWx+YuhYG2sb3tpV747eT2u7WTd7nOGthYxooVJR4T/6fZfIko89XgAb5cQbg0mzBazaATzU+xJdARd5gKeIz6t/wFIojSuVV4sD3Bj6VYnhNh205mZ4IpoMUdW8FA62YVQ4Bjxio7vYBekjmas4u0gwIynFYpn8vaQ3Kg5O9bO09N52cJlCWxu1LD3uopD7aCBxEB0JvOfvB/kDorzrZGMbiU6kDTk5IwvAXuFGkXo1RPmHOmdYpyt0yhepqylBwBQ1ta7alKOFFl9vop79Rm3Ej+naXYQZXuXLlknDnAQM6N2tyHPN+OHUTzOywQKBgQDognTdCiopINAWryux8zrCMa9x0OnJ1p6IwrCtWIYnpTR/zYe35fzFk29MN8iEU/mj8UCaHkq6jQdK2ZSd/w8qZ5BDxvL3DQThvn4mk2jyUIwzYFuoPDJEd2JzTabKrRs2nWlGsb4tjiLTR3EBTzSNyhy3jaHEwz+LMYKt1RCVgwKBgQCfTyGT0pPGRM3zOKXkQjeTwgwiNqEQfvJDvIKNqrTPXH6SRyZjptdTy669y4Is8K2qnj7LdHmdznDB0p0uLG5ajSK0yAvstT2Be6DTtPEmWUfCXmd7dXHBFpmcJGeGCGgcYuGPIAp+BhUJdC14AzOlzwLMVgMZAnufpalJzJZC2QKBgEa6aJJU5T5dnO9JmiEnvFgWhSg6ROQBRXiUwOn6Y9TrjGzUONTYQWxYzSKCVZzrbqVQXjaDwHe/Lti2F194l0Ru/JOhkYdVjoadTAKwFNyhZAS86yTiKuho6KnqlTJSNlUGzV+T8iYpdq6OrPGZBoObV+D+VJdmTY4qIXZ/uevBAoGAaurgziT1qItk7WeHbrpCMph5LD0du8R3M57Av/Nhhgm9qM63pQnEWijJDfgXVbHx3CB4ZHO3a4Gxi2Oeue534a33LP1Vg3ff35bpSSyW6/Xfg0N4kBMyVTSlw3VrRTk/Jk32s+f9IrdSRxYBrhn3/7//UMQpMA/KiErWYwE7l4ECgYAzYfE2zUdUw3+NDFYP3d/rZdAhmlKW7Lx76tCfGYO1Bcbx1TDGYrqa9s12GX+jFsSc03a3QmUednf+NeLR3lYSb8Sm9BoYZKRmLt8p1HYeulsEjYMOd7tPqwXXl4B/HiZm9sPJeglr/pA6Rvp3KTQdk1+i075olUsPmryalsfLgw==\",\"alipay_public_key\":\"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAn12eE8oUUiZZIOqj6+1toJ71xyfx74r7GpgiqK9uWaebLMghFkjqqg4NApOgXcNckPRo5oDl5G6j20h8vAPS6zhkeQItph5qqSpCj7i06ieeBUReijmyeHY6jL0gMc8gMJyX4FbdASZQvE2D1B3aveDT1YL0X2AedTziBWxu+qlE5d3QA+KBfoxd6azzTjayi0VrDNN7rpni8/GKILBfty+KypLZc3ZrVUvEa+dkRY4VXAG36wYbU+FIudutra3BM2zTOGWPL+p9pbHrVZ/MGhRS1nWntRy+sCaYJidq9RVgY54UPb8OolvnL56LGi56BT1shFZetDfLnkfLDfIUvwIDAQAB\",\"sign_type\":\"RSA2\",\"returnUrl\":\"http://daguoge.vaiwan.com/callback/return\",\"notifyUrl\":\"http://daguoge.vaiwan.com/callback/notify\"}";
        AliPayAccount aliPayAccount = new Gson().fromJson(value, AliPayAccount.class);
        String URL = aliPayAccount.getApplicationUrl();
        String app_id = aliPayAccount.getApp_id();
        String alipay_public_key = aliPayAccount.getAlipay_public_key();
        String app_private_key = aliPayAccount.getApp_private_key();
        String sign_type = aliPayAccount.getSign_type();

        AlipayClient alipayClient = new DefaultAlipayClient(URL, app_id, app_private_key, FORMAT, CHARSET, alipay_public_key, sign_type);
        //设置请求参数
        AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
        request.setBizContent("{" +
                "\"out_trade_no\":\"20150320010101001\"," +
                "\"product_code\":\"FAST_INSTANT_TRADE_PAY\"," +
                "\"total_amount\":88.88," +
                "\"subject\":\"Iphone6 16G\"," +
                "\"body\":\"Iphone6 16G\"," +
                "\"time_expire\":\"2016-12-31 10:05:01\"," +
                "      \"goods_detail\":[{" +
                "        \"goods_id\":\"apple-01\"," +
                "\"alipay_goods_id\":\"20010001\"," +
                "\"goods_name\":\"ipad\"," +
                "\"quantity\":1," +
                "\"price\":2000," +
                "\"goods_category\":\"34543238\"," +
                "\"categories_tree\":\"124868003|126232002|126252004\"," +
                "\"body\":\"特价手机\"," +
                "\"show_url\":\"http://www.alipay.com/xxx.jpg\"" +
                "        }]," +
                "\"passback_params\":\"merchantBizType%3d3C%26merchantBizNo%3d2016010101111\"," +
                "\"extend_params\":{" +
                "\"sys_service_provider_id\":\"2088511833207846\"," +
                "\"hb_fq_num\":\"3\"," +
                "\"hb_fq_seller_percent\":\"100\"," +
                "\"industry_reflux_info\":\"{\\\\\\\"scene_code\\\\\\\":\\\\\\\"metro_tradeorder\\\\\\\",\\\\\\\"channel\\\\\\\":\\\\\\\"xxxx\\\\\\\",\\\\\\\"scene_data\\\\\\\":{\\\\\\\"asset_name\\\\\\\":\\\\\\\"ALIPAY\\\\\\\"}}\"," +
                "\"card_type\":\"S0JP0000\"" +
                "    }," +
                "\"goods_type\":\"0\"," +
                "\"timeout_express\":\"90m\"," +
                "\"promo_params\":\"{\\\"storeIdType\\\":\\\"1\\\"}\"," +
                "\"royalty_info\":{" +
                "\"royalty_type\":\"ROYALTY\"," +
                "        \"royalty_detail_infos\":[{" +
                "          \"serial_no\":1," +
                "\"trans_in_type\":\"userId\"," +
                "\"batch_no\":\"123\"," +
                "\"out_relation_id\":\"20131124001\"," +
                "\"trans_out_type\":\"userId\"," +
                "\"trans_out\":\"2088101126765726\"," +
                "\"trans_in\":\"2088101126708402\"," +
                "\"amount\":0.1," +
                "\"desc\":\"分账测试1\"," +
                "\"amount_percentage\":\"100\"" +
                "          }]" +
                "    }," +
                "\"sub_merchant\":{" +
                "\"merchant_id\":\"19023454\"," +
                "\"merchant_type\":\"alipay: 支付宝分配的间连商户编号, merchant: 商户端的间连商户编号\"" +
                "    }," +
                "\"merchant_order_no\":\"20161008001\"," +
                "\"enable_pay_channels\":\"pcredit,moneyFund,debitCardExpress\"," +
                "\"store_id\":\"NJ_001\"," +
                "\"disable_pay_channels\":\"pcredit,moneyFund,debitCardExpress\"," +
                "\"qr_pay_mode\":\"1\"," +
                "\"qrcode_width\":100," +
                "\"settle_info\":{" +
                "        \"settle_detail_infos\":[{" +
                "          \"trans_in_type\":\"cardAliasNo\"," +
                "\"trans_in\":\"A0001\"," +
                "\"summary_dimension\":\"A0001\"," +
                "\"settle_entity_id\":\"2088xxxxx;ST_0001\"," +
                "\"settle_entity_type\":\"SecondMerchant、Store\"," +
                "\"amount\":0.1" +
                "          }]" +
                "    }," +
                "\"invoice_info\":{" +
                "\"key_info\":{" +
                "\"is_support_invoice\":true," +
                "\"invoice_merchant_name\":\"ABC|003\"," +
                "\"tax_num\":\"1464888883494\"" +
                "      }," +
                "\"details\":\"[{\\\"code\\\":\\\"100294400\\\",\\\"name\\\":\\\"服饰\\\",\\\"num\\\":\\\"2\\\",\\\"sumPrice\\\":\\\"200.00\\\",\\\"taxRate\\\":\\\"6%\\\"}]\"" +
                "    }," +
                "\"agreement_sign_params\":{" +
                "\"personal_product_code\":\"GENERAL_WITHHOLDING_P\"," +
                "\"sign_scene\":\"INDUSTRY|CARRENTAL\"," +
                "\"external_agreement_no\":\"test\"," +
                "\"external_logon_id\":\"13852852877\"," +
                "\"sign_validity_period\":\"2m\"," +
                "\"third_party_type\":\"PARTNER\"," +
                "\"buckle_app_id\":\"1001164\"," +
                "\"buckle_merchant_id\":\"268820000000414397785\"," +
                "\"promo_params\":\"{\\\"key\\\",\\\"value\\\"}\"" +
                "    }," +
                "\"integration_type\":\"PCWEB\"," +
                "\"request_from_url\":\"https://\"," +
                "\"business_params\":\"{\\\"data\\\":\\\"123\\\"}\"," +
                "\"ext_user_info\":{" +
                "\"name\":\"李明\"," +
                "\"mobile\":\"16587658765\"," +
                "\"cert_type\":\"IDENTITY_CARD\"," +
                "\"cert_no\":\"362334768769238881\"," +
                "\"min_age\":\"18\"," +
                "\"fix_buyer\":\"F\"," +
                "\"need_check_info\":\"F\"" +
                "    }" +
                "  }");
        AlipayTradePagePayResponse response = alipayClient.pageExecute(request);
        if (response.isSuccess()) {
            System.out.println("调用成功");
        } else {
            System.out.println("调用失败");
        }
    }

}

生成二维码

public class QRCodeUtil {
   private static final String CHARSET = "utf-8";
   private static final String FORMAT_NAME = "JPG";
   // 二维码尺寸
   private static final int QRCODE_SIZE = 300;
   // LOGO宽度
   private static final int WIDTH = 240;
   // LOGO高度
   private static final int HEIGHT = 240;
 
   private static BufferedImage createImage(String content, String imgPath, boolean needCompress) throws Exception {
      Hashtable<EncodeHintType, Object> hints = new Hashtable<EncodeHintType, Object>();
      hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
      hints.put(EncodeHintType.CHARACTER_SET, CHARSET);
      hints.put(EncodeHintType.MARGIN, 1);
      BitMatrix bitMatrix = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, QRCODE_SIZE, QRCODE_SIZE,
            hints);
      int width = bitMatrix.getWidth();
      int height = bitMatrix.getHeight();
      BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
      for (int x = 0; x < width; x++) {
         for (int y = 0; y < height; y++) {
            image.setRGB(x, y, bitMatrix.get(x, y) ? 0xFF000000 : 0xFFFFFFFF);
         }
      }
      if (imgPath == null || "".equals(imgPath)) {
         return image;
      }
      // 插入图片
      QRCodeUtil.insertImage(image, imgPath, needCompress);
      return image;
   }
   
    /**
     * 功能描述: 生成二维码 BufferedImage.
     *
     * @param content
     * @param
     * @param
     * @return java.awt.image.BufferedImage
     * @author yangshao
     * @date 2019/9/25 10:13
     */
    public static BufferedImage getBufferImage(String content) throws Exception {
        Hashtable<EncodeHintType, Object> hints = new Hashtable<EncodeHintType, Object>();
        hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
        hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
        hints.put(EncodeHintType.MARGIN, 1);
        BitMatrix bitMatrix = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, WIDTH, HEIGHT, hints);
        int width = bitMatrix.getWidth();
        int height = bitMatrix.getHeight();
        BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
        for (int x = 0; x < width; x++) {
            for (int y = 0; y < height; y++) {
                image.setRGB(x, y, bitMatrix.get(x, y) ? 0xFF000000 : 0xFFFFFFFF);
            }
        }
        return image;
    }
    /**
     * 功能描述:  生成base64格式二维码.
     *
     * @param content  content
     * @return string
     * @author yangshao
     * @date 2019/9/25 10:28
     */
    public static String getBase64Code(String content) {
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        try {
            BufferedImage image = getBufferImage(content);
            //转换成png格式的IO流
            ImageIO.write(image, "png", byteArrayOutputStream);
        } catch (Exception e) {
            e.printStackTrace();
        }
        byte[] bytes = byteArrayOutputStream.toByteArray();
        BASE64Encoder encoder = new BASE64Encoder();
        String base64 = encoder.encodeBuffer(bytes).trim();
        /*base64 = "data:image/png;base64," + base64;*/
        return base64;
    }
 
   private static void insertImage(BufferedImage source, String imgPath, boolean needCompress) throws Exception {
      File file = new File(imgPath);
      if (!file.exists()) {
         System.err.println("" + imgPath + "   该文件不存在!");
         return;
      }
      Image src = ImageIO.read(new File(imgPath));
      int width = src.getWidth(null);
      int height = src.getHeight(null);
      if (needCompress) { // 压缩LOGO
         if (width > WIDTH) {
            width = WIDTH;
         }
         if (height > HEIGHT) {
            height = HEIGHT;
         }
         Image image = src.getScaledInstance(width, height, Image.SCALE_SMOOTH);
         BufferedImage tag = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
         Graphics g = tag.getGraphics();
         g.drawImage(image, 0, 0, null); // 绘制缩小后的图
         g.dispose();
         src = image;
      }
      // 插入LOGO
      Graphics2D graph = source.createGraphics();
      int x = (QRCODE_SIZE - width) / 2;
      int y = (QRCODE_SIZE - height) / 2;
      graph.drawImage(src, x, y, width, height, null);
      Shape shape = new RoundRectangle2D.Float(x, y, width, width, 6, 6);
      graph.setStroke(new BasicStroke(3f));
      graph.draw(shape);
      graph.dispose();
   }
 
   public static void encode(String content, String imgPath, String destPath, boolean needCompress) throws Exception {
      BufferedImage image = QRCodeUtil.createImage(content, imgPath, needCompress);
      mkdirs(destPath);
      // String file = new Random().nextInt(99999999)+".jpg";
      // ImageIO.write(image, FORMAT_NAME, new File(destPath+"/"+file));
      ImageIO.write(image, FORMAT_NAME, new File(destPath));
   }
 
   public static BufferedImage encode(String content, String imgPath, boolean needCompress) throws Exception {
      BufferedImage image = QRCodeUtil.createImage(content, imgPath, needCompress);
      return image;
   }
 
   public static void mkdirs(String destPath) {
      File file = new File(destPath);
      // 当文件夹不存在时,mkdirs会自动创建多层目录,区别于mkdir.(mkdir如果父目录不存在则会抛出异常)
      if (!file.exists() && !file.isDirectory()) {
         file.mkdirs();
      }
   }
 
   public static void encode(String content, String imgPath, String destPath) throws Exception {
      QRCodeUtil.encode(content, imgPath, destPath, false);
   }
   // 被注释的方法
   /*
    * public static void encode(String content, String destPath, boolean
    * needCompress) throws Exception { QRCodeUtil.encode(content, null, destPath,
    * needCompress); }
    */
 
   public static void encode(String content, String destPath) throws Exception {
      QRCodeUtil.encode(content, null, destPath, false);
   }
 
   public static void encode(String content, String imgPath, OutputStream output, boolean needCompress)
         throws Exception {
      BufferedImage image = QRCodeUtil.createImage(content, imgPath, needCompress);
      ImageIO.write(image, FORMAT_NAME, output);
   }
 
   public static void encode(String content, OutputStream output) throws Exception {
      QRCodeUtil.encode(content, null, output, false);
   }
 
   public static String decode(File file) throws Exception {
      BufferedImage image;
      image = ImageIO.read(file);
      if (image == null) {
         return null;
      }
      BufferedImageLuminanceSource source = new BufferedImageLuminanceSource(image);
      BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
      Result result;
      Hashtable<DecodeHintType, Object> hints = new Hashtable<DecodeHintType, Object>();
      hints.put(DecodeHintType.CHARACTER_SET, CHARSET);
      result = new MultiFormatReader().decode(bitmap, hints);
      String resultStr = result.getText();
      return resultStr;
   }
 
   public static String decode(String path) throws Exception {
      return QRCodeUtil.decode(new File(path));
   }
 
}

支付宝回调接口


这部分 坑就在于 验签功能,验签失败,可能是由于沙箱环境配置问题。注意支付宝公钥和私钥不要对应错。
回调接口 在沙箱中一样要被本地代码对应(回调路径要放到服务器上测试)

public String doNotify() {
    Map<String, String> params = new HashMap<String, String>();
    String orderID = "";
    try {
        //获取支付宝POST过来反馈信息
        Map<String, String[]> requestParams = request.getParameterMap();
        for (Iterator<String> 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.toString());

        SystemConfig queryOneSystemConfig = orderPayService.queryOneSystemConfig("cm2_aliPayAccount");
        AliPayAccount aliPayAccount = new Gson().fromJson(queryOneSystemConfig.getConfigValue(), AliPayAccount.class);
        String alipay_public_key = aliPayAccount.getAlipay_public_key();
        String sign_type = aliPayAccount.getSign_type();

        log.info("alipay_public_key:" + alipay_public_key);
        log.info("CHARSET:" + CHARSET);
        log.info("sign_type:" + sign_type);
        /**
         * 调用SDK验证签名
         */
        boolean signVerified = AlipaySignature.rsaCheckV1(params, alipay_public_key, CHARSET, sign_type);
        log.info("验证结果:" + signVerified);
        /**
         * 实际验证过程建议商户务必添加以下校验:
         *             1、需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号,
         *             2、判断total_amount是否确实为该订单的实际金额(即商户订单创建时的金额),
         *             3、校验通知中的seller_id(或者seller_email) 是否为out_trade_no这笔单据的对应的操作方(有的时候,一个商户可能有多个seller_id/seller_email)
         *             4、验证app_id是否为该商户本身。
         */
        /**
         * 商户订单号
         */
        String out_trade_no = new String(request.getParameter("out_trade_no").getBytes("ISO-8859-1"), CHARSET);
        log.info("out_trade_no : " + out_trade_no);

        PayRecord queryOrderPayRecordByOrderID = this.orderPayService.queryOrderPayRecordByOut_Trade_NoAndType(out_trade_no, OrderPayTypeEnum.ALIPAY.getValue());
        if (null == queryOrderPayRecordByOrderID) {
            log.error("当前商户订单号不存在");
            return "fail";
        }
        orderID = queryOrderPayRecordByOrderID.getOrderID();

        if (signVerified) {
            //支付宝交易号
            String trade_no = new String(request.getParameter("trade_no").getBytes("ISO-8859-1"), CHARSET);
            log.info("trade_no : " + trade_no);
            //付款金额
            String total_amount = new String(request.getParameter("total_amount").getBytes("ISO-8859-1"), CHARSET);
            log.info("total_amount : " + total_amount);
            String formatAmountByCent = CommonUtils.formatAmountByCent(queryOrderPayRecordByOrderID.getPayAmount());
            if (!total_amount.equals(formatAmountByCent)) {
                this.payLogService.createPayLogByReceive(orderID, new Gson().toJson(params), 1, "支付异步调用,支付金额和返回金额不匹配");
                log.error("支付的金额和返回的金额不匹配");
                return "fail";
            }
            //交易状态
            String trade_status = new String(request.getParameter("trade_status").getBytes("ISO-8859-1"), CHARSET);

            log.info("交易状态:" + trade_status);

            String seller_id = new String(request.getParameter("seller_id").getBytes("ISO-8859-1"), CHARSET);
            log.info("seller_id:" + seller_id);
            //支付时间
            String gmt_payment = new String(request.getParameter("gmt_payment").getBytes("ISO-8859-1"), CHARSET);
            log.info("gmt_payment:" + gmt_payment);
            String sign = new String(request.getParameter("sign").getBytes("ISO-8859-1"), CHARSET);
            if (OrderPayStatusEnum.PAID.getValue() == queryOrderPayRecordByOrderID.getPayStatus() || OrderPayStatusEnum.PAYMENT_END.getValue() == queryOrderPayRecordByOrderID.getPayStatus()) {
                log.info("数据库状态已更新,不需要操作:" + queryOrderPayRecordByOrderID.getPayStatus());
                this.payLogService.createPayLogByReceive(orderID, new Gson().toJson(params), 1, "支付异步调用,支付状态已完成");
                return "success";
            }
            String buyer_logon_id = new String(request.getParameter("buyer_logon_id").getBytes("ISO-8859-1"), CHARSET);

            long longValue = (new BigDecimal(total_amount).multiply(new BigDecimal("100"))).longValue();
            queryOrderPayRecordByOrderID.setPayAmount(longValue);
            queryOrderPayRecordByOrderID.setTrade_no(trade_no);
            queryOrderPayRecordByOrderID.setPayType(OrderPayTypeEnum.ALIPAY.getValue());
            queryOrderPayRecordByOrderID.setComment(queryOrderPayRecordByOrderID.getComment() + "时间:" + new Date() + "==>支付宝异步数据");
            queryOrderPayRecordByOrderID.setEditTime(new Date());
            queryOrderPayRecordByOrderID.setSeller_id(seller_id);
            queryOrderPayRecordByOrderID.setBuyer_logon_id(buyer_logon_id);
            Date payTime = null;
            if (null != gmt_payment && !"".equals(gmt_payment)) {
                SimpleDateFormat sDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                payTime = sDateFormat.parse(gmt_payment);
            }
            queryOrderPayRecordByOrderID.setPayTime(payTime);
            queryOrderPayRecordByOrderID.setSign(sign);
            //交易状态:WAIT_BUYER_PAY(交易创建,等待买家付款)、TRADE_CLOSED(未付款交易超时关闭,或支付完成后全额退款)、TRADE_SUCCESS(交易支付成功)、TRADE_FINISHED(交易结束,不可退款)
            if (trade_status.equals("TRADE_FINISHED")) {
                //注意:
                //退款日期超过可退款期限后(如三个月可退款),支付宝系统发送该交易状态通知
                queryOrderPayRecordByOrderID.setPayStatus(OrderPayStatusEnum.PAYMENT_END.getValue());
                this.orderPayService.updateOrderPayRecord(queryOrderPayRecordByOrderID);
            } else if (trade_status.equals("TRADE_SUCCESS")) {
                queryOrderPayRecordByOrderID.setPayStatus(OrderPayStatusEnum.PAID.getValue());
                this.orderPayService.updateOrderPayRecord(queryOrderPayRecordByOrderID);
            } else if (trade_status.equals("TRADE_CLOSED")) {
                queryOrderPayRecordByOrderID.setPayStatus(OrderPayStatusEnum.PAYMENT_FAILED.getValue());
                this.orderPayService.updateOrderPayRecord(queryOrderPayRecordByOrderID);
            } else {
                log.error("当前状态不作处理,让支付再次异步:" + trade_status);
                this.payLogService.createPayLogByReceive(orderID, new Gson().toJson(params), 1, "支付异步调用,状态不处理");
                return "fail";
            }
            this.payLogService.createPayLogByReceive(orderID, new Gson().toJson(params), 0, "支付异步调用");
            return "success";
        } else {//验证失败
            log.error("异步同步数据验证失败");
            this.payLogService.createPayLogByReceive(orderID, new Gson().toJson(params), 1, "支付异步调用,验证失败");
            return "fail";
        }
    } catch (Exception e) {
        log.error("异步同步数据异常:", e);
        this.payLogService.createPayLogByReceive(orderID, new Gson().toJson(params), 1, "支付异步调用异常");
        return "fail";
    }

}
  • 8
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Cnyzu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值