分享

微信小微商户/特约商户进件V3版本对接...

 极风狼 2021-11-11

今天我们来讲一下微信小微商户进件V3版本的接口对接。

首先我们来看一下官方的文档:

https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/tool/applyment/chapter3_1.shtml

根据文档提示,小微商户进件对接协议和特约商户是一样的,只是参数不一样。我们这里以小微商户说明(主要是公司需要)。

 

首先引入JRE包,后面会用到。这里使用的是maven。

  1. <dependency>
  2. <groupId>com.github.wechatpay-apiv3</groupId>
  3. <artifactId>wechatpay-apache-httpclient</artifactId>
  4. <version>0.2.1</version>
  5. </dependency>

进件需要我们先上传身份证照片,请参考

微信小微商户进件(二):图片上传

 

上代码

ApplymentBo.java

  1. package com.pay.wechat.bo.small.v3;

  2. import java.io.ByteArrayInputStream;
  3. import java.security.cert.X509Certificate;
  4. import java.util.HashMap;
  5. import java.util.Map;

  6. import org.springframework.stereotype.Component;

  7. import com.util.OrderIDUtil;
  8. import com.pay.wechat.bo.small.v3.util.CertUtil;
  9. import com.pay.wechat.bo.small.v3.util.HttpUrlUtil;
  10. import com.pay.wechat.bo.small.v3.util.RsaEncryptUtil;
  11. import com.pay.contrib.apache.httpclient.util.PemUtil;

  12. import net.sf.json.JSONObject;

  13. /**
  14. * 小微商户进件V3版本<br>
  15. * 文档地址:https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/tool/applyment/chapter3_1.shtml
  16. *
  17. * @author libaibai@chinatenet.com
  18. * @version 1.0 2020年9月3日
  19. */
  20. @Component
  21. public class ApplymentBo {

  22. /**
  23. * 进件
  24. *
  25. * @param contactName 管理员姓名
  26. * @param contactIdNum 管理员身份证号码
  27. * @param contactMobile 管理员手机号码
  28. * @param contactMail 管理员邮箱
  29. * @param microName 门店名称
  30. * @param microAddressCode
  31. * 门店编码,参考:https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/ecommerce/applyments/chapter4_1.shtml
  32. * @param microAddress 门店地址
  33. * @param idCardCopy 身份证正面照片
  34. * @param idCardNational 身份证反面照片
  35. * @param cardPeriodBegin 身份证有效期开始时间,格式:2011-06-24
  36. * @param cardPeriodEnd 身份证有效期结束时间,格式:2011-06-24
  37. * @param servicePhone 客户电话
  38. * @param accountBank 开户行银行名称
  39. * @param bankAddressCode 开户行编码
  40. * @param bankName 开户银行全称(含支行] <pre>
  41. * 1、“开户银行”为17家直连银行无需填写
  42. * 2、“开户银行”为其他银行,则开户银行全称(含支行)和开户银行联行号二选一 //
  43. * 3、需填写银行全称,如"深圳农村商业银行XXX支行",详细参见《开户银行全称(含支行)对照表》
  44. * 示例值:施秉县农村信用合作联社城关信用社
  45. * </pre>
  46. *
  47. * @param accountNumber 银行卡号
  48. * @throws Exception
  49. */
  50. public void exe(String contactName, String contactIdNum, String contactMobile, String contactMail, String microName, String microAddressCode,
  51. String microAddress, String idCardCopy, String idCardNational, String cardPeriodBegin, String cardPeriodEnd, String servicePhone,
  52. String accountBank, String bankAddressCode, String bankName, String accountNumber) throws Exception {

  53. // 获取微信平台证书 并解析方法在后面
  54. String certString = CertUtil.getCertStr();
  55. ByteArrayInputStream stringStream = new ByteArrayInputStream(certString.getBytes());
  56. // 下面所有加密参数需要的对象
  57. X509Certificate certx = PemUtil.loadCertificate(stringStream);

  58. // 超级管理员信息
  59. Map<String, Object> contact_info = new HashMap<String, Object>();
  60. String contact_name = RsaEncryptUtil.rsaEncryptOAEP(contactName, certx); // 超级管理员姓名
  61. String contact_id_number = RsaEncryptUtil.rsaEncryptOAEP(contactIdNum, certx); // 超级管理员身份证件号码
  62. String mobile_phone = RsaEncryptUtil.rsaEncryptOAEP(contactMobile, certx);// 联系手机
  63. String contact_email = RsaEncryptUtil.rsaEncryptOAEP(contactMail, certx);// 联系邮箱
  64. contact_info.put("contact_name", contact_name);
  65. contact_info.put("contact_id_number", contact_id_number);
  66. contact_info.put("mobile_phone", mobile_phone);
  67. contact_info.put("contact_email", contact_email);

  68. // 主体资料
  69. String subject_type = "SUBJECT_TYPE_MICRO"; // 主体类型
  70. String micro_biz_type = "MICRO_TYPE_STORE"; // 小微经营类型
  71. String micro_name = microName; // 门店名称
  72. String micro_address_code = microAddressCode; // 门店省市编码
  73. String micro_address = microAddress; // 门店街道名称

  74. String store_entrance_pic = "oO5EoYZsdukezw2NXUxEkb9vTU7PgOu5GyMpNVdMVj5aJAwD85_8kNpakg-s4917roa97XFJf0GPdBNHEvkyf0XPzrOjeKjoBYmEL_eSk7I"; // 门店门口照片
  75. String micro_indoor_copy = "oO5EoYZsdukezw2NXUxEkb9vTU7PgOu5GyMpNVdMVj5aJAwD85_8kNpakg-s4917roa97XFJf0GPdBNHEvkyf0XPzrOjeKjoBYmEL_eSk7I"; // 店内环境照片

  76. // 证件类型,IDENTIFICATION_TYPE_IDCARD
  77. String id_doc_type = "IDENTIFICATION_TYPE_IDCARD";

  78. // String id_card_copy = idCardCopy; // 身份证人像面照片
  79. // String id_card_national = idCardNational; // 身份证国徽面照片

  80. String id_card_name = RsaEncryptUtil.rsaEncryptOAEP(contactName, certx); // 身份证姓名
  81. String id_card_number = RsaEncryptUtil.rsaEncryptOAEP(contactIdNum, certx); // 身份证号码
  82. // String card_period_begin = "2011-06-24"; // 身份证有效期开始时间示例值:2026-06-06
  83. // String card_period_end = "2021-06-24"; // 身份证有效期结束时间示例值:2026-06-06

  84. Map<String, Object> subject_info = new HashMap<String, Object>(); // 主体资料
  85. Map<String, Object> micro_biz_info = new HashMap<String, Object>(); // 小微商户辅助材料
  86. Map<String, Object> micro_store_info = new HashMap<String, Object>(); // 门店场所信息
  87. Map<String, Object> identity_info = new HashMap<String, Object>(); // 经营者身份证件
  88. Map<String, Object> id_card_info = new HashMap<String, Object>(); // 身份证信息

  89. micro_store_info.put("micro_name", micro_name);
  90. micro_store_info.put("micro_address_code", micro_address_code);
  91. micro_store_info.put("micro_address", micro_address);
  92. micro_store_info.put("store_entrance_pic", store_entrance_pic);
  93. micro_store_info.put("micro_indoor_copy", micro_indoor_copy);

  94. micro_biz_info.put("micro_biz_type", micro_biz_type);
  95. micro_biz_info.put("micro_store_info", micro_store_info);

  96. id_card_info.put("id_card_copy", idCardCopy);
  97. id_card_info.put("id_card_national", idCardNational);
  98. id_card_info.put("id_card_name", id_card_name);
  99. id_card_info.put("id_card_number", id_card_number);
  100. id_card_info.put("card_period_begin", cardPeriodBegin);
  101. id_card_info.put("card_period_end", cardPeriodEnd);

  102. identity_info.put("id_doc_type", id_doc_type);
  103. identity_info.put("id_card_info", id_card_info);

  104. subject_info.put("subject_type", subject_type);
  105. subject_info.put("micro_biz_info", micro_biz_info);
  106. subject_info.put("identity_info", identity_info);

  107. // 经营资料
  108. // String merchant_shortname = "张三停车场"; // 商户简称
  109. // String service_phone = "0755222222"; // 客服电话
  110. Map<String, Object> business_info = new HashMap<String, Object>();
  111. business_info.put("merchant_shortname", microName);
  112. business_info.put("service_phone", servicePhone);

  113. // 结算规则
  114. // 入驻结算规则ID;请选择结算规则ID,详细参见《费率结算规则对照表》 示例值:小微商户:703
  115. String settlement_id = "703";//
  116. String qualification_type = "停车缴费"; // 所属行业;请填写所属行业名称,建议参见《费率结算规则对照表》 示例值:餐饮
  117. Map<String, Object> settlement_info = new HashMap<String, Object>();
  118. settlement_info.put("settlement_id", settlement_id);
  119. settlement_info.put("qualification_type", qualification_type);

  120. // 收款银行卡
  121. // 账户类型 若主体为小微,可填写:经营者个人银行卡 枚举值:
  122. // BANK_ACCOUNT_TYPE_PERSONAL:经营者个人银行卡
  123. // 示例值:BANK_ACCOUNT_TYPE_CORPORATE
  124. String bank_account_type = "BANK_ACCOUNT_TYPE_PERSONAL";

  125. String account_name = RsaEncryptUtil.rsaEncryptOAEP(contactName, certx); // 开户名称(该字段需进行加密处理)
  126. // String account_bank = "建设银行"; // 开户银行开户银行,详细参见《开户银行对照表》 示例值:工商银行
  127. // String bank_address_code = "440300"; // 开户银行省市编码至少精确到市,详细参见《省市区编号对照表》 示例值:110000

  128. // 1、“开户银行”为17家直连银行无需填写
  129. // 2、“开户银行”为其他银行,则开户银行全称(含支行)和开户银行联行号二选一
  130. // 3、需填写银行全称,如"深圳农村商业银行XXX支行",详细参见《开户银行全称(含支行)对照表》
  131. // 示例值:施秉县农村信用合作联社城关信用社
  132. // String bank_name = ""; // 开户银行全称(含支行]
  133. String account_number = RsaEncryptUtil.rsaEncryptOAEP(accountNumber, certx); // 银行账号(该字段需进行加密处理)
  134. Map<String, Object> bank_account_info = new HashMap<String, Object>();
  135. bank_account_info.put("bank_account_type", bank_account_type);
  136. bank_account_info.put("account_name", account_name);
  137. bank_account_info.put("account_bank", accountBank);
  138. bank_account_info.put("bank_address_code", bankAddressCode);
  139. bank_account_info.put("bank_name", bankName);
  140. bank_account_info.put("account_number", account_number);

  141. String business_code = OrderIDUtil.getOrderID(null); // 申请单号
  142. Map<String, Object> map = new HashMap<String, Object>();
  143. map.put("business_code", business_code);
  144. map.put("contact_info", contact_info);
  145. map.put("subject_info", subject_info);
  146. map.put("business_info", business_info);
  147. map.put("settlement_info", settlement_info);
  148. map.put("bank_account_info", bank_account_info);
  149. try {
  150. String body = JSONObject.fromObject(map).toString();
  151. String str = HttpUrlUtil.sendPost(body);
  152. System.out.println(str);
  153. } catch (Exception e) {
  154. e.printStackTrace();
  155. }
  156. }

  157. public static void main(String[] args) {
  158. String contactName = "张**"; // 超级管理员姓名
  159. String contactIdNum = "520201************"; // 身份证号码
  160. String contactMobile = "139*********"; // 手机号码
  161. String contactMail = "3*******@qq.com"; // 联系邮箱
  162. String microName = "联运公司停车场"; // 门店名称(也用于简称)

  163. // 门店省市编码
  164. // 参考:https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/ecommerce/applyments/chapter4_1.shtml
  165. String microAddressCode = "520200";
  166. String microAddress = "**************路52号"; // 门店地址

  167. String idCardCopy = "eYjXW9AMg-***********************************************-w508XSWniUlM"; // 身份证正面照
  168. String idCardNational = "eYjXW9AMg-************************************-RPk8-eZ7PJvD8XBtaTZ1a1YEIN1PSeuRnkM"; // 身份证反面照

  169. String cardPeriodBegin = "2007-08-10"; // 身份证有效期开始时间2011-06-24
  170. String cardPeriodEnd = "2027-08-10";// 身份证有效期结束时间2011-06-24
  171. String servicePhone = "139*********";// 客服电话; 无特殊使用管理员手机号码
  172. String accountBank = "招商银行"; // 开户银行
  173. String bankAddressCode = "520200"; // 开户银行省市编码至少精确到市,详细参见《省市区编号对照表》 示例值:110000;

  174. // 1、“开户银行”为17家直连银行无需填写
  175. // 2、“开户银行”为其他银行,则开户银行全称(含支行)和开户银行联行号二选一
  176. // 3、需填写银行全称,如"深圳农村商业银行XXX支行",详细参见《开户银行全称(含支行)对照表》
  177. // 示例值:施秉县农村信用合作联社城关信用社
  178. String bankName = ""; // 开户银行全称(含支行]
  179. String accountNumber = "************************"; // 银行账号

  180. ApplymentBo t = new ApplymentBo();
  181. try {
  182. t.exe(contactName, contactIdNum, contactMobile, contactMail, microName, microAddressCode, microAddress, idCardCopy, idCardNational,
  183. cardPeriodBegin, cardPeriodEnd, servicePhone, accountBank, bankAddressCode, bankName, accountNumber);
  184. } catch (Exception e) {
  185. e.printStackTrace();
  186. }
  187. }
  188. }

httpUrlUtil.java

  1. package com.pay.wechat.bo.small.v3.util;

  2. import java.security.PrivateKey;
  3. import java.security.Signature;
  4. import java.util.Base64;

  5. import javax.ws.rs.core.Response;

  6. import org.apache.cxf.jaxrs.client.WebClient;

  7. import com.util.Config;
  8. import com.util.UUIDUtil;

  9. import okhttp3.HttpUrl;

  10. /**
  11. * HttpUrl工具类
  12. *
  13. * @author libaibai
  14. * @version 1.0 2020年9月4日
  15. */
  16. public class HttpUrlUtil {

  17. public static String SCHEMA = "WECHATPAY2-SHA256-RSA2048";
  18. public static String merchantId = Config.MCHIDSP; // 服务商

  19. public static String POST = "POST";
  20. public static String GET = "GET";

  21. public static String host = "https://api.mch.weixin.qq.com";
  22. public static String APPLY_PATH = "/v3/applyment4sub/applyment/"; // 申请单url
  23. public static String CERT_PATH = "/v3/certificates"; // 获取微信平台证书url
  24. public static String APPLY_QUERY_PATH = "/v3/applyment4sub/applyment/applyment_id/"; // 查询申请状态

  25. /**
  26. * POST请求
  27. */
  28. public static String sendPost(String body) {
  29. String url = host + APPLY_PATH;
  30. try {
  31. // 获取微信平台商户证书序列号
  32. String wxSerialNo = CertUtil.getCertSerialNo();
  33. String authorization = getToken(POST, url, body);
  34. WebClient client = WebClient.create(host);
  35. client.reset();
  36. client.header("Content-Type", "application/json; charset=UTF-8");
  37. client.header("Accept", "application/json");
  38. client.header("user-agent", "application/json");
  39. client.header("Wechatpay-Serial", wxSerialNo);
  40. client.header("Authorization", authorization);
  41. client.path(APPLY_PATH);
  42. Response r = client.post(body);
  43. return r.readEntity(String.class);
  44. } catch (Exception e) {
  45. return null;
  46. }
  47. }

  48. /**
  49. * get请求
  50. */
  51. public static String sendGet() {
  52. // 请求URL
  53. String url = host + CERT_PATH;
  54. try {
  55. String authorization = getToken(GET, url, "");
  56. WebClient client = WebClient.create(host);
  57. client.reset();
  58. client.header("Content-Type", "application/json; charset=UTF-8");
  59. client.header("Accept", "application/json");
  60. client.header("User-Agent", "application/json");
  61. client.header("Authorization", authorization);
  62. client.path(CERT_PATH);
  63. Response r = client.get();
  64. return r.readEntity(String.class);
  65. } catch (Exception e) {
  66. e.printStackTrace();
  67. return null;
  68. }
  69. }

  70. /**
  71. * get请求
  72. */
  73. public static String sendGet(String applymentId) {
  74. // 请求URL
  75. String url = host + APPLY_QUERY_PATH + applymentId;
  76. try {
  77. String authorization = getToken(GET, url, "");
  78. WebClient client = WebClient.create(host);
  79. client.reset();
  80. client.header("Content-Type", "application/json; charset=UTF-8");
  81. client.header("Accept", "application/json");
  82. client.header("User-Agent", "application/json");
  83. client.header("Authorization", authorization);
  84. client.path(APPLY_QUERY_PATH + applymentId);
  85. Response r = client.get();
  86. return r.readEntity(String.class);
  87. } catch (Exception e) {
  88. e.printStackTrace();
  89. return null;
  90. }
  91. }

  92. /**
  93. * 获取加密串
  94. *
  95. * @param method
  96. * @param url
  97. * @param body
  98. * @return
  99. */
  100. public static String getToken(String method, String url, String body) {
  101. String nonceStr = UUIDUtil.getUUID32();
  102. long timestamp = System.currentTimeMillis() / 1000;
  103. HttpUrl httpUrl = HttpUrl.parse(url);
  104. String message = buildMessage(method, httpUrl, timestamp, nonceStr, body);
  105. String signature = null;
  106. String certificateSerialNo = null;
  107. try {
  108. signature = sign(message.getBytes("utf-8"));
  109. certificateSerialNo = CertUtil.getSerialNo("");
  110. } catch (Exception e) {
  111. e.printStackTrace();
  112. }

  113. return SCHEMA + " mchid=\"" + merchantId + "\"," + "nonce_str=\"" + nonceStr + "\"," + "timestamp=\"" + timestamp + "\"," + "serial_no=\""
  114. + certificateSerialNo + "\"," + "signature=\"" + signature + "\"";
  115. }

  116. /**
  117. * 得到签名字符串
  118. */
  119. public static String sign(byte[] message) throws Exception {
  120. Signature sign = Signature.getInstance("SHA256withRSA");
  121. PrivateKey privateKey = CertUtil.getPrivateKey();
  122. sign.initSign(privateKey);
  123. sign.update(message);
  124. return Base64.getEncoder().encodeToString(sign.sign());
  125. }

  126. public static String buildMessage(String method, HttpUrl url, long timestamp, String nonceStr, String body) {
  127. String canonicalUrl = url.encodedPath();
  128. if (url.encodedQuery() != null) {
  129. canonicalUrl += "?" + url.encodedQuery();
  130. }
  131. return method + "\n" + canonicalUrl + "\n" + timestamp + "\n" + nonceStr + "\n" + body + "\n";
  132. }
  133. }

CertUtil.java

  1. package com.pay.wechat.bo.small.v3.util;

  2. import java.io.BufferedInputStream;
  3. import java.io.FileInputStream;
  4. import java.io.IOException;
  5. import java.io.InputStream;
  6. import java.nio.charset.StandardCharsets;
  7. import java.nio.file.Files;
  8. import java.nio.file.Paths;
  9. import java.security.KeyFactory;
  10. import java.security.NoSuchAlgorithmException;
  11. import java.security.PrivateKey;
  12. import java.security.cert.CertificateException;
  13. import java.security.cert.CertificateExpiredException;
  14. import java.security.cert.CertificateFactory;
  15. import java.security.cert.CertificateNotYetValidException;
  16. import java.security.cert.X509Certificate;
  17. import java.security.spec.InvalidKeySpecException;
  18. import java.security.spec.PKCS8EncodedKeySpec;

  19. import org.apache.commons.codec.binary.Base64;

  20. import net.sf.json.JSONArray;
  21. import net.sf.json.JSONObject;

  22. /**
  23. * 证书工具类
  24. *
  25. * @author libaibai
  26. * @version 1.0 2020年9月4日
  27. */
  28. public class CertUtil {

  29. // 微信证书私钥路径(从微信商户平台下载,保存在本地)
  30. public static String APICLIENT_KEY = "G:\\workspace\\dlysw\\src\\main\\resources\\conf\\cert\\apiclient_key.pem";

  31. // 微信商户证书路径(从微信商户平台下载,保存在本地)
  32. public static String APICLIENT_CERT = "G:\\workspace\\dlysw\\src\\main\\resources\\conf\\cert\\apiclient_cert.pem";

  33. /**
  34. * 获取私钥。
  35. *
  36. * @param apiclient_key 私钥文件路径 (required)
  37. * @return 私钥对象
  38. */
  39. public static PrivateKey getPrivateKey() throws IOException {
  40. String content = new String(Files.readAllBytes(Paths.get(APICLIENT_KEY)), StandardCharsets.UTF_8);
  41. try {
  42. String privateKey = content.replace("-----BEGIN PRIVATE KEY-----", "").replace("-----END PRIVATE KEY-----", "").replaceAll("\\s+", "");
  43. KeyFactory kf = KeyFactory.getInstance("RSA");
  44. return kf.generatePrivate(new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKey)));
  45. } catch (NoSuchAlgorithmException e) {
  46. throw new RuntimeException("当前Java环境不支持RSA", e);
  47. } catch (InvalidKeySpecException e) {
  48. throw new RuntimeException("无效的密钥格式");
  49. }
  50. }

  51. /**
  52. * 获取商户证书。
  53. *
  54. * @param filename 证书文件路径 (required)
  55. * @return X509证书
  56. */
  57. public static X509Certificate getCertificate(String filename) throws IOException {
  58. InputStream fis = new FileInputStream(APICLIENT_CERT);
  59. try (BufferedInputStream bis = new BufferedInputStream(fis)) {
  60. CertificateFactory cf = CertificateFactory.getInstance("X509");
  61. X509Certificate cert = (X509Certificate) cf.generateCertificate(bis);
  62. cert.checkValidity();
  63. return cert;
  64. } catch (CertificateExpiredException e) {
  65. throw new RuntimeException("证书已过期", e);
  66. } catch (CertificateNotYetValidException e) {
  67. throw new RuntimeException("证书尚未生效", e);
  68. } catch (CertificateException e) {
  69. throw new RuntimeException("无效的证书文件", e);
  70. }
  71. }

  72. /**
  73. * 获取商户证书序列号
  74. *
  75. * @param certPath 获取商户证书序列号 传递商号证书路径 apiclient_cert
  76. * @return
  77. * @throws IOException
  78. */
  79. public static String getSerialNo(String certPath) throws IOException {
  80. X509Certificate certificate = getCertificate(certPath);
  81. return certificate.getSerialNumber().toString(16).toUpperCase();
  82. }

  83. /**
  84. * 获取微信平台证书序列号
  85. *
  86. * @return
  87. * @throws Exception
  88. */
  89. public static String getCertSerialNo() throws Exception {
  90. try {
  91. String str = HttpUrlUtil.sendGet();
  92. System.out.println(str);
  93. JSONObject json = JSONObject.fromObject(str);
  94. JSONArray jsonArray = JSONArray.fromObject(json.optString("data"));
  95. JSONObject jsonObject = jsonArray.getJSONObject(0);
  96. return jsonObject.optString("serial_no");
  97. } catch (Exception e) {
  98. e.printStackTrace();
  99. }
  100. return null;
  101. }

  102. /**
  103. * 获取微信平台证书
  104. *
  105. * @return
  106. * @throws Exception
  107. */
  108. public static String getCertStr() throws Exception {
  109. try {
  110. String str = HttpUrlUtil.sendGet();
  111. JSONObject json = JSONObject.fromObject(str);
  112. JSONArray jsonArray = JSONArray.fromObject(json.optString("data"));
  113. JSONObject jsonObject = jsonArray.getJSONObject(0);
  114. JSONObject jsonCert = JSONObject.fromObject(jsonObject.optString("encrypt_certificate"));
  115. //System.out.println(str);
  116. //System.out.println(jsonCert);
  117. String certKeyString = AesUtil.decryptToString(jsonCert.getString("associated_data").getBytes(),
  118. jsonCert.getString("nonce").getBytes(), jsonCert.getString("ciphertext"));
  119. return certKeyString;
  120. } catch (Exception e) {
  121. e.printStackTrace();
  122. }
  123. return null;
  124. }

  125. public static void main(String[] args) {
  126. try {
  127. System.out.println(CertUtil.getCertStr());
  128. } catch (Exception e) {
  129. // TODO Auto-generated catch block
  130. e.printStackTrace();
  131. }
  132. }
  133. }

AesUtil.java

  1. package com.dlysw.pay.wechat.bo.small.v3.util;

  2. import java.security.InvalidAlgorithmParameterException;
  3. import java.security.InvalidKeyException;
  4. import java.security.NoSuchAlgorithmException;
  5. import java.util.Base64;

  6. import javax.crypto.Cipher;
  7. import javax.crypto.NoSuchPaddingException;
  8. import javax.crypto.spec.GCMParameterSpec;
  9. import javax.crypto.spec.SecretKeySpec;

  10. import com.dlyspublic.util.Config;

  11. /**
  12. *
  13. * @author libaibai
  14. * @version 1.0 2020年9月8日
  15. */
  16. public class AesUtil {

  17. public static final int TAG_LENGTH_BIT = 128;
  18. public static byte[] aesKey = Config.AES_KEY_APIV3.getBytes(); // APIv3密钥

  19. public static String decryptToString(byte[] associatedData, byte[] nonce, String ciphertext) throws Exception {
  20. try {
  21. Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
  22. SecretKeySpec key = new SecretKeySpec(aesKey, "AES");
  23. GCMParameterSpec spec = new GCMParameterSpec(TAG_LENGTH_BIT, nonce);
  24. cipher.init(Cipher.DECRYPT_MODE, key, spec);
  25. cipher.updateAAD(associatedData);
  26. return new String(cipher.doFinal(Base64.getDecoder().decode(ciphertext)), "utf-8");
  27. } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
  28. throw new IllegalStateException(e);
  29. } catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
  30. throw new IllegalArgumentException(e);
  31. }
  32. }

  33. }

ok,直接运行ApplymentBo.java里面的main方法,得到返回结果

{"applyment_id": xxxxxxxxxxxxxx}

就表示进件成功了,我们登陆到商户平台看看。创建员工=API的就是刚才我们申请成功的。

 

 

最后,我们也把状态查询的代码也贴一下

ApplymentQueryBo.java

  1. package com.pay.wechat.bo.small.v3;

  2. import org.springframework.stereotype.Component;

  3. import com.pay.wechat.bo.small.v3.util.HttpUrlUtil;

  4. /**
  5. * 小微商户进件查询
  6. *
  7. * @author libaibai
  8. * @version 1.0 2020年9月8日
  9. */
  10. @Component
  11. public class ApplymentQueryBo {

  12. /**
  13. * 执行
  14. *
  15. * @param applymentId 申请单号
  16. * @return
  17. */
  18. public String query(String applymentId) {
  19. String str = HttpUrlUtil.sendGet(applymentId);
  20. System.out.println(str);
  21. return str;
  22. }

  23. public static void main(String[] args) {
  24. String applymentId = "200000xxxxxxxxxx";
  25. ApplymentQueryBo b = new ApplymentQueryBo();
  26. b.query(applymentId);
  27. }
  28. }

返回成功:

{"applyment_id":200000xxxxxxxxxx,"applyment_state":"APPLYMENT_STATE_TO_BE_SIGNED","applyment_state_msg":"请超级管理员使用微信打开返回的“签约链接”,根据页面指引完成签约","audit_detail":[],"business_code":"WEB|1590030703","sign_url":"https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=gQGb7zwAAAAAAAAAAS5odHRwOi8vd2VpeGluLnFxLmNvbS9xLzAyeWZaMDl3b3JlUjIxbG9PLU52Y1YAAgRY5VZfAwQAjScA","sub_mchid":"15947111111"}

 

补充:

  1. package com.pay.wechat.bo.small.v3.util;

  2. import java.io.IOException;
  3. import java.security.InvalidKeyException;
  4. import java.security.NoSuchAlgorithmException;
  5. import java.security.PrivateKey;
  6. import java.security.cert.X509Certificate;
  7. import java.util.Base64;

  8. import javax.crypto.BadPaddingException;
  9. import javax.crypto.Cipher;
  10. import javax.crypto.IllegalBlockSizeException;
  11. import javax.crypto.NoSuchPaddingException;

  12. /**
  13. * 敏感信息加密
  14. *
  15. * @author libaibai
  16. * @version 1.0 2020年9月3日
  17. */
  18. public class RsaEncryptUtil {

  19. /**
  20. * 加密
  21. *
  22. * @param message
  23. * @param certificate
  24. * @return
  25. * @throws IllegalBlockSizeException
  26. * @throws IOException
  27. */
  28. public static String rsaEncryptOAEP(String message, X509Certificate certificate) throws IllegalBlockSizeException, IOException {
  29. try {
  30. Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding");
  31. cipher.init(Cipher.ENCRYPT_MODE, certificate.getPublicKey());

  32. byte[] data = message.getBytes("utf-8");
  33. byte[] cipherdata = cipher.doFinal(data);
  34. return Base64.getEncoder().encodeToString(cipherdata);
  35. } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
  36. throw new RuntimeException("当前Java环境不支持RSA v1.5/OAEP", e);
  37. } catch (InvalidKeyException e) {
  38. throw new IllegalArgumentException("无效的证书", e);
  39. } catch (IllegalBlockSizeException | BadPaddingException e) {
  40. throw new IllegalBlockSizeException("加密原串的长度不能超过214字节");
  41. }
  42. }

  43. /**
  44. * 解密
  45. *
  46. * @param ciphertext
  47. * @param privateKey
  48. * @return
  49. * @throws BadPaddingException
  50. * @throws IOException
  51. */
  52. public static String rsaDecryptOAEP(String ciphertext, PrivateKey privateKey) throws BadPaddingException, IOException {
  53. try {
  54. Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding");
  55. cipher.init(Cipher.DECRYPT_MODE, privateKey);

  56. byte[] data = Base64.getDecoder().decode(ciphertext);
  57. return new String(cipher.doFinal(data), "utf-8");
  58. } catch (NoSuchPaddingException | NoSuchAlgorithmException e) {
  59. throw new RuntimeException("当前Java环境不支持RSA v1.5/OAEP", e);
  60. } catch (InvalidKeyException e) {
  61. throw new IllegalArgumentException("无效的私钥", e);
  62. } catch (BadPaddingException | IllegalBlockSizeException e) {
  63. throw new BadPaddingException("解密失败");
  64. }
  65. }
  66. }

 

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约