Springboot Alipay website payment, APP payment, single transfer to users, refund function

Posted by allaboutthekick on Fri, 05 Jun 2020 06:07:56 +0200

1, Overview

Recently, in writing projects, I met the place where I would like to call Alipay payment (website management website payment, APP user payment, APP user withdrawals, that is, single transfer to users, refund). I've studied for a long time, and all of them are connected. Now I'll share my code.

First of all, the acquisition of qualifications is available on Alipay's official platform. I created an application directly, set up the public key and private key, and then added the functions of website payment, APP payment, transfer and refund to the application.

2, Implementation

1, build a new springboot project to add Alipay's dependency:

        <!--Alipay-->
        <dependency>
            <groupId>com.alipay.sdk</groupId>
            <artifactId>alipay-sdk-java</artifactId>
            <version>4.9.79.ALL</version>
        </dependency>

2, create Alipay configuration class AlipayConfig.java

package com.lmj.alipay.config;

import java.io.FileWriter;
import java.io.IOException;

/**
 * @author Abell
 * @descibe Alipay payment
 * @date 2020/6/4 13:39
 */
public class AlipayConfig {
    // With ID, your APPID account is your APPID corresponding Alipay account.
    public static String app_id = "Your appid";
    // Merchant private key, your RSA2 private key in PKCS8 format
    public static String merchant_private_key = "Your merchant_private_key ";
    // Alipay public key, see address: https://openhome.alipay.com/platform/keyManage.htm  The Alipay public key corresponding to APPID.
    public static String alipay_public_key = "Your Alipay public key";
    // The path of the asynchronous notification page of the server requires a complete path in the format of http: / /. You can't add such custom parameters as? id=123. You must be able to access the Internet normally. Test and modify the springboot port and the Internet address
    public static String notify_url = "http://localhost:8080/notify_url "; / / this is for local test
    // The path of the page Jump synchronization notification page needs a complete path in the format of http: / /. You can't add such custom parameters as? id=123. You must be able to access the Internet (get net123 through netapp or purchase and generate it yourself) normally
    public static String return_url = "http://localhost:8080//return_url";
    // Signature method
    public static String sign_type = "RSA2";
    // Character encoding format
    public static String charset = "utf-8";
    // Alipay gateway
    public static String gatewayUrl = "https://openapi.alipay.com/gateway.do "; / / note that this gateway is alipaydev in the sandbox environment
    // Alipay gateway
    public static String log_path = "D:\\";

    /**
     * Write a log to facilitate testing (see the requirements of the website, or change to store the records in the database)
     */
    public static void logResult(String sWord) {
        FileWriter writer = null;
        try {
            writer = new FileWriter(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. Create a tool class AliPayUtil.java

 package com.lmj.alipay.util;

import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.internal.util.AlipaySignature;
import com.lmj.alipay.config.AlipayConfig;
import lombok.extern.slf4j.Slf4j;

import javax.servlet.http.HttpServletRequest;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/**
 * @author Abell
 * @descibe Alipay payment
 * @date 2020/6/4 13:40
 */
@Slf4j
public class AliPayUtil {
    public static AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.gatewayUrl, AlipayConfig.app_id, AlipayConfig.merchant_private_key, "json", AlipayConfig.charset, AlipayConfig.alipay_public_key, AlipayConfig.sign_type);

    /*** transcoding */
    public static String getByte(String param) {
        try {
            return new String(param.getBytes("ISO-8859-1"), "UTF-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            return null;
        }
    }

    /*** Verification signature*/
    public static boolean rsaCheckV1(HttpServletRequest request) {
        // https://docs.open.alipay.com/54/106370
        // Get feedback from Alipay POST
        Map<String, String> params = new HashMap<>();
        Map requestParams = request.getParameterMap();
        for (Iterator iter = requestParams.keySet().iterator(); iter.hasNext(); ) {
            String name = (String) iter.next();
            String[] values = (String[]) requestParams.get(name);
            String valueStr = "";
            for (int i = 0; i < values.length; i++) {
                valueStr = (i == values.length - 1) ? valueStr + values[i]
                        : valueStr + values[i] + ",";
            }
            params.put(name, valueStr);
        }
        try {
            boolean signVerified = AlipaySignature.rsaCheckV1(params, AlipayConfig.alipay_public_key, AlipayConfig.charset, AlipayConfig.sign_type);
            return signVerified;
        } catch (AlipayApiException e) {
            log.debug("verify sigin error, exception is:{}", e);
            return false;
        }
    }
}

4. Create AliController.java Alipay website payment, app payment, single transfer to user functions:

package com.lmj.alipay.controller;

import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.domain.AlipayTradeCloseModel;
import com.alipay.api.domain.AlipayTradeFastpayRefundQueryModel;
import com.alipay.api.domain.AlipayTradePagePayModel;
import com.alipay.api.domain.AlipayTradeRefundModel;
import com.alipay.api.request.*;
import com.alipay.api.response.*;
import com.lmj.alipay.config.AlipayConfig;
import com.lmj.alipay.util.AliPayUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.Map;
import java.util.UUID;

/**
 * @author Abell
 * @descibe Alipay payment
 * @date 2020/6/4 13:38
 */
@Slf4j
@Controller
public class AliController {
    @ResponseBody
    @RequestMapping("/paySuccess")
    public String paySuccess() {
        return "Payment successful!";
    }

    @ResponseBody
    @RequestMapping("/payFail")
    public String payFail() {
        return "Payment failure!";
    }


    /**
     * Alipay transfer interface
     *
     * @param outBizNo     The unique order number of the merchant side. For the same transfer request, the merchant must ensure that the order number is unique.
     * @param transAmount  Total order amount, unit: yuan, accurate to two decimal places (minimum transfer of single transaction: 0.1 yuan)
     * @param payeeAccount Collection account
     * @param name         Real name of payee
     * @throws AlipayApiException
     */
    @RequestMapping(value = "/transfer")
    @ResponseBody
    public boolean transfer(String outBizNo, BigDecimal transAmount, String payeeAccount, String name) throws AlipayApiException {
        AlipayClient alipayClient = AliPayUtil.alipayClient;
        AlipayFundTransToaccountTransferRequest request = new AlipayFundTransToaccountTransferRequest();
        request.setBizContent("{" +
                "\"out_biz_no\":'" + outBizNo + "'," +
                "\"payee_type\":\"ALIPAY_LOGONID\"," +
                "\"payee_account\":'" + payeeAccount + "'," +
                "\"amount\":'" + transAmount + "'," +
                "\"payer_show_name\":\"A company\"," +
                "\"payee_real_name\":'" + name + "'," +
                "\"remark\":\"Withdrawal application\"" +
                "  }");
        AlipayFundTransToaccountTransferResponse response = alipayClient.execute(request);
        if (response.isSuccess()) {
            System.out.println("Call successful outBizNo by+" + outBizNo + "Transfer to" + name + ",Account number is" + payeeAccount + ",common" + transAmount + "Yuan.");
            return true;
        } else {
            System.out.println("Call failed");
            return false;
        }

    }

    /**
     * App payment
     *
     * @return
     * @throws AlipayApiException
     */
    @ResponseBody
    @RequestMapping(value = "appAliPay")
    public boolean appAliPay() throws AlipayApiException {
        AlipayClient alipayClient = AliPayUtil.alipayClient;
        AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
        request.setBizContent("{" +
                "\"timeout_express\":\"90m\"," +
                "\"total_amount\":\"0.01\"," +
                "\"product_code\":\"QUICK_MSECURITY_PAY\"," +
                "\"body\":\"Iphone6 16G\"," +
                "\"subject\":\"Physical goods\"," +
                "\"out_trade_no\":\"70501111111S001111119\"," +
                "\"time_expire\":\"2020-6-5 10:30:54\"," +
                "\"goods_type\":\"0\"," +
                "\"promo_params\":\"{\\\"storeIdType\\\":\\\"1\\\"}\"," +
                "\"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\"" +
                "    }," +
                "\"merchant_order_no\":\"20161008001\"," +
                "\"enable_pay_channels\":\"pcredit,moneyFund,debitCardExpress\"," +
                "\"store_id\":\"NJ_001\"," +
                "\"specified_channel\":\"pcredit\"," +
                "\"disable_pay_channels\":\"pcredit,moneyFund,debitCardExpress\"," +
                "      \"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\":\"Physical goods tested\"," +
                "\"show_url\":\"http://www.alipay.com/xxx.jpg\"" +
                "        }]," +
                "\"ext_user_info\":{" +
                "\"name\":\"Li Ming\"," +
                "\"mobile\":\"16587658765\"," +
                "\"cert_type\":\"IDENTITY_CARD\"," +
                "\"cert_no\":\"362334768769238881\"," +
                "\"min_age\":\"18\"," +
                "\"fix_buyer\":\"F\"," +
                "\"need_check_info\":\"F\"" +
                "    }," +
                "\"business_params\":\"{\\\"data\\\":\\\"123\\\"}\"," +
                "\"agreement_sign_params\":{" +
                "\"personal_product_code\":\"CYCLE_PAY_AUTH_P\"," +
                "\"sign_scene\":\"INDUSTRY|DIGITAL_MEDIA\"," +
                "\"external_agreement_no\":\"test20190701\"," +
                "\"external_logon_id\":\"13852852877\"," +
                "\"access_params\":{" +
                "\"channel\":\"ALIPAYAPP\"" +
                "      }," +
                "\"sub_merchant\":{" +
                "\"sub_merchant_id\":\"2088123412341234\"," +
                "\"sub_merchant_name\":\"Didi travel\"," +
                "\"sub_merchant_service_name\":\"Didi travel secret free payment\"," +
                "\"sub_merchant_service_description\":\"No secret fare, up to 500 per time\"" +
                "      }," +
                "\"period_rule_params\":{" +
                "\"period_type\":\"DAY\"," +
                "\"period\":3," +
                "\"execute_time\":\"2019-01-23\"," +
                "\"single_amount\":10.99," +
                "\"total_amount\":600," +
                "\"total_payments\":12" +
                "      }," +
                "\"allow_huazhi_degrade\":false," +
                "\"sign_notify_url\":\"http://www.merchant.com/receiveSignNotify\"" +
                "    }" +
                "  }");
        AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request);
        if (response.isSuccess()) {
            System.out.println("Call successful");
            return true;
        } else {
            System.out.println("Call failed");
            return false;
        }
    }

    /*** Website Payment*/
    @RequestMapping(value = "/goAlipay", produces = "text/html; charset=UTF-8")
    @ResponseBody
    public String goAlipay() throws IOException, AlipayApiException {
        //Get the initialized AlipayClient
        AlipayClient alipayClient = AliPayUtil.alipayClient;
        // Order model
        String productCode = "FAST_INSTANT_TRADE_PAY";
        AlipayTradePagePayModel model = new AlipayTradePagePayModel();
        model.setOutTradeNo(String.valueOf(System.currentTimeMillis()));
        model.setSubject("Payment test");
        model.setTotalAmount("0.01");
        model.setBody("Payment test of 0.01 element");
        model.setProductCode(productCode);

        AlipayTradePagePayRequest pagePayRequest = new AlipayTradePagePayRequest();
        pagePayRequest.setReturnUrl(AlipayConfig.return_url);
        pagePayRequest.setNotifyUrl(AlipayConfig.notify_url);
        pagePayRequest.setBizModel(model);
        //request
        String result = alipayClient.pageExecute(pagePayRequest).getBody();
        return result;
    }

    /*** Synchronous callback*/
    @RequestMapping("/return_url")
    public ModelAndView return_url(HttpServletResponse response, HttpServletRequest request) throws IOException, AlipayApiException {
        log.info(">>>>>>>>Payment successful, Enter synchronization notification interface...");
        boolean verifyResult = AliPayUtil.rsaCheckV1(request);
        ModelAndView mv = null;
        if (verifyResult) {
            //Merchant order number
            String out_trade_no = AliPayUtil.getByte(request.getParameter("out_trade_no"));
            //Alipay transaction number
            String trade_no = AliPayUtil.getByte(request.getParameter("trade_no"));
            log.info("Merchant order number:{},Alipay transaction number,{}", out_trade_no, trade_no);
            mv = new ModelAndView("paySuccess");
        } else {
            mv = new ModelAndView("payFail");
        }
        return mv;
    }

    /*** Asynchronous callback*/
    @ResponseBody
    @RequestMapping(value = "/notify_url", method = RequestMethod.POST)
    public String notify_url(HttpServletResponse response, HttpServletRequest request) throws IOException, AlipayApiException {
        log.info(">>>>>>>>Payment successful, Enter asynchronous notification interface...");
        // The signature must be verified to prevent hackers from tampering with parameters
        Map<String, String[]> parameterMap = request.getParameterMap();
        StringBuilder notifyBuild = new StringBuilder(">>>>>>>>>> alipay notify >>>>>>>>>>>>>>\n");
        parameterMap.forEach((key, value) -> notifyBuild.append(key + "=" + value[0] + "\n"));
        notifyBuild.append(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
        log.info(notifyBuild.toString());
        boolean flag = AliPayUtil.rsaCheckV1(request);
        if (flag) {
            /**
             * TODO The correctness of the notification data needs to be verified strictly as follows
             *
             * The merchant needs to verify out in the notification data_ trade_ Whether no is the order number created in the merchant system,
             * And judge total_ Whether the amount is the actual amount of the order (i.e. the amount when the merchant order is created),
             * At the same time, you need to verify the seller in the notice_ ID (or seller_email) is out_trade_no the corresponding operator of this document (sometimes, a merchant may have multiple sellers_ id/seller_ email),
             *
             * If any of the above verification fails, it indicates that this notification is an exception notification, which must be ignored.
             * After the above verification is passed, merchants must correctly conduct different business processes according to different types of business notifications of Alipay, and filter repeated notification of the result data.
             * In Alipay's business notifications, only transaction notification status is TRADE_SUCCESS or trade_ At FINISHED, Alipay will decide to pay the buyer successfully.
             */

            //Transaction status
            String tradeStatus = AliPayUtil.getByte(request.getParameter("trade_status"));
            // Merchant order number
            String out_trade_no = AliPayUtil.getByte(request.getParameter("out_trade_no"));
            //Alipay transaction number
            String trade_no = AliPayUtil.getByte(request.getParameter("trade_no"));
            //Payment amount
            String total_amount = AliPayUtil.getByte(request.getParameter("total_amount"));
            log.info("Transaction status:{},Merchant order number:{},Alipay transaction number:{},Payment amount:{}", tradeStatus, out_trade_no, trade_no, total_amount);
            // TRADE_ Finished (indicates that the transaction has been successfully closed, and no subsequent operation can be performed on the transaction);
            // TRADE_ Success (indicates that the transaction has been successfully completed, and subsequent operations can be carried out for the transaction, such as: distribution, refund, etc.);
            if (tradeStatus.equals("TRADE_FINISHED")) {
                //Judge whether the order has been processed in the merchant website
                //If it has not been processed, according to the order number (out_trade_no) find the details of the order in the order system of the merchant website,
                // And judge total_ Whether the amount is the actual amount of the order (i.e. the amount at the time of the merchant order creation) and execute the merchant's business procedures
                //Please be sure to judge the total at the time of request_ fee,seller_id and total obtained during notification_ fee,seller_id is consistent
                //If it has been processed, the business procedure of the merchant will not be executed

                //be careful:
                //If a refundable agreement is signed, and the refund date exceeds the refundable period (such as a refund for three months), the Alipay system sends the notification of the transaction status.
                //If there is no signing a refund agreement, then the Alipay system sends the status notification after payment is completed.
            } else if (tradeStatus.equals("TRADE_SUCCESS")) {
                //Judge whether the order has been processed in the merchant website
                //If it has not been processed, according to the order number (out_trade_no) find the details of the order in the order system of the merchant website,
                // And judge total_ Is the amount the actual amount of the order (i.e., the amount at the time the merchant order was created) and performs the merchant's business procedures
                //Please be sure to judge the total at the time of request_ fee,seller_id and total obtained during notification_ fee,seller_id is consistent
                //If it has been processed, the business procedure of the merchant will not be executed

                //be careful:
                //If a refundable agreement is signed, then the Alipay system sends the notification of the transaction status after payment is completed.

            }
            return "success";
        }
        return "fail";
    }

    /***View payment flow*/
    @RequestMapping(value = "/queryPay")
    @ResponseBody
    public String queryPay(String orderId) throws IOException, AlipayApiException {
        AlipayClient alipayClient = AliPayUtil.alipayClient;
        AlipayTradePagePayModel model = new AlipayTradePagePayModel();
        model.setOutTradeNo(orderId);
        //Set request parameters
        AlipayTradeQueryRequest alipayRequest = new AlipayTradeQueryRequest();
        alipayRequest.setBizModel(model);
        //request
        String result = alipayClient.execute(alipayRequest).getBody();
        return result;
    }

    /**
     * refund
     *
     * @param orderNo Merchant order number
     * @return
     */
    @PostMapping("/refund")
    @ResponseBody
    public String refund(String orderNo) throws AlipayApiException {
        AlipayTradeRefundRequest alipayRequest = new AlipayTradeRefundRequest();
        AlipayTradeRefundModel model = new AlipayTradeRefundModel();
        // Merchant order number
        model.setOutTradeNo(orderNo);
        // refund amount 
        model.setRefundAmount("0.01");
        // Reason for refund
        model.setRefundReason("No reason to return");
        // Refund order number (the same order can be refunded in several parts, which must be passed when it is divided into several parts)
        String outOrderId = UUID.randomUUID().toString();
        model.setOutRequestNo(outOrderId);
        log.info("Refund request No.:{}", outOrderId);
        alipayRequest.setBizModel(model);
        AlipayTradeRefundResponse alipayResponse = AliPayUtil.alipayClient.execute(alipayRequest);
        return alipayResponse.getBody();
    }

    /**
     * Refund inquiry
     *
     * @param orderNo       Merchant order number
     * @param refundOrderNo When requesting the refund interface, the incoming refund request number. If it is not passed in at the time of refund request, the value is the external order number at the time of transaction creation
     * @return
     * @throws AlipayApiException
     */
    @GetMapping("/refundQuery")
    @ResponseBody
    public String refundQuery(String orderNo, String refundOrderNo) throws AlipayApiException {
        AlipayTradeFastpayRefundQueryRequest alipayRequest = new AlipayTradeFastpayRefundQueryRequest();

        AlipayTradeFastpayRefundQueryModel model = new AlipayTradeFastpayRefundQueryModel();
        model.setOutTradeNo(orderNo);
        model.setOutRequestNo(refundOrderNo);
        alipayRequest.setBizModel(model);
        AlipayTradeFastpayRefundQueryResponse alipayResponse = AliPayUtil.alipayClient.execute(alipayRequest);
        System.out.println(alipayResponse.getBody());

        return alipayResponse.getBody();
    }

    /**
     * Close transaction
     *
     * @param orderNo
     * @return
     * @throws AlipayApiException
     */
    @PostMapping("/close")
    @ResponseBody
    public String close(String orderNo) throws AlipayApiException {
        AlipayTradeCloseRequest alipayRequest = new AlipayTradeCloseRequest();
        AlipayTradeCloseModel model = new AlipayTradeCloseModel();
        model.setOutTradeNo(orderNo);
        alipayRequest.setBizModel(model);
        AlipayTradeCloseResponse alipayResponse = AliPayUtil.alipayClient.execute(alipayRequest);
        System.out.println(alipayResponse.getBody());
        return alipayResponse.getBody();
    }
}

3, Interface description

1. To pay for the fake data used here, visit directly without passing any parameters: http://localhost:8080/goAlipay
2. APP payment also uses fake data, which can be accessed directly: http://localhost:8080/appAliPay
3. For a single transfer to the user, you need to fill in the corresponding parameters:

Account is Alipay account, whose name is real name, for example:
One thing to note here is that the minimum amount is 0.1 yuan.
3, refund, first payment on the website to pay 0.01 yuan, and then you look at your Alipay corresponding merchant order number (short), and then pass a orderNo, this parameter is equal to the merchant order number will be successful refund, I directly write the amount is dead, in actual development can be passed with parameters.

4, Summary

This is the function of Springboot, Alipay website payment, APP payment, single transfer to users and refund. If you need other functions, you can refer to the example of Alipay open platform. The main thing is to fill in all parameters correctly. Let me put my project catalog here:
It's very easy to understand these things.
If you need the source code, click here to download: https://download.csdn.net/download/weixin_42322648/12500374

Topics: Java SpringBoot SDK Lombok