java接入微信支付的Native方式

前言:

微信有很多种方式,本文章只是讲解如何对接微信支付的Native方式 官方Native方式文档:https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_1

1.依赖引入

代码语言:javascript
复制
 <dependency>
            <groupId>com.github.wxpay</groupId>
            <artifactId>wxpay-sdk</artifactId>
            <version>0.0.3</version>
        </dependency>
        <!--httpclient支持-->
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
        </dependency>
    </dependencies>

2.工具类

HttpClient,该工具类是发起网络请求的

代码语言:javascript
复制
package com.zb.util;

import org.apache.http.Consts;
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.ParseException;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.*;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.util.EntityUtils;

import javax.net.ssl.SSLContext;
import java.io.IOException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

public class HttpClient {
private String url;
private Map<String, String> param;
private int statusCode;
private String content;
private String xmlParam;
private boolean isHttps;

public boolean isHttps() {
    return isHttps;
}

public void setHttps(boolean isHttps) {
    this.isHttps = isHttps;
}

public String getXmlParam() {
    return xmlParam;
}

public void setXmlParam(String xmlParam) {
    this.xmlParam = xmlParam;
}

public HttpClient(String url, Map&lt;String, String&gt; param) {
    this.url = url;
    this.param = param;
}

public HttpClient(String url) {
    this.url = url;
}

public void setParameter(Map&lt;String, String&gt; map) {
    param = map;
}

public void addParameter(String key, String value) {
    if (param == null)
        param = new HashMap&lt;String, String&gt;();
    param.put(key, value);
}

public void post() throws ClientProtocolException, IOException {
    HttpPost http = new HttpPost(url);
    setEntity(http);
    execute(http);
}

public void put() throws ClientProtocolException, IOException {
    HttpPut http = new HttpPut(url);
    setEntity(http);
    execute(http);
}

public void get() throws ClientProtocolException, IOException {
    if (param != null) {
        StringBuilder url = new StringBuilder(this.url);
        boolean isFirst = true;
        for (String key : param.keySet()) {
            if (isFirst) {
                url.append(&#34;?&#34;);
            }else {
                url.append(&#34;&amp;&#34;);
            }
            url.append(key).append(&#34;=&#34;).append(param.get(key));
        }
        this.url = url.toString();
    }
    HttpGet http = new HttpGet(url);
    execute(http);
}

/**
 * set http post,put param
 */
private void setEntity(HttpEntityEnclosingRequestBase http) {
    if (param != null) {
        List&lt;NameValuePair&gt; nvps = new LinkedList&lt;NameValuePair&gt;();
        for (String key : param.keySet()) {
            nvps.add(new BasicNameValuePair(key, param.get(key))); // 参数
        }
        http.setEntity(new UrlEncodedFormEntity(nvps, Consts.UTF_8)); // 设置参数
    }
    if (xmlParam != null) {
        http.setEntity(new StringEntity(xmlParam, Consts.UTF_8));
    }
}

private void execute(HttpUriRequest http) throws ClientProtocolException,
        IOException {
    CloseableHttpClient httpClient = null;
    try {
        if (isHttps) {
            SSLContext sslContext = new SSLContextBuilder()
                    .loadTrustMaterial(null, new TrustStrategy() {
                        // 信任所有
                        @Override
                        public boolean isTrusted(X509Certificate[] chain,
                                                 String authType)
                                throws CertificateException {
                            return true;
                        }
                    }).build();
            SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
                    sslContext);
            httpClient = HttpClients.custom().setSSLSocketFactory(sslsf)
                    .build();
        } else {
            httpClient = HttpClients.createDefault();
        }
        CloseableHttpResponse response = httpClient.execute(http);
        try {
            if (response != null) {
                if (response.getStatusLine() != null) {
                    statusCode = response.getStatusLine().getStatusCode();
                }
                HttpEntity entity = response.getEntity();
                // 响应内容
                content = EntityUtils.toString(entity, Consts.UTF_8);
            }
        } finally {
            response.close();
        }
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        httpClient.close();
    }
}

public int getStatusCode() {
    return statusCode;
}

public String getContent() throws ParseException, IOException {
    return content;
}

}

3.Yml文件配置

放入:

公众账号ID

商户号

商户密钥

回调地址

appid

partner

partnerkey

notifyurl

image-1679390160129

4.主要代码

注意看注释

代码语言:javascript
复制
package com.zb.controller;

import com.github.wxpay.sdk.WXPayUtil;
import com.zb.util.HttpClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;
import java.util.Map;

@RestController
public class WxController {

@Value(&#34;${weixin.appid}&#34;)
private String appid;
@Value(&#34;${weixin.partner}&#34;)
private String partner;
@Value(&#34;${weixin.partnerkey}&#34;)
private String partnerkey;
@Value(&#34;${weixin.notifyurl}&#34;)
private String notifyurl;

@RequestMapping(&#34;/wxpay&#34;)
public Map&lt;String,String&gt; map(String qian) throws Exception{

    //必要参数封装
    Map&lt;String,String&gt; map = new HashMap&lt;&gt;();
    //公众账号ID
    map.put(&#34;appid&#34;,appid);
    //商户号
    map.put(&#34;mch_id&#34;,partner);
    //随机字符串
    // 官方规定要32位内,这里使用官方工具直接生成
    map.put(&#34;nonce_str&#34;, WXPayUtil.generateNonceStr());
    //商品名字
    map.put(&#34;body&#34;,&#34;测试商品&#34;);
    //商户订单号,32位内,最低6位,不可重复
    map.put(&#34;out_trade_no&#34;,&#34;123333133&#34;);
    //金额,默认1=0.01
    map.put(&#34;total_fee&#34;,qian);
    //终端IP,就发起请求的服务器ip
    map.put(&#34;spbill_create_ip&#34;,&#34;127.0.0.1&#34;);
    //回调地址
    //支付完成后,跳转到那
    //可以写个方法,返回到指定方法
    //必须外网,像127.0.0.1/xx/xx会无法跳转
    map.put(&#34;notify_url&#34;,notifyurl);
    //接口方式
    map.put(&#34;trade_type&#34;,&#34;NATIVE&#34;);
    //微信Api
    String url =  &#34;https://api.mch.weixin.qq.com/pay/unifiedorder&#34;;

    //签名生成
    //根据参数和商户密钥
    String sign = WXPayUtil.generateSignedXml(map, partnerkey);


    //使用工具,发起请求
    //url是微信Api
    HttpClient http = new HttpClient(url);
    //APi是否https?是
    http.setHttps(true);
    //微信官方,要求发送必须xml方式发送,所以这个就是将Map转成xml
    http.setXmlParam(sign);
    //http.post这个要保持最后写,不然无法发起参数
    http.post();

    //获取支付完的返回值,官方返回xml类型
    String content = http.getContent();

    //将xml类型的值转换map
    return WXPayUtil.xmlToMap(content);
}

}

5.回调代码

具体介绍可以前往官方:https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_7&index=8

代码语言:javascript
复制
    @RequestMapping("/notifyurl")
public String notifyurl(HttpServletRequest request) throws Exception{
//支付完微信官方会给你回调地址发一些信息
//使用流来获取HttpServletRequest的请求
InputStream is = request.getInputStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buff = new byte[1024];
int len = 0;
while ((len = is.read(buff)) != -1) {
baos.write(buff, 0, len);
}
baos.close();
is.close();
String data = new String(baos.toByteArray(), "UTF-8");

    //微信要求发回调发送下面2个参
    //如果不发送
    //微信总共会发起多次通知,通知频率为15s/15s/30s/3m/10m/20m/30m/30m/30m/60m/3h/3h/3h/6h/6h - 总计 24h4m)
    Map&lt;String, String&gt; result = new HashMap&lt;&gt;();
    result.put(&#34;return_code&#34;, &#34;SUCCESS&#34;);
    result.put(&#34;return_msg&#34;, &#34;OK&#34;);

    //到这里就可以写一些数据库更新方法
    //
    //
    //比如更新某给支付成功数据库字段什么的

    return WXPayUtil.mapToXml(result);
}</code></pre></div></div><h2 id="9iju2" name="6.%E6%9F%A5%E8%AF%A2%E8%AE%A2%E5%8D%95">6.查询订单</h2><p>到第5部,就可以说已经结束了,此处第6部分是附加功能,可有可无</p><p>传入订单号即可

官方文档:https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_2

代码语言:javascript
复制
 public Map<String, String> cardcha(String hao) throws Exception {
Map<String, String> param = new HashMap<>();
String api = "https://api.mch.weixin.qq.com/pay/orderquery";
param.put("appid", appid);
param.put("mch_id", partner);
param.put("out_trade_no",hao);
param.put("nonce_str", WXPayUtil.generateNonceStr());
String xml = WXPayUtil.generateSignedXml(param, partnerkey);
HttpClient http = new HttpClient(api);
http.setXmlParam(xml);
http.setHttps(true);
http.post();
String content = http.getContent();
return WXPayUtil.xmlToMap(content);
}

7. 前端

接口返回主要拿这个:code_url
他是微信内部链接地址,如web的http://xxx.xx/xxx
拿到它后,可以使用jquery.qrcode前端组件,直接生成出二维码

image-1679407420498