实战开发支付SDK —— 项目结构设计讲解(微信、支付宝)

文章目录结构

一、引言

二、整体结构设计

三、项目代码结构

四、项目通用工具类

MapUtil : 

MoneyUtil : 

RandomUtil : 

XmlUtil​​​​​​​ : 


一、引言

上一章节介绍有关于SDK内容,以及项目搭建。

那么本章就来说说,在开始动手写代码之前,我们先要确认好,这个SDK你想做成什么样子,想怎么去设计。

郑重声明:小编并非专业架构设计师,但小编至少认为该项目的架构设计是很容易理解,所以才用文章的形式分享出来,也可以更加一层的理解熟悉对接支付内容,不是有句话是这么说:知其然知其所以然。

那我们这个SDK具体需要做成什么样子呢?

在开发任何功能之前,作为程序员的我们来说,肯定需要确认一下具体需求是什么,要做成什么样子。

假设现在小编公司需要开发一个专门来用支付的项目,然后领导噼里啪啦说要求一周内搞定,那这个时候如果之前小伙伴没有对接过支付,那么肯定就会去乖乖的看微信、支付宝等对接文档对吧。

对于稍微有经验的老司机来说,就可能会去找相对应的开源第三方支付SDK,那么重点来了,这些第三方支付的SDK他帮你实现了对接微信和支付宝,不需要小伙伴跑过去看文档了,只需要了解这个第三方支付SDK的用法就行啦。

需求就很清晰明了,我们需要在底层对接支付宝和微信,并且对外提供配置,最终能够快速实现支付的功能的效果,那么本次小编所要给大家分享的支付SDK,其中包含了微信和支付宝,当然第一版只实现了基本的支付功能。

二、整体结构设计

小伙伴可以先思考一下 ~~~~

其实这个SDK说起来并不是很复杂,无非就是让用户设置支付相关信息,然后调用相对应功能的方法就行啦。

当当当当当然,小编画图也不是专业的,请各位谅解、谅解、谅解

base-pay-sdk : 就是我们这个项目的名字啦。

WxPayConfig:这个是需要用户来进行配置的,比如说appid、商户号、商户密钥等等,配置好之后我们需要交给BasePayService里面去。

AliPayConfig:这个是支付宝支付所需要的配置,appid、商户私钥、支付宝公钥等等,最后也是交给BasePayService。

BasePayService:这个对象相当于一个桥梁,它负责接收用户配置和对外提供方法调用,以及与下面的实现层进行交互。

实现层就主要就是分支付宝和微信了,这个项目主要编写代码时间都在这个实现层里面了。实现层会各自使用到WxPayConfig、AliPayConfig,拿到相对应的基本数据之后,会对每一个功能进行不同的代码实现,

这里可能就会有小伙伴想问,小编这个图是怎么想出来的呢?

这个是根据实际情况出想来的,小伙伴们可以这样想:

用户层:这个SDK肯定是提供给公司或者其他人所使用,那么对于用户来说肯定是使用越方便越好。比如说发起支付,用户先设置对应的基本信息(appid、商户号等等),在发起支付的时候,肯定还需要对应的订单信息(订单号、订单名称、金额等等),剩下的发起支付功能都交给底层具体实现执行。

实现层:会定义支付宝、微信所请求的接口信息、请求参数、响应参数、状态等等等,把具体实现代码封转起来,不像外界爆露。

三、项目代码结构

项目总体分层大概就是这个样子了,那么接下来就是项目目录结构了,小伙伴们可以先建立空目录,后续具体代码实现都会一一所使用到的。

config:配置包,存放相关信息配置。 (微信、支付宝参数配置)

constants:常量包,存放项目公用常量,

enums:枚举包,小编在实际公司开发中,发现很多都不喜欢写枚举,而是直接代码里面写,其实这种习惯一点都不好。枚举可以存放一些状态或者说数据标识。最常见的就是成功的状态是什么,失败的状态又是什么。

model:实体包,存放基本字段数据,请求实体、响应实体等等。

service:服务包,定义相关api接口,以及对应的接口实现。 (重要代码就都在这个里面了)

utils:通用包,一些通用的类都可以存放到这,比如MapUtil 等等。

以上包名小伙伴可以自由发挥,没有什么特殊爱好,以上只是偏向小编个人习惯。

四、项目通用工具类

MapUtil : 


import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.lang3.StringUtils;
import org.simpleframework.xml.Element;

import java.io.IOException;
import java.lang.reflect.Field;
import java.net.URLEncoder;
import java.util.*;

/**
 * @Auther: IT贱男
 * @Date: 2019/12/19 14:05
 * @Description: Map 工具类
 */
public class MapUtil {

    final static ObjectMapper objectMapper;

    static {
        objectMapper = new ObjectMapper();
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    }

    /**
     * 对象转map
     *
     * @param obj
     * @return
     */
    public static Map<String, String> buildMap(Object obj) {
        Map<String, String> map = new HashMap<>();

        try {
            Class<?> clazz = obj.getClass();
            for (Field field : clazz.getDeclaredFields()) {
                field.setAccessible(true);
                String fieldName = field.getName();

                //如果 element 注解 name 字段设置了内容, 使用其当成字段名
                Element element = field.getAnnotation(Element.class);
                if (element != null && StringUtils.isNotEmpty(element.name())) {
                    fieldName = element.name();
                }

                String value = field.get(obj) == null ? "" : String.valueOf(field.get(obj));
                map.put(fieldName, value);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return map;
    }

    /**
     * map转为url
     * 结果类似 token=abccdssx&sign=ccsacccss
     *
     * @return
     */
    public static String toUrl(Map<String, String> map) {

        String url = "";
        for (Map.Entry<String, String> entry : map.entrySet()) {
            url += entry.getKey() + "=" + entry.getValue() + "&";
        }

        //移除最后一个&
        url = StringUtils.substringBeforeLast(url, "&");

        return url;
    }

    /**
     * map转url 排序后转
     *
     * @param map
     * @return
     */
    public static String toUrlWithSort(Map<String, String> map) {
        List<String> keys = new ArrayList<>(map.keySet());
        Collections.sort(keys);

        String prestr = "";
        for (int i = 0; i < keys.size(); i++) {
            String key = keys.get(i);
            String value = map.get(key);

            if (i == keys.size() - 1) {//拼接时,不包括最后一个&字符
                prestr = prestr + key + "=" + value;
            } else {
                prestr = prestr + key + "=" + value + "&";
            }
        }
        return prestr;
    }

    /**
     * 移除map中空的key和value
     *
     * @param map
     * @return
     */
    public static Map<String, String> removeEmptyKeyAndValue(Map<String, String> map) {

        Iterator<Map.Entry<String, String>> it = map.entrySet().iterator();
        while (it.hasNext()) {
            Map.Entry<String, String> entry = it.next();
            String key = entry.getKey();
            String value = entry.getValue();

            if (StringUtils.isBlank(key) || StringUtils.isBlank(value)) {
                it.remove();
            }
        }

        return map;
    }

    /**
     * 将map中的key转换成小写
     *
     * @param map
     * @return
     */
    public static Map<String, String> keyToLowerCase(Map<String, String> map) {
        Map<String, String> responseMap = new HashMap<>();

        Iterator<Map.Entry<String, String>> it = map.entrySet().iterator();
        while (it.hasNext()) {
            Map.Entry<String, String> entry = it.next();
            String key = entry.getKey();
            String value = entry.getValue();

            responseMap.put(key.toLowerCase(), value);
        }

        return responseMap;
    }

    /**
     * map转url 排序后转
     *
     * @param map
     * @return
     */
    public static String toUrlWithSortAndEncode(Map<String, String> map) {
        List<String> keys = new ArrayList<>(map.keySet());
        Collections.sort(keys);

        String prestr = "";
        for (int i = 0; i < keys.size(); i++) {
            String key = keys.get(i);
            String value = map.get(key);
            if (value == null) {
                break;
            }
            if (i == keys.size() - 1) {//拼接时,不包括最后一个&字符
                prestr = prestr + key + "=" + URLEncoder.encode(value);
            } else {
                prestr = prestr + key + "=" + URLEncoder.encode(value) + "&";
            }
        }
        return prestr;
    }

    /**
     * 表单字符串转化成 hashMap
     *
     * @param orderinfo
     * @return
     */
    public static HashMap<String, String> form2Map(String orderinfo) {
        String listinfo[];
        HashMap<String, String> map = new HashMap<String, String>();
        listinfo = orderinfo.split("&");
        for (String s : listinfo) {
            String list[] = s.split("=");
            if (list.length > 1) {
                map.put(list[0], list[1]);
            }
        }
        return map;
    }

    /**
     * 对象转map,将字段转换为下划线形式
     *
     * @param obj
     * @return
     */
    public static Map<String, String> object2MapWithUnderline(Object obj) {
        Map<String, String> map = new HashMap<>();
        try {
            Class<?> clazz = obj.getClass();
            for (Field field : clazz.getDeclaredFields()) {
                field.setAccessible(true);
                String fieldName = field.getName();
                fieldName = CamelCaseUtil.toUnderlineName(fieldName);
                String value = field.get(obj) == null ? "" : String.valueOf(field.get(obj));
                map.put(fieldName, value);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return map;
    }

    /**
     * 表单字符串转化成 hashMap,将具有下划线的key转换为小驼峰
     * @param orderinfo,
     * @return
     */
    public static HashMap<String, String> form2MapWithCamelCase( String orderinfo) {
        String listinfo[];
        HashMap<String, String> map = new HashMap<String, String>();
        listinfo = orderinfo.split("&");
        for(String s : listinfo)
        {
            String list[]  = s.split("=");
            if(list.length>1)
            {
                map.put(CamelCaseUtil.toCamelCase(list[0]),list[1]);
            }
        }
        return map;
    }

    /**
     * map转对象
     *
     * @param obj
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> T mapToObject(Object obj, Class<T> clazz) {
        try {
            return objectMapper.readValue(serialize(obj), clazz);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static String serialize(Object obj) {
        if (obj == null) {
            return null;
        }
        if (obj.getClass() == String.class) {
            return (String) obj;
        }
        try {
            return objectMapper.writeValueAsString(obj);
        } catch (JsonProcessingException e) {
            return null;
        }
    }

}

MoneyUtil : 

import java.math.BigDecimal;

/**
 * @Auther: IT贱男
 * @Date: 2019/12/19 14:19
 * @Description: 订单金额处理
 */
public class MoneyUtil {

    /**
     * 元转分 符合ISO 4217标准的三位字母代码
     *
     * @param yuan
     * @return
     */
    public static Integer YuanToFen(Double yuan) {
        return BigDecimal.valueOf(yuan).movePointRight(2).intValue();
    }

    /**
     * 分转元
     *
     * @param fen
     * @return
     */
    public static Double FenToYuan(Integer fen) {
        return new BigDecimal(fen).movePointLeft(2).doubleValue();
    }

}

RandomUtil​​​​​​​ : 

/**
 * @Auther: IT贱男
 * @Date: 2019/12/19 13:49
 * @Description: 生成随机数
 */
public class RandomUtil {

    // 主要保证签名不可预测。我们推荐生成随机数算法如下:调用随机数函数生成,将得到的值转换为字符串。

    private static final String RANDOM_STR = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";

    private static final java.util.Random RANDOM = new java.util.Random();

    public static String getRandomStr() {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < 16; i++) {
            sb.append(RANDOM_STR.charAt(RANDOM.nextInt(RANDOM_STR.length())));
        }
        return sb.toString();
    }
}

XmlUtil​​​​​​​ : 

mport org.simpleframework.xml.Serializer;
import org.simpleframework.xml.core.Persister;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.Map;

/**
 * @Auther: IT贱男
 * @Date: 2019/12/19 15:20
 * @Description: xml处理工具类
 */
public class XmlUtil {

    /**
     * xml转对象
     *
     * @param xml
     * @param objClass
     * @return
     */
    public static Object toObject(String xml, Class objClass) {
        Serializer serializer = new Persister();
        try {
            return serializer.read(objClass, xml);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * xml 转 字符
     *
     * @param obj
     * @return
     */
    public static String toString(Object obj) {
        Serializer serializer = new Persister();
        StringWriter output = new StringWriter();
        try {
            serializer.write(obj, output);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return output.toString();
    }

    /**
     * xml 转 map
     *
     * @param strXML XML字符串
     * @return XML数据转换后的Map
     * @throws Exception
     */
    public static Map<String, String> toMap(String strXML) {
        try {
            Map<String, String> data = new HashMap<>();
            DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
            DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
            InputStream stream = new ByteArrayInputStream(strXML.getBytes("UTF-8"));
            org.w3c.dom.Document doc = documentBuilder.parse(stream);
            doc.getDocumentElement().normalize();
            NodeList nodeList = doc.getDocumentElement().getChildNodes();
            for (int idx = 0; idx < nodeList.getLength(); ++idx) {
                Node node = nodeList.item(idx);
                if (node.getNodeType() == Node.ELEMENT_NODE) {
                    org.w3c.dom.Element element = (org.w3c.dom.Element) node;
                    data.put(element.getNodeName(), element.getTextContent());
                }
            }
            try {
                stream.close();
            } catch (Exception ex) {
                // do nothing
            }
            return data;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

}
发布了152 篇原创文章 · 获赞 422 · 访问量 43万+

猜你喜欢

转载自blog.csdn.net/weixin_38111957/article/details/103662866