WeChat applet: java realizes the method of obtaining mobile phone number

Table of contents

1. Now the simpler way

-> interface name

---> Function description

-> call method

---> HTTPS calls

---> Third-party calls

---> Request parameters

---> return parameter

2. Implementation method

1. Add fastjson dependency 

2. http request class

3. Json string tool class

4. Interface method

3. Another introduction to access_token


1. Now the simpler way

WeChat official document introduction: 

-> interface name

getPhoneNumber

---> Function description

This interface needs to be used together with the ability to quickly fill in the mobile phone number component . After the user clicks and agrees, the  bindgetphonenumber dynamic token can be obtained through the event callback code, and then the interface will be called codeto exchange for the user's mobile phone number.

Note: Each code can only be used once, and the code is valid for 5 minutes.

-> call method

---> HTTPS calls


POST https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=ACCESS_TOKEN 

---> Third-party calls

  • The calling method and entry and exit parameters are the same as HTTPS, only the calling token is different

  • The permission set id to which this interface belongs is: 18

  • After the service provider is authorized by one of the permission sets, it can use the authorizer_access_token to make calls on behalf of the merchant

---> Request parameters

Attributes type required illustrate
access_token string yes Interface call credentials, this parameter is a URL parameter, not a Body parameter. Use access_token or authorizer_access_token
code string yes Mobile phone number to obtain the certificate

---> return parameter

Attributes type illustrate
errcode number error code
errmsg string error message
phone_info object User phone number information
Attributes type illustrate
phoneNumber string The mobile phone number bound by the user (overseas mobile phone numbers will have area codes)
purePhoneNumber string Cell phone number without area code
countryCode string area code
watermark object data watermark
Attributes type illustrate
timestamp number Timestamp of the user's mobile phone number acquisition operation
appid string applet appid

2. Implementation method

1. Add fastjson dependency 

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.75</version>
        </dependency>

2. http request class

import lombok.extern.slf4j.Slf4j;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.slf4j.MDC;
import org.springframework.http.HttpStatus;
import org.springframework.util.CollectionUtils;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * HTTP/HTTPS 请求封装: GET / POST
 * 默认失败重试3次
 * @author admin
 */
@Slf4j
public class HttpClientSslUtils {

	/**
	 * 默认的字符编码格式
	 */
	private static final String DEFAULT_CHAR_SET = "UTF-8";
	/**
	 * 默认连接超时时间 (毫秒)
	 */
	private static final Integer DEFAULT_CONNECTION_TIME_OUT = 2000;
	/**
	 * 默认socket超时时间 (毫秒)
	 */
	private static final Integer DEFAULT_SOCKET_TIME_OUT = 3000;

	/** socketTimeOut上限 */
	private static final Integer SOCKET_TIME_OUT_UPPER_LIMIT = 10000;

	/** socketTimeOut下限 */
	private static final Integer SOCKET_TIME_OUT_LOWER_LIMIT = 1000;

	private static CloseableHttpClient getHttpClient() {
		RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(DEFAULT_SOCKET_TIME_OUT)
			.setConnectTimeout(DEFAULT_CONNECTION_TIME_OUT).build();
		return HttpClients.custom().setDefaultRequestConfig(requestConfig)
			.setRetryHandler(new DefaultHttpRequestRetryHandler()).build();
	}

	private static CloseableHttpClient getHttpClient(Integer socketTimeOut) {
		RequestConfig requestConfig =
			RequestConfig.custom().setSocketTimeout(socketTimeOut).setConnectTimeout(DEFAULT_CONNECTION_TIME_OUT)
				.build();
		return HttpClients.custom().setDefaultRequestConfig(requestConfig)
			.setRetryHandler(new DefaultHttpRequestRetryHandler()).build();
	}

	public static String doPost(String url, String requestBody) throws Exception {
		return doPost(url, requestBody, ContentType.APPLICATION_JSON);
	}

	public static String doPost(String url, String requestBody, Integer socketTimeOut) throws Exception {
		return doPost(url, requestBody, ContentType.APPLICATION_JSON, null, socketTimeOut);
	}

	public static String doPost(String url, String requestBody, ContentType contentType) throws Exception {
		return doPost(url, requestBody, contentType, null);
	}

	public static String doPost(String url, String requestBody, List<BasicHeader> headers) throws Exception {
		return doPost(url, requestBody, ContentType.APPLICATION_JSON, headers);
	}

	public static String doPost(String url, String requestBody, ContentType contentType, List<BasicHeader> headers)
		throws Exception {
		return doPost(url, requestBody, contentType, headers, getHttpClient());
	}

	public static String doPost(String url, String requestBody, ContentType contentType, List<BasicHeader> headers,
                                Integer socketTimeOut) throws Exception {
		if (socketTimeOut < SOCKET_TIME_OUT_LOWER_LIMIT || socketTimeOut > SOCKET_TIME_OUT_UPPER_LIMIT) {
			log.error("socketTimeOut非法");
			throw new Exception();
		}
		return doPost(url, requestBody, contentType, headers, getHttpClient(socketTimeOut));
	}


	/**
	 * 通用Post远程服务请求
	 * @param url
	 * 	请求url地址
	 * @param requestBody
	 * 	请求体body
	 * @param contentType
	 * 	内容类型
	 * @param headers
	 * 	请求头
	 * @return String 业务自行解析
	 * @throws Exception
	 */
	public static String doPost(String url, String requestBody, ContentType contentType, List<BasicHeader> headers,
                                CloseableHttpClient client) throws Exception {

		// 构造http方法,设置请求和传输超时时间,重试3次
		CloseableHttpResponse response = null;
		long startTime = System.currentTimeMillis();
		try {
			HttpPost post = new HttpPost(url);
			if (!CollectionUtils.isEmpty(headers)) {
				for (BasicHeader header : headers) {
					post.setHeader(header);
				}
			}
			StringEntity entity =
				new StringEntity(requestBody, ContentType.create(contentType.getMimeType(), DEFAULT_CHAR_SET));
			post.setEntity(entity);
			response = client.execute(post);
			if (response.getStatusLine().getStatusCode() != HttpStatus.OK.value()) {
				log.error("业务请求返回失败:{}", EntityUtils.toString(response.getEntity()));
				throw new Exception();
			}
			String result = EntityUtils.toString(response.getEntity());
			return result;
		} finally {
			releaseResourceAndLog(url, requestBody, response, startTime);
		}
	}

	/**
	 * 暂时用于智慧园区业务联调方式
	 * @param url 业务请求url
	 * @param param 业务参数
	 * @return
	 * @throws Exception
	 */
    public static String doPostWithUrlEncoded(String url,
                                              Map<String, String> param) throws Exception {
        // 创建Httpclient对象
        CloseableHttpClient httpClient = getHttpClient();
        CloseableHttpResponse response = null;
        long startTime = System.currentTimeMillis();
        try {
            // 创建Http Post请求
            HttpPost httpPost = new HttpPost(url);
            // 创建参数列表
            if (param != null) {
                List<org.apache.http.NameValuePair> paramList = new ArrayList<>();
                for (String key : param.keySet()) {
                    paramList.add(new BasicNameValuePair(key, param.get(key)));
                }
                // 模拟表单
                UrlEncodedFormEntity entity = new UrlEncodedFormEntity(paramList, DEFAULT_CHAR_SET);
                httpPost.setEntity(entity);
            }
            // 执行http请求
            response = httpClient.execute(httpPost);
            if (response.getStatusLine().getStatusCode() != HttpStatus.OK.value()) {
				log.error("业务请求返回失败:{}" , EntityUtils.toString(response.getEntity()));
				throw new Exception();
            }
            String resultString = EntityUtils.toString(response.getEntity(), DEFAULT_CHAR_SET);
            return resultString;
        } finally {
            releaseResourceAndLog(url, param == null ? null : param.toString(), response, startTime);
        }
    }

	private static void releaseResourceAndLog(String url, String request, CloseableHttpResponse response, long startTime) {
		if (null != response) {
			try {
				response.close();
				recordInterfaceLog(startTime, url, request);
			} catch (IOException e) {
				log.error(e.getMessage());
			}
		}
	}

	public static String doGet(String url) throws Exception {
		return doGet(url, ContentType.DEFAULT_TEXT);
	}

	public static String doGet(String url, ContentType contentType) throws Exception {
		return doGet(url, contentType, null);
	}

	public static String doGet(String url, List<BasicHeader> headers) throws Exception {
		return doGet(url, ContentType.DEFAULT_TEXT, headers);
	}

	/**
	 * 通用Get远程服务请求
	 * @param url
	 * 	请求参数
	 * @param contentType
	 * 	请求参数类型
	 * @param headers
	 * 	请求头可以填充
	 * @return String 业务自行解析数据
	 * @throws Exception
	 */
	public static String doGet(String url, ContentType contentType, List<BasicHeader> headers) throws Exception {
		CloseableHttpResponse response = null;
		long startTime = System.currentTimeMillis();
		try {
			CloseableHttpClient client = getHttpClient();
			HttpGet httpGet = new HttpGet(url);
			if (!CollectionUtils.isEmpty(headers)) {
				for (BasicHeader header : headers) {
					httpGet.setHeader(header);
				}
			}
			if(contentType != null){
				httpGet.setHeader("Content-Type", contentType.getMimeType());
			}
			response = client.execute(httpGet);
			if (response.getStatusLine().getStatusCode() != HttpStatus.OK.value()) {
				log.error("业务请求返回失败:{}", EntityUtils.toString(response.getEntity()));
				throw new Exception();
			}
			String result = EntityUtils.toString(response.getEntity());
			return result;
		} finally {
			releaseResourceAndLog(url, null, response, startTime);
		}
	}

	private static void recordInterfaceLog(long startTime, String url, String request) {
		long endTime = System.currentTimeMillis();
		long timeCost = endTime - startTime;
		MDC.put("totalTime", String.valueOf(timeCost));
		MDC.put("url", url);
		MDC.put("logType", "third-platform-service");
		log.info("HttpClientSslUtils 远程请求:{} 参数:{} 耗时:{}ms", url, request, timeCost);
	}
}

3. Json string tool class

import com.alibaba.fastjson.TypeReference;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;

import java.text.SimpleDateFormat;

@Slf4j
public class JsonUtil {

    /**
     * 定义映射对象
     */
    public static ObjectMapper objectMapper = new ObjectMapper();

    /**
     * 日期格式化
     */
    private static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";

    static {
        //对象的所有字段全部列入
        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        //取消默认转换timestamps形式
        objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
        //忽略空Bean转json的错误
        objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
        //所有的日期格式都统一为以下的样式,即yyyy-MM-dd HH:mm:ss
        objectMapper.setDateFormat(new SimpleDateFormat(DATE_FORMAT));
        //忽略 在json字符串中存在,但是在java对象中不存在对应属性的情况。防止错误
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

    }

    /**
     * string转JsonNode
     *
     * @param jsonString
     * @return com.fasterxml.jackson.databind.JsonNode
     */
    public static JsonNode stringToJsonNode(String jsonString) throws JsonProcessingException {

        return objectMapper.readTree(jsonString);

    }

    /**
     * 对象转json字符串
     *
     * @param obj
     * @param <T>
     */
    public static <T> String objToString(T obj) {

        if (obj == null) {
            return null;
        }
        try {
            return obj instanceof String ? (String) obj : objectMapper.writeValueAsString(obj);
        } catch (JsonProcessingException e) {
            log.warn("Parse Object to String error : {}", e.getMessage());
            return null;
        }
    }

    /**
     * 对象转格式化的字符串字符串
     *
     * @param obj
     * @param <T>
     * @return
     */
    public static <T> String objToPrettyString(T obj) {
        if (obj == null) {
            return null;
        }
        try {
            return obj instanceof String ? (String) obj : objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(obj);
        } catch (JsonProcessingException e) {
            log.warn("Parse Object to String error : {}", e.getMessage());
            return null;
        }
    }

    /**
     * json字符串转对象
     *
     * @param jsonString
     * @param cls
     * @param <T>
     */
    public static <T> T stringToObj(String jsonString, Class<T> cls) {
        if (StringUtils.isEmpty(jsonString) || cls == null) {
            return null;
        }
        try {
            return cls.equals(String.class) ? (T) jsonString : objectMapper.readValue(jsonString, cls);
        } catch (JsonProcessingException e) {
            log.warn("Parse String to Object error : {}", e.getMessage());
            return null;
        }
    }

}

4. Interface method

 @PostMapping("/getPhone")
    public ResultResponse getPhone(@RequestBody @Valid WxMiniGetPhone param) {
        JSONObject wxJson;
        // 获取token
        String token_url = null;

        /**
         * appid切换
         */
        if (param.getWxAppletType() == 1) {
            token_url = String.format("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s", bAppID, bAppSecret);
        } else if (param.getWxAppletType() == 2) {
            token_url = String.format("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s", aAppId, aSecret);
        } else {
            throw new UserServiceException("异常");
        }


        try {
            JSONObject token = JSON.parseObject(HttpClientSslUtils.doGet(token_url));
            if (token == null) {
                log.info("获取token失败");
                return null;
            }
            String accessToken = token.getString("access_token");
            if (StringUtils.isEmpty(accessToken)) {
                log.info("获取token失败");
                return null;
            }
            log.info("token : {}", accessToken);
            //获取phone
            String url = "https://api.weixin.qq.com/wxa/business/getuserphonenumber"
                    + "?access_token=" + accessToken;
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("code", param.getCode());
            String reqJsonStr = JsonUtil.objToString(jsonObject);
            wxJson = JSON.parseObject(HttpClientSslUtils.doPost(url, reqJsonStr));

            if (wxJson == null) {
                log.info("获取手机号失败");
                return ResultResponse.error("获取手机号失败!");
            }
            return ResultResponse.ok("获取成功!").setData(wxJson);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return ResultResponse.error("获取失败,请重试!");
    }

3. Another introduction to access_token

access_token has a limited number of times, 2000 times a day exceeds the number of times the accessToken needs to be refreshed, 10 times/month

Guess you like

Origin blog.csdn.net/pingzhuyan/article/details/131143111