├── .gitignore ├── .gitignore.bak ├── README.md ├── logback.xml ├── pom.xml └── src └── main ├── java ├── alipay │ └── util │ │ └── UtilDate.java └── spring │ └── boot │ └── pay │ ├── App.java │ ├── PayInterface.java │ ├── common │ ├── .DS_Store │ ├── FastJsonUtil.java │ ├── HttpServRequestHelper.java │ ├── Log.java │ ├── MoneyFormat.java │ ├── RandomCodeHelper.java │ ├── RegexUtils.java │ ├── Util.java │ ├── XmlHelper.java │ ├── encrypt │ │ ├── .DS_Store │ │ ├── AliMD5.java │ │ ├── CryptAES.java │ │ ├── DES.java │ │ ├── ProtectData.java │ │ ├── RSA.java │ │ └── WXMD5.java │ └── http │ │ ├── HttpClientHelper.java │ │ └── HttpHelper.java │ ├── config │ ├── bean │ │ ├── JdbcConfiguration.java │ │ └── WebConfiguration.java │ ├── dictionary │ │ ├── Constant.java │ │ ├── PayType.java │ │ └── SignType.java │ └── properties │ │ ├── .DS_Store │ │ ├── AliPayProperties.java │ │ ├── JdbcProperties.java │ │ ├── SecurityProperties.java │ │ └── WxPayProperties.java │ ├── controller │ └── TradeController.java │ ├── dao │ ├── BaseDao.java │ └── TradeDao.java │ ├── framework │ ├── base │ │ ├── PayStatus.java │ │ ├── PayTask.java │ │ └── ResultModel.java │ ├── filter │ │ └── LogFilter.java │ ├── processor │ │ ├── Processor.java │ │ ├── ProcessorManager.java │ │ └── ProcessorResolver.java │ └── security │ │ ├── CheckIpEndpoint.java │ │ ├── CheckSignEndpoint.java │ │ └── SecurityManager.java │ ├── model │ ├── CouponInfo.java │ ├── KeInfo.java │ ├── OpenIdInfo.java │ ├── OrderInfo.java │ ├── Trade.java │ └── TradeForPost.java │ ├── processor │ └── TradeResultProcessor.java │ ├── resolver │ └── TradeProcessorResolver.java │ ├── service │ └── TradeService.java │ └── thirdparty │ ├── alipay │ ├── AliPayController.java │ ├── AliPayHelper.java │ ├── AliPayProcessor.java │ └── AliPayService.java │ └── wxpay │ ├── WxController.java │ ├── WxPayHelper.java │ ├── WxPayProcessor.java │ └── WxPayService.java └── resource ├── config ├── application.properties ├── db.properties └── whitelist.txt └── payconfig ├── ali └── alipayconfig.properties └── wx ├── jsapi.txt └── wxconfig.properties /.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | /.settings 3 | /.project 4 | /.classpath 5 | -------------------------------------------------------------------------------- /.gitignore.bak: -------------------------------------------------------------------------------- 1 | /target/ 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # spring-boot-pay 2 | 利用 srping boot实现的支付宝快捷支付,wap支付和微信扫码支付,公众号支付 3 | -------------------------------------------------------------------------------- /logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n 12 | 13 | 14 | 15 | 16 | 18 | 19 | 20 | ${LOG_HOME}/pay.%d{yyyy-MM-dd}.log 21 | 22 | 23 | 7 24 | 25 | 26 | 27 | %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n 28 | 29 | 30 | 32 | 10MB 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | spring.boot.pay 6 | spring-boot-pay 7 | 0.0.1-SNAPSHOT 8 | jar 9 | 10 | spring-boot-pay 11 | http://maven.apache.org 12 | 13 | 14 | UTF-8 15 | 16 | 17 | 18 | 19 | org.springframework.cloud 20 | spring-cloud-netflix 21 | 1.3.1.RELEASE 22 | pom 23 | import 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | com.alibaba.druid 33 | druid-wrapper 34 | 0.2.9 35 | 36 | 37 | com.alibaba 38 | fastjson 39 | 1.2.7 40 | 41 | 42 | 43 | org.apache.commons 44 | commons-lang3 45 | 3.4 46 | 47 | 48 | 49 | 50 | 51 | org.aspectj 52 | aspectjrt 53 | 1.8.10 54 | 55 | 56 | 57 | org.aspectj 58 | aspectjtools 59 | 1.8.10 60 | 61 | 62 | 63 | 64 | org.jdom 65 | jdom 66 | 1.1 67 | 68 | 69 | com.google.zxing 70 | core 71 | 3.0.1 72 | 73 | 74 | 75 | dom4j 76 | dom4j 77 | 78 | 79 | 80 | org.springframework.boot 81 | spring-boot-starter-web 82 | 83 | 84 | 85 | 86 | org.springframework 87 | spring-jdbc 88 | 89 | 90 | 91 | javax.inject 92 | javax.inject 93 | 94 | 95 | 96 | 97 | org.apache.directory.studio 98 | org.apache.commons.codec 99 | 1.8 100 | 101 | 102 | org.springframework.cloud 103 | spring-cloud-starter-hystrix 104 | 105 | 106 | org.springframework.cloud 107 | spring-cloud-starter-hystrix-dashboard 108 | 109 | 110 | 111 | 112 | 113 | org.springframework.boot 114 | spring-boot-maven-plugin 115 | 1.3.0.RELEASE 116 | 117 | 1.8 118 | 1.8 119 | utf-8 120 | exec 121 | spring.boot.pay.PayInterface 122 | 123 | 124 | 125 | 126 | repackage 127 | 128 | 129 | 130 | 131 | 132 | org.apache.maven.plugins 133 | maven-compiler-plugin 134 | 2.0.2 135 | 136 | 1.8 137 | 1.8 138 | utf-8 139 | 140 | 141 | 142 | 143 | 144 | 145 | -------------------------------------------------------------------------------- /src/main/java/alipay/util/UtilDate.java: -------------------------------------------------------------------------------- 1 | 2 | package alipay.util; 3 | 4 | import java.text.DateFormat; 5 | import java.text.SimpleDateFormat; 6 | import java.util.Date; 7 | import java.util.Random; 8 | 9 | /* * 10 | *类名:UtilDate 11 | *功能:自定义订单类 12 | *详细:工具类,可以用作获取系统日期、订单编号等 13 | *版本:3.3 14 | *日期:2012-08-17 15 | *说明: 16 | *以下代码只是为了方便商户测试而提供的样例代码,商户可以根据自己网站的需要,按照技术文档编写,并非一定要使用该代码。 17 | *该代码仅供学习和研究支付宝接口使用,只是提供一个参考。 18 | */ 19 | public class UtilDate { 20 | 21 | /** 年月日时分秒(无下划线) yyyyMMddHHmmss */ 22 | public static final String dtLong = "yyyyMMddHHmmss"; 23 | 24 | /** 完整时间 yyyy-MM-dd HH:mm:ss */ 25 | public static final String simple = "yyyy-MM-dd HH:mm:ss"; 26 | 27 | /** 年月日(无下划线) yyyyMMdd */ 28 | public static final String dtShort = "yyyyMMdd"; 29 | 30 | 31 | /** 32 | * 返回系统当前时间(精确到毫秒),作为一个唯一的订单编号 33 | * @return 34 | * 以yyyyMMddHHmmss为格式的当前系统时间 35 | */ 36 | public static String getOrderNum(){ 37 | Date date=new Date(); 38 | DateFormat df=new SimpleDateFormat(dtLong); 39 | return df.format(date); 40 | } 41 | 42 | /** 43 | * 获取系统当前日期(精确到毫秒),格式:yyyy-MM-dd HH:mm:ss 44 | * @return 45 | */ 46 | public static String getDateFormatter(){ 47 | Date date=new Date(); 48 | DateFormat df=new SimpleDateFormat(simple); 49 | return df.format(date); 50 | } 51 | 52 | /** 53 | * 获取系统当期年月日(精确到天),格式:yyyyMMdd 54 | * @return 55 | */ 56 | public static String getDate(){ 57 | Date date=new Date(); 58 | DateFormat df=new SimpleDateFormat(dtShort); 59 | return df.format(date); 60 | } 61 | 62 | /** 63 | * 产生随机的三位数 64 | * @return 65 | */ 66 | public static String getThree(){ 67 | Random rad=new Random(); 68 | return rad.nextInt(1000)+""; 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/spring/boot/pay/App.java: -------------------------------------------------------------------------------- 1 | package spring.boot.pay; 2 | 3 | /** 4 | * Hello world! 5 | * 6 | */ 7 | public class App 8 | { 9 | public static void main( String[] args ) 10 | { 11 | System.out.println( "Hello World!" ); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/spring/boot/pay/PayInterface.java: -------------------------------------------------------------------------------- 1 | package spring.boot.pay; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import org.springframework.boot.SpringApplication; 6 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 7 | import org.springframework.boot.autoconfigure.SpringBootApplication; 8 | import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; 9 | import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker; 10 | import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard; 11 | 12 | import spring.boot.pay.config.properties.AliPayProperties; 13 | import spring.boot.pay.config.properties.JdbcProperties; 14 | import spring.boot.pay.config.properties.SecurityProperties; 15 | import spring.boot.pay.config.properties.WxPayProperties; 16 | 17 | 18 | @SpringBootApplication 19 | @EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class}) 20 | @EnableCircuitBreaker 21 | @EnableHystrixDashboard 22 | public class PayInterface{ 23 | 24 | private static Logger logger = LoggerFactory.getLogger(PayInterface.class); 25 | 26 | public static void main(String[] args) throws Exception { 27 | 28 | initConfig(); 29 | 30 | logger.info("Config加载完毕~");// 31 | 32 | 33 | SpringApplication.run(PayInterface.class, args); 34 | } 35 | 36 | public static void initConfig() throws Exception{ 37 | 38 | SecurityProperties.init(); 39 | JdbcProperties.init(); 40 | WxPayProperties.init(); 41 | AliPayProperties.init(); 42 | } 43 | 44 | 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/spring/boot/pay/common/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softprog/spring-boot-pay/876a0dc720082756e78a542f5bc3251a4615578e/src/main/java/spring/boot/pay/common/.DS_Store -------------------------------------------------------------------------------- /src/main/java/spring/boot/pay/common/FastJsonUtil.java: -------------------------------------------------------------------------------- 1 | package spring.boot.pay.common; 2 | 3 | import java.time.LocalDateTime; 4 | 5 | import com.alibaba.fastjson.JSON; 6 | import com.alibaba.fastjson.JSONObject; 7 | 8 | public final class FastJsonUtil { 9 | 10 | public static final String objToString (T obj){ 11 | 12 | return JSON.toJSONString(obj); 13 | } 14 | 15 | public static final T stringToObj (String data,T obj){ 16 | 17 | return (T)JSON.parseObject(data, obj.getClass()); 18 | } 19 | 20 | public static final JSONObject stringToJSONObj (String data){ 21 | 22 | return JSON.parseObject(data); 23 | } 24 | 25 | public static class VO { 26 | 27 | private LocalDateTime date; 28 | 29 | public LocalDateTime getDate() { 30 | return date; 31 | } 32 | 33 | public void setDate(LocalDateTime date) { 34 | this.date = date; 35 | } 36 | 37 | } 38 | } 39 | 40 | 41 | -------------------------------------------------------------------------------- /src/main/java/spring/boot/pay/common/HttpServRequestHelper.java: -------------------------------------------------------------------------------- 1 | package spring.boot.pay.common; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.IOException; 5 | import java.util.Map; 6 | import java.util.SortedMap; 7 | import java.util.TreeMap; 8 | 9 | import javax.servlet.http.HttpServletRequest; 10 | 11 | public class HttpServRequestHelper { 12 | 13 | public static String getRequestBody(HttpServletRequest request) throws IOException { 14 | StringBuilder sb = new StringBuilder(); 15 | BufferedReader reader = request.getReader(); 16 | String inputLine = reader.readLine(); 17 | while (inputLine != null) { 18 | sb.append(inputLine); 19 | inputLine = reader.readLine(); 20 | } 21 | reader.close(); 22 | return sb.toString(); 23 | } 24 | 25 | 26 | public static SortedMap genSortedMap(HttpServletRequest request){ 27 | 28 | SortedMap params = new TreeMap(); 29 | Map requestParams = request.getParameterMap(); 30 | for (String name: requestParams.keySet()) { 31 | 32 | String[] values = requestParams.get(name); 33 | String valueStr = ""; 34 | for (int i = 0; i < values.length; i++) { 35 | valueStr = (i == values.length - 1) ? valueStr + values[i] 36 | : valueStr + values[i] + ","; 37 | } 38 | params.put(name, valueStr); 39 | } 40 | return params; 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/spring/boot/pay/common/Log.java: -------------------------------------------------------------------------------- 1 | package spring.boot.pay.common; 2 | 3 | import org.slf4j.Logger; 4 | 5 | 6 | /** 7 | * User: rizenguo 8 | * Date: 2014/11/12 9 | * Time: 14:32 10 | */ 11 | public class Log { 12 | 13 | public static final String LOG_TYPE_TRACE = "logTypeTrace"; 14 | public static final String LOG_TYPE_DEBUG = "logTypeDebug"; 15 | public static final String LOG_TYPE_INFO = "logTypeInfo"; 16 | public static final String LOG_TYPE_WARN = "logTypeWarn"; 17 | public static final String LOG_TYPE_ERROR = "logTypeError"; 18 | 19 | //打印日志 20 | private Logger logger; 21 | 22 | public Log(Logger log){ 23 | logger = log; 24 | } 25 | 26 | public void t(String s){ 27 | logger.trace(s); 28 | } 29 | 30 | public void d(String s){ 31 | logger.debug(s); 32 | } 33 | 34 | public void i(String s){ 35 | logger.info(s); 36 | } 37 | 38 | public void w(String s){ 39 | logger.warn(s); 40 | } 41 | 42 | public void e(String s){ 43 | logger.error(s); 44 | } 45 | 46 | public void log(String type,String s){ 47 | if(type.equals(Log.LOG_TYPE_TRACE)){ 48 | t(s); 49 | }else if(type.equals(Log.LOG_TYPE_DEBUG)){ 50 | d(s); 51 | }else if(type.equals(Log.LOG_TYPE_INFO)){ 52 | i(s); 53 | }else if(type.equals(Log.LOG_TYPE_WARN)){ 54 | w(s); 55 | }else if(type.equals(Log.LOG_TYPE_ERROR)){ 56 | e(s); 57 | } 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/spring/boot/pay/common/MoneyFormat.java: -------------------------------------------------------------------------------- 1 | package spring.boot.pay.common; 2 | 3 | import java.math.BigDecimal; 4 | 5 | public class MoneyFormat { 6 | 7 | /** 8 | * 将分转化为元 9 | */ 10 | public static final String fen2yuan(BigDecimal money){ 11 | int yuan = money.intValue()/100; 12 | int fen = money.intValue() %100; 13 | 14 | if(fen<10){ 15 | return yuan+".0"+fen; 16 | } 17 | 18 | return yuan+"."+fen; 19 | } 20 | 21 | /** 22 | * 将元转化为分 23 | */ 24 | public static int yuan2fen(String money) { 25 | int yuan = 0; 26 | int fen = 0; 27 | int index = money.indexOf("."); 28 | if(index != -1){ 29 | yuan = Integer.parseInt(money.substring(0,index)); 30 | String fenStr = money.substring(index+1); 31 | fenStr = fenStr.length()==1 ? fenStr+"0" : fenStr ; 32 | fen = Integer.parseInt(fenStr); 33 | }else { 34 | yuan = Integer.parseInt(money); 35 | } 36 | return yuan*100 + fen; 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/spring/boot/pay/common/RandomCodeHelper.java: -------------------------------------------------------------------------------- 1 | package spring.boot.pay.common; 2 | 3 | import java.util.concurrent.ThreadLocalRandom; 4 | 5 | public class RandomCodeHelper { 6 | 7 | /** 8 | * 随机生成指定长度字符串 9 | */ 10 | public static String randomCharCode(int length) { 11 | StringBuilder builder = new StringBuilder(length); 12 | for (int i = 0; i < length; i++) { 13 | builder.append((char) (ThreadLocalRandom.current().nextInt(33, 128))); 14 | } 15 | String code = builder.toString(); 16 | return code; 17 | } 18 | 19 | /** 20 | * 随机生成一串指定长度的数字 21 | */ 22 | public static String randomNumCode(int length) { 23 | StringBuilder builder = new StringBuilder(length); 24 | for (int i = 0; i < length; i++) { 25 | builder.append( ThreadLocalRandom.current().nextInt(0,10)); 26 | } 27 | String code = builder.toString(); 28 | return code; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/spring/boot/pay/common/RegexUtils.java: -------------------------------------------------------------------------------- 1 | package spring.boot.pay.common; 2 | 3 | 4 | import java.util.regex.Pattern; 5 | 6 | /** 7 | * 正则工具类 8 | * 提供验证邮箱、手机号、电话号码、身份证号码、数字等方法 9 | */ 10 | public final class RegexUtils { 11 | 12 | /** 13 | * 验证Email 14 | * @param email email地址,格式:zhangsan@sina.com,zhangsan@xxx.com.cn,xxx代表邮件服务商 15 | * @return 验证成功返回true,验证失败返回false 16 | */ 17 | public static boolean checkEmail(String email) { 18 | String regex = "\\w+@\\w+\\.[a-z]+(\\.[a-z]+)?"; 19 | return Pattern.matches(regex, email); 20 | } 21 | 22 | /** 23 | * 验证身份证号码 24 | * @param idCard 居民身份证号码15位或18位,最后一位可能是数字或字母 25 | * @return 验证成功返回true,验证失败返回false 26 | */ 27 | public static boolean checkIdCard(String idCard) { 28 | String regex = "[1-9]\\d{13,16}[a-zA-Z0-9]{1}"; 29 | return Pattern.matches(regex,idCard); 30 | } 31 | 32 | /** 33 | * 验证手机号码(支持国际格式,+86135xxxx...(中国内地),+00852137xxxx...(中国香港)) 34 | * @param mobile 移动、联通、电信运营商的号码段 35 | *

移动的号段:134(0-8)、135、136、137、138、139、147(预计用于TD上网卡) 36 | *、150、151、152、157(TD专用)、158、159、187(未启用)、188(TD专用)

37 | *

联通的号段:130、131、132、155、156(世界风专用)、185(未启用)、186(3g)

38 | *

电信的号段:133、153、180(未启用)、189

39 | * @return 验证成功返回true,验证失败返回false 40 | */ 41 | public static boolean checkMobile(String mobile) { 42 | String regex = "(\\+\\d+)?1[3458]\\d{9}$"; 43 | return Pattern.matches(regex,mobile); 44 | } 45 | 46 | /** 47 | * 验证固定电话号码 48 | * @param phone 电话号码,格式:国家(地区)电话代码 + 区号(城市代码) + 电话号码,如:+8602085588447 49 | *

国家(地区) 代码 :标识电话号码的国家(地区)的标准国家(地区)代码。它包含从 0 到 9 的一位或多位数字, 50 | * 数字之后是空格分隔的国家(地区)代码。

51 | *

区号(城市代码):这可能包含一个或多个从 0 到 9 的数字,地区或城市代码放在圆括号—— 52 | * 对不使用地区或城市代码的国家(地区),则省略该组件。

53 | *

电话号码:这包含从 0 到 9 的一个或多个数字

54 | * @return 验证成功返回true,验证失败返回false 55 | */ 56 | public static boolean checkPhone(String phone) { 57 | String regex = "(\\+\\d+)?(\\d{3,4}\\-?)?\\d{7,8}$"; 58 | return Pattern.matches(regex, phone); 59 | } 60 | 61 | /** 62 | * 验证整数(正整数和负整数) 63 | * @param digit 一位或多位0-9之间的整数 64 | * @return 验证成功返回true,验证失败返回false 65 | */ 66 | public static boolean checkDigit(String digit) { 67 | String regex = "\\-?[1-9]\\d+"; 68 | return Pattern.matches(regex,digit); 69 | } 70 | 71 | /** 72 | * 验证整数和浮点数(正负整数和正负浮点数) 73 | * @param decimals 一位或多位0-9之间的浮点数,如:1.23,233.30 74 | * @return 验证成功返回true,验证失败返回false 75 | */ 76 | public static boolean checkDecimals(String decimals) { 77 | String regex = "\\-?[1-9]\\d+(\\.\\d+)?"; 78 | return Pattern.matches(regex,decimals); 79 | } 80 | 81 | /** 82 | * 验证空白字符 83 | * @param blankSpace 空白字符,包括:空格、\t、\n、\r、\f、\x0B 84 | * @return 验证成功返回true,验证失败返回false 85 | */ 86 | public static boolean checkBlankSpace(String blankSpace) { 87 | String regex = "\\s+"; 88 | return Pattern.matches(regex,blankSpace); 89 | } 90 | 91 | /** 92 | * 验证中文 93 | * @param chinese 中文字符 94 | * @return 验证成功返回true,验证失败返回false 95 | */ 96 | public static boolean checkChinese(String chinese) { 97 | String regex = "^[\u4E00-\u9FA5]+$"; 98 | return Pattern.matches(regex,chinese); 99 | } 100 | 101 | /** 102 | * 验证日期(年月日) 103 | * @param birthday 日期,格式:1992-09-03,或1992.09.03 104 | * @return 验证成功返回true,验证失败返回false 105 | */ 106 | public static boolean checkBirthday(String birthday) { 107 | String regex = "[1-9]{4}([-./])\\d{1,2}\\1\\d{1,2}"; 108 | return Pattern.matches(regex,birthday); 109 | } 110 | 111 | /** 112 | * 验证URL地址 113 | * @param url 格式:http://blog.csdn.net:80/xyang81/article/details/7705960? 或 http://www.csdn.net:80 114 | * @return 验证成功返回true,验证失败返回false 115 | */ 116 | public static boolean checkURL(String url) { 117 | String regex = "(https?://(w{3}\\.)?)?\\w+\\.\\w+(\\.[a-zA-Z]+)*(:\\d{1,5})?(/\\w*)*(\\??(.+=.*)?(&.+=.*)?)?"; 118 | return Pattern.matches(regex, url); 119 | } 120 | 121 | /** 122 | * 匹配中国邮政编码 123 | * @param postcode 邮政编码 124 | * @return 验证成功返回true,验证失败返回false 125 | */ 126 | public static boolean checkPostcode(String postcode) { 127 | String regex = "[1-9]\\d{5}"; 128 | return Pattern.matches(regex, postcode); 129 | } 130 | 131 | /** 132 | * 匹配IP地址(简单匹配,格式,如:192.168.1.1,127.0.0.1,没有匹配IP段的大小) 133 | * @param ipAddress IPv4标准地址 134 | * @return 验证成功返回true,验证失败返回false 135 | */ 136 | public static boolean checkIpAddress(String ipAddress) { 137 | String regex = "[1-9](\\d{1,2})?\\.(0|([1-9](\\d{1,2})?))\\.(0|([1-9](\\d{1,2})?))\\.(0|([1-9](\\d{1,2})?))"; 138 | return Pattern.matches(regex, ipAddress); 139 | } 140 | 141 | } -------------------------------------------------------------------------------- /src/main/java/spring/boot/pay/common/Util.java: -------------------------------------------------------------------------------- 1 | package spring.boot.pay.common; 2 | 3 | import java.awt.image.BufferedImage; 4 | import java.io.ByteArrayInputStream; 5 | import java.io.ByteArrayOutputStream; 6 | import java.io.FileInputStream; 7 | import java.io.IOException; 8 | import java.io.InputStream; 9 | import java.lang.reflect.Field; 10 | import java.util.HashMap; 11 | import java.util.Map; 12 | import java.util.Properties; 13 | 14 | import javax.imageio.ImageIO; 15 | import javax.servlet.http.HttpServletResponse; 16 | 17 | import org.apache.commons.codec.binary.Base64; 18 | import org.apache.commons.lang3.StringUtils; 19 | import org.slf4j.LoggerFactory; 20 | 21 | import com.google.zxing.BarcodeFormat; 22 | import com.google.zxing.EncodeHintType; 23 | import com.google.zxing.MultiFormatWriter; 24 | import com.google.zxing.WriterException; 25 | import com.google.zxing.common.BitMatrix; 26 | 27 | import spring.boot.pay.config.dictionary.Constant; 28 | 29 | public class Util { 30 | 31 | // 打log用 32 | private static Log logger = new Log(LoggerFactory.getLogger(Util.class)); 33 | 34 | /** 35 | * 通过反射的方式遍历对象的属性和属性值,方便调试 36 | * 37 | * @param o 38 | * 要遍历的对象 39 | * @throws Exception 40 | */ 41 | public static void reflect(Object o) throws Exception { 42 | Class cls = o.getClass(); 43 | Field[] fields = cls.getDeclaredFields(); 44 | for (int i = 0; i < fields.length; i++) { 45 | Field f = fields[i]; 46 | f.setAccessible(true); 47 | Util.log(f.getName() + " -> " + f.get(o)); 48 | } 49 | } 50 | 51 | public static byte[] readInput(InputStream in) throws IOException { 52 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 53 | int len = 0; 54 | byte[] buffer = new byte[1024]; 55 | while ((len = in.read(buffer)) > 0) { 56 | out.write(buffer, 0, len); 57 | } 58 | out.close(); 59 | in.close(); 60 | return out.toByteArray(); 61 | } 62 | 63 | public static String inputStreamToString(InputStream is) throws IOException { 64 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 65 | int i; 66 | while ((i = is.read()) != -1) { 67 | baos.write(i); 68 | } 69 | return baos.toString(); 70 | } 71 | 72 | public static InputStream getStringStream(String sInputString) { 73 | ByteArrayInputStream tInputStringStream = null; 74 | if (sInputString != null && !sInputString.trim().equals("")) { 75 | tInputStringStream = new ByteArrayInputStream(sInputString.getBytes()); 76 | } 77 | return tInputStringStream; 78 | } 79 | 80 | public static String getStringFromMap(Map map, String key, String defaultValue) { 81 | if (key == "" || key == null) { 82 | return defaultValue; 83 | } 84 | String result = (String) map.get(key); 85 | if (result == null) { 86 | return defaultValue; 87 | } else { 88 | return result; 89 | } 90 | } 91 | 92 | public static int getIntFromMap(Map map, String key) { 93 | if (key == "" || key == null) { 94 | return 0; 95 | } 96 | if (map.get(key) == null) { 97 | return 0; 98 | } 99 | return Integer.parseInt((String) map.get(key)); 100 | } 101 | public static String GetImageStr(String path) 102 | {//将图片文件转化为字节数组字符串,并对其进行Base64编码处理 103 | String imgFile = path;//待处理的图片 104 | InputStream in = null; 105 | byte[] data = null; 106 | //读取图片字节数组 107 | try 108 | { 109 | in = new FileInputStream(imgFile); 110 | data = new byte[in.available()]; 111 | in.read(data); 112 | in.close(); 113 | } 114 | catch (IOException e) 115 | { 116 | e.printStackTrace(); 117 | } 118 | //对字节数组Base64编码 119 | 120 | return Base64.encodeBase64String(data);//返回Base64编码过的字节数组字符串 121 | } 122 | /** 123 | * 打log接口 124 | * 125 | * @param log 126 | * 要打印的log字符串 127 | * @return 返回log 128 | */ 129 | public static String log(Object log) { 130 | logger.i(log.toString()); 131 | // System.out.println(log); 132 | return log.toString(); 133 | } 134 | 135 | /** 136 | * 读取本地的xml数据,一般用来自测用 137 | * 138 | * @param localPath 139 | * 本地xml文件路径 140 | * @return 读到的xml字符串 141 | */ 142 | public static final String getLocalXMLString(final String localPath) throws IOException { 143 | return Util.inputStreamToString(Util.class.getResourceAsStream(localPath)); 144 | } 145 | 146 | public static final Properties getProperties(final String propertiesPath) { 147 | 148 | InputStream in= Object.class.getResourceAsStream(propertiesPath); 149 | Properties properties = new Properties(); 150 | 151 | if (in!=null) { 152 | 153 | try { 154 | 155 | properties.load(in); 156 | 157 | } catch (IOException e) { 158 | // TODO Auto-generated catch block 159 | e.printStackTrace(); 160 | } finally { 161 | 162 | try { 163 | in.close(); 164 | } catch (IOException e) { 165 | // TODO Auto-generated catch block 166 | e.printStackTrace(); 167 | } 168 | } 169 | 170 | } 171 | return properties; 172 | } 173 | public static final String getFileData(final String path) { 174 | 175 | InputStream fis= null; 176 | int i = 0; 177 | char c; 178 | 179 | 180 | try{ 181 | // create new file input stream 182 | fis= Object.class.getResourceAsStream(path); 183 | 184 | //fis = new FileInputStream( Thread.currentThread().getContextClassLoader().getResource(path).getPath()); 185 | byte[] bs = new byte[fis.available()]; 186 | // read bytes to the buffer 187 | fis.read(bs); 188 | 189 | return new String(bs,Constant.CHARSET); 190 | 191 | }catch(Exception ex){ 192 | // if any error occurs 193 | ex.printStackTrace(); 194 | }finally{ 195 | 196 | // releases all system resources from the streams 197 | if(fis!=null){ 198 | try { 199 | fis.close(); 200 | } catch (IOException e) { 201 | // TODO Auto-generated catch block 202 | e.printStackTrace(); 203 | } 204 | } 205 | } 206 | 207 | return ""; 208 | } 209 | 210 | /** 211 | * 生成二维码图片 不存储 直接以流的形式输出到页面 212 | * @param content 213 | * @param response 214 | */ 215 | @SuppressWarnings({ "unchecked", "rawtypes" }) 216 | public static void encodeQrcode(String content,HttpServletResponse response){ 217 | if(StringUtils.isBlank(content)) 218 | return; 219 | MultiFormatWriter multiFormatWriter = new MultiFormatWriter(); 220 | Map hints = new HashMap(); 221 | hints.put(EncodeHintType.CHARACTER_SET, "UTF-8"); //设置字符集编码类型 222 | BitMatrix bitMatrix = null; 223 | try { 224 | bitMatrix = multiFormatWriter.encode(content, BarcodeFormat.QR_CODE, 300, 300,hints); 225 | BufferedImage image = toBufferedImage(bitMatrix); 226 | //输出二维码图片流 227 | try { 228 | ImageIO.write(image, "png", response.getOutputStream()); 229 | } catch (IOException e) { 230 | // TODO Auto-generated catch block 231 | logger.e(e.getMessage()); 232 | } 233 | } catch (WriterException e1) { 234 | // TODO Auto-generated catch block 235 | logger.e(e1.getMessage()); 236 | } 237 | } 238 | private static final int BLACK = 0xFF000000; 239 | private static final int WHITE = 0xFFFFFFFF; 240 | 241 | public static BufferedImage toBufferedImage(BitMatrix matrix) { 242 | int width = matrix.getWidth(); 243 | int height = matrix.getHeight(); 244 | BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); 245 | for (int x = 0; x < width; x++) { 246 | for (int y = 0; y < height; y++) { 247 | image.setRGB(x, y, matrix.get(x, y) ? BLACK : WHITE); 248 | } 249 | } 250 | return image; 251 | } 252 | 253 | } 254 | -------------------------------------------------------------------------------- /src/main/java/spring/boot/pay/common/XmlHelper.java: -------------------------------------------------------------------------------- 1 | package spring.boot.pay.common; 2 | 3 | import java.io.IOException; 4 | import java.io.StringReader; 5 | import java.util.List; 6 | import java.util.SortedMap; 7 | import java.util.TreeMap; 8 | 9 | import org.jdom.Document; 10 | import org.jdom.Element; 11 | import org.jdom.JDOMException; 12 | import org.jdom.input.SAXBuilder; 13 | 14 | public class XmlHelper { 15 | 16 | 17 | /** 18 | * 解析xml,返回第一级元素键值对。如果第一级元素有子节点,则此节点的值是子节点的xml数据。 19 | */ 20 | public static SortedMap parseXmlToMap(String strXml){ 21 | if (null == strXml || "".equals(strXml)) { 22 | return new TreeMap(); 23 | } 24 | 25 | SortedMap m = new TreeMap(); 26 | 27 | StringReader read = new StringReader(strXml); 28 | SAXBuilder builder = new SAXBuilder(); 29 | Document doc; 30 | try { 31 | doc = builder.build(read); 32 | } catch (JDOMException e) { 33 | throw new RuntimeException(e); 34 | } catch (IOException e) { 35 | throw new RuntimeException(e); 36 | } 37 | Element root = doc.getRootElement(); 38 | List list = root.getChildren(); 39 | for (Element e : list) { 40 | String k = e.getName(); 41 | String v = e.getChildren().isEmpty() ? e.getTextNormalize() : getChildrenText(e.getChildren()); 42 | m.put(k, v); 43 | } 44 | 45 | //关闭流 46 | read.close(); 47 | 48 | return m; 49 | } 50 | 51 | /** 52 | * 获取子结点的xml 53 | * @param children 54 | * @return String 55 | */ 56 | private static String getChildrenText(List children) { 57 | StringBuilder sb = new StringBuilder(); 58 | if (!children.isEmpty()) { 59 | for (Element e : children) { 60 | String name = e.getName(); 61 | String value = e.getTextNormalize(); 62 | List list = e.getChildren(); 63 | sb.append("<").append(name).append(">"); 64 | if (!list.isEmpty()) { 65 | sb.append(getChildrenText(list)); 66 | } 67 | sb.append(value); 68 | sb.append(""); 69 | } 70 | } 71 | 72 | return sb.toString(); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/spring/boot/pay/common/encrypt/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softprog/spring-boot-pay/876a0dc720082756e78a542f5bc3251a4615578e/src/main/java/spring/boot/pay/common/encrypt/.DS_Store -------------------------------------------------------------------------------- /src/main/java/spring/boot/pay/common/encrypt/AliMD5.java: -------------------------------------------------------------------------------- 1 | package spring.boot.pay.common.encrypt; 2 | 3 | 4 | import java.io.UnsupportedEncodingException; 5 | import java.security.MessageDigest; 6 | import java.security.SignatureException; 7 | 8 | import org.apache.commons.codec.binary.Hex; 9 | import org.apache.commons.codec.digest.DigestUtils; 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | 13 | public final class AliMD5 { 14 | 15 | private static Logger logger = LoggerFactory.getLogger(AliMD5.class); 16 | 17 | private AliMD5() { 18 | } 19 | 20 | 21 | public static String encode(String origin, String charsetName) { 22 | String resultString = null; 23 | try { 24 | resultString = origin; 25 | MessageDigest md = MessageDigest.getInstance("MD5"); 26 | if (charsetName == null || "".equals(charsetName)) 27 | resultString = Hex.encodeHexString(md.digest(resultString 28 | .getBytes())); 29 | else 30 | resultString = Hex.encodeHexString(md.digest(resultString 31 | .getBytes(charsetName))); 32 | } catch (Exception e) { 33 | logger.error("md5 encode fail", e); 34 | } 35 | return resultString; 36 | } 37 | 38 | public static String encode(byte[] data) { 39 | 40 | String resultString = ""; 41 | try { 42 | MessageDigest md = MessageDigest.getInstance("MD5"); 43 | resultString = Hex.encodeHexString(md.digest(data)); 44 | } catch (Exception e) { 45 | logger.error("md5 encode fail", e); 46 | } 47 | 48 | return resultString; 49 | } 50 | 51 | /** 52 | * 签名字符串 53 | * @param text 需要签名的字符串 54 | * @param sign 签名结果 55 | * @param key 密钥 56 | * @param input_charset 编码格式 57 | * @return 签名结果 58 | */ 59 | public static boolean verify(String text, String sign, String key, String input_charset) { 60 | text = text + key; 61 | String mysign = DigestUtils.md5Hex(getContentBytes(text, input_charset)); 62 | if(mysign.equals(sign)) { 63 | return true; 64 | } 65 | else { 66 | return false; 67 | } 68 | } 69 | 70 | /** 71 | * @param content 72 | * @param charset 73 | * @return 74 | * @throws SignatureException 75 | * @throws UnsupportedEncodingException 76 | */ 77 | private static byte[] getContentBytes(String content, String charset) { 78 | if (charset == null || "".equals(charset)) { 79 | return content.getBytes(); 80 | } 81 | try { 82 | return content.getBytes(charset); 83 | } catch (UnsupportedEncodingException e) { 84 | throw new RuntimeException("MD5签名过程中出现错误,指定的编码集不对,您目前指定的编码集是:" + charset); 85 | } 86 | } 87 | /** 88 | * 签名字符串 89 | * @param text 需要签名的字符串 90 | * @param key 密钥 91 | * @param input_charset 编码格式 92 | * @return 签名结果 93 | */ 94 | public static String sign(String text, String key, String input_charset) { 95 | text = text + key; 96 | return DigestUtils.md5Hex(getContentBytes(text, input_charset)); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/main/java/spring/boot/pay/common/encrypt/CryptAES.java: -------------------------------------------------------------------------------- 1 | package spring.boot.pay.common.encrypt; 2 | 3 | import java.security.Key; 4 | 5 | import javax.crypto.Cipher; 6 | import javax.crypto.spec.SecretKeySpec; 7 | 8 | import org.apache.commons.codec.binary.Base64; 9 | 10 | public class CryptAES { 11 | 12 | private static final String AESTYPE ="AES/ECB/PKCS5Padding"; 13 | 14 | public static String AES_Encrypt(String keyStr, String plainText) { 15 | byte[] encrypt = null; 16 | try{ 17 | Key key = generateKey(keyStr); 18 | Cipher cipher = Cipher.getInstance(AESTYPE); 19 | cipher.init(Cipher.ENCRYPT_MODE, key); 20 | 21 | encrypt = cipher.doFinal(plainText.getBytes()); 22 | }catch(Exception e){ 23 | e.printStackTrace(); 24 | } 25 | return new String(Base64.encodeBase64(encrypt)); 26 | } 27 | 28 | public static String AES_Decrypt(String keyStr, String encryptData) { 29 | byte[] decrypt = null; 30 | try{ 31 | Key key = generateKey(keyStr); 32 | Cipher cipher = Cipher.getInstance(AESTYPE); 33 | cipher.init(Cipher.DECRYPT_MODE, key); 34 | decrypt = cipher.doFinal(Base64.decodeBase64(encryptData)); 35 | }catch(Exception e){ 36 | e.printStackTrace(); 37 | } 38 | return new String(decrypt).trim(); 39 | } 40 | 41 | private static Key generateKey(String key)throws Exception{ 42 | try{ 43 | 44 | byte[] raw = Base64.decodeBase64(key); 45 | SecretKeySpec keySpec = new SecretKeySpec(raw, "AES"); 46 | return keySpec; 47 | }catch(Exception e){ 48 | e.printStackTrace(); 49 | throw e; 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/spring/boot/pay/common/encrypt/DES.java: -------------------------------------------------------------------------------- 1 | package spring.boot.pay.common.encrypt; 2 | 3 | 4 | import java.security.Key; 5 | 6 | import javax.crypto.Cipher; 7 | import javax.crypto.KeyGenerator; 8 | import javax.crypto.SecretKey; 9 | import javax.crypto.SecretKeyFactory; 10 | import javax.crypto.spec.DESKeySpec; 11 | 12 | /** 13 | * DES Coder
14 | * secret key length: 56 bit, default: 56 bit
15 | * mode: ECB/CBC/PCBC/CTR/CTS/CFB/CFB8 to CFB128/OFB/OBF8 to OFB128
16 | * padding: Nopadding/PKCS5Padding/ISO10126Padding/ 17 | * @author Aub 18 | * 19 | */ 20 | public class DES { 21 | 22 | /** 23 | * 密钥算法 24 | */ 25 | private static final String KEY_ALGORITHM = "DES"; 26 | 27 | private static final String DEFAULT_CIPHER_ALGORITHM = "DES/ECB/PKCS5Padding"; 28 | 29 | 30 | /** 31 | * 初始化密钥 32 | * 33 | * @return byte[] 密钥 34 | * @throws Exception 35 | */ 36 | public static byte[] initSecretKey() throws Exception{ 37 | //返回生成指定算法的秘密密钥的 KeyGenerator 对象 38 | KeyGenerator kg = KeyGenerator.getInstance(KEY_ALGORITHM); 39 | //初始化此密钥生成器,使其具有确定的密钥大小 40 | kg.init(56); 41 | //生成一个密钥 42 | SecretKey secretKey = kg.generateKey(); 43 | return secretKey.getEncoded(); 44 | } 45 | 46 | /** 47 | * 转换密钥 48 | * 49 | * @param key 二进制密钥 50 | * @return Key 密钥 51 | * @throws Exception 52 | */ 53 | private static Key toKey(byte[] key) throws Exception{ 54 | //实例化DES密钥规则 55 | DESKeySpec dks = new DESKeySpec(key); 56 | //实例化密钥工厂 57 | SecretKeyFactory skf = SecretKeyFactory.getInstance(KEY_ALGORITHM); 58 | //生成密钥 59 | SecretKey secretKey = skf.generateSecret(dks); 60 | return secretKey; 61 | } 62 | 63 | /** 64 | * 加密 65 | * 66 | * @param data 待加密数据 67 | * @param key 密钥 68 | * @return byte[] 加密数据 69 | * @throws Exception 70 | */ 71 | public static byte[] encrypt(byte[] data,Key key) throws Exception{ 72 | return encrypt(data, key,DEFAULT_CIPHER_ALGORITHM); 73 | } 74 | 75 | /** 76 | * 加密 77 | * 78 | * @param data 待加密数据 79 | * @param key 二进制密钥 80 | * @return byte[] 加密数据 81 | * @throws Exception 82 | */ 83 | public static byte[] encrypt(byte[] data,byte[] key) throws Exception{ 84 | return encrypt(data, key,DEFAULT_CIPHER_ALGORITHM); 85 | } 86 | 87 | 88 | /** 89 | * 加密 90 | * 91 | * @param data 待加密数据 92 | * @param key 二进制密钥 93 | * @param cipherAlgorithm 加密算法/工作模式/填充方式 94 | * @return byte[] 加密数据 95 | * @throws Exception 96 | */ 97 | public static byte[] encrypt(byte[] data,byte[] key,String cipherAlgorithm) throws Exception{ 98 | //还原密钥 99 | Key k = toKey(key); 100 | return encrypt(data, k, cipherAlgorithm); 101 | } 102 | 103 | /** 104 | * 加密 105 | * 106 | * @param data 待加密数据 107 | * @param key 密钥 108 | * @param cipherAlgorithm 加密算法/工作模式/填充方式 109 | * @return byte[] 加密数据 110 | * @throws Exception 111 | */ 112 | public static byte[] encrypt(byte[] data,Key key,String cipherAlgorithm) throws Exception{ 113 | //实例化 114 | Cipher cipher = Cipher.getInstance(cipherAlgorithm); 115 | //使用密钥初始化,设置为加密模式 116 | cipher.init(Cipher.ENCRYPT_MODE, key); 117 | //执行操作 118 | return cipher.doFinal(data); 119 | } 120 | 121 | 122 | 123 | /** 124 | * 解密 125 | * 126 | * @param data 待解密数据 127 | * @param key 二进制密钥 128 | * @return byte[] 解密数据 129 | * @throws Exception 130 | */ 131 | public static byte[] decrypt(byte[] data,byte[] key) throws Exception{ 132 | return decrypt(data, key,DEFAULT_CIPHER_ALGORITHM); 133 | } 134 | 135 | /** 136 | * 解密 137 | * 138 | * @param data 待解密数据 139 | * @param key 密钥 140 | * @return byte[] 解密数据 141 | * @throws Exception 142 | */ 143 | public static byte[] decrypt(byte[] data,Key key) throws Exception{ 144 | return decrypt(data, key,DEFAULT_CIPHER_ALGORITHM); 145 | } 146 | 147 | /** 148 | * 解密 149 | * 150 | * @param data 待解密数据 151 | * @param key 二进制密钥 152 | * @param cipherAlgorithm 加密算法/工作模式/填充方式 153 | * @return byte[] 解密数据 154 | * @throws Exception 155 | */ 156 | public static byte[] decrypt(byte[] data,byte[] key,String cipherAlgorithm) throws Exception{ 157 | //还原密钥 158 | Key k = toKey(key); 159 | return decrypt(data, k, cipherAlgorithm); 160 | } 161 | 162 | /** 163 | * 解密 164 | * 165 | * @param data 待解密数据 166 | * @param key 密钥 167 | * @param cipherAlgorithm 加密算法/工作模式/填充方式 168 | * @return byte[] 解密数据 169 | * @throws Exception 170 | */ 171 | public static byte[] decrypt(byte[] data,Key key,String cipherAlgorithm) throws Exception{ 172 | //实例化 173 | Cipher cipher = Cipher.getInstance(cipherAlgorithm); 174 | //使用密钥初始化,设置为解密模式 175 | cipher.init(Cipher.DECRYPT_MODE, key); 176 | //执行操作 177 | return cipher.doFinal(data); 178 | } 179 | 180 | // private static String showByteArray(byte[] data){ 181 | // if(null == data){ 182 | // return null; 183 | // } 184 | // StringBuilder sb = new StringBuilder("{"); 185 | // for(byte b:data){ 186 | // sb.append(b).append(","); 187 | // } 188 | // sb.deleteCharAt(sb.length()-1); 189 | // sb.append("}"); 190 | // return sb.toString(); 191 | // } 192 | // 193 | // public static void main(String[] args) throws Exception { 194 | // byte[] key = initSecretKey(); 195 | //// byte[] key = "12345678".getBytes(); 196 | // System.out.println("key:"+ showByteArray(key)); 197 | // 198 | // Key k = toKey(key); 199 | // 200 | // String data ="DES数据"; 201 | // System.out.println("加密前数据: string:"+data); 202 | // System.out.println("加密前数据: byte[]:"+showByteArray(data.getBytes())); 203 | // System.out.println(); 204 | // byte[] encryptData = encrypt(data.getBytes(), k); 205 | // System.out.println("加密后数据: byte[]:"+showByteArray(encryptData)); 206 | // System.out.println("加密后数据: hexStr:"+ Hex.encodeHexString(encryptData)); 207 | // System.out.println(); 208 | // byte[] decryptData = decrypt(encryptData, k); 209 | // System.out.println("解密后数据: byte[]:"+showByteArray(decryptData)); 210 | // System.out.println("解密后数据: string:"+new String(decryptData)); 211 | // } 212 | } 213 | -------------------------------------------------------------------------------- /src/main/java/spring/boot/pay/common/encrypt/ProtectData.java: -------------------------------------------------------------------------------- 1 | package spring.boot.pay.common.encrypt; 2 | 3 | /** 4 | * 5 | * @author chenlei 6 | * 7 | *根据指定的加密方式,对数据进行加解密 8 | */ 9 | public final class ProtectData { 10 | 11 | public static final String AESEncrypt(String key,String data){ 12 | 13 | return CryptAES.AES_Encrypt(key, data); 14 | } 15 | 16 | public static final String AESDecrypt(String key,String data){ 17 | 18 | return CryptAES.AES_Decrypt(key, data); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/spring/boot/pay/common/encrypt/RSA.java: -------------------------------------------------------------------------------- 1 | package spring.boot.pay.common.encrypt; 2 | 3 | import java.io.ByteArrayInputStream; 4 | import java.io.ByteArrayOutputStream; 5 | import java.io.InputStream; 6 | import java.security.KeyFactory; 7 | import java.security.PrivateKey; 8 | import java.security.PublicKey; 9 | import java.security.spec.PKCS8EncodedKeySpec; 10 | import java.security.spec.X509EncodedKeySpec; 11 | import java.util.Arrays; 12 | 13 | import javax.crypto.Cipher; 14 | 15 | import org.apache.commons.codec.binary.Base64; 16 | import org.slf4j.Logger; 17 | import org.slf4j.LoggerFactory; 18 | 19 | import spring.boot.pay.config.dictionary.Constant; 20 | 21 | public class RSA{ 22 | 23 | public static final String SIGN_ALGORITHMS = "SHA1WithRSA"; 24 | 25 | private static final Logger LOGGER = LoggerFactory.getLogger(RSA.class); 26 | 27 | /** 28 | * RSA签名 29 | * @param content 待签名数据 30 | * @param privateKey 商户私钥 31 | * @return 签名值 32 | */ 33 | public static String sign(String content,String privateKey) 34 | { 35 | try 36 | { 37 | PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec( Base64.decodeBase64(privateKey) ); 38 | KeyFactory keyFactory = KeyFactory.getInstance("RSA"); 39 | PrivateKey priKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec); 40 | 41 | java.security.Signature signature = java.security.Signature 42 | .getInstance(SIGN_ALGORITHMS); 43 | 44 | signature.initSign(priKey); 45 | signature.update( content.getBytes(Constant.CHARSET) ); 46 | 47 | byte[] signed = signature.sign(); 48 | 49 | return Base64.encodeBase64String(signed); 50 | } catch (Exception e) { 51 | LOGGER.error(e.getMessage(),e); 52 | } 53 | 54 | return null; 55 | } 56 | 57 | /** 58 | * RSA验签名检查 59 | * @param content 待签名数据 60 | * @param sign 签名值 61 | * @param publicKey 公钥 62 | * @return 布尔值 63 | */ 64 | public static boolean verify(String content, String sign, String publicKey) { 65 | try { 66 | KeyFactory keyFactory = KeyFactory.getInstance("RSA"); 67 | byte[] encodedKey = Base64.decodeBase64(publicKey); 68 | PublicKey pubKey = keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey)); 69 | java.security.Signature signature = java.security.Signature.getInstance(SIGN_ALGORITHMS); 70 | signature.initVerify(pubKey); 71 | signature.update(content.getBytes(Constant.CHARSET)); 72 | return signature.verify( Base64.decodeBase64(sign) ); 73 | 74 | } catch (Exception e) { 75 | LOGGER.error(e.getMessage(),e); 76 | } 77 | return false; 78 | } 79 | 80 | /** 81 | * 解密 82 | * @param content 密文 83 | * @param privateKey 商户私钥 84 | * @param input_charset 编码格式 85 | * @return 解密后的字符串 86 | */ 87 | public static String decrypt(String content, String privateKey, String input_charset) throws Exception { 88 | PrivateKey key = getPrivateKey(privateKey); 89 | 90 | Cipher cipher = Cipher.getInstance("RSA"); 91 | cipher.init(Cipher.DECRYPT_MODE, key); 92 | 93 | InputStream ins = new ByteArrayInputStream(Base64.decodeBase64(content)); 94 | ByteArrayOutputStream writer = new ByteArrayOutputStream(); 95 | 96 | //rsa解密的字节大小最多是128,将需要解密的内容,按128位拆开解密 97 | byte[] buf = new byte[128]; 98 | int bufLength; 99 | while ((bufLength = ins.read(buf)) != -1) { 100 | if (buf.length == bufLength) { 101 | writer.write(cipher.doFinal(buf)); 102 | } else { 103 | writer.write(cipher.doFinal(Arrays.copyOf(buf,bufLength))); 104 | } 105 | } 106 | return new String(writer.toByteArray(), input_charset); 107 | } 108 | 109 | 110 | /** 111 | * 得到私钥 112 | * @param key 密钥字符串(经过base64编码) 113 | * @throws Exception 114 | */ 115 | public static PrivateKey getPrivateKey(String key) throws Exception { 116 | 117 | byte[] keyBytes = Base64.decodeBase64(key); 118 | 119 | PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes); 120 | 121 | KeyFactory keyFactory = KeyFactory.getInstance("RSA"); 122 | 123 | return keyFactory.generatePrivate(keySpec); 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/main/java/spring/boot/pay/common/encrypt/WXMD5.java: -------------------------------------------------------------------------------- 1 | package spring.boot.pay.common.encrypt; 2 | 3 | import java.security.MessageDigest; 4 | 5 | 6 | public class WXMD5 { 7 | private final static String[] hexDigits = {"0", "1", "2", "3", "4", "5", "6", "7", 8 | "8", "9", "a", "b", "c", "d", "e", "f"}; 9 | 10 | /** 11 | * 转换字节数组为16进制字串 12 | * @param b 字节数组 13 | * @return 16进制字串 14 | */ 15 | public static String byteArrayToHexString(byte[] b) { 16 | StringBuilder resultSb = new StringBuilder(); 17 | for (byte aB : b) { 18 | resultSb.append(byteToHexString(aB)); 19 | } 20 | return resultSb.toString(); 21 | } 22 | 23 | /** 24 | * 转换byte到16进制 25 | * @param b 要转换的byte 26 | * @return 16进制格式 27 | */ 28 | private static String byteToHexString(byte b) { 29 | int n = b; 30 | if (n < 0) { 31 | n = 256 + n; 32 | } 33 | int d1 = n / 16; 34 | int d2 = n % 16; 35 | return hexDigits[d1] + hexDigits[d2]; 36 | } 37 | 38 | /** 39 | * MD5编码 40 | * @param origin 原始字符串 41 | * @return 经过MD5加密之后的结果 42 | */ 43 | public static String MD5Encode(String origin) { 44 | String resultString = null; 45 | try { 46 | resultString = origin; 47 | MessageDigest md = MessageDigest.getInstance("MD5"); 48 | resultString = byteArrayToHexString(md.digest(resultString.getBytes())); 49 | } catch (Exception e) { 50 | e.printStackTrace(); 51 | } 52 | return resultString; 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/spring/boot/pay/common/http/HttpClientHelper.java: -------------------------------------------------------------------------------- 1 | package spring.boot.pay.common.http; 2 | 3 | import java.util.List; 4 | import java.util.Map; 5 | import java.util.concurrent.TimeUnit; 6 | 7 | import org.apache.http.HttpEntity; 8 | import org.apache.http.NameValuePair; 9 | import org.apache.http.client.methods.HttpGet; 10 | import org.apache.http.client.methods.HttpPost; 11 | import org.apache.http.client.utils.URLEncodedUtils; 12 | import org.apache.http.config.Registry; 13 | import org.apache.http.config.RegistryBuilder; 14 | import org.apache.http.conn.HttpClientConnectionManager; 15 | import org.apache.http.conn.socket.ConnectionSocketFactory; 16 | import org.apache.http.conn.socket.PlainConnectionSocketFactory; 17 | import org.apache.http.conn.ssl.SSLConnectionSocketFactory; 18 | import org.apache.http.entity.StringEntity; 19 | import org.apache.http.impl.client.CloseableHttpClient; 20 | import org.apache.http.impl.client.HttpClients; 21 | import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; 22 | 23 | import spring.boot.pay.config.dictionary.Constant; 24 | 25 | public final class HttpClientHelper { 26 | 27 | private static PoolingHttpClientConnectionManager connectionManager; 28 | 29 | static { 30 | Registry r = RegistryBuilder. create() 31 | .register("http", PlainConnectionSocketFactory.getSocketFactory()) 32 | .register("https", SSLConnectionSocketFactory.getSocketFactory()).build(); 33 | 34 | connectionManager = new PoolingHttpClientConnectionManager(r); 35 | connectionManager.setMaxTotal(300); 36 | connectionManager.setDefaultMaxPerRoute(30); 37 | 38 | new IdleConnectionMonitorThread(connectionManager).start(); 39 | } 40 | 41 | private CloseableHttpClient httpClient; 42 | 43 | private HttpClientHelper() { 44 | httpClient = HttpClients.custom().setConnectionManager(connectionManager).build(); 45 | } 46 | 47 | public static HttpClientHelper create() { 48 | return new HttpClientHelper(); 49 | } 50 | 51 | /** 52 | * @param params 53 | * http body内容 54 | * @param url 55 | * http url 56 | * @param retryNumber 57 | * 失败重试次数 58 | * @return success:http response String fail:null 59 | */ 60 | public String execute(String params, String url, int retryNumber, String charset) { 61 | 62 | HttpPost request = new HttpPost(url); 63 | request.setHeader("Content-Type", "application/x-www-form-urlencoded;charset=" + charset); 64 | 65 | if (!params.isEmpty()) { 66 | HttpEntity entity = null; 67 | entity = new StringEntity(params, charset); 68 | request.setEntity(entity); 69 | } 70 | 71 | String result = HttpHelper.execute(httpClient, request); 72 | 73 | if (result == null && retryNumber > 0) { 74 | return execute(params, url, retryNumber - 1); 75 | } 76 | 77 | return result; 78 | } 79 | 80 | public String execute(String params, String url, int retryNumber) { 81 | return execute(params, url, retryNumber, Constant.CHARSET); 82 | } 83 | 84 | public String execute(String params, String url) { 85 | return execute(params, url, 0); 86 | } 87 | 88 | public String execute(List params, String url, int retryNumber) { 89 | return execute(URLEncodedUtils.format(params, Constant.CHARSET), url, retryNumber); 90 | } 91 | 92 | public String execute(Map params, String url) { 93 | return execute(HttpHelper.generateBody(params), url, 0); 94 | } 95 | public String executeForGET(String url, int retryNumber, String charset) { 96 | 97 | HttpGet request = new HttpGet(url); 98 | request.setHeader("Content-Type", "application/x-www-form-urlencoded;charset=" + charset); 99 | 100 | String result = HttpHelper.execute(httpClient, request); 101 | 102 | if (result == null && retryNumber > 0) { 103 | return executeForGET(url, retryNumber, charset); 104 | } 105 | 106 | return result; 107 | } 108 | /** 109 | * 该线程用于清理过期和空闲的连接 110 | */ 111 | private static class IdleConnectionMonitorThread extends Thread { 112 | 113 | private final HttpClientConnectionManager connMgr; 114 | private volatile boolean shutdown; 115 | 116 | public IdleConnectionMonitorThread(HttpClientConnectionManager connMgr) { 117 | super(); 118 | this.connMgr = connMgr; 119 | } 120 | 121 | @Override 122 | public void run() { 123 | try { 124 | while (!shutdown) { 125 | synchronized (this) { 126 | wait(6000); 127 | // 关闭失效连接 128 | connMgr.closeExpiredConnections(); 129 | // 关闭空闲超过30秒的连接 130 | connMgr.closeIdleConnections(30, TimeUnit.SECONDS); 131 | } 132 | } 133 | } catch (InterruptedException ex) { 134 | } 135 | } 136 | 137 | public void shutdown() { 138 | shutdown = true; 139 | synchronized (this) { 140 | notifyAll(); 141 | } 142 | } 143 | } 144 | 145 | } 146 | -------------------------------------------------------------------------------- /src/main/java/spring/boot/pay/common/http/HttpHelper.java: -------------------------------------------------------------------------------- 1 | package spring.boot.pay.common.http; 2 | 3 | import java.io.IOException; 4 | import java.util.ArrayList; 5 | import java.util.List; 6 | import java.util.Map; 7 | 8 | import org.apache.commons.lang3.exception.ExceptionUtils; 9 | import org.apache.http.HttpEntity; 10 | import org.apache.http.NameValuePair; 11 | import org.apache.http.client.methods.CloseableHttpResponse; 12 | import org.apache.http.client.methods.HttpUriRequest; 13 | import org.apache.http.client.utils.URLEncodedUtils; 14 | import org.apache.http.impl.client.CloseableHttpClient; 15 | import org.apache.http.message.BasicNameValuePair; 16 | import org.apache.http.util.EntityUtils; 17 | import org.slf4j.Logger; 18 | import org.slf4j.LoggerFactory; 19 | 20 | import spring.boot.pay.config.dictionary.Constant; 21 | 22 | 23 | public final class HttpHelper { 24 | 25 | private static Logger logger = LoggerFactory.getLogger(HttpHelper.class); 26 | 27 | public static String execute(CloseableHttpClient httpClient,HttpUriRequest request){ 28 | return execute(httpClient,request,Constant.CHARSET); 29 | } 30 | 31 | public static String execute(CloseableHttpClient httpClient,HttpUriRequest request,String charset){ 32 | 33 | CloseableHttpResponse response = null; 34 | try { 35 | response = httpClient.execute(request); 36 | HttpEntity entity = response.getEntity(); 37 | String str = EntityUtils.toString(entity,charset); 38 | return str; 39 | } catch (Exception e) { 40 | logger.warn("execute http request fail,exception stacktrace is:\n {}", ExceptionUtils.getStackTrace(e)); 41 | return null; 42 | }finally { 43 | try { 44 | if(response != null) 45 | response.close(); 46 | } catch (IOException e) { 47 | logger.warn("close http response fail,exception stacktrace is:\n {}", ExceptionUtils.getStackTrace(e)); 48 | } 49 | } 50 | } 51 | 52 | public static String generateBody(Map map) { 53 | return generateBody(map,Constant.CHARSET); 54 | } 55 | 56 | public static String generateBody(Map map,String charset) { 57 | List list = new ArrayList(); 58 | for (Map.Entry entry : map.entrySet()) { 59 | list.add( new BasicNameValuePair(entry.getKey(), entry.getValue()) ); 60 | } 61 | return URLEncodedUtils.format(list, charset); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/spring/boot/pay/config/bean/JdbcConfiguration.java: -------------------------------------------------------------------------------- 1 | package spring.boot.pay.config.bean; 2 | 3 | import javax.sql.DataSource; 4 | 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | import org.springframework.jdbc.core.JdbcTemplate; 8 | import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; 9 | 10 | import com.alibaba.druid.pool.DruidDataSource; 11 | 12 | import spring.boot.pay.config.properties.JdbcProperties; 13 | 14 | @Configuration 15 | public class JdbcConfiguration { 16 | 17 | @Bean 18 | public DataSource dataSource(){ 19 | DruidDataSource dataSource = new DruidDataSource(); 20 | dataSource.setDriverClassName(JdbcProperties.driverClass); 21 | dataSource.setUrl(JdbcProperties.jdbcUrl); 22 | dataSource.setUsername(JdbcProperties.userName); 23 | dataSource.setPassword(JdbcProperties.password); 24 | dataSource.setMaxActive(JdbcProperties.maxActive); 25 | 26 | return dataSource; 27 | } 28 | 29 | @Bean(name="pay") 30 | public JdbcTemplate jdbcTemplate(){ 31 | JdbcTemplate jdbcTemplate = new JdbcTemplate(); 32 | jdbcTemplate.setDataSource(dataSource()); 33 | return jdbcTemplate; 34 | } 35 | 36 | 37 | @Bean(name="namePay") 38 | public NamedParameterJdbcTemplate namedParameterJdbcTemplate(){ 39 | NamedParameterJdbcTemplate namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource()); 40 | return namedParameterJdbcTemplate; 41 | } 42 | 43 | @Bean 44 | public DataSource dataSourceOrder(){ 45 | DruidDataSource dataSource = new DruidDataSource(); 46 | dataSource.setDriverClassName(JdbcProperties.driverClass); 47 | dataSource.setUrl(JdbcProperties.jdbcUrl_order); 48 | dataSource.setUsername(JdbcProperties.userName_order); 49 | dataSource.setPassword(JdbcProperties.password_order); 50 | dataSource.setMaxActive(JdbcProperties.maxActive); 51 | 52 | return dataSource; 53 | } 54 | 55 | @Bean(name="order") 56 | public JdbcTemplate jdbcTemplateOrder(){ 57 | JdbcTemplate jdbcTemplate = new JdbcTemplate(); 58 | jdbcTemplate.setDataSource(dataSourceOrder()); 59 | return jdbcTemplate; 60 | } 61 | 62 | @Bean(name="nameOrder") 63 | public NamedParameterJdbcTemplate namedParameterJdbcTemplateOrder(){ 64 | NamedParameterJdbcTemplate namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSourceOrder()); 65 | return namedParameterJdbcTemplate; 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/spring/boot/pay/config/bean/WebConfiguration.java: -------------------------------------------------------------------------------- 1 | package spring.boot.pay.config.bean; 2 | 3 | import java.io.FileNotFoundException; 4 | import java.util.Locale; 5 | 6 | import javax.servlet.Filter; 7 | 8 | import org.springframework.boot.autoconfigure.web.ErrorController; 9 | import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer; 10 | import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer; 11 | import org.springframework.boot.web.servlet.ErrorPage; 12 | import org.springframework.context.annotation.Bean; 13 | import org.springframework.context.annotation.Configuration; 14 | import org.springframework.http.HttpStatus; 15 | import org.springframework.web.filter.CharacterEncodingFilter; 16 | import org.springframework.web.servlet.View; 17 | import org.springframework.web.servlet.ViewResolver; 18 | import org.springframework.web.servlet.view.json.MappingJackson2JsonView; 19 | 20 | import spring.boot.pay.config.dictionary.Constant; 21 | 22 | @Configuration 23 | public class WebConfiguration { 24 | 25 | 26 | @Bean 27 | public EmbeddedServletContainerCustomizer containerCustomizer() throws FileNotFoundException { 28 | 29 | return new EmbeddedServletContainerCustomizer() { 30 | @Override 31 | public void customize(ConfigurableEmbeddedServletContainer container) { 32 | container.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND, "/html/404.html")); 33 | 34 | // if(container instanceof TomcatEmbeddedServletContainerFactory) { 35 | // TomcatEmbeddedServletContainerFactory containerFactory = (TomcatEmbeddedServletContainerFactory) container; 36 | // containerFactory.addAdditionalTomcatConnectors(createSslConnector("/app/paycenter/ssl.p12", "lys418754658", "PKCS12",null)); 37 | // } 38 | } 39 | }; 40 | } 41 | 42 | // private Connector createSslConnector(String absoluteKeystoreFile, 43 | // String keystorePassword, String keystoreType, String keystoreAlias) { 44 | // 45 | // Connector connector = new Connector(); 46 | // connector.setPort(8443); 47 | // connector.setSecure(true); 48 | // connector.setScheme("https"); 49 | // connector.setAttribute("SSLEnabled", true); 50 | // connector.setAttribute("sslProtocol", "TLS"); 51 | // connector.setAttribute("protocol", "org.apache.coyote.http11.Http11Protocol"); 52 | // connector.setAttribute("clientAuth", false); 53 | // connector.setAttribute("keystoreFile", absoluteKeystoreFile); 54 | // connector.setAttribute("keystoreType", keystoreType); 55 | // connector.setAttribute("keystorePass", keystorePassword); 56 | // if(keystoreAlias != null) { 57 | // connector.setAttribute("keystoreAlias", keystoreAlias.toLowerCase()); 58 | // } 59 | // connector.setAttribute("keyPass", keystorePassword); 60 | // 61 | // return connector; 62 | // } 63 | 64 | @Bean 65 | public ErrorController errorController(){ 66 | return new ErrorController() { 67 | @Override 68 | public String getErrorPath() { 69 | return "/html/error.html"; 70 | } 71 | }; 72 | } 73 | 74 | 75 | @Bean 76 | public ViewResolver jsonViewResolver(){ 77 | return new ViewResolver() { 78 | MappingJackson2JsonView view = new MappingJackson2JsonView(); 79 | @Override 80 | public View resolveViewName(String viewName, Locale locale) throws Exception { 81 | if(viewName.equals("jsonView")) { 82 | return view; 83 | } 84 | return null; 85 | } 86 | }; 87 | } 88 | 89 | @Bean 90 | public Filter characterEncodingFilter() { 91 | CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter(); 92 | characterEncodingFilter.setEncoding(Constant.CHARSET); 93 | characterEncodingFilter.setForceEncoding(true); 94 | return characterEncodingFilter; 95 | } 96 | 97 | 98 | // @Component 99 | // public static class SasAllowOriginInterceptor extends HandlerInterceptorAdapter { 100 | // 101 | // @Override 102 | // public boolean preHandle(HttpServletRequest request, 103 | // HttpServletResponse response, Object handler) 104 | // throws Exception { 105 | // response.setHeader("Access-Control-Allow-Origin", "*"); 106 | // response.setHeader("Access-Control-Allow-Methods", 107 | // "GET, POST, PUT, DELETE, OPTIONS"); 108 | // return true; 109 | // } 110 | // } 111 | 112 | } 113 | -------------------------------------------------------------------------------- /src/main/java/spring/boot/pay/config/dictionary/Constant.java: -------------------------------------------------------------------------------- 1 | package spring.boot.pay.config.dictionary; 2 | 3 | /** 4 | * Created by chenlei on 2016/01/06. 5 | */ 6 | public class Constant { 7 | 8 | public static final String CHARSET = "UTF-8"; 9 | //来自于微信服务号产生的key 10 | public static final String AESkeyStr = "qazwsxed"; 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/spring/boot/pay/config/dictionary/PayType.java: -------------------------------------------------------------------------------- 1 | package spring.boot.pay.config.dictionary; 2 | 3 | /** 4 | * Created by chenlei on 15/12/15. 5 | */ 6 | public class PayType { 7 | 8 | 9 | 10 | /** 11 | * 微信公众号支付 12 | */ 13 | public static final int WX_PAY_JSAPI = 1; 14 | 15 | /** 16 | * 微信扫码支付 17 | */ 18 | public static final int WX_PAY_QC = 2; 19 | 20 | /** 21 | * 阿里银联支付 22 | */ 23 | public static final int ALI_PAY_WAP = 3; 24 | 25 | /** 26 | * 阿里手机网站支付 27 | */ 28 | public static final int ALI_PAY_NetBank =4; 29 | 30 | /** 31 | * 阿里直接支付 32 | */ 33 | public static final int ALI_PAY_Direct =5; 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/spring/boot/pay/config/dictionary/SignType.java: -------------------------------------------------------------------------------- 1 | package spring.boot.pay.config.dictionary; 2 | 3 | /** 4 | * Created by chenlei on 15/12/15. 5 | */ 6 | public class SignType { 7 | 8 | public static final String RSA = "RSA"; 9 | 10 | public static final String MD5 = "MD5"; 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/spring/boot/pay/config/properties/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softprog/spring-boot-pay/876a0dc720082756e78a542f5bc3251a4615578e/src/main/java/spring/boot/pay/config/properties/.DS_Store -------------------------------------------------------------------------------- /src/main/java/spring/boot/pay/config/properties/AliPayProperties.java: -------------------------------------------------------------------------------- 1 | package spring.boot.pay.config.properties; 2 | 3 | import java.io.IOException; 4 | import java.util.Properties; 5 | 6 | import spring.boot.pay.common.Util; 7 | 8 | public class AliPayProperties { 9 | 10 | /** 11 | * 支付宝提供给商户的服务接入网关URL(新) 12 | */ 13 | public static final String ALIPAY_GATEWAY_NEW = "https://mapi.alipay.com/gateway.do?"; 14 | 15 | // 数字组成的字符串 16 | public static String partner = ""; 17 | 18 | // 支付宝的公钥,无需修改该值 19 | public static String aliPublicKey = ""; 20 | 21 | // 字符编码格式 目前支持 gbk 或 utf-8 22 | public static String charset = "utf-8"; 23 | 24 | // 支付签名方式 不需修改 25 | public static String signType = "MD5"; 26 | 27 | //卖家支付宝帐户 28 | public static String sellerEmail = ""; 29 | public static String domain=""; 30 | public static String defaultbank=""; 31 | //服务器异步通知页面路径为银行支付 32 | public static final String notify_url = "/pay/alipay/notify"; 33 | //服务器异步通知页面路径为即时到帐支付 34 | public static final String notify_url_direct = "/pay/alipay/notifydirect"; 35 | //服务成功直接返回页面路径 36 | public static String return_url = ""; 37 | 38 | //服务成功直接返回页面路径用于移动端 39 | public static String m_return_url = ""; 40 | //服务器异步通知页面路径 41 | public static String showUrl = ""; 42 | 43 | public static void init() throws IOException { 44 | Properties properties = Util.getProperties("/payconfig/ali/alipayconfig.properties"); 45 | partner=properties.getProperty("partner"); 46 | aliPublicKey=properties.getProperty("aliPublicKey"); 47 | sellerEmail=properties.getProperty("sellerEmail"); 48 | domain=properties.getProperty("domain"); 49 | defaultbank=properties.getProperty("defaultbank"); 50 | showUrl=properties.getProperty("showUrl"); 51 | return_url=properties.getProperty("returnUrl"); 52 | m_return_url=properties.getProperty("mreturnUrl"); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/spring/boot/pay/config/properties/JdbcProperties.java: -------------------------------------------------------------------------------- 1 | package spring.boot.pay.config.properties; 2 | 3 | import java.io.IOException; 4 | import java.util.Properties; 5 | 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | import spring.boot.pay.common.Util; 10 | 11 | public class JdbcProperties { 12 | 13 | private static final Logger LOGGER = LoggerFactory.getLogger(JdbcProperties.class); 14 | 15 | public static String driverClass = ""; 16 | public static String jdbcUrl = ""; 17 | public static String userName = ""; 18 | public static String password = ""; 19 | public static String jdbcUrl_order = ""; 20 | public static String userName_order = ""; 21 | public static String password_order = ""; 22 | public static int maxActive = 0; 23 | 24 | 25 | public static void init() throws IOException { 26 | 27 | Properties properties =Util.getProperties("/config/db.properties"); 28 | 29 | 30 | jdbcUrl = properties.getProperty("url"); 31 | userName = properties.getProperty("username"); 32 | password = properties.getProperty("password"); 33 | driverClass = properties.getProperty("driverClass"); 34 | if(properties.getProperty("maxActive") != null){ 35 | maxActive = Integer.parseInt( properties.getProperty("maxActive") ); 36 | } 37 | jdbcUrl_order = properties.getProperty("url_order"); 38 | userName_order = properties.getProperty("username_order"); 39 | password_order = properties.getProperty("password_order"); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/spring/boot/pay/config/properties/SecurityProperties.java: -------------------------------------------------------------------------------- 1 | package spring.boot.pay.config.properties; 2 | 3 | 4 | import java.io.BufferedReader; 5 | import java.io.IOException; 6 | import java.io.InputStream; 7 | import java.io.InputStreamReader; 8 | import java.util.HashSet; 9 | import java.util.Set; 10 | 11 | public class SecurityProperties { 12 | 13 | //ip白名单 14 | private static Set whiteList; 15 | 16 | public static void init() throws IOException { 17 | 18 | Set set = new HashSet(); 19 | 20 | InputStream in= Object.class.getResourceAsStream("/config/whitelist.txt"); 21 | 22 | InputStreamReader reader = new InputStreamReader(in); 23 | BufferedReader bufferedReader = new BufferedReader(reader); 24 | for(String str = bufferedReader.readLine(); 25 | str != null; 26 | str = bufferedReader.readLine()){ 27 | set.add(str); 28 | } 29 | 30 | whiteList = set; 31 | 32 | in.close(); 33 | } 34 | 35 | public static Set getWhiteList(){ 36 | return whiteList; 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/spring/boot/pay/config/properties/WxPayProperties.java: -------------------------------------------------------------------------------- 1 | package spring.boot.pay.config.properties; 2 | 3 | import java.io.IOException; 4 | import java.util.Properties; 5 | 6 | import spring.boot.pay.common.Util; 7 | 8 | public class WxPayProperties { 9 | 10 | 11 | public static final String NOTIFY_URL = "/pay/wxpay/notify"; 12 | public static final String KEY="1Q2w3e4r5t6y7u8i9Oa1s2d3f4g5h6j7"; 13 | //向微信后台发起预支付的url 14 | public static String WX_PRE_PAY_URL = ""; 15 | 16 | public static final String DOWN_LOAD_BILL="/pay/wxpay/downloadBill"; 17 | public static final String GET_OPENID_URL="https://api.weixin.qq.com/sns/oauth2/access_token"; 18 | //网页支付 19 | public static final String TRADE_TYPE_JSAPI = "JSAPI"; 20 | 21 | public static final String TRADE_TYPE_QC = "NATIVE"; 22 | public static String DOMAIN=""; 23 | public static String MDOMAIN=""; 24 | public static String APP_ID= ""; 25 | //商户id 26 | public static String MCH_ID = ""; 27 | public static String Fee_Type="CNY"; 28 | 29 | public static String defSpBillCreateIp = "127.0.0.1"; 30 | public static String jsapiData=""; 31 | 32 | public static void init() throws IOException { 33 | 34 | Properties properties = Util.getProperties("/payconfig/wx/wxconfig.properties"); 35 | APP_ID=properties.getProperty("appid"); 36 | MCH_ID=properties.getProperty("mch_id"); 37 | Fee_Type=properties.getProperty("feeType"); 38 | WX_PRE_PAY_URL=properties.getProperty("unifiedorder"); 39 | DOMAIN=properties.getProperty("domain"); 40 | MDOMAIN=properties.getProperty("mdomain"); 41 | jsapiData= Util.getFileData("/payconfig/wx/jsapi.txt"); 42 | 43 | 44 | } 45 | 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/spring/boot/pay/controller/TradeController.java: -------------------------------------------------------------------------------- 1 | package spring.boot.pay.controller; 2 | 3 | import java.io.PrintWriter; 4 | import java.math.BigDecimal; 5 | import java.util.SortedMap; 6 | import java.util.TreeMap; 7 | 8 | import javax.servlet.http.HttpServletRequest; 9 | import javax.servlet.http.HttpServletResponse; 10 | 11 | import org.slf4j.Logger; 12 | import org.slf4j.LoggerFactory; 13 | import org.springframework.beans.factory.annotation.Autowired; 14 | import org.springframework.stereotype.Controller; 15 | import org.springframework.web.bind.annotation.RequestMapping; 16 | import org.springframework.web.bind.annotation.RequestMethod; 17 | import org.springframework.web.bind.annotation.ResponseBody; 18 | 19 | import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; 20 | import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty; 21 | import com.sun.xml.internal.messaging.saaj.util.Base64; 22 | 23 | import spring.boot.pay.common.Util; 24 | import spring.boot.pay.common.encrypt.ProtectData; 25 | import spring.boot.pay.config.dictionary.Constant; 26 | import spring.boot.pay.config.dictionary.PayType; 27 | import spring.boot.pay.config.properties.WxPayProperties; 28 | import spring.boot.pay.framework.base.ResultModel; 29 | import spring.boot.pay.model.OrderInfo; 30 | import spring.boot.pay.model.Trade; 31 | import spring.boot.pay.service.TradeService; 32 | import spring.boot.pay.thirdparty.wxpay.WxPayHelper; 33 | 34 | @Controller 35 | @SuppressWarnings("unused") 36 | public class TradeController { 37 | 38 | private static final Logger LOGGER = LoggerFactory.getLogger(TradeController.class); 39 | // 发起支付的时间离订单过期时间至少相差5分钟 40 | private static final int FIVE_MINUTE = 5 * 1000 * 60; 41 | 42 | @Autowired 43 | TradeService tradeService; 44 | 45 | /** 46 | * 解析支付url得到支付请求信息 进行支付 47 | */ 48 | @RequestMapping(value = "/pay/trade", method = RequestMethod.GET) 49 | @ResponseBody 50 | @HystrixCommand(fallbackMethod="fallback",commandProperties = { 51 | @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "500"), 52 | 53 | }, 54 | threadPoolProperties = { 55 | @HystrixProperty(name = "maxQueueSize", value = "0") 56 | }) 57 | // @CheckIpEndpoint 58 | public String trade(HttpServletRequest request, HttpServletResponse response, String data) throws Exception { 59 | 60 | ValidateResult result = this.validateTradeDataForWx(data, request); 61 | 62 | if (result.getTrade() == null) { 63 | 64 | return result.getResult(); 65 | } 66 | 67 | ResultModel processResult = this.tradeService.doTrade(result.getTrade()); 68 | 69 | if (!processResult.isSuccess()) { 70 | return ProtectData.AESEncrypt(Constant.AESkeyStr, toFailureJson(processResult.getBody().toString())); 71 | } 72 | 73 | return ProtectData.AESEncrypt(Constant.AESkeyStr, toSuccessJson(processResult.getBody().toString())); 74 | 75 | } 76 | public String fallback(HttpServletRequest request, HttpServletResponse response, String data){ 77 | return "系统已达到最大上限."; 78 | } 79 | /** 80 | * 解析支付url得到支付请求信息 进行支付 81 | */ 82 | @RequestMapping(value = "/pay/tradewxlogin", method = RequestMethod.GET) 83 | @ResponseBody 84 | 85 | public void tradeforwxlogin(HttpServletRequest request, HttpServletResponse response) throws Exception { 86 | String oid = request.getParameter("oid"); 87 | response.sendRedirect(WxPayHelper.authorizeLogin(oid)); 88 | } 89 | 90 | /** 91 | * 解析支付url得到支付请求信息 进行支付 92 | */ 93 | @RequestMapping(value = "/pay/tradewxjs", method = RequestMethod.GET) 94 | @ResponseBody 95 | // @CheckIpEndpoint 96 | public void tradeWXJS(HttpServletRequest request, HttpServletResponse response, String data) throws Exception { 97 | 98 | 99 | ValidateResult result = this.validateTradeDataForWxjs(data,request); 100 | 101 | if (result.getTrade() == null) { 102 | 103 | return; 104 | } 105 | 106 | 107 | ResultModel processResult = this.tradeService.doTrade(result.getTrade()); 108 | // ResultModel processResult =ResultModel.successModel("32343"); 109 | if (!processResult.isSuccess()) { 110 | return; 111 | } 112 | 113 | 114 | 115 | 116 | response.setContentType("text/html; charset=UTF-8"); 117 | response.setCharacterEncoding("UTF-8"); String 118 | jsapiData=handleJSAPI(processResult.getBody().toString()); 119 | PrintWriter out = response.getWriter(); 120 | out.print(jsapiData); 121 | out.close(); 122 | //response.getOutputStream().print(new String(jsapiData.getBytes(), 123 | // "UTF-8")); 124 | //response.getOutputStream().flush(); 125 | // response.getOutputStream().close(); 126 | 127 | return; 128 | 129 | } 130 | /** 131 | * 解析支付url得到支付请求信息 进行支付 132 | */ 133 | @RequestMapping(value = "/pay/tradealibank", method = RequestMethod.GET) 134 | @ResponseBody 135 | // @CheckIpEndpoint 136 | public void tradeAliBank(HttpServletRequest request, HttpServletResponse response, String data) throws Exception { 137 | 138 | ValidateResult result = this.validateTradeDataForAliBank(data,request); 139 | 140 | if (result.getTrade() == null) { 141 | 142 | return; 143 | } 144 | 145 | 146 | ResultModel processResult = this.tradeService.doTrade(result.getTrade()); 147 | // ResultModel processResult =ResultModel.successModel("32343"); 148 | if (!processResult.isSuccess()) { 149 | return; 150 | } 151 | 152 | // if (trade.getPayType() == PayType.ALI_PAY_NetBank || 153 | // trade.getPayType() == PayType.ALI_PAY_WAP) { 154 | response.setContentType("text/html; charset=UTF-8"); 155 | response.setCharacterEncoding("UTF-8"); 156 | response.getWriter().write(new String(processResult.getBody().toString().getBytes(), "UTF-8")); 157 | // response.getOutputStream().flush(); 158 | // response.getOutputStream().close(); 159 | // } 160 | 161 | /* 162 | * if (trade.getPayType() == PayType.WX_PAY_JSAPI) { 163 | * response.setContentType("text/html; charset=UTF-8"); 164 | * response.setCharacterEncoding("UTF-8"); String 165 | * jsapiData=handleJSAPI(processResult.getBody().toString()); 166 | * 167 | * response.getOutputStream().print(new String(jsapiData.getBytes(), 168 | * "UTF-8")); response.getOutputStream().flush(); 169 | * response.getOutputStream().close(); 170 | * 171 | * } 172 | */ 173 | 174 | return; 175 | 176 | } 177 | 178 | /** 179 | * 解析支付url得到支付请求信息 进行支付 180 | */ 181 | @RequestMapping(value = "/pay/tradealidirect", method = RequestMethod.GET) 182 | @ResponseBody 183 | // @CheckIpEndpoint 184 | public void tradeAliDirect(HttpServletRequest request, HttpServletResponse response, String data) throws Exception { 185 | 186 | ValidateResult result = this.validateTradeDataForAliDirect(data,request); 187 | 188 | if (result.getTrade() == null) { 189 | 190 | return; 191 | } 192 | 193 | ResultModel processResult = this.tradeService.doTrade(result.getTrade()); 194 | 195 | if (!processResult.isSuccess()) { 196 | return; 197 | } 198 | 199 | 200 | response.setContentType("text/html; charset=UTF-8"); 201 | response.setCharacterEncoding("UTF-8"); 202 | LOGGER.error("ALI_PAY_Direct exec here"); 203 | System.out.println("ALI_PAY_Direct exec here"); 204 | response.getWriter().write(new String(processResult.getBody().toString().getBytes(), "UTF-8")); 205 | LOGGER.error("ALI_PAY_Direct exec ok"); 206 | System.out.println("ALI_PAY_Direct exec ok"); 207 | 208 | return; 209 | 210 | } 211 | 212 | /** 213 | * 解析支付url得到支付请求信息 进行支付 214 | */ 215 | @RequestMapping(value = "/pay/tradealiwap", method = RequestMethod.GET) 216 | @ResponseBody 217 | // @CheckIpEndpoint 218 | public void tradeAliWAP(HttpServletRequest request, HttpServletResponse response, String data) throws Exception { 219 | 220 | ValidateResult result = this.validateTradeDataForAliWAP(data,request); 221 | 222 | if (result.getTrade() == null) { 223 | 224 | return; 225 | } 226 | 227 | ResultModel processResult = this.tradeService.doTrade(result.getTrade()); 228 | 229 | if (!processResult.isSuccess()) { 230 | return; 231 | } 232 | 233 | 234 | response.setContentType("text/html; charset=UTF-8"); 235 | response.setCharacterEncoding("UTF-8"); 236 | LOGGER.error("tradealiwap exec here"); 237 | System.out.println("tradealiwap exec here"); 238 | response.getWriter().write(new String(processResult.getBody().toString().getBytes(), "UTF-8")); 239 | LOGGER.error("tradealiwap exec ok"); 240 | System.out.println("tradealiwap exec ok"); 241 | 242 | return; 243 | 244 | } 245 | 246 | /** 247 | * 生成二维码图片并直接以流的形式输出到页面 248 | * 249 | * @param code_url 250 | * @param response 251 | */ 252 | @RequestMapping("qr_code.img") 253 | @ResponseBody 254 | public void getQRCode(String code_url, HttpServletResponse response) { 255 | Util.encodeQrcode(code_url, response); 256 | } 257 | 258 | private String toSuccessJson(final String msg) { 259 | return "{\"returnCode\":1,\"code\":0,\"body\":" + msg + "}"; 260 | } 261 | 262 | private String toFailureJson(final String msg) { 263 | return "{\"returnCode\":0,\"code\":1,\"body\":" + msg + "}"; 264 | } 265 | 266 | private String getClientIP(HttpServletRequest request) { 267 | String ip = request.getHeader("X-Forwarded-For"); 268 | if (ip == null || ip.length() == 0 || "unknown".equals(ip)) { 269 | ip = request.getHeader("Proxy-Client-IP"); 270 | } 271 | if (ip == null || ip.length() == 0 || "unknown".equals(ip)) { 272 | ip = request.getHeader("WL-Proxy-Client-IP"); 273 | } 274 | if (ip == null || ip.length() == 0 || "unknown".equals(ip)) { 275 | ip = request.getRemoteAddr(); 276 | } 277 | if (ip.equals("0:0:0:0:0:0:0:1")) { 278 | 279 | ip = "127.0.0.1"; 280 | } 281 | return ip == null ? "" : ip; 282 | } 283 | 284 | private static final String handleJSAPI(String data) { 285 | 286 | SortedMap params = new TreeMap(); 287 | String timeStamp = String.valueOf(new java.util.Date().getTime()).substring(0, 10); 288 | String nonceStr = WxPayHelper.getNonceStr(); 289 | params.put("appId", WxPayProperties.APP_ID); 290 | params.put("timeStamp", timeStamp); 291 | params.put("nonceStr", nonceStr); 292 | params.put("package","prepay_id=" + data); 293 | params.put("signType", "MD5"); 294 | String sign = WxPayHelper.createSign(params); 295 | 296 | return WxPayProperties.jsapiData.replace("$appId", WxPayProperties.APP_ID).replace("$timeStamp", timeStamp) 297 | .replace("$nonceStr", nonceStr).replace("$prepayId", "prepay_id=" + data).replace("$paySign", sign) 298 | .replace("$url", "http://m.zhugexuetang.cn"); 299 | } 300 | 301 | private String handleData(String data) { 302 | 303 | data = Base64.base64Decode(data); 304 | 305 | String dec = ProtectData.AESDecrypt(Constant.AESkeyStr, data); 306 | 307 | return dec; 308 | } 309 | 310 | private final ValidateResult validateTradeDataForWx(String data, HttpServletRequest request) { 311 | 312 | ValidateResult result = new ValidateResult(); 313 | result.setTrade(null); 314 | result.setResult(toFailureJson("ok")); 315 | String dec = this.handleData(data); 316 | if (dec.equals("")) { 317 | 318 | result.setResult(toFailureJson("解密失败")); 319 | 320 | return result; 321 | } 322 | 323 | String[] tradeParam = dec.split("&"); 324 | Trade trade = new Trade(); 325 | trade.setBody(tradeParam[1].split("=")[1]); 326 | if (trade.getBody().length() > 20) { 327 | trade.setBody(trade.getBody().substring(0, 20) + "..."); 328 | } 329 | trade.setOrderId(tradeParam[0].split("=")[1]); 330 | trade.setIp(this.getClientIP(request)); 331 | trade.setPayType(PayType.WX_PAY_QC); 332 | 333 | String orderId = trade.getOrderId(); 334 | 335 | OrderInfo info = tradeService.getOrderInfo(orderId); 336 | 337 | if (info.getNum() == 0) { 338 | 339 | result.setResult(toFailureJson("获取订单表订单失败")); 340 | 341 | return result; 342 | } 343 | 344 | if (info.getState()) { 345 | 346 | result.setResult(toFailureJson("订单已经支付成功")); 347 | 348 | return result; 349 | } 350 | 351 | trade.setTotalFee(info.getTotalFee()); 352 | 353 | if (trade.getTotalFee().compareTo(BigDecimal.ZERO) == 0) { 354 | 355 | result.setResult(toFailureJson("获取订单表订单失败")); 356 | 357 | return result; 358 | } 359 | 360 | 361 | result.setTrade(trade); 362 | 363 | return result; 364 | } 365 | private final ValidateResult validateTradeDataForWxjs(String data, HttpServletRequest request) { 366 | 367 | ValidateResult result = new ValidateResult(); 368 | result.setTrade(null); 369 | result.setResult(toFailureJson("ok")); 370 | String dec = this.handleData(data); 371 | if (dec.equals("")) { 372 | 373 | result.setResult(toFailureJson("解密失败")); 374 | 375 | return result; 376 | } 377 | 378 | String[] tradeParam = dec.split("&"); 379 | Trade trade = new Trade(); 380 | trade.setBody(tradeParam[2].split("=")[1]); 381 | if (trade.getBody().length() > 20) { 382 | trade.setBody(trade.getBody().substring(0, 20) + "..."); 383 | } 384 | trade.setOrderId(tradeParam[0].split("=")[1]); 385 | trade.setIp(this.getClientIP(request)); 386 | trade.setPayType(PayType.WX_PAY_JSAPI); 387 | 388 | trade.setOpenId(tradeParam[1].split("=")[1]); 389 | 390 | String orderId = trade.getOrderId(); 391 | 392 | OrderInfo info = tradeService.getOrderInfo(orderId); 393 | 394 | if (info.getNum() == 0) { 395 | 396 | result.setResult(toFailureJson("获取订单表订单失败")); 397 | 398 | return result; 399 | } 400 | 401 | if (info.getState()) { 402 | 403 | result.setResult(toFailureJson("订单已经支付成功")); 404 | 405 | return result; 406 | } 407 | 408 | trade.setTotalFee(info.getTotalFee()); 409 | 410 | if (trade.getTotalFee().compareTo(BigDecimal.ZERO) == 0) { 411 | 412 | result.setResult(toFailureJson("获取订单表订单失败")); 413 | 414 | return result; 415 | } 416 | 417 | 418 | result.setTrade(trade); 419 | return result; 420 | } 421 | 422 | private final ValidateResult validateTradeDataForAliBank(String data, HttpServletRequest request) { 423 | 424 | ValidateResult result = new ValidateResult(); 425 | result.setTrade(null); 426 | result.setResult(toFailureJson("ok")); 427 | String dec = this.handleData(data); 428 | if (dec.equals("")) { 429 | 430 | result.setResult(toFailureJson("解密失败")); 431 | 432 | return result; 433 | } 434 | 435 | String[] tradeParam = dec.split("&"); 436 | Trade trade = new Trade(); 437 | trade.setBody(tradeParam[2].split("=")[1]); 438 | if (trade.getBody().length() > 20) { 439 | trade.setBody(trade.getBody().substring(0, 20) + "..."); 440 | } 441 | 442 | trade.setOrderId(tradeParam[0].split("=")[1]); 443 | trade.setIp(this.getClientIP(request)); 444 | trade.setPayType(PayType.ALI_PAY_NetBank); 445 | trade.setBank(tradeParam[1].split("=")[1]); 446 | String orderId = trade.getOrderId(); 447 | 448 | OrderInfo info = tradeService.getOrderInfo(orderId); 449 | 450 | if (info.getNum() == 0) { 451 | 452 | result.setResult(toFailureJson("获取订单表订单失败")); 453 | 454 | return result; 455 | } 456 | 457 | if (info.getState()) { 458 | 459 | result.setResult(toFailureJson("订单已经支付成功")); 460 | 461 | return result; 462 | } 463 | trade.setTotalFee(info.getTotalFee()); 464 | 465 | if (trade.getTotalFee().compareTo(BigDecimal.ZERO) == 0) { 466 | 467 | result.setResult(toFailureJson("获取订单表订单失败")); 468 | 469 | return result; 470 | } 471 | 472 | result.setTrade(trade); 473 | return result; 474 | } 475 | 476 | private final ValidateResult validateTradeDataForAliDirect(String data, HttpServletRequest request) { 477 | 478 | ValidateResult result = new ValidateResult(); 479 | result.setTrade(null); 480 | result.setResult(toFailureJson("ok")); 481 | String dec = this.handleData(data); 482 | if (dec.equals("")) { 483 | 484 | result.setResult(toFailureJson("解密失败")); 485 | 486 | return result; 487 | } 488 | 489 | String[] tradeParam = dec.split("&"); 490 | Trade trade = new Trade(); 491 | trade.setBody(tradeParam[1].split("=")[1]); 492 | if (trade.getBody().length() > 20) { 493 | trade.setBody(trade.getBody().substring(0, 20) + "..."); 494 | } 495 | 496 | 497 | trade.setOrderId(tradeParam[0].split("=")[1]); 498 | trade.setIp(this.getClientIP(request)); 499 | trade.setPayType(PayType.ALI_PAY_Direct); 500 | 501 | String orderId = trade.getOrderId(); 502 | 503 | OrderInfo info = tradeService.getOrderInfo(orderId); 504 | 505 | if (info.getNum() == 0) { 506 | 507 | result.setResult(toFailureJson("获取订单表订单失败")); 508 | 509 | return result; 510 | } 511 | 512 | if (info.getState()) { 513 | 514 | result.setResult(toFailureJson("订单已经支付成功")); 515 | 516 | return result; 517 | } 518 | 519 | trade.setTotalFee(info.getTotalFee()); 520 | 521 | if (trade.getTotalFee().compareTo(BigDecimal.ZERO) == 0) { 522 | 523 | result.setResult(toFailureJson("获取订单表订单失败")); 524 | 525 | return result; 526 | } 527 | 528 | 529 | result.setTrade(trade); 530 | return result; 531 | } 532 | private final ValidateResult validateTradeDataForAliWAP(String data, HttpServletRequest request) { 533 | 534 | ValidateResult result = new ValidateResult(); 535 | result.setTrade(null); 536 | result.setResult(toFailureJson("ok")); 537 | String dec = this.handleData(data); 538 | if (dec.equals("")) { 539 | 540 | result.setResult(toFailureJson("解密失败")); 541 | 542 | return result; 543 | } 544 | 545 | String[] tradeParam = dec.split("&"); 546 | Trade trade = new Trade(); 547 | trade.setBody(tradeParam[1].split("=")[1]); 548 | if (trade.getBody().length() > 20) { 549 | trade.setBody(trade.getBody().substring(0, 20) + "..."); 550 | } 551 | 552 | trade.setOrderId(tradeParam[0].split("=")[1]); 553 | trade.setIp(this.getClientIP(request)); 554 | trade.setPayType(PayType.ALI_PAY_WAP); 555 | String orderId = trade.getOrderId(); 556 | 557 | OrderInfo info = tradeService.getOrderInfo(orderId); 558 | 559 | if (info.getNum() == 0) { 560 | 561 | result.setResult(toFailureJson("获取订单表订单失败")); 562 | 563 | return result; 564 | } 565 | 566 | if (info.getState()) { 567 | 568 | result.setResult(toFailureJson("订单已经支付成功")); 569 | 570 | return result; 571 | } 572 | 573 | trade.setTotalFee(info.getTotalFee()); 574 | 575 | if (trade.getTotalFee().compareTo(BigDecimal.ZERO) == 0) { 576 | 577 | result.setResult(toFailureJson("获取订单表订单失败")); 578 | 579 | return result; 580 | } 581 | 582 | 583 | result.setTrade(trade); 584 | return result; 585 | } 586 | 587 | private class ValidateResult { 588 | 589 | public Trade getTrade() { 590 | return trade; 591 | } 592 | 593 | public void setTrade(Trade trade) { 594 | this.trade = trade; 595 | } 596 | 597 | public String getResult() { 598 | return result; 599 | } 600 | 601 | public void setResult(String result) { 602 | this.result = result; 603 | } 604 | 605 | private Trade trade; 606 | private String result; 607 | } 608 | } -------------------------------------------------------------------------------- /src/main/java/spring/boot/pay/dao/BaseDao.java: -------------------------------------------------------------------------------- 1 | package spring.boot.pay.dao; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.beans.factory.annotation.Qualifier; 5 | import org.springframework.jdbc.core.JdbcTemplate; 6 | import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; 7 | 8 | public class BaseDao { 9 | 10 | @Autowired 11 | @Qualifier("pay") 12 | protected JdbcTemplate jdbcTemplate; 13 | 14 | @Autowired 15 | @Qualifier("namePay") 16 | protected NamedParameterJdbcTemplate namedParameterJdbcTemplate; 17 | 18 | @Autowired 19 | @Qualifier("order") 20 | protected JdbcTemplate jdbcTemplateOrder; 21 | 22 | @Autowired 23 | @Qualifier("nameOrder") 24 | protected NamedParameterJdbcTemplate namedParameterJdbcTemplateOrder; 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/spring/boot/pay/dao/TradeDao.java: -------------------------------------------------------------------------------- 1 | package spring.boot.pay.dao; 2 | 3 | import java.math.BigDecimal; 4 | 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import org.springframework.stereotype.Repository; 8 | 9 | import spring.boot.pay.model.Trade; 10 | 11 | @Repository 12 | public class TradeDao extends BaseDao { 13 | 14 | private Logger logger = LoggerFactory.getLogger(getClass()); 15 | 16 | /** 17 | * 保存交易请求 18 | * 19 | * @param trade 20 | * 交易请求 21 | * @return 成功返回交易请求的id,保存失败返回-1 22 | */ 23 | public int save(Trade trade) { 24 | 25 | throw new UnsupportedOperationException(); 26 | 27 | } 28 | 29 | /** 30 | * 更新交易的结果 31 | * 32 | * @param trade 33 | * 交易结果 34 | */ 35 | public void updateReturnContent(Trade trade) { 36 | 37 | throw new UnsupportedOperationException(); 38 | } 39 | 40 | /** 41 | * 更新交易的结果 42 | * 43 | * @param trade 44 | * 交易结果 45 | */ 46 | public void updateResult(Trade trade) { 47 | throw new UnsupportedOperationException(); 48 | } 49 | 50 | /** 51 | * 更新交易的状态,用于交易完成后 52 | * 53 | * @param trade 54 | * 交易的状态 55 | */ 56 | public void updateStatus(int status, String orderId) { 57 | 58 | throw new UnsupportedOperationException(); 59 | } 60 | 61 | /** 62 | * 查询订单号是否在交易中,以及状态 63 | */ 64 | public Trade checkOrderStatus(Trade trade) { 65 | throw new UnsupportedOperationException(); 66 | } 67 | 68 | public BigDecimal getOrderTotalPrice(String orderId) { 69 | throw new UnsupportedOperationException(); 70 | } 71 | 72 | 73 | } -------------------------------------------------------------------------------- /src/main/java/spring/boot/pay/framework/base/PayStatus.java: -------------------------------------------------------------------------------- 1 | package spring.boot.pay.framework.base; 2 | 3 | 4 | public interface PayStatus { 5 | 6 | int STATUS_ACCEPTED = 0;//已接收 7 | int STATUS_TRYING = 1; //正在进行 8 | int STATUS_FAIL = 2; //失败 9 | int STATUS_SUCCESS = 3; //成功 10 | 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/spring/boot/pay/framework/base/PayTask.java: -------------------------------------------------------------------------------- 1 | package spring.boot.pay.framework.base; 2 | 3 | 4 | public interface PayTask { 5 | 6 | int STATUS_ACCEPTED = 0;//已接收 7 | int STATUS_TRYING = 1; //正在进行 8 | int STATUS_FAIL = 2; //失败 9 | int STATUS_SUCCESS = 3; //成功 10 | int STATUS_FirstFailed_pre=4;//未得到第三方支付响应提供的预支付id 11 | int STATUS_FirstFailed_qc=5;//未得到第三方支付响应提供的codeurl 12 | 13 | 14 | 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/spring/boot/pay/framework/base/ResultModel.java: -------------------------------------------------------------------------------- 1 | package spring.boot.pay.framework.base; 2 | 3 | 4 | public final class ResultModel { 5 | 6 | public static final int RETURN_CODE_SUCCESS = 0; 7 | public static final int RETURN_CODE_FAIL = 1; 8 | 9 | private ResultModel(int code, Object body) { 10 | this.code = code; 11 | this.body = body; 12 | } 13 | 14 | private int code = 0; 15 | private Object body; 16 | 17 | 18 | public int getCode() { 19 | return code; 20 | } 21 | public Object getBody() { 22 | return body; 23 | } 24 | public void setBody(Object body) { 25 | this.body = body; 26 | } 27 | public boolean isSuccess(){ 28 | return getCode() == RETURN_CODE_SUCCESS; 29 | } 30 | public boolean isFail(){ 31 | return getCode() != RETURN_CODE_SUCCESS; 32 | } 33 | 34 | 35 | public static ResultModel buildModel(boolean flag){ 36 | if(flag) 37 | return successModel(); 38 | return failModel(); 39 | } 40 | public static ResultModel successModel(){ 41 | return successModel(""); 42 | } 43 | public static ResultModel successModel(Object body){ 44 | return new ResultModel(RETURN_CODE_SUCCESS,body); 45 | } 46 | public static ResultModel failModel(){ 47 | return failModel(""); 48 | } 49 | public static ResultModel failModel(Object body){ 50 | return new ResultModel(RETURN_CODE_FAIL,body); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/spring/boot/pay/framework/filter/LogFilter.java: -------------------------------------------------------------------------------- 1 | package spring.boot.pay.framework.filter; 2 | 3 | 4 | import java.io.IOException; 5 | 6 | import javax.servlet.Filter; 7 | import javax.servlet.FilterChain; 8 | import javax.servlet.FilterConfig; 9 | import javax.servlet.ServletException; 10 | import javax.servlet.ServletRequest; 11 | import javax.servlet.ServletResponse; 12 | import javax.servlet.http.HttpServletRequest; 13 | import javax.servlet.http.HttpServletResponse; 14 | 15 | import org.slf4j.Logger; 16 | import org.slf4j.LoggerFactory; 17 | import org.springframework.stereotype.Component; 18 | 19 | import com.alibaba.fastjson.JSON; 20 | 21 | import spring.boot.pay.common.HttpServRequestHelper; 22 | 23 | @Component 24 | public class LogFilter implements Filter { 25 | 26 | Logger logger = LoggerFactory.getLogger(getClass()); 27 | 28 | @Override 29 | public void init(FilterConfig filterConfig) throws ServletException { 30 | 31 | } 32 | 33 | @Override 34 | public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { 35 | 36 | HttpServletRequest req = (HttpServletRequest) request; 37 | HttpServletResponse resp = (HttpServletResponse)response; 38 | resp.setHeader("Access-Control-Allow-Origin", "*.*.com"); 39 | 40 | long start = System.nanoTime(); 41 | 42 | try { 43 | chain.doFilter(request,response); 44 | }catch (Exception e){ 45 | logger.error(e.getMessage(),e); 46 | } 47 | 48 | long end = System.nanoTime(); 49 | 50 | String url = req.getRequestURL().toString(); 51 | 52 | if(logger.isInfoEnabled()) 53 | logger.info("ip:{} , url: {} , time: {} , params:{}",getClientIP(req),url,end-start, JSON.toJSON(HttpServRequestHelper.genSortedMap(req))); 54 | 55 | } 56 | 57 | @Override 58 | public void destroy() { 59 | 60 | } 61 | 62 | private String getClientIP(HttpServletRequest request) { 63 | 64 | String ip = request.getHeader("X-Forwarded-For"); 65 | 66 | if (ip == null || ip.length() == 0 || "unknown".equals(ip)) { 67 | ip = request.getHeader("Proxy-Client-IP"); 68 | } 69 | if (ip == null || ip.length() == 0 || "unknown".equals(ip)) { 70 | ip = request.getHeader("WL-Proxy-Client-IP"); 71 | } 72 | if (ip == null || ip.length() == 0 || "unknown".equals(ip)) { 73 | ip = request.getRemoteAddr(); 74 | } 75 | return ip==null?"":ip; 76 | } 77 | } 78 | 79 | -------------------------------------------------------------------------------- /src/main/java/spring/boot/pay/framework/processor/Processor.java: -------------------------------------------------------------------------------- 1 | package spring.boot.pay.framework.processor; 2 | 3 | import spring.boot.pay.framework.base.PayTask; 4 | import spring.boot.pay.framework.base.ResultModel; 5 | 6 | public interface Processor { 7 | 8 | ResultModel process(T t); 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/spring/boot/pay/framework/processor/ProcessorManager.java: -------------------------------------------------------------------------------- 1 | package spring.boot.pay.framework.processor; 2 | 3 | import java.lang.reflect.ParameterizedType; 4 | import java.lang.reflect.Type; 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | 8 | import org.springframework.beans.BeansException; 9 | import org.springframework.beans.factory.config.BeanPostProcessor; 10 | import org.springframework.stereotype.Component; 11 | 12 | import com.alibaba.fastjson.JSON; 13 | 14 | import spring.boot.pay.framework.base.PayTask; 15 | import spring.boot.pay.framework.base.ResultModel; 16 | 17 | @Component 18 | public class ProcessorManager implements BeanPostProcessor { 19 | 20 | private static final Class DEF_TASK_TYPE = Object.class; 21 | 22 | private Map processorMap = new HashMap(); 23 | private Map resolverMap = new HashMap(); 24 | 25 | 26 | public ResultModel process(PayTask payTask) { 27 | 28 | Class taskType = payTask.getClass(); 29 | 30 | ProcessorResolver resolver = resolverMap.get(taskType); 31 | if(resolver == null){ 32 | resolver = resolverMap.get(DEF_TASK_TYPE); 33 | } 34 | if(resolver == null){ 35 | throw new RuntimeException("could not find processor to process task:"+ JSON.toJSONString(payTask)); 36 | } 37 | 38 | Class processorClass = resolver.resolve(payTask); 39 | if(processorClass == null){ 40 | throw new RuntimeException("could not find processor to process task:"+ JSON.toJSONString(payTask)); 41 | } 42 | 43 | return processorMap.get(processorClass).process(payTask); 44 | } 45 | 46 | 47 | @Override 48 | public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { 49 | return bean; 50 | } 51 | 52 | @Override 53 | public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { 54 | 55 | if(bean instanceof Processor){ 56 | Processor processor = (Processor)bean; 57 | processorMap.put(processor.getClass(),processor); 58 | } 59 | 60 | if(bean instanceof ProcessorResolver){ 61 | Class taskType = getGeneric(bean.getClass(),ProcessorResolver.class); 62 | if(taskType == null){ 63 | taskType = DEF_TASK_TYPE; 64 | } 65 | 66 | ProcessorResolver processorResolver = resolverMap.get(taskType); 67 | //每种任务类型只能有一个ProcessorResolver 68 | if(processorResolver != null){ 69 | throw new RuntimeException("each taskType could not mapping more then one ProcessorResolver"); 70 | } 71 | resolverMap.put(taskType,(ProcessorResolver)bean); 72 | } 73 | 74 | return bean; 75 | } 76 | 77 | public static Class getGeneric(Class clazz,Class superClass){ 78 | Type[] types = clazz.getGenericInterfaces(); 79 | for( Type type : types ){ 80 | if( !( type.getTypeName().contains(superClass.getTypeName()) ) ){ 81 | continue; 82 | } 83 | if( !(type instanceof ParameterizedType) ){ 84 | continue; 85 | } 86 | 87 | ParameterizedType parameterizedType = (ParameterizedType)type; 88 | return (Class)parameterizedType.getActualTypeArguments()[0]; 89 | } 90 | return null; 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /src/main/java/spring/boot/pay/framework/processor/ProcessorResolver.java: -------------------------------------------------------------------------------- 1 | package spring.boot.pay.framework.processor; 2 | 3 | import spring.boot.pay.framework.base.PayTask; 4 | 5 | public interface ProcessorResolver{ 6 | 7 | Class resolve(T task); 8 | 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/spring/boot/pay/framework/security/CheckIpEndpoint.java: -------------------------------------------------------------------------------- 1 | package spring.boot.pay.framework.security; 2 | 3 | import java.lang.annotation.Documented; 4 | import java.lang.annotation.ElementType; 5 | import java.lang.annotation.Retention; 6 | import java.lang.annotation.RetentionPolicy; 7 | import java.lang.annotation.Target; 8 | 9 | /** 10 | * 加上该注解后 只有白名单中的ip请求才可以访问此方法 11 | */ 12 | @Target(ElementType.METHOD) 13 | @Retention(RetentionPolicy.RUNTIME) 14 | @Documented 15 | public @interface CheckIpEndpoint { 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/spring/boot/pay/framework/security/CheckSignEndpoint.java: -------------------------------------------------------------------------------- 1 | package spring.boot.pay.framework.security; 2 | 3 | import java.lang.annotation.Documented; 4 | import java.lang.annotation.ElementType; 5 | import java.lang.annotation.Retention; 6 | import java.lang.annotation.RetentionPolicy; 7 | import java.lang.annotation.Target; 8 | 9 | /** 10 | * 加上该注解后 只有白名单中的ip请求才可以访问此方法 11 | */ 12 | @Target(ElementType.METHOD) 13 | @Retention(RetentionPolicy.RUNTIME) 14 | @Documented 15 | public @interface CheckSignEndpoint { 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/spring/boot/pay/framework/security/SecurityManager.java: -------------------------------------------------------------------------------- 1 | package spring.boot.pay.framework.security; 2 | 3 | import java.util.Set; 4 | 5 | import javax.servlet.http.HttpServletRequest; 6 | 7 | import org.aspectj.lang.ProceedingJoinPoint; 8 | import org.aspectj.lang.annotation.Around; 9 | import org.aspectj.lang.annotation.Aspect; 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | import org.springframework.context.annotation.Configuration; 13 | 14 | import spring.boot.pay.config.properties.SecurityProperties; 15 | 16 | 17 | @Configuration 18 | @Aspect 19 | public class SecurityManager { 20 | 21 | private static final Logger LOGGER = LoggerFactory.getLogger(SecurityManager.class); 22 | 23 | //IP白名单 24 | private Set whiteList = SecurityProperties.getWhiteList(); 25 | 26 | @Around("@annotation(spring.boot.pay.framework.security.CheckIpEndpoint)") 27 | public Object checkIP(ProceedingJoinPoint point) throws Throwable { 28 | for (Object arg : point.getArgs()) { 29 | if ( arg instanceof HttpServletRequest ) { 30 | HttpServletRequest request = (HttpServletRequest) arg; 31 | String ip = getClientIP(request); 32 | if ( whiteList.contains(ip)) { 33 | return point.proceed(); 34 | } 35 | LOGGER.warn("illegal ip:{} try to visit {}",ip,request.getRequestURI()); 36 | return null; 37 | } 38 | } 39 | throw new RuntimeException("the method which use @CheckIpEndpoint must has a HttpServletRequest as Parameter"); 40 | } 41 | 42 | 43 | // @Around("@annotation()") 44 | // public Object checkSign(ProceedingJoinPoint point) throws Throwable { 45 | // for (Object arg : point.getArgs()) { 46 | // if ( arg instanceof HttpServletRequest ) { 47 | // HttpServletRequest request = (HttpServletRequest) arg; 48 | // 49 | // return null; 50 | // } 51 | // } 52 | // throw new RuntimeException("the method which use @CheckSignEndpoint must has a HttpServletRequest as Parameter"); 53 | // } 54 | // 55 | // 56 | // /** 57 | // * 检查签名 58 | // */ 59 | // public ResultModel checkSign(HttpServletRequest request){ 60 | // 61 | // String accountType = request.getParameter("accountType"); 62 | // String sign = request.getParameter("sign"); 63 | // 64 | // if(accountType == null || sign == null || (!accountType.matches("\\d{1,3}")) ){ 65 | // return ResultModel.failModel(); 66 | // } 67 | // 68 | // SignBuilder signBuilder = new SignBuilder(partnerCache.getSecret(Integer.parseInt(accountType))); 69 | // Enumeration parameterNames = request.getParameterNames(); 70 | // while ( parameterNames.hasMoreElements() ){ 71 | // String key = parameterNames.nextElement(); 72 | // signBuilder.append(key,request.getParameter(key)); 73 | // } 74 | // String realSign = signBuilder.build(); 75 | // 76 | // if(sign.equalsIgnoreCase(realSign)){ 77 | // return ResultModel.successModel(); 78 | // } 79 | // 80 | // return ResultModel.failModel(); 81 | // } 82 | 83 | private String getClientIP(HttpServletRequest request) { 84 | String ip = request.getHeader("X-Forwarded-For"); 85 | if (ip == null || ip.length() == 0 || "unknown".equals(ip)) { 86 | ip = request.getHeader("Proxy-Client-IP"); 87 | } 88 | if (ip == null || ip.length() == 0 || "unknown".equals(ip)) { 89 | ip = request.getHeader("WL-Proxy-Client-IP"); 90 | } 91 | if (ip == null || ip.length() == 0 || "unknown".equals(ip)) { 92 | ip = request.getRemoteAddr(); 93 | } 94 | return ip==null?"":ip; 95 | } 96 | 97 | } 98 | -------------------------------------------------------------------------------- /src/main/java/spring/boot/pay/model/CouponInfo.java: -------------------------------------------------------------------------------- 1 | package spring.boot.pay.model; 2 | 3 | import java.math.BigDecimal; 4 | 5 | public class CouponInfo { 6 | 7 | private String productId; 8 | private int state; 9 | private BigDecimal price; 10 | private String code; 11 | 12 | public BigDecimal getPrice() { 13 | return price; 14 | } 15 | public void setPrice(BigDecimal price) { 16 | this.price = price; 17 | } 18 | 19 | public int getState() { 20 | return state; 21 | } 22 | public void setState(int state) { 23 | this.state = state; 24 | } 25 | public String getProductId() { 26 | return productId; 27 | } 28 | public void setProductId(String productId) { 29 | this.productId = productId; 30 | } 31 | public String getCode() { 32 | return code; 33 | } 34 | public void setCode(String code) { 35 | this.code = code; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/spring/boot/pay/model/KeInfo.java: -------------------------------------------------------------------------------- 1 | package spring.boot.pay.model; 2 | 3 | import java.math.BigDecimal; 4 | 5 | //endPtime,sellPrice,buyPrice 6 | public class KeInfo { 7 | 8 | private int kid=0; 9 | private long endPtime; 10 | private BigDecimal backPlayPrice; 11 | private BigDecimal buyPrice; 12 | 13 | public int getKid() { 14 | return kid; 15 | } 16 | public void setKid(int kid) { 17 | this.kid = kid; 18 | } 19 | public long getEndPtime() { 20 | return endPtime; 21 | } 22 | public void setEndPtime(long endPtime) { 23 | this.endPtime = endPtime; 24 | } 25 | 26 | public BigDecimal getBuyPrice() { 27 | return buyPrice; 28 | } 29 | public void setBuyPrice(BigDecimal buyPrice) { 30 | this.buyPrice = buyPrice; 31 | } 32 | public BigDecimal getBackPlayPrice() { 33 | return backPlayPrice; 34 | } 35 | public void setBackPlayPrice(BigDecimal backPlayPrice) { 36 | this.backPlayPrice = backPlayPrice; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/spring/boot/pay/model/OpenIdInfo.java: -------------------------------------------------------------------------------- 1 | package spring.boot.pay.model; 2 | 3 | public class OpenIdInfo { 4 | 5 | private String access_token; 6 | private int expires_in; 7 | private String refresh_token; 8 | private String openid; 9 | private String scope; 10 | public String getAccess_token() { 11 | return access_token; 12 | } 13 | public void setAccess_token(String access_token) { 14 | this.access_token = access_token; 15 | } 16 | public int getExpires_in() { 17 | return expires_in; 18 | } 19 | public void setExpires_in(int expires_in) { 20 | this.expires_in = expires_in; 21 | } 22 | public String getRefresh_token() { 23 | return refresh_token; 24 | } 25 | public void setRefresh_token(String refresh_token) { 26 | this.refresh_token = refresh_token; 27 | } 28 | public String getOpenid() { 29 | return openid; 30 | } 31 | public void setOpenid(String openid) { 32 | this.openid = openid; 33 | } 34 | public String getScope() { 35 | return scope; 36 | } 37 | public void setScope(String scope) { 38 | this.scope = scope; 39 | } 40 | public String getUnionid() { 41 | return unionid; 42 | } 43 | public void setUnionid(String unionid) { 44 | this.unionid = unionid; 45 | } 46 | private String unionid; 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/spring/boot/pay/model/OrderInfo.java: -------------------------------------------------------------------------------- 1 | package spring.boot.pay.model; 2 | 3 | import java.math.BigDecimal; 4 | 5 | public class OrderInfo { 6 | 7 | public int num; 8 | public boolean state; 9 | public BigDecimal totalFee; 10 | public int getNum() { 11 | return num; 12 | } 13 | public void setNum(int num) { 14 | this.num = num; 15 | } 16 | public boolean getState() { 17 | return state; 18 | } 19 | public void setState(boolean state) { 20 | this.state = state; 21 | } 22 | public BigDecimal getTotalFee() { 23 | return totalFee; 24 | } 25 | public void setTotalFee(BigDecimal totalFee) { 26 | this.totalFee = totalFee; 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/spring/boot/pay/model/Trade.java: -------------------------------------------------------------------------------- 1 | package spring.boot.pay.model; 2 | 3 | import java.math.BigDecimal; 4 | import java.time.LocalDateTime; 5 | 6 | import spring.boot.pay.framework.base.PayTask; 7 | 8 | 9 | 10 | /** 11 | * 支付model类 12 | */ 13 | public class Trade implements PayTask{ 14 | 15 | private Integer id; 16 | //订单号 17 | private String orderId; 18 | //描述 19 | private String body; 20 | //货币类型 ,暂时只支持人民 21 | private String feeType = "CNY"; 22 | //订单总金额 23 | private BigDecimal totalFee; 24 | private double price; 25 | //支付方式(微信支付,支付宝支付) 26 | private int payType; 27 | //如使用第三方支付时的一些标示,只有使用特殊的支付方式时才需要 28 | private String openId; 29 | //如使用第三方支付时的一些标示,只有使用特殊的支付方式时才需要 30 | private String code; 31 | //支付完成后的返回页面,返回时将携带支付结果 32 | private String returnUrl; 33 | 34 | //第三方(微信,支付宝)的交易号 35 | private String outTradeId; 36 | 37 | //发起请求的时间 38 | private LocalDateTime requestTime; 39 | //获得结果的时间 40 | private LocalDateTime resultTime; 41 | 42 | //任务状态 43 | private int tradeStatus = 1;//1 成功 0 失败 44 | 45 | //调用端ip 46 | private String ip=""; 47 | private int productId; 48 | private String productType; 49 | private int orderType; 50 | private int quantity; 51 | private String requestContent; 52 | private String returnContent; 53 | private String pageReturnContent; 54 | private String deviceInfo; 55 | private long phone; 56 | private int payStatus; 57 | private String finishTime; 58 | private String bank; 59 | /** 60 | * 0支付宝 1 微信 61 | */ 62 | private int source; 63 | 64 | private String coupon; 65 | 66 | private String orgName; 67 | 68 | public String getCoupon() { 69 | return coupon; 70 | } 71 | public void setCoupon(String coupon) { 72 | this.coupon = coupon; 73 | } 74 | public String getProductType() { 75 | return productType; 76 | } 77 | public void setProductType(String productType) { 78 | this.productType = productType; 79 | } 80 | public int getOrderType() { 81 | return orderType; 82 | } 83 | public void setOrderType(int orderType) { 84 | this.orderType = orderType; 85 | } 86 | public int getQuantity() { 87 | return quantity; 88 | } 89 | public void setQuantity(int quantity) { 90 | this.quantity = quantity; 91 | } 92 | 93 | public String getOpenId() { 94 | return openId; 95 | } 96 | public void setOpenId(String openId) { 97 | this.openId = openId; 98 | } 99 | public String getReturnUrl() { 100 | return returnUrl; 101 | } 102 | public void setReturnUrl(String returnUrl) { 103 | this.returnUrl = returnUrl; 104 | } 105 | public String getOrderId() { 106 | return orderId; 107 | } 108 | public void setOrderId(String orderId) { 109 | this.orderId = orderId; 110 | } 111 | 112 | 113 | public String getFeeType() { 114 | return feeType; 115 | } 116 | public void setFeeType(String feeType) { 117 | this.feeType = feeType; 118 | } 119 | public BigDecimal getTotalFee() { 120 | return totalFee; 121 | } 122 | public void setTotalFee(BigDecimal totalFee) { 123 | this.totalFee = totalFee; 124 | } 125 | 126 | public String getBody() { 127 | return body; 128 | } 129 | public void setBody(String body) { 130 | this.body = body; 131 | } 132 | public int getPayType() { 133 | return payType; 134 | } 135 | public void setPayType(int payType) { 136 | this.payType = payType; 137 | } 138 | 139 | 140 | public String getOutTradeId() { 141 | return outTradeId; 142 | } 143 | public void setOutTradeId(String outTradeId) { 144 | this.outTradeId = outTradeId; 145 | } 146 | public Integer getId() { 147 | return id; 148 | } 149 | public void setId(Integer id) { 150 | this.id = id; 151 | } 152 | 153 | 154 | public LocalDateTime getRequestTime() { 155 | return requestTime; 156 | } 157 | 158 | public void setRequestTime(LocalDateTime requestTime) { 159 | this.requestTime = requestTime; 160 | } 161 | 162 | public LocalDateTime getResultTime() { 163 | return resultTime; 164 | } 165 | 166 | public void setResultTime(LocalDateTime resultTime) { 167 | this.resultTime = resultTime; 168 | } 169 | public int getTradeStatus() { 170 | return tradeStatus; 171 | } 172 | public void setTradeStatus(int tradeStatus) { 173 | this.tradeStatus = tradeStatus; 174 | } 175 | 176 | public String getIp() { 177 | return ip; 178 | } 179 | public void setIp(String ip) { 180 | this.ip = ip; 181 | } 182 | public int getProductId() { 183 | return productId; 184 | } 185 | public void setProductId(int productId) { 186 | this.productId = productId; 187 | } 188 | public String getRequestContent() { 189 | return requestContent; 190 | } 191 | public void setRequestContent(String requestContent) { 192 | this.requestContent = requestContent; 193 | } 194 | /** 195 | * @return the returnContent 196 | */ 197 | public String getReturnContent() { 198 | return returnContent; 199 | } 200 | /** 201 | * @param returnContent the returnContent to set 202 | */ 203 | public void setReturnContent(String returnContent) { 204 | this.returnContent = returnContent; 205 | } 206 | /** 207 | * @return the price 208 | */ 209 | public double getPrice() { 210 | return price; 211 | } 212 | /** 213 | * @param price the price to set 214 | */ 215 | public void setPrice(double price) { 216 | this.price = price; 217 | } 218 | /** 219 | * @return the deviceInfo 220 | */ 221 | public String getDeviceInfo() { 222 | return deviceInfo; 223 | } 224 | /** 225 | * @param deviceInfo the deviceInfo to set 226 | */ 227 | public void setDeviceInfo(String deviceInfo) { 228 | this.deviceInfo = deviceInfo; 229 | } 230 | /** 231 | * @return the pageReturnContent 232 | */ 233 | public String getPageReturnContent() { 234 | return pageReturnContent; 235 | } 236 | /** 237 | * @param pageReturnContent the pageReturnContent to set 238 | */ 239 | public void setPageReturnContent(String pageReturnContent) { 240 | this.pageReturnContent = pageReturnContent; 241 | } 242 | /** 243 | * @return the phone 244 | */ 245 | public long getPhone() { 246 | return phone; 247 | } 248 | /** 249 | * @param phone the phone to set 250 | */ 251 | public void setPhone(long phone) { 252 | this.phone = phone; 253 | } 254 | /** 255 | * @return the payStatus 256 | */ 257 | public int getPayStatus() { 258 | return payStatus; 259 | } 260 | /** 261 | * @param payStatus the payStatus to set 262 | */ 263 | public void setPayStatus(int payStatus) { 264 | this.payStatus = payStatus; 265 | } 266 | public String getFinishTime() { 267 | return finishTime; 268 | } 269 | public void setFinishTime(String finishTime) { 270 | this.finishTime = finishTime; 271 | } 272 | public int getSource() { 273 | return source; 274 | } 275 | /** 276 | * 277 | * @param source 0支付宝 1 微信 278 | */ 279 | public void setSource(int source) { 280 | this.source = source; 281 | } 282 | public String getCode() { 283 | return code; 284 | } 285 | public void setCode(String code) { 286 | this.code = code; 287 | } 288 | public String getBank() { 289 | return bank; 290 | } 291 | public void setBank(String bank) { 292 | this.bank = bank; 293 | } 294 | public String getOrgName() { 295 | return orgName; 296 | } 297 | public void setOrgName(String orgName) { 298 | this.orgName = orgName; 299 | } 300 | 301 | } 302 | -------------------------------------------------------------------------------- /src/main/java/spring/boot/pay/model/TradeForPost.java: -------------------------------------------------------------------------------- 1 | package spring.boot.pay.model; 2 | 3 | 4 | public class TradeForPost { 5 | 6 | 7 | //订单号 8 | private String orderId; 9 | //描述 10 | private String body; 11 | //货币类型 ,暂时只支持人民 12 | private String feeType = "CNY"; 13 | //订单总金额 14 | private double totalFee; 15 | private double price; 16 | //支付方式(微信支付,支付宝支付) 17 | private int payType; 18 | 19 | private int productId; 20 | 21 | private int quantity; 22 | 23 | private String deviceInfo; 24 | private int phone; 25 | 26 | 27 | public int getQuantity() { 28 | return quantity; 29 | } 30 | public void setQuantity(int quantity) { 31 | this.quantity = quantity; 32 | } 33 | 34 | 35 | public String getOrderId() { 36 | return orderId; 37 | } 38 | public void setOrderId(String orderId) { 39 | this.orderId = orderId; 40 | } 41 | 42 | 43 | public String getFeeType() { 44 | return feeType; 45 | } 46 | public void setFeeType(String feeType) { 47 | this.feeType = feeType; 48 | } 49 | public double getTotalFee() { 50 | return totalFee; 51 | } 52 | public void setTotalFee(double totalFee) { 53 | this.totalFee = totalFee; 54 | } 55 | 56 | public String getBody() { 57 | return body; 58 | } 59 | public void setBody(String body) { 60 | this.body = body; 61 | } 62 | public int getPayType() { 63 | return payType; 64 | } 65 | public void setPayType(int payType) { 66 | this.payType = payType; 67 | } 68 | 69 | /** 70 | * @return the price 71 | */ 72 | public double getPrice() { 73 | return price; 74 | } 75 | /** 76 | * @param price the price to set 77 | */ 78 | public void setPrice(double price) { 79 | this.price = price; 80 | } 81 | /** 82 | * @return the deviceInfo 83 | */ 84 | public String getDeviceInfo() { 85 | return deviceInfo; 86 | } 87 | /** 88 | * @param deviceInfo the deviceInfo to set 89 | */ 90 | public void setDeviceInfo(String deviceInfo) { 91 | this.deviceInfo = deviceInfo; 92 | } 93 | 94 | /** 95 | * @return the phone 96 | */ 97 | public int getPhone() { 98 | return phone; 99 | } 100 | /** 101 | * @param phone the phone to set 102 | */ 103 | public void setPhone(int phone) { 104 | this.phone = phone; 105 | } 106 | /** 107 | * @return the productId 108 | */ 109 | public int getProductId() { 110 | return productId; 111 | } 112 | /** 113 | * @param productId the productId to set 114 | */ 115 | public void setProductId(int productId) { 116 | this.productId = productId; 117 | } 118 | 119 | 120 | } 121 | -------------------------------------------------------------------------------- /src/main/java/spring/boot/pay/processor/TradeResultProcessor.java: -------------------------------------------------------------------------------- 1 | package spring.boot.pay.processor; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.stereotype.Component; 7 | 8 | import spring.boot.pay.framework.base.ResultModel; 9 | import spring.boot.pay.framework.processor.Processor; 10 | import spring.boot.pay.model.Trade; 11 | import spring.boot.pay.service.TradeService; 12 | 13 | 14 | 15 | 16 | @Component 17 | public class TradeResultProcessor implements Processor { 18 | 19 | protected Logger logger = LoggerFactory.getLogger(getClass()); 20 | 21 | @Autowired 22 | TradeService tradeService; 23 | 24 | 25 | /** 26 | * 当接收到支付结果通知后 执行一些必要的的操作 27 | */ 28 | @Override 29 | public ResultModel process(Trade result){ 30 | 31 | tradeService.updateResult(result); 32 | ResultModel updateResult =ResultModel.buildModel(true); 33 | 34 | return updateResult; 35 | 36 | 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/spring/boot/pay/resolver/TradeProcessorResolver.java: -------------------------------------------------------------------------------- 1 | package spring.boot.pay.resolver; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.stereotype.Component; 5 | 6 | import spring.boot.pay.config.dictionary.PayType; 7 | import spring.boot.pay.framework.base.PayTask; 8 | import spring.boot.pay.framework.processor.Processor; 9 | import spring.boot.pay.framework.processor.ProcessorManager; 10 | import spring.boot.pay.framework.processor.ProcessorResolver; 11 | import spring.boot.pay.model.Trade; 12 | import spring.boot.pay.processor.TradeResultProcessor; 13 | import spring.boot.pay.thirdparty.alipay.AliPayProcessor; 14 | import spring.boot.pay.thirdparty.wxpay.WxPayProcessor; 15 | 16 | 17 | 18 | 19 | 20 | @Component 21 | @SuppressWarnings("unused") 22 | public class TradeProcessorResolver implements ProcessorResolver{ 23 | 24 | @Autowired 25 | ProcessorManager processorManager; 26 | 27 | @Override 28 | public Class resolve(Trade trade) { 29 | 30 | if(trade.getTradeStatus() == PayTask.STATUS_SUCCESS) 31 | return TradeResultProcessor.class; 32 | if( trade.getPayType()==PayType.WX_PAY_JSAPI) 33 | return WxPayProcessor.class; 34 | if( trade.getPayType()==PayType.WX_PAY_QC) 35 | return WxPayProcessor.class; 36 | if( trade.getPayType()==PayType.ALI_PAY_NetBank) 37 | return AliPayProcessor.class; 38 | if( trade.getPayType()==PayType.ALI_PAY_WAP) 39 | return AliPayProcessor.class; 40 | 41 | if( trade.getPayType()==PayType.ALI_PAY_Direct){ 42 | return AliPayProcessor.class; 43 | } 44 | return WxPayProcessor.class; 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/spring/boot/pay/service/TradeService.java: -------------------------------------------------------------------------------- 1 | package spring.boot.pay.service; 2 | 3 | import java.math.BigDecimal; 4 | 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.stereotype.Component; 9 | 10 | import spring.boot.pay.dao.TradeDao; 11 | import spring.boot.pay.framework.base.ResultModel; 12 | import spring.boot.pay.framework.processor.ProcessorManager; 13 | import spring.boot.pay.model.OrderInfo; 14 | import spring.boot.pay.model.Trade; 15 | 16 | 17 | 18 | @Component 19 | public class TradeService { 20 | 21 | private static final Logger LOGGER = LoggerFactory.getLogger(TradeService.class); 22 | 23 | @Autowired 24 | TradeDao tradeDao; 25 | @Autowired 26 | ProcessorManager processorManager; 27 | 28 | public ResultModel doTrade(Trade trade){ 29 | return processorManager.process(trade); 30 | } 31 | 32 | public void updateResult(Trade trade){ 33 | tradeDao.updateResult(trade); 34 | 35 | } 36 | 37 | /** 38 | * 查询订单号是否存在,以及状态等 39 | */ 40 | public OrderInfo getOrderInfo(String orderId){ 41 | 42 | throw new UnsupportedOperationException(); 43 | } 44 | 45 | /** 46 | * 查询订单号是否在交易中,以及状态 47 | */ 48 | public BigDecimal getOrderTotalPrice(String orderId) { 49 | 50 | return tradeDao.getOrderTotalPrice(orderId); 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/spring/boot/pay/thirdparty/alipay/AliPayController.java: -------------------------------------------------------------------------------- 1 | package spring.boot.pay.thirdparty.alipay; 2 | 3 | import java.io.PrintWriter; 4 | import java.math.BigDecimal; 5 | import java.time.LocalDateTime; 6 | import java.util.HashMap; 7 | import java.util.Iterator; 8 | import java.util.Map; 9 | 10 | import javax.servlet.http.HttpServletRequest; 11 | import javax.servlet.http.HttpServletResponse; 12 | 13 | import org.springframework.beans.factory.annotation.Autowired; 14 | import org.springframework.stereotype.Controller; 15 | import org.springframework.web.bind.annotation.RequestMapping; 16 | 17 | import spring.boot.pay.config.properties.AliPayProperties; 18 | import spring.boot.pay.framework.base.PayStatus; 19 | import spring.boot.pay.framework.base.PayTask; 20 | import spring.boot.pay.framework.base.ResultModel; 21 | import spring.boot.pay.framework.processor.ProcessorManager; 22 | import spring.boot.pay.model.Trade; 23 | 24 | @Controller 25 | public class AliPayController { 26 | @Autowired 27 | ProcessorManager processorManager; 28 | 29 | @RequestMapping(value = AliPayProperties.notify_url) 30 | public void notify(HttpServletRequest request, HttpServletResponse response) throws Exception { 31 | 32 | // 获取支付宝POST过来反馈信息 33 | Map params = new HashMap(); 34 | Map requestParams = request.getParameterMap(); 35 | String requestContent = ""; 36 | for (Iterator iter = requestParams.keySet().iterator(); iter.hasNext();) { 37 | String name = (String) iter.next(); 38 | String[] values = (String[]) requestParams.get(name); 39 | String valueStr = ""; 40 | for (int i = 0; i < values.length; i++) { 41 | valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ","; 42 | 43 | } 44 | // 乱码解决,这段代码在出现乱码时使用。如果mysign和sign不相等也可以使用这段代码转化 45 | // valueStr = new String(valueStr.getBytes("ISO-8859-1"), "gbk"); 46 | params.put(name, valueStr); 47 | 48 | requestContent += name + ":" + valueStr + "|"; 49 | } 50 | 51 | // 获取支付宝的通知返回参数,可参考技术文档中页面跳转同步通知参数列表(以下仅供参考)// 52 | // 商户订单号 53 | if(request.getParameter("out_trade_no")==null){ 54 | 55 | return; 56 | } 57 | String out_trade_no = new String(request.getParameter("out_trade_no").getBytes("ISO-8859-1"), "UTF-8"); 58 | 59 | // 支付宝交易号 60 | 61 | String trade_no = new String(request.getParameter("trade_no").getBytes("ISO-8859-1"), "UTF-8"); 62 | 63 | // 交易状态 64 | String trade_status = new String(request.getParameter("trade_status").getBytes("ISO-8859-1"), "UTF-8"); 65 | // 交易状态 66 | String total_fee = new String(request.getParameter("total_fee").getBytes("ISO-8859-1"), "UTF-8"); 67 | // 获取支付宝的通知返回参数,可参考技术文档中页面跳转同步通知参数列表(以上仅供参考)// 68 | PrintWriter out = response.getWriter(); 69 | 70 | if (AliPayHelper.verify(params)) {// 验证成功 71 | 72 | 73 | Trade payResult = new Trade(); 74 | payResult.setPayStatus( 75 | "TRADE_SUCCESS".equals(trade_status) ? PayStatus.STATUS_SUCCESS : PayStatus.STATUS_FAIL); 76 | payResult.setTradeStatus( 77 | "TRADE_SUCCESS".equals(trade_status) ? PayTask.STATUS_SUCCESS : PayTask.STATUS_FAIL); 78 | String totalFeeStr = total_fee; 79 | payResult.setTotalFee(new BigDecimal(totalFeeStr)); 80 | payResult.setOrderId(out_trade_no); 81 | payResult.setOutTradeId(trade_no); 82 | payResult.setReturnContent(requestContent); 83 | payResult.setResultTime(LocalDateTime.now()); 84 | 85 | payResult.setSource(2);//来源为阿里银行 86 | ResultModel processResult = processorManager.process(payResult); 87 | 88 | out.write("sucess"); 89 | } else {// 验证失败 90 | out.write("failure"); 91 | } 92 | 93 | out.flush(); 94 | out.close(); 95 | } 96 | 97 | @RequestMapping(value = AliPayProperties.notify_url_direct) 98 | public void notifyDirect(HttpServletRequest request, HttpServletResponse response) throws Exception { 99 | 100 | // 获取支付宝POST过来反馈信息 101 | Map params = new HashMap(); 102 | Map requestParams = request.getParameterMap(); 103 | String requestContent = ""; 104 | for (Iterator iter = requestParams.keySet().iterator(); iter.hasNext();) { 105 | String name = (String) iter.next(); 106 | String[] values = (String[]) requestParams.get(name); 107 | String valueStr = ""; 108 | for (int i = 0; i < values.length; i++) { 109 | valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ","; 110 | 111 | } 112 | // 乱码解决,这段代码在出现乱码时使用。如果mysign和sign不相等也可以使用这段代码转化 113 | // valueStr = new String(valueStr.getBytes("ISO-8859-1"), "gbk"); 114 | params.put(name, valueStr); 115 | 116 | requestContent += name + ":" + valueStr + "|"; 117 | } 118 | 119 | // 获取支付宝的通知返回参数,可参考技术文档中页面跳转同步通知参数列表(以下仅供参考)// 120 | // 商户订单号 121 | if(request.getParameter("out_trade_no")==null){ 122 | 123 | return; 124 | } 125 | String out_trade_no = new String(request.getParameter("out_trade_no").getBytes("ISO-8859-1"), "UTF-8"); 126 | 127 | // 支付宝交易号 128 | 129 | String trade_no = new String(request.getParameter("trade_no").getBytes("ISO-8859-1"), "UTF-8"); 130 | 131 | // 交易状态 132 | String trade_status = new String(request.getParameter("trade_status").getBytes("ISO-8859-1"), "UTF-8"); 133 | // 交易状态 134 | String total_fee = new String(request.getParameter("total_fee").getBytes("ISO-8859-1"), "UTF-8"); 135 | // 获取支付宝的通知返回参数,可参考技术文档中页面跳转同步通知参数列表(以上仅供参考)// 136 | PrintWriter out = response.getWriter(); 137 | 138 | if (AliPayHelper.verify(params)) {// 验证成功 139 | 140 | 141 | Trade payResult = new Trade(); 142 | payResult.setPayStatus( 143 | "TRADE_SUCCESS".equals(trade_status) ? PayStatus.STATUS_SUCCESS : PayStatus.STATUS_FAIL); 144 | payResult.setTradeStatus( 145 | "TRADE_SUCCESS".equals(trade_status) ? PayTask.STATUS_SUCCESS : PayTask.STATUS_FAIL); 146 | String totalFeeStr = total_fee; 147 | payResult.setTotalFee(new BigDecimal(totalFeeStr)); 148 | payResult.setOrderId(out_trade_no); 149 | payResult.setOutTradeId(trade_no); 150 | payResult.setReturnContent(requestContent); 151 | payResult.setResultTime(LocalDateTime.now()); 152 | 153 | payResult.setSource(0);//来源为即时到帐 154 | ResultModel processResult = processorManager.process(payResult); 155 | 156 | out.write("sucess"); 157 | } else {// 验证失败 158 | out.write("failure"); 159 | } 160 | 161 | out.flush(); 162 | out.close(); 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /src/main/java/spring/boot/pay/thirdparty/alipay/AliPayHelper.java: -------------------------------------------------------------------------------- 1 | package spring.boot.pay.thirdparty.alipay; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.IOException; 5 | import java.io.InputStreamReader; 6 | import java.net.HttpURLConnection; 7 | import java.net.MalformedURLException; 8 | import java.net.URL; 9 | import java.util.ArrayList; 10 | import java.util.Collections; 11 | import java.util.HashMap; 12 | import java.util.List; 13 | import java.util.Map; 14 | 15 | import org.apache.http.NameValuePair; 16 | import org.apache.http.message.BasicNameValuePair; 17 | import org.dom4j.Document; 18 | import org.dom4j.DocumentException; 19 | import org.dom4j.Node; 20 | import org.dom4j.io.SAXReader; 21 | 22 | import spring.boot.pay.common.encrypt.AliMD5; 23 | import spring.boot.pay.common.http.HttpClientHelper; 24 | import spring.boot.pay.config.properties.AliPayProperties; 25 | 26 | 27 | public class AliPayHelper { 28 | 29 | /** 30 | * 除去数组中的空值和签名参数 31 | * @param sArray 签名参数组 32 | * @return 去掉空值与签名参数后的新签名参数组 33 | */ 34 | public static Map paraFilter(Map sArray) { 35 | 36 | Map result = new HashMap(); 37 | 38 | if (sArray == null || sArray.size() <= 0) { 39 | return result; 40 | } 41 | 42 | for (String key : sArray.keySet()) { 43 | String value = sArray.get(key); 44 | if (value == null || value.equals("") || key.equalsIgnoreCase("sign") 45 | || key.equalsIgnoreCase("sign_type")) { 46 | continue; 47 | } 48 | result.put(key, value); 49 | } 50 | 51 | return result; 52 | } 53 | 54 | /** 55 | * 把数组所有元素排序,并按照“参数=参数值”的模式用“&”字符拼接成字符串 56 | * @param params 需要排序并参与字符拼接的参数组 57 | * @return 拼接后字符串 58 | */ 59 | public static String createLinkString(Map params) { 60 | 61 | List keys = new ArrayList(params.keySet()); 62 | Collections.sort(keys); 63 | 64 | String prestr = ""; 65 | 66 | for (int i = 0; i < keys.size(); i++) { 67 | String key = keys.get(i); 68 | String value = params.get(key); 69 | 70 | if (i == keys.size() - 1) {//拼接时,不包括最后一个&字符 71 | prestr = prestr + key + "=" + value; 72 | } else { 73 | prestr = prestr + key + "=" + value + "&"; 74 | } 75 | } 76 | 77 | return prestr; 78 | } 79 | 80 | /** 81 | * 支付宝消息验证地址 82 | */ 83 | private static final String HTTPS_VERIFY_URL = "https://mapi.alipay.com/gateway.do?service=notify_verify&"; 84 | 85 | /** 86 | * 验证消息是否是支付宝发出的合法消息 87 | * @param params 通知返回来的参数数组 88 | * @return 验证结果 89 | */ 90 | public static boolean verify(Map params) { 91 | 92 | //判断responsetTxt是否为true,isSign是否为true 93 | //responsetTxt的结果不是true,与服务器设置问题、合作身份者ID、notify_id一分钟失效有关 94 | //isSign不是true,与安全校验码、请求时的参数格式(如:带自定义参数等)、编码格式有关 95 | String responseTxt = "false"; 96 | if(params.get("notify_id") != null) { 97 | String notify_id = params.get("notify_id"); 98 | responseTxt = verifyResponse(notify_id); 99 | } 100 | String sign = ""; 101 | if(params.get("sign") != null) {sign = params.get("sign");} 102 | boolean isSign = getSignVeryfy(params, sign); 103 | 104 | //写日志记录(若要调试,请取消下面两行注释) 105 | //String sWord = "responseTxt=" + responseTxt + "\n isSign=" + isSign + "\n 返回回来的参数:" + AlipayCore.createLinkString(params); 106 | //AlipayCore.logResult(sWord); 107 | 108 | if (isSign && responseTxt.equals("true")) { 109 | return true; 110 | } else { 111 | return false; 112 | } 113 | } 114 | 115 | /** 116 | * 根据反馈回来的信息,生成签名结果 117 | * @param Params 通知返回来的参数数组 118 | * @param sign 比对的签名结果 119 | * @return 生成的签名结果 120 | */ 121 | private static boolean getSignVeryfy(Map Params, String sign) { 122 | //过滤空值、sign与sign_type参数 123 | Map sParaNew = paraFilter(Params); 124 | //获取待签名字符串 125 | String preSignStr =createLinkString(sParaNew); 126 | //获得签名验证结果 127 | boolean isSign = false; 128 | if(AliPayProperties.signType.equals("MD5") ) { 129 | isSign = AliMD5.verify(preSignStr, sign, AliPayProperties.aliPublicKey, AliPayProperties.charset); 130 | } 131 | return isSign; 132 | } 133 | 134 | /** 135 | * 获取远程服务器ATN结果,验证返回URL 136 | * @param notify_id 通知校验ID 137 | * @return 服务器ATN结果 138 | * 验证结果集: 139 | * invalid命令参数不对 出现这个错误,请检测返回处理中partner和key是否为空 140 | * true 返回正确信息 141 | * false 请检查防火墙或者是服务器阻止端口问题以及验证时间是否超过一分钟 142 | */ 143 | private static String verifyResponse(String notify_id) { 144 | //获取远程服务器ATN结果,验证是否是支付宝服务器发来的请求 145 | 146 | String partner = AliPayProperties.partner; 147 | String veryfy_url = HTTPS_VERIFY_URL + "partner=" + partner + "¬ify_id=" + notify_id; 148 | 149 | return checkUrl(veryfy_url); 150 | } 151 | 152 | /** 153 | * 获取远程服务器ATN结果 154 | * @param urlvalue 指定URL路径地址 155 | * @return 服务器ATN结果 156 | * 验证结果集: 157 | * invalid命令参数不对 出现这个错误,请检测返回处理中partner和key是否为空 158 | * true 返回正确信息 159 | * false 请检查防火墙或者是服务器阻止端口问题以及验证时间是否超过一分钟 160 | */ 161 | private static String checkUrl(String urlvalue) { 162 | String inputLine = ""; 163 | 164 | try { 165 | URL url = new URL(urlvalue); 166 | HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection(); 167 | BufferedReader in = new BufferedReader(new InputStreamReader(urlConnection 168 | .getInputStream())); 169 | inputLine = in.readLine().toString(); 170 | } catch (Exception e) { 171 | e.printStackTrace(); 172 | inputLine = ""; 173 | } 174 | 175 | return inputLine; 176 | } 177 | 178 | 179 | 180 | 181 | /** 182 | * 生成签名结果 183 | * @param sPara 要签名的数组 184 | * @return 签名结果字符串 185 | */ 186 | public static String buildRequestMysign(Map sPara) { 187 | String prestr = createLinkString(sPara); //把数组所有元素,按照“参数=参数值”的模式用“&”字符拼接成字符串 188 | String mysign = ""; 189 | if(AliPayProperties.signType.equals("MD5") ) { 190 | mysign = AliMD5.sign(prestr, AliPayProperties.aliPublicKey, AliPayProperties.charset); 191 | } 192 | return mysign; 193 | } 194 | 195 | /** 196 | * 生成要请求给支付宝的参数数组 197 | * @param sParaTemp 请求前的参数数组 198 | * @return 要请求的参数数组 199 | */ 200 | private static Map buildRequestPara(Map sParaTemp) { 201 | //除去数组中的空值和签名参数 202 | Map sPara =paraFilter(sParaTemp); 203 | //生成签名结果 204 | String mysign = buildRequestMysign(sPara); 205 | 206 | //签名结果与签名方式加入请求提交参数组中 207 | sPara.put("sign", mysign); 208 | sPara.put("sign_type", AliPayProperties.signType); 209 | 210 | return sPara; 211 | } 212 | 213 | /** 214 | * 建立请求,以表单HTML形式构造(默认) 215 | * @param sParaTemp 请求参数数组 216 | * @param strMethod 提交方式。两个值可选:post、get 217 | * @param strButtonName 确认按钮显示文字 218 | * @return 提交表单HTML文本 219 | */ 220 | public static String buildRequest(Map sParaTemp, String strMethod, String strButtonName) { 221 | //待请求参数数组 222 | Map sPara = buildRequestPara(sParaTemp); 223 | List keys = new ArrayList(sPara.keySet()); 224 | 225 | StringBuffer sbHtml = new StringBuffer(); 226 | 227 | sbHtml.append("
"); 230 | 231 | for (int i = 0; i < keys.size(); i++) { 232 | String name = (String) keys.get(i); 233 | String value = (String) sPara.get(name); 234 | 235 | sbHtml.append(""); 236 | } 237 | 238 | //submit按钮控件请不要含有name属性 239 | sbHtml.append("
"); 240 | sbHtml.append(""); 241 | 242 | return sbHtml.toString(); 243 | } 244 | 245 | /** 246 | * 建立请求,以表单HTML形式构造,带文件上传功能 247 | * @param sParaTemp 请求参数数组 248 | * @param strMethod 提交方式。两个值可选:post、get 249 | * @param strButtonName 确认按钮显示文字 250 | * @param strParaFileName 文件上传的参数名 251 | * @return 提交表单HTML文本 252 | */ 253 | public static String buildRequest(Map sParaTemp, String strMethod, String strButtonName, String strParaFileName) { 254 | //待请求参数数组 255 | Map sPara = buildRequestPara(sParaTemp); 256 | List keys = new ArrayList(sPara.keySet()); 257 | 258 | StringBuffer sbHtml = new StringBuffer(); 259 | 260 | sbHtml.append("
"); 263 | 264 | for (int i = 0; i < keys.size(); i++) { 265 | String name = (String) keys.get(i); 266 | String value = (String) sPara.get(name); 267 | 268 | sbHtml.append(""); 269 | } 270 | 271 | sbHtml.append(""); 272 | 273 | //submit按钮控件请不要含有name属性 274 | sbHtml.append("
"); 275 | 276 | return sbHtml.toString(); 277 | } 278 | 279 | /** 280 | * 建立请求,以模拟远程HTTP的POST请求方式构造并获取支付宝的处理结果 281 | * 如果接口中没有上传文件参数,那么strParaFileName与strFilePath设置为空值 282 | * 如:buildRequest("", "",sParaTemp) 283 | * @param strParaFileName 文件类型的参数名 284 | * @param strFilePath 文件路径 285 | * @param sParaTemp 请求参数数组 286 | * @return 支付宝处理结果 287 | * @throws Exception 288 | */ 289 | public static String buildRequest(String strParaFileName, String strFilePath,Map sParaTemp) throws Exception { 290 | //待请求参数数组 291 | Map sPara = buildRequestPara(sParaTemp); 292 | 293 | /* HttpProtocolHandler httpProtocolHandler = HttpProtocolHandler.getInstance(); 294 | 295 | HttpRequest request = new HttpRequest(HttpResultType.BYTES); 296 | //设置编码集 297 | request.setCharset(AlipayConfig.input_charset); 298 | 299 | request.setParameters(generatNameValuePair(sPara)); 300 | request.setUrl(ALIPAY_GATEWAY_NEW+"_input_charset="+AlipayConfig.input_charset); 301 | 302 | HttpResponse response = httpProtocolHandler.execute(request,strParaFileName,strFilePath); 303 | if (response == null) { 304 | return null; 305 | } 306 | 307 | String strResult = response.getStringResult(); 308 | 309 | return strResult;*/ 310 | HttpClientHelper.create().execute(sPara, AliPayProperties.ALIPAY_GATEWAY_NEW+"_input_charset="+ AliPayProperties.charset); 311 | return ""; 312 | } 313 | 314 | /** 315 | * MAP类型数组转换成NameValuePair类型 316 | * @param properties MAP类型数组 317 | * @return NameValuePair类型数组 318 | */ 319 | private static NameValuePair[] generatNameValuePair(Map properties) { 320 | NameValuePair[] nameValuePair = new BasicNameValuePair[properties.size()]; 321 | 322 | int i = 0; 323 | for (Map.Entry entry : properties.entrySet()) { 324 | nameValuePair[i++] = new BasicNameValuePair(entry.getKey(), entry.getValue()); 325 | } 326 | 327 | return nameValuePair; 328 | } 329 | 330 | /** 331 | * 用于防钓鱼,调用接口query_timestamp来获取时间戳的处理函数 332 | * 注意:远程解析XML出错,与服务器是否支持SSL等配置有关 333 | * @return 时间戳字符串 334 | * @throws IOException 335 | * @throws DocumentException 336 | * @throws MalformedURLException 337 | */ 338 | public static String query_timestamp() throws MalformedURLException, 339 | DocumentException, IOException { 340 | 341 | //构造访问query_timestamp接口的URL串 342 | String strUrl = AliPayProperties.ALIPAY_GATEWAY_NEW + "service=query_timestamp&partner=" + AliPayProperties.partner + "&_input_charset" +AliPayProperties.charset; 343 | StringBuffer result = new StringBuffer(); 344 | 345 | SAXReader reader = new SAXReader(); 346 | Document doc = reader.read(new URL(strUrl).openStream()); 347 | 348 | List nodeList = doc.selectNodes("//alipay/*"); 349 | 350 | for (Node node : nodeList) { 351 | // 截取部分不需要解析的信息 352 | if (node.getName().equals("is_success") && node.getText().equals("T")) { 353 | // 判断是否有成功标示 354 | List nodeList1 = doc.selectNodes("//response/timestamp/*"); 355 | for (Node node1 : nodeList1) { 356 | result.append(node1.getText()); 357 | } 358 | } 359 | } 360 | 361 | return result.toString(); 362 | } 363 | } 364 | -------------------------------------------------------------------------------- /src/main/java/spring/boot/pay/thirdparty/alipay/AliPayProcessor.java: -------------------------------------------------------------------------------- 1 | package spring.boot.pay.thirdparty.alipay; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.stereotype.Component; 5 | 6 | import spring.boot.pay.config.dictionary.PayType; 7 | import spring.boot.pay.framework.base.ResultModel; 8 | import spring.boot.pay.framework.processor.Processor; 9 | import spring.boot.pay.model.Trade; 10 | 11 | @Component 12 | @SuppressWarnings("unused") 13 | public class AliPayProcessor implements Processor { 14 | 15 | @Autowired 16 | AliPayService aliPayService; 17 | 18 | @Override 19 | public ResultModel process(Trade trade) { 20 | 21 | if (trade.getPayType() == PayType.ALI_PAY_NetBank) { 22 | 23 | ResultModel serviceResult = aliPayService.sendDataForDirectPayForbank(trade); 24 | if (!serviceResult.isSuccess()) { 25 | return serviceResult; 26 | } 27 | 28 | return ResultModel.successModel(serviceResult.getBody()); 29 | } 30 | if (trade.getPayType() == PayType.ALI_PAY_Direct) { 31 | 32 | ResultModel serviceResult = aliPayService.sendDataForDirectPay(trade); 33 | if (!serviceResult.isSuccess()) { 34 | return serviceResult; 35 | } 36 | 37 | return ResultModel.successModel(serviceResult.getBody()); 38 | } 39 | if (trade.getPayType() == PayType.ALI_PAY_WAP) { 40 | 41 | ResultModel serviceResult = aliPayService.sendDataForWAPDirectPay(trade); 42 | if (!serviceResult.isSuccess()) { 43 | return serviceResult; 44 | } 45 | 46 | return ResultModel.successModel(serviceResult.getBody()); 47 | } 48 | 49 | return null; 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/spring/boot/pay/thirdparty/alipay/AliPayService.java: -------------------------------------------------------------------------------- 1 | package spring.boot.pay.thirdparty.alipay; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | import org.apache.commons.lang3.StringUtils; 7 | import org.springframework.beans.factory.InitializingBean; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.stereotype.Service; 10 | 11 | import spring.boot.pay.config.properties.AliPayProperties; 12 | import spring.boot.pay.dao.TradeDao; 13 | import spring.boot.pay.framework.base.PayTask; 14 | import spring.boot.pay.framework.base.ResultModel; 15 | import spring.boot.pay.model.Trade; 16 | 17 | @Service 18 | public class AliPayService implements InitializingBean { 19 | 20 | 21 | @Autowired 22 | TradeDao tradeDao; 23 | 24 | public ResultModel sendDataForDirectPayForbank(Trade trade) { 25 | 26 | 27 | //////////////////////////////////// 请求参数////////////////////////////////////// 28 | 29 | // 支付类型 30 | String payment_type = "1"; 31 | // 必填,不能修改 32 | // 服务器异步通知页面路径 33 | String notify_url = AliPayProperties.domain+AliPayProperties.notify_url; 34 | // 需http://格式的完整路径,不能加?id=123这类自定义参数 35 | 36 | // 页面跳转同步通知页面路径 37 | String return_url =AliPayProperties.return_url; 38 | // 需http://格式的完整路径,不能加?id=123这类自定义参数,不能写成http://localhost/ 39 | 40 | // 商户订单号 41 | String out_trade_no = trade.getOrderId(); 42 | // 商户网站订单系统中唯一订单号,必填 43 | 44 | // 订单名称 45 | String subject =trade.getBody(); 46 | // 必填 47 | 48 | // 付款金额 49 | String total_fee =String.valueOf(trade.getTotalFee()); 50 | // 必填 51 | 52 | // 订单描述 53 | 54 | String body =trade.getBody(); 55 | // 默认支付方式 56 | String paymethod = "bankPay"; 57 | 58 | //超时时间 59 | String itBPay = "1m"; 60 | //选填 61 | ////////////////////////////////////////////////////////////////////////////////// 62 | 63 | // 把请求参数打包成数组 64 | Map sParaTemp = new HashMap(); 65 | sParaTemp.put("service", "create_direct_pay_by_user"); 66 | sParaTemp.put("partner", AliPayProperties.partner); 67 | sParaTemp.put("seller_email", AliPayProperties.sellerEmail); 68 | sParaTemp.put("_input_charset", AliPayProperties.charset); 69 | sParaTemp.put("payment_type", payment_type); 70 | sParaTemp.put("notify_url", notify_url); 71 | sParaTemp.put("return_url", return_url); 72 | sParaTemp.put("out_trade_no", out_trade_no); 73 | sParaTemp.put("subject", subject); 74 | sParaTemp.put("total_fee", total_fee); 75 | //sParaTemp.put("body", body); 76 | 77 | sParaTemp.put("paymethod", paymethod); 78 | sParaTemp.put("defaultbank", trade.getBank()); 79 | //sParaTemp.put("show_url", AliPayProperties.showUrl); 80 | //sParaTemp.put("it_b_pay", itBPay); 81 | // sParaTemp.put("anti_phishing_key", anti_phishing_key); 82 | //sParaTemp.put("exter_invoke_ip", exter_invoke_ip); 83 | 84 | String requestContent=""; 85 | for (int i = 0; i < sParaTemp.keySet().size(); i++) { 86 | String name = (String) sParaTemp.get(i); 87 | String value = (String) sParaTemp.get(i); 88 | requestContent+=name+":"+value+"|"; 89 | 90 | } 91 | 92 | trade.setRequestContent(requestContent); 93 | trade.setTradeStatus(PayTask.STATUS_ACCEPTED); 94 | 95 | if (StringUtils.isBlank(trade.getOrderId())) { 96 | 97 | //错误处理 98 | } 99 | trade.setRequestTime(java.time.LocalDateTime.now()); 100 | tradeDao.checkOrderStatus(trade); 101 | if(trade.getTradeStatus()!=-1){//交易表中没订单,则插入订单 102 | tradeDao.save(trade); 103 | } 104 | 105 | // 建立请求 106 | String sHtmlText = AliPayHelper.buildRequest(sParaTemp, "get", "ok"); 107 | 108 | return ResultModel.successModel(sHtmlText); 109 | } 110 | 111 | public ResultModel sendDataForDirectPay(Trade trade) { 112 | 113 | 114 | //////////////////////////////////// 请求参数////////////////////////////////////// 115 | 116 | // 支付类型 117 | String payment_type = "1"; 118 | // 必填,不能修改 119 | // 服务器异步通知页面路径 120 | String notify_url = AliPayProperties.domain+AliPayProperties.notify_url_direct; 121 | // 需http://格式的完整路径,不能加?id=123这类自定义参数 122 | 123 | // 页面跳转同步通知页面路径 124 | String return_url =AliPayProperties.return_url; 125 | // 需http://格式的完整路径,不能加?id=123这类自定义参数,不能写成http://localhost/ 126 | 127 | // 商户订单号 128 | String out_trade_no = trade.getOrderId(); 129 | // 商户网站订单系统中唯一订单号,必填 130 | 131 | // 订单名称 132 | String subject =trade.getBody(); 133 | // 必填 134 | 135 | // 付款金额 136 | String total_fee =String.valueOf(trade.getTotalFee()); 137 | // 必填 138 | 139 | // 订单描述 140 | 141 | String body =trade.getBody(); 142 | 143 | //超时时间 144 | String itBPay = "1m"; 145 | //选填 146 | ////////////////////////////////////////////////////////////////////////////////// 147 | 148 | // 把请求参数打包成数组 149 | Map sParaTemp = new HashMap(); 150 | sParaTemp.put("service", "create_direct_pay_by_user"); 151 | sParaTemp.put("partner", AliPayProperties.partner); 152 | sParaTemp.put("seller_email", AliPayProperties.sellerEmail); 153 | sParaTemp.put("_input_charset", AliPayProperties.charset); 154 | sParaTemp.put("payment_type", payment_type); 155 | sParaTemp.put("notify_url", notify_url); 156 | sParaTemp.put("return_url", return_url); 157 | sParaTemp.put("out_trade_no", out_trade_no); 158 | sParaTemp.put("subject", subject); 159 | sParaTemp.put("total_fee", total_fee); 160 | sParaTemp.put("paymethod", ""); 161 | sParaTemp.put("defaultbank",""); 162 | //sParaTemp.put("body", body); 163 | 164 | //sParaTemp.put("show_url", AliPayProperties.showUrl); 165 | //sParaTemp.put("it_b_pay", itBPay); 166 | // sParaTemp.put("anti_phishing_key", anti_phishing_key); 167 | //sParaTemp.put("exter_invoke_ip", exter_invoke_ip); 168 | 169 | String requestContent=""; 170 | for (int i = 0; i < sParaTemp.keySet().size(); i++) { 171 | String name = (String) sParaTemp.get(i); 172 | String value = (String) sParaTemp.get(i); 173 | requestContent+=name+":"+value+"|"; 174 | 175 | } 176 | 177 | trade.setRequestContent(requestContent); 178 | trade.setTradeStatus(PayTask.STATUS_ACCEPTED); 179 | 180 | if (StringUtils.isBlank(trade.getOrderId())) { 181 | 182 | //错误处理 183 | } 184 | trade.setRequestTime(java.time.LocalDateTime.now()); 185 | tradeDao.checkOrderStatus(trade); 186 | if(trade.getTradeStatus()!=-1){//交易表中没订单,则插入订单 187 | tradeDao.save(trade); 188 | } 189 | 190 | // 建立请求 191 | String sHtmlText = AliPayHelper.buildRequest(sParaTemp, "get", "ok"); 192 | 193 | return ResultModel.successModel(sHtmlText); 194 | } 195 | 196 | public ResultModel sendDataForWAPDirectPay(Trade trade) { 197 | 198 | 199 | //支付类型 200 | String payment_type = "1"; 201 | // 必填,不能修改 202 | // 服务器异步通知页面路径 203 | String notify_url = AliPayProperties.domain+AliPayProperties.notify_url; 204 | // 需http://格式的完整路径,不能加?id=123这类自定义参数 205 | 206 | // 页面跳转同步通知页面路径 207 | String return_url =AliPayProperties.m_return_url; 208 | // 需http://格式的完整路径,不能加?id=123这类自定义参数,不能写成http://localhost/ 209 | 210 | //商户订单号 211 | String out_trade_no = trade.getOrderId(); 212 | //商户网站订单系统中唯一订单号,必填 213 | 214 | //订单名称 215 | String subject = trade.getBody(); 216 | //必填 217 | 218 | //付款金额 219 | String total_fee = String.valueOf(trade.getTotalFee()); 220 | //必填 221 | 222 | 223 | //订单描述 224 | String body = trade.getBody(); 225 | //选填 226 | //超时时间 227 | String itBPay = "1m"; 228 | 229 | 230 | ////////////////////////////////////////////////////////////////////////////////// 231 | 232 | //把请求参数打包成数组 233 | Map sParaTemp = new HashMap(); 234 | sParaTemp.put("service", "alipay.wap.create.direct.pay.by.user"); 235 | sParaTemp.put("partner", AliPayProperties.partner); 236 | sParaTemp.put("seller_id", AliPayProperties.partner); 237 | sParaTemp.put("_input_charset", AliPayProperties.charset); 238 | sParaTemp.put("payment_type", payment_type); 239 | sParaTemp.put("notify_url", notify_url); 240 | sParaTemp.put("return_url", return_url); 241 | sParaTemp.put("out_trade_no", out_trade_no); 242 | sParaTemp.put("subject", subject); 243 | sParaTemp.put("total_fee", total_fee); 244 | sParaTemp.put("show_url", AliPayProperties.showUrl); 245 | sParaTemp.put("body", body); 246 | sParaTemp.put("it_b_pay", itBPay); 247 | //sParaTemp.put("extern_token", extern_token); 248 | String requestContent=""; 249 | for (int i = 0; i < sParaTemp.keySet().size(); i++) { 250 | String name = (String) sParaTemp.get(i); 251 | String value = (String) sParaTemp.get(i); 252 | requestContent+=name+":"+value+"|"; 253 | 254 | } 255 | 256 | 257 | trade.setRequestContent(requestContent); 258 | trade.setTradeStatus(PayTask.STATUS_ACCEPTED); 259 | 260 | if (StringUtils.isBlank(trade.getOrderId())) { 261 | 262 | //错误处理 263 | } 264 | tradeDao.checkOrderStatus(trade); 265 | if(trade.getTradeStatus()!=-1){//交易表中没订单,则插入订单 266 | tradeDao.save(trade); 267 | } 268 | //建立请求 269 | String sHtmlText = AliPayHelper.buildRequest(sParaTemp,"get","ok"); 270 | 271 | return ResultModel.successModel(sHtmlText); 272 | } 273 | 274 | @Override 275 | public void afterPropertiesSet() throws Exception { 276 | // TODO Auto-generated method stub 277 | 278 | } 279 | 280 | } 281 | -------------------------------------------------------------------------------- /src/main/java/spring/boot/pay/thirdparty/wxpay/WxController.java: -------------------------------------------------------------------------------- 1 | package spring.boot.pay.thirdparty.wxpay; 2 | 3 | import java.io.PrintWriter; 4 | import java.math.BigDecimal; 5 | import java.time.LocalDateTime; 6 | import java.util.SortedMap; 7 | 8 | import javax.servlet.http.HttpServletRequest; 9 | import javax.servlet.http.HttpServletResponse; 10 | 11 | import org.slf4j.Logger; 12 | import org.slf4j.LoggerFactory; 13 | import org.springframework.beans.factory.annotation.Autowired; 14 | import org.springframework.stereotype.Controller; 15 | import org.springframework.web.bind.annotation.RequestMapping; 16 | 17 | import spring.boot.pay.common.HttpServRequestHelper; 18 | import spring.boot.pay.common.XmlHelper; 19 | import spring.boot.pay.config.properties.WxPayProperties; 20 | import spring.boot.pay.framework.base.PayStatus; 21 | import spring.boot.pay.framework.base.PayTask; 22 | import spring.boot.pay.framework.base.ResultModel; 23 | import spring.boot.pay.framework.processor.ProcessorManager; 24 | import spring.boot.pay.model.Trade; 25 | 26 | @Controller 27 | @SuppressWarnings("unused") 28 | public class WxController { 29 | 30 | private static final Logger LOGGER = LoggerFactory.getLogger(WxController.class); 31 | 32 | private static final String WX_RESPONSE_SUCCESS = " "; 33 | private static final String WX_RESPONSE_FAIL = " "; 34 | 35 | 36 | @Autowired WxPayService wxPayService; 37 | 38 | @Autowired 39 | ProcessorManager processorManager; 40 | /** 41 | * 接收微信通知 42 | * 43 | * 44 | * 45 | * 46 | * 47 | * 48 | * 49 | * 50 | * 51 | * 52 | * 53 | * 54 | * 55 | * 56 | * 200 57 | * 58 | * 59 | * 60 | */ 61 | @RequestMapping(value = WxPayProperties.NOTIFY_URL) 62 | public void notify(HttpServletRequest request,HttpServletResponse response) throws Exception { 63 | 64 | String notifyXml = HttpServRequestHelper.getRequestBody(request); 65 | 66 | if(LOGGER.isInfoEnabled()) { 67 | LOGGER.info("received WeChat notify,notify context is {}", notifyXml); 68 | } 69 | 70 | PrintWriter out = response.getWriter(); 71 | 72 | Trade payResult = buildPayResult(notifyXml); 73 | 74 | if(payResult == null){ 75 | out.write(WX_RESPONSE_FAIL); 76 | } else { 77 | 78 | payResult.setSource(1); 79 | ResultModel processResult = processorManager.process(payResult); 80 | if(payResult.getTradeStatus()==PayTask.STATUS_SUCCESS){ 81 | out.write(WX_RESPONSE_SUCCESS); 82 | }else { 83 | out.write(WX_RESPONSE_FAIL); 84 | } 85 | } 86 | 87 | out.flush(); 88 | out.close(); 89 | } 90 | 91 | 92 | private Trade buildPayResult(String notifyXml){ 93 | 94 | SortedMap map = XmlHelper.parseXmlToMap(notifyXml); 95 | if( map == null ){ 96 | LOGGER.warn("can not parse the notify params,the notify xml={}", notifyXml); 97 | return null; 98 | } 99 | 100 | String sign = WxPayHelper.createSign(map); 101 | if( !sign.equalsIgnoreCase(map.get("sign")) ){ 102 | LOGGER.warn("check params error,the notify xml is:" + notifyXml+";the sign of mine is:"+sign); 103 | return null; 104 | } 105 | 106 | Trade payResult = new Trade(); 107 | payResult.setPayStatus("SUCCESS".equals(map.get("result_code")) ? PayStatus.STATUS_SUCCESS : PayStatus.STATUS_FAIL); 108 | payResult.setTradeStatus("SUCCESS".equals(map.get("result_code")) ? PayTask.STATUS_SUCCESS : PayTask.STATUS_FAIL); 109 | String totalFeeStr = map.get("total_fee"); 110 | payResult.setTotalFee(new BigDecimal(totalFeeStr)); 111 | payResult.setOrderId(map.get("out_trade_no")); 112 | payResult.setOutTradeId(map.get("transaction_id")); 113 | payResult.setReturnContent(notifyXml); 114 | payResult.setResultTime(LocalDateTime.now()); 115 | 116 | return payResult; 117 | } 118 | 119 | 120 | } 121 | -------------------------------------------------------------------------------- /src/main/java/spring/boot/pay/thirdparty/wxpay/WxPayHelper.java: -------------------------------------------------------------------------------- 1 | package spring.boot.pay.thirdparty.wxpay; 2 | 3 | 4 | 5 | import java.io.UnsupportedEncodingException; 6 | import java.text.SimpleDateFormat; 7 | import java.util.Date; 8 | import java.util.Map; 9 | import java.util.SortedMap; 10 | 11 | import spring.boot.pay.common.encrypt.WXMD5; 12 | import spring.boot.pay.config.properties.WxPayProperties; 13 | 14 | 15 | public class WxPayHelper { 16 | 17 | 18 | /** 19 | * 取出一个指定长度大小的随机正整数. 20 | * @param length int 设定所取出随机数的长度。length小于11 21 | * @return int 返回生成的随机数。 22 | */ 23 | public static int buildRandom(int length) { 24 | int num = 1; 25 | double random = Math.random(); 26 | if (random < 0.1) { 27 | random = random + 0.1; 28 | } 29 | for (int i = 0; i < length; i++) { 30 | num = num * 10; 31 | } 32 | return (int) ((random * num)); 33 | } 34 | 35 | 36 | /** 37 | * 获取当前时间 yyyyMMddHHmmss 38 | * 39 | * @return String 40 | */ 41 | public static String getCurrTime() { 42 | Date now = new Date(); 43 | SimpleDateFormat outFormat = new SimpleDateFormat("yyyyMMddHHmmss"); 44 | return outFormat.format(now); 45 | } 46 | 47 | /** 48 | * 获取随机字符串 49 | */ 50 | public static String getNonceStr() { 51 | // 随机数 52 | String currTime = getCurrTime(); 53 | // 8位日期 54 | String strTime = currTime.substring(8, currTime.length()); 55 | // 四位随机数 56 | String strRandom = buildRandom(4) + ""; 57 | // 10位序列号,可以自行调整。 58 | return strTime + strRandom; 59 | } 60 | 61 | 62 | /** 63 | * 创建md5摘要,规则是:按参数名称a-z排序,遇到空值的参数不参加签名。 64 | */ 65 | public static String createSign(SortedMap packageParams) { 66 | StringBuilder sb = new StringBuilder(); 67 | 68 | for (Map.Entry entry : packageParams.entrySet()) { 69 | 70 | String k = entry.getKey(); 71 | String v = entry.getValue(); 72 | if (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) { 73 | sb.append(k).append("=").append(v).append("&"); 74 | } 75 | } 76 | 77 | // String key="1Q2w3e4r5t6y7u8i9Oa1s2d3f4g5h6j7"; 78 | sb.append("key="+WxPayProperties.KEY); 79 | // 商户key即密钥: 80 | return WXMD5.MD5Encode(sb.toString()) 81 | .toUpperCase(); 82 | 83 | } 84 | 85 | public static String getRequestXml(SortedMap parameters) { 86 | StringBuilder sb = new StringBuilder(); 87 | String sign=WxPayHelper.createSign(parameters); 88 | 89 | sb.append(""); 90 | for (Map.Entry entry : parameters.entrySet()) { 91 | String k = entry.getKey(); 92 | String v = entry.getValue(); 93 | if ("attach".equalsIgnoreCase(k) || "body".equalsIgnoreCase(k) || "sign".equalsIgnoreCase(k)) { 94 | sb.append("<").append(k).append(">"); 96 | } else { 97 | sb.append("<").append(k).append(">").append(v).append(""); 99 | } 100 | } 101 | 102 | sb.append("<").append("sign").append(">"); 104 | sb.append(""); 105 | return sb.toString(); 106 | } 107 | 108 | public static String authorizeLogin(String oid) { 109 | 110 | String state=WXMD5.MD5Encode(String.valueOf(new java.util.Date().getTime()).substring(0, 10)); 111 | 112 | String urltemplate = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=%s&redirect_uri=%s&response_type=%s&scope" 113 | + "=snsapi_base&state=%s#wechat_redirect"; 114 | String url=null; 115 | try { 116 | url = String.format(urltemplate, WxPayProperties.APP_ID, 117 | java.net.URLEncoder.encode(WxPayProperties.DOMAIN+"/pay/tradewxjs/?oid="+oid,"UTF-8"),"code",state); 118 | } catch (UnsupportedEncodingException e) { 119 | // TODO Auto-generated catch block 120 | e.printStackTrace(); 121 | } 122 | 123 | return url; 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/main/java/spring/boot/pay/thirdparty/wxpay/WxPayProcessor.java: -------------------------------------------------------------------------------- 1 | package spring.boot.pay.thirdparty.wxpay; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.stereotype.Component; 5 | 6 | import spring.boot.pay.framework.base.ResultModel; 7 | import spring.boot.pay.framework.processor.Processor; 8 | import spring.boot.pay.model.Trade; 9 | 10 | 11 | 12 | @Component 13 | @SuppressWarnings("unused") 14 | public class WxPayProcessor implements Processor { 15 | 16 | @Autowired 17 | WxPayService wxPayService; 18 | 19 | @Override 20 | public ResultModel process(Trade trade) { 21 | 22 | ResultModel serviceResult = wxPayService.getPayJson(trade); 23 | if( !serviceResult.isSuccess() ){ 24 | return serviceResult; 25 | } 26 | 27 | 28 | return ResultModel.successModel(serviceResult.getBody()); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/spring/boot/pay/thirdparty/wxpay/WxPayService.java: -------------------------------------------------------------------------------- 1 | package spring.boot.pay.thirdparty.wxpay; 2 | 3 | import java.text.SimpleDateFormat; 4 | import java.time.format.DateTimeFormatter; 5 | import java.util.Map; 6 | import java.util.SortedMap; 7 | import java.util.TreeMap; 8 | 9 | import org.apache.commons.lang3.StringUtils; 10 | import org.apache.http.impl.client.CloseableHttpClient; 11 | import org.slf4j.Logger; 12 | import org.slf4j.LoggerFactory; 13 | import org.springframework.beans.factory.InitializingBean; 14 | import org.springframework.beans.factory.annotation.Autowired; 15 | import org.springframework.stereotype.Service; 16 | 17 | import spring.boot.pay.common.MoneyFormat; 18 | import spring.boot.pay.common.XmlHelper; 19 | import spring.boot.pay.common.http.HttpClientHelper; 20 | import spring.boot.pay.config.dictionary.PayType; 21 | import spring.boot.pay.config.properties.WxPayProperties; 22 | import spring.boot.pay.dao.TradeDao; 23 | import spring.boot.pay.framework.base.PayTask; 24 | import spring.boot.pay.framework.base.ResultModel; 25 | import spring.boot.pay.model.Trade; 26 | 27 | @Service 28 | public class WxPayService implements InitializingBean { 29 | 30 | public static final Logger LOGGER = LoggerFactory.getLogger(WxPayService.class); 31 | 32 | private HttpClientHelper HttpClient = HttpClientHelper.create(); 33 | 34 | private CloseableHttpClient refundClient; 35 | 36 | private static final String PAY_TEMPLATE = "{\"appId\":\"%s\",\"timeStamp\":\"%s\",\"nonceStr\":\"%s\"," 37 | + "\"package\":\"%s\",\"signType\":\"MD5" + "\",\"paySign\":\"%s\"}"; 38 | private static final String PAY_TEMPLATE_NOJSON = "appId=%s,timeStamp=%s,nonceStr=%s," 39 | + "package=%s,signType=MD5,paySign=%s}"; 40 | private static final SimpleDateFormat SIMPLE_DATE_FORMAT = new SimpleDateFormat("yyyyMMddHHmmss"); 41 | 42 | @Autowired 43 | TradeDao tradeDao; 44 | 45 | /** 46 | * 创建预支付请求的xml参数 47 | */ 48 | private String createXmlParams4PrePay(Trade trade) { 49 | 50 | SortedMap params = new TreeMap(); 51 | params.put("appid", WxPayProperties.APP_ID); 52 | params.put("body", trade.getBody()); 53 | params.put("mch_id", WxPayProperties.MCH_ID); 54 | params.put("nonce_str", WxPayHelper.getNonceStr()); 55 | params.put("notify_url", WxPayProperties.DOMAIN + WxPayProperties.NOTIFY_URL); 56 | params.put("out_trade_no", trade.getOrderId()); 57 | LOGGER.error("支付参数" + WxPayProperties.DOMAIN + WxPayProperties.NOTIFY_URL); 58 | if (PayType.WX_PAY_JSAPI == trade.getPayType()) { 59 | params.put("openid", trade.getOpenId()); 60 | } 61 | 62 | params.put("spbill_create_ip", trade.getIp()); 63 | params.put("total_fee", String.valueOf(MoneyFormat.yuan2fen(String.valueOf(trade.getTotalFee())))); 64 | 65 | if (PayType.WX_PAY_QC == trade.getPayType()) { 66 | params.put("trade_type", WxPayProperties.TRADE_TYPE_QC); 67 | } else { 68 | params.put("trade_type", WxPayProperties.TRADE_TYPE_JSAPI); 69 | } 70 | 71 | trade.setRequestTime(java.time.LocalDateTime.now()); 72 | params.put("time_start", trade.getRequestTime().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"))); 73 | if(trade.getOrderType()!=-1){ 74 | params.put("time_expire", 75 | trade.getRequestTime().plusSeconds(45).format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"))); 76 | // params.put("limit_pay","no_credit");//指定不能使用信用卡支付 77 | } 78 | 79 | return WxPayHelper.getRequestXml(params); 80 | 81 | } 82 | 83 | /** 84 | * 发送https请求到微信获取 预支付id 85 | */ 86 | private Map acquirePrePayReturnInfo(String xmlParam) { 87 | 88 | String str = HttpClient.execute(xmlParam, WxPayProperties.WX_PRE_PAY_URL, 1); 89 | 90 | Map map = XmlHelper.parseXmlToMap(str); 91 | map.put("return", str); 92 | 93 | return map; 94 | } 95 | 96 | /** 97 | * 根据预支付信息创建用于支付的json串 98 | * 99 | * @param packageStr 100 | * 预支付信息 101 | * @return 用于提交给微信进行支付的json串 102 | */ 103 | private String cretePayJson(String packageStr) { 104 | 105 | SortedMap map = new TreeMap<>(); 106 | String timestamp = String.valueOf(System.currentTimeMillis() / 1000); 107 | map.put("appId", WxPayProperties.APP_ID); 108 | map.put("nonceStr", WxPayHelper.getNonceStr()); 109 | map.put("timeStamp", timestamp); 110 | map.put("package", packageStr); 111 | map.put("signType", "MD5"); 112 | 113 | return String.format(PAY_TEMPLATE, WxPayProperties.APP_ID, timestamp, map.get("nonceStr"), packageStr, 114 | WxPayHelper.createSign(map)); 115 | } 116 | 117 | /** 118 | * 根据支付信息 生成微信支付的请求串 119 | */ 120 | public ResultModel getPayJson(Trade trade) { 121 | 122 | 123 | 124 | String xmlParams = createXmlParams4PrePay(trade); 125 | trade.setRequestContent(xmlParams); 126 | trade.setTradeStatus(PayTask.STATUS_ACCEPTED); 127 | 128 | if (StringUtils.isBlank(trade.getOrderId())) { 129 | 130 | //错误处理 131 | } 132 | tradeDao.checkOrderStatus(trade); 133 | if (trade.getTradeStatus() != -1) {// 交易表中没订单,则插入订单 134 | tradeDao.save(trade); 135 | } 136 | 137 | 138 | Map map = acquirePrePayReturnInfo(xmlParams); 139 | String prePayId = map.get("prepay_id"); 140 | String codeUrl = map.get("code_url"); 141 | 142 | trade.setReturnContent(map.get("return")); 143 | tradeDao.updateReturnContent(trade); 144 | LOGGER.error("获取预支付id失败,xmlParams is:{},resp is:{}", xmlParams, map.get("return")); 145 | if (prePayId == null) { 146 | LOGGER.error("获取预支付id失败,xmlParams is:{},resp is:{}", xmlParams, map.get("return")); 147 | 148 | tradeDao.updateStatus(PayTask.STATUS_FirstFailed_pre, trade.getOrderId()); 149 | 150 | return ResultModel.failModel("获取预支付id失败"); 151 | } 152 | 153 | if (trade.getPayType() == PayType.WX_PAY_QC) { 154 | 155 | if (codeUrl == null) { 156 | 157 | LOGGER.error("获取二维码连接失败,xmlParams is:{},resp is:{}", xmlParams, map.get("return")); 158 | 159 | tradeDao.updateStatus(PayTask.STATUS_FirstFailed_qc, trade.getOrderId()); 160 | 161 | return ResultModel.failModel("获取二维码连接失败"); 162 | } else { 163 | 164 | String wxPayParam = cretePayJson("prepay_id=" + prePayId + "$code_url=" + codeUrl); 165 | 166 | return ResultModel.successModel(wxPayParam); 167 | } 168 | } 169 | 170 | if (trade.getPayType() == PayType.WX_PAY_JSAPI) { 171 | return ResultModel.successModel(prePayId); 172 | } 173 | 174 | String wxPayParam = cretePayJson("prepay_id=" + prePayId); 175 | 176 | return ResultModel.successModel(wxPayParam); 177 | } 178 | 179 | 180 | 181 | 182 | @Override 183 | public void afterPropertiesSet() throws Exception { 184 | // TODO Auto-generated method stub 185 | 186 | } 187 | 188 | } 189 | -------------------------------------------------------------------------------- /src/main/resource/config/application.properties: -------------------------------------------------------------------------------- 1 | # LOGGING 2 | logging.path=/var/logs/pay 3 | logging.file=myapp.log 4 | logging.config=logback.xml 5 | "logging.level.org.springframework=DEBUG" (TRACE, DEBUG, INFO, WARN, ERROR, FATAL, OFF) 6 | 7 | # IDENTITY (ContextIdApplicationContextInitializer) 8 | spring.application.name=spring.boot.pay 9 | 10 | # EMBEDDED SERVER CONFIGURATION (ServerProperties) 11 | server.port= 12 | server.tomcat.max-threads = 10 13 | 14 | -------------------------------------------------------------------------------- /src/main/resource/config/db.properties: -------------------------------------------------------------------------------- 1 | url=jdbc:mysql:// 2 | username= 3 | password= 4 | driverClass=com.mysql.jdbc.Driver 5 | maxActive=10 6 | url_order=jdbc:mysql:// 7 | username_order= 8 | password_order= 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/main/resource/config/whitelist.txt: -------------------------------------------------------------------------------- 1 | 127.0.0.1 2 | -------------------------------------------------------------------------------- /src/main/resource/payconfig/ali/alipayconfig.properties: -------------------------------------------------------------------------------- 1 | partner= 2 | aliPublicKey= 3 | sellerEmail= 4 | domain= 5 | defaultbank= 6 | showUrl= 7 | returnUrl= 8 | mreturnUrl=//移动端回调 -------------------------------------------------------------------------------- /src/main/resource/payconfig/wx/jsapi.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /src/main/resource/payconfig/wx/wxconfig.properties: -------------------------------------------------------------------------------- 1 | appid= 2 | mch_id= 3 | fee_type= 4 | unifiedorder= 5 | domain= 6 | mdomain= 7 | orderquery= 8 | downloadbill= --------------------------------------------------------------------------------