From 701070b9546b633aecb056eed3f8208e5191a51d Mon Sep 17 00:00:00 2001
From: Binary Wang
Date: Thu, 30 Jul 2026 11:35:44 +0800
Subject: [PATCH 1/2] =?UTF-8?q?feat:=20=E5=B0=8F=E7=A8=8B=E5=BA=8F?=
=?UTF-8?q?=E8=8E=B7=E5=8F=96=E6=89=8B=E6=9C=BA=E5=8F=B7=E6=94=AF=E6=8C=81?=
=?UTF-8?q?openid=E6=A0=A1=E9=AA=8C?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../wx/miniapp/api/WxMaUserService.java | 11 ++++++++
.../miniapp/api/impl/WxMaUserServiceImpl.java | 8 ++++++
.../WxMaUserServiceImplPhoneNumberTest.java | 28 +++++++++++++++++++
3 files changed, 47 insertions(+)
create mode 100644 weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaUserServiceImplPhoneNumberTest.java
diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaUserService.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaUserService.java
index bc8b69a14f..7b382f4706 100644
--- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaUserService.java
+++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaUserService.java
@@ -73,6 +73,17 @@ public interface WxMaUserService {
*/
WxMaPhoneNumberInfo getPhoneNumber(String code) throws WxErrorException;
+ /**
+ * 获取手机号信息,并校验手机号获取凭证与用户的绑定关系。
+ *
+ * @param code 每个code只能使用一次,code的有效期为5min。code获取方式参考手机号快速验证组件
+ * @param openid 用户openid,传入后微信服务端将校验其与code的绑定关系
+ * @return 用户手机号信息
+ * @throws WxErrorException .
+ * @apiNote 该接口用于将code换取用户手机号。
+ */
+ WxMaPhoneNumberInfo getPhoneNumber(String code, String openid) throws WxErrorException;
+
/**
* 获取手机号信息,基础库:2.21.2及以上或2023年8月28日起
*
diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaUserServiceImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaUserServiceImpl.java
index c9c7a7b773..e15e500656 100644
--- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaUserServiceImpl.java
+++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaUserServiceImpl.java
@@ -67,8 +67,16 @@ public WxMaPhoneNumberInfo getPhoneNoInfo(String sessionKey, String encryptedDat
@Override
public WxMaPhoneNumberInfo getPhoneNumber(String code) throws WxErrorException {
+ return this.getPhoneNumber(code, null);
+ }
+
+ @Override
+ public WxMaPhoneNumberInfo getPhoneNumber(String code, String openid) throws WxErrorException {
JsonObject param = new JsonObject();
param.addProperty("code", code);
+ if (openid != null) {
+ param.addProperty("openid", openid);
+ }
String responseContent = this.service.post(GET_PHONE_NUMBER_URL, param.toString());
JsonObject response = GsonParser.parse(responseContent);
if (response.has(PHONE_INFO)) {
diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaUserServiceImplPhoneNumberTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaUserServiceImplPhoneNumberTest.java
new file mode 100644
index 0000000000..790a7503a3
--- /dev/null
+++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaUserServiceImplPhoneNumberTest.java
@@ -0,0 +1,28 @@
+package cn.binarywang.wx.miniapp.api.impl;
+
+import cn.binarywang.wx.miniapp.api.WxMaService;
+import me.chanjar.weixin.common.error.WxErrorException;
+import org.testng.annotations.Test;
+
+import static cn.binarywang.wx.miniapp.constant.WxMaApiUrlConstants.User.GET_PHONE_NUMBER_URL;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+/**
+ * {@link WxMaUserServiceImpl} 获取手机号接口的单元测试。
+ */
+public class WxMaUserServiceImplPhoneNumberTest {
+
+ @Test
+ public void shouldSendOpenidWhenGettingPhoneNumber() throws WxErrorException {
+ WxMaService wxMaService = mock(WxMaService.class);
+ when(wxMaService.post(anyString(), anyString())).thenReturn("{\"phone_info\":{}}");
+
+ new WxMaUserServiceImpl(wxMaService).getPhoneNumber("phone-code", "user-openid");
+
+ verify(wxMaService).post(GET_PHONE_NUMBER_URL, "{\"code\":\"phone-code\",\"openid\":\"user-openid\"}");
+ }
+}
From e27d3cce5a1e2edb0e953ae3e4f2d507288ba8b4 Mon Sep 17 00:00:00 2001
From: Binary Wang
Date: Thu, 30 Jul 2026 11:59:28 +0800
Subject: [PATCH 2/2] =?UTF-8?q?fix:=20=E5=A4=84=E7=90=86=E8=8E=B7=E5=8F=96?=
=?UTF-8?q?=E6=89=8B=E6=9C=BA=E5=8F=B7=E6=8E=A5=E5=8F=A3=E5=AE=A1=E6=A0=B8?=
=?UTF-8?q?=E6=84=8F=E8=A7=81?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../chanjar/weixin/common/util/DataUtils.java | 6 +++--
.../weixin/common/util/DataUtilsTest.java | 10 ++++++++
.../wx/miniapp/api/WxMaUserService.java | 4 +++-
.../miniapp/api/impl/BaseWxMaServiceImpl.java | 2 +-
.../miniapp/api/impl/WxMaUserServiceImpl.java | 3 ++-
.../WxMaUserServiceImplPhoneNumberTest.java | 24 ++++++++++++++++++-
6 files changed, 43 insertions(+), 6 deletions(-)
diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/DataUtils.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/DataUtils.java
index 095363cf8d..2e9f5d4c7b 100644
--- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/DataUtils.java
+++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/DataUtils.java
@@ -17,8 +17,10 @@ public class DataUtils {
*/
public static E handleDataWithSecret(E data) {
E dataForLog = data;
- if (data instanceof String && StringUtils.contains((String) data, "secret=")) {
- dataForLog = (E) RegExUtils.replaceAll((String) data, "(^|[?&])secret=[^&]*", "$1secret=******");
+ if (data instanceof String) {
+ String stringData = (String) data;
+ stringData = RegExUtils.replaceAll(stringData, "(^|[?&])secret=[^&]*", "$1secret=******");
+ dataForLog = (E) RegExUtils.replaceAll(stringData, "(\\\"openid\\\"\\s*:\\s*\\\")[^\\\"]*(\\\")", "$1******$2");
}
return dataForLog;
}
diff --git a/weixin-java-common/src/test/java/me/chanjar/weixin/common/util/DataUtilsTest.java b/weixin-java-common/src/test/java/me/chanjar/weixin/common/util/DataUtilsTest.java
index 1bda61a237..c5088e78c9 100644
--- a/weixin-java-common/src/test/java/me/chanjar/weixin/common/util/DataUtilsTest.java
+++ b/weixin-java-common/src/test/java/me/chanjar/weixin/common/util/DataUtilsTest.java
@@ -47,4 +47,14 @@ public void testHandleDataWithSecretEncodedValue() {
assertFalse(s.contains("%2F"), "Encoded characters in the secret must be masked too");
assertTrue(s.contains("&secret=******&"), "Secret should be replaced with asterisks");
}
+
+ @Test
+ public void testHandleDataWithOpenidInJson() {
+ String data = "{\"code\":\"phone-code\",\"openid\":\"user-openid\"}";
+
+ String result = DataUtils.handleDataWithSecret(data);
+
+ assertFalse(result.contains("user-openid"));
+ assertTrue(result.contains("\"openid\":\"******\""));
+ }
}
diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaUserService.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaUserService.java
index 7b382f4706..65b817f79f 100644
--- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaUserService.java
+++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaUserService.java
@@ -82,7 +82,9 @@ public interface WxMaUserService {
* @throws WxErrorException .
* @apiNote 该接口用于将code换取用户手机号。
*/
- WxMaPhoneNumberInfo getPhoneNumber(String code, String openid) throws WxErrorException;
+ default WxMaPhoneNumberInfo getPhoneNumber(String code, String openid) throws WxErrorException {
+ return this.getPhoneNumber(code);
+ }
/**
* 获取手机号信息,基础库:2.21.2及以上或2023年8月28日起
diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/BaseWxMaServiceImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/BaseWxMaServiceImpl.java
index 9d6c2c0fa6..bf69439a65 100644
--- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/BaseWxMaServiceImpl.java
+++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/BaseWxMaServiceImpl.java
@@ -374,7 +374,7 @@ public WxMaApiResponse execute(
Map headers,
String data)
throws WxErrorException {
- String dataForLog = "Headers: " + headers.toString() + " Body: " + data;
+ String dataForLog = "Headers: " + headers.toString() + " Body: " + DataUtils.handleDataWithSecret(data);
return executeWithRetry(
(uriWithAccessToken) -> executor.execute(uriWithAccessToken, headers, data, WxType.MiniApp),
uri,
diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaUserServiceImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaUserServiceImpl.java
index e15e500656..abbb021e8c 100644
--- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaUserServiceImpl.java
+++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaUserServiceImpl.java
@@ -16,6 +16,7 @@
import me.chanjar.weixin.common.util.SignUtils;
import me.chanjar.weixin.common.util.json.GsonParser;
import org.apache.commons.codec.digest.DigestUtils;
+import org.apache.commons.lang3.StringUtils;
import java.util.Map;
@@ -74,7 +75,7 @@ public WxMaPhoneNumberInfo getPhoneNumber(String code) throws WxErrorException {
public WxMaPhoneNumberInfo getPhoneNumber(String code, String openid) throws WxErrorException {
JsonObject param = new JsonObject();
param.addProperty("code", code);
- if (openid != null) {
+ if (StringUtils.isNotBlank(openid)) {
param.addProperty("openid", openid);
}
String responseContent = this.service.post(GET_PHONE_NUMBER_URL, param.toString());
diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaUserServiceImplPhoneNumberTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaUserServiceImplPhoneNumberTest.java
index 790a7503a3..610cd13bf8 100644
--- a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaUserServiceImplPhoneNumberTest.java
+++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaUserServiceImplPhoneNumberTest.java
@@ -1,7 +1,10 @@
package cn.binarywang.wx.miniapp.api.impl;
import cn.binarywang.wx.miniapp.api.WxMaService;
+import com.google.gson.JsonObject;
import me.chanjar.weixin.common.error.WxErrorException;
+import me.chanjar.weixin.common.util.json.GsonParser;
+import org.mockito.ArgumentCaptor;
import org.testng.annotations.Test;
import static cn.binarywang.wx.miniapp.constant.WxMaApiUrlConstants.User.GET_PHONE_NUMBER_URL;
@@ -10,6 +13,8 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
/**
* {@link WxMaUserServiceImpl} 获取手机号接口的单元测试。
@@ -23,6 +28,23 @@ public void shouldSendOpenidWhenGettingPhoneNumber() throws WxErrorException {
new WxMaUserServiceImpl(wxMaService).getPhoneNumber("phone-code", "user-openid");
- verify(wxMaService).post(GET_PHONE_NUMBER_URL, "{\"code\":\"phone-code\",\"openid\":\"user-openid\"}");
+ ArgumentCaptor requestBody = ArgumentCaptor.forClass(String.class);
+ verify(wxMaService).post(eq(GET_PHONE_NUMBER_URL), requestBody.capture());
+ JsonObject request = GsonParser.parse(requestBody.getValue());
+ assertEquals(request.get("code").getAsString(), "phone-code");
+ assertEquals(request.get("openid").getAsString(), "user-openid");
+ }
+
+ @Test
+ public void shouldIgnoreBlankOpenidWhenGettingPhoneNumber() throws WxErrorException {
+ WxMaService wxMaService = mock(WxMaService.class);
+ when(wxMaService.post(anyString(), anyString())).thenReturn("{\"phone_info\":{}}");
+
+ new WxMaUserServiceImpl(wxMaService).getPhoneNumber("phone-code", " ");
+
+ ArgumentCaptor requestBody = ArgumentCaptor.forClass(String.class);
+ verify(wxMaService).post(eq(GET_PHONE_NUMBER_URL), requestBody.capture());
+ JsonObject request = GsonParser.parse(requestBody.getValue());
+ assertFalse(request.has("openid"));
}
}