Java, micro-channel pay (pay scan code mode two) real case

Abstract : A recent project involved the payment service, which uses a micro-channel payment and Alipay, in the process of doing also encountered some problems, so now summarize comb, to share with people in need, but also look back to their own future leave ideas.

A: micro-channel pay access preparation work :

First, micro-channel pay only supports enterprise users, individual users can not access micro-channel pay, so in order to access micro-channel payment, you first need to have a public micro-channel number, the enterprises can apply. With micro-channel public number, you can apply for micro-channel pay-related content, so until you are ready to start writing code needs to put the following parameters apply good: the public account ID, micro-channel pay merchant number, API key, AppSecret is corresponding APPID Interface password, callback address (the callback outside the network must be guaranteed access to this address) and send a request computer's IP

Two: micro-channel payment process Description:

With these parameters mentioned above, then we can access micro-channel pay, here I look at the official documentation micro-channel payment (https://pay.weixin.qq.com/wiki/doc/api/index. html), visit this address may see a variety of payment methods to choose from, here we choose the way to pay the scan code (https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter= 6_1)

Here we choose mode two, the following two timing chart look pattern, as shown below:

Model II and a comparison mode, the process is simpler, it does not depend on paid set callback URL. Call the merchant back office systems to micro-channel pay unified under a single interface, a micro letter backstage return link system parameters code_url, merchant back office systems will code_url value generated two-dimensional code images, users use micro-channel client scan code to initiate the payment. Note: code_url valid for two hours after the expiration scan code can not initiate a payment.

 Business Process Description:

(1) The system generates a background line merchant user to buy the goods.

(2) the user to confirm the payment call micro-channel pay [ unified under a single API ] generate a pre-payment transaction;

(3)微信支付系统收到请求后生成预支付交易单,并返回交易会话的二维码链接code_url。

(4)商户后台系统根据返回的code_url生成二维码。

(5)用户打开微信“扫一扫”扫描二维码,微信客户端将扫码内容发送到微信支付系统。

(6)微信支付系统收到客户端请求,验证链接有效性后发起用户支付,要求用户授权。

(7)用户在微信客户端输入密码,确认支付后,微信客户端提交授权。

(8)微信支付系统根据用户授权完成支付交易。

(9)微信支付系统完成支付交易后给微信客户端返回交易结果,并将交易结果通过短信、微信消息提示用户。微信客户端展示支付交易结果页面。

(10)微信支付系统通过发送异步消息通知商户后台系统支付结果。商户后台系统需回复接收情况,通知微信后台系统不再发送该单的支付通知。

(11)未收到支付通知的情况,商户后台系统调用【查询订单API】。

(12)商户确认订单已支付后给用户发货。

三:微信支付所需要的maven依赖:

<!--生成二维码jar-->
        <dependency>
            <groupId>com.google.zxing</groupId>
            <artifactId>core</artifactId>
            <version>3.2.1</version>
        </dependency>

四:微信支付调用统一下单接口的核心代码

3.1:微信支付工具类:

HttpUtil.java

package com.micai.springboot.util.pay.wx;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.URL;
import java.net.URLConnection;

/**
 * http工具类,负责发起post请求并获取的返回
 */
public class HttpUtil {

    private static final Logger logger = LoggerFactory.getLogger(HttpUtil.class);

    private final static int CONNECT_TIMEOUT = 5000; // in milliseconds  
    private final static String DEFAULT_ENCODING = "UTF-8";  
      
    public static String postData(String urlStr, String data){  
        return postData(urlStr, data, null);
    }
      
    public static String postData(String urlStr, String data, String contentType){  
        BufferedReader reader = null;  
        try {  
            URL url = new URL(urlStr);  
            URLConnection conn = url.openConnection();  
            conn.setDoOutput(true);  
            conn.setConnectTimeout(CONNECT_TIMEOUT);  
            conn.setReadTimeout(CONNECT_TIMEOUT);  
            if(contentType != null)  
                conn.setRequestProperty("content-type", contentType);  
            OutputStreamWriter writer = new OutputStreamWriter(conn.getOutputStream(), DEFAULT_ENCODING);  
            if(data == null)  
                data = "";  
            writer.write(data);   
            writer.flush();  
            writer.close();    
  
            reader = new BufferedReader(new InputStreamReader(conn.getInputStream(), DEFAULT_ENCODING));  
            StringBuilder sb = new StringBuilder();  
            String line = null;  
            while ((line = reader.readLine()) != null) {  
                sb.append(line);  
                sb.append("\r\n");  
            }  
            return sb.toString();  
        } catch (IOException e) {  
            logger.error("Error connecting to " + urlStr + ": " + e.getMessage());
        } finally {
            try {  
                if (reader != null)  
                    reader.close();  
            } catch (IOException e) {
                e.printStackTrace();
            }  
        }
        return null;  
    }  

}

MD5Util.java

package com.micai.springboot.util.pay.wx;

import java.security.MessageDigest;

public class MD5Util {
    
    private static String byteArrayToHexString(byte b[]) {  
        StringBuffer resultSb = new StringBuffer();  
        for (int i = 0; i < b.length; i++)  
            resultSb.append(byteToHexString(b[i]));  
  
        return resultSb.toString();  
    }  
  
    private static String byteToHexString(byte b) {  
        int n = b;  
        if (n < 0)  
            n += 256;  
        int d1 = n / 16;  
        int d2 = n % 16;  
        return hexDigits[d1] + hexDigits[d2];  
    }  
  
    public static String MD5Encode(String origin, String charsetname) {  
        String resultString = null;  
        try {  
            resultString = new String(origin);  
            MessageDigest md = MessageDigest.getInstance("MD5");  
            if (charsetname == null || "".equals(charsetname))  
                resultString = byteArrayToHexString(md.digest(resultString  
                        .getBytes()));  
            else  
                resultString = byteArrayToHexString(md.digest(resultString  
                        .getBytes(charsetname)));  
        } catch (Exception exception) {  
        }  
        return resultString;  
    }  
  
    private static final String hexDigits[] = { "0", "1", "2", "3", "4", "5",  
            "6", "7", "8", "9", "a", "b", "c", "d", "e", "f" };  

}

PayConfigUtil.java

package com.micai.springboot.util.pay.wx;

public class PayConfigUtil {
	//初始化
//	public final static String APP_ID = "11111111111"; //公众账号appid(改为自己实际的)
//	public final static String APP_SECRET = "";
//	public final static String MCH_ID = "111111"; //商户号(改为自己实际的)
//	public final static String API_KEY = "11111111111"; //(改为自己实际的)key设置路径:微信商户平台(pay.weixin.qq.com)-->账户设置-->API安全-->密钥设置
	
	//统一下单
	public final static String UFDODER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder";
//	public final static String NOTIFY_URL = "http://xxxxxxx"; //微信支付回调接口,就是微信那边收到(改为自己实际的)
//	//企业向个人账号付款的URL
//	public final static String SEND_EED_PACK_URL = "https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers";
//
//	public final static String CREATE_IP = "113.69.246.11";//发起支付ip(改为自己实际的)
	
}
PayToolUtil.java
package com.micai.springboot.util.pay.wx;

import java.text.SimpleDateFormat;
import java.util.*;

public class PayToolUtil {

    /** 
     * 是否签名正确,规则是:按参数名称a-z排序,遇到空值的参数不参加签名。 
     * @return boolean 
     */  
    public static boolean isTenpaySign(String characterEncoding, SortedMap<Object, Object> packageParams, String API_KEY) {  
        StringBuffer sb = new StringBuffer();  
        Set es = packageParams.entrySet();  
        Iterator it = es.iterator();  
        while(it.hasNext()) {  
            Map.Entry entry = (Map.Entry)it.next();  
            String k = (String)entry.getKey();  
            String v = (String)entry.getValue();  
            if(!"sign".equals(k) && null != v && !"".equals(v)) {  
                sb.append(k + "=" + v + "&");  
            }  
        }  
          
        sb.append("key=" + API_KEY);  
          
        //算出摘要  
        String mysign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toLowerCase();  
        String tenpaySign = ((String)packageParams.get("sign")).toLowerCase();  
        
        //System.out.println(tenpaySign + "    " + mysign);  
        return tenpaySign.equals(mysign);  
    }

    /**
     * 创建sign签名
     * @param characterEncoding 编码格式
     * @param packageParams 请求参数
     * @param API_KEY API密钥
     * @return
     */
    public static String createSign(String characterEncoding, SortedMap<Object, Object> packageParams, String API_KEY) {  
        StringBuffer sb = new StringBuffer();  
        Set es = packageParams.entrySet();  
        Iterator it = es.iterator();  
        while (it.hasNext()) {  
            Map.Entry entry = (Map.Entry) it.next();  
            String k = (String) entry.getKey();  
            String v = (String) entry.getValue();  
            if (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) {  
                sb.append(k + "=" + v + "&");  
            }  
        }  
        sb.append("key=" + API_KEY);  
        String sign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase();  
        return sign;  
    }  

    /**
     * 将请求参数转换为xml格式的string
     * @param parameters 请求参数
     * @return 转换后的字符串
     */
    public static String getRequestXml(SortedMap<Object, Object> parameters) {  
        StringBuffer sb = new StringBuffer();
        sb.append("<xml>");
        Set es = parameters.entrySet();  
        Iterator it = es.iterator();  
        while (it.hasNext()) {
            Map.Entry entry = (Map.Entry) it.next();  
            String k = (String) entry.getKey();  
            String v = (String) entry.getValue();  
            if ("attach".equalsIgnoreCase(k) || "body".equalsIgnoreCase(k) || "sign".equalsIgnoreCase(k)) {
                sb.append("<" + k + ">" + "<![CDATA[" + v + "]]></" + k + ">");
            } else {
                sb.append("<" + k + ">" + v + "</" + k + ">");
            }  
        }
        sb.append("</xml>");
        return sb.toString();
    }  
  
    /**
     * 取出一个指定长度大小的随机正整数
     * @param length int 设定所取出随机数的长度。length小于11
     * @return int 返回生成的随机数。
     */
    public static int buildRandom(int length) {  
        int num = 1;  
        double random = Math.random();  
        if (random < 0.1) {  
            random = random + 0.1;  
        }  
        for (int i = 0; i < length; i++) {  
            num = num * 10;  
        }  
        return (int) ((random * num));  
    }

    /**
     * 获取当前时间 yyyyMMddHHmmss
     * @return
     */
    public static String getCurrTime() {  
        Date now = new Date();  
        SimpleDateFormat outFormat = new SimpleDateFormat("yyyyMMddHHmmss");  
        return outFormat.format(now);
    }
    
}

QRUtil.java

package com.micai.springboot.util.pay.wx;

import com.google.zxing.common.BitMatrix;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;

/**
 * 二维码生产工具类
 */
public class QRUtil {
	
   private static final int BLACK = 0xFF000000; 
   private static final int WHITE = 0xFFFFFFFF; 
    
   private QRUtil() {} 

   public static BufferedImage toBufferedImage(BitMatrix matrix) {
     int width = matrix.getWidth(); 
     int height = matrix.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, matrix.get(x, y) ? BLACK : WHITE); 
       } 
     } 
     return image; 
   } 

   public static void writeToFile(BitMatrix matrix, String format, File file)
       throws IOException { 
     BufferedImage image = toBufferedImage(matrix); 
     if (!ImageIO.write(image, format, file)) { 
       throw new IOException("Could not write an image of format " + format + " to " + file); 
     } 
   }

   public static void writeToStream(BitMatrix matrix, String format, OutputStream stream)
       throws IOException { 
     BufferedImage image = toBufferedImage(matrix); 
     if (!ImageIO.write(image, format, stream)) { 
       throw new IOException("Could not write an image of format " + format); 
     }
   }
}
XMLUtil4jdom.java
package com.micai.springboot.util.pay.wx;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

public class XMLUtil4jdom {

    /** 
     * 解析xml,返回第一级元素键值对。如果第一级元素有子节点,则此节点的值是子节点的xml数据。 
     * @param strxml 
     * @return 
     * @throws JDOMException 
     * @throws IOException 
     */  
    public static Map doXMLParse(String strxml) throws JDOMException, IOException {  
        strxml = strxml.replaceFirst("encoding=\".*\"", "encoding=\"UTF-8\"");  
        if(null == strxml || "".equals(strxml)) {
            return null;  
        }
        Map<String, String> m = new HashMap<String, String>();
        InputStream in = new ByteArrayInputStream(strxml.getBytes("UTF-8"));  
        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 = XMLUtil4jdom.getChildrenText(children);  
            }  
            m.put(k, v);
        }  
        //关闭流
        in.close();  
        return m;
    }  
      
    /** 
     * 获取子结点的xml 
     * @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(XMLUtil4jdom.getChildrenText(list));  
                }  
                sb.append(value);  
                sb.append("</" + name + ">");  
            }  
        }  
        return sb.toString();
    }

}

3.2:微信支付实体对象:

WxpayVo.java
package com.micai.springboot.vo.pay;

import java.io.Serializable;

/**
 * @Auther: zhaoxinguo
 * @Date: 2018/8/31 11:34
 * @Description:
 */
public class WxpayVo implements Serializable {

    private String app_id;//公众账号ID
    private String mch_id;//微信支付商户号
    private String key;//API密钥
    private String app_secret;//AppSecret是APPID对应的接口密码

    private String out_trade_no;// 商户订单号
    private String currTime;
    private String strTime;
    private String strRandom;
    private String nonce_str;//随机字符串
    private String spbill_create_ip;
    private String notify_url;
    private String trade_type;
    private String total_fee;

    public String getApp_id() {
        return app_id;
    }

    public void setApp_id(String app_id) {
        this.app_id = app_id;
    }

    public String getMch_id() {
        return mch_id;
    }

    public void setMch_id(String mch_id) {
        this.mch_id = mch_id;
    }

    public String getKey() {
        return key;
    }

    public void setKey(String key) {
        this.key = key;
    }

    public String getApp_secret() {
        return app_secret;
    }

    public void setApp_secret(String app_secret) {
        this.app_secret = app_secret;
    }

    public String getOut_trade_no() {
        return out_trade_no;
    }

    public void setOut_trade_no(String out_trade_no) {
        this.out_trade_no = out_trade_no;
    }

    public String getCurrTime() {
        return currTime;
    }

    public void setCurrTime(String currTime) {
        this.currTime = currTime;
    }

    public String getStrTime() {
        return strTime;
    }

    public void setStrTime(String strTime) {
        this.strTime = strTime;
    }

    public String getStrRandom() {
        return strRandom;
    }

    public void setStrRandom(String strRandom) {
        this.strRandom = strRandom;
    }

    public String getNonce_str() {
        return nonce_str;
    }

    public void setNonce_str(String nonce_str) {
        this.nonce_str = nonce_str;
    }

    public String getSpbill_create_ip() {
        return spbill_create_ip;
    }

    public void setSpbill_create_ip(String spbill_create_ip) {
        this.spbill_create_ip = spbill_create_ip;
    }

    public String getNotify_url() {
        return notify_url;
    }

    public void setNotify_url(String notify_url) {
        this.notify_url = notify_url;
    }

    public String getTrade_type() {
        return trade_type;
    }

    public void setTrade_type(String trade_type) {
        this.trade_type = trade_type;
    }

    public String getTotal_fee() {
        return total_fee;
    }

    public void setTotal_fee(String total_fee) {
        this.total_fee = total_fee;
    }
}

3.3:微信支付业务对象:

PayBaseController.java
package com.micai.springboot.controller.pay;

import com.micai.springboot.base.BaseController;
import org.springframework.beans.factory.annotation.Value;

/**
 * @Auther: zhaoxinguo
 * @Date: 2018/8/31 13:40
 * @Description:
 */
public abstract class PayBaseController extends BaseController {

    // 支付宝支付参数配置 //
    @Value("${ALIPAY.APPID}")
    protected String app_id;//应用ID,您的APPID,收款账号既是您的APPID对应支付宝账号
    @Value("${ALIPAY.PRIVATEKEY}")
    protected String merchant_private_key;//商户私钥,您的PKCS8格式RSA2私钥
    @Value("${ALIPAY.PUBLICKEY}")
    protected String alipay_public_key;//支付宝公钥,查看地址:https://openhome.alipay.com/platform/keyManage.htm 对应APPID下的支付宝公钥。
    @Value("${ALIPAY.NOTIFY_URL}")
    protected String notify_url;//服务器异步通知页面路径
    @Value("${ALIPAY.RETURNA_URL}")
    protected String return_url;//页面跳转同步通知页面路径
    @Value("${ALIPAY.SIGN}")
    protected String sign_type = "RSA2";//签名方式
    protected String charset = "utf-8";//字符编码格式
    @Value("${ALIPAY.GATEWAY_URL}")
    protected String gateway_url;//支付宝网关

    // 微信支付参数配置 //
    @Value("${WXPAY.APPID}")
    protected String APPID;//公众账号ID
    @Value("${WXPAY.MCHID}")
    protected String MCHID;//微信支付商户号
    @Value("${WXPAY.KEY}")
    protected String KEY;//API密钥
    @Value("${WXPAY.APPSECRET}")
    protected String APPSECRET;//AppSecret是APPID对应的接口密码
    @Value("${WXPAY.NOTIFY_URL}")
    protected String NOTIFY_URL;//回调地址。测试回调必须保证外网能访问到此地址
    @Value("${WXPAY.CREATE_IP}")
    protected String CREATE_IP;//发起请求的电脑IP

}

WxpayController.java

package com.micai.springboot.controller.pay;

import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix;
import com.micai.springboot.util.pay.wx.*;
import com.micai.springboot.vo.pay.WxpayVo;
import org.jdom.JDOMException;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.*;

/**
 * @Auther: zhaoxinguo
 * @Date: 2018/8/31 10:37
 * @Description: 微信支付后台接口
 */
@RestController
@RequestMapping(value = "/wxpay")
public class WxpayController extends PayBaseController {

    /**
     * 微信支付->扫码支付(模式二)->统一下单->微信二维码
     * @return
     */
    @GetMapping("/qrcode")
    public void wxpayPay(HttpServletResponse response) {
        String urlCode = null;
        // 获取订单信息
        WxpayVo vo = new WxpayVo();
        String out_trade_no = UUID.randomUUID().toString().replace("-", "");
        vo.setOut_trade_no(out_trade_no);
        // 账号信息
        vo.setApp_id(APPID);
        vo.setMch_id(MCHID);
        vo.setKey(KEY);
        String currTime = PayToolUtil.getCurrTime();
        vo.setCurrTime(currTime);
        String strTime = currTime.substring(8, currTime.length());
        vo.setStrTime(strTime);
        String strRandom = String.valueOf(PayToolUtil.buildRandom(4));
        vo.setStrRandom(strRandom);
        String nonce_str = strTime + strRandom;
        vo.setNonce_str(nonce_str);
        vo.setSpbill_create_ip(CREATE_IP);
        vo.setNotify_url(NOTIFY_URL);
        vo.setTrade_type("NATIVE");
        String total_fee = "1";
        vo.setTotal_fee(total_fee);//价格的单位为分

        SortedMap<Object,Object> packageParams = new TreeMap<Object,Object>();
        packageParams.put("appid", APPID);//公众账号ID
        packageParams.put("mch_id", MCHID);//商户号
        packageParams.put("nonce_str", nonce_str);//随机字符串
        packageParams.put("body", "资源");  //商品描述
        packageParams.put("out_trade_no", out_trade_no);//商户订单号
        packageParams.put("total_fee", total_fee); //标价金额 订单总金额,单位为分
        packageParams.put("spbill_create_ip", CREATE_IP);//终端IP APP和网页支付提交用户端ip,Native支付填调用微信支付API的机器IP
        packageParams.put("notify_url", NOTIFY_URL);//通知地址 异步接收微信支付结果通知的回调地址,通知url必须为外网可访问的url,不能携带参数
        packageParams.put("trade_type", "NATIVE");//交易类型 NATIVE 扫码支付
        // 签名
        String sign = PayToolUtil.createSign("UTF-8", packageParams, KEY);
        packageParams.put("sign", sign);

        // 将请求参数转换为xml格式的string
        String requestXML = PayToolUtil.getRequestXml(packageParams);
        logger.info("requestXML:{}", requestXML);

        // 调用微信支付统一下单接口
        String resXml = HttpUtil.postData(PayConfigUtil.UFDODER_URL, requestXML);
        logger.info("resXml: {}", resXml);

        // 解析微信支付结果
        Map map = null;
        try {
            map = XMLUtil4jdom.doXMLParse(resXml);
            logger.info("map: {}", map);
        } catch (JDOMException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        // 返回微信支付的二维码连接
        urlCode = (String) map.get("code_url");
        logger.info("urlCode:{}", urlCode);

        try {
            int width = 300;
            int height = 300;
            //二维码的图片格式
            String format = "gif";
            Hashtable hints = new Hashtable();
            //内容所使用编码
            hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
            BitMatrix bitMatrix;
            bitMatrix = new MultiFormatWriter().encode(urlCode, BarcodeFormat.QR_CODE, width, height, hints);
            QRUtil.writeToStream(bitMatrix, format, response.getOutputStream());
        } catch (WriterException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    /**
     * 微信支付-回调
     * @param request
     * @param response
     */
    @PostMapping("/notify")
    public String wxpayNotify(HttpServletRequest request, HttpServletResponse response) {
        //读取参数
        InputStream inputStream ;
        StringBuffer sb = null;
        try {
            sb = new StringBuffer();
            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();
        } catch (IOException e) {
            e.printStackTrace();
        }

        //解析xml成map
        Map<String, String> map = new HashMap<String, String>();
        try {
            map = XMLUtil4jdom.doXMLParse(sb.toString());
        } catch (JDOMException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        //过滤空 设置 TreeMap
        SortedMap<Object,Object> packageParams = new TreeMap<Object,Object>();
        Iterator it = map.keySet().iterator();
        while (it.hasNext()) {
            String parameter = (String) it.next();
            String parameterValue = map.get(parameter);
            String v = "";
            if(null != parameterValue) {
                v = parameterValue.trim();
            }
            packageParams.put(parameter, v);
        }

        //判断签名是否正确
        if(PayToolUtil.isTenpaySign("UTF-8", packageParams, KEY)) {
            //------------------------------
            //处理业务开始
            //------------------------------
            String resXml = "";
            if("SUCCESS".equals((String)packageParams.get("result_code"))){
                // 这里是支付成功
                //////////执行自己的业务逻辑////////////////
                String mch_id = (String)packageParams.get("mch_id");
                String openid = (String)packageParams.get("openid");
                String is_subscribe = (String)packageParams.get("is_subscribe");
                String out_trade_no = (String)packageParams.get("out_trade_no");

                String total_fee = (String)packageParams.get("total_fee");

                //////////执行自己的业务逻辑////////////////
                //暂时使用最简单的业务逻辑来处理:只是将业务处理结果保存到session中
                //(根据自己的实际业务逻辑来调整,很多时候,我们会操作业务表,将返回成功的状态保留下来)
                request.getSession().setAttribute("_PAY_RESULT", "OK");

                logger.info("支付成功");

                //通知微信.异步确认成功.必写.不然会一直通知后台.八次之后就认为交易失败了.
                resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>"
                        + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";

            } else {
                resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"
                        + "<return_msg><![CDATA[报文为空]]></return_msg>" + "</xml> ";
                return ("fail");
            }
            //------------------------------
            //处理业务完毕
            //------------------------------
            try {
                BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
                out.write(resXml.getBytes());
                out.flush();
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

        } else{
            logger.info("通知签名验证失败");
        }
        return ("success");
    }

}

五:访问支付URLhttp://dvnq2b.natappfree.cc/wxpay/qrcode、回返回微信的支付二维码,用户通过微信的扫一扫,就可以扫码支付了、这里注意:由于微信回调需要能外网访问的域名,所以我们这里使用了一个内网穿透工具natapp,具体怎么实现内网穿透,直接官网就可以了很简单,这是附上natapp的官网地址:https://natapp.cn/、配置好内容穿透工具后,修改下微信回调的配置文件,如下:

## 微信支付配置 测试环境(如果需要测试,改成自己的正式环境) ##
WXPAY.APPID=wx82b23234467t34347661
WXPAY.MCHID=1234dsdf5345
WXPAY.KEY=rtert345dfgh345fg34ddfg345fdg
WXPAY.APPSECRET=36546dfghdfgdszdfsdffg45354dfg
WXPAY.NOTIFY_URL= http://dvnq2b.natappfree.cc/wxpay/notify
WXPAY.CREATE_IP=192.168.0.190

访问支付url返回微信二维码,如下图:

 

使用微信的扫一扫,扫码支付,如下图:

微信支付回调,如下图:

这里对于回调只是简单输出了日志,你可以根据自己的实际情况选择做相应的处理,一般都是对订单的支付状态做更新。

六:总结: 

After the above process so, I believe we all understand the process of micro-channel pay, where we make a summary of the above processes, in order to access micro-channel pay, the job must be business users, individual users do not support, so writing code at the beginning before going to the relevant person responsible for applying good micro-channel pay-related configuration parameters, with these in order to carry out the following work, the most important thing is micro-channel pay pullback here, callback, you must configure can external network access in a production environment the URL, domain name must be filed at the same time ever, two domain names can be here for the convenience of our test, so we used the internal network penetration tools natapp, the tool has both free channels also have toll lanes, toll lanes is also very cheap, If the only test, free passage enough, one more thing to note is that the callback micro-channel payment, default micro-channel callback several times, so there will be repeated callback question, here we leave you with a thought, how to prevent micro-channel multiple callbacks, so as not to affect the business, that are interested in small partner can leave pay . These are the micro-channel pay (pay scan code mode two) of all content, and want to have complete source code of a small partner, you can add exchange group, group number: 715 224 124.

Guess you like

Origin www.cnblogs.com/sxdtzhaoxinguo/p/11325197.html