您的位置:首页 > 移动开发 > 微信开发

微信支付的准备工作

2018-03-16 10:54 232 查看
最近忙于研究微信支付的相关内容,自己有踏入了不少的坑。在这里说一下吧,


微信提供的网站有:

微信开放平台:https://open.weixin.qq.com/

用于微信中绑定开发的App,小程序等,实现App支付。

微信公众平台:https://mp.weixin.qq.com/

用于申请微信公众号,小程序的,

微信支付平台:https://pay.weixin.qq.com/

用于查询微信公众号微信支付的订单,可以申请h5支付

一、申请微信服务号

想要开始微信支付,必须要有一个微信服务号,

然后配置微信服务器,等等。

对于微信支付的类型:

扫码支付、公众号支付、App支付、h5支付。

对于扫码支付、公众号支付只需要申请公众号就可以获取,

对于App支付、h5支付,则需要进一步的去申请权限。

关于支付的一些字段:

appid 开发者ID

mch_id 商户号

AppSecret 开发者密码,API秘钥

这些在申请时,会都发到你的邮箱中。

二、开发时,准备的工具类:

1.微信的传输数据为xml

这里借用的是com.thoughtworks.xstream.jar包

public static <T> String beanToXml(T xml){
XStream stream = new XStream();
stream.alias("xml", xml.getClass());
return stream.toXML(xml).replace("__", "_");
}

public static <T> void beanToXml(T xml,OutputStream out) throws UnsupportedEncodingException, IOException{
String content = beanToXml(xml);
out.write(content.getBytes("utf-8"));
}

public static <T> T xmlToBean(String xmlStr,Class<T> clazz){
XStream stream = new XStream();
stream.alias("xml", clazz);
stream.alias("root", clazz);
stream.ignoreUnknownElements();
@SuppressWarnings("unchecked")
T t = (T) stream.fromXML(xmlStr);
return t;
}

@SuppressWarnings("unchecked")
public static <T> T xmlToBean(String xmlStr,T obj){
XStream stream = new XStream();
stream.alias("xml", obj.getClass());
stream.alias("root", obj.getClass());
stream.ignoreUnknownElements();
return (T) stream.fromXML(xmlStr, obj);
}

public static <T> T xmlToBean(InputStream in,Class<T> clazz){
XStream stream = new XStream();
stream.alias("xml", clazz);
stream.alias("root", clazz);
stream.ignoreUnknownElements();
@SuppressWarnings("unchecked")
T t = (T) stream.fromXML(in);
return t;
}


2.对传输的数据按照属性ASCII顺序排列形成键值对的格式,

再加上秘钥,获取签名

public static <T> String getSignTemp(T t){
if(t == null){
throw new RuntimeException("参数不能为空");
}
StringBuilder sb = new StringBuilder();
//1.对参数按照key=value的格式,并按照参数名ASCII字典序排序
try {
Class<?> clazz = t.getClass();
Field[] fields = clazz.getDeclaredFields();
List<Field> list = new ArrayList<>(Arrays.asList(fields));
Collections.sort(list, new Comparator<Field>(){

@Override
public int compare(Field o1, Field o2) {
return o1.getName().compareTo(o2.getName());
}

});
for(Field field : list){
String name = field.getName();
//序列号省略
if(name.contains("serialVersionUID")){
continue;
}
//签名省略
if(name.equals("sign")){
continue;
}
field.setAccessible(true);
if(field.get(t) == null){
continue;
}
if(field.getType() == String.class && StringUtils.isEmpty(field.get(t))){
continue;
}
if(name.equals("packageValue")){
name = "package";
}
sb.append(name).append("=").append(field.get(t)).append("&");
}
//2.拼接API密钥
//      sb.append("key=").append(PayConstant.KEY);

sb.append("key=").append(PayConstant.APP_SECRET);
} catch (Exception e) {
throw new RuntimeException("获取参数字符串失败", e);
}
//3.返回拼接文本
return sb.toString();
}
public static String Md5Digest(String str) {
if(StringUtils.isEmpty(str)){
throw new RuntimeException("字符串不能为空");
}
byte[] data = DigestUtils.md5(str);

//将字节数组转为16进制字符串
StringBuffer sb = new StringBuffer();
for(byte b:data){
String sHex = Integer.toHexString(b&0xff);
if(sHex.length()==1){
sHex = "0"+sHex;
}
sb.append(sHex);
}
String content = sb.toString().toUpperCase();
return content;
}


3.对于支付退款时,需要使用https请求,并且要使用证书验证

构造https请求

String pwd = PayConstant.MCH_ID;
// 1.获取证书 输入流, 获取证书密码
//          InputStream in = new FileInputStream(PayConstant.API_FILE_PATH);
InputStream in = HttpUtil.class.getClassLoader().getResourceAsStream("apiclient_cert.p12");

char[] password = pwd.toCharArray();

// 2.实例化密钥库 & 初始化密钥工厂
KeyStore key = KeyStore.getInstance("PKCS12");
key.load(in, password);

KeyManagerFactory keyManager = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManager.init(key, password);

// 3.创建 SSLContext
SSLContext ssl = SSLContext.getInstance("TLS");;
ssl.init(keyManager.getKeyManagers(), null, new SecureRandom());
SSLConnectionSocketFactory sslFactory = new SSLConnectionSocketFactory(
ssl,
new String[]{"TLSv1"},
null,
new DefaultHostnameVerifier());
// 4.创建连接管理类
BasicHttpClientConnectionManager connManager = new BasicHttpClientConnectionManager(
RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", PlainConnectionSocketFactory.getSocketFactory())
.register("https", sslFactory)
.build(),
null,
null,
null
);
HttpClient httpClient = HttpClientBuilder.create()
.setConnectionManager(connManager)
.build();


4.对于申请退款时,返回的数据需要进行AES解密。

注意:这里可能会报秘钥的长度非法,

这里因为java当前jre只支持秘钥128位的,而秘钥 192、256位的,则会报错。

因此这里需要从官网下载对秘钥长度没有限制的jar,把之前的替换了就可以了。

把里面的两个jar包:local_policy.jar 和 US_export_policy.jar 替换掉原来 Jdk 安装目录 $\Java\jre{6|7|8}\lib\security 下的两个jar 包接可以了

下载地址:

jdk1.7:http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html

jdk1.8:http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html

/**
* 密钥算法
*/
private static final String ALGORITHM = "AES";
/**
* 加解密算法/工作模式/填充方式
*/
private static final String ALGORITHM_MODE_PADDING = "AES/ECB/PKCS5Padding";
/**
* 生成Key
*/
public static SecretKey key = new SecretKeySpec(
MyDigestUtils.Md5Digest("2IBtBXdrqC3kCBs4gaceL7nl2nnFadQv")
.toLowerCase().getBytes(), ALGORITHM);

/**
* 加密
* @param data
* @return
* @throws Exception
*/
public static String encryptData(String data) throws Exception{
Cipher cipher = Cipher.getInstance(ALGORITHM_MODE_PADDING);
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] s = cipher.doFinal(data.getBytes("utf-8"));
return Base64Utils.encodeToString(s);
}

/**
* 解密
* @param base64Data
* @return
* @throws Exception
*/
public static String decryptData(String base64Data) throws Exception{
Cipher cipher = Cipher.getInstance(ALGORITHM_MODE_PADDING);
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] data = cipher.doFinal(Base64Utils.decodeFromString(base64Data));

return new String(data, "utf-8");
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息