Shopping Mall Project Server Practices SSM - ----- Front Desk Payment Interface (Payment in Sweep Payment in Face)

Posted by Cultureshock on Wed, 09 Oct 2019 18:16:12 +0200

Payment of documents in person

  • Verification process:

  • Payment process:

  • Common Constants
package com.mmall.common;

import com.google.common.collect.Sets;

import java.util.Set;

//Setting Common Quantity
public class Const {
    public static final String CURRENT_USER = "currentUser";
    public static final String EMAIL="email";
    public static final String USERNAME="username";
    public interface Role{
        int ROLE_CUSTOMER=0;//Ordinary users
        int ROLE_ADMIN=1;//Administrators
    }

    public enum ProductStatusEnum{
        ON_SALE(1,"On-line");
        private String value;
        private int code;
        ProductStatusEnum(int code,String value){
            this.code=code;
            this.value=value;
        }
        public String getValue(){
            return value;
        }
        public int getCode(){
            return code;
        }
    }
    public enum OrderStatusEnum{
        CANCELED(0,"Cancelled"),
        NO_PAY(10,"Unpaid"),
        PAID(20,"Payment made"),
        SHIPPED(40,"Shipped"),
        ORDER_SUCCESS(50,"Order Completion"),
        ORDER_CLOSE(60,"Order closure");

        private String value;
        private int code;
        OrderStatusEnum(int code,String value){
            this.code=code;
            this.value=value;

        }
        public String getValue(){
            return value;
        }
        public int getCode(){
            return code;
        }

        public static OrderStatusEnum codeOf(int code){
            for(OrderStatusEnum orderStatusEnum : values()){
                if(orderStatusEnum.getCode() == code){
                    return orderStatusEnum;
                }
            }
            throw new RuntimeException("No corresponding enumeration was found");
        }
    }
        public interface AlipayCallback

    {
        String TRADE_STATUS_WAIT_BUYER_PAY = "WAIT_BUYER_PAY";
        String TRADE_STATUS_TRADE_SUCCESS = "TRADE_SUCCESS";

        String RESPONSE_SUCCESS = "success";
        String RESPONSE_FAILED = "failed";

    }

    public enum PayPlatformEnum{
        ALIPAY(1,"Alipay");

        PayPlatformEnum(int code,String value){
            this.code = code;
            this.value = value;
        }
        private String value;
        private int code;

        public String getValue() {
            return value;
        }

        public int getCode() {
            return code;
        }
    }

    public enum PaymentTypeEnum{
        ONLINE_PAY(1,"Online payment");

        PaymentTypeEnum(int code,String value){
            this.code = code;
            this.value = value;
        }
        private String value;
        private int code;

        public String getValue() {
            return value;
        }

        public int getCode() {
            return code;
        }

        public static PaymentTypeEnum codeOf(int code){
            for(PaymentTypeEnum paymentTypeEnum : values()){
                if(paymentTypeEnum.getCode() == code){
                    return paymentTypeEnum;
                }
            }
            throw new RuntimeException("No corresponding enumeration was found");
        }

    }

}

 

I. Payment (Sweep Payment Payment in Face)

  • Ideas:

1. When a user generates an order and initiates payment, the merchant initiates an advance order request to Alipay with the user-generated order data and asynchronous notification callback address as the request parameters. After successful pre-order, Alipay returns a payment code to the merchant, who stores the payment code on the FTP server and then displays it to the user for payment.

2. User sweep code to make payment. Alipay will actively call back the change information of the order to the merchant along the callback address of the merchant when it invokes the request for pre-order.

3. Businessmen use RSA2 to verify the results of Alipay callbacks, confirming that the results are returned by Alipay. In addition, we also need to verify whether the data order number returned by Alipay is created for merchants, and whether the total transaction amount is correct, etc.

4. If the certification is passed, the merchant will return "success" to Alipay, that is, payment success. (Write the payment amount and status of the order into the order form, and also put the payment information into the payment database table.)

  • controller
    //Payment in person
    @RequestMapping("pay.do")
    @ResponseBody
    public ServerResponse pay(HttpSession session, Long orderNo, HttpServletRequest request){
        User user= (User) session.getAttribute(Const.CURRENT_USER);
        if (user == null){
            return ServerResponse.createByErrorCodeMessage(ResponseCode.NEED_LOGIN.getCode(),ResponseCode.NEED_LOGIN.getDesc());
        }
        String path=request.getSession().getServletContext().getRealPath("upload");
        return iOrderService.pay(orderNo,user.getId(),path);
    }
  • impl
 //Journal
    private static final Logger logger = LoggerFactory.getLogger(OrderServiceImpl.class);
    //payment
    public ServerResponse pay(Long orderNo, Integer userId, String path) {

        Map<String, String> resultMap = Maps.newHashMap();
        //Query whether the user has the order
        Order order = orderMapper.selectByUserIdAndOrderNo(userId, orderNo);
        if (order == null) {
            return ServerResponse.createByErrorMessage("Users do not have the order");
        }
        resultMap.put("orderNo", String.valueOf(order.getOrderNo()));

        // (Mandatory) The only order number in the order system of the merchant website, which is less than 64 characters, can only contain letters, numbers and underscores.
        // To ensure that the merchant end of the system can not be duplicated, it is recommended to generate by database sequence.
        String outTradeNo = order.getOrderNo().toString();

        // The title of the order, which roughly describes the user's purpose of payment. For example, "xxx x brand XXXX stores pay for browsing consumption in person"
        String subject = new StringBuilder().append("mmall Online Mall Scanning Payment, Order Number").append(outTradeNo).toString();

        // The total amount of the order, in yuan, shall not exceed 100 million yuan.
        // If the discount amount, non-discount amount and total order amount are introduced at the same time, the following conditions must be satisfied: [total order amount]= [discount amount]+ [non-discount amount]
        String totalAmount = order.getPayment().toString();

        // (optional) The non-discountable amount of the order can be allocated with the discount activities of the merchant platform. If the liquor does not participate in the discount, the corresponding amount will be filled in to this field.
        // If the value is not passed in, but the value is passed in [total order amount], [discount amount], then the value defaults to [total order amount] - [discount amount]
        String undiscountableAmount = "0";

        // The Seller Alipay Account ID is used to support a subscription account to support the payment to different receipt accounts. (The payment to sellerId corresponding Alipay Account)
        // If the field is empty, the default is the PID of the merchant who signed the Alipay contract, that is, the PID corresponding to the appid.
        String sellerId = "";

        // Order description, can be a detailed description of the transaction or commodities, such as filling in the "purchase of goods 2 total 15.00 yuan"
        String body = new StringBuilder().append("Order").append(outTradeNo).append("Buying Commodities altogether").append(totalAmount).append("element").toString();

        // Business Operator Number. Add this parameter to make sales statistics for business operators.
        String operatorId = "test_operator_id";

        // (Must fill in) the number of the store, through the store number and the business background, you can configure discount information to the store accurately, and inquire about the technical support of Alipay in detail.
        String storeId = "test_store_id";

        // Business expansion parameters can be added by Alipay's system number (through setSysService ProviderId method). For details, please consult Alipay's technical support.
        ExtendParams extendParams = new ExtendParams();
        extendParams.setSysServiceProviderId("2088100200300400500");

        // Payment overtime, defined as 120 minutes
        String timeoutExpress = "120m";

        // The detailed list of goods declared by Alipay shall be filled in with the detailed information of the goods purchased.
        List<GoodsDetail> goodsDetailList = new ArrayList<GoodsDetail>();
        //OrderItem has a detailed list of orders for its own mall
        List<OrderItem> orderItemList = orderItemMapper.getByOderNoAndUserId(orderNo, userId);

        //The foreach loop, where information about each item is added to GoodsDetail
        for (OrderItem orderItem : orderItemList) {
            // Create a merchandise information with parameters meaning of commodity id (using national standard), name, unit price (unit score), quantity. If you need to add a commodity category, see Goods Detail for details.
            GoodsDetail goods = GoodsDetail.newInstance(orderItem.getProductId().toString(), orderItem.getProductName(), BigDecimalUtil.mul(orderItem.getCurrentUnitPrice().doubleValue(), new Double(100).doubleValue()).longValue(), orderItem.getQuantity());
            // Create an item and add it to the list of items
            goodsDetailList.add(goods);
        }

        // Create a scanned payment request builder and set the request parameters
        AlipayTradePrecreateRequestBuilder builder = new AlipayTradePrecreateRequestBuilder()
                .setSubject(subject).setTotalAmount(totalAmount).setOutTradeNo(outTradeNo)
                .setUndiscountableAmount(undiscountableAmount).setSellerId(sellerId).setBody(body)
                .setOperatorId(operatorId).setStoreId(storeId).setExtendParams(extendParams)
                .setTimeoutExpress(timeoutExpress)
                .setNotifyUrl(PropertiesUtil.getProperty("alipay.callback.url"))//Alipay server actively notifies the merchant server of the specified page http path, according to the need to set up
                .setGoodsDetailList(goodsDetailList);

        /** Be sure to call Configs.init() to set default parameters before creating the AlipayTradeService
         *  Configs It reads the configuration information of the zfbinfo.properties file under classpath, and if it is not found, confirms whether the file is in the classpath directory.
         */
        Configs.init("zfbinfo.properties");

        /** Use the default parameters provided by Configs
         *  AlipayTradeService You can use singletons or static member objects without repeating new
         */
        AlipayTradeService tradeService = new AlipayTradeServiceImpl.ClientBuilder().build();

        AlipayF2FPrecreateResult result = tradeService.tradePrecreate(builder);

        switch (result.getTradeStatus()) {
            case SUCCESS:
                logger.info("Successful advance order of Alipay: )");

                AlipayTradePrecreateResponse response = result.getResponse();
                dumpResponse(response);

                // Alipay successfully generated two-dimensional code by placing an order in advance and uploaded the two-dimensional code picture to FTP server.
                //A folder that declares uploaded files saved
                File folder = new File(path);
                //Create a folder if it does not exist
                if (!folder.exists()) {
                    //The permissions of folders are writable
                    folder.setWritable(true);
                    //create folder
                    folder.mkdirs();
                }
                // Need to be modified to run the path on the machine
                //Two-Dimensional Code Path
                String qrPath = String.format(path + "/qr-%s.png", response.getOutTradeNo());
                //The generated two-dimensional code file name, named after the order number, is automatically replaced by% s, such as qr-11111111.png
                String qrFileName = String.format("qr-%s.png", response.getOutTradeNo());
                //Get the picture generated by two-dimensional code
                ZxingUtils.getQRCodeImge(response.getQrCode(), 256, qrPath);

                //Create uploaded files
                File targetFile = new File(path, qrFileName);
                try {
                    //Upload to FTP Server
                    FTPUtil.uploadFile(Lists.<File>newArrayList(targetFile));
                } catch (IOException e) {
                    logger.error("Upload two-dimensional code anomaly", e);
                }
                logger.info("qrPath:" + qrPath);
                //Two-dimensional code URL s
                String qrUrl = PropertiesUtil.getProperty("ftp.server.http.prefix") + targetFile.getName();
                resultMap.put("quUrl", qrUrl);
                return ServerResponse.createBySuccess(resultMap);


            case FAILED:
                logger.error("Alipay failed to place an advance order!!!");
                return ServerResponse.createByErrorMessage("Alipay failed to place an advance order!!!");

            case UNKNOWN:
                logger.error("System exception, single state unknown in advance!!!");
                return ServerResponse.createByErrorMessage("System exception, single state unknown in advance!!!");

            default:
                logger.error("Unsupported transaction status, transaction return exception!!!");
                return ServerResponse.createByErrorMessage("Unsupported transaction status, transaction return exception!!!");

        }

    }

    // Simple Print Response
    private void dumpResponse(AlipayResponse response) {
        if (response != null) {
            logger.info(String.format("code:%s, msg:%s", response.getCode(), response.getMsg()));
            if (StringUtils.isNotEmpty(response.getSubCode())) {
                logger.info(String.format("subCode:%s, subMsg:%s", response.getSubCode(),
                        response.getSubMsg()));
            }
            logger.info("body:" + response.getBody());
        }
    }

2. Alipay Callback Verification

  • controller
//Alipay callback function
    //Using the sdk method https://openclub.alipay.com/club/history/read/2214
    @RequestMapping("alipay_callback.do")
    @ResponseBody
    public Object alipayCallback(HttpServletRequest request){
        Map<String,String> params= Maps.newHashMap();
        //Recording the parameters and values of Alipay
        Map requestParams=request.getParameterMap();
        //value of the passed parameter is taken out
        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);
        }
        logger.info("Alipay callback, sign:{},trade_statux;{},parameter:{}",params.get("sign"),params.get("trade_status"),params.toString());

        //Verify the correctness of the callback. Is it Alipay's?
        //In the list of notification return parameters, except sign and sign_type, all parameters returned by notification are parameters to be checked.
        //Since sign has been automatically removed from Alipay, we have to remove sign_type ourselves.
        params.remove("sign_type");
        try {
            //Call SDK to verify signature and verify Alipay public key
            boolean alipayRSACheckedV2= AlipaySignature.rsaCheckV2(params, Configs.getAlipayPublicKey(),"utf-8",Configs.getSignType());
            //Verification failed
            if(!alipayRSACheckedV2){
                return ServerResponse.createByErrorMessage("Illegal request, validation failed");
            }

        } catch (AlipayApiException e) {
            logger.error("Alipay Verification Callback Abnormal",e);
        }
	/* The actual verification process suggests that merchants must add the following checks:
	1,It is necessary to verify whether out_trade_no in the notification data is the order number created in the merchant system.
	2,Determine whether total_amount is the actual amount of the order (that is, the amount at the time the merchant order was created).
	3,Check whether the seller_id (or seller_email) in the notification is the corresponding operation of the out_trade_no document (sometimes, a merchant may have more than one seller_id/seller_email)
	4,Verify that app_id is the merchant itself.
	*/
        //Verify various data
        ServerResponse serverResponse=iOrderService.aliCallback(params);

        //If the verification is successful, Alipay will be returned to success.
        if (serverResponse.isSuccess()){
           return Const.AlipayCallback.RESPONSE_SUCCESS;
       }
        return Const.AlipayCallback.RESPONSE_FAILED;
    }
  • impl
//This link allows you to view the Alipay asynchronous notification parameter https://docs.open.alipay.com/194/103296#s5
    public ServerResponse aliCallback(Map<String, String> params) {
        //Order number
        Long orderNo = Long.parseLong(params.get("out_trade_no"));
        //Alipay Trading Number
        String tradeNo = params.get("trade_no");
        //Transaction status
        String tradeStatus = params.get("trade_status");
        //Query for the order
        Order order = orderMapper.selectByOrderNo(orderNo);
        if (order == null) {
            return ServerResponse.createByErrorMessage("Orders not in this store");
        }
        //If there is an order, and the order has been paid
        if (order.getStatus() >= Const.OrderStatusEnum.PAID.getCode()) {
            return ServerResponse.createBySuccess("Order reuse");
        }
        //In Alipay's business notification, Alipay will be recognized as a successful buyer only if the transaction notification status is TRADE_SUCCESS or TRADE_FINISHED.
        //If the value of trade status is equal to TRADE_SUCCESS
        if (Const.AlipayCallback.TRADE_STATUS_TRADE_SUCCESS.equals(tradeStatus)) {
            //Add the order information to the order form
            order.setPaymentTime(DateTimeUtil.strToDate(params.get("gmt_payment")));
            order.setStatus(Const.OrderStatusEnum.PAID.getCode());
            orderMapper.updateByPrimaryKeySelective(order);
        }
        //Set the payment information for the order in the payment information table
        PayInfo payInfo = new PayInfo();
        payInfo.setUserId(order.getUserId());
        payInfo.setOrderNo(order.getOrderNo());
        payInfo.setPayPlatform(Const.PayPlatformEnum.ALIPAY.getCode());
        payInfo.setPlatformNumber(tradeNo);
        payInfo.setPlatformStatus(tradeStatus);

        payInfoMapper.insert(payInfo);
        return ServerResponse.createBySuccess();
    }

3. Query the status of order payment

  • controller
        //After payment, jump to the order page and inquire about the status of order payment
        @RequestMapping("query_order_pay_status.do")
        @ResponseBody
        public ServerResponse<Boolean> queryOrderPayStatus(HttpSession session, Long orderNo){
            User user= (User) session.getAttribute(Const.CURRENT_USER);
            if (user == null){
                return ServerResponse.createByErrorCodeMessage(ResponseCode.NEED_LOGIN.getCode(),ResponseCode.NEED_LOGIN.getDesc());
            }
                    ServerResponse serverResponse=iOrderService.queryOrderPayStatus(user.getId(),orderNo);
            if (serverResponse.isSuccess()){
                return ServerResponse.createBySuccess(true);
            }
            return ServerResponse.createBySuccess(false);
        }
  • impl
//After payment, jump to the order and inquire about the payment status of the order
    public ServerResponse queryOrderPayStatus(Integer userId, Long orderNo) {
        Order order = orderMapper.selectByUserIdAndOrderNo(userId, orderNo);
        if (order == null) {
            return ServerResponse.createByErrorMessage("Orders not in this store");
        }
        if (order.getStatus() >= Const.OrderStatusEnum.PAID.getCode()) {
            return ServerResponse.createBySuccess();
        }
        return ServerResponse.createByError();
    }

 

Topics: ftp Session Database SDK