Wechat payment repeated callback and java wechat payment callback

I have been studying the issue of wechat payment callback in recent days. I found that the former wechat payment callback was normal and didn't care much. When I tested on my project today, I found that the same code in my project wechat payment callback always repeated execution, resulting in the callback logic being executed after payment success, which is a headache. The callback logic is executing, indicating that the callback is executing normally

There are some answers on the Internet:

Wechat didn't receive SUCCESS message normally. It is recommended to use resXml:

resXml ="<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>"+ "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";

Change to: resXml = "SUCCESS";

 

Some people also said that this method is feasible, but I failed to implement it successfully.

Some people say it's changed to

resXml ="<xml>" + "<return_code>SUCCESS</return_code>"+ "<return_msg>OK</return_msg>" + "</xml> ";

Some people also say that I can modify it or not.

First look at my payment callback code( Only wechat payment callback is mentioned here, because wechat payment is connected here. I also have an article about wechat payment, click this link)

/**
 * @author SpringRoot
 * @description Wechat payment callback
 * @date 2020/3/4-11:17
 */
@Service
public class WeiXinPayNotifyServiceImpl implements WeiXinPayNotifyService {
    private final static Logger LOGGER = LoggerFactory.getLogger(WeiXinPayNotifyServiceImpl.class);

    @Override
    public void weixinpay_notify(HttpServletRequest request,
                                 HttpServletResponse response) {
    /**
          These are payments via wechat. You can get some values by returning the following screenshot. This code doesn't need to worry about the following codes that will continue to show success without repeated callbacks. The payments here are all JSAPA payments
     */

        InputStream inputStream;
        StringBuffer sb = new StringBuffer();
        try {
            inputStream = request.getInputStream();
            String s;
            BufferedReader in = new BufferedReader(new InputStreamReader(
                    inputStream, "UTF-8"));
            while ((s = in.readLine()) != null) {
                sb.append(s);
            }
            in.close();
            inputStream.close();
            Map<String, String> m = new HashMap<String, String>();
            m = WXUtil.doXMLParse(sb.toString());
            SortedMap<Object, Object> packageParams = new TreeMap<Object, Object>();
            Iterator it = m.keySet().iterator();
            while (it.hasNext()) {
                String parameter = (String) it.next();
                String parameterValue = m.get(parameter);
                String v = "";
                if (null != parameterValue) {
                    v = parameterValue.trim();
                }
                packageParams.put(parameter, v);
            }
            String key = WxPayPojo.MCH_KEY; // Secret key this is the secret key of your wechat merchant platform
            if (WXUtil.isTenpaySign("UTF-8", packageParams, key)) {
                String resXml = "";
                if ("SUCCESS".equals((String) packageParams.get("result_code"))) {

                    // Get the returned parameters
                    String openid = (String) packageParams.get("openid");
                    String transaction_id = (String) packageParams
                            .get("transaction_id");
                    String orderNumberMain = (String) packageParams
                            .get("out_trade_no");
                    // Here you can write the business you need
                    LOGGER.debug("I'm a callback function!---I did it.---------------------");
                    LOGGER.debug("openid---->" + openid);
                    LOGGER.debug("transaction_id---->" + transaction_id);
                    LOGGER.debug("out_trade_no---->" + orderNumberMain);
                    resXml =
                            "<xml>"
                            + "<return_code><![CDATA[SUCCESS]]></return_code>"
                            + "<return_msg><![CDATA[OK]]></return_msg>"
                            + "</xml> ";
                    BufferedOutputStream out = new BufferedOutputStream(
                            response.getOutputStream());
                    out.write(resXml.getBytes());
                    out.flush();
                    out.close();
                    return;
                } else {
                    LOGGER.debug("Callback failure");
                }
            } else {
                LOGGER.debug("Callback failure");
            }
        } catch (IOException e) {
            e.printStackTrace();
        } catch (JDOMException e) {
            e.printStackTrace();
        }
    }
}

The following are the parameters that can be called by wechat payment callback. You can see what your project will use. Generally, you can get the order number through the order number operation( Click here to view the wechat payment callback JSAPI document):

That is to say, the above can be mapped to the return value through packageParams.get(").

Here is what I modified after referring to a blog, which meets my requirements: the link is as follows: https://blog.csdn.net/qq_37105358/article/details/81285779

/**
     * Wechat applet payment success callback function
     * @param request
     * @param response
     * @throws Exception
     */
    @RequestMapping(value = "/weixinpay/notify")
    public void wxNotify(HttpServletRequest request,HttpServletResponse response) throws Exception{
        BufferedReader br = new BufferedReader(new InputStreamReader((ServletInputStream)request.getInputStream()));
        String line = null;
        StringBuilder sb = new StringBuilder();
        while((line = br.readLine()) != null){
            sb.append(line);
        }
        br.close();
        //sb returns xml for wechat
        String notityXml = sb.toString();
        String resXml = "";
        System.out.println("Received message:" + notityXml);

        Map map = PayUtil.doXMLParse(notityXml);

        String returnCode = (String) map.get("return_code");
        if("SUCCESS".equals(returnCode)){
            //Verify that the signature is correct
            Map<String, String> validParams = PayUtil.paraFilter(map);  //sign and null value parameters need to be removed during callback signature verification
            String validStr = PayUtil.createLinkString(validParams);//All elements of the array are spliced into strings with "&" characters according to the pattern of "parameter = parameter value"
            String sign = PayUtil.sign(validStr, WxPayPojo.MCH_KEY, "utf-8").toUpperCase();//Assembling and generating signature for server-side verification
            // Because wechat callbacks can be as many as eight times, when the first callback is successful, we will no longer execute the logic

            //According to the introduction of wechat official website, not only the callback parameters are verified here, but also the returned amount and the system order amount are compared and matched
            if(sign.equals(map.get("sign"))){
                // Get the returned parameters
          //In this case, I also mentioned what parameters are needed to get the parameter list directly through map.get, which I also listed above
                String openid = (String) map.get("openid");
                String transaction_id = (String) map.get("transaction_id");
                String orderNumberMain = (String) map.get("out_trade_no");

                /**Callback logic coding*/
                //Notify wechat server that payment has been made successfully
                resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>"
                        + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";
            } else {
                System.out.println("Wechat payment callback failed!Inconsistent signature");
            }
        }else{
            resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"
                    + "<return_msg><![CDATA[The message is empty.]]></return_msg>" + "</xml> ";
        }
        System.out.println(resXml);
        System.out.println("End of wechat payment callback data");

        BufferedOutputStream out = new BufferedOutputStream(
                response.getOutputStream());
        out.write(resXml.getBytes());
        out.flush();
        out.close();
    }

Tools used:

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.SignatureException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.commons.codec.digest.DigestUtils;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;

public class PayUtil {
    /**
     *      * Signature string
     *      * @param text Signature required string
     *      * @param key secret key
     *      * @param input_charset Encoding format
     *      * @return Signature result
     *      
     */
    public static String sign(String text, String key, String input_charset) {
        text = text + "&key=" + key;
        return DigestUtils.md5Hex(getContentBytes(text, input_charset));
    }

    /**
     *      * Signature string
     *      *  @param text Signature required string
     *      * @param sign Signature result
     *      * @param key secret key
     *      * @param input_charset Encoding format
     *      * @return Signature result
     *      
     */
    public static boolean verify(String text, String sign, String key, String input_charset) {
        text = text + key;
        String mysign = DigestUtils.md5Hex(getContentBytes(text, input_charset));
        if (mysign.equals(sign)) {
            return true;
        } else {
            return false;
        }
    }

    /**
     *      * @param content
     *      * @param charset
     *      * @return
     *      * @throws SignatureException
     *      * @throws UnsupportedEncodingException
     *      
     */
    public static byte[] getContentBytes(String content, String charset) {
        if (charset == null || "".equals(charset)) {
            return content.getBytes();
        }
        try {
            return content.getBytes(charset);
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException("MD5 An error occurred during signing,The specified encoding set is incorrect,The encoding set you currently specify is:" + charset);
        }
    }

    private static boolean isValidChar(char ch) {
        if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z'))
            return true;
        if ((ch >= 0x4e00 && ch <= 0x7fff) || (ch >= 0x8000 && ch <= 0x952f))
            return true;// Simplified Chinese character coding
        return false;
    }

    /**
     *      * Remove null values and signature parameters from the array
     *      * @param sArray Signature parameter group
     *      * @return New signature parameter group after removing null value and signature parameter
     *      
     */
    public static Map<String, String> paraFilter(Map<String, String> sArray) {
        Map<String, String> result = new HashMap<String, String>();
        if (sArray == null || sArray.size() <= 0) {
            return result;
        }
        for (String key : sArray.keySet()) {
            String value = sArray.get(key);
            if (value == null || value.equals("") || key.equalsIgnoreCase("sign")
                    || key.equalsIgnoreCase("sign_type")) {
                continue;
            }
            result.put(key, value);
        }
        return result;
    }

    /**
     *      * Sort all elements of the array, and splice "&" characters into strings according to the mode of "parameter = parameter value"
     *      * @param params Parameter groups that need to be sorted and participate in character splicing
     *      * @return Concatenated string
     *      
     */
    public static String createLinkString(Map<String, String> params) {
        List<String> keys = new ArrayList<String>(params.keySet());
        Collections.sort(keys);
        String prestr = "";
        for (int i = 0; i < keys.size(); i++) {
            String key = keys.get(i);
            String value = params.get(key);
            if (i == keys.size() - 1) {// When splicing, the last & character is not included
                prestr = prestr + key + "=" + value;
            } else {
                prestr = prestr + key + "=" + value + "&";
            }
        }
        return prestr;
    }

    /**
     *      *
     *      * @param requestUrl Request address
     *      * @param requestMethod Request method
     *      * @param outputStr parameter
     *      
     */
    public static String httpRequest(String requestUrl, String requestMethod, String outputStr) {
// Create SSLContext
        StringBuffer buffer = null;
        try {
            URL url = new URL(requestUrl);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod(requestMethod);
            conn.setDoOutput(true);
            conn.setDoInput(true);
            conn.connect();
            //Write content to server
            if (null != outputStr) {
                OutputStream os = conn.getOutputStream();
                os.write(outputStr.getBytes("utf-8"));
                os.close();
            }
            // Read the content returned by the server
            InputStream is = conn.getInputStream();
            InputStreamReader isr = new InputStreamReader(is, "utf-8");
            BufferedReader br = new BufferedReader(isr);
            buffer = new StringBuffer();
            String line = null;
            while ((line = br.readLine()) != null) {
                buffer.append(line);
            }
            br.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return buffer.toString();
    }

    public static String urlEncodeUTF8(String source) {
        String result = source;
        try {
            result = java.net.URLEncoder.encode(source, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return result;
    }

    /**
     *      * Parse the xml and return the first level element key value pair. If the first level element has children, the value of this node is the xml data of the children.
     *      * @param strxml
     *      * @return
     *      * @throws JDOMException
     *      * @throws IOException
     *      
     */
    public static Map doXMLParse(String strxml) throws Exception {
        if (null == strxml || "".equals(strxml)) {
            return null;
        }

        Map m = new HashMap();
        InputStream in = String2Inputstream(strxml);
        SAXBuilder builder = new SAXBuilder();
        Document doc = builder.build(in);
        Element root = doc.getRootElement();
        List list = root.getChildren();
        Iterator it = list.iterator();
        while (it.hasNext()) {
            Element e = (Element) it.next();
            String k = e.getName();
            String v = "";
            List children = e.getChildren();
            if (children.isEmpty()) {
                v = e.getTextNormalize();
            } else {
                v = getChildrenText(children);
            }

            m.put(k, v);
        }

        //Closed flow
        in.close();

        return m;
    }

    /**
     *      * Get the xml of the child node
     *      * @param children
     *      * @return String
     *      
     */
    public static String getChildrenText(List children) {
        StringBuffer sb = new StringBuffer();
        if (!children.isEmpty()) {
            Iterator it = children.iterator();
            while (it.hasNext()) {
                Element e = (Element) it.next();
                String name = e.getName();
                String value = e.getTextNormalize();
                List list = e.getChildren();
                sb.append("<" + name + ">");
                if (!list.isEmpty()) {
                    sb.append(getChildrenText(list));
                }
                sb.append(value);
                sb.append("</" + name + ">");
            }
        }

        return sb.toString();
    }

    public static InputStream String2Inputstream(String str) {
        return new ByteArrayInputStream(str.getBytes());
    }
}

 

116 original articles published, 181 praised, 10000 visitors+
Private letter follow

Tags: Java xml encoding Apache

Posted on Wed, 04 Mar 2020 22:07:31 -0800 by xcmir