JAVA WeChat Pay

1. Breve descripción

  Ahora muchos proyectos necesitan usar la interfaz de pago de WeChat, el documento oficial también es una descripción simple, es realmente difícil de entender si la tecnología no es avanzada (no puedo entenderlo cuando leo el documento oficial), todavía necesito para recopilarlo y resumirlo yo mismo.

Visto mucho en línea

Algunos de ellos todavía no pudieron tener éxito al final. A continuación, compartiré mi pago de WeChat. La cuenta oficial de WeChat o el mini programa de este WeChat Pay requiere la autenticación de WeChat; de lo contrario, no puede solicitar WeChat Pay, por lo que no hablaré de ello.

Ahora que hemos llegado a este punto, creo que todas las premisas están listas. Ir directamente al código ~

2. Pago de Java WeChat

Uno, primero comprenda el significado de cada parámetro

  https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=9_1 Esta es la parte de interceptación de la descripción oficial de WeChat. Consulte este enlace para obtener más detalles.

2. Código de pago de WeChat

Primero cree la capa de servicio e inserte el siguiente código:

public interface WeiXinPayService {
 
    Object WeiXinPay(String outTradeNo,String openid, String body, int total_fee) throws UnsupportedEncodingException;
}
@Service
public class WeiXinPayServiceImpl implements WeiXinPayService {
    private static final Logger LOGGER = LoggerFactory
            .getLogger(WeiXinPayServiceImpl.class);
 
    @Override
    public Object WeiXinPay(String outTradeNo,String openid, String body, int total_fee) {
 
        String appid = "" // 公众号--》“开发者ID”  微信小程序,或者公众号的APPID
        String mch_id = "" // 商户号,将该值赋值给partner
        String key =  "" // 微信支付商户平台登录)--》“API安全”--》“API密钥”--“设置密钥”(设置之后的那个值就是partnerkey,32位)
        LOGGER.debug(appid);
        LOGGER.debug(mch_id);
       LOGGER.debug(key);
//        String body = body; // 描述           int total_fee = total_fee; // 支付金额
        String notify_url = ""; // 回调链接
//        String out_trade_no = IdUtils.genOrderName();//生成订单号
//        LOGGER.debug("outTradeNo---:"+out_trade_no);
        LOGGER.debug("openid是----"+openid);
        LOGGER.debug("appid---"+appid);
        LOGGER.debug("mch_id---"+mch_id);
        Map<Object, Object> map = null;
        try {
            map = WeiXinAtcion.me.weixinPlay(mch_id, appid,
                    key, openid, total_fee, outTradeNo, notify_url, body);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (DocumentException e) {
            e.printStackTrace();
        }
        return map;
    }
}

Luego crea una capa de controlador de control:

/**
 * <p>Title: WeiXinPayController</p>  
  * <p>Description: 微信支付</p>  
  * @author SpringRoot  
  * @date 2020/3/4-11:17
 */
@Controller
public class WeiXinPayController {
    private static final Logger LOGGER = LoggerFactory
            .getLogger(WeiXinPayController.class);
 
    @Autowired
    private WeiXinPayService weiXinPayService;
 
    /**
     * 支付接口
     *
     * @param openid
     * @param body      说明
     * @param total_fee 总价
     * @return
     */
    @RequestMapping("/WeiXinPay")
    public @ResponseBody
    Object WeiXinPay(String outTradeNo,String openid, String body, int total_fee) {
        LOGGER.debug("outTradeNo-------------"+outTradeNo);
        LOGGER.debug("openid-------------"+openid);
        LOGGER.debug("body-------------"+body);
        LOGGER.debug("total_fee-------------"+total_fee);
        try {
            return weiXinPayService.WeiXinPay(outTradeNo,openid, body, total_fee);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return TTResult.fail();
    }
}

Herramientas utilizadas en el código anterior: Primero que nada WeiXinAtcion

/**
 * <p>Title: WeiXinAtcion</p>  
  * <p>Description: </p>  
  * @author SpringRoot
  * @date 2020/3/4-11:17
 */
@Component
public class WeiXinAtcion {
    //密钥
    public static final WeiXinAtcion me = new WeiXinAtcion();
    private static final Logger LOGGER = LoggerFactory
            .getLogger(WeiXinAtcion.class);
 
    /**
     * 生成微信订单
     *
     * @param mch_id
     * @param appid
     * @param key
     * @param openid
     * @param total_fee
     * @param out_trade_no
     * @param notify_url
     * @param body
     * @return
     * @throws UnsupportedEncodingException
     * @throws DocumentException
     */
    public SortedMap<Object, Object> weixinPlay(String mch_id, String appid, String key, String openid, int total_fee, String out_trade_no, String notify_url, String body) throws UnsupportedEncodingException, DocumentException, DocumentException {
 
        SortedMap<Object, Object> paymentPo = new TreeMap<Object, Object>();
        paymentPo.put("appid", appid);
        paymentPo.put("mch_id", mch_id);
        paymentPo.put("nonce_str", WXUtil.generate());
        paymentPo.put("body", body);
        paymentPo.put("out_trade_no", out_trade_no);
        paymentPo.put("total_fee", String.valueOf(total_fee));
        paymentPo.put("spbill_create_ip","服务器的ip地址");//此处是公网ip
        paymentPo.put("notify_url", notify_url);
        paymentPo.put("trade_type", "JSAPI");
        paymentPo.put("openid", openid);
        String sign = WXUtil.createSign_ChooseWXPay("UTF-8", paymentPo, key);
        paymentPo.put("sign", sign);
        String param = WXUtil.getRequestXml(paymentPo);
        //将参数通过post请求传给微信端
        String request = WXUtil.httpRequest("https://api.mch.weixin.qq.com/pay/unifiedorder", "POST", param);
        Map<String, String> map = new HashMap<String, String>();         // 将解析结果存储在HashMap中
        InputStream in = new ByteArrayInputStream(request.getBytes());
        SAXReader reader = new SAXReader();                              // 读取输入流
        Document document = reader.read(in);
 
        Element root = document.getRootElement();                        // 得到xml根元素
        @SuppressWarnings("unchecked")                                   // 得到根元素的所有子节点
                List<Element> elementList = root.elements();
        for (Element element : elementList) {
            map.put(element.getName(), element.getText());
        }
        SortedMap<Object, Object> result = new TreeMap<Object, Object>();
 
        LOGGER.debug("第一次签名返回码" + map.get("return_code"));
        LOGGER.debug("第一次签名返回结果" + map.get("return_msg"));
        //第一次签名成功
        if (map.get("return_code").equals("SUCCESS")) {                  // 业务结果
            String nonceStr = WXUtil.generate();
            Long timeStamp = System.currentTimeMillis() / 1000;
            SortedMap<Object, Object> params = new TreeMap<Object, Object>();
 
            params.put("appId", appid);
            params.put("nonceStr", nonceStr);
            params.put("package", "prepay_id=" + map.get("prepay_id"));
            params.put("signType", "MD5");
            params.put("timeStamp", timeStamp);
 
            //第二次签名成功
            LOGGER.debug("开始第二次签名");
            String paySign = WXUtil.createSign_ChooseWXPay("UTF-8", params, key);
            result.put("paySign", paySign);
            result.put("timeStamp", timeStamp + "");
            result.put("nonceStr", nonceStr);
            result.put("out_trade_no", paymentPo.get("out_trade_no"));
            result.put("package", "prepay_id=" + map.get("prepay_id"));
            result.put("return_code", "SUCCESS");
        } else {
            result.put("return_code", "Fail");
            result.put("return_msg", map.get("return_msg"));
 
        }
        return result;
    }
}

Herramientas: WXUtil

public class WXUtil {
     
    private static final Logger LOGGER = LoggerFactory.getLogger(WXUtil.class);
    /**
     * 随机字符串
     * @return
     */
    public static String generate() {
        return UUID.randomUUID().toString().trim().replaceAll("-", "");
    }
     
    /**
     * 解析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 m = new HashMap();
        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 = WXUtil.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(WXUtil.getChildrenText(list));
                }
                sb.append(value);
                sb.append("</" + name + ">");
            }
        }
        return sb.toString();
    }
    /**
     * 将请求参数转换为xml格式的string字符串,微信服务器接收的是xml格式的字符串
     * @param parameters
     * @return
     */
    public static String getRequestXml(SortedMap<Object, Object> parameters) {
        StringBuffer sb = new StringBuffer();
        sb.append("<xml>");
        Set<Entry<Object, Object>> es = parameters.entrySet();
        Iterator<Entry<Object, Object>> it = es.iterator();
        while (it.hasNext()) {
            Entry<Object, Object> entry = (Entry<Object, Object>) 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();
    }
 
 
     
    /**
     * sign签名,必须使用MD5签名,且编码为UTF-8
     * @param characterEncoding
     * @param parameters
     * @return
     */
    public static String createSign_ChooseWXPay(String characterEncoding, SortedMap<Object, Object> parameters, String key) {
        StringBuffer sb = new StringBuffer();
        Set<Entry<Object, Object>> es = parameters.entrySet();
        Iterator<Entry<Object, Object>> it = es.iterator();
        while (it.hasNext()) {
            Entry<Object, Object> entry = (Entry<Object, Object>) it.next();
            String k = (String) entry.getKey();
            Object v = entry.getValue();
            if (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) {
                sb.append(k + "=" + v + "&");
            }
        }
        /** 支付密钥必须参与加密,放在字符串最后面 */
        sb.append("key=" + key);
        String sign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase();
        return sign;
    }
 
 
 
 
 
    public static String httpRequest(String requestUrl,String requestMethod,String outputStr){
        // 创建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();
 
        //往服务器端写内容
        if(null !=outputStr){
            OutputStream os=conn.getOutputStream();
            os.write(outputStr.getBytes("utf-8"));
            os.close();
        }
        // 读取服务器端返回的内容
        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);
         }
        }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;
    }
         
    /**
     * 退款和企业付款到银行卡
     */
   public static Map<String, String> doRefund(HttpServletRequest request,String url,String data,String partner,String apiclient_certLocation) throws Exception {
    // p12证书的位置
            // 微信公众平台:“微信支付”--》“商户信息”--》“交易数据”--》“详情请登录微信支付商户平台查看”(登录)--》“API安全”--》“API证书”--》“下载证书”
            // 下载证书后将apiclient_cert.p12放在src目录下面(出于安全考虑,请自行下载自己的证书)
            KeyStore keyStore = KeyStore.getInstance("PKCS12");
            String url2 = request.getSession().getServletContext().getRealPath("/")
                    + "cert/" + apiclient_certLocation;
            LOGGER.debug("url2--->"+url2);
            File file=new File(url2);
            FileInputStream instream = new FileInputStream(file);// P12文件目录
        try {
            keyStore.load(instream, partner.toCharArray());
        } finally {
            instream.close();
        }
        SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, partner.toCharArray()).build();
        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext,new String[] { "TLSv1" }, null,SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
        CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
        try {
            HttpPost httpost = new HttpPost(url); // 设置响应头信息
            httpost.addHeader("Connection", "keep-alive");
            httpost.addHeader("Accept", "*/*");
            httpost.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
//          httpost.addHeader("Host", "api.mch.weixin.qq.com");
            httpost.addHeader("X-Requested-With", "XMLHttpRequest");
            httpost.addHeader("Cache-Control", "max-age=0");
            httpost.addHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0) ");
            httpost.setEntity(new StringEntity(data, "UTF-8"));
            CloseableHttpResponse response = httpclient.execute(httpost);
            try {
                HttpEntity entity = response.getEntity();
                String jsonStr = EntityUtils.toString(response.getEntity(), "UTF-8");
                EntityUtils.consume(entity);
                return WXUtil.doXMLParse(jsonStr);
            } finally {
                response.close();
            }
        } finally {
            httpclient.close();
        }
    }
 
 
    /**
     * 得到公钥
     * @param request
     * @param url 请求微信端的链接
     * @param data//请求的数据
     * @param partner
     * @param apiclient_certLocation
     * @return
     * @throws Exception
     */
    public static Object getPublicKey(HttpServletRequest request,String url,String data,String partner,String apiclient_certLocation) throws Exception {
        // p12证书的位置
        // 微信公众平台:“微信支付”--》“商户信息”--》“交易数据”--》“详情请登录微信支付商户平台查看”(登录)--》“API安全”--》“API证书”--》“下载证书”
        // 下载证书后将apiclient_cert.p12放在src目录下面(出于安全考虑,请自行下载自己的证书)
        KeyStore keyStore = KeyStore.getInstance("PKCS12");
        String url2 = request.getSession().getServletContext().getRealPath("/")
                + "cert/" + apiclient_certLocation;
        LOGGER.debug("url2--->"+url2);
        File file=new File(url2);
        FileInputStream instream = new FileInputStream(file);// P12文件目录
        try {
            keyStore.load(instream, partner.toCharArray());
        } finally {
            instream.close();
        }
        SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, partner.toCharArray()).build();
        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext,new String[] { "TLSv1" }, null,SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
        CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
        try {
            HttpPost httpost = new HttpPost(url); // 设置响应头信息
            httpost.addHeader("Connection", "keep-alive");
            httpost.addHeader("Accept", "*/*");
            httpost.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
            httpost.addHeader("X-Requested-With", "XMLHttpRequest");
            httpost.addHeader("Cache-Control", "max-age=0");
            httpost.addHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0) ");
            httpost.setEntity(new StringEntity(data, "UTF-8"));
            CloseableHttpResponse response = httpclient.execute(httpost);
            try {
                HttpEntity entity = response.getEntity();
                String jsonStr = EntityUtils.toString(response.getEntity(), "UTF-8");
                EntityUtils.consume(entity);
                return jsonStr;
            } finally {
                response.close();
            }
        } finally {
            httpclient.close();
        }
    }
 
    /** 
     * 是否签名正确,规则是:按参数名称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()) {  
            Entry entry = (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();  
           
        return tenpaySign.equals(mysign);
    }
 
    public static Map<String, String> doRefund(String string, String xML, String mCH_ID, String cERT) {
        // TODO Auto-generated method stub
        return null;
    }
}

Clase de herramienta que devuelve resultados: TTResult

public class CommonResult {
    // 定义jackson对象
    private static final ObjectMapper MAPPER = new ObjectMapper();
 
    // 响应业务状态
    private Integer status;  // 200 代表成功, 500 代表失败
 
    // 响应消息
    private String msg;
 
    // 响应中的数据
    private Object data;
 
    public static CommonResult build(Integer status, String msg, Object data) {
        return new TTResult(status, msg, data);
    }
 
    public static CommonResult ok(Object data) {
        return new TTResult(data);
    }
     
     
    public static CommonResult ok() {
        return new TTResult(null);
    }
 
    public static CommonResult fail(){
        return new TTResult(500,"fail",null);
    }
     
    public static CommonResult fail(Object data){
        return new TTResult(500,"fail",data);
    }
     
    public TTResult() {
 
    }
 
    public static CommonResult build(Integer status, String msg) {
        return new TTResult(status, msg, null);
    }
     
    public TTResult(Integer status, String msg, Object data) {
        this.status = status;
        this.msg = msg;
        this.data = data;
    }
 
    public TTResult(Object data) {
        this.status = 200;
        this.msg = "OK";
        this.data = data;
    }
     
 
    // public Boolean isOK() {
    // return this.status == 200;
    // }
 
    public Integer getStatus() {
        return status;
    }
 
    public void setStatus(Integer status) {
        this.status = status;
    }
 
    public String getMsg() {
        return msg;
    }
 
    public void setMsg(String msg) {
        this.msg = msg;
    }
 
    public Object getData() {
        return data;
    }
 
    public void setData(Object data) {
        this.data = data;
    }
 
    /**
     * 将json结果集转化为TTResult对象
     * 
     * @param jsonData
     *            json数据
     * @param clazz
     *            TTResult中的object类型
     * @return
     */
    public static CommonResult formatToPojo(String jsonData, Class<?> clazz) {
        try {
            if (clazz == null) {
                return MAPPER.readValue(jsonData, TTResult.class);
            }
            JsonNode jsonNode = MAPPER.readTree(jsonData);
            JsonNode data = jsonNode.get("data");
            Object obj = null;
            if (clazz != null) {
                if (data.isObject()) {
                    obj = MAPPER.readValue(data.traverse(), clazz);
                } else if (data.isTextual() || data.isNumber()) {
                    obj = MAPPER.readValue(data.asText(), clazz);
                }
            }
            return build(jsonNode.get("status").intValue(), jsonNode.get("msg")
                    .asText(), obj);
        } catch (Exception e) {
            return null;
        }
    }
 
    /**
     * 没有object对象的转化
     * 
     * @param json
     * @return
     */
    public static CommonResult format(String json) {
        try {
            return MAPPER.readValue(json, TTResult.class);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
 
    /**
     * Object是集合转化
     * 
     * @param jsonData
     *            json数据
     * @param clazz
     *            集合中的类型
     * @return
     */
    public static CommonResult formatToList(String jsonData, Class<?> clazz) {
        try {
            JsonNode jsonNode = MAPPER.readTree(jsonData);
            JsonNode data = jsonNode.get("data");
            Object obj = null;
            if (data.isArray() && data.size() > 0) {
                obj = MAPPER.readValue(data.traverse(), MAPPER.getTypeFactory()
                        .constructCollectionType(List.class, clazz));
            }
            return build(jsonNode.get("status").intValue(), jsonNode.get("msg")
                    .asText(), obj);
        } catch (Exception e) {
            return null;
        }
    }
}

Para los parámetros involucrados en el código anterior, vaya a la documentación oficial de WeChat para ver las instrucciones. Cada parámetro tiene una explicación. Según yo, WeChat Pay definitivamente funcionará.

Supongo que te gusta

Origin blog.csdn.net/baidu_39322753/article/details/104672848
Recomendado
Clasificación