diff --git a/weixin-java-pay/REAL_NAME_USAGE.md b/weixin-java-pay/REAL_NAME_USAGE.md new file mode 100644 index 000000000..867bca9ce --- /dev/null +++ b/weixin-java-pay/REAL_NAME_USAGE.md @@ -0,0 +1,143 @@ +# 微信支付实名验证接口使用说明 + +## 概述 + +微信支付实名验证接口允许商户查询用户的实名认证状态,如果用户未实名认证,接口会返回引导用户进行实名认证的URL。 + +## 官方文档 + +https://pay.wechatpay.cn/doc/v2/merchant/4011987607 + +## 接口说明 + +### 查询用户实名认证信息 + +- **接口地址**:`https://api.mch.weixin.qq.com/userinfo/realnameauth/query` +- **请求方式**:POST(需要使用商户证书) +- **请求参数**: + - `appid`:公众账号ID(自动填充) + - `mch_id`:商户号(自动填充) + - `openid`:用户在商户appid下的唯一标识 + - `nonce_str`:随机字符串(自动生成) + - `sign`:签名(自动生成) + +- **返回参数**: + - `return_code`:返回状态码 + - `return_msg`:返回信息 + - `result_code`:业务结果 + - `openid`:用户标识 + - `is_certified`:实名认证状态(Y-已实名认证,N-未实名认证) + - `cert_info`:实名认证信息(加密,仅已实名时返回) + - `guide_url`:引导用户进行实名认证的URL(仅未实名时返回) + +## 使用示例 + +### 1. 获取实名验证服务 + +```java +// 获取WxPayService实例 +WxPayService wxPayService = ... // 根据你的配置初始化 + +// 获取实名验证服务 +RealNameService realNameService = wxPayService.getRealNameService(); +``` + +### 2. 查询用户实名认证状态(完整方式) + +```java +import com.github.binarywang.wxpay.bean.realname.RealNameRequest; +import com.github.binarywang.wxpay.bean.realname.RealNameResult; +import com.github.binarywang.wxpay.exception.WxPayException; + +try { + // 构建请求对象 + RealNameRequest request = RealNameRequest.newBuilder() + .openid("oUpF8uMuAJO_M2pxb1Q9zNjWeS6o") // 用户的openid + .build(); + + // 调用查询接口 + RealNameResult result = realNameService.queryRealName(request); + + // 处理返回结果 + if ("Y".equals(result.getIsCertified())) { + System.out.println("用户已实名认证"); + System.out.println("认证信息:" + result.getCertInfo()); + } else { + System.out.println("用户未实名认证"); + System.out.println("引导链接:" + result.getGuideUrl()); + // 可以将guide_url提供给用户,引导其完成实名认证 + } +} catch (WxPayException e) { + System.err.println("查询失败:" + e.getMessage()); +} +``` + +### 3. 查询用户实名认证状态(简化方式) + +```java +import com.github.binarywang.wxpay.bean.realname.RealNameResult; +import com.github.binarywang.wxpay.exception.WxPayException; + +try { + // 直接传入openid进行查询 + RealNameResult result = realNameService.queryRealName("oUpF8uMuAJO_M2pxb1Q9zNjWeS6o"); + + // 处理返回结果 + if ("Y".equals(result.getIsCertified())) { + System.out.println("用户已实名认证"); + } else { + System.out.println("用户未实名认证,引导链接:" + result.getGuideUrl()); + } +} catch (WxPayException e) { + System.err.println("查询失败:" + e.getMessage()); +} +``` + +## 注意事项 + +1. **证书要求**:本接口需要使用商户证书进行请求,请确保已正确配置商户证书。 + +2. **OPENID获取**:openid是用户在商户appid下的唯一标识,需要通过微信公众平台或小程序获取。 + +3. **认证信息**:`cert_info`字段返回的信息是加密的,需要使用相应的解密方法才能获取明文信息。 + +4. **引导链接**:当用户未实名时,返回的`guide_url`可以用于引导用户完成实名认证,建议在小程序或H5页面中使用。 + +5. **频率限制**:请注意接口调用频率限制,避免频繁查询同一用户的实名状态。 + +## 业务场景 + +- **转账前校验**:在进行企业付款到零钱等操作前,可以先查询用户的实名认证状态 +- **风控审核**:作为业务风控的一部分,确认用户已完成实名认证 +- **用户引导**:发现用户未实名时,引导用户完成实名认证以使用相关功能 + +## 错误处理 + +```java +try { + RealNameResult result = realNameService.queryRealName(openid); + // 处理结果... +} catch (WxPayException e) { + // 处理异常 + String errorCode = e.getErrCode(); + String errorMsg = e.getErrCodeDes(); + + // 根据错误码进行相应处理 + switch (errorCode) { + case "SYSTEMERROR": + // 系统错误,建议稍后重试 + break; + case "PARAM_ERROR": + // 参数错误,检查openid是否正确 + break; + default: + // 其他错误 + break; + } +} +``` + +## 相关链接 + +- [微信支付官方文档](https://pay.wechatpay.cn/doc/v2/merchant/4011987607) +- [WxJava项目主页](https://github.com/binarywang/WxJava) diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/realname/RealNameRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/realname/RealNameRequest.java new file mode 100644 index 000000000..a06318d61 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/realname/RealNameRequest.java @@ -0,0 +1,50 @@ +package com.github.binarywang.wxpay.bean.realname; + +import com.github.binarywang.wxpay.bean.request.BaseWxPayRequest; +import com.thoughtworks.xstream.annotations.XStreamAlias; +import lombok.*; +import me.chanjar.weixin.common.annotation.Required; + +import java.util.Map; + +/** + *
+ * 微信支付实名验证请求对象.
+ * 详见文档:https://pay.wechatpay.cn/doc/v2/merchant/4011987607
+ * 
+ * + * @author Binary Wang + */ +@Data +@EqualsAndHashCode(callSuper = true) +@Builder(builderMethodName = "newBuilder") +@NoArgsConstructor +@AllArgsConstructor +@XStreamAlias("xml") +public class RealNameRequest extends BaseWxPayRequest { + private static final long serialVersionUID = 1L; + + /** + *
+   * 字段名:用户标识
+   * 变量名:openid
+   * 是否必填:是
+   * 类型:String(128)
+   * 示例值:oUpF8uMuAJO_M2pxb1Q9zNjWeS6o
+   * 描述:用户在商户appid下的唯一标识
+   * 
+ */ + @Required + @XStreamAlias("openid") + private String openid; + + @Override + protected void checkConstraints() { + //do nothing + } + + @Override + protected void storeMap(Map map) { + map.put("openid", openid); + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/realname/RealNameResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/realname/RealNameResult.java new file mode 100644 index 000000000..0300cd88b --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/realname/RealNameResult.java @@ -0,0 +1,91 @@ +package com.github.binarywang.wxpay.bean.realname; + +import com.github.binarywang.wxpay.bean.result.BaseWxPayResult; +import com.thoughtworks.xstream.annotations.XStreamAlias; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import org.w3c.dom.Document; + +import java.io.Serializable; + +/** + *
+ * 微信支付实名验证返回结果.
+ * 详见文档:https://pay.wechatpay.cn/doc/v2/merchant/4011987607
+ * 
+ * + * @author Binary Wang + */ +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +@XStreamAlias("xml") +public class RealNameResult extends BaseWxPayResult implements Serializable { + private static final long serialVersionUID = 1L; + + /** + *
+   * 字段名:用户标识
+   * 变量名:openid
+   * 是否必填:否
+   * 类型:String(128)
+   * 示例值:oUpF8uMuAJO_M2pxb1Q9zNjWeS6o
+   * 描述:用户在商户appid下的唯一标识
+   * 
+ */ + @XStreamAlias("openid") + private String openid; + + /** + *
+   * 字段名:实名认证状态
+   * 变量名:is_certified
+   * 是否必填:是
+   * 类型:String(1)
+   * 示例值:Y
+   * 描述:Y-已实名认证 N-未实名认证
+   * 
+ */ + @XStreamAlias("is_certified") + private String isCertified; + + /** + *
+   * 字段名:实名认证信息
+   * 变量名:cert_info
+   * 是否必填:否
+   * 类型:String(256)
+   * 示例值:
+   * 描述:实名认证的相关信息,如姓名等(加密)
+   * 
+ */ + @XStreamAlias("cert_info") + private String certInfo; + + /** + *
+   * 字段名:引导链接
+   * 变量名:guide_url
+   * 是否必填:否
+   * 类型:String(256)
+   * 示例值:
+   * 描述:未实名时,引导用户进行实名认证的URL
+   * 
+ */ + @XStreamAlias("guide_url") + private String guideUrl; + + /** + * 从XML结构中加载额外的属性 + * + * @param d Document + */ + @Override + protected void loadXml(Document d) { + openid = readXmlString(d, "openid"); + isCertified = readXmlString(d, "is_certified"); + certInfo = readXmlString(d, "cert_info"); + guideUrl = readXmlString(d, "guide_url"); + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/RealNameService.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/RealNameService.java new file mode 100644 index 000000000..d69bda7d3 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/RealNameService.java @@ -0,0 +1,43 @@ +package com.github.binarywang.wxpay.service; + +import com.github.binarywang.wxpay.bean.realname.RealNameRequest; +import com.github.binarywang.wxpay.bean.realname.RealNameResult; +import com.github.binarywang.wxpay.exception.WxPayException; + +/** + *
+ * 微信支付实名验证相关服务类.
+ * 详见文档:https://pay.wechatpay.cn/doc/v2/merchant/4011987607
+ * 
+ * + * @author Binary Wang + */ +public interface RealNameService { + /** + *
+   * 获取用户实名认证信息API.
+   * 用于商户查询用户的实名认证状态,如果用户未实名认证,会返回引导用户实名认证的URL
+   * 文档详见:https://pay.wechatpay.cn/doc/v2/merchant/4011987607
+   * 接口链接:https://api.mch.weixin.qq.com/userinfo/realnameauth/query
+   * 
+ * + * @param request 请求对象 + * @return 实名认证查询结果 + * @throws WxPayException the wx pay exception + */ + RealNameResult queryRealName(RealNameRequest request) throws WxPayException; + + /** + *
+   * 获取用户实名认证信息API(简化方法).
+   * 用于商户查询用户的实名认证状态,如果用户未实名认证,会返回引导用户实名认证的URL
+   * 文档详见:https://pay.wechatpay.cn/doc/v2/merchant/4011987607
+   * 接口链接:https://api.mch.weixin.qq.com/userinfo/realnameauth/query
+   * 
+ * + * @param openid 用户openid + * @return 实名认证查询结果 + * @throws WxPayException the wx pay exception + */ + RealNameResult queryRealName(String openid) throws WxPayException; +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/WxPayService.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/WxPayService.java index cfb2479ae..dab89a014 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/WxPayService.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/WxPayService.java @@ -1707,6 +1707,13 @@ WxPayRefundQueryResult refundQuery(String transactionId, String outTradeNo, Stri */ PartnerPayScoreSignPlanService getPartnerPayScoreSignPlanService(); + /** + * 获取实名验证服务类 + * + * @return the real name service + */ + RealNameService getRealNameService(); + /** * 获取医保支付服务类 * diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/BaseWxPayServiceImpl.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/BaseWxPayServiceImpl.java index f2e343df2..b4c2b919a 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/BaseWxPayServiceImpl.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/BaseWxPayServiceImpl.java @@ -137,6 +137,9 @@ public abstract class BaseWxPayServiceImpl implements WxPayService { @Getter private final BusinessOperationTransferService businessOperationTransferService = new BusinessOperationTransferServiceImpl(this); + @Getter + private final RealNameService realNameService = new RealNameServiceImpl(this); + @Getter private final MiPayService miPayService = new MiPayServiceImpl(this); diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/RealNameServiceImpl.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/RealNameServiceImpl.java new file mode 100644 index 000000000..9a1c57fe0 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/RealNameServiceImpl.java @@ -0,0 +1,41 @@ +package com.github.binarywang.wxpay.service.impl; + +import com.github.binarywang.wxpay.bean.realname.RealNameRequest; +import com.github.binarywang.wxpay.bean.realname.RealNameResult; +import com.github.binarywang.wxpay.bean.result.BaseWxPayResult; +import com.github.binarywang.wxpay.exception.WxPayException; +import com.github.binarywang.wxpay.service.RealNameService; +import com.github.binarywang.wxpay.service.WxPayService; +import lombok.RequiredArgsConstructor; + +/** + *
+ * 微信支付实名验证相关服务实现类.
+ * 详见文档:https://pay.wechatpay.cn/doc/v2/merchant/4011987607
+ * 
+ * + * @author Binary Wang + */ +@RequiredArgsConstructor +public class RealNameServiceImpl implements RealNameService { + private final WxPayService payService; + + @Override + public RealNameResult queryRealName(RealNameRequest request) throws WxPayException { + request.checkAndSign(this.payService.getConfig()); + String url = this.payService.getPayBaseUrl() + "/userinfo/realnameauth/query"; + + String responseContent = this.payService.post(url, request.toXML(), true); + RealNameResult result = BaseWxPayResult.fromXML(responseContent, RealNameResult.class); + result.checkResult(this.payService, request.getSignType(), true); + return result; + } + + @Override + public RealNameResult queryRealName(String openid) throws WxPayException { + RealNameRequest request = RealNameRequest.newBuilder() + .openid(openid) + .build(); + return this.queryRealName(request); + } +} diff --git a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/RealNameServiceImplTest.java b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/RealNameServiceImplTest.java new file mode 100644 index 000000000..dda237194 --- /dev/null +++ b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/RealNameServiceImplTest.java @@ -0,0 +1,54 @@ +package com.github.binarywang.wxpay.service.impl; + +import com.github.binarywang.wxpay.bean.realname.RealNameRequest; +import com.github.binarywang.wxpay.bean.realname.RealNameResult; +import com.github.binarywang.wxpay.exception.WxPayException; +import com.github.binarywang.wxpay.service.WxPayService; +import com.github.binarywang.wxpay.testbase.ApiTestModule; +import com.google.inject.Inject; +import lombok.extern.slf4j.Slf4j; +import org.testng.annotations.Guice; +import org.testng.annotations.Test; + +/** + *
+ *  实名验证测试类.
+ * 
+ * + * @author Binary Wang + */ +@Test +@Guice(modules = ApiTestModule.class) +@Slf4j +public class RealNameServiceImplTest { + + @Inject + private WxPayService payService; + + /** + * 测试查询用户实名认证信息. + * + * @throws WxPayException the wx pay exception + */ + @Test + public void testQueryRealName() throws WxPayException { + RealNameRequest request = RealNameRequest.newBuilder() + .openid("oUpF8uMuAJO_M2pxb1Q9zNjWeS6o") + .build(); + + RealNameResult result = this.payService.getRealNameService().queryRealName(request); + log.info("实名认证查询结果:{}", result); + } + + /** + * 测试查询用户实名认证信息(简化方法). + * + * @throws WxPayException the wx pay exception + */ + @Test + public void testQueryRealNameSimple() throws WxPayException { + RealNameResult result = this.payService.getRealNameService() + .queryRealName("oUpF8uMuAJO_M2pxb1Q9zNjWeS6o"); + log.info("实名认证查询结果:{}", result); + } +}