一起学习微信支付,开发之前的准备

刚起手学习小程序2周,微信支付也是花了4天,今天第4天做完了代码这块分享下自己的学习成果。

我的框架是springboot mybatis mysql 前端主流用vue 框架不是我搭建的,是分模块的,有1个自己的核心jar 和页面外层的jar

说点能够通用的 这里微信支付需要用到的jar




        

            com.thoughtworks.xstream

            xstream

            1.4.10

        

        

        

            com.squareup.retrofit2

            retrofit

            2.3.0

        

        

        

            com.squareup.retrofit2

            converter-simplexml

            2.3.0

        

        

    com.squareup.okhttp3

    okhttp

    3.2.0







    org.bouncycastle

    bcprov-jdk15on

    1.60




上面的jar有okhttp请求 加密解密 xml转换的

项目目录结构 方便大家更明显的感受

一起学习微信支付,开发之前的准备_第1张图片
image

接下来是先发2个公共的文件


* * *

**1.WxPayUtils.java**

package com.alpha.modules.wxpay.utils;

import java.io.Writer;

import java.lang.reflect.Field;

import java.math.BigDecimal;

import java.net.InetAddress;

import java.net.UnknownHostException;

import java.text.SimpleDateFormat;

import java.util.Date;

import java.util.Map;

import java.util.Random;

import java.util.SortedMap;

import java.util.TreeMap;

import org.apache.commons.codec.digest.DigestUtils;

import org.apache.commons.lang3.StringUtils;

import org.simpleframework.xml.Element;

import org.simpleframework.xml.Serializer;

import org.simpleframework.xml.core.Persister;

import com.thoughtworks.xstream.XStream;

import com.thoughtworks.xstream.annotations.XStreamAlias;

import com.thoughtworks.xstream.core.util.QuickWriter;

import com.thoughtworks.xstream.io.HierarchicalStreamWriter;

import com.thoughtworks.xstream.io.naming.NoNameCoder;

import com.thoughtworks.xstream.io.xml.PrettyPrintWriter;

import com.thoughtworks.xstream.io.xml.XppDriver;

/**

* @Author: Temple

* @Description: Xml-Object 转换

* @Date: 2018年12月17日 11:33:21

*/

public class WxPayUtils {

public static final String ALL_CHAR = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";

public static final String LETTER_CHAR = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";

public static final String NUMBER_CHAR = "0123456789";

public static final Random RANDOM = new Random();

public static final SimpleDateFormat SIMPLE_DATE_FORMAT = new SimpleDateFormat("yyyyMMddHHmmss");

/**

* 生成指定长度的随机字符串(包含数字和字母)

*

* @param length

*            随机字符串长度

* @return

*/

public static String getRandomStr(Integer length) {

StringBuilder builder = new StringBuilder();

for (int i = 0; i < length; i++) {

builder.append(ALL_CHAR.charAt(RANDOM.nextInt(ALL_CHAR.length())));

}

return builder.toString();

}

/**

* 生成指定长度的随即纯数字字符串

*

* @param length

*            随即字符串长度

* @return

*/

public static String getRandomNum(Integer length) {

StringBuilder builder = new StringBuilder();

for (int i = 0; i < length; i++) {

builder.append(NUMBER_CHAR.charAt(RANDOM.nextInt(NUMBER_CHAR.length())));

}

return builder.toString();

}

/**

* 生成随即纯字母字符串

*

* @param length

*            随即字符串长度

* @return

*/

public static String getRandomLetter(Integer length) {

StringBuilder builder = new StringBuilder();

for (int i = 0; i < length; i++) {

builder.append(LETTER_CHAR.charAt(RANDOM.nextInt(LETTER_CHAR.length())));

}

return builder.toString();

}

/**

* 获取随机订单号(系统当前时间+随机数字字符串)

*

* @return

*/

public static String getTradeNo() {

StringBuilder builder = new StringBuilder();

Date date = new Date();

builder.append(SIMPLE_DATE_FORMAT.format(date)).append(getRandomNum(17));

return builder.toString();

}

private static XStream xStream = new XStream(new XppDriver(new NoNameCoder()) {

@Override

public HierarchicalStreamWriter createWriter(Writer out) {

return new PrettyPrintWriter(out) {

// 对所有xml节点的转换都增加CDATA标记

// boolean cdata = true;

boolean cdata = false;

@Override

@SuppressWarnings("rawtypes")

public void startNode(String name, Class clazz) {

super.startNode(name, clazz);

}

//// 当对象属性带下划线时,XStream会转换成双下划线,

// 重写这个方法,不再像XppDriver那样调用nameCoder来进行编译,而是直接返回节点名称,避免双下划线出现

@Override

public String encodeNode(String name) {

return name;

}

@Override

protected void writeText(QuickWriter writer, String text) {

if (cdata) {

writer.write("");

} else {

writer.write(text);

}

}

};

}

});

/**

* 对象转xml

*

* @param obj

* @return

*/

public static String objToXML(Object obj) {

// 使用注解设置别名必须在使用之前加上注解类才有作用

xStream.processAnnotations(obj.getClass());

return xStream.toXML(obj);

}

public static Object xmlToObj(String xml, Class objClass) {

Serializer serializer = new Persister();

try {

return serializer.read(objClass, xml);

} catch (Exception e) {

e.printStackTrace();

}

return null;

}

/**

* 签名-MD5

*

* @param signMap

*            待签名Map

* @param key

*            密钥

* @return MD5

*/

public static String signByMD5(Map signMap, String key) {

SortedMap sortedMap = new TreeMap(signMap);

StringBuilder builder = new StringBuilder();

for (Map.Entry entry : sortedMap.entrySet()) {

String mapKey = entry.getKey();

if (!mapKey.equals("sign") && !mapKey.equals("key") && StringUtils.isNotBlank(entry.getValue())) {

builder.append(entry.getKey()).append("=").append(entry.getValue()).append("&");

}

}

builder.append("key=").append(key);

return DigestUtils.md5Hex(builder.toString()).toUpperCase();

}

/**

* 签名-MD5

*

* @param signObj

*            待签名对象

* @param key

*            密钥

* @return

*/

public static String signByMD5(Object signObj, String key) throws IllegalAccessException {

SortedMap sortedMap = new TreeMap();

StringBuilder builder = new StringBuilder();

Class clazz = signObj.getClass();

Field[] fields = clazz.getDeclaredFields(); // 反射回去对象所有字段,包括私有字段

for (Field field : fields) {

field.setAccessible(true);

if (field.get(signObj) != null) {

String name = field.getName();

XStreamAlias anno = field.getAnnotation(XStreamAlias.class); // 若字段上含有@XStreamAlias注解,则参数名取注解值

Element eleAnno = field.getAnnotation(Element.class); // 若字段上含有@Element注解,则参数名取注解值

if (anno != null) {

name = anno.value();

}

if (eleAnno != null) {

name = eleAnno.name();

}

sortedMap.put(name, field.get(signObj).toString());

}

}

for (Map.Entry entry : sortedMap.entrySet()) {

String mapKey = entry.getKey();

if (!mapKey.equals("sign") && !mapKey.equals("key") && StringUtils.isNotBlank(entry.getValue())) {

builder.append(mapKey).append("=").append(entry.getValue()).append("&");

}

}

builder.append("key=").append(key);

return DigestUtils.md5Hex(builder.toString()).toUpperCase();

}

/**

    * 圆转分

    * @param amount 圆单位金额

    * @return

    */

    public static Integer yuanToFen(BigDecimal amount) {

        return amount.movePointRight(2).intValue();

    }

    /**

    * 分转圆

    * @param amount 分单位金额

    * @return

    */

    public static Double fenToYuan(BigDecimal amount) {

        return amount.movePointLeft(2).doubleValue();

    }

    /**

    * 获取ip地址

    * @param request

    * @return

    */

    public static String getIpAddr() { 

        InetAddress addr = null; 

        try { 

        addr= InetAddress.getLocalHost();

            String localip=addr.getHostAddress();

            return localip;

        } catch (UnknownHostException e) { 

            return null; 

        } 

    }

    public static String returnXML(String return_code) {

        return "";

    }

}

public static String getXmlString(HttpServletRequest request){

    BufferedReader reader = null;

        String line = "";

        String xmlString = null;

            try {

reader = request.getReader();

StringBuffer inputString = new StringBuffer();

            while ((line = reader.readLine()) != null) {

                inputString.append(line);

            }

            xmlString = inputString.toString();

            request.getReader().close();

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

    return xmlString;

    }

* * *

2.AESUtils.java   AES/ECB/PKCS7Padding加密解密

package com.alpha.modules.wxpay.utils;

import java.security.Security;

import javax.crypto.Cipher;

import javax.crypto.spec.SecretKeySpec;

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

import org.apache.commons.codec.digest.DigestUtils;

import org.bouncycastle.jce.provider.BouncyCastleProvider;

public class AESUtils {

public static final String ALGORITHM = "AES/ECB/PKCS7Padding";

public static String encode(String data,String key) throws Exception{

Security.addProvider(new BouncyCastleProvider());

String md5Key = DigestUtils.md5Hex(key).toLowerCase();

SecretKeySpec keySpec = new SecretKeySpec(md5Key.getBytes(), "AES");

Cipher cipher = Cipher.getInstance(ALGORITHM);

cipher.init(Cipher.ENCRYPT_MODE, keySpec);

byte[] encode = Base64.encodeBase64(cipher.doFinal(data.getBytes()));

return new String(encode,"UTF-8");

}

    public static String decode(String data,String key) throws Exception{

      byte[] decodeBase64 = Base64.decodeBase64(data);

      SecretKeySpec keySpec = new SecretKeySpec(DigestUtils.md5Hex(key).toLowerCase().getBytes(), "AES"); //生成加密解密需要的Key 

      Security.addProvider(new BouncyCastleProvider());

      Cipher cipher = Cipher.getInstance(ALGORITHM);

      cipher.init(Cipher.DECRYPT_MODE, keySpec); 

      return new String(cipher.doFinal(decodeBase64), "UTF-8"); 

    }

}

WxConfigure.java

package com.alpha.modules.wxpay.utils;

import com.alpha.common.utils.FileUtils;

public class WxConfigure {

//小程序ID

private static String appID = "wx***";

//小程序secret

private static String secret = "83**";

//商户号

private static String mch_id = "**";

//商户密钥

private static String key = "***";

    //支付回调地址

private static String payNotifyUrl="http://***/app/payNotifyUrl";

//退款回调地址

private static String refundNotifyUrl="http://***/app/refundNotifyUrl";

//证书

private static String certPath=FileUtils.getAbsolutePath("certificate/")+"apiclient_cert.p12";

public static String getSecret() {

return secret;

}

public static void setSecret(String secret) {

WxConfigure.secret = secret;

}

public static String getKey() {

return key;

}

public static void setKey(String key) {

WxConfigure.key = key;

}

public static String getAppID() {

return appID;

}

public static void setAppID(String appID) {

WxConfigure.appID = appID;

}

public static String getMch_id() {

return mch_id;

}

public static void setMch_id(String mch_id) {

WxConfigure.mch_id = mch_id;

}

public static String getPayNotifyUrl() {

return payNotifyUrl;

}

public static void setPayNotifyUrl(String payNotifyUrl) {

WxConfigure.payNotifyUrl = payNotifyUrl;

}

public static String getRefundNotifyUrl() {

return refundNotifyUrl;

}

public static void setRefundNotifyUrl(String refundNotifyUrl) {

WxConfigure.refundNotifyUrl = refundNotifyUrl;

}

public static String getCertPath() {

return certPath;

}

public static void setCertPath(String certPath) {

WxConfigure.certPath = certPath;

}

}

上面一写配置自己可以考虑写在yml文件里面,目前我先简单的这样写,域名我是用花生壳的,可以用免费的也可以自己买便宜的2级域名,然后本地调试需要花钱内穿实现通过域名访问本地程序,不懂可以私密我,需要的多的我可以单独写一篇内穿的玩法。


接下来会分享核心代码了,就行把封装的更新下,开始更新

下一篇:小程序之微信支付,从小程序到服务端完整流程和代码

你可能感兴趣的:(一起学习微信支付,开发之前的准备)