API开通微信点击计划,商家小票

前面我们讲过,开通点击计划,可以跳转到商家小票页面(可增加业务操作,比如:申请电子发票等),

具体请参考:微信点金计划,开通商家小票

这里我们讲一下如何使用api打开商家小票功能,而不是手动点击开通。

 

微信官方文档

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

 

主代码如下:

ChangeGoldplanSatusBo.java

package com.dlys.pay.wechat.bo.v3.goldplan;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import com.dlys.pay.wechat.bo.v3.util.HttpUrlUtil;

import net.sf.json.JSONObject;

/**
 * 点金计划管理api
 * 
 * @author libaibai
 * @version 1.0 2020年12月23日
 */
public class ChangeGoldplanSatusBo {

	private static final Logger LOG = LogManager.getLogger(ChangeGoldplanSatusBo.class);
	
	public static void exe(String sub_mchid) {

		// 开通或关闭点金计划的动作,枚举值:
		// OPEN:表示开通点金计划
		// CLOSE:表示关闭点金计划
		String operation_type = "OPEN";

		JSONObject json = new JSONObject();
		json.put("sub_mchid", sub_mchid);
		json.put("operation_type", operation_type);
		String str = HttpUrlUtil.sendPost(HttpUrlUtil.GOLDPLAN_PATH, json.toString());
		LOG.info("点金计划开通成功,str=" + str);
	}
	
	public static void main(String[] args) {
		String sub_mchid = "16050*****";
		ChangeGoldplanSatusBo.exe(sub_mchid);
	}
}

 

ChangeCustomPageStatusBo.java

package com.dlys.pay.wechat.bo.v3.goldplan;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import com.dlys.pay.wechat.bo.v3.util.HttpUrlUtil;

import net.sf.json.JSONObject;

/**
 * 商家小票管理api
 * 
 * @author libaibai
 * @version 1.0 2020年12月23日
 */
public class ChangeCustomPageStatusBo {

	private static final Logger LOG = LogManager.getLogger(ChangeCustomPageStatusBo.class);

	public static void exe(String sub_mchid) {

		// 开通或关闭点金计划的动作,枚举值:
		// OPEN:表示开通点金计划
		// CLOSE:表示关闭点金计划
		String operation_type = "OPEN";

		JSONObject json = new JSONObject();
		json.put("sub_mchid", sub_mchid);
		json.put("operation_type", operation_type);
		String str = HttpUrlUtil.sendPost(HttpUrlUtil.GOLDPLAN_CUS_PATH, json.toString());
		LOG.info("点金计划商家页面开通成功,str=" + str);
	}

	public static void main(String[] args) {
		String sub_mchid = "16050*****";
		ChangeCustomPageStatusBo.exe(sub_mchid);
	}
}

 

AesUtil.java

package com.dlys.pay.wechat.bo.v3.util;

import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;

import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import com.dlyspublic.util.Config;

/**
 * 
 * @author libaibai
 * @version 1.0 2020年9月8日
 */
public class AesUtil {

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

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

}

 

CertUtil.java

package com.dlys.pay.wechat.bo.v3.util;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.cert.CertificateException;
import java.security.cert.CertificateExpiredException;
import java.security.cert.CertificateFactory;
import java.security.cert.CertificateNotYetValidException;
import java.security.cert.X509Certificate;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;

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

import com.dlys.pay.wechat.util.HttpsRequest;
import com.google.common.io.ByteStreams;

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

/**
 * 证书工具类
 * 
 * @author libaibai
 * @version 1.0 2020年9月4日
 */
public class CertUtil {

	// 微信证书私钥
//	public static String APICLIENT_KEY = "G:\\workspace\\dlysw\\src\\main\\resources\\conf\\cert\\apiclient_key.pem";
	public static String APICLIENT_KEY = "conf/cert/apiclient_key.pem";

	// 微信商户证书
	public static String APICLIENT_CERT = "conf/cert/apiclient_cert.pem";
//	public static String APICLIENT_CERT = "G:\\workspace\\dlysw\\src\\main\\resources\\conf\\cert\\apiclient_cert.pem";

	/**
	 * 获取私钥。
	 *
	 * @param apiclient_key 私钥文件路径 (required)
	 * @return 私钥对象
	 */
	public static PrivateKey getPrivateKey() throws IOException {
		InputStream instream = HttpsRequest.class.getClassLoader().getResourceAsStream(APICLIENT_KEY);
		String content = new String(ByteStreams.toByteArray(instream), StandardCharsets.UTF_8);
		try {
			String privateKey = content.replace("-----BEGIN PRIVATE KEY-----", "").replace("-----END PRIVATE KEY-----", "").replaceAll("\\s+", "");
			KeyFactory kf = KeyFactory.getInstance("RSA");
			return kf.generatePrivate(new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKey)));
		} catch (NoSuchAlgorithmException e) {
			throw new RuntimeException("当前Java环境不支持RSA", e);
		} catch (InvalidKeySpecException e) {
			throw new RuntimeException("无效的密钥格式");
		}
	}

	/**
	 * 获取商户证书。
	 *
	 * @param filename 证书文件路径 (required)
	 * @return X509证书
	 */
	public static X509Certificate getCertificate(String filename) throws IOException {
//		InputStream fis = new FileInputStream(APICLIENT_CERT);
		InputStream fis = HttpsRequest.class.getClassLoader().getResourceAsStream(APICLIENT_CERT);;
		try (BufferedInputStream bis = new BufferedInputStream(fis)) {
			CertificateFactory cf = CertificateFactory.getInstance("X509");
			X509Certificate cert = (X509Certificate) cf.generateCertificate(bis);
			cert.checkValidity();
			return cert;
		} catch (CertificateExpiredException e) {
			throw new RuntimeException("证书已过期", e);
		} catch (CertificateNotYetValidException e) {
			throw new RuntimeException("证书尚未生效", e);
		} catch (CertificateException e) {
			throw new RuntimeException("无效的证书文件", e);
		}
	}

	/**
	 * 获取商户证书序列号
	 * 
	 * @param certPath 获取商户证书序列号 传递商号证书路径 apiclient_cert
	 * @return
	 * @throws IOException
	 */
	public static String getSerialNo(String certPath) throws IOException {
		X509Certificate certificate = getCertificate(certPath);
		return certificate.getSerialNumber().toString(16).toUpperCase();
	}

	/**
	 * 获取微信平台证书序列号
	 * 
	 * @return
	 * @throws Exception
	 */
	public static String getCertSerialNo() throws Exception {
		try {
			String str = HttpUrlUtil.sendGet();
//			System.out.println(str);
			JSONObject json = JSONObject.fromObject(str);
			JSONArray jsonArray = JSONArray.fromObject(json.optString("data"));
			JSONObject jsonObject = jsonArray.getJSONObject(0);
			return jsonObject.optString("serial_no");
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 获取微信平台证书
	 * 
	 * @return
	 * @throws Exception
	 */
	public static String getCertStr() throws Exception {
		try {
			String str = HttpUrlUtil.sendGet();
			JSONObject json = JSONObject.fromObject(str);
			JSONArray jsonArray = JSONArray.fromObject(json.optString("data"));
			JSONObject jsonObject = jsonArray.getJSONObject(0);
			JSONObject jsonCert = JSONObject.fromObject(jsonObject.optString("encrypt_certificate"));
//			System.out.println(str);
//			System.out.println(jsonCert);
			String certKeyString = AesUtil.decryptToString(jsonCert.getString("associated_data").getBytes(),
					jsonCert.getString("nonce").getBytes(), jsonCert.getString("ciphertext"));
			return certKeyString;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	public static void main(String[] args) {
		try {
			System.out.println(CertUtil.getCertStr());
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

 

HttpUrlUtil.java

package com.dlys.pay.wechat.bo.v3.util;

import java.security.PrivateKey;
import java.security.Signature;
import java.util.Base64;

import javax.ws.rs.core.Response;

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

import com.dlyspublic.util.Config;
import com.tenet.util.UUIDUtil;

import okhttp3.HttpUrl;

/**
 * 微信V3接口HttpUrl工具类
 * 
 * @author libaibai
 * @version 1.0 2020年9月4日
 */
public class HttpUrlUtil {

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

	public static String POST = "POST";
	public static String GET = "GET";

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

	public static String GOLDPLAN_PATH = "/v3/goldplan/merchants/changegoldplanstatus"; // 点金计划管理API
	public static String GOLDPLAN_CUS_PATH = "/v3/goldplan/merchants/changecustompagestatus"; // 商家小票管理API

	/**
	 * POST请求
	 * 
	 * @param path 请求接口path
	 * @param body
	 * @return
	 */
	public static String sendPost(String path, String body) {
		String url = HOST + path;
		try {
			// 获取微信平台商户证书序列号
			String wxSerialNo = CertUtil.getCertSerialNo();
			String authorization = getToken(POST, url, body);
			WebClient client = WebClient.create(HOST);
			client.reset();
			client.header("Content-Type", "application/json; charset=UTF-8");
			client.header("Accept", "application/json");
			client.header("user-agent", "application/json");
			client.header("Wechatpay-Serial", wxSerialNo);
			client.header("Authorization", authorization);
			client.path(path);
			Response r = client.post(body);
			return r.readEntity(String.class);
		} catch (Exception e) {
			return null;
		}
	}

	/**
	 * get请求
	 */
	public static String sendGet() {
		// 请求URL
		String url = HOST + CERT_PATH;
		try {
			String authorization = getToken(GET, url, "");
			WebClient client = WebClient.create(HOST);
			client.reset();
			client.header("Content-Type", "application/json; charset=UTF-8");
			client.header("Accept", "application/json");
			client.header("User-Agent", "application/json");
			client.header("Authorization", authorization);
			client.path(CERT_PATH);
			Response r = client.get();
			return r.readEntity(String.class);
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

	/**
	 * get请求
	 */
	public static String sendGet(String applymentId) {
		// 请求URL
		String url = HOST + APPLY_QUERY_PATH + applymentId;
		try {
			String authorization = getToken(GET, url, "");
			WebClient client = WebClient.create(HOST);
			client.reset();
			client.header("Content-Type", "application/json; charset=UTF-8");
			client.header("Accept", "application/json");
			client.header("User-Agent", "application/json");
			client.header("Authorization", authorization);
			client.path(APPLY_QUERY_PATH + applymentId);
			Response r = client.get();
			return r.readEntity(String.class);
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

	/**
	 * 获取加密串
	 * 
	 * @param method
	 * @param url
	 * @param body
	 * @return
	 */
	public static String getToken(String method, String url, String body) {
		String nonceStr = UUIDUtil.getUUID32();
		long timestamp = System.currentTimeMillis() / 1000;
		HttpUrl httpUrl = HttpUrl.parse(url);
		String message = buildMessage(method, httpUrl, timestamp, nonceStr, body);
		String signature = null;
		String certificateSerialNo = null;
		try {
			signature = sign(message.getBytes("utf-8"));
			certificateSerialNo = CertUtil.getSerialNo("");
		} catch (Exception e) {
			e.printStackTrace();
		}

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

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

	public static String buildMessage(String method, HttpUrl url, long timestamp, String nonceStr, String body) {
		String canonicalUrl = url.encodedPath();
		if (url.encodedQuery() != null) {
			canonicalUrl += "?" + url.encodedQuery();
		}
		return method + "\n" + canonicalUrl + "\n" + timestamp + "\n" + nonceStr + "\n" + body + "\n";
	}
}

 

RsaEncryptUtil.java

package com.dlys.pay.wechat.bo.v3.util;

import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import java.util.Base64;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;

/**
 * 敏感信息加密
 * 
 * @author libaibai
 * @version 1.0 2020年9月3日
 */
public class RsaEncryptUtil {

	/**
	 * 加密
	 * 
	 * @param message
	 * @param certificate
	 * @return
	 * @throws IllegalBlockSizeException
	 * @throws IOException
	 */
	public static String rsaEncryptOAEP(String message, X509Certificate certificate) throws IllegalBlockSizeException, IOException {
		try {
			Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding");
			cipher.init(Cipher.ENCRYPT_MODE, certificate.getPublicKey());

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

	/**
	 * 解密
	 * 
	 * @param ciphertext
	 * @param privateKey
	 * @return
	 * @throws BadPaddingException
	 * @throws IOException
	 */
	public static String rsaDecryptOAEP(String ciphertext, PrivateKey privateKey) throws BadPaddingException, IOException {
		try {
			Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding");
			cipher.init(Cipher.DECRYPT_MODE, privateKey);

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

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

木小百99

听说打赏的人都发财了

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值