├── src └── main │ ├── resources │ ├── application.yml │ ├── keystore.p12 │ ├── wxinfo.properties │ ├── zfbinfo.properties │ ├── spring-context-dubbo.xml │ ├── application-dev.properties │ ├── logback-spring.xml │ └── acp_sdk.properties │ ├── webapp │ ├── index.jsp │ └── WEB-INF │ │ └── web.xml │ └── java │ └── com │ └── howie │ ├── domain │ ├── service │ │ └── UserService.java │ ├── annotation │ │ └── Cluster.java │ ├── mapper │ │ ├── UserMapper.java │ │ └── User.java │ ├── datasource │ │ ├── ReadWriteSplitRoutingDataSource.java │ │ ├── DataSourceConfiguration.java │ │ └── MybatisConfiguration.java │ ├── controller │ │ ├── UserController.java │ │ └── TestController.java │ └── interceptor │ │ └── ClusterIntecerptor.java │ ├── pay │ ├── Service │ │ ├── IUnionPayService.java │ │ ├── IWeixinPayService.java │ │ └── IAliPayService.java │ ├── Constants │ │ ├── PayWay.java │ │ ├── PayType.java │ │ └── Constants.java │ ├── WXUtils │ │ ├── MD5Util.java │ │ ├── qrCodeUtil.java │ │ ├── OpenIdClass.java │ │ ├── HttpUtil.java │ │ ├── XMLUtil.java │ │ ├── ClientCustomSSL.java │ │ ├── MobileUtil.java │ │ ├── PayCommonUtil.java │ │ └── ConfigUtil.java │ ├── Main │ │ ├── Application.java │ │ └── Swagger2.java │ ├── AliUtils │ │ ├── AliPayConfig.java │ │ ├── CommonUtil.java │ │ ├── DateUtil.java │ │ └── AddressUtils.java │ ├── Controller │ │ ├── PayController.java │ │ ├── UnionPayController.java │ │ ├── AliPayController.java │ │ ├── WeixinMobilePayController.java │ │ └── WeixinPayController.java │ ├── DTO │ │ └── InfoDTO.java │ ├── UnionpayUtils │ │ ├── BaseHttpSSLSocketFactory.java │ │ ├── UnionConfig.java │ │ ├── SDKUtil.java │ │ ├── HttpClient.java │ │ ├── SDKConstants.java │ │ └── SDKConfig.java │ └── ServiceImpl │ │ ├── UnionPayServiceImpl.java │ │ ├── AliPayServiceImpl.java │ │ └── WeixinPayServiceImpl.java │ ├── domian │ └── serviceImpl │ │ └── UserServiceImpl.java │ ├── Swagger2Config.java │ └── Application.java ├── README.md └── pom.xml /src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | profiles: 3 | active: dev -------------------------------------------------------------------------------- /src/main/webapp/index.jsp: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Hello World!

4 | 5 | 6 | -------------------------------------------------------------------------------- /src/main/resources/keystore.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Howie1995/UnionPay/HEAD/src/main/resources/keystore.p12 -------------------------------------------------------------------------------- /src/main/webapp/WEB-INF/web.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | Archetype Created Web Application 7 | 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # UnionPay 2 | 3 | 这是别人的一个开源项目(戳[这里](https://gitee.com/52itstyle/spring-boot-pay)),当初本意是通过模仿这个项目去学习spring-boot的一些东西,后来在学习过程中加了一些自己的东西; 4 | 5 | 6 | 然后,如果在实际项目中有订单的业务也是可以拿去用的,添加对应的业务逻辑即可,工程里还自定义注解实现了分库的操作....... 7 | 8 | 9 | 然后,下面这个是一个博客的链接([戳](https://blog.csdn.net/Dream__Snow/article/details/82414181)),里面是我在学习过程中的一些总结,还有一些其他的优秀博客的集锦 10 | 11 | 12 | 希望能对有需要的朋友有点帮助,就酱..... 13 | -------------------------------------------------------------------------------- /src/main/java/com/howie/domain/service/UserService.java: -------------------------------------------------------------------------------- 1 | package com.howie.domain.service; 2 | 3 | import org.springframework.stereotype.Service; 4 | 5 | import com.howie.domain.annotation.Cluster; 6 | import com.howie.domain.mapper.User; 7 | 8 | public interface UserService { 9 | 10 | User findByName(String name); 11 | 12 | int insert(String id,String name,String pass); 13 | 14 | } -------------------------------------------------------------------------------- /src/main/java/com/howie/domain/annotation/Cluster.java: -------------------------------------------------------------------------------- 1 | package com.howie.domain.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Target({ElementType.METHOD,ElementType.TYPE}) 9 | @Retention(RetentionPolicy.RUNTIME) 10 | public @interface Cluster { 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/howie/pay/Service/IUnionPayService.java: -------------------------------------------------------------------------------- 1 | package com.howie.pay.service; 2 | 3 | import java.util.Map; 4 | 5 | import com.howie.pay.dto.InfoDTO; 6 | 7 | 8 | public interface IUnionPayService { 9 | /** 10 | * 银联支付 11 | * @author hongyang.jiang 12 | */ 13 | String unionPay(InfoDTO infoDTO); 14 | 15 | /** 16 | * @author hongyang.jiang 17 | */ 18 | String validate(Map valideData, String encoding); 19 | /** 20 | * 对账单下载 21 | * @author hongyang.jiang 22 | */ 23 | void fileTransfer(); 24 | } 25 | -------------------------------------------------------------------------------- /src/main/resources/wxinfo.properties: -------------------------------------------------------------------------------- 1 | #\u670d\u52a1\u53f7\u7684\u5e94\u7528ID 2 | APP_ID = XXXXXXXXXXXXXXXX 3 | #\u670d\u52a1\u53f7\u7684\u5e94\u7528\u5bc6\u94a5 4 | APP_SECRET = XXXXXXXXXXXXXXXX 5 | #\u670d\u52a1\u53f7\u7684\u914d\u7f6etoken 6 | TOKEN = XXXXXXXXXXXXXXXX 7 | #\u5546\u6237\u53f7 8 | MCH_ID = XXXXXXXXXXXXXXXX 9 | #API\u5bc6\u94a5 10 | API_KEY = XXXXXXXXXXXXXXXX 11 | #\u7b7e\u540d\u52a0\u5bc6\u65b9\u5f0f 12 | SIGN_TYPE = MD5 13 | #/\u5fae\u4fe1\u652f\u4ed8\u8bc1\u4e66\u540d\u79f0,\u5230\u5fae\u4fe1\u7f51\u7ad9\u4e0b\u8f7d 14 | CERT_PATH = apiclient_cert.p12 -------------------------------------------------------------------------------- /src/main/java/com/howie/domain/mapper/UserMapper.java: -------------------------------------------------------------------------------- 1 | package com.howie.domain.mapper; 2 | 3 | import org.apache.ibatis.annotations.Insert; 4 | import org.apache.ibatis.annotations.Mapper; 5 | import org.apache.ibatis.annotations.Param; 6 | import org.apache.ibatis.annotations.Select; 7 | 8 | import com.howie.domain.annotation.Cluster; 9 | 10 | @Mapper 11 | public interface UserMapper { 12 | 13 | @Select("SELECT * FROM USER WHERE username = #{name}") 14 | User findByName(@Param("name") String name); 15 | 16 | @Insert("INSERT INTO `user`(`user`.id,`user`.username,`user`.`password`) VALUES(#{id},#{name},#{pass})") 17 | int insert(@Param("id")String id,@Param("name") String name,@Param("pass")String pass); 18 | 19 | } -------------------------------------------------------------------------------- /src/main/java/com/howie/domain/mapper/User.java: -------------------------------------------------------------------------------- 1 | package com.howie.domain.mapper; 2 | 3 | public class User { 4 | 5 | private String id; 6 | private String username; 7 | private String password; 8 | 9 | public String getId() { 10 | return id; 11 | } 12 | public void setId(String id) { 13 | this.id = id; 14 | } 15 | public String getUsername() { 16 | return username; 17 | } 18 | public void setUsername(String username) { 19 | this.username = username; 20 | } 21 | public String getPassword() { 22 | return password; 23 | } 24 | public void setPassword(String password) { 25 | this.password = password; 26 | } 27 | @Override 28 | public String toString() { 29 | return "user [id=" + id + ", username=" + username + ", password=" 30 | + password + "]"; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/howie/domian/serviceImpl/UserServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.howie.domian.serviceImpl; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.stereotype.Service; 5 | 6 | import com.howie.domain.annotation.Cluster; 7 | import com.howie.domain.mapper.User; 8 | import com.howie.domain.mapper.UserMapper; 9 | import com.howie.domain.service.UserService; 10 | 11 | @Service(value="userService") 12 | public class UserServiceImpl implements UserService { 13 | 14 | @Autowired 15 | private UserMapper userMapper; 16 | 17 | @Override 18 | @Cluster 19 | public User findByName(String name) { 20 | return userMapper.findByName(name); 21 | } 22 | 23 | @Override 24 | public int insert(String id, String name, String pass) { 25 | return userMapper.insert(id, name, pass); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/howie/pay/Constants/PayWay.java: -------------------------------------------------------------------------------- 1 | package com.howie.pay.constants; 2 | /** 3 | * 支付途径 4 | * @author hongyang.jiang 5 | */ 6 | public enum PayWay { 7 | PC("PC,平板",(short)1),MOBILE("手机",(short)2); 8 | 9 | private Short code; 10 | private String name; 11 | 12 | private PayWay(String name, Short code) { 13 | this.name = name; 14 | this.code = code; 15 | } 16 | 17 | public static String getName(Short code,String name) { 18 | for (PayWay c : PayWay.values()) { 19 | if (c.getCode() == code) { 20 | return c.name; 21 | } 22 | } 23 | return null; 24 | } 25 | 26 | public String getName() { 27 | return name; 28 | } 29 | 30 | public void setName(String name) { 31 | this.name = name; 32 | } 33 | 34 | public short getCode() { 35 | return code; 36 | } 37 | 38 | public void setCode(short code) { 39 | this.code = code; 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/com/howie/pay/Constants/PayType.java: -------------------------------------------------------------------------------- 1 | package com.howie.pay.constants; 2 | /** 3 | * 支付类型 4 | * @author hongyang.jiang 5 | */ 6 | public enum PayType { 7 | /**支付类型*/ 8 | ALI("支付宝",(short)1),WECHAT("微信",(short)2),UNION("银联",(short)3); 9 | 10 | private Short code; 11 | private String name; 12 | 13 | private PayType(String name, Short code) { 14 | this.name = name; 15 | this.code = code; 16 | } 17 | 18 | public static String getName(Short code,String name) { 19 | for (PayType c : PayType.values()) { 20 | if (c.getCode() == code) { 21 | return c.name; 22 | } 23 | } 24 | return null; 25 | } 26 | 27 | public String getName() { 28 | return name; 29 | } 30 | 31 | public void setName(String name) { 32 | this.name = name; 33 | } 34 | 35 | public short getCode() { 36 | return code; 37 | } 38 | 39 | public void setCode(short code) { 40 | this.code = code; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/com/howie/domain/datasource/ReadWriteSplitRoutingDataSource.java: -------------------------------------------------------------------------------- 1 | package com.howie.domain.datasource; 2 | 3 | import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; 4 | 5 | 6 | public class ReadWriteSplitRoutingDataSource extends AbstractRoutingDataSource { 7 | 8 | private static final ThreadLocal contextHolder = new ThreadLocal(); 9 | 10 | /*@Override 11 | public void afterPropertiesSet() { 12 | setDefaultTargetDataSource(); 13 | }*/ 14 | 15 | @Override 16 | protected Object determineCurrentLookupKey() { 17 | return getDBType(); 18 | } 19 | 20 | public enum DBType { 21 | MASTER, CLUSTER 22 | } 23 | 24 | public static void setDbType(DBType dbType) { 25 | if (dbType == null) 26 | throw new NullPointerException(); 27 | contextHolder.set(dbType); 28 | } 29 | 30 | public static DBType getDBType() { 31 | return contextHolder.get() == null ? DBType.MASTER : contextHolder.get(); 32 | } 33 | 34 | public static void clearDbType() { 35 | contextHolder.remove(); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/howie/pay/Service/IWeixinPayService.java: -------------------------------------------------------------------------------- 1 | package com.howie.pay.service; 2 | 3 | import com.howie.pay.dto.InfoDTO; 4 | 5 | public interface IWeixinPayService { 6 | /** 7 | * 微信支付下单(模式二) 扫码支付 还有模式一 适合固定商品ID 有兴趣的同学可以自行研究 8 | * @author hongyang.jiang 9 | */ 10 | String weixinPay2(InfoDTO infoDTO); 11 | 12 | /** 13 | * 微信支付下单(模式一) 14 | * @author hongyang.jiang 15 | */ 16 | void weixinPay1(InfoDTO infoDTO); 17 | 18 | /** 19 | * 微信支付退款 20 | * @author hongyang.jiang 21 | */ 22 | String weixinRefund(InfoDTO infoDTO); 23 | 24 | /** 25 | * 关闭订单 26 | * @author hongyang.jiang 27 | */ 28 | String weixinCloseorder(InfoDTO infoDTO); 29 | 30 | /** 31 | * 下载微信账单 32 | * @author hongyang.jiang 33 | */ 34 | void saveBill(); 35 | 36 | /** 37 | * 微信公众号支付返回一个url地址 38 | * @author hongyang.jiang 39 | */ 40 | String weixinPayMobile(InfoDTO infoDTO); 41 | 42 | /** 43 | * H5支付 唤醒 微信APP 进行支付 申请入口:登录商户平台-->产品中心-->我的产品-->支付产品-->H5支付 44 | * @author hongyang.jiang 45 | */ 46 | String weixinPayH5(InfoDTO infoDTO); 47 | } 48 | -------------------------------------------------------------------------------- /src/main/resources/zfbinfo.properties: -------------------------------------------------------------------------------- 1 | # \u652f\u4ed8\u5b9d\u7f51\u5173\u540d\u3001partnerId\u548cappId 2 | open_api_domain = https://openapi.alipay.com/gateway.do 3 | mcloud_api_domain = http://mcloudmonitor.com/gateway.do 4 | #\u6b64\u5904\u8bf7\u586b\u5199\u4f60\u7684PID 5 | pid =XXXXXXXXXXXXXXXX 6 | #\u6b64\u5904\u8bf7\u586b\u5199\u4f60\u5f53\u9762\u4ed8\u7684APPID 7 | appid =XXXXXXXXXXXXXXXX 8 | 9 | # RSA\u79c1\u94a5\u3001\u516c\u94a5\u548c\u652f\u4ed8\u5b9d\u516c\u94a5 10 | private_key = XXXXXXXXXXXXXXXX 11 | public_key = XXXXXXXXXXXXXXXX 12 | alipay_public_key = XXXXXXXXXXXXXXXX 13 | 14 | # \u5f53\u9762\u4ed8\u6700\u5927\u67e5\u8be2\u6b21\u6570\u548c\u67e5\u8be2\u95f4\u9694\uff08\u6beb\u79d2\uff09 15 | max_query_retry = 5 16 | query_duration = 5000 17 | 18 | # \u5f53\u9762\u4ed8\u6700\u5927\u64a4\u9500\u6b21\u6570\u548c\u64a4\u9500\u95f4\u9694\uff08\u6beb\u79d2\uff09 19 | max_cancel_retry = 3 20 | cancel_duration = 2000 21 | 22 | # \u4ea4\u6613\u4fdd\u969c\u7ebf\u7a0b\u7b2c\u4e00\u6b21\u8c03\u5ea6\u5ef6\u8fdf\u548c\u8c03\u5ea6\u95f4\u9694\uff08\u79d2\uff09 23 | heartbeat_delay = 5 24 | heartbeat_duration = 900 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/main/java/com/howie/pay/Constants/Constants.java: -------------------------------------------------------------------------------- 1 | package com.howie.pay.constants; 2 | 3 | import org.springframework.util.ClassUtils; 4 | 5 | public class Constants { 6 | 7 | public static final String SF_FILE_SEPARATOR = System.getProperty("file.separator");//文件分隔符 8 | public static final String SF_LINE_SEPARATOR = System.getProperty("line.separator");//行分隔符 9 | public static final String SF_PATH_SEPARATOR = System.getProperty("path.separator");//路径分隔符 10 | 11 | public static final String QRCODE_PATH = ClassUtils.getDefaultClassLoader().getResource("static").getPath()+SF_FILE_SEPARATOR+"qrcode"; 12 | 13 | //微信账单 相关字段 用于load文本到数据库 14 | public static final String WEIXIN_BILL = "tradetime, ghid, mchid, submch, deviceid, wxorder, bzorder, openid, tradetype, tradestatus, bank, currency, totalmoney, redpacketmoney, wxrefund, bzrefund, refundmoney, redpacketrefund, refundtype, refundstatus, productname, bzdatapacket, fee, rate"; 15 | 16 | public static final String PATH_BASE_INFO_XML = SF_FILE_SEPARATOR+"WEB-INF"+SF_FILE_SEPARATOR+"xmlConfig"+SF_FILE_SEPARATOR; 17 | 18 | public static final String CURRENT_USER = "UserInfo"; 19 | 20 | public static final String SUCCESS = "success"; 21 | 22 | public static final String FAIL = "fail"; 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/howie/domain/controller/UserController.java: -------------------------------------------------------------------------------- 1 | package com.howie.domain.controller; 2 | 3 | import io.swagger.annotations.Api; 4 | import io.swagger.annotations.ApiOperation; 5 | 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.stereotype.Controller; 8 | import org.springframework.web.bind.annotation.RequestMapping; 9 | import org.springframework.web.bind.annotation.RequestMethod; 10 | import org.springframework.web.bind.annotation.RestController; 11 | 12 | import com.howie.domain.annotation.Cluster; 13 | import com.howie.domain.mapper.User; 14 | import com.howie.domain.service.UserService; 15 | 16 | @Controller 17 | @RestController 18 | @RequestMapping("/user") 19 | @Api(tags="用戶管理接口") 20 | public class UserController { 21 | 22 | @Autowired 23 | private UserService userService; 24 | 25 | @RequestMapping(value="/addUser.json",method=RequestMethod.GET) 26 | @ApiOperation(value="添加用戶",notes="添加用戶") 27 | int addUser(String id,String name,String pass){ 28 | return userService.insert(id, name, pass); 29 | } 30 | 31 | @RequestMapping(value="/queryUser.json",method=RequestMethod.GET) 32 | @ApiOperation(value="查詢用戶",notes="查詢用戶") 33 | User queryUser(String name){ 34 | return userService.findByName(name); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/howie/domain/controller/TestController.java: -------------------------------------------------------------------------------- 1 | package com.howie.domain.controller; 2 | 3 | import io.swagger.annotations.Api; 4 | import io.swagger.annotations.ApiOperation; 5 | 6 | import java.time.LocalDateTime; 7 | 8 | import org.springframework.beans.factory.annotation.Value; 9 | import org.springframework.stereotype.Controller; 10 | import org.springframework.web.bind.annotation.RequestMapping; 11 | import org.springframework.web.bind.annotation.RequestMethod; 12 | import org.springframework.web.bind.annotation.RestController; 13 | 14 | @Controller 15 | @RestController 16 | @RequestMapping("/test") 17 | @Api(tags="測試接口") 18 | public class TestController { 19 | 20 | @Value("${test_variable}") 21 | private int test_variable; 22 | 23 | @RequestMapping(value="/test.json",method=RequestMethod.GET) 24 | @ApiOperation(value="测试接口",notes="返回问候信息") 25 | String index(){ 26 | return "Hello,This is SpringBoot!"; 27 | } 28 | 29 | 30 | @RequestMapping(value="/now.json",method=RequestMethod.GET) 31 | @ApiOperation(value="测试时间接口",notes="返回当前时间") 32 | String now(){ 33 | return "現在是北京時間:"+LocalDateTime.now().toString(); 34 | } 35 | 36 | @RequestMapping(value="/getVar.json",method=RequestMethod.GET) 37 | @ApiOperation(value="获取yml配置变量",notes="获取配置文件变量") 38 | int getVar(){ 39 | return test_variable; 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/com/howie/pay/WXUtils/MD5Util.java: -------------------------------------------------------------------------------- 1 | package com.howie.pay.wxUtils; 2 | 3 | import java.security.MessageDigest; 4 | /** 5 | * MD5加密 6 | * @author hongyang.jiang 7 | */ 8 | public class MD5Util { 9 | 10 | private static String byteArrayToHexString(byte b[]) { 11 | StringBuffer resultSb = new StringBuffer(); 12 | for (int i = 0; i < b.length; i++) 13 | resultSb.append(byteToHexString(b[i])); 14 | 15 | return resultSb.toString(); 16 | } 17 | 18 | private static String byteToHexString(byte b) { 19 | int n = b; 20 | if (n < 0) 21 | n += 256; 22 | int d1 = n / 16; 23 | int d2 = n % 16; 24 | return hexDigits[d1] + hexDigits[d2]; 25 | } 26 | 27 | public static String MD5Encode(String origin, String charsetname) { 28 | String resultString = null; 29 | try { 30 | resultString = new String(origin); 31 | MessageDigest md = MessageDigest.getInstance("MD5"); 32 | if (charsetname == null || "".equals(charsetname)) 33 | resultString = byteArrayToHexString(md.digest(resultString.getBytes())); 34 | else 35 | resultString = byteArrayToHexString(md.digest(resultString.getBytes(charsetname))); 36 | } catch (Exception exception) { 37 | } 38 | return resultString; 39 | } 40 | 41 | private static final String hexDigits[] = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f" }; 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/com/howie/pay/WXUtils/qrCodeUtil.java: -------------------------------------------------------------------------------- 1 | package com.howie.pay.wxUtils; 2 | import java.util.SortedMap; 3 | import java.util.TreeMap; 4 | 5 | import com.alipay.demo.trade.utils.ZxingUtils; 6 | /** 7 | * 二维码生成器(扫码支付模式一) 8 | * @author hongyang.jiang 9 | */ 10 | public class qrCodeUtil { 11 | //商户支付回调URL设置指引:进入公众平台-->微信支付-->开发配置-->扫码支付-->修改 加入回调URL 12 | public static void main(String[] args) { 13 | //注意参数初始化 这只是个Demo 14 | SortedMap packageParams = new TreeMap(); 15 | //封装通用参数 16 | ConfigUtil.commonParams(packageParams); 17 | packageParams.put("product_id", "20170731");//真实商品ID 18 | packageParams.put("time_stamp", PayCommonUtil.getCurrTime()); 19 | //生成签名 20 | String sign = PayCommonUtil.createSign("UTF-8", packageParams, ConfigUtil.API_KEY); 21 | //组装二维码信息(注意全角和半角:的区别 狗日的腾讯) 22 | StringBuffer qrCode = new StringBuffer(); 23 | qrCode.append("weixin://wxpay/bizpayurl?"); 24 | qrCode.append("appid="+ConfigUtil.APP_ID); 25 | qrCode.append("&mch_id="+ConfigUtil.MCH_ID); 26 | qrCode.append("&nonce_str="+packageParams.get("nonce_str")); 27 | qrCode.append("&product_id=20170731"); 28 | qrCode.append("&time_stamp="+packageParams.get("time_stamp")); 29 | qrCode.append("&sign="+sign); 30 | //生成二维码 31 | ZxingUtils.getQRCodeImge(qrCode.toString(), 256, "D:\\weixn.png"); 32 | } 33 | } -------------------------------------------------------------------------------- /src/main/java/com/howie/domain/interceptor/ClusterIntecerptor.java: -------------------------------------------------------------------------------- 1 | package com.howie.domain.interceptor; 2 | 3 | import org.aspectj.lang.ProceedingJoinPoint; 4 | import org.aspectj.lang.annotation.Around; 5 | import org.aspectj.lang.annotation.Aspect; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | import org.springframework.core.Ordered; 9 | import org.springframework.stereotype.Component; 10 | 11 | import com.howie.domain.annotation.Cluster; 12 | import com.howie.domain.datasource.ReadWriteSplitRoutingDataSource; 13 | 14 | @Aspect 15 | @Component 16 | public class ClusterIntecerptor implements Ordered { 17 | 18 | public static final Logger logger = LoggerFactory.getLogger(ClusterIntecerptor.class); 19 | 20 | @Around("@annotation(cluster)") 21 | public Object proceed(ProceedingJoinPoint proceedingJoinPoint,Cluster cluster) throws Throwable { 22 | try { 23 | logger.info("set database connection to read only"); 24 | ReadWriteSplitRoutingDataSource.setDbType(ReadWriteSplitRoutingDataSource.DBType.CLUSTER); 25 | Object result = proceedingJoinPoint.proceed(); 26 | return result; 27 | }finally { 28 | ReadWriteSplitRoutingDataSource.clearDbType(); 29 | logger.info("restore database connection"); 30 | } 31 | } 32 | 33 | @Override 34 | public int getOrder() { 35 | // TODO Auto-generated method stub 36 | return 0; 37 | } 38 | 39 | } -------------------------------------------------------------------------------- /src/main/java/com/howie/domain/datasource/DataSourceConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.howie.domain.datasource; 2 | 3 | import javax.sql.DataSource; 4 | 5 | import org.springframework.beans.factory.annotation.Value; 6 | import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder; 7 | import org.springframework.boot.context.properties.ConfigurationProperties; 8 | import org.springframework.context.annotation.Bean; 9 | import org.springframework.context.annotation.Configuration; 10 | import org.springframework.context.annotation.Primary; 11 | import org.springframework.transaction.annotation.EnableTransactionManagement; 12 | 13 | @Configuration 14 | @EnableTransactionManagement 15 | public class DataSourceConfiguration { 16 | 17 | @Value("${druid.type}") 18 | private Class dataSourceType; 19 | 20 | @Bean(name="masterDataSource") 21 | @Primary 22 | @ConfigurationProperties(prefix="druid.master.datasource") 23 | public DataSource masterDataSource(){ 24 | DataSource masterDataSource = DataSourceBuilder.create().type(dataSourceType).build(); 25 | return masterDataSource; 26 | } 27 | 28 | @Bean(name="clusterDataSource") 29 | @ConfigurationProperties(prefix="druid.cluster.datasource") 30 | public DataSource clusterDataSource(){ 31 | DataSource clusterDataSource = DataSourceBuilder.create().type(dataSourceType).build(); 32 | return clusterDataSource; 33 | } 34 | 35 | } -------------------------------------------------------------------------------- /src/main/resources/spring-context-dubbo.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/main/java/com/howie/pay/Service/IAliPayService.java: -------------------------------------------------------------------------------- 1 | package com.howie.pay.service; 2 | import com.howie.pay.dto.InfoDTO; 3 | /** 4 | * 扫码支付以及手机H5支付 5 | * @author hongyang.jiang 6 | */ 7 | public interface IAliPayService { 8 | /** 9 | * 阿里支付预下单 10 | * 如果你调用的是当面付预下单接口(alipay.trade.precreate),调用成功后订单实际上是没有生成,因为创建一笔订单要买家、卖家、金额三要素。 11 | * 预下单并没有创建订单,所以根据商户订单号操作订单,比如查询或者关闭,会报错订单不存在。 12 | * 当用户扫码后订单才会创建,用户扫码之前二维码有效期2小时,扫码之后有效期根据timeout_express时间指定。 13 | * @author hongyang.jiang 14 | * 15 | */ 16 | String aliPay(InfoDTO infoDTO); 17 | /** 18 | * 阿里支付退款 19 | * @author hongyang.jiang 20 | * 21 | */ 22 | String aliRefund(InfoDTO infoDTO); 23 | /** 24 | * 关闭订单 25 | * @author hongyang.jiang 26 | * 27 | */ 28 | String aliCloseorder(InfoDTO infoDTO); 29 | /** 30 | * 下载对账单 31 | * @author hongyang.jiang 32 | * 33 | */ 34 | String downloadBillUrl(String billDate,String billType); 35 | /** 36 | * 手机H5支付、腾讯相关软件下不支持、使用UC等浏览器打开 37 | * 方法一: 38 | * 对于页面跳转类API,SDK不会也无法像系统调用类API一样自动请求支付宝并获得结果,而是在接受request请求对象后, 39 | * 为开发者生成前台页面请求需要的完整form表单的html(包含自动提交脚本),商户直接将这个表单的String输出到http response中即可。 40 | * 方法二: 41 | * 如果是远程调用返回消费放一个form表单 然后调用方刷新到页面自动提交即可 42 | * @author hongyang.jiang 43 | */ 44 | String aliPayMobile(InfoDTO InfoDTO); 45 | /** 46 | * 网站支付 47 | * @author hongyang.jiang 48 | * 49 | */ 50 | String aliPayPc(InfoDTO InfoDTO); 51 | /** 52 | * APP支付 53 | * @author hongyang.jiang 54 | * 55 | */ 56 | String appPay(InfoDTO InfoDTO); 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/com/howie/pay/Main/Application.java: -------------------------------------------------------------------------------- 1 | package com.howie.pay.Main; 2 | import java.io.IOException; 3 | 4 | import org.apache.log4j.Logger; 5 | import org.springframework.boot.SpringApplication; 6 | import org.springframework.boot.autoconfigure.SpringBootApplication; 7 | import org.springframework.context.annotation.ImportResource; 8 | import org.springframework.stereotype.Controller; 9 | import org.springframework.web.bind.annotation.RequestMapping; 10 | import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; 11 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; 12 | /** 13 | * 支付主控 14 | * @author hongyang.jiang 15 | */ 16 | @SpringBootApplication 17 | @ImportResource({"classpath:spring-context-dubbo.xml"}) 18 | @Controller 19 | public class Application extends WebMvcConfigurerAdapter { 20 | private static final Logger logger = Logger.getLogger(Application.class); 21 | 22 | @RequestMapping("/") 23 | public String greeting() { 24 | return "index"; 25 | } 26 | 27 | @Override 28 | public void addResourceHandlers(ResourceHandlerRegistry registry) { 29 | registry.addResourceHandler("/cert/**").addResourceLocations( 30 | "classpath:/cert/"); 31 | super.addResourceHandlers(registry); 32 | logger.info("自定义静态资源目录,这只是个Demo,生产肯定不会暴露"); 33 | } 34 | 35 | public static void main(String[] args) throws InterruptedException, 36 | IOException { 37 | SpringApplication.run(Application.class, args); 38 | // 初始化 支付宝 微信 银联 参数 涉及机密 此文件不提交 请自行配置加载 39 | //Configs.init("zfbinfo.properties"); 40 | //ConfigUtil.init("wxinfo.properties"); 41 | //SDKConfig.getConfig().loadPropertiesFromSrc(); 42 | logger.info("支付项目启动 "); 43 | } 44 | 45 | } -------------------------------------------------------------------------------- /src/main/java/com/howie/pay/AliUtils/AliPayConfig.java: -------------------------------------------------------------------------------- 1 | package com.howie.pay.aliUtils; 2 | import com.alipay.api.AlipayClient; 3 | import com.alipay.api.DefaultAlipayClient; 4 | import com.alipay.demo.trade.config.Configs; 5 | import com.alipay.demo.trade.service.AlipayTradeService; 6 | import com.alipay.demo.trade.service.impl.AlipayTradeServiceImpl; 7 | /** 8 | * 配置公共参数 9 | * @author hongyang.jiang 10 | */ 11 | public final class AliPayConfig { 12 | 13 | /** 14 | * 私有的默认构造子,保证外界无法直接实例化 15 | */ 16 | private AliPayConfig(){}; 17 | /** 18 | * 参数类型 19 | */ 20 | public static String PARAM_TYPE = "json"; 21 | /** 22 | * 编码 23 | */ 24 | public static String CHARSET = "UTF-8"; 25 | /** 26 | * 类级的内部类,也就是静态的成员式内部类,该内部类的实例与外部类的实例 27 | * 没有绑定关系,而且只有被调用到才会装载,从而实现了延迟加载 28 | */ 29 | private static class SingletonHolder{ 30 | /** 31 | * 静态初始化器,由JVM来保证线程安全 32 | */ 33 | private static AlipayClient alipayClient = new DefaultAlipayClient( 34 | Configs.getOpenApiDomain(), Configs.getAppid(), 35 | Configs.getPrivateKey(), PARAM_TYPE, CHARSET, 36 | Configs.getAlipayPublicKey(),"RSA2"); 37 | 38 | private static AlipayTradeService tradeService = new AlipayTradeServiceImpl.ClientBuilder().build(); 39 | } 40 | /** 41 | * 支付宝APP请求客户端实例 42 | * @author hongyang.jiang 43 | */ 44 | public static AlipayClient getAlipayClient(){ 45 | return SingletonHolder.alipayClient; 46 | } 47 | /** 48 | * 电脑端预下单 49 | * @author hongyang.jiang 50 | */ 51 | public static AlipayTradeService getAlipayTradeService(){ 52 | return SingletonHolder.tradeService; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/com/howie/pay/WXUtils/OpenIdClass.java: -------------------------------------------------------------------------------- 1 | package com.howie.pay.wxUtils; 2 | 3 | /** 4 | * 微信用户信息 5 | */ 6 | public class OpenIdClass { 7 | private String access_token; 8 | private String expires_in; 9 | private String refresh_token; 10 | private String openid; 11 | private String scope; 12 | private String unionid; 13 | 14 | public String getAccess_token() { 15 | return access_token; 16 | } 17 | 18 | public void setAccess_token(String access_token) { 19 | this.access_token = access_token; 20 | } 21 | 22 | public String getExpires_in() { 23 | return expires_in; 24 | } 25 | 26 | public void setExpires_in(String expires_in) { 27 | this.expires_in = expires_in; 28 | } 29 | 30 | public String getRefresh_token() { 31 | return refresh_token; 32 | } 33 | 34 | public void setRefresh_token(String refresh_token) { 35 | this.refresh_token = refresh_token; 36 | } 37 | 38 | public String getOpenid() { 39 | return openid; 40 | } 41 | 42 | public void setOpenid(String openid) { 43 | this.openid = openid; 44 | } 45 | 46 | public String getScope() { 47 | return scope; 48 | } 49 | 50 | public void setScope(String scope) { 51 | this.scope = scope; 52 | } 53 | 54 | public String getUnionid() { 55 | return unionid; 56 | } 57 | 58 | public void setUnionid(String unionid) { 59 | this.unionid = unionid; 60 | } 61 | 62 | public String getErrcode() { 63 | return errcode; 64 | } 65 | 66 | public void setErrcode(String errcode) { 67 | this.errcode = errcode; 68 | } 69 | 70 | public String getErrmsg() { 71 | return errmsg; 72 | } 73 | 74 | public void setErrmsg(String errmsg) { 75 | this.errmsg = errmsg; 76 | } 77 | 78 | private String errcode; 79 | private String errmsg; 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/com/howie/Swagger2Config.java: -------------------------------------------------------------------------------- 1 | package com.howie; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | 6 | import springfox.documentation.builders.ApiInfoBuilder; 7 | import springfox.documentation.builders.PathSelectors; 8 | import springfox.documentation.builders.RequestHandlerSelectors; 9 | import springfox.documentation.service.ApiInfo; 10 | import springfox.documentation.service.Contact; 11 | import springfox.documentation.spi.DocumentationType; 12 | import springfox.documentation.spring.web.plugins.Docket; 13 | import springfox.documentation.swagger2.annotations.EnableSwagger2; 14 | 15 | @Configuration 16 | @EnableSwagger2 17 | public class Swagger2Config { 18 | 19 | @Bean 20 | public Docket payApi(){ 21 | return new Docket(DocumentationType.SWAGGER_2) 22 | .groupName("支付接口文档") 23 | .apiInfo(apiInfo()) 24 | .select() 25 | .apis(RequestHandlerSelectors.basePackage("com.howie.pay.controller")) 26 | .paths(PathSelectors.any()) 27 | .build(); 28 | } 29 | 30 | public Docket webApi(){ 31 | return new Docket(DocumentationType.SWAGGER_2) 32 | .groupName("测试接口文档") 33 | .apiInfo(apiInfo()) 34 | .select() 35 | .apis(RequestHandlerSelectors.basePackage("com.howie.domain.controller")) 36 | .paths(PathSelectors.any()) 37 | .build(); 38 | } 39 | 40 | private ApiInfo apiInfo() { 41 | // TODO Auto-generated method stub 42 | return new ApiInfoBuilder() 43 | .title("Spring-Boot-Test") 44 | .description("测试SpringBoot") 45 | //.termsOfServiceUrl("http://blog.52itstyle.com") 46 | .contact(new Contact("j.howie","It's a secret!","j.howie1995@outlook.com")) 47 | .version("1.0") 48 | .build(); 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/com/howie/pay/WXUtils/HttpUtil.java: -------------------------------------------------------------------------------- 1 | package com.howie.pay.wxUtils; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.IOException; 5 | import java.io.InputStreamReader; 6 | import java.io.OutputStreamWriter; 7 | import java.net.URL; 8 | import java.net.URLConnection; 9 | /** 10 | * http请求(这里用户获取订单url生成二维码) 11 | * @author hongyang.jiang 12 | */ 13 | public class HttpUtil { 14 | private final static int CONNECT_TIMEOUT = 5000; // in milliseconds 15 | private final static String DEFAULT_ENCODING = "UTF-8"; 16 | 17 | public static String postData(String urlStr, String data) { 18 | return postData(urlStr, data, null); 19 | } 20 | 21 | public static String postData(String urlStr, String data, String contentType) { 22 | BufferedReader reader = null; 23 | try { 24 | URL url = new URL(urlStr); 25 | URLConnection conn = url.openConnection(); 26 | conn.setDoOutput(true); 27 | conn.setConnectTimeout(CONNECT_TIMEOUT); 28 | conn.setReadTimeout(CONNECT_TIMEOUT); 29 | if (contentType != null) 30 | conn.setRequestProperty("content-type", contentType); 31 | OutputStreamWriter writer = new OutputStreamWriter(conn.getOutputStream(), DEFAULT_ENCODING); 32 | if (data == null) 33 | data = ""; 34 | writer.write(data); 35 | writer.flush(); 36 | writer.close(); 37 | 38 | reader = new BufferedReader(new InputStreamReader(conn.getInputStream(), DEFAULT_ENCODING)); 39 | StringBuilder sb = new StringBuilder(); 40 | String line = null; 41 | while ((line = reader.readLine()) != null) { 42 | sb.append(line); 43 | sb.append("\r\n"); 44 | } 45 | return sb.toString(); 46 | } catch (IOException e) { 47 | e.printStackTrace(); 48 | } finally { 49 | try { 50 | if (reader != null) 51 | reader.close(); 52 | } catch (IOException e) { 53 | } 54 | } 55 | return null; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/com/howie/Application.java: -------------------------------------------------------------------------------- 1 | package com.howie; 2 | import java.io.IOException; 3 | 4 | import org.apache.log4j.Logger; 5 | import org.mybatis.spring.annotation.MapperScan; 6 | import org.springframework.boot.SpringApplication; 7 | import org.springframework.boot.autoconfigure.SpringBootApplication; 8 | import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; 9 | import org.springframework.context.annotation.ImportResource; 10 | import org.springframework.stereotype.Controller; 11 | import org.springframework.web.bind.annotation.RequestMapping; 12 | import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; 13 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; 14 | 15 | @SpringBootApplication(exclude = { 16 | DataSourceAutoConfiguration.class 17 | }) 18 | @MapperScan("com.howie.domain.mapper") 19 | @ImportResource({"classpath:spring-context-dubbo.xml"}) 20 | @Controller 21 | public class Application extends WebMvcConfigurerAdapter { 22 | private static final Logger logger = Logger.getLogger(Application.class); 23 | 24 | @RequestMapping("/") 25 | public String greeting() { 26 | return "index"; 27 | } 28 | 29 | @Override 30 | public void addResourceHandlers(ResourceHandlerRegistry registry) { 31 | registry.addResourceHandler("/cert/**").addResourceLocations( 32 | "classpath:/cert/"); 33 | super.addResourceHandlers(registry); 34 | logger.info("自定义静态资源目录,这只是个Demo,生产肯定不会暴露"); 35 | } 36 | 37 | public static void main(String[] args) throws InterruptedException, 38 | IOException { 39 | SpringApplication.run(Application.class, args); 40 | // 初始化 支付宝 微信 银联 参数 涉及机密 此文件不提交 请自行配置加载 41 | //Configs.init("zfbinfo.properties"); 42 | //ConfigUtil.init("wxinfo.properties"); 43 | //SDKConfig.getConfig().loadPropertiesFromSrc(); 44 | logger.info("支付项目启动 "); 45 | } 46 | 47 | } -------------------------------------------------------------------------------- /src/main/java/com/howie/pay/Controller/PayController.java: -------------------------------------------------------------------------------- 1 | package com.howie.pay.controller; 2 | 3 | import io.swagger.annotations.Api; 4 | import io.swagger.annotations.ApiOperation; 5 | 6 | import javax.servlet.http.HttpServletRequest; 7 | import javax.servlet.http.HttpServletResponse; 8 | 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | import org.springframework.stereotype.Controller; 12 | import org.springframework.ui.Model; 13 | import org.springframework.web.bind.annotation.RequestMapping; 14 | import org.springframework.web.bind.annotation.RequestMethod; 15 | import org.springframework.web.bind.annotation.ResponseBody; 16 | 17 | import com.howie.pay.aliUtils.DateUtil; 18 | 19 | @Api(tags = "支付后台") 20 | @Controller 21 | @RequestMapping(value = "pay") 22 | public class PayController { 23 | private static final Logger logger = LoggerFactory 24 | .getLogger(PayController.class); 25 | 26 | @ApiOperation(value = "登陆首页") 27 | @RequestMapping(value = "index", method = RequestMethod.GET) 28 | public String index() { 29 | logger.info("登陆首页"); 30 | return "web/index"; 31 | } 32 | 33 | @ApiOperation(value = "登陆") 34 | @RequestMapping(value = "login", method = RequestMethod.POST) 35 | public @ResponseBody String login(HttpServletRequest request, 36 | HttpServletResponse response, String account, String password) 37 | throws Exception { 38 | logger.info("登陆"); 39 | String param = "false"; 40 | if ("admin".equals(account) && "111111".equals(password)) { 41 | param = "true"; 42 | } 43 | return param; 44 | } 45 | 46 | @ApiOperation(value = "后台展示") 47 | @RequestMapping(value = "main", method = RequestMethod.GET) 48 | public String main(HttpServletRequest request, 49 | HttpServletResponse response, Model model) throws Exception { 50 | model.addAttribute("ip", "192.168.1.66"); 51 | model.addAttribute("address", "青岛"); 52 | model.addAttribute("time", DateUtil.getTime()); 53 | return "web/main"; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/com/howie/domain/datasource/MybatisConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.howie.domain.datasource; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | import javax.annotation.Resource; 7 | import javax.sql.DataSource; 8 | 9 | import org.apache.commons.logging.Log; 10 | import org.apache.commons.logging.LogFactory; 11 | import org.apache.ibatis.session.SqlSessionFactory; 12 | import org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration; 13 | import org.springframework.boot.autoconfigure.AutoConfigureAfter; 14 | import org.springframework.context.annotation.Bean; 15 | import org.springframework.context.annotation.Configuration; 16 | import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; 17 | 18 | @Configuration 19 | @AutoConfigureAfter({DataSourceConfiguration.class}) 20 | public class MybatisConfiguration extends MybatisAutoConfiguration { 21 | 22 | private static Log logger = LogFactory.getLog(MybatisConfiguration.class); 23 | 24 | @Resource(name = "masterDataSource") 25 | private DataSource masterDataSource; 26 | @Resource(name = "clusterDataSource") 27 | private DataSource clusterDataSource; 28 | 29 | @Bean 30 | public SqlSessionFactory sqlSessionFactory() throws Exception { 31 | return super.sqlSessionFactory(roundRobinDataSourceProxy()); 32 | } 33 | 34 | public AbstractRoutingDataSource roundRobinDataSourceProxy() { 35 | ReadWriteSplitRoutingDataSource proxy = new ReadWriteSplitRoutingDataSource(); 36 | /*Map targetDataResources = 37 | new ClassLoaderRepository.SoftHashMap();*/ 38 | Map targetDataResources = new HashMap(); 39 | targetDataResources.put(ReadWriteSplitRoutingDataSource.DBType.MASTER, masterDataSource); 40 | targetDataResources.put(ReadWriteSplitRoutingDataSource.DBType.CLUSTER, clusterDataSource); 41 | //proxy.setDefaultTargetDataSource(masterDataSource);// 默认源 42 | proxy.setTargetDataSources(targetDataResources); 43 | proxy.afterPropertiesSet(); 44 | return proxy; 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/com/howie/pay/DTO/InfoDTO.java: -------------------------------------------------------------------------------- 1 | package com.howie.pay.dto; 2 | 3 | public class InfoDTO { 4 | private static final long serialVersionUID = 1L; 5 | private String productId;// 商品ID 6 | private String subject;//订单名称 7 | private String body;// 商品描述 8 | private String totalFee;// 总金额(单位是分) 9 | private String outTradeNo;// 订单号(唯一) 10 | private String spbillCreateIp;// 发起人IP地址 11 | private String attach;// 附件数据主要用于商户携带订单的自定义数据 12 | private Short payType;// 支付类型(1:支付宝 2:微信 3:银联) 13 | private Short payWay;// 支付方式 (1:PC,平板 2:手机) 14 | private String frontUrl;// 前台回调地址 非扫码支付使用 15 | public String getProductId() { 16 | return productId; 17 | } 18 | public void setProductId(String productId) { 19 | this.productId = productId; 20 | } 21 | public String getSubject() { 22 | return subject; 23 | } 24 | public void setSubject(String subject) { 25 | this.subject = subject; 26 | } 27 | public String getBody() { 28 | return body; 29 | } 30 | public void setBody(String body) { 31 | this.body = body; 32 | } 33 | public String getTotalFee() { 34 | return totalFee; 35 | } 36 | public void setTotalFee(String totalFee) { 37 | this.totalFee = totalFee; 38 | } 39 | public String getOutTradeNo() { 40 | return outTradeNo; 41 | } 42 | public void setOutTradeNo(String outTradeNo) { 43 | this.outTradeNo = outTradeNo; 44 | } 45 | public String getSpbillCreateIp() { 46 | return spbillCreateIp; 47 | } 48 | public void setSpbillCreateIp(String spbillCreateIp) { 49 | this.spbillCreateIp = spbillCreateIp; 50 | } 51 | public String getAttach() { 52 | return attach; 53 | } 54 | public void setAttach(String attach) { 55 | this.attach = attach; 56 | } 57 | public Short getPayType() { 58 | return payType; 59 | } 60 | public void setPayType(Short payType) { 61 | this.payType = payType; 62 | } 63 | public Short getPayWay() { 64 | return payWay; 65 | } 66 | public void setPayWay(Short payWay) { 67 | this.payWay = payWay; 68 | } 69 | public String getFrontUrl() { 70 | return frontUrl; 71 | } 72 | public void setFrontUrl(String frontUrl) { 73 | this.frontUrl = frontUrl; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/com/howie/pay/Main/Swagger2.java: -------------------------------------------------------------------------------- 1 | package com.howie.pay.Main; 2 | import org.springframework.context.annotation.Bean; 3 | import org.springframework.context.annotation.Configuration; 4 | import springfox.documentation.builders.ApiInfoBuilder; 5 | import springfox.documentation.builders.PathSelectors; 6 | import springfox.documentation.builders.RequestHandlerSelectors; 7 | import springfox.documentation.service.ApiInfo; 8 | import springfox.documentation.service.Contact; 9 | import springfox.documentation.spi.DocumentationType; 10 | import springfox.documentation.spring.web.plugins.Docket; 11 | import springfox.documentation.swagger2.annotations.EnableSwagger2; 12 | @Configuration 13 | @EnableSwagger2 14 | public class Swagger2 { 15 | @Bean 16 | public Docket webApi() { 17 | return new Docket(DocumentationType.SWAGGER_2) 18 | .groupName("支付后台API接口文档") 19 | .apiInfo(apiInfo()) 20 | .select() 21 | .apis(RequestHandlerSelectors.basePackage("com.itstyle.modules.web")) 22 | .paths(PathSelectors.any()).build(); 23 | } 24 | @Bean 25 | public Docket alipayApi() { 26 | return new Docket(DocumentationType.SWAGGER_2) 27 | .groupName("支付宝API接口文档") 28 | .apiInfo(apiInfo()) 29 | .select() 30 | .apis(RequestHandlerSelectors.basePackage("com.itstyle.modules.alipay")) 31 | .paths(PathSelectors.any()).build(); 32 | } 33 | @Bean 34 | public Docket weixinpayApi() { 35 | return new Docket(DocumentationType.SWAGGER_2) 36 | .groupName("微信API接口文档") 37 | .apiInfo(apiInfo()) 38 | .select() 39 | .apis(RequestHandlerSelectors.basePackage("com.itstyle.modules.weixinpay")) 40 | .paths(PathSelectors.any()).build(); 41 | } 42 | @Bean 43 | public Docket unionpayApi() { 44 | return new Docket(DocumentationType.SWAGGER_2) 45 | .groupName("银联API接口文档") 46 | .apiInfo(apiInfo()) 47 | .select() 48 | .apis(RequestHandlerSelectors.basePackage("com.itstyle.modules.unionpay")) 49 | .paths(PathSelectors.any()).build(); 50 | } 51 | private ApiInfo apiInfo() { 52 | return new ApiInfoBuilder() 53 | .title("支付系统") 54 | .description("微信、支付宝、银联支付服务") 55 | .termsOfServiceUrl("http://blog.52itstyle.com") 56 | .contact(new Contact("科帮网 ", "http://blog.52itstyle.com", "345849402@qq.com")) 57 | .version("1.0").build(); 58 | } 59 | 60 | } -------------------------------------------------------------------------------- /src/main/java/com/howie/pay/WXUtils/XMLUtil.java: -------------------------------------------------------------------------------- 1 | package com.howie.pay.wxUtils; 2 | 3 | import java.io.ByteArrayInputStream; 4 | import java.io.IOException; 5 | import java.io.InputStream; 6 | import java.util.HashMap; 7 | import java.util.Iterator; 8 | import java.util.List; 9 | import java.util.Map; 10 | 11 | import org.jdom.Document; 12 | import org.jdom.Element; 13 | import org.jdom.JDOMException; 14 | import org.jdom.input.SAXBuilder; 15 | /** 16 | * XML解析 17 | * @author hongyang.jiang 18 | */ 19 | public class XMLUtil { 20 | /** 21 | * 解析xml,返回第一级元素键值对。如果第一级元素有子节点,则此节点的值是子节点的xml数据。 22 | * @author hongyang.jiang 23 | */ 24 | @SuppressWarnings({ "rawtypes", "unchecked" }) 25 | public static Map doXMLParse(String strxml) throws JDOMException, IOException { 26 | strxml = strxml.replaceFirst("encoding=\".*\"", "encoding=\"UTF-8\""); 27 | 28 | if (null == strxml || "".equals(strxml)) { 29 | return null; 30 | } 31 | 32 | Map m = new HashMap(); 33 | 34 | InputStream in = new ByteArrayInputStream(strxml.getBytes("UTF-8")); 35 | SAXBuilder builder = new SAXBuilder(); 36 | Document doc = builder.build(in); 37 | Element root = doc.getRootElement(); 38 | List list = root.getChildren(); 39 | Iterator it = list.iterator(); 40 | while (it.hasNext()) { 41 | Element e = (Element) it.next(); 42 | String k = e.getName(); 43 | String v = ""; 44 | List children = e.getChildren(); 45 | if (children.isEmpty()) { 46 | v = e.getTextNormalize(); 47 | } else { 48 | v = XMLUtil.getChildrenText(children); 49 | } 50 | 51 | m.put(k, v); 52 | } 53 | 54 | // 关闭流 55 | in.close(); 56 | 57 | return m; 58 | } 59 | 60 | /** 61 | * 获取子结点的xml 62 | * @author hongyang.jiang 63 | */ 64 | @SuppressWarnings({ "rawtypes" }) 65 | public static String getChildrenText(List children) { 66 | StringBuffer sb = new StringBuffer(); 67 | if (!children.isEmpty()) { 68 | Iterator it = children.iterator(); 69 | while (it.hasNext()) { 70 | Element e = (Element) it.next(); 71 | String name = e.getName(); 72 | String value = e.getTextNormalize(); 73 | List list = e.getChildren(); 74 | sb.append("<" + name + ">"); 75 | if (!list.isEmpty()) { 76 | sb.append(XMLUtil.getChildrenText(list)); 77 | } 78 | sb.append(value); 79 | sb.append(""); 80 | } 81 | } 82 | 83 | return sb.toString(); 84 | } 85 | 86 | } 87 | -------------------------------------------------------------------------------- /src/main/java/com/howie/pay/WXUtils/ClientCustomSSL.java: -------------------------------------------------------------------------------- 1 | package com.howie.pay.wxUtils; 2 | import java.io.File; 3 | import java.io.FileInputStream; 4 | import java.security.KeyStore; 5 | 6 | import javax.net.ssl.SSLContext; 7 | 8 | import org.apache.http.HttpEntity; 9 | import org.apache.http.client.methods.CloseableHttpResponse; 10 | import org.apache.http.client.methods.HttpPost; 11 | import org.apache.http.conn.ssl.SSLConnectionSocketFactory; 12 | import org.apache.http.conn.ssl.SSLContexts; 13 | import org.apache.http.entity.StringEntity; 14 | import org.apache.http.impl.client.CloseableHttpClient; 15 | import org.apache.http.impl.client.HttpClients; 16 | import org.apache.http.util.EntityUtils; 17 | import org.springframework.util.ResourceUtils; 18 | 19 | import com.howie.pay.constants.Constants; 20 | /** 21 | * 退款认证 22 | * @author hongyang.jiang 23 | */ 24 | public class ClientCustomSSL { 25 | public static String doRefund(String url,String data) throws Exception { 26 | /** 27 | * PKCS12证书 是从微信商户平台-》账户设置-》 API安全 中下载的 28 | */ 29 | KeyStore keyStore = KeyStore.getInstance("PKCS12"); 30 | File certfile = ResourceUtils.getFile("classpath:cert"+ Constants.SF_FILE_SEPARATOR + ConfigUtil.CERT_PATH); 31 | FileInputStream instream = new FileInputStream(certfile); 32 | try { 33 | keyStore.load(instream, ConfigUtil.MCH_ID.toCharArray()); 34 | } finally { 35 | instream.close(); 36 | } 37 | SSLContext sslcontext = SSLContexts.custom() 38 | .loadKeyMaterial(keyStore, ConfigUtil.MCH_ID.toCharArray()) 39 | .build(); 40 | SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory( 41 | sslcontext, 42 | new String[] { "TLSv1" }, 43 | null, 44 | SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER); 45 | CloseableHttpClient httpclient = HttpClients.custom() 46 | .setSSLSocketFactory(sslsf) 47 | .build(); 48 | try { 49 | HttpPost httpost = new HttpPost(url); 50 | httpost.setEntity(new StringEntity(data, "UTF-8")); 51 | CloseableHttpResponse response = httpclient.execute(httpost); 52 | try { 53 | HttpEntity entity = response.getEntity(); 54 | String jsonStr = EntityUtils.toString(response.getEntity(), "UTF-8"); 55 | EntityUtils.consume(entity); 56 | return jsonStr; 57 | } finally { 58 | response.close(); 59 | } 60 | } finally { 61 | httpclient.close(); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/com/howie/pay/WXUtils/MobileUtil.java: -------------------------------------------------------------------------------- 1 | package com.howie.pay.wxUtils; 2 | 3 | import java.io.InputStream; 4 | import java.net.URL; 5 | import java.util.HashMap; 6 | import java.util.List; 7 | import java.util.Map; 8 | 9 | import javax.servlet.http.HttpServletRequest; 10 | 11 | import org.dom4j.Document; 12 | import org.dom4j.Element; 13 | import org.dom4j.io.SAXReader; 14 | 15 | import com.google.gson.Gson; 16 | import com.howie.pay.wxUtils.ConfigUtil; 17 | 18 | /** 19 | * 微信H5支付工具类 创建者 创建时间 2017年7月31日 20 | */ 21 | public class MobileUtil { 22 | /** 23 | * 获取用户openID 24 | * @author hongyang.jiang 25 | */ 26 | public static String getOpenId(String code) { 27 | if (code != null) { 28 | String url = "https://api.weixin.qq.com/sns/oauth2/access_token?" 29 | + "appid=" + ConfigUtil.APP_ID + "&secret=" 30 | + ConfigUtil.APP_SECRET + "&code=" + code 31 | + "&grant_type=authorization_code"; 32 | String returnData = getReturnData(url); 33 | Gson gson = new Gson(); 34 | OpenIdClass openIdClass = gson.fromJson(returnData, 35 | OpenIdClass.class); 36 | if (openIdClass.getOpenid() != null) { 37 | return openIdClass.getOpenid(); 38 | } 39 | } 40 | return "**************"; 41 | } 42 | 43 | public static String getReturnData(String urlString) { 44 | String res = ""; 45 | try { 46 | URL url = new URL(urlString); 47 | java.net.HttpURLConnection conn = (java.net.HttpURLConnection) url 48 | .openConnection(); 49 | conn.connect(); 50 | java.io.BufferedReader in = new java.io.BufferedReader( 51 | new java.io.InputStreamReader(conn.getInputStream(), 52 | "UTF-8")); 53 | String line; 54 | while ((line = in.readLine()) != null) { 55 | res += line; 56 | } 57 | in.close(); 58 | } catch (Exception e) { 59 | e.printStackTrace(); 60 | } 61 | return res; 62 | } 63 | 64 | /** 65 | * 回调request 参数解析为map格式 66 | * @author hongyang.jiang 67 | */ 68 | @SuppressWarnings("unchecked") 69 | public static Map parseXml(HttpServletRequest request) 70 | throws Exception { 71 | // 解析结果存储在HashMap 72 | Map map = new HashMap(); 73 | InputStream inputStream = request.getInputStream(); 74 | // 读取输入流 75 | SAXReader reader = new SAXReader(); 76 | Document document = reader.read(inputStream); 77 | // 得到xml根元素 78 | Element root = document.getRootElement(); 79 | // 得到根元素的所有子节点 80 | List elementList = root.elements(); 81 | // 遍历所有子节点 82 | for (Element e : elementList) 83 | map.put(e.getName(), e.getText()); 84 | // 释放资源 85 | inputStream.close(); 86 | inputStream = null; 87 | return map; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/main/resources/application-dev.properties: -------------------------------------------------------------------------------- 1 | #\u9879\u76eecontextPath 2 | server.context-path=/springboot_pay 3 | #\u670d\u52a1\u7aef\u53e3 4 | server.port=8080 5 | #session\u6700\u5927\u8d85\u65f6\u65f6\u95f4(\u5206\u949f)\uff0c\u9ed8\u8ba4\u4e3a30 6 | server.session-timeout=60 7 | #\u8be5\u670d\u52a1\u7ed1\u5b9aIP\u5730\u5740\uff0c\u542f\u52a8\u670d\u52a1\u5668\u65f6\u5982\u672c\u673a\u4e0d\u662f\u8be5IP\u5730\u5740\u5219\u629b\u51fa\u5f02\u5e38\u542f\u52a8\u5931\u8d25\uff0c\u53ea\u6709\u7279\u6b8a\u9700\u6c42\u7684\u60c5\u51b5\u4e0b\u624d\u914d\u7f6e 8 | # server.address=192.168.16.11 9 | 10 | #tomcat\u6700\u5927\u7ebf\u7a0b\u6570\uff0c\u9ed8\u8ba4\u4e3a200 11 | server.tomcat.max-threads=100 12 | #tomcat\u7684URI\u7f16\u7801 13 | server.tomcat.uri-encoding=UTF-8 14 | 15 | #HTTPS\u8bc1\u4e66 \u5982\u679c\u4e0d\u9700\u8981 https\u8bbf\u95ee \u6ce8\u91ca\u6389\u5373\u53ef \u7531\u4e8e\u67d0\u4e9b\u6d4f\u89c8\u5668\u8bbf\u95ee\u4e0d\u652f\u6301 16 | #server.ssl.key-store: classpath:keystore.p12 17 | #server.ssl.key-store-password: 123456 18 | #server.ssl.keyStoreType: PKCS12 19 | 20 | #spring boot\u4ece\u63a7\u5236\u53f0\u6253\u5370\u51fa\u6765\u7684\u65e5\u5fd7\u7ea7\u522b\u53ea\u6709ERROR, WARN \u8fd8\u6709INFO\uff0c\u5982\u679c\u4f60\u60f3\u8981\u6253\u5370debug\u7ea7\u522b\u7684\u65e5\u5fd7 21 | #debug=true 22 | logging.level.root=INFO 23 | 24 | spring.thymeleaf.mode=LEGACYHTML5 25 | 26 | #dev tools 27 | spring.devtools.livereload.enabled=true 28 | spring.thymeleaf.cache=false 29 | spring.thymeleaf.cache-period=0 30 | spring.thymeleaf.template.cache=false 31 | # \u9759\u6001\u6587\u4ef6\u8bf7\u6c42\u5339\u914d\u65b9\u5f0f 32 | spring.mvc.static-path-pattern=/** 33 | # \u4fee\u6539\u9ed8\u8ba4\u7684\u9759\u6001\u5bfb\u5740\u8d44\u6e90\u76ee\u5f55 \u591a\u4e2a\u4f7f\u7528\u9017\u53f7\u5206\u9694 34 | spring.resources.static-locations = classpath:/META-INF/resources/,classpath:/resources/,classpath:/static/,classpath:/public/,,classpath:/templates/ 35 | 36 | #============================# 37 | #===== zookeeper sttings ====# 38 | #============================# 39 | #zookeeper\u96c6\u7fa4\u914d\u7f6e 40 | #\u516c\u53f8\u914d\u7f6e\uff1a 41 | dubbo.registry.address=172.28.20.66:2181 42 | #\u5bb6\u91cc\u914d\u7f6e\uff1a 43 | #dubbo.registry.address=192.168.51.216:2181 44 | 45 | #\u9879\u76ee\u5730\u5740 46 | server.context.url = http://localhost:8080/springboot_pay/ 47 | #\u652f\u4ed8\u5b9d\u540e\u53f0\u56de\u8c03 48 | alipay.notify.url=https://blog.52itstyle.com/alipay/pay 49 | #\u5fae\u4fe1\u540e\u53f0\u56de\u8c03 50 | wexinpay.notify.url=https://blog.52itstyle.com/weixin/pay 51 | #\u94f6\u8054\u540e\u53f0\u56de\u8c03 52 | unionpay.notify.url=https://blog.52itstyle.com/union/pay 53 | 54 | #\u6d4b\u8bd5\u53d8\u91cf 55 | test_variable=17 56 | 57 | #druid 58 | druid.type=com.alibaba.druid.pool.DruidDataSource 59 | #\u6570\u636e\u5e93\u4e3b\u5e93 60 | druid.master.datasource.url=jdbc:mysql://localhost:3306/exercise?userUnicode=true&characterEncoding=UTF8&useSSL=false 61 | druid.master.datasource.username = root 62 | druid.master.datasource.password = 123456 63 | druid.master.datasource.driver-class-name = com.mysql.jdbc.Driver 64 | #\u6570\u636e\u5e93\u4ece\u5e93 65 | druid.cluster.datasource.url=jdbc:mysql://localhost:3306/exercise_cluster?userUnicode=true&characterEncoding=UTF8&useSSL=false 66 | druid.cluster.datasource.username = root 67 | druid.cluster.datasource.password = 123456 68 | druid.cluster.datasource.driver-class-name = com.mysql.jdbc.Driver 69 | 70 | 71 | -------------------------------------------------------------------------------- /src/main/java/com/howie/pay/UnionpayUtils/BaseHttpSSLSocketFactory.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Licensed Property to China UnionPay Co., Ltd. 4 | * 5 | * (C) Copyright of China UnionPay Co., Ltd. 2010 6 | * All Rights Reserved. 7 | * 8 | * 9 | * Modification History: 10 | * ============================================================================= 11 | * Author Date Description 12 | * ------------ ---------- --------------------------------------------------- 13 | * xshu 2014-05-28 SSLSocket 链接工具类(用于https) 14 | * ============================================================================= 15 | */ 16 | package com.howie.pay.unionpayUtils; 17 | 18 | import java.io.IOException; 19 | import java.net.InetAddress; 20 | import java.net.Socket; 21 | import java.net.UnknownHostException; 22 | import java.security.cert.X509Certificate; 23 | 24 | import javax.net.ssl.HostnameVerifier; 25 | import javax.net.ssl.SSLContext; 26 | import javax.net.ssl.SSLSession; 27 | import javax.net.ssl.SSLSocketFactory; 28 | import javax.net.ssl.TrustManager; 29 | import javax.net.ssl.X509TrustManager; 30 | 31 | import org.slf4j.Logger; 32 | import org.slf4j.LoggerFactory; 33 | 34 | public class BaseHttpSSLSocketFactory extends SSLSocketFactory { 35 | private static final Logger logger = LoggerFactory.getLogger(BaseHttpSSLSocketFactory.class); 36 | private SSLContext getSSLContext() { 37 | return createEasySSLContext(); 38 | } 39 | 40 | @Override 41 | public Socket createSocket(InetAddress arg0, int arg1, InetAddress arg2, 42 | int arg3) throws IOException { 43 | return getSSLContext().getSocketFactory().createSocket(arg0, arg1, 44 | arg2, arg3); 45 | } 46 | 47 | @Override 48 | public Socket createSocket(String arg0, int arg1, InetAddress arg2, int arg3) 49 | throws IOException, UnknownHostException { 50 | return getSSLContext().getSocketFactory().createSocket(arg0, arg1, 51 | arg2, arg3); 52 | } 53 | 54 | @Override 55 | public Socket createSocket(InetAddress arg0, int arg1) throws IOException { 56 | return getSSLContext().getSocketFactory().createSocket(arg0, arg1); 57 | } 58 | 59 | @Override 60 | public Socket createSocket(String arg0, int arg1) throws IOException, 61 | UnknownHostException { 62 | return getSSLContext().getSocketFactory().createSocket(arg0, arg1); 63 | } 64 | 65 | @Override 66 | public String[] getSupportedCipherSuites() { 67 | return null; 68 | } 69 | 70 | @Override 71 | public String[] getDefaultCipherSuites() { 72 | return null; 73 | } 74 | 75 | @Override 76 | public Socket createSocket(Socket arg0, String arg1, int arg2, boolean arg3) 77 | throws IOException { 78 | return getSSLContext().getSocketFactory().createSocket(arg0, arg1, 79 | arg2, arg3); 80 | } 81 | 82 | private SSLContext createEasySSLContext() { 83 | try { 84 | SSLContext context = SSLContext.getInstance("SSL"); 85 | context.init(null, 86 | new TrustManager[] { MyX509TrustManager.manger }, null); 87 | return context; 88 | } catch (Exception e) { 89 | logger.error(e.getMessage(), e); 90 | return null; 91 | } 92 | } 93 | 94 | public static class MyX509TrustManager implements X509TrustManager { 95 | 96 | static MyX509TrustManager manger = new MyX509TrustManager(); 97 | 98 | public MyX509TrustManager() { 99 | } 100 | 101 | public X509Certificate[] getAcceptedIssuers() { 102 | return null; 103 | } 104 | 105 | public void checkClientTrusted(X509Certificate[] chain, String authType) { 106 | } 107 | 108 | public void checkServerTrusted(X509Certificate[] chain, String authType) { 109 | } 110 | } 111 | 112 | /** 113 | * 解决由于服务器证书问题导致HTTPS无法访问的情况 PS:HTTPS hostname wrong: should be 114 | */ 115 | public static class TrustAnyHostnameVerifier implements HostnameVerifier { 116 | public boolean verify(String hostname, SSLSession session) { 117 | // 直接返回true 118 | return true; 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/main/resources/logback-spring.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | spring-boot-pay 5 | 6 | 7 | 8 | log/spring-boot-pay-info.log 9 | 10 | 11 | 12 | log/spring-boot-pay-info-%d{yyyy-MM-dd}.%i.log 13 | 14 | 128MB 15 | 16 | 17 | 30 18 | 19 | true 20 | 21 | %d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %logger Line:%-3L - %msg%n 22 | utf-8 23 | 24 | 25 | info 26 | ACCEPT 27 | DENY 28 | 29 | 30 | 31 | 32 | log/spring-boot-pay-error.log 33 | 34 | 35 | 36 | log/spring-boot-pay-error-%d{yyyy-MM-dd}.%i.log 37 | 38 | 2MB 39 | 40 | 41 | 180 42 | 43 | true 44 | 45 | 46 | %d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %logger Line:%-3L - %msg%n 47 | utf-8 48 | 49 | 50 | 51 | 52 | ERROR 53 | 54 | ACCEPT 55 | 56 | DENY 57 | 58 | 59 | 60 | 61 | 62 | 63 | %d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %logger Line:%-3L - %msg%n 64 | utf-8 65 | 66 | 67 | 68 | INFO 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /src/main/java/com/howie/pay/AliUtils/CommonUtil.java: -------------------------------------------------------------------------------- 1 | package com.howie.pay.aliUtils; 2 | 3 | import java.math.BigDecimal; 4 | 5 | import org.apache.commons.lang3.StringUtils; 6 | 7 | 8 | public class CommonUtil { 9 | /** 10 | * 除法 11 | */ 12 | public static BigDecimal divide(String arg1, String arg2) { 13 | if (StringUtils.isEmpty(arg1)) { 14 | arg1 = "0.0"; 15 | } 16 | if (StringUtils.isEmpty(arg2)) { 17 | arg2 = "0.0"; 18 | } 19 | BigDecimal big3 = new BigDecimal("0.00"); 20 | if (Double.parseDouble(arg2) != 0) { 21 | BigDecimal big1 = new BigDecimal(arg1); 22 | BigDecimal big2 = new BigDecimal(arg2); 23 | big3 = big1.divide(big2, 2, BigDecimal.ROUND_HALF_EVEN); 24 | } 25 | return big3; 26 | } 27 | 28 | /** 29 | * 乘法 30 | */ 31 | public static BigDecimal mul(String arg1, String arg2) { 32 | if (StringUtils.isEmpty(arg1)) { 33 | arg1 = "0.0"; 34 | } 35 | if (StringUtils.isEmpty(arg2)) { 36 | arg2 = "0.0"; 37 | } 38 | BigDecimal big1 = new BigDecimal(arg1); 39 | BigDecimal big2 = new BigDecimal(arg2); 40 | BigDecimal big3 = big1.multiply(big2); 41 | return big3; 42 | } 43 | 44 | /** 45 | * 减法 46 | */ 47 | public static BigDecimal sub(String arg1, String arg2) { 48 | if (StringUtils.isEmpty(arg1)) { 49 | arg1 = "0.0"; 50 | } 51 | if (StringUtils.isEmpty(arg2)) { 52 | arg2 = "0.0"; 53 | } 54 | BigDecimal big1 = new BigDecimal(arg1); 55 | BigDecimal big2 = new BigDecimal(arg2); 56 | BigDecimal big3 = big1.subtract(big2); 57 | return big3; 58 | } 59 | 60 | /** 61 | * 加法 62 | */ 63 | public static BigDecimal add(String arg1, String arg2) { 64 | if (StringUtils.isEmpty(arg1)) { 65 | arg1 = "0.0"; 66 | } 67 | if (StringUtils.isEmpty(arg2)) { 68 | arg2 = "0.0"; 69 | } 70 | BigDecimal big1 = new BigDecimal(arg1); 71 | BigDecimal big2 = new BigDecimal(arg2); 72 | BigDecimal big3 = big1.add(big2); 73 | return big3; 74 | } 75 | 76 | /** 77 | * 加法 78 | */ 79 | public static String add2(String arg1, String arg2) { 80 | if (StringUtils.isEmpty(arg1)) { 81 | arg1 = "0.0"; 82 | } 83 | if (StringUtils.isEmpty(arg2)) { 84 | arg2 = "0.0"; 85 | } 86 | BigDecimal big1 = new BigDecimal(arg1); 87 | BigDecimal big2 = new BigDecimal(arg2); 88 | BigDecimal big3 = big1.add(big2); 89 | return big3.toString(); 90 | } 91 | 92 | /** 93 | * 四舍五入保留N位小数 先四舍五入在使用double值自动去零 94 | */ 95 | public static String setScare(BigDecimal arg, int scare) { 96 | BigDecimal bl = arg.setScale(scare, BigDecimal.ROUND_HALF_UP); 97 | return String.valueOf(bl.doubleValue()); 98 | } 99 | 100 | public static double setDifScare(double arg) { 101 | BigDecimal bd = new BigDecimal(arg); 102 | BigDecimal bl = bd.setScale(2, BigDecimal.ROUND_HALF_UP); 103 | return Double.parseDouble(bl.toString()); 104 | } 105 | 106 | /** 107 | * 四舍五入保留两位小数 先四舍五入在使用double值自动去零 108 | */ 109 | public static String setDifScare(String arg) { 110 | BigDecimal bd = new BigDecimal(arg); 111 | BigDecimal bl = bd.setScale(2, BigDecimal.ROUND_HALF_UP); 112 | return bl.toString(); 113 | } 114 | 115 | /** 116 | * 四舍五入保留N位小数 先四舍五入在使用double值自动去零(传参String类型) 117 | */ 118 | public static String setDifScare(String arg, int i) { 119 | BigDecimal bd = new BigDecimal(arg); 120 | BigDecimal bl = bd.setScale(i, BigDecimal.ROUND_HALF_UP); 121 | return bl.toString(); 122 | } 123 | 124 | /** 125 | * 转化成百分数 先四舍五入在使用double值自动去零 126 | */ 127 | public static String setFenScare(BigDecimal arg) { 128 | BigDecimal bl = arg.setScale(3, BigDecimal.ROUND_HALF_UP); 129 | String scare = String.valueOf(mul(bl.toString(), "100").doubleValue()); 130 | String fenScare = scare + "%"; 131 | return fenScare; 132 | } 133 | 134 | /** 135 | * 使用java正则表达式去掉多余的.与0 136 | */ 137 | public static String subZeroAndDot(String s) { 138 | if (s.indexOf(".") > 0) { 139 | s = s.replaceAll("0+?$", "");// 去掉多余的0 140 | s = s.replaceAll("[.]$", "");// 如最后一位是.则去掉 141 | } 142 | return s; 143 | } 144 | } -------------------------------------------------------------------------------- /src/main/java/com/howie/pay/WXUtils/PayCommonUtil.java: -------------------------------------------------------------------------------- 1 | package com.howie.pay.wxUtils; 2 | 3 | import java.text.SimpleDateFormat; 4 | import java.util.Date; 5 | import java.util.Iterator; 6 | import java.util.Map; 7 | import java.util.Set; 8 | import java.util.SortedMap; 9 | 10 | public class PayCommonUtil { 11 | /** 12 | * 是否签名正确,规则是:按参数名称a-z排序,遇到空值的参数不参加签名。 13 | * @author hongyang.jiang 14 | */ 15 | @SuppressWarnings({ "rawtypes"}) 16 | public static boolean isTenpaySign(String characterEncoding, SortedMap packageParams, String API_KEY) { 17 | StringBuffer sb = new StringBuffer(); 18 | Set es = packageParams.entrySet(); 19 | Iterator it = es.iterator(); 20 | while(it.hasNext()) { 21 | Map.Entry entry = (Map.Entry)it.next(); 22 | String k = (String)entry.getKey(); 23 | String v = (String)entry.getValue(); 24 | if(!"sign".equals(k) && null != v && !"".equals(v)) { 25 | sb.append(k + "=" + v + "&"); 26 | } 27 | } 28 | sb.append("key=" + API_KEY); 29 | //算出摘要 30 | String mysign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toLowerCase(); 31 | String tenpaySign = ((String)packageParams.get("sign")).toLowerCase(); 32 | return tenpaySign.equals(mysign); 33 | } 34 | /** 35 | * sign签名 36 | * @author hongyang.jiang 37 | */ 38 | @SuppressWarnings({ "rawtypes"}) 39 | public static String createSign(String characterEncoding, SortedMap packageParams, String API_KEY) { 40 | StringBuffer sb = new StringBuffer(); 41 | Set es = packageParams.entrySet(); 42 | Iterator it = es.iterator(); 43 | while (it.hasNext()) { 44 | Map.Entry entry = (Map.Entry) it.next(); 45 | String k = (String) entry.getKey(); 46 | String v = (String) entry.getValue(); 47 | if (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) { 48 | sb.append(k + "=" + v + "&"); 49 | } 50 | } 51 | sb.append("key=" + API_KEY); 52 | String sign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase(); 53 | return sign; 54 | } 55 | 56 | /** 57 | * 将请求参数转换为xml格式的string 58 | * @author hongyang.jiang 59 | */ 60 | @SuppressWarnings({ "rawtypes"}) 61 | public static String getRequestXml(SortedMap parameters) { 62 | StringBuffer sb = new StringBuffer(); 63 | sb.append(""); 64 | Set es = parameters.entrySet(); 65 | Iterator it = es.iterator(); 66 | while (it.hasNext()) { 67 | Map.Entry entry = (Map.Entry) it.next(); 68 | String k = (String) entry.getKey(); 69 | String v = (String) entry.getValue(); 70 | if ("attach".equalsIgnoreCase(k) || "body".equalsIgnoreCase(k) || "sign".equalsIgnoreCase(k)) { 71 | sb.append("<" + k + ">" + ""); 72 | } else { 73 | sb.append("<" + k + ">" + v + ""); 74 | } 75 | } 76 | sb.append(""); 77 | return sb.toString(); 78 | } 79 | 80 | /** 81 | * 取出一个指定长度大小的随机正整数. 82 | * @author hongyang.jiang 83 | */ 84 | public static int buildRandom(int length) { 85 | int num = 1; 86 | double random = Math.random(); 87 | if (random < 0.1) { 88 | random = random + 0.1; 89 | } 90 | for (int i = 0; i < length; i++) { 91 | num = num * 10; 92 | } 93 | return (int) ((random * num)); 94 | } 95 | 96 | /** 97 | * 获取当前时间 yyyyMMddHHmmss 98 | * @author hongyang.jiang 99 | */ 100 | public static String getCurrTime() { 101 | Date now = new Date(); 102 | SimpleDateFormat outFormat = new SimpleDateFormat("yyyyMMddHHmmss"); 103 | String s = outFormat.format(now); 104 | return s; 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/main/java/com/howie/pay/Controller/UnionPayController.java: -------------------------------------------------------------------------------- 1 | package com.howie.pay.controller; 2 | 3 | import io.swagger.annotations.Api; 4 | import io.swagger.annotations.ApiOperation; 5 | 6 | import java.util.Enumeration; 7 | import java.util.HashMap; 8 | import java.util.Iterator; 9 | import java.util.Map; 10 | import java.util.Map.Entry; 11 | 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.beans.factory.annotation.Autowired; 18 | import org.springframework.stereotype.Controller; 19 | import org.springframework.ui.ModelMap; 20 | import org.springframework.web.bind.annotation.RequestMapping; 21 | import org.springframework.web.bind.annotation.RequestMethod; 22 | 23 | import com.howie.pay.constants.PayWay; 24 | import com.howie.pay.dto.InfoDTO; 25 | import com.howie.pay.service.IUnionPayService; 26 | import com.howie.pay.unionpayUtils.AcpService; 27 | import com.howie.pay.unionpayUtils.SDKConstants; 28 | /** 29 | * 银联支付 30 | */ 31 | @Api(tags ="银联支付") 32 | @Controller 33 | @RequestMapping(value = "unionpay") 34 | public class UnionPayController { 35 | private static final Logger logger = LoggerFactory.getLogger(UnionPayController.class); 36 | 37 | @Autowired 38 | private IUnionPayService unionPayService; 39 | 40 | 41 | @ApiOperation(value="银联支付主页") 42 | @RequestMapping(value="index",method=RequestMethod.GET) 43 | public String index() { 44 | return "unionpay/index"; 45 | } 46 | @ApiOperation(value="电脑支付") 47 | @RequestMapping(value="pcPay",method=RequestMethod.POST) 48 | public String pcPay(InfoDTO product,ModelMap map) { 49 | logger.info("电脑支付"); 50 | product.setPayWay(PayWay.PC.getCode()); 51 | String form = unionPayService.unionPay(product); 52 | map.addAttribute("form", form); 53 | return "unionpay/pay"; 54 | } 55 | @ApiOperation(value="手机H5支付") 56 | @RequestMapping(value="mobilePay",method=RequestMethod.POST) 57 | public String mobilePay(InfoDTO product,ModelMap map) { 58 | logger.info("手机H5支付"); 59 | product.setPayWay(PayWay.MOBILE.getCode()); 60 | String form = unionPayService.unionPay(product); 61 | map.addAttribute("form", form); 62 | return "unionpay/pay"; 63 | } 64 | 65 | /** 66 | * 银联回调 67 | * @author hongyang.jiang 68 | */ 69 | @ApiOperation(value="银联回调通知") 70 | @RequestMapping(value="pay",method=RequestMethod.POST) 71 | public void union_notify(HttpServletRequest request, HttpServletResponse response) throws Exception { 72 | logger.info("银联接收后台通知开始"); 73 | String encoding = request.getParameter(SDKConstants.param_encoding); 74 | // 获取银联通知服务器发送的后台通知参数 75 | Map reqParam = getAllRequestParam(request); 76 | //打印参数 77 | logger.info(reqParam.toString()); 78 | Map valideData = null; 79 | if (null != reqParam && !reqParam.isEmpty()) { 80 | Iterator> it = reqParam.entrySet().iterator(); 81 | valideData = new HashMap(reqParam.size()); 82 | while (it.hasNext()) { 83 | Entry e = it.next(); 84 | String key = (String) e.getKey(); 85 | String value = (String) e.getValue(); 86 | value = new String(value.getBytes(encoding), encoding); 87 | valideData.put(key, value); 88 | } 89 | } 90 | //重要!验证签名前不要修改reqParam中的键值对的内容,否则会验签不过 91 | if (!AcpService.validate(valideData, encoding)) { 92 | logger.info("银联验证签名结果[失败]."); 93 | } else { 94 | logger.info("银联验证签名结果[成功]."); 95 | String outtradeno =valideData.get("orderId");//订单号 96 | String reqReserved = valideData.get("reqReserved");//辅助信息(字段穿透) 97 | logger.info("处理相关业务逻辑{},{}",outtradeno,reqReserved); 98 | response.getWriter().print("ok");//返回给银联服务器http 200 状态码 99 | } 100 | } 101 | /** 102 | * 获取请求参数中所有的信息 103 | * @author hongyang.jiang 104 | */ 105 | public static Map getAllRequestParam(final HttpServletRequest request) { 106 | Map res = new HashMap(); 107 | Enumeration temp = request.getParameterNames(); 108 | if (null != temp) { 109 | while (temp.hasMoreElements()) { 110 | String en = (String) temp.nextElement(); 111 | String value = request.getParameter(en); 112 | res.put(en, value); 113 | //在报文上送时,如果字段的值为空,则不上送<下面的处理为在获取所有参数数据时,判断若值为空,则删除这个字段> 114 | //System.out.println("ServletUtil类247行 temp数据的键=="+en+" 值==="+value); 115 | if (null == res.get(en) || "".equals(res.get(en))) { 116 | res.remove(en); 117 | } 118 | } 119 | } 120 | return res; 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/main/resources/acp_sdk.properties: -------------------------------------------------------------------------------- 1 | ######SDK\u914d\u7f6e\u6587\u4ef6 \u8bc1\u4e66\u7684\u5b58\u653e\u8def\u5f84\u6839\u636e\u5b9e\u9645\u60c5\u51b5\u914d\u7f6e\uff0c\u4ea4\u6613\u5730\u5740\u548c\u8bc1\u4e66\u6839\u636ePM\u73af\u5883\u3001\u751f\u4ea7\u73af\u5883\u914d\u5957\u914d\u7f6e##### 2 | 3 | ##########################\u5165\u7f51\u6d4b\u8bd5\u73af\u5883\u4ea4\u6613\u53d1\u9001\u5730\u5740\uff08\u7ebf\u4e0a\u6d4b\u8bd5\u9700\u8981\u4f7f\u7528\u751f\u4ea7\u73af\u5883\u4ea4\u6613\u8bf7\u6c42\u5730\u5740\uff09############################# 4 | 5 | ##\u4ea4\u6613\u8bf7\u6c42\u5730\u5740 6 | acpsdk.frontTransUrl=https://101.231.204.80:5000/gateway/api/frontTransReq.do 7 | acpsdk.backTransUrl=https://101.231.204.80:5000/gateway/api/backTransReq.do 8 | acpsdk.singleQueryUrl=https://101.231.204.80:5000/gateway/api/queryTrans.do 9 | acpsdk.batchTransUrl=https://101.231.204.80:5000/gateway/api/batchTrans.do 10 | acpsdk.fileTransUrl=https://101.231.204.80:9080/ 11 | acpsdk.appTransUrl=https://101.231.204.80:5000/gateway/api/appTransReq.do 12 | acpsdk.cardTransUrl=https://101.231.204.80:5000/gateway/api/cardTransReq.do 13 | 14 | #\u4ee5\u4e0b\u7f34\u8d39\u4ea7\u54c1\u4f7f\u7528\uff0c\u5176\u4f59\u4ea7\u54c1\u7528\u4e0d\u5230 15 | acpsdk.jfFrontTransUrl=https://101.231.204.80:5000/jiaofei/api/frontTransReq.do 16 | acpsdk.jfBackTransUrl=https://101.231.204.80:5000/jiaofei/api/backTransReq.do 17 | acpsdk.jfSingleQueryUrl=https://101.231.204.80:5000/jiaofei/api/queryTrans.do 18 | acpsdk.jfCardTransUrl=https://101.231.204.80:5000/jiaofei/api/cardTransReq.do 19 | acpsdk.jfAppTransUrl=https://101.231.204.80:5000/jiaofei/api/appTransReq.do 20 | 21 | #########################\u5165\u7f51\u6d4b\u8bd5\u73af\u5883\u7b7e\u540d\u8bc1\u4e66\u914d\u7f6e ################################ 22 | 23 | ##\u7b7e\u540d\u8bc1\u4e66\u8def\u5f84\uff0c\u5fc5\u987b\u4f7f\u7528\u7edd\u5bf9\u8def\u5f84\uff0c\u5982\u679c\u4e0d\u60f3\u4f7f\u7528\u7edd\u5bf9\u8def\u5f84\uff0c\u53ef\u4ee5\u81ea\u884c\u5b9e\u73b0\u76f8\u5bf9\u8def\u5f84\u83b7\u53d6\u8bc1\u4e66\u7684\u65b9\u6cd5\uff1b\u6d4b\u8bd5\u8bc1\u4e66\u6240\u6709\u5546\u6237\u5171\u7528\u5f00\u53d1\u5305\u4e2d\u7684\u6d4b\u8bd5\u7b7e\u540d\u8bc1\u4e66\uff0c\u751f\u4ea7\u73af\u5883\u8bf7\u4ececfca\u4e0b\u8f7d\u5f97\u5230 24 | #windows\u4e0b 25 | #acpsdk.signCert.path=D:/certs/acp_prod_sign_inst.pfx 26 | #linux\u4e0b\uff08\u6ce8\u610f\uff1a\u5728linux\u4e0b\u8bfb\u53d6\u8bc1\u4e66\u9700\u8981\u4fdd\u8bc1\u8bc1\u4e66\u6709\u88ab\u5e94\u7528\u8bfb\u7684\u6743\u9650\uff09 27 | acpsdk.signCert.path=D:/assets/acp_test_sign.pfx 28 | 29 | ##\u7b7e\u540d\u8bc1\u4e66\u5bc6\u7801\uff0c\u6d4b\u8bd5\u73af\u5883\u56fa\u5b9a000000\uff0c\u751f\u4ea7\u73af\u5883\u8bf7\u4fee\u6539\u4e3a\u4ececfca\u4e0b\u8f7d\u7684\u6b63\u5f0f\u8bc1\u4e66\u7684\u5bc6\u7801\uff0c\u6b63\u5f0f\u73af\u5883\u8bc1\u4e66\u5bc6\u7801\u4f4d\u6570\u9700\u5c0f\u4e8e\u7b49\u4e8e6\u4f4d\uff0c\u5426\u5219\u4e0a\u4f20\u5230\u5546\u6237\u670d\u52a1\u7f51\u7ad9\u4f1a\u5931\u8d25 30 | acpsdk.signCert.pwd=000000 31 | ##\u7b7e\u540d\u8bc1\u4e66\u7c7b\u578b\uff0c\u56fa\u5b9a\u4e0d\u9700\u8981\u4fee\u6539 32 | acpsdk.signCert.type=PKCS12 33 | 34 | ##########################\u9a8c\u7b7e\u8bc1\u4e66\u914d\u7f6e################################ 35 | ##\u9a8c\u8bc1\u7b7e\u540d\u8bc1\u4e66\u76ee\u5f55\uff0c\u53ea\u914d\u7f6e\u5230\u76ee\u5f55\u5373\u53ef\uff0c\u5fc5\u987b\u4f7f\u7528\u7edd\u5bf9\u8def\u5f84\uff0c\u5982\u679c\u4e0d\u60f3\u4f7f\u7528\u7edd\u5bf9\u8def\u5f84\uff0c\u53ef\u4ee5\u81ea\u884c\u5b9e\u73b0\u76f8\u5bf9\u8def\u5f84\u83b7\u53d6\u8bc1\u4e66\u7684\u65b9\u6cd5\uff1b\u6d4b\u8bd5\u8bc1\u4e66\u6240\u6709\u5546\u6237\u5171\u7528\u5f00\u53d1\u5305\u4e2d\u7684\u6d4b\u8bd5\u9a8c\u8bc1\u8bc1\u4e66\uff0c\u751f\u4ea7\u73af\u5883\u6240\u6709\u5546\u6237\u5171\u7528\u5f00\u53d1\u5305\u4e2d\u7684\u751f\u4ea7\u9a8c\u7b7e\u8bc1\u4e66 36 | #windows\u4e0b 37 | #acpsdk.validateCert.dir=D:/certs/ 38 | #linux\u4e0b\uff08\u6ce8\u610f\uff1a\u5728linux\u4e0b\u8bfb\u53d6\u8bc1\u4e66\u9700\u8981\u4fdd\u8bc1\u8bc1\u4e66\u6709\u88ab\u5e94\u7528\u8bfb\u7684\u6743\u9650\uff09 39 | acpsdk.validateCert.dir=D:/assets/ 40 | 41 | 42 | ##########################\u52a0\u5bc6\u8bc1\u4e66\u914d\u7f6e################################ 43 | ##\u654f\u611f\u4fe1\u606f\u52a0\u5bc6\u8bc1\u4e66\u8def\u5f84(\u5546\u6237\u53f7\u5f00\u901a\u4e86\u5546\u6237\u5bf9\u654f\u611f\u4fe1\u606f\u52a0\u5bc6\u7684\u6743\u9650\uff0c\u9700\u8981\u5bf9 \u5361\u53f7accNo\uff0cpin\u548cphoneNo\uff0ccvn2\uff0cexpired\u52a0\u5bc6\uff08\u5982\u679c\u8fd9\u4e9b\u4e0a\u9001\u7684\u8bdd\uff09\uff0c\u5bf9\u654f\u611f\u4fe1\u606f\u52a0\u5bc6\u4f7f\u7528) 44 | #acpsdk.encryptCert.path=d:/certs/acp_prod_enc.cer 45 | #\u6b63\u5f0f\u751f\u4ea7\u73af\u5883\u8def\u5f84 46 | acpsdk.encryptCert.path=D:/assets/acp_test_enc.cer 47 | 48 | ##\u662f\u5426\u542f\u7528\u591a\u8bc1\u4e66\u6a21\u5f0f(true:\u5355\u8bc1\u4e66|false:\u591a\u8bc1\u4e66---\u6ca1\u6709\u914d\u7f6e\u6b64\u9879\u65f6,\u9ed8\u8ba4\u4e3a\u5355\u8bc1\u4e66\u6a21\u5f0f) 49 | acpsdk.singleMode=true 50 | 51 | 52 | -------------------------------------------------------------------------------- /src/main/java/com/howie/pay/Controller/AliPayController.java: -------------------------------------------------------------------------------- 1 | package com.howie.pay.controller; 2 | 3 | import io.swagger.annotations.Api; 4 | import io.swagger.annotations.ApiOperation; 5 | 6 | import java.io.BufferedOutputStream; 7 | import java.util.Enumeration; 8 | import java.util.HashMap; 9 | import java.util.Map; 10 | 11 | import javax.servlet.http.HttpServletRequest; 12 | import javax.servlet.http.HttpServletResponse; 13 | 14 | import org.slf4j.Logger; 15 | import org.slf4j.LoggerFactory; 16 | import org.springframework.beans.factory.annotation.Autowired; 17 | import org.springframework.stereotype.Controller; 18 | import org.springframework.ui.ModelMap; 19 | import org.springframework.web.bind.annotation.RequestMapping; 20 | import org.springframework.web.bind.annotation.RequestMethod; 21 | 22 | import com.alipay.api.AlipayApiException; 23 | import com.alipay.api.internal.util.AlipaySignature; 24 | import com.alipay.demo.trade.config.Configs; 25 | import com.howie.pay.constants.Constants; 26 | import com.howie.pay.dto.InfoDTO; 27 | import com.howie.pay.service.IAliPayService; 28 | 29 | 30 | 31 | @Api(tags = "支付宝支付") 32 | @Controller 33 | @RequestMapping(value = "alipay") 34 | public class AliPayController { 35 | private static final Logger logger = LoggerFactory 36 | .getLogger(AliPayController.class); 37 | @Autowired 38 | private IAliPayService aliPayService; 39 | 40 | @ApiOperation(value = "支付主页") 41 | @RequestMapping(value = "index", method = RequestMethod.GET) 42 | public String index() { 43 | return "alipay/index.html"; 44 | } 45 | 46 | @ApiOperation(value = "电脑支付") 47 | @RequestMapping(value = "pcPay", method = RequestMethod.POST) 48 | public String pcPay(InfoDTO product, ModelMap map) { 49 | logger.info("电脑支付"); 50 | String form = aliPayService.aliPayPc(product); 51 | map.addAttribute("form", form); 52 | return "alipay/pay"; 53 | } 54 | 55 | @ApiOperation(value = "手机H5支付") 56 | @RequestMapping(value = "mobilePay", method = RequestMethod.POST) 57 | public String mobilePay(InfoDTO product, ModelMap map) { 58 | logger.info("手机H5支付"); 59 | String form = aliPayService.aliPayMobile(product); 60 | map.addAttribute("form", form); 61 | return "alipay/pay"; 62 | } 63 | 64 | @ApiOperation(value = "二维码支付") 65 | @RequestMapping(value = "qcPay", method = RequestMethod.POST) 66 | public String qcPay(InfoDTO product, ModelMap map) { 67 | logger.info("二维码支付"); 68 | String message = aliPayService.aliPay(product); 69 | if (Constants.SUCCESS.equals(message)) { 70 | String img = "../qrcode/" + product.getOutTradeNo() + ".png"; 71 | map.addAttribute("img", img); 72 | } else { 73 | // 失败 74 | } 75 | return "alipay/qcpay"; 76 | } 77 | 78 | @ApiOperation(value = "app支付服务端") 79 | @RequestMapping(value = "appPay", method = RequestMethod.POST) 80 | public String appPay(InfoDTO product, ModelMap map) { 81 | logger.info("app支付服务端"); 82 | String orderString = aliPayService.appPay(product); 83 | map.addAttribute("orderString", orderString); 84 | return "alipay/pay"; 85 | } 86 | 87 | /** 88 | * 支付宝支付回调(二维码、H5、网站) 89 | * @author hongyang.jiang 90 | */ 91 | @ApiOperation(value = "支付宝支付回调(二维码、H5、网站)") 92 | @RequestMapping(value = "pay", method = RequestMethod.POST) 93 | public void alipay_notify(HttpServletRequest request, 94 | HttpServletResponse response) throws Exception { 95 | String message = "success"; 96 | Map params = new HashMap(); 97 | // 取出所有参数是为了验证签名 98 | Enumeration parameterNames = request.getParameterNames(); 99 | while (parameterNames.hasMoreElements()) { 100 | String parameterName = parameterNames.nextElement(); 101 | params.put(parameterName, request.getParameter(parameterName)); 102 | } 103 | // 验证签名 校验签名 104 | boolean signVerified = false; 105 | try { 106 | signVerified = AlipaySignature.rsaCheckV1(params, 107 | Configs.getAlipayPublicKey(), "UTF-8"); 108 | } catch (AlipayApiException e) { 109 | e.printStackTrace(); 110 | message = "failed"; 111 | } 112 | if (signVerified) { 113 | logger.info("支付宝验证签名成功!"); 114 | // 若参数中的appid和填入的appid不相同,则为异常通知 115 | if (!Configs.getAppid().equals(params.get("app_id"))) { 116 | logger.info("与付款时的appid不同,此为异常通知,应忽略!"); 117 | message = "failed"; 118 | } else { 119 | String outtradeno = params.get("out_trade_no"); 120 | // 在数据库中查找订单号对应的订单,并将其金额与数据库中的金额对比,若对不上,也为异常通知 121 | String status = params.get("trade_status"); 122 | if (status.equals("WAIT_BUYER_PAY")) { // 如果状态是正在等待用户付款 123 | logger.info(outtradeno + "订单的状态正在等待用户付款"); 124 | } else if (status.equals("TRADE_CLOSED")) { // 如果状态是未付款交易超时关闭,或支付完成后全额退款 125 | logger.info(outtradeno + "订单的状态已经关闭"); 126 | } else if (status.equals("TRADE_SUCCESS") 127 | || status.equals("TRADE_FINISHED")) { // 如果状态是已经支付成功 128 | logger.info("(支付宝订单号:" + outtradeno + "付款成功)"); 129 | // 这里 根据实际业务场景 做相应的操作 130 | } else { 131 | 132 | } 133 | } 134 | } else { // 如果验证签名没有通过 135 | message = "failed"; 136 | logger.info("验证签名失败!"); 137 | } 138 | BufferedOutputStream out = new BufferedOutputStream( 139 | response.getOutputStream()); 140 | out.write(message.getBytes()); 141 | out.flush(); 142 | out.close(); 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /src/main/java/com/howie/pay/WXUtils/ConfigUtil.java: -------------------------------------------------------------------------------- 1 | package com.howie.pay.wxUtils; 2 | import java.util.Map; 3 | import java.util.SortedMap; 4 | import java.util.TreeMap; 5 | import org.apache.commons.configuration.Configuration; 6 | import org.apache.commons.configuration.ConfigurationException; 7 | import org.apache.commons.configuration.PropertiesConfiguration; 8 | 9 | /** 10 | * 相关配置参数 11 | * @author hongyang.jiang 12 | */ 13 | public class ConfigUtil { 14 | private static Configuration configs; 15 | public static String APP_ID;// 服务号的应用ID 16 | public static String APP_SECRET;// 服务号的应用密钥 17 | public static String TOKEN;// 服务号的配置token 18 | public static String MCH_ID;// 商户号 19 | public static String API_KEY;// API密钥 20 | public static String SIGN_TYPE;// 签名加密方式 21 | public static String CERT_PATH ;//微信支付证书 22 | 23 | public static synchronized void init(String filePath) { 24 | if (configs != null) { 25 | return; 26 | } 27 | try { 28 | configs = new PropertiesConfiguration(filePath); 29 | } catch (ConfigurationException e) { 30 | e.printStackTrace(); 31 | } 32 | 33 | if (configs == null) { 34 | throw new IllegalStateException("can`t find file by path:" 35 | + filePath); 36 | } 37 | APP_ID = configs.getString("APP_ID"); 38 | APP_SECRET = configs.getString("APP_SECRET"); 39 | TOKEN = configs.getString("TOKEN"); 40 | MCH_ID = configs.getString("MCH_ID"); 41 | API_KEY = configs.getString("API_KEY"); 42 | SIGN_TYPE = configs.getString("SIGN_TYPE"); 43 | CERT_PATH = configs.getString("CERT_PATH"); 44 | } 45 | 46 | /** 47 | * 微信基础接口地址 48 | */ 49 | // 获取token接口(GET) 50 | public final static String TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET"; 51 | // oauth2授权接口(GET) 52 | public final static String OAUTH2_URL = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code"; 53 | // 刷新access_token接口(GET) 54 | public final static String REFRESH_TOKEN_URL = "https://api.weixin.qq.com/sns/oauth2/refresh_token?appid=APPID&grant_type=refresh_token&refresh_token=REFRESH_TOKEN"; 55 | // 菜单创建接口(POST) 56 | public final static String MENU_CREATE_URL = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN"; 57 | // 菜单查询(GET) 58 | public final static String MENU_GET_URL = "https://api.weixin.qq.com/cgi-bin/menu/get?access_token=ACCESS_TOKEN"; 59 | // 菜单删除(GET) 60 | public final static String MENU_DELETE_URL = "https://api.weixin.qq.com/cgi-bin/menu/delete?access_token=ACCESS_TOKEN"; 61 | /** 62 | * 微信支付接口地址 63 | */ 64 | // 微信支付统一接口(POST) 65 | public final static String UNIFIED_ORDER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder"; 66 | // 微信退款接口(POST) 67 | public final static String REFUND_URL = "https://api.mch.weixin.qq.com/secapi/pay/refund"; 68 | // 订单查询接口(POST) 69 | public final static String CHECK_ORDER_URL = "https://api.mch.weixin.qq.com/pay/orderquery"; 70 | // 关闭订单接口(POST) 71 | public final static String CLOSE_ORDER_URL = "https://api.mch.weixin.qq.com/pay/closeorder"; 72 | // 退款查询接口(POST) 73 | public final static String CHECK_REFUND_URL = "https://api.mch.weixin.qq.com/pay/refundquery"; 74 | // 对账单接口(POST) 75 | public final static String DOWNLOAD_BILL_URL = "https://api.mch.weixin.qq.com/pay/downloadbill"; 76 | // 短链接转换接口(POST) 77 | public final static String SHORT_URL = "https://api.mch.weixin.qq.com/tools/shorturl"; 78 | // 接口调用上报接口(POST) 79 | public final static String REPORT_URL = "https://api.mch.weixin.qq.com/payitil/report"; 80 | 81 | /** 82 | * 基础参数 83 | *@author hongyang.jiang 84 | */ 85 | public static void commonParams(SortedMap packageParams) { 86 | // 账号信息 87 | String appid = ConfigUtil.APP_ID; // appid 88 | String mch_id = ConfigUtil.MCH_ID; // 商业号 89 | // 生成随机字符串 90 | String currTime = PayCommonUtil.getCurrTime(); 91 | String strTime = currTime.substring(8, currTime.length()); 92 | String strRandom = PayCommonUtil.buildRandom(4) + ""; 93 | String nonce_str = strTime + strRandom; 94 | packageParams.put("appid", appid);// 公众账号ID 95 | packageParams.put("mch_id", mch_id);// 商户号 96 | packageParams.put("nonce_str", nonce_str);// 随机字符串 97 | } 98 | 99 | /** 100 | * 该接口主要用于扫码原生支付模式一中的二维码链接转成短链接(weixin://wxpay/s/XXXXXX),减小二维码数据量,提升扫描速度和精确度 101 | * @author hongyang.jiang 102 | */ 103 | @SuppressWarnings("rawtypes") 104 | public static void shorturl(String urlCode) { 105 | try { 106 | String key = ConfigUtil.API_KEY; // key 107 | SortedMap packageParams = new TreeMap(); 108 | ConfigUtil.commonParams(packageParams); 109 | packageParams.put("long_url", urlCode);// URL链接 110 | String sign = PayCommonUtil.createSign("UTF-8", packageParams, key); 111 | packageParams.put("sign", sign);// 签名 112 | String requestXML = PayCommonUtil.getRequestXml(packageParams); 113 | String resXml = HttpUtil.postData(ConfigUtil.SHORT_URL, requestXML); 114 | Map map = XMLUtil.doXMLParse(resXml); 115 | String returnCode = (String) map.get("return_code"); 116 | if ("SUCCESS".equals(returnCode)) { 117 | String resultCode = (String) map.get("return_code"); 118 | if ("SUCCESS".equals(resultCode)) { 119 | urlCode = (String) map.get("short_url"); 120 | } 121 | } 122 | } catch (Exception e) { 123 | e.printStackTrace(); 124 | } 125 | } 126 | } -------------------------------------------------------------------------------- /src/main/java/com/howie/pay/UnionpayUtils/UnionConfig.java: -------------------------------------------------------------------------------- 1 | package com.howie.pay.unionpayUtils; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.File; 5 | import java.io.FileInputStream; 6 | import java.io.InputStreamReader; 7 | import java.text.SimpleDateFormat; 8 | import java.util.ArrayList; 9 | import java.util.Date; 10 | import java.util.HashMap; 11 | import java.util.Iterator; 12 | import java.util.LinkedHashMap; 13 | import java.util.List; 14 | import java.util.Map; 15 | import java.util.Map.Entry; 16 | import java.util.TreeMap; 17 | /** 18 | * 基础配置参数 19 | * @author hongyang.jiang 20 | * 21 | */ 22 | public class UnionConfig { 23 | 24 | public static String merId="777290058110048"; 25 | 26 | //默认配置的是UTF-8 27 | public static String encoding_UTF8 = "UTF-8"; 28 | 29 | public static String encoding_GBK = "GBK"; 30 | //全渠道固定值 31 | public static String version = "5.0.0"; 32 | 33 | // 商户发送交易时间 格式:YYYYMMDDhhmmss 34 | public static String getCurrentTime() { 35 | return new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()); 36 | } 37 | 38 | // AN8..40 商户订单号,不能含"-"或"_" 39 | public static String getOrderId() { 40 | return new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()); 41 | } 42 | 43 | /** 44 | * 组装请求,返回报文字符串用于显示 45 | * @param data 46 | * @return 47 | */ 48 | public static String genHtmlResult(Map data){ 49 | 50 | TreeMap tree = new TreeMap(); 51 | Iterator> it = data.entrySet().iterator(); 52 | while (it.hasNext()) { 53 | Entry en = it.next(); 54 | tree.put(en.getKey(), en.getValue()); 55 | } 56 | it = tree.entrySet().iterator(); 57 | StringBuffer sf = new StringBuffer(); 58 | while (it.hasNext()) { 59 | Entry en = it.next(); 60 | String key = en.getKey(); 61 | String value = en.getValue(); 62 | if("respCode".equals(key)){ 63 | sf.append(""+key + SDKConstants.EQUAL + value+"
"); 64 | }else 65 | sf.append(key + SDKConstants.EQUAL + value+"
"); 66 | } 67 | return sf.toString(); 68 | } 69 | /** 70 | * 功能:解析全渠道商户对账文件中的ZM文件并以List方式返回 71 | * 适用交易:对账文件下载后对文件的查看 72 | * @param filePath ZM文件全路径 73 | * @return 包含每一笔交易中 序列号 和 值 的map序列 74 | */ 75 | @SuppressWarnings("rawtypes") 76 | public static List parseZMFile(String filePath){ 77 | int lengthArray[] = {3,11,11,6,10,19,12,4,2,21,2,32,2,6,10,13,13,4,15,2,2,6,2,4,32,1,21,15,1,15,32,13,13,8,32,13,13,12,2,1,131}; 78 | return parseFile(filePath,lengthArray); 79 | } 80 | 81 | /** 82 | * 功能:解析全渠道商户对账文件中的ZME文件并以List方式返回 83 | * 适用交易:对账文件下载后对文件的查看 84 | * @param filePath ZME文件全路径 85 | * @return 包含每一笔交易中 序列号 和 值 的map序列 86 | */ 87 | @SuppressWarnings("rawtypes") 88 | public static List parseZMEFile(String filePath){ 89 | int lengthArray[] = {3,11,11,6,10,19,12,4,2,21,2,32,2,6,10,13,13,4,15,2,2,6,2,4,32,1,21,15,1,15,32,13,13,8,32,13,13,12,2,1,131}; 90 | return parseFile(filePath,lengthArray); 91 | } 92 | 93 | /** 94 | * 功能:解析全渠道商户 ZM,ZME对账文件 95 | * @param filePath 96 | * @param lengthArray 参照《全渠道平台接入接口规范 第3部分 文件接口》 全渠道商户对账文件 6.1 ZM文件和6.2 ZME 文件 格式的类型长度组成int型数组 97 | * @return 98 | */ 99 | @SuppressWarnings({ "rawtypes", "unchecked" }) 100 | private static List parseFile(String filePath,int lengthArray[]){ 101 | List ZmDataList = new ArrayList(); 102 | try { 103 | String encoding="UTF-8"; 104 | File file=new File(filePath); 105 | if(file.isFile() && file.exists()){ //判断文件是否存在 106 | InputStreamReader read = new InputStreamReader( 107 | new FileInputStream(file),encoding);//考虑到编码格式 108 | BufferedReader bufferedReader = new BufferedReader(read); 109 | String lineTxt = null; 110 | while((lineTxt = bufferedReader.readLine()) != null){ 111 | //解析的结果MAP,key为对账文件列序号,value为解析的值 112 | Map ZmDataMap = new LinkedHashMap(); 113 | //左侧游标 114 | int leftIndex = 0; 115 | //右侧游标 116 | int rightIndex = 0; 117 | for(int i=0;i ZmDataMapTmp = ZmDataList.get(i); 136 | 137 | for(Iterator it = ZmDataMapTmp.keySet().iterator();it.hasNext();){ 138 | Integer key = it.next(); 139 | String value = ZmDataMapTmp.get(key); 140 | System.out.println("序号:"+ key + " 值: '"+ value +"'"); 141 | } 142 | } 143 | return ZmDataList; 144 | } 145 | 146 | 147 | public static void main(String[] args) { 148 | System.out.println(AcpService.encryptTrack("12", "utf-8")); 149 | SDKConfig.getConfig().loadPropertiesFromSrc(); 150 | 151 | Map customerInfoMap = new HashMap(); 152 | //customerInfoMap.put("certifTp", "01"); 153 | //customerInfoMap.put("certifId", "341126197709218366"); 154 | //customerInfoMap.put("customerNm", "互联网"); 155 | customerInfoMap.put("phoneNo", "13552535506"); 156 | //customerInfoMap.put("smsCode", "123456"); 157 | //customerInfoMap.put("pin", "626262"); //密码加密 158 | //customerInfoMap.put("cvn2", "123"); //卡背面的cvn2三位数字 159 | //customerInfoMap.put("expired", "1711"); //有效期 年在前月在后 160 | 161 | //System.out.println(getCustomerInfoWithEncrypt(customerInfoMap,"6217001210048797565")); 162 | 163 | parseZMFile("C:\\Users\\wulh\\Desktop\\802310048993424_20150905\\INN15090588ZM_802310048993424"); 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /src/main/java/com/howie/pay/AliUtils/DateUtil.java: -------------------------------------------------------------------------------- 1 | package com.howie.pay.aliUtils; 2 | import java.sql.Timestamp; 3 | import java.text.DateFormat; 4 | import java.text.ParseException; 5 | import java.text.SimpleDateFormat; 6 | import java.util.Calendar; 7 | import java.util.Date; 8 | 9 | import org.apache.commons.lang.StringUtils; 10 | /** 11 | * 日期操作类 12 | * @author hongyang.jiang 13 | */ 14 | public class DateUtil { 15 | private final static SimpleDateFormat sdfYear = new SimpleDateFormat("yyyy"); 16 | 17 | private final static SimpleDateFormat sdfDay = new SimpleDateFormat("yyyy-MM-dd"); 18 | 19 | private final static SimpleDateFormat sdfDays = new SimpleDateFormat("yyyyMMdd"); 20 | 21 | private final static SimpleDateFormat sdfTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 22 | 23 | /** 24 | * 获取YYYY格式 25 | */ 26 | public static String getYear() { 27 | return sdfYear.format(new Date()); 28 | } 29 | 30 | Timestamp timestamp = new Timestamp(new Date().getTime()); 31 | 32 | /** 33 | * 获取YYYY-MM-DD格式 34 | */ 35 | public static String getDay() { 36 | return sdfDay.format(new Date()); 37 | } 38 | 39 | /** 40 | * 获取YYYYMMDD格式 41 | */ 42 | public static String getDays() { 43 | return sdfDays.format(new Date()); 44 | } 45 | 46 | /** 47 | * 获取YYYY-MM-DD hh:mm:ss格式 48 | */ 49 | public static String getTime() { 50 | return sdfTime.format(new Date()); 51 | } 52 | 53 | /** 54 | * @Description:(日期比较,如果s>=e 返回true 否则返回false) 55 | */ 56 | public static boolean compareDate(String s, String e) { 57 | if (fomatDate(s) == null || fomatDate(e) == null) { 58 | return false; 59 | } 60 | return fomatDate(s).getTime() >= fomatDate(e).getTime(); 61 | } 62 | 63 | /** 64 | * 格式化日期 65 | */ 66 | public static Date fomatDate(String date) { 67 | DateFormat fmt = new SimpleDateFormat("yyyy-MM-dd"); 68 | try { 69 | return fmt.parse(date); 70 | } catch (ParseException e) { 71 | e.printStackTrace(); 72 | return null; 73 | } 74 | } 75 | 76 | /** 77 | * 校验日期是否合法 78 | */ 79 | public static boolean isValidDate(String s) { 80 | DateFormat fmt = new SimpleDateFormat("yyyy-MM-dd"); 81 | try { 82 | fmt.parse(s); 83 | return true; 84 | } catch (Exception e) { 85 | // 如果throw java.text.ParseException或者NullPointerException,就说明格式不对 86 | return false; 87 | } 88 | } 89 | 90 | /** 91 | * 年数 92 | * @param startTime 93 | * @param endTime 94 | * @return 95 | */ 96 | public static int getDiffYear(String startTime, String endTime) { 97 | DateFormat fmt = new SimpleDateFormat("yyyy-MM-dd"); 98 | try { 99 | int years = (int) (((fmt.parse(endTime).getTime() - fmt.parse(startTime).getTime()) / (1000 * 60 * 60 * 24)) / 365); 100 | return years; 101 | } catch (Exception e) { 102 | // 如果throw java.text.ParseException或者NullPointerException,就说明格式不对 103 | return 0; 104 | } 105 | } 106 | 107 | /** 108 | * 功能描述:时间相减得到天数 109 | */ 110 | public static long getDaySub(String beginDateStr, String endDateStr) { 111 | long day = 0; 112 | java.text.SimpleDateFormat format = new java.text.SimpleDateFormat("yyyy-MM-dd"); 113 | java.util.Date beginDate = null; 114 | java.util.Date endDate = null; 115 | 116 | try { 117 | beginDate = format.parse(beginDateStr); 118 | endDate = format.parse(endDateStr); 119 | } catch (ParseException e) { 120 | e.printStackTrace(); 121 | } 122 | day = (endDate.getTime() - beginDate.getTime()) / (24 * 60 * 60 * 1000); 123 | // System.out.println("相隔的天数="+day); 124 | 125 | return day; 126 | } 127 | 128 | /** 129 | * 得到n天之后的日期 130 | */ 131 | public static String getAfterDayDate(String days) { 132 | int daysInt = Integer.parseInt(days); 133 | 134 | Calendar canlendar = Calendar.getInstance(); // java.util包 135 | canlendar.add(Calendar.DATE, daysInt); // 日期减 如果不够减会将月变动 136 | Date date = canlendar.getTime(); 137 | 138 | SimpleDateFormat sdfd = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 139 | String dateStr = sdfd.format(date); 140 | 141 | return dateStr; 142 | } 143 | /** 144 | * 得到n天之前的日期 145 | */ 146 | public static String getBeforeDayDate(String days) { 147 | int daysInt = Integer.parseInt(days); 148 | 149 | Calendar canlendar = Calendar.getInstance(); // java.util包 150 | canlendar.add(Calendar.DATE, daysInt); // 日期减 如果不够减会将月变动 151 | Date date = canlendar.getTime(); 152 | 153 | String dateStr = sdfDays.format(date); 154 | 155 | return dateStr; 156 | } 157 | 158 | /** 159 | * 得到n天之后是周几 160 | */ 161 | public static String getAfterDayWeek(String days) { 162 | int daysInt = Integer.parseInt(days); 163 | 164 | Calendar canlendar = Calendar.getInstance(); // java.util包 165 | canlendar.add(Calendar.DATE, daysInt); // 日期减 如果不够减会将月变动 166 | Date date = canlendar.getTime(); 167 | 168 | SimpleDateFormat sdf = new SimpleDateFormat("E"); 169 | String dateStr = sdf.format(date); 170 | 171 | return dateStr; 172 | } 173 | 174 | /** 175 | * 按照yyyy-MM-dd HH:mm:ss的格式,日期转字符串 176 | */ 177 | public static String date2Str(Date date) { 178 | return date2Str(date, "yyyy-MM-dd"); 179 | } 180 | 181 | /** 182 | * 按照yyyy-MM-dd HH:mm:ss的格式,字符串转日期 183 | */ 184 | public static Date str2Date(String date) { 185 | if (StringUtils.isNotBlank(date)) { 186 | SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 187 | try { 188 | return sdf.parse(date); 189 | } catch (ParseException e) { 190 | e.printStackTrace(); 191 | } 192 | return new Date(); 193 | } else { 194 | return null; 195 | } 196 | } 197 | 198 | /** 199 | * 按照参数format的格式,日期转字符串 200 | */ 201 | public static String date2Str(Date date, String format) { 202 | if (null == format) { 203 | format = "yyyy-MM-dd"; 204 | } 205 | if (date != null) { 206 | SimpleDateFormat sdf = new SimpleDateFormat(format); 207 | return sdf.format(date); 208 | } else { 209 | return ""; 210 | } 211 | } 212 | 213 | /** 214 | * 把时间根据时、分、秒转换为时间段 215 | */ 216 | public static String getTimes(String StrDate) { 217 | String resultTimes = ""; 218 | 219 | SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 220 | java.util.Date now; 221 | 222 | try { 223 | now = new Date(); 224 | java.util.Date date = df.parse(StrDate); 225 | long times = now.getTime() - date.getTime(); 226 | long day = times / (24 * 60 * 60 * 1000); 227 | long hour = (times / (60 * 60 * 1000) - day * 24); 228 | long min = ((times / (60 * 1000)) - day * 24 * 60 - hour * 60); 229 | long sec = (times / 1000 - day * 24 * 60 * 60 - hour * 60 * 60 - min * 60); 230 | 231 | StringBuffer sb = new StringBuffer(); 232 | // sb.append("发表于:"); 233 | if (hour > 0) { 234 | sb.append(hour + "小时前"); 235 | } else if (min > 0) { 236 | sb.append(min + "分钟前"); 237 | } else { 238 | sb.append(sec + "秒前"); 239 | } 240 | 241 | resultTimes = sb.toString(); 242 | } catch (ParseException e) { 243 | e.printStackTrace(); 244 | } 245 | 246 | return resultTimes; 247 | } 248 | 249 | /** 250 | * 251 | * @return 252 | */ 253 | public static String getTimestamp() { 254 | return String.valueOf(System.currentTimeMillis() / 1000); 255 | } 256 | } 257 | -------------------------------------------------------------------------------- /src/main/java/com/howie/pay/AliUtils/AddressUtils.java: -------------------------------------------------------------------------------- 1 | package com.howie.pay.aliUtils; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.DataOutputStream; 5 | import java.io.IOException; 6 | import java.io.InputStreamReader; 7 | import java.io.UnsupportedEncodingException; 8 | import java.net.HttpURLConnection; 9 | import java.net.URL; 10 | import javax.servlet.http.HttpServletRequest; 11 | import org.apache.commons.lang.StringUtils; 12 | 13 | /** 14 | * 根据IP地址获取详细的地域信息 15 | * @author hongyang.jiang 16 | */ 17 | public class AddressUtils { 18 | /** 19 | * 20 | * @param content 21 | * 请求的参数 格式为:name=xxx&pwd=xxx 22 | * @param encoding 23 | * 服务器端请求编码。如GBK,UTF-8等 24 | * @return 25 | * @throws UnsupportedEncodingException 26 | */ 27 | public static String getAddresses(String ip) throws UnsupportedEncodingException { 28 | String urlStr ="http://ip.taobao.com/service/getIpInfo.php"; 29 | String returnStr = getResult(urlStr, ip); 30 | if (returnStr != null) { 31 | // 处理返回的省市区信息 32 | String[] temp = returnStr.split(","); 33 | if (temp.length < 3) { 34 | return "0";// 无效IP,局域网测试 35 | } 36 | String region = (temp[5].split(":"))[1].replaceAll("\"", ""); 37 | region = decodeUnicode(region);// 省份 38 | 39 | String country = ""; 40 | String area = ""; 41 | // String region = ""; 42 | String city = ""; 43 | String county = ""; 44 | String isp = ""; 45 | for (int i = 0; i < temp.length; i++) { 46 | switch (i) { 47 | case 1: 48 | country = (temp[i].split(":"))[2].replaceAll("\"", ""); 49 | country = decodeUnicode(country);// 国家 50 | break; 51 | case 3: 52 | area = (temp[i].split(":"))[1].replaceAll("\"", ""); 53 | area = decodeUnicode(area);// 地区 54 | break; 55 | case 5: 56 | region = (temp[i].split(":"))[1].replaceAll("\"", ""); 57 | region = decodeUnicode(region);// 省份 58 | break; 59 | case 7: 60 | city = (temp[i].split(":"))[1].replaceAll("\"", ""); 61 | city = decodeUnicode(city);// 市区 62 | break; 63 | case 9: 64 | county = (temp[i].split(":"))[1].replaceAll("\"", ""); 65 | county = decodeUnicode(county);// 地区 66 | break; 67 | case 11: 68 | isp = (temp[i].split(":"))[1].replaceAll("\"", ""); 69 | isp = decodeUnicode(isp); // ISP公司 70 | break; 71 | } 72 | } 73 | String address = region+city; 74 | if(StringUtils.isBlank(address)){ 75 | address = "地球村"; 76 | } 77 | return address; 78 | } 79 | return null; 80 | } 81 | 82 | /** 83 | * @param urlStr 84 | * 请求的地址 85 | * @param content 86 | * 请求的参数 格式为:name=xxx&pwd=xxx 87 | * @param encoding 88 | * 服务器端请求编码。如GBK,UTF-8等 89 | * @return 90 | */ 91 | private static String getResult(String urlStr, String ip) { 92 | URL url = null; 93 | HttpURLConnection connection = null; 94 | try { 95 | url = new URL(urlStr); 96 | connection = (HttpURLConnection) url.openConnection();// 新建连接实例 97 | /** 98 | * 超时错误 由 2s改为5s 99 | */ 100 | connection.setConnectTimeout(5000);// 设置连接超时时间,单位毫秒 101 | connection.setReadTimeout(5000);// 设置读取数据超时时间,单位毫秒 102 | connection.setDoOutput(true);// 是否打开输出流 true|false 103 | connection.setDoInput(true);// 是否打开输入流true|false 104 | connection.setRequestMethod("POST");// 提交方法POST|GET 105 | connection.setUseCaches(false);// 是否缓存true|false 106 | connection.connect();// 打开连接端口 107 | DataOutputStream out = new DataOutputStream(connection.getOutputStream());// 打开输出流往对端服务器写数据 108 | out.writeBytes("ip="+ip);// 写数据,也就是提交你的表单 name=xxx&pwd=xxx 109 | out.flush();// 刷新 110 | out.close();// 关闭输出流 111 | BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream(), "utf-8"));// 往对端写完数据对端服务器返回数据 112 | // ,以BufferedReader流来读取 113 | StringBuffer buffer = new StringBuffer(); 114 | String line = ""; 115 | while ((line = reader.readLine()) != null) { 116 | buffer.append(line); 117 | } 118 | reader.close(); 119 | return buffer.toString(); 120 | } catch (IOException e) { 121 | e.printStackTrace(); 122 | } finally { 123 | if (connection != null) { 124 | connection.disconnect();// 关闭连接 125 | } 126 | } 127 | return null; 128 | } 129 | 130 | /** 131 | * unicode 转换成 中文 132 | * @param theString 133 | * @return 134 | */ 135 | public static String decodeUnicode(String theString) { 136 | char aChar; 137 | int len = theString.length(); 138 | StringBuffer outBuffer = new StringBuffer(len); 139 | for (int x = 0; x < len;) { 140 | aChar = theString.charAt(x++); 141 | if (aChar == '\\') { 142 | aChar = theString.charAt(x++); 143 | if (aChar == 'u') { 144 | int value = 0; 145 | for (int i = 0; i < 4; i++) { 146 | aChar = theString.charAt(x++); 147 | switch (aChar) { 148 | case '0': 149 | case '1': 150 | case '2': 151 | case '3': 152 | case '4': 153 | case '5': 154 | case '6': 155 | case '7': 156 | case '8': 157 | case '9': 158 | value = (value << 4) + aChar - '0'; 159 | break; 160 | case 'a': 161 | case 'b': 162 | case 'c': 163 | case 'd': 164 | case 'e': 165 | case 'f': 166 | value = (value << 4) + 10 + aChar - 'a'; 167 | break; 168 | case 'A': 169 | case 'B': 170 | case 'C': 171 | case 'D': 172 | case 'E': 173 | case 'F': 174 | value = (value << 4) + 10 + aChar - 'A'; 175 | break; 176 | default: 177 | throw new IllegalArgumentException("Malformed encoding."); 178 | } 179 | } 180 | outBuffer.append((char) value); 181 | } else { 182 | if (aChar == 't') { 183 | aChar = '\t'; 184 | } else if (aChar == 'r') { 185 | aChar = '\r'; 186 | } else if (aChar == 'n') { 187 | aChar = '\n'; 188 | } else if (aChar == 'f') { 189 | aChar = '\f'; 190 | } 191 | outBuffer.append(aChar); 192 | } 193 | } else { 194 | outBuffer.append(aChar); 195 | } 196 | } 197 | return outBuffer.toString(); 198 | } 199 | /** 200 | * 获取IP地址 201 | * @author hongyang.jiang 202 | */ 203 | public static String getIpAddr(HttpServletRequest request) 204 | { 205 | String ip = request.getHeader("X-Real-IP"); 206 | if(!StringUtils.isBlank(ip) && !"unknown".equalsIgnoreCase(ip)) 207 | return ip; 208 | ip = request.getHeader("X-Forwarded-For"); 209 | if(!StringUtils.isBlank(ip) && !"unknown".equalsIgnoreCase(ip)) 210 | { 211 | int index = ip.indexOf(','); 212 | if(index != -1) 213 | return ip.substring(0, index); 214 | else 215 | return ip; 216 | } 217 | if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) 218 | ip = request.getHeader("Proxy-Client-IP"); 219 | if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) 220 | ip = request.getHeader("WL-Proxy-Client-IP"); 221 | if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) 222 | ip = request.getHeader("HTTP_CLIENT_IP"); 223 | if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) 224 | ip = request.getHeader("HTTP_X_FORWARDED_FOR"); 225 | if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) 226 | ip = request.getRemoteAddr(); 227 | return ip; 228 | } 229 | } 230 | -------------------------------------------------------------------------------- /src/main/java/com/howie/pay/ServiceImpl/UnionPayServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.howie.pay.serviceImpl; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | import org.springframework.beans.factory.annotation.Value; 9 | 10 | import com.alibaba.dubbo.config.annotation.Service; 11 | import com.howie.pay.aliUtils.CommonUtil; 12 | import com.howie.pay.constants.Constants; 13 | import com.howie.pay.constants.PayWay; 14 | import com.howie.pay.dto.InfoDTO; 15 | import com.howie.pay.service.IUnionPayService; 16 | import com.howie.pay.unionpayUtils.AcpService; 17 | import com.howie.pay.unionpayUtils.SDKConfig; 18 | import com.howie.pay.unionpayUtils.UnionConfig; 19 | @Service 20 | public class UnionPayServiceImpl implements IUnionPayService{ 21 | private static final Logger logger = LoggerFactory.getLogger(UnionPayServiceImpl.class); 22 | 23 | @Value("${unionpay.notify.url}") 24 | private String notify_url; 25 | 26 | /** 27 | * 银联支付返回一个form表单 28 | * @author hongyang.jiang 29 | */ 30 | @Override 31 | public String unionPay(InfoDTO product) { 32 | Map requestData = new HashMap(); 33 | /***银联全渠道系统,产品参数,除了encoding自行选择外其他不需修改***/ 34 | requestData.put("version", UnionConfig.version); //版本号,全渠道默认值 35 | requestData.put("encoding", UnionConfig.encoding_UTF8); //字符集编码,可以使用UTF-8,GBK两种方式 36 | requestData.put("signMethod", "01"); //签名方法,只支持 01:RSA方式证书加密 37 | requestData.put("txnType", "01"); //交易类型 ,01:消费 38 | requestData.put("txnSubType", "01"); //交易子类型, 01:自助消费 39 | requestData.put("bizType", "000201"); //业务类型,B2C网关支付,手机wap支付 40 | //渠道类型,这个字段区分B2C网关支付和手机wap支付;07:PC,平板 08:手机 41 | if(product.getPayWay()==PayWay.MOBILE.getCode()){//手机支付 42 | requestData.put("channelType", "08"); 43 | }else{//PC支付 44 | requestData.put("channelType", "07"); 45 | } 46 | //前台回调地址(自定义) 47 | String frontUrl = "http://git.oschina.net/52itstyle"; 48 | requestData.put("frontUrl", frontUrl); 49 | /***商户接入参数 测试账号***/ 50 | requestData.put("merId", UnionConfig.merId); //商户号码,请改成自己申请的正式商户号或者open上注册得来的777测试商户号 51 | requestData.put("accessType", "0"); //接入类型,0:直连商户 52 | requestData.put("orderId", product.getOutTradeNo()); //商户订单号,8-40位数字字母,不能含“-”或“_”,可以自行定制规则 53 | requestData.put("txnTime", UnionConfig.getCurrentTime()); //订单发送时间,取系统时间,格式为YYYYMMDDhhmmss,必须取当前时间,否则会报txnTime无效 54 | requestData.put("currencyCode", "156"); //交易币种(境内商户一般是156 人民币) 55 | requestData.put("txnAmt", CommonUtil.subZeroAndDot(product.getTotalFee())); //交易金额,单位分,不要带小数点 56 | //这里组织穿透数据 业务以及交易类型(使用json数据报错) 57 | requestData.put("reqReserved","自定义参数"); //请求方保留域,如需使用请启用即可;透传字段(可以实现商户自定义参数的追踪)本交易的后台通知,对本交易的交易状态查询交易、对账文件中均会原样返回,商户可以按需上传,长度为1-1024个字节 58 | 59 | //前台通知地址 (需设置为外网能访问 http https均可),支付成功后的页面 点击“返回商户”按钮的时候将异步通知报文post到该地址 60 | //如果想要实现过几秒中自动跳转回商户页面权限,需联系银联业务申请开通自动返回商户权限 61 | //异步通知参数详见open.unionpay.com帮助中心 下载 产品接口规范 网关支付产品接口规范 消费交易 商户通知 62 | //requestData.put("frontUrl", UnionConfig.frontUrl); 63 | 64 | //后台通知地址(需设置为【外网】能访问 http https均可),支付成功后银联会自动将异步通知报文post到商户上送的该地址,失败的交易银联不会发送后台通知 65 | //后台通知参数详见open.unionpay.com帮助中心 下载 产品接口规范 网关支付产品接口规范 消费交易 商户通知 66 | //注意:1.需设置为外网能访问,否则收不到通知 2.http https均可 3.收单后台通知后需要10秒内返回http200或302状态码 67 | // 4.如果银联通知服务器发送通知后10秒内未收到返回状态码或者应答码非http200,那么银联会间隔一段时间再次发送。总共发送5次,每次的间隔时间为0,1,2,4分钟。 68 | // 5.后台通知地址如果上送了带有?的参数,例如:http://abc/web?a=b&c=d 在后台通知处理程序验证签名之前需要编写逻辑将这些字段去掉再验签,否则将会验签失败 69 | requestData.put("backUrl", notify_url); 70 | 71 | ////////////////////////////////////////////////// 72 | // 73 | // 报文中特殊用法请查看 PCwap网关跳转支付特殊用法.txt 74 | // 75 | ////////////////////////////////////////////////// 76 | 77 | /**请求参数设置完毕,以下对请求参数进行签名并生成html表单,将表单写入浏览器跳转打开银联页面**/ 78 | Map submitFromData = AcpService.sign(requestData,UnionConfig.encoding_UTF8); //报文中certId,signature的值是在signData方法中获取并自动赋值的,只要证书配置正确即可。 79 | 80 | String requestFrontUrl = SDKConfig.getConfig().getFrontRequestUrl(); //获取请求银联的前台地址:对应属性文件acp_sdk.properties文件中的acpsdk.frontTransUrl 81 | String form = AcpService.createAutoFormHtml(requestFrontUrl, submitFromData,UnionConfig.encoding_UTF8); //生成自动跳转的Html表单 82 | 83 | logger.info("打印请求HTML,此为请求报文,为联调排查问题的依据:{}",form); 84 | //将生成的html写到浏览器中完成自动跳转打开银联支付页面;这里调用signData之后,将html写到浏览器跳转到银联页面之前均不能对html中的表单项的名称和值进行修改,如果修改会导致验签不通过 85 | //resp.getWriter().write(html); 86 | return form; 87 | } 88 | 89 | @Override 90 | public String validate(Map valideData, String encoding) { 91 | String message = Constants.SUCCESS; 92 | if (!AcpService.validate(valideData, encoding)) { 93 | message = Constants.FAIL; 94 | } 95 | return message; 96 | } 97 | @Override 98 | public void fileTransfer() { 99 | Map data = new HashMap(); 100 | 101 | /*** 银联全渠道系统,产品参数,除了encoding自行选择外其他不需修改 ***/ 102 | 103 | // 版本号 全渠道默认值 104 | data.put("version", UnionConfig.version); 105 | 106 | // 字符集编码 可以使用UTF-8,GBK两种方式 107 | data.put("encoding", UnionConfig.encoding_UTF8); 108 | 109 | // 签名方法 110 | data.put("signMethod", "01"); 111 | 112 | // 交易类型 76-对账文件下载 113 | data.put("txnType", "76"); 114 | 115 | // 交易子类型 01-对账文件下载 116 | data.put("txnSubType", "01"); 117 | 118 | // 业务类型,固定 119 | data.put("bizType", "000000"); 120 | 121 | /*** 商户接入参数 ***/ 122 | 123 | // 接入类型,商户接入填0,不需修改 124 | 125 | data.put("accessType", "0"); 126 | 127 | // 商户代码,请替换正式商户号测试,如使用的是自助化平台注册的777开头的商户号,该商户号没有权限测文件下载接口的, 128 | 129 | // 请使用测试参数里写的文件下载的商户号和日期测。如需777商户号的真实交易的对账文件,请使用自助化平台下载文件。 130 | 131 | data.put("merId", UnionConfig.merId); 132 | 133 | // 清算日期,如果使用正式商户号测试则要修改成自己想要获取对账文件的日期, 134 | // 测试环境如果使用700000000000001商户号则固定填写0119 135 | 136 | //data.put("settleDate", settleDate); 137 | 138 | // 订单发送时间,取系统时间,格式为YYYYMMDDhhmmss,必须取当前时间,否则会报txnTime无效 139 | 140 | data.put("txnTime", UnionConfig.getCurrentTime()); 141 | 142 | // 文件类型,一般商户填写00即可 143 | 144 | data.put("fileType", "00"); 145 | 146 | /** 请求参数设置完毕,以下对请求参数进行签名并发送http post请求,接收同步应答报文-------------> **/ 147 | 148 | Map reqData = AcpService.sign(data, 149 | UnionConfig.encoding_UTF8); 150 | 151 | // 报文中certId,signature的值是在signData方法中获取并自动赋值的,只要证书配置正确即可。 152 | 153 | String url = SDKConfig.getConfig().getFileTransUrl(); 154 | 155 | // 获取请求银联的前台地址:对应属性文件acp_sdk.properties文件中的acpsdk.fileTransUrl 156 | 157 | Map rspData = AcpService.post(reqData, url, 158 | UnionConfig.encoding_UTF8); 159 | 160 | if (!rspData.isEmpty()) { 161 | 162 | if (AcpService.validate(rspData, UnionConfig.encoding_UTF8)) { 163 | 164 | logger.info("验证签名成功"); 165 | 166 | String respCode = rspData.get("respCode"); 167 | 168 | if ("00".equals(respCode)) { 169 | 170 | // 交易成功,解析返回报文中的fileContent并落地 171 | 172 | /* String zipFilePath = AcpService.deCodeFileContent(rspData, 173 | "d:\\", UnionConfig.encoding_UTF8); 174 | 175 | // 对落地的zip文件解压缩并解析 176 | 177 | String outPutDirectory = "d:\\"; 178 | 179 | List fileList = UnionConfig.unzip(zipFilePath, 180 | outPutDirectory); 181 | 182 | // 解析ZM,ZME文件 183 | 184 | for (String file : fileList) { 185 | 186 | if (file.indexOf("ZM_") != -1) { 187 | 188 | List ZmDataList = UnionConfig.parseZMFile(file); 189 | 190 | fileContentDispaly = UnionConfig.getFileContentTable( 191 | ZmDataList, file); 192 | 193 | } else if (file.indexOf("ZME_") != -1) { 194 | 195 | UnionConfig.parseZMEFile(file); 196 | 197 | } 198 | 199 | }*/ 200 | // TODO 201 | } else { 202 | // 其他应答码为失败请排查原因 203 | // TODO 204 | } 205 | 206 | } else { 207 | logger.info("验证签名失败"); 208 | // TODO 检查验证签名失败的原因 209 | } 210 | } else { 211 | // 未返回正确的http状态 212 | logger.info("未获取到返回报文或返回http状态码非200"); 213 | } 214 | } 215 | } 216 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | com.howie 5 | Union-Pay 6 | war 7 | 0.0.1-SNAPSHOT 8 | spring_boot_test Maven Webapp 9 | http://maven.apache.org 10 | 11 | 12 | org.springframework.boot 13 | spring-boot-starter-parent 14 | 1.5.1.RELEASE 15 | 16 | 17 | 18 | 19 | junit 20 | junit 21 | 4.12 22 | test 23 | 24 | 25 | org.springframework.boot 26 | spring-boot-starter-web 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter-test 31 | test 32 | 33 | 34 | org.mybatis.spring.boot 35 | mybatis-spring-boot-starter 36 | 1.1.1 37 | 38 | 39 | mysql 40 | mysql-connector-java 41 | 5.1.46 42 | 43 | 44 | org.springframework.boot 45 | spring-boot-starter-data-jpa 46 | 47 | 48 | org.springframework.boot 49 | spring-boot-starter-cache 50 | 51 | 52 | 53 | org.springframework.boot 54 | spring-boot-starter-redis 55 | 1.3.1.RELEASE 56 | 57 | 58 | 59 | org.apache.commons 60 | commons-dbcp2 61 | 2.5.0 62 | 63 | 64 | 65 | com.alibaba 66 | druid 67 | 1.0.18 68 | 69 | 70 | 71 | io.springfox 72 | springfox-swagger2 73 | 2.6.1 74 | 75 | 76 | io.springfox 77 | springfox-swagger-ui 78 | 2.6.1 79 | 80 | 81 | 82 | com.github.pagehelper 83 | pagehelper-spring-boot-starter 84 | 1.1.0 85 | 86 | 87 | mybatis-spring-boot-starter 88 | org.mybatis.spring.boot 89 | 90 | 91 | 92 | 93 | 94 | org.aspectj 95 | aspectjweaver 96 | 1.8.13 97 | 98 | 99 | 100 | org.springframework.boot 101 | spring-boot-starter-thymeleaf 102 | 103 | 104 | 105 | org.springframework 106 | springloaded 107 | 108 | 109 | 110 | com.acts 111 | alipay-trade-sdk 112 | 1.0.0 113 | 114 | 115 | com.acts 116 | alipay-sdk-java 117 | 1.0.0 118 | 119 | 120 | org.apache.commons 121 | commons-lang3 122 | 3.6 123 | 124 | 125 | commons-collections 126 | commons-collections 127 | 128 | 129 | commons-beanutils 130 | commons-beanutils 131 | 132 | 133 | 134 | net.sf.json-lib 135 | json-lib 136 | 2.4 137 | 138 | jdk15 139 | 140 | 141 | 142 | commons-configuration 143 | commons-configuration 144 | 1.10 145 | 146 | 147 | 148 | com.google.code.gson 149 | gson 150 | 151 | 152 | 153 | com.google.zxing 154 | core 155 | 3.2.1 156 | 157 | 158 | net.sourceforge.nekohtml 159 | nekohtml 160 | 161 | 162 | 163 | jdom 164 | jdom 165 | 1.1 166 | 167 | 168 | 169 | org.apache.httpcomponents 170 | httpclient 171 | 4.3.4 172 | 173 | 174 | 175 | dom4j 176 | dom4j 177 | 1.6.1 178 | 179 | 180 | 181 | org.bouncycastle 182 | bcprov-jdk16 183 | 1.46 184 | 185 | 186 | net.sf.ezmorph 187 | ezmorph 188 | 1.0.6 189 | 190 | 191 | 192 | com.alibaba 193 | dubbo 194 | 195 | 196 | 2.8.4 197 | 198 | 199 | spring 200 | org.springframework 201 | 202 | 203 | 204 | 205 | 206 | com.101tec 207 | zkclient 208 | 0.6 209 | 210 | 211 | org.slf4j 212 | slf4j-log4j12 213 | 214 | 215 | 216 | 217 | 218 | org.javassist 219 | javassist 220 | 221 | 222 | 223 | org.projectlombok 224 | lombok 225 | 226 | 227 | 228 | spring_boot_test 229 | 230 | 231 | 232 | org.springframework.boot 233 | spring-boot-maven-plugin 234 | 235 | 236 | com.howie.Application 237 | 238 | 239 | 240 | 241 | repackage 242 | 243 | 244 | 245 | 246 | 247 | 248 | org.springframework 249 | springloaded 250 | 1.2.7.RELEASE 251 | 252 | 253 | 254 | 255 | 256 | 257 | -------------------------------------------------------------------------------- /src/main/java/com/howie/pay/Controller/WeixinMobilePayController.java: -------------------------------------------------------------------------------- 1 | package com.howie.pay.controller; 2 | 3 | import io.swagger.annotations.Api; 4 | import io.swagger.annotations.ApiOperation; 5 | 6 | import java.io.BufferedOutputStream; 7 | import java.util.Map; 8 | import java.util.SortedMap; 9 | import java.util.TreeMap; 10 | 11 | import javax.servlet.http.HttpServletRequest; 12 | import javax.servlet.http.HttpServletResponse; 13 | 14 | import org.apache.commons.lang3.StringUtils; 15 | import org.slf4j.Logger; 16 | import org.slf4j.LoggerFactory; 17 | import org.springframework.beans.factory.annotation.Autowired; 18 | import org.springframework.beans.factory.annotation.Value; 19 | import org.springframework.stereotype.Controller; 20 | import org.springframework.ui.ModelMap; 21 | import org.springframework.web.bind.annotation.RequestMapping; 22 | import org.springframework.web.bind.annotation.RequestMethod; 23 | 24 | import com.howie.pay.aliUtils.AddressUtils; 25 | import com.howie.pay.aliUtils.DateUtil; 26 | import com.howie.pay.dto.InfoDTO; 27 | import com.howie.pay.service.IWeixinPayService; 28 | import com.howie.pay.wxUtils.ConfigUtil; 29 | import com.howie.pay.wxUtils.HttpUtil; 30 | import com.howie.pay.wxUtils.MobileUtil; 31 | import com.howie.pay.wxUtils.PayCommonUtil; 32 | import com.howie.pay.wxUtils.XMLUtil; 33 | 34 | /** 35 | * 微信H5支付 36 | */ 37 | @Api(tags = "微信H5支付") 38 | @Controller 39 | @RequestMapping(value = "weixinMobile") 40 | public class WeixinMobilePayController { 41 | private static final Logger logger = LoggerFactory 42 | .getLogger(WeixinMobilePayController.class); 43 | @Autowired 44 | private IWeixinPayService weixinPayService; 45 | @Value("${server.context.url}") 46 | private String server_url; 47 | 48 | @ApiOperation(value = "H5支付(需要公众号内支付)") 49 | @RequestMapping(value = "pay", method = RequestMethod.POST) 50 | public String pay(InfoDTO product, ModelMap map) { 51 | logger.info("H5支付(需要公众号内支付)"); 52 | String url = weixinPayService.weixinPayMobile(product); 53 | return "redirect:" + url; 54 | } 55 | 56 | @ApiOperation(value = "公众号H5支付主页") 57 | @RequestMapping(value = "payPage", method = RequestMethod.GET) 58 | public String pay(HttpServletRequest request, HttpServletResponse response) 59 | throws Exception { 60 | return "weixin/payPage"; 61 | } 62 | 63 | @ApiOperation(value = "纯H5支付(不建议在APP端使用)") 64 | @RequestMapping(value = "h5pay", method = RequestMethod.POST) 65 | public String h5pay(InfoDTO product, ModelMap map) { 66 | logger.info("纯H5支付(不建议在APP端使用)"); 67 | // mweb_url为拉起微信支付收银台的中间页面,可通过访问该url来拉起微信客户端,完成支付,mweb_url的有效期为5分钟。 68 | String mweb_url = weixinPayService.weixinPayH5(product); 69 | if (StringUtils.isNotBlank(mweb_url)) { 70 | return "redirect:" + mweb_url; 71 | } else { 72 | return "redirect:https://blog.52itstyle.com";// 自定义错误页面 73 | } 74 | } 75 | 76 | @ApiOperation(value = "小程序支付(需要HTTPS)") 77 | @RequestMapping(value = "smallRoutine", method = RequestMethod.POST) 78 | public String smallRoutine(InfoDTO product, ModelMap map) { 79 | logger.info("小程序支付(需要HTTPS)、不需要支付目录和授权域名"); 80 | String url = weixinPayService.weixinPayMobile(product); 81 | return "redirect:" + url; 82 | } 83 | 84 | /** 85 | * 预下单(对于已经产生的订单) 86 | * 87 | * @Author 88 | * @param request 89 | * @param response 90 | * @return 91 | * @throws Exception 92 | * String 93 | * @Date 2017年7月31日 更新日志 2017年7月31日 科帮网 首次创建 94 | * 95 | */ 96 | @SuppressWarnings("rawtypes") 97 | @ApiOperation(value = "预下单") 98 | @RequestMapping(value = "dopay", method = RequestMethod.POST) 99 | public String dopay(HttpServletRequest request, HttpServletResponse response) 100 | throws Exception { 101 | String orderNo = request.getParameter("outTradeNo"); 102 | String totalFee = request.getParameter("totalFee"); 103 | // 获取code 这个在微信支付调用时会自动加上这个参数 无须设置 104 | String code = request.getParameter("code"); 105 | // 获取用户openID(JSAPI支付必须传openid) 106 | String openId = MobileUtil.getOpenId(code); 107 | String notify_url = server_url + "/weixinMobile/WXPayBack";// 回调接口 108 | String trade_type = "JSAPI";// 交易类型H5支付 也可以是小程序支付参数 109 | SortedMap packageParams = new TreeMap(); 110 | ConfigUtil.commonParams(packageParams); 111 | packageParams.put("body", "报告");// 商品描述 112 | packageParams.put("out_trade_no", orderNo);// 商户订单号 113 | packageParams.put("total_fee", totalFee);// 总金额 114 | packageParams.put("spbill_create_ip", AddressUtils.getIpAddr(request));// 发起人IP地址 115 | packageParams.put("notify_url", notify_url);// 回调地址 116 | packageParams.put("trade_type", trade_type);// 交易类型 117 | packageParams.put("openid", openId);// 用户openID 118 | String sign = PayCommonUtil.createSign("UTF-8", packageParams, 119 | ConfigUtil.API_KEY); 120 | packageParams.put("sign", sign);// 签名 121 | String requestXML = PayCommonUtil.getRequestXml(packageParams); 122 | String resXml = HttpUtil.postData(ConfigUtil.UNIFIED_ORDER_URL, 123 | requestXML); 124 | Map map = XMLUtil.doXMLParse(resXml); 125 | String returnCode = (String) map.get("return_code"); 126 | String returnMsg = (String) map.get("return_msg"); 127 | StringBuffer url = new StringBuffer(); 128 | if ("SUCCESS".equals(returnCode)) { 129 | String resultCode = (String) map.get("result_code"); 130 | String errCodeDes = (String) map.get("err_code_des"); 131 | if ("SUCCESS".equals(resultCode)) { 132 | // 获取预支付交易会话标识 133 | String prepay_id = (String) map.get("prepay_id"); 134 | String prepay_id2 = "prepay_id=" + prepay_id; 135 | String packages = prepay_id2; 136 | SortedMap finalpackage = new TreeMap(); 137 | String timestamp = DateUtil.getTimestamp(); 138 | String nonceStr = packageParams.get("nonce_str").toString(); 139 | finalpackage.put("appId", ConfigUtil.APP_ID); 140 | finalpackage.put("timeStamp", timestamp); 141 | finalpackage.put("nonceStr", nonceStr); 142 | finalpackage.put("package", packages); 143 | finalpackage.put("signType", "MD5"); 144 | // 这里很重要 参数一定要正确 狗日的腾讯 参数到这里就成大写了 145 | // 可能报错信息(支付验证签名失败 get_brand_wcpay_request:fail) 146 | sign = PayCommonUtil.createSign("UTF-8", finalpackage, 147 | ConfigUtil.API_KEY); 148 | url.append("redirect:/weixinMobile/payPage?"); 149 | url.append("timeStamp=" + timestamp + "&nonceStr=" + nonceStr 150 | + "&package=" + packages); 151 | url.append("&signType=MD5" + "&paySign=" + sign + "&appid=" 152 | + ConfigUtil.APP_ID); 153 | url.append("&orderNo=" + orderNo + "&totalFee=" + totalFee); 154 | } else { 155 | logger.info("订单号:{}错误信息:{}", orderNo, errCodeDes); 156 | url.append("redirect:/weixinMobile/error?code=0&orderNo=" 157 | + orderNo);// 该订单已支付 158 | } 159 | } else { 160 | logger.info("订单号:{}错误信息:{}", orderNo, returnMsg); 161 | url.append("redirect:/weixinMobile/error?code=1&orderNo=" + orderNo);// 系统错误 162 | } 163 | return url.toString(); 164 | } 165 | 166 | /** 167 | * 手机支付完成回调 168 | * 169 | * @Author 170 | * @param request 171 | * @param response 172 | * @param platForm 173 | * void 174 | * @Date 2017年7月31日 更新日志 2017年7月31日 科帮网 首次创建 175 | * 176 | */ 177 | @ApiOperation(value = "手机支付完成回调") 178 | @RequestMapping(value = "WXPayBack", method = RequestMethod.POST) 179 | public void WXPayBack(HttpServletRequest request, 180 | HttpServletResponse response) { 181 | String resXml = ""; 182 | try { 183 | // 解析XML 184 | Map map = MobileUtil.parseXml(request); 185 | String return_code = map.get("return_code");// 状态 186 | String out_trade_no = map.get("out_trade_no");// 订单号 187 | if (return_code.equals("SUCCESS")) { 188 | if (out_trade_no != null) { 189 | // 处理订单逻辑 190 | logger.info("微信手机支付回调成功订单号:{}", out_trade_no); 191 | resXml = "" 192 | + "" 193 | + "" 194 | + " "; 195 | } 196 | } else { 197 | logger.info("微信手机支付回调失败订单号:{}", out_trade_no); 198 | resXml = "" 199 | + "" 200 | + "" 201 | + " "; 202 | } 203 | } catch (Exception e) { 204 | logger.error("手机支付回调通知失败", e); 205 | resXml = "" + "" 206 | + "" + " "; 207 | } 208 | try { 209 | // ------------------------------ 210 | // 处理业务完毕 211 | // ------------------------------ 212 | BufferedOutputStream out = new BufferedOutputStream( 213 | response.getOutputStream()); 214 | out.write(resXml.getBytes()); 215 | out.flush(); 216 | out.close(); 217 | } catch (Exception e) { 218 | e.printStackTrace(); 219 | } 220 | } 221 | } 222 | -------------------------------------------------------------------------------- /src/main/java/com/howie/pay/UnionpayUtils/SDKUtil.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Licensed Property to China UnionPay Co., Ltd. 4 | * 5 | * (C) Copyright of China UnionPay Co., Ltd. 2010 6 | * All Rights Reserved. 7 | * 8 | * 9 | * Modification History: 10 | * ============================================================================= 11 | * Author Date Description 12 | * ------------ ---------- --------------------------------------------------- 13 | * xshu 2014-05-28 MPI工具类 14 | * ============================================================================= 15 | */ 16 | package com.howie.pay.unionpayUtils; 17 | 18 | import java.io.UnsupportedEncodingException; 19 | import java.util.HashMap; 20 | import java.util.Iterator; 21 | import java.util.Map; 22 | import java.util.Map.Entry; 23 | import java.util.Set; 24 | import java.util.TreeMap; 25 | 26 | import org.apache.commons.lang.StringUtils; 27 | import org.slf4j.Logger; 28 | import org.slf4j.LoggerFactory; 29 | 30 | public class SDKUtil { 31 | private static final Logger logger = LoggerFactory.getLogger(SDKUtil.class); 32 | /** 33 | * 生成签名值(SHA1摘要算法) 34 | * 35 | * @param data 36 | * 待签名数据Map键值对形式 37 | * @param encoding 38 | * 编码 39 | * @return 签名是否成功 40 | */ 41 | public static boolean sign(Map data, String encoding) { 42 | if (isEmpty(encoding)) { 43 | encoding = "UTF-8"; 44 | } 45 | // 设置签名证书序列号 46 | data.put(SDKConstants.param_certId, CertUtil.getSignCertId()); 47 | // 将Map信息转换成key1=value1&key2=value2的形式 48 | String stringData = coverMap2String(data); 49 | logger.info("待签名请求报文串:[" + stringData + "]"); 50 | /** 51 | * 签名\base64编码 52 | */ 53 | byte[] byteSign = null; 54 | String stringSign = null; 55 | try { 56 | // 通过SHA1进行摘要并转16进制 57 | byte[] signDigest = SecureUtil.sha1X16(stringData, encoding); 58 | byteSign = SecureUtil.base64Encode(SecureUtil.signBySoft( 59 | CertUtil.getSignCertPrivateKey(), signDigest)); 60 | stringSign = new String(byteSign); 61 | // 设置签名域值 62 | data.put(SDKConstants.param_signature, stringSign); 63 | return true; 64 | } catch (Exception e) { 65 | logger.error("签名异常", e); 66 | return false; 67 | } 68 | } 69 | 70 | /** 71 | * 通过传入的证书绝对路径和证书密码读取签名证书进行签名并返回签名值
72 | * 73 | * @param data 74 | * 待签名数据Map键值对形式 75 | * @param encoding 76 | * 编码 77 | * @param certPath 78 | * 证书绝对路径 79 | * @param certPwd 80 | * 证书密码 81 | * @return 签名值 82 | */ 83 | public static boolean signByCertInfo(Map data, 84 | String certPath, String certPwd,String encoding) { 85 | if (isEmpty(encoding)) { 86 | encoding = "UTF-8"; 87 | } 88 | if (isEmpty(certPath) || isEmpty(certPwd)) { 89 | logger.info("Invalid Parameter:CertPath=[" + certPath 90 | + "],CertPwd=[" + certPwd + "]"); 91 | return false; 92 | } 93 | // 设置签名证书序列号 94 | data.put(SDKConstants.param_certId, 95 | CertUtil.getCertIdByKeyStoreMap(certPath, certPwd)); 96 | // 将Map信息转换成key1=value1&key2=value2的形式 97 | String stringData = coverMap2String(data); 98 | /** 99 | * 签名\base64编码 100 | */ 101 | byte[] byteSign = null; 102 | String stringSign = null; 103 | try { 104 | byte[] signDigest = SecureUtil.sha1X16(stringData, encoding); 105 | byteSign = SecureUtil.base64Encode(SecureUtil.signBySoft( 106 | CertUtil.getSignCertPrivateKeyByStoreMap(certPath, certPwd), 107 | signDigest)); 108 | stringSign = new String(byteSign); 109 | // 设置签名域值 110 | data.put(SDKConstants.param_signature, stringSign); 111 | return true; 112 | } catch (Exception e) { 113 | logger.error("签名异常", e); 114 | return false; 115 | } 116 | } 117 | 118 | 119 | /** 120 | * 将Map中的数据转换成按照Key的ascii码排序后的key1=value1&key2=value2的形式 不包含签名域signature 121 | * 122 | * @param data 123 | * 待拼接的Map数据 124 | * @return 拼接好后的字符串 125 | */ 126 | public static String coverMap2String(Map data) { 127 | TreeMap tree = new TreeMap(); 128 | Iterator> it = data.entrySet().iterator(); 129 | while (it.hasNext()) { 130 | Entry en = it.next(); 131 | if (SDKConstants.param_signature.equals(en.getKey().trim())) { 132 | continue; 133 | } 134 | tree.put(en.getKey(), en.getValue()); 135 | } 136 | it = tree.entrySet().iterator(); 137 | StringBuffer sf = new StringBuffer(); 138 | while (it.hasNext()) { 139 | Entry en = it.next(); 140 | sf.append(en.getKey() + SDKConstants.EQUAL + en.getValue() 141 | + SDKConstants.AMPERSAND); 142 | } 143 | return sf.substring(0, sf.length() - 1); 144 | } 145 | 146 | 147 | /** 148 | * 兼容老方法 将形如key=value&key=value的字符串转换为相应的Map对象 149 | * 150 | * @param result 151 | * @return 152 | */ 153 | public static Map coverResultString2Map(String result) { 154 | return convertResultStringToMap(result); 155 | } 156 | 157 | /** 158 | * 将形如key=value&key=value的字符串转换为相应的Map对象 159 | * 160 | * @param result 161 | * @return 162 | */ 163 | public static Map convertResultStringToMap(String result) { 164 | Map map =null; 165 | try { 166 | 167 | if(StringUtils.isNotBlank(result)){ 168 | if(result.startsWith("{") && result.endsWith("}")){ 169 | System.out.println(result.length()); 170 | result = result.substring(1, result.length()-1); 171 | } 172 | map = parseQString(result); 173 | } 174 | 175 | } catch (UnsupportedEncodingException e) { 176 | logger.error(e.getMessage(), e); 177 | } 178 | return map; 179 | } 180 | 181 | 182 | /** 183 | * 解析应答字符串,生成应答要素 184 | * 185 | * @param str 186 | * 需要解析的字符串 187 | * @return 解析的结果map 188 | * @throws UnsupportedEncodingException 189 | */ 190 | public static Map parseQString(String str) 191 | throws UnsupportedEncodingException { 192 | 193 | Map map = new HashMap(); 194 | int len = str.length(); 195 | StringBuilder temp = new StringBuilder(); 196 | char curChar; 197 | String key = null; 198 | boolean isKey = true; 199 | boolean isOpen = false;//值里有嵌套 200 | char openName = 0; 201 | if(len>0){ 202 | for (int i = 0; i < len; i++) {// 遍历整个带解析的字符串 203 | curChar = str.charAt(i);// 取当前字符 204 | if (isKey) {// 如果当前生成的是key 205 | 206 | if (curChar == '=') {// 如果读取到=分隔符 207 | key = temp.toString(); 208 | temp.setLength(0); 209 | isKey = false; 210 | } else { 211 | temp.append(curChar); 212 | } 213 | } else {// 如果当前生成的是value 214 | if(isOpen){ 215 | if(curChar == openName){ 216 | isOpen = false; 217 | } 218 | 219 | }else{//如果没开启嵌套 220 | if(curChar == '{'){//如果碰到,就开启嵌套 221 | isOpen = true; 222 | openName ='}'; 223 | } 224 | if(curChar == '['){ 225 | isOpen = true; 226 | openName =']'; 227 | } 228 | } 229 | if (curChar == '&' && !isOpen) {// 如果读取到&分割符,同时这个分割符不是值域,这时将map里添加 230 | putKeyValueToMap(temp, isKey, key, map); 231 | temp.setLength(0); 232 | isKey = true; 233 | }else{ 234 | temp.append(curChar); 235 | } 236 | } 237 | 238 | } 239 | putKeyValueToMap(temp, isKey, key, map); 240 | } 241 | return map; 242 | } 243 | 244 | private static void putKeyValueToMap(StringBuilder temp, boolean isKey, 245 | String key, Map map) 246 | throws UnsupportedEncodingException { 247 | if (isKey) { 248 | key = temp.toString(); 249 | if (key.length() == 0) { 250 | throw new RuntimeException("QString format illegal"); 251 | } 252 | map.put(key, ""); 253 | } else { 254 | if (key.length() == 0) { 255 | throw new RuntimeException("QString format illegal"); 256 | } 257 | map.put(key, temp.toString()); 258 | } 259 | } 260 | 261 | /** 262 | * 判断字符串是否为NULL或空 263 | * 264 | * @param s 265 | * 待判断的字符串数据 266 | * @return 判断结果 true-是 false-否 267 | */ 268 | public static boolean isEmpty(String s) { 269 | return null == s || "".equals(s.trim()); 270 | } 271 | 272 | /** 273 | * 过滤请求报文中的空字符串或者空字符串 274 | * @param contentData 275 | * @return 276 | */ 277 | public static Map filterBlank(Map contentData){ 278 | logger.info("打印请求报文域 :"); 279 | Map submitFromData = new HashMap(); 280 | Set keyset = contentData.keySet(); 281 | 282 | for(String key:keyset){ 283 | String value = contentData.get(key); 284 | if (StringUtils.isNotBlank(value)) { 285 | // 对value值进行去除前后空处理 286 | submitFromData.put(key, value.trim()); 287 | logger.info(key + "-->" + String.valueOf(value)); 288 | } 289 | } 290 | return submitFromData; 291 | } 292 | } 293 | -------------------------------------------------------------------------------- /src/main/java/com/howie/pay/Controller/WeixinPayController.java: -------------------------------------------------------------------------------- 1 | package com.howie.pay.controller; 2 | 3 | import io.swagger.annotations.Api; 4 | import io.swagger.annotations.ApiOperation; 5 | 6 | import java.io.BufferedOutputStream; 7 | import java.io.BufferedReader; 8 | import java.io.InputStream; 9 | import java.io.InputStreamReader; 10 | import java.util.HashMap; 11 | import java.util.Iterator; 12 | import java.util.Map; 13 | import java.util.SortedMap; 14 | import java.util.TreeMap; 15 | 16 | import javax.servlet.http.HttpServletRequest; 17 | import javax.servlet.http.HttpServletResponse; 18 | 19 | import org.slf4j.Logger; 20 | import org.slf4j.LoggerFactory; 21 | import org.springframework.beans.factory.annotation.Autowired; 22 | import org.springframework.beans.factory.annotation.Value; 23 | import org.springframework.stereotype.Controller; 24 | import org.springframework.ui.ModelMap; 25 | import org.springframework.web.bind.annotation.RequestMapping; 26 | import org.springframework.web.bind.annotation.RequestMethod; 27 | 28 | import com.howie.pay.constants.Constants; 29 | import com.howie.pay.dto.InfoDTO; 30 | import com.howie.pay.service.IWeixinPayService; 31 | import com.howie.pay.wxUtils.ConfigUtil; 32 | import com.howie.pay.wxUtils.HttpUtil; 33 | import com.howie.pay.wxUtils.PayCommonUtil; 34 | import com.howie.pay.wxUtils.XMLUtil; 35 | 36 | /** 37 | * 微信支付 38 | */ 39 | @Api(tags = "微信支付") 40 | @Controller 41 | @RequestMapping(value = "weixin") 42 | public class WeixinPayController { 43 | private static final Logger logger = LoggerFactory 44 | .getLogger(WeixinPayController.class); 45 | @Autowired 46 | private IWeixinPayService weixinPayService; 47 | 48 | @Value("${wexinpay.notify.url}") 49 | private String notify_url; 50 | 51 | @ApiOperation(value = "支付主页") 52 | @RequestMapping(value = "index", method = RequestMethod.GET) 53 | public String index() { 54 | return "weixinpay/index"; 55 | } 56 | 57 | @ApiOperation(value = "二维码支付(模式一)根据商品ID预先生成二维码") 58 | @RequestMapping(value = "qcPay1", method = RequestMethod.POST) 59 | public String qcPay1(InfoDTO product, ModelMap map) { 60 | logger.info("二维码支付(模式一)"); 61 | weixinPayService.weixinPay1(product); 62 | String img = "../qrcode/" + product.getProductId() + ".png"; 63 | map.addAttribute("img", img); 64 | return "weixinpay/qcpay"; 65 | } 66 | 67 | @ApiOperation(value = "二维码支付(模式二)下单并生成二维码") 68 | @RequestMapping(value = "qcPay2", method = RequestMethod.POST) 69 | public String qcPay2(InfoDTO product, ModelMap map) { 70 | logger.info("二维码支付(模式二)"); 71 | // 参数自定义 这只是个Demo 72 | product.setProductId("20170721"); 73 | product.setBody("两个苹果八毛钱 "); 74 | product.setSpbillCreateIp("192.168.1.66"); 75 | String message = weixinPayService.weixinPay2(product); 76 | if (Constants.SUCCESS.equals(message)) { 77 | String img = "../qrcode/" + product.getOutTradeNo() + ".png"; 78 | map.addAttribute("img", img); 79 | } else { 80 | // 失败 81 | } 82 | return "weixinpay/qcpay"; 83 | } 84 | 85 | /** 86 | * 支付后台回调 87 | * @author hongyang.jiang 88 | */ 89 | @SuppressWarnings({ "unchecked", "rawtypes" }) 90 | @ApiOperation(value = "支付后台回调") 91 | @RequestMapping(value = "pay", method = RequestMethod.POST) 92 | public void weixin_notify(HttpServletRequest request, 93 | HttpServletResponse response) throws Exception { 94 | // 读取参数 95 | InputStream inputStream = request.getInputStream(); 96 | StringBuffer sb = new StringBuffer(); 97 | String s; 98 | BufferedReader in = new BufferedReader(new InputStreamReader( 99 | inputStream, "UTF-8")); 100 | while ((s = in.readLine()) != null) { 101 | sb.append(s); 102 | } 103 | in.close(); 104 | inputStream.close(); 105 | 106 | // 解析xml成map 107 | Map m = new HashMap(); 108 | m = XMLUtil.doXMLParse(sb.toString()); 109 | 110 | // 过滤空 设置 TreeMap 111 | SortedMap packageParams = new TreeMap(); 112 | Iterator it = m.keySet().iterator(); 113 | while (it.hasNext()) { 114 | String parameter = (String) it.next(); 115 | String parameterValue = m.get(parameter); 116 | 117 | String v = ""; 118 | if (null != parameterValue) { 119 | v = parameterValue.trim(); 120 | } 121 | packageParams.put(parameter, v); 122 | } 123 | // 账号信息 124 | String key = ConfigUtil.API_KEY; // key 125 | // 判断签名是否正确 126 | if (PayCommonUtil.isTenpaySign("UTF-8", packageParams, key)) { 127 | logger.info("微信支付成功回调"); 128 | // ------------------------------ 129 | // 处理业务开始 130 | // ------------------------------ 131 | String resXml = ""; 132 | if ("SUCCESS".equals((String) packageParams.get("result_code"))) { 133 | // 这里是支付成功 134 | String orderNo = (String) packageParams.get("out_trade_no"); 135 | logger.info("微信订单号{}付款成功", orderNo); 136 | // 这里 根据实际业务场景 做相应的操作 137 | // 通知微信.异步确认成功.必写.不然会一直通知后台.八次之后就认为交易失败了. 138 | resXml = "" 139 | + "" 140 | + "" + " "; 141 | } else { 142 | logger.info("支付失败,错误信息:{}", packageParams.get("err_code")); 143 | resXml = "" 144 | + "" 145 | + "" 146 | + " "; 147 | } 148 | // ------------------------------ 149 | // 处理业务完毕 150 | // ------------------------------ 151 | BufferedOutputStream out = new BufferedOutputStream( 152 | response.getOutputStream()); 153 | out.write(resXml.getBytes()); 154 | out.flush(); 155 | out.close(); 156 | } else { 157 | logger.info("通知签名验证失败"); 158 | } 159 | 160 | } 161 | 162 | /** 163 | * 模式一支付回调URL(生成二维码见 qrCodeUtil) 164 | * 商户支付回调URL设置指引:进入公众平台-->微信支付-->开发配置-->扫码支付-->修改 165 | * @author hongyang.jiang 166 | */ 167 | @SuppressWarnings({ "unchecked", "rawtypes" }) 168 | @ApiOperation(value = "模式一支付回调URL") 169 | @RequestMapping(value = "bizpayurl", method = RequestMethod.POST) 170 | public void bizpayurl(HttpServletRequest request, 171 | HttpServletResponse response) throws Exception { 172 | logger.info("模式一支付回调URL"); 173 | // 读取参数 174 | InputStream inputStream = request.getInputStream(); 175 | StringBuffer sb = new StringBuffer(); 176 | String s; 177 | BufferedReader in = new BufferedReader(new InputStreamReader( 178 | inputStream, "UTF-8")); 179 | while ((s = in.readLine()) != null) { 180 | sb.append(s); 181 | } 182 | in.close(); 183 | inputStream.close(); 184 | 185 | // 解析xml成map 186 | Map map = XMLUtil.doXMLParse(sb.toString()); 187 | // 过滤空 设置 TreeMap 188 | SortedMap packageParams = new TreeMap(); 189 | Iterator it = map.keySet().iterator(); 190 | while (it.hasNext()) { 191 | String parameter = (String) it.next(); 192 | String parameterValue = map.get(parameter); 193 | 194 | String v = ""; 195 | if (null != parameterValue) { 196 | v = parameterValue.trim(); 197 | } 198 | packageParams.put(parameter, v); 199 | } 200 | // 判断签名是否正确 201 | if (PayCommonUtil.isTenpaySign("UTF-8", packageParams, 202 | ConfigUtil.API_KEY)) { 203 | // 统一下单 204 | SortedMap params = new TreeMap(); 205 | ConfigUtil.commonParams(params); 206 | // 随即生成一个 入库 走业务逻辑 207 | String out_trade_no = Long.toString(System.currentTimeMillis()); 208 | params.put("body", "模式一扫码支付");// 商品描述 209 | params.put("out_trade_no", out_trade_no);// 商户订单号 210 | params.put("total_fee", "100");// 总金额 211 | params.put("spbill_create_ip", "192.168.1.66");// 发起人IP地址 212 | params.put("notify_url", notify_url);// 回调地址 213 | params.put("trade_type", "NATIVE");// 交易类型 214 | 215 | String paramsSign = PayCommonUtil.createSign("UTF-8", params, 216 | ConfigUtil.API_KEY); 217 | params.put("sign", paramsSign);// 签名 218 | String requestXML = PayCommonUtil.getRequestXml(params); 219 | 220 | String resXml = HttpUtil.postData(ConfigUtil.UNIFIED_ORDER_URL, 221 | requestXML); 222 | Map payResult = XMLUtil.doXMLParse(resXml); 223 | String returnCode = (String) payResult.get("return_code"); 224 | if ("SUCCESS".equals(returnCode)) { 225 | String resultCode = (String) payResult.get("result_code"); 226 | if ("SUCCESS".equals(resultCode)) { 227 | logger.info("(订单号:{}生成微信支付码成功)", out_trade_no); 228 | 229 | String prepay_id = payResult.get("prepay_id"); 230 | SortedMap prepayParams = new TreeMap(); 231 | ConfigUtil.commonParams(params); 232 | prepayParams.put("prepay_id", prepay_id); 233 | prepayParams.put("return_code", "SUCCESS"); 234 | prepayParams.put("result_code", "SUCCESS"); 235 | String prepaySign = PayCommonUtil.createSign("UTF-8", 236 | prepayParams, ConfigUtil.API_KEY); 237 | prepayParams.put("sign", prepaySign); 238 | String prepayXml = PayCommonUtil 239 | .getRequestXml(prepayParams); 240 | 241 | // 通知微信 预下单成功 242 | BufferedOutputStream out = new BufferedOutputStream( 243 | response.getOutputStream()); 244 | out.write(prepayXml.getBytes()); 245 | out.flush(); 246 | out.close(); 247 | } else { 248 | String errCodeDes = (String) map.get("err_code_des"); 249 | logger.info("(订单号:{}生成微信支付码(系统)失败[{}])", out_trade_no, 250 | errCodeDes); 251 | } 252 | } else { 253 | String returnMsg = (String) map.get("return_msg"); 254 | logger.info("(订单号:{} 生成微信支付码(通信)失败[{}])", out_trade_no, 255 | returnMsg); 256 | } 257 | } else { 258 | logger.info("签名错误"); 259 | } 260 | } 261 | } 262 | -------------------------------------------------------------------------------- /src/main/java/com/howie/pay/UnionpayUtils/HttpClient.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Licensed Property to China UnionPay Co., Ltd. 4 | * 5 | * (C) Copyright of China UnionPay Co., Ltd. 2010 6 | * All Rights Reserved. 7 | * 8 | * 9 | * Modification History: 10 | * ============================================================================= 11 | * Author Date Description 12 | * ------------ ---------- --------------------------------------------------- 13 | * xshu 2014-05-28 HTTP通信工具类 14 | * ============================================================================= 15 | */ 16 | package com.howie.pay.unionpayUtils; 17 | 18 | import java.io.BufferedReader; 19 | import java.io.ByteArrayOutputStream; 20 | import java.io.IOException; 21 | import java.io.InputStream; 22 | import java.io.PrintStream; 23 | import java.io.UnsupportedEncodingException; 24 | import java.net.HttpURLConnection; 25 | import java.net.MalformedURLException; 26 | import java.net.ProtocolException; 27 | import java.net.URISyntaxException; 28 | import java.net.URL; 29 | import java.net.URLConnection; 30 | import java.net.URLEncoder; 31 | import java.util.Map; 32 | import java.util.Map.Entry; 33 | 34 | import javax.net.ssl.HttpsURLConnection; 35 | 36 | import org.slf4j.Logger; 37 | import org.slf4j.LoggerFactory; 38 | 39 | import com.howie.pay.unionpayUtils.BaseHttpSSLSocketFactory.TrustAnyHostnameVerifier; 40 | public class HttpClient { 41 | private static final Logger logger = LoggerFactory.getLogger(HttpClient.class); 42 | /** 43 | * 目标地址 44 | */ 45 | private URL url; 46 | 47 | /** 48 | * 通信连接超时时间 49 | */ 50 | private int connectionTimeout; 51 | 52 | /** 53 | * 通信读超时时间 54 | */ 55 | private int readTimeOut; 56 | 57 | /** 58 | * 通信结果 59 | */ 60 | private String result; 61 | 62 | /** 63 | * 获取通信结果 64 | * @return 65 | */ 66 | public String getResult() { 67 | return result; 68 | } 69 | 70 | /** 71 | * 设置通信结果 72 | * @param result 73 | */ 74 | public void setResult(String result) { 75 | this.result = result; 76 | } 77 | 78 | /** 79 | * 构造函数 80 | * @param url 目标地址 81 | * @param connectionTimeout HTTP连接超时时间 82 | * @param readTimeOut HTTP读写超时时间 83 | */ 84 | public HttpClient(String url, int connectionTimeout, int readTimeOut) { 85 | try { 86 | this.url = new URL(url); 87 | this.connectionTimeout = connectionTimeout; 88 | this.readTimeOut = readTimeOut; 89 | } catch (MalformedURLException e) { 90 | logger.error(e.getMessage(), e); 91 | } 92 | } 93 | 94 | /** 95 | * 发送信息到服务端 96 | * @param data 97 | * @param encoding 98 | * @return 99 | * @throws Exception 100 | */ 101 | public int send(Map data, String encoding) throws Exception { 102 | try { 103 | HttpURLConnection httpURLConnection = createConnection(encoding); 104 | if(null == httpURLConnection){ 105 | throw new Exception("创建联接失败"); 106 | } 107 | String sendData = this.getRequestParamString(data, encoding); 108 | logger.info("请求报文:[" + sendData + "]"); 109 | this.requestServer(httpURLConnection, sendData, 110 | encoding); 111 | this.result = this.response(httpURLConnection, encoding); 112 | logger.info("同步返回报文:[" + result + "]"); 113 | return httpURLConnection.getResponseCode(); 114 | } catch (Exception e) { 115 | throw e; 116 | } 117 | } 118 | 119 | /** 120 | * 发送信息到服务端 GET方式 121 | * @param data 122 | * @param encoding 123 | * @return 124 | * @throws Exception 125 | */ 126 | public int sendGet(String encoding) throws Exception { 127 | try { 128 | HttpURLConnection httpURLConnection = createConnectionGet(encoding); 129 | if(null == httpURLConnection){ 130 | throw new Exception("创建联接失败"); 131 | } 132 | this.result = this.response(httpURLConnection, encoding); 133 | logger.info("同步返回报文:[" + result + "]"); 134 | return httpURLConnection.getResponseCode(); 135 | } catch (Exception e) { 136 | throw e; 137 | } 138 | } 139 | 140 | 141 | /** 142 | * HTTP Post发送消息 143 | * 144 | * @param connection 145 | * @param message 146 | * @throws IOException 147 | */ 148 | private void requestServer(final URLConnection connection, String message, String encoder) 149 | throws Exception { 150 | PrintStream out = null; 151 | try { 152 | connection.connect(); 153 | out = new PrintStream(connection.getOutputStream(), false, encoder); 154 | out.print(message); 155 | out.flush(); 156 | } catch (Exception e) { 157 | throw e; 158 | } finally { 159 | if (null != out) { 160 | out.close(); 161 | } 162 | } 163 | } 164 | 165 | /** 166 | * 显示Response消息 167 | * 168 | * @param connection 169 | * @param CharsetName 170 | * @return 171 | * @throws URISyntaxException 172 | * @throws IOException 173 | */ 174 | private String response(final HttpURLConnection connection, String encoding) 175 | throws URISyntaxException, IOException, Exception { 176 | InputStream in = null; 177 | StringBuilder sb = new StringBuilder(1024); 178 | BufferedReader br = null; 179 | try { 180 | if (200 == connection.getResponseCode()) { 181 | in = connection.getInputStream(); 182 | sb.append(new String(read(in), encoding)); 183 | } else { 184 | in = connection.getErrorStream(); 185 | sb.append(new String(read(in), encoding)); 186 | } 187 | logger.info("HTTP Return Status-Code:[" 188 | + connection.getResponseCode() + "]"); 189 | return sb.toString(); 190 | } catch (Exception e) { 191 | throw e; 192 | } finally { 193 | if (null != br) { 194 | br.close(); 195 | } 196 | if (null != in) { 197 | in.close(); 198 | } 199 | if (null != connection) { 200 | connection.disconnect(); 201 | } 202 | } 203 | } 204 | 205 | public static byte[] read(InputStream in) throws IOException { 206 | byte[] buf = new byte[1024]; 207 | int length = 0; 208 | ByteArrayOutputStream bout = new ByteArrayOutputStream(); 209 | while ((length = in.read(buf, 0, buf.length)) > 0) { 210 | bout.write(buf, 0, length); 211 | } 212 | bout.flush(); 213 | return bout.toByteArray(); 214 | } 215 | 216 | /** 217 | * 创建连接 218 | * 219 | * @return 220 | * @throws ProtocolException 221 | */ 222 | private HttpURLConnection createConnection(String encoding) throws ProtocolException { 223 | HttpURLConnection httpURLConnection = null; 224 | try { 225 | httpURLConnection = (HttpURLConnection) url.openConnection(); 226 | } catch (IOException e) { 227 | logger.error(e.getMessage(), e); 228 | return null; 229 | } 230 | httpURLConnection.setConnectTimeout(this.connectionTimeout);// 连接超时时间 231 | httpURLConnection.setReadTimeout(this.readTimeOut);// 读取结果超时时间 232 | httpURLConnection.setDoInput(true); // 可读 233 | httpURLConnection.setDoOutput(true); // 可写 234 | httpURLConnection.setUseCaches(false);// 取消缓存 235 | httpURLConnection.setRequestProperty("Content-type", 236 | "application/x-www-form-urlencoded;charset=" + encoding); 237 | httpURLConnection.setRequestMethod("POST"); 238 | if ("https".equalsIgnoreCase(url.getProtocol())) { 239 | HttpsURLConnection husn = (HttpsURLConnection) httpURLConnection; 240 | husn.setSSLSocketFactory(new BaseHttpSSLSocketFactory()); 241 | husn.setHostnameVerifier(new TrustAnyHostnameVerifier());//解决由于服务器证书问题导致HTTPS无法访问的情况 242 | return husn; 243 | } 244 | return httpURLConnection; 245 | } 246 | 247 | /** 248 | * 创建连接 249 | * 250 | * @return 251 | * @throws ProtocolException 252 | */ 253 | private HttpURLConnection createConnectionGet(String encoding) throws ProtocolException { 254 | HttpURLConnection httpURLConnection = null; 255 | try { 256 | httpURLConnection = (HttpURLConnection) url.openConnection(); 257 | } catch (IOException e) { 258 | logger.error(e.getMessage(), e); 259 | return null; 260 | } 261 | httpURLConnection.setConnectTimeout(this.connectionTimeout);// 连接超时时间 262 | httpURLConnection.setReadTimeout(this.readTimeOut);// 读取结果超时时间 263 | httpURLConnection.setUseCaches(false);// 取消缓存 264 | httpURLConnection.setRequestProperty("Content-type", 265 | "application/x-www-form-urlencoded;charset=" + encoding); 266 | httpURLConnection.setRequestMethod("GET"); 267 | if ("https".equalsIgnoreCase(url.getProtocol())) { 268 | HttpsURLConnection husn = (HttpsURLConnection) httpURLConnection; 269 | husn.setSSLSocketFactory(new BaseHttpSSLSocketFactory()); 270 | husn.setHostnameVerifier(new TrustAnyHostnameVerifier());//解决由于服务器证书问题导致HTTPS无法访问的情况 271 | return husn; 272 | } 273 | return httpURLConnection; 274 | } 275 | 276 | /** 277 | * 将Map存储的对象,转换为key=value&key=value的字符 278 | * 279 | * @param requestParam 280 | * @param coder 281 | * @return 282 | */ 283 | private String getRequestParamString(Map requestParam, String coder) { 284 | if (null == coder || "".equals(coder)) { 285 | coder = "UTF-8"; 286 | } 287 | StringBuffer sf = new StringBuffer(""); 288 | String reqstr = ""; 289 | if (null != requestParam && 0 != requestParam.size()) { 290 | for (Entry en : requestParam.entrySet()) { 291 | try { 292 | sf.append(en.getKey() 293 | + "=" 294 | + (null == en.getValue() || "".equals(en.getValue()) ? "" : URLEncoder 295 | .encode(en.getValue(), coder)) + "&"); 296 | } catch (UnsupportedEncodingException e) { 297 | logger.error(e.getMessage(), e); 298 | return ""; 299 | } 300 | } 301 | reqstr = sf.substring(0, sf.length() - 1); 302 | } 303 | logger.info("请求报文(已做过URLEncode编码):[" + reqstr + "]"); 304 | return reqstr; 305 | } 306 | 307 | } 308 | -------------------------------------------------------------------------------- /src/main/java/com/howie/pay/ServiceImpl/AliPayServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.howie.pay.serviceImpl; 2 | 3 | import java.io.File; 4 | 5 | import net.sf.json.JSONObject; 6 | 7 | import org.apache.commons.lang3.StringUtils; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | import org.springframework.beans.factory.annotation.Value; 11 | 12 | import com.alibaba.dubbo.config.annotation.Service; 13 | import com.alipay.api.AlipayApiException; 14 | import com.alipay.api.AlipayClient; 15 | import com.alipay.api.AlipayResponse; 16 | import com.alipay.api.domain.AlipayTradeAppPayModel; 17 | import com.alipay.api.request.AlipayDataDataserviceBillDownloadurlQueryRequest; 18 | import com.alipay.api.request.AlipayTradeAppPayRequest; 19 | import com.alipay.api.request.AlipayTradeCloseRequest; 20 | import com.alipay.api.request.AlipayTradePagePayRequest; 21 | import com.alipay.api.request.AlipayTradeWapPayRequest; 22 | import com.alipay.api.response.AlipayDataDataserviceBillDownloadurlQueryResponse; 23 | import com.alipay.api.response.AlipayTradeAppPayResponse; 24 | import com.alipay.api.response.AlipayTradeCloseResponse; 25 | import com.alipay.api.response.AlipayTradePrecreateResponse; 26 | import com.alipay.demo.trade.config.Configs; 27 | import com.alipay.demo.trade.model.ExtendParams; 28 | import com.alipay.demo.trade.model.builder.AlipayTradePrecreateRequestBuilder; 29 | import com.alipay.demo.trade.model.builder.AlipayTradeRefundRequestBuilder; 30 | import com.alipay.demo.trade.model.result.AlipayF2FPrecreateResult; 31 | import com.alipay.demo.trade.model.result.AlipayF2FRefundResult; 32 | import com.alipay.demo.trade.utils.ZxingUtils; 33 | import com.howie.pay.aliUtils.AliPayConfig; 34 | import com.howie.pay.aliUtils.CommonUtil; 35 | import com.howie.pay.constants.Constants; 36 | import com.howie.pay.dto.InfoDTO; 37 | import com.howie.pay.service.IAliPayService; 38 | @Service 39 | public class AliPayServiceImpl implements IAliPayService { 40 | private static final Logger logger = LoggerFactory.getLogger(AliPayServiceImpl.class); 41 | 42 | @Value("${alipay.notify.url}") 43 | private String notify_url; 44 | 45 | @Override 46 | public String aliPay(InfoDTO product) { 47 | logger.info("订单号:{}生成支付宝支付码",product.getOutTradeNo()); 48 | String message = Constants.SUCCESS; 49 | //二维码存放路径 50 | System.out.println(Constants.QRCODE_PATH); 51 | String imgPath= Constants.QRCODE_PATH+Constants.SF_FILE_SEPARATOR+product.getOutTradeNo()+".png"; 52 | String outTradeNo = product.getOutTradeNo(); 53 | String subject = product.getSubject(); 54 | String totalAmount = CommonUtil.divide(product.getTotalFee(), "100").toString(); 55 | // 如果该字段为空,则默认为与支付宝签约的商户的PID,也就是appid对应的PID 56 | String sellerId = ""; 57 | // (必填) 商户门店编号,通过门店号和商家后台可以配置精准到门店的折扣信息,详询支付宝技术支持 58 | String storeId = "test_store_id"; 59 | // 业务扩展参数,目前可添加由支付宝分配的系统商编号(通过setSysServiceProviderId方法),详情请咨询支付宝技术支持 60 | ExtendParams extendParams = new ExtendParams(); 61 | extendParams.setSysServiceProviderId("2088100200300400500"); 62 | // 订单描述,可以对交易或商品进行一个详细地描述,比如填写"购买商品2件共15.00元" 63 | String body = product.getBody(); 64 | // 支付超时,定义为120分钟 65 | String timeoutExpress = "120m"; 66 | // 创建扫码支付请求builder,设置请求参数 67 | AlipayTradePrecreateRequestBuilder builder = new AlipayTradePrecreateRequestBuilder() 68 | .setSubject(subject) 69 | .setTotalAmount(totalAmount) 70 | .setOutTradeNo(outTradeNo) 71 | .setSellerId(sellerId) 72 | .setBody(body)//128长度 --附加信息 73 | .setStoreId(storeId) 74 | .setExtendParams(extendParams) 75 | .setTimeoutExpress(timeoutExpress) 76 | .setNotifyUrl(notify_url);//支付宝服务器主动通知商户服务器里指定的页面http路径,根据需要设置 77 | 78 | AlipayF2FPrecreateResult result = AliPayConfig.getAlipayTradeService().tradePrecreate(builder); 79 | switch (result.getTradeStatus()) { 80 | case SUCCESS: 81 | logger.info("支付宝预下单成功: )"); 82 | 83 | AlipayTradePrecreateResponse response = result.getResponse(); 84 | dumpResponse(response); 85 | ZxingUtils.getQRCodeImge(response.getQrCode(), 256, imgPath); 86 | break; 87 | 88 | case FAILED: 89 | logger.info("支付宝预下单失败!!!"); 90 | message = Constants.FAIL; 91 | break; 92 | 93 | case UNKNOWN: 94 | logger.info("系统异常,预下单状态未知!!!"); 95 | message = Constants.FAIL; 96 | break; 97 | 98 | default: 99 | logger.info("不支持的交易状态,交易返回异常!!!"); 100 | message = Constants.FAIL; 101 | break; 102 | } 103 | return message; 104 | } 105 | // 简单打印应答 106 | private void dumpResponse(AlipayResponse response) { 107 | if (response != null) { 108 | logger.info(String.format("code:%s, msg:%s", response.getCode(), response.getMsg())); 109 | if (StringUtils.isNotEmpty(response.getSubCode())) { 110 | logger.info(String.format("subCode:%s, subMsg:%s", response.getSubCode(), response.getSubMsg())); 111 | } 112 | logger.info("body:" + response.getBody()); 113 | } 114 | } 115 | @Override 116 | public String aliRefund(InfoDTO product) { 117 | logger.info("订单号:"+product.getOutTradeNo()+"支付宝退款"); 118 | String message = Constants.SUCCESS; 119 | // (必填) 外部订单号,需要退款交易的商户外部订单号 120 | String outTradeNo = product.getOutTradeNo(); 121 | // (必填) 退款金额,该金额必须小于等于订单的支付金额,单位为元 122 | String refundAmount = CommonUtil.divide(product.getTotalFee(), "100").toString(); 123 | 124 | // (必填) 退款原因,可以说明用户退款原因,方便为商家后台提供统计 125 | String refundReason = "正常退款,用户买多了"; 126 | 127 | // (必填) 商户门店编号,退款情况下可以为商家后台提供退款权限判定和统计等作用,详询支付宝技术支持 128 | String storeId = "test_store_id"; 129 | 130 | // 创建退款请求builder,设置请求参数 131 | AlipayTradeRefundRequestBuilder builder = new AlipayTradeRefundRequestBuilder() 132 | .setOutTradeNo(outTradeNo) 133 | .setRefundAmount(refundAmount) 134 | .setRefundReason(refundReason) 135 | //.setOutRequestNo(outRequestNo) 136 | .setStoreId(storeId); 137 | 138 | AlipayF2FRefundResult result = AliPayConfig.getAlipayTradeService().tradeRefund(builder); 139 | switch (result.getTradeStatus()) { 140 | case SUCCESS: 141 | logger.info("支付宝退款成功: )"); 142 | break; 143 | 144 | case FAILED: 145 | logger.info("支付宝退款失败!!!"); 146 | message = Constants.FAIL; 147 | break; 148 | 149 | case UNKNOWN: 150 | logger.info("系统异常,订单退款状态未知!!!"); 151 | message = Constants.FAIL; 152 | break; 153 | 154 | default: 155 | logger.info("不支持的交易状态,交易返回异常!!!"); 156 | message = Constants.FAIL; 157 | break; 158 | } 159 | return message; 160 | } 161 | /** 162 | * 如果你调用的是当面付预下单接口(alipay.trade.precreate),调用成功后订单实际上是没有生成,因为创建一笔订单要买家、卖家、金额三要素。 163 | * 预下单并没有创建订单,所以根据商户订单号操作订单,比如查询或者关闭,会报错订单不存在。 164 | * 当用户扫码后订单才会创建,用户扫码之前二维码有效期2小时,扫码之后有效期根据timeout_express时间指定。 165 | * =====只有支付成功后 调用此订单才可以===== 166 | */ 167 | @Override 168 | public String aliCloseorder(InfoDTO product) { 169 | logger.info("订单号:"+product.getOutTradeNo()+"支付宝关闭订单"); 170 | String message = Constants.SUCCESS; 171 | try { 172 | String imgPath= Constants.QRCODE_PATH+Constants.SF_FILE_SEPARATOR+"alipay_"+product.getOutTradeNo()+".png"; 173 | File file = new File(imgPath); 174 | if(file.exists()){ 175 | AlipayClient alipayClient = AliPayConfig.getAlipayClient(); 176 | AlipayTradeCloseRequest request = new AlipayTradeCloseRequest(); 177 | request.setBizContent("{" + 178 | " \"out_trade_no\":\""+product.getOutTradeNo()+"\"" + 179 | " }"); 180 | AlipayTradeCloseResponse response = alipayClient.execute(request); 181 | if(response.isSuccess()){//扫码未支付的情况 182 | logger.info("订单号:"+product.getOutTradeNo()+"支付宝关闭订单成功并删除支付二维码"); 183 | file.delete(); 184 | }else{ 185 | if("ACQ.TRADE_NOT_EXIST".equals(response.getSubCode())){ 186 | logger.info("订单号:"+product.getOutTradeNo()+response.getSubMsg()+"(预下单 未扫码的情况)"); 187 | }else if("ACQ.TRADE_STATUS_ERROR".equals(response.getSubCode())){ 188 | logger.info("订单号:"+product.getOutTradeNo()+response.getSubMsg()); 189 | }else{ 190 | logger.info("订单号:"+product.getOutTradeNo()+"支付宝关闭订单失败"+response.getSubCode()+response.getSubMsg()); 191 | message = Constants.FAIL; 192 | } 193 | } 194 | } 195 | } catch (Exception e) { 196 | e.printStackTrace(); 197 | message = Constants.FAIL; 198 | logger.info("订单号:"+product.getOutTradeNo()+"支付宝关闭订单异常"); 199 | } 200 | return message; 201 | } 202 | @Override 203 | public String downloadBillUrl(String billDate,String billType) { 204 | logger.info("获取支付宝订单地址:"+billDate); 205 | String downloadBillUrl = ""; 206 | try { 207 | AlipayDataDataserviceBillDownloadurlQueryRequest request = new AlipayDataDataserviceBillDownloadurlQueryRequest(); 208 | request.setBizContent("{" + " \"bill_type\":\"trade\"," 209 | + " \"bill_date\":\"2016-12-26\"" + " }"); 210 | 211 | AlipayDataDataserviceBillDownloadurlQueryResponse response 212 | = AliPayConfig.getAlipayClient().execute(request); 213 | if (response.isSuccess()) { 214 | logger.info("获取支付宝订单地址成功:"+billDate); 215 | downloadBillUrl = response.getBillDownloadUrl();//获取下载地 216 | } else { 217 | logger.info("获取支付宝订单地址失败"+response.getSubMsg()+":"+billDate); 218 | } 219 | } catch (Exception e) { 220 | e.printStackTrace(); 221 | logger.info("获取支付宝订单地址异常:"+billDate); 222 | } 223 | return downloadBillUrl; 224 | } 225 | @Override 226 | public String aliPayMobile(InfoDTO product) { 227 | logger.info("支付宝手机支付下单"); 228 | AlipayTradeWapPayRequest alipayRequest = new AlipayTradeWapPayRequest(); 229 | String returnUrl = "回调地址 http 自定义"; 230 | alipayRequest.setReturnUrl(returnUrl);//前台通知 231 | alipayRequest.setNotifyUrl(notify_url);//后台回调 232 | JSONObject bizContent = new JSONObject(); 233 | bizContent.put("out_trade_no", product.getOutTradeNo()); 234 | bizContent.put("total_amount", product.getTotalFee());//订单金额:元 235 | bizContent.put("subject",product.getSubject());//订单标题 236 | bizContent.put("seller_id", Configs.getPid());//实际收款账号,一般填写商户PID即可 237 | bizContent.put("product_code", "QUICK_WAP_PAY");//手机网页支付 238 | bizContent.put("body", "两个苹果五毛钱"); 239 | String biz = bizContent.toString().replaceAll("\"", "'"); 240 | alipayRequest.setBizContent(biz); 241 | logger.info("业务参数:"+alipayRequest.getBizContent()); 242 | String form = Constants.FAIL; 243 | try { 244 | form = AliPayConfig.getAlipayClient().pageExecute(alipayRequest).getBody(); 245 | } catch (AlipayApiException e) { 246 | logger.error("支付宝构造表单失败",e); 247 | } 248 | return form; 249 | } 250 | @Override 251 | public String aliPayPc(InfoDTO product) { 252 | logger.info("支付宝PC支付下单"); 253 | AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest(); 254 | String returnUrl = "前台回调地址 http 自定义"; 255 | alipayRequest.setReturnUrl(returnUrl);//前台通知 256 | alipayRequest.setNotifyUrl(notify_url);//后台回调 257 | JSONObject bizContent = new JSONObject(); 258 | bizContent.put("out_trade_no", product.getOutTradeNo()); 259 | bizContent.put("total_amount", product.getTotalFee());//订单金额:元 260 | bizContent.put("subject",product.getSubject());//订单标题 261 | bizContent.put("seller_id", Configs.getPid());//实际收款账号,一般填写商户PID即可 262 | bizContent.put("product_code", "FAST_INSTANT_TRADE_PAY");//电脑网站支付 263 | bizContent.put("body", "两个苹果五毛钱"); 264 | String biz = bizContent.toString().replaceAll("\"", "'"); 265 | alipayRequest.setBizContent(biz); 266 | logger.info("业务参数:"+alipayRequest.getBizContent()); 267 | String form = Constants.FAIL; 268 | try { 269 | form = AliPayConfig.getAlipayClient().pageExecute(alipayRequest).getBody(); 270 | } catch (AlipayApiException e) { 271 | logger.error("支付宝构造表单失败",e); 272 | } 273 | return form; 274 | } 275 | @Override 276 | public String appPay(InfoDTO product) { 277 | String orderString = Constants.FAIL; 278 | // 实例化客户端 279 | AlipayClient alipayClient = AliPayConfig.getAlipayClient(); 280 | // 实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称:alipay.trade.app.pay 281 | AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest(); 282 | // SDK已经封装掉了公共参数,这里只需要传入业务参数。以下方法为sdk的model入参方式(model和biz_content同时存在的情况下取biz_content)。 283 | AlipayTradeAppPayModel model = new AlipayTradeAppPayModel(); 284 | model.setBody(product.getBody()); 285 | model.setSubject(product.getSubject()); 286 | model.setOutTradeNo(product.getOutTradeNo()); 287 | model.setTimeoutExpress("30m"); 288 | model.setTotalAmount(product.getTotalFee()); 289 | model.setProductCode("QUICK_MSECURITY_PAY"); 290 | request.setBizModel(model); 291 | request.setNotifyUrl("商户外网可以访问的异步地址"); 292 | try { 293 | // 这里和普通的接口调用不同,使用的是sdkExecute 294 | AlipayTradeAppPayResponse response = alipayClient 295 | .sdkExecute(request); 296 | orderString = response.getBody();//就是orderString 可以直接给客户端请求,无需再做处理。 297 | //System.out.println(response.getBody()); 298 | } catch (AlipayApiException e) { 299 | e.printStackTrace(); 300 | } 301 | return orderString ; 302 | } 303 | } 304 | -------------------------------------------------------------------------------- /src/main/java/com/howie/pay/ServiceImpl/WeixinPayServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.howie.pay.serviceImpl; 2 | 3 | import java.util.Map; 4 | import java.util.SortedMap; 5 | import java.util.TreeMap; 6 | 7 | import net.sf.json.JSONObject; 8 | 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | import org.springframework.beans.factory.annotation.Value; 12 | 13 | import com.alibaba.dubbo.config.annotation.Service; 14 | import com.alipay.demo.trade.utils.ZxingUtils; 15 | import com.howie.pay.aliUtils.CommonUtil; 16 | import com.howie.pay.constants.Constants; 17 | import com.howie.pay.dto.InfoDTO; 18 | import com.howie.pay.service.IWeixinPayService; 19 | import com.howie.pay.wxUtils.ClientCustomSSL; 20 | import com.howie.pay.wxUtils.ConfigUtil; 21 | import com.howie.pay.wxUtils.HttpUtil; 22 | import com.howie.pay.wxUtils.PayCommonUtil; 23 | import com.howie.pay.wxUtils.XMLUtil; 24 | 25 | @Service 26 | public class WeixinPayServiceImpl implements IWeixinPayService { 27 | private static final Logger logger = LoggerFactory 28 | .getLogger(WeixinPayServiceImpl.class); 29 | 30 | @Value("${wexinpay.notify.url}") 31 | private String notify_url; 32 | @Value("${server.context.url}") 33 | private String server_url; 34 | 35 | @SuppressWarnings("rawtypes") 36 | @Override 37 | public String weixinPay2(InfoDTO product) { 38 | logger.info("订单号:{}生成微信支付码", product.getOutTradeNo()); 39 | String message = Constants.SUCCESS; 40 | try { 41 | String imgPath = Constants.QRCODE_PATH 42 | + Constants.SF_FILE_SEPARATOR + product.getOutTradeNo() 43 | + ".png"; 44 | // 账号信息 45 | String key = ConfigUtil.API_KEY; // key 46 | String trade_type = "NATIVE";// 交易类型 原生扫码支付 47 | SortedMap packageParams = new TreeMap(); 48 | ConfigUtil.commonParams(packageParams); 49 | packageParams.put("product_id", product.getProductId());// 商品ID 50 | packageParams.put("body", product.getBody());// 商品描述 51 | packageParams.put("out_trade_no", product.getOutTradeNo());// 商户订单号 52 | String totalFee = product.getTotalFee(); 53 | totalFee = CommonUtil.subZeroAndDot(totalFee); 54 | packageParams.put("total_fee", totalFee);// 总金额 55 | packageParams.put("spbill_create_ip", product.getSpbillCreateIp());// 发起人IP地址 56 | packageParams.put("notify_url", notify_url);// 回调地址 57 | packageParams.put("trade_type", trade_type);// 交易类型 58 | String sign = PayCommonUtil.createSign("UTF-8", packageParams, key); 59 | packageParams.put("sign", sign);// 签名 60 | 61 | String requestXML = PayCommonUtil.getRequestXml(packageParams); 62 | String resXml = HttpUtil.postData(ConfigUtil.UNIFIED_ORDER_URL, 63 | requestXML); 64 | Map map = XMLUtil.doXMLParse(resXml); 65 | String returnCode = (String) map.get("return_code"); 66 | if ("SUCCESS".equals(returnCode)) { 67 | String resultCode = (String) map.get("result_code"); 68 | if ("SUCCESS".equals(resultCode)) { 69 | logger.info("订单号:{}生成微信支付码成功", product.getOutTradeNo()); 70 | String urlCode = (String) map.get("code_url"); 71 | ConfigUtil.shorturl(urlCode);// 转换为短链接 72 | ZxingUtils.getQRCodeImge(urlCode, 256, imgPath);// 生成二维码 73 | } else { 74 | String errCodeDes = (String) map.get("err_code_des"); 75 | logger.info("订单号:{}生成微信支付码(系统)失败:{}", 76 | product.getOutTradeNo(), errCodeDes); 77 | message = Constants.FAIL; 78 | } 79 | } else { 80 | String returnMsg = (String) map.get("return_msg"); 81 | logger.info("(订单号:{}生成微信支付码(通信)失败:{}", product.getOutTradeNo(), 82 | returnMsg); 83 | message = Constants.FAIL; 84 | } 85 | } catch (Exception e) { 86 | logger.error("订单号:{}生成微信支付码失败(系统异常))", product.getOutTradeNo(), e); 87 | message = Constants.FAIL; 88 | } 89 | return message; 90 | } 91 | 92 | @Override 93 | public void weixinPay1(InfoDTO product) { 94 | // 商户支付回调URL设置指引:进入公众平台-->微信支付-->开发配置-->扫码支付-->修改 加入回调URL 95 | // 注意参数初始化 这只是个Demo 96 | SortedMap packageParams = new TreeMap(); 97 | // 封装通用参数 98 | ConfigUtil.commonParams(packageParams); 99 | packageParams.put("product_id", product.getProductId());// 真实商品ID 100 | packageParams.put("time_stamp", PayCommonUtil.getCurrTime()); 101 | // 生成签名 102 | String sign = PayCommonUtil.createSign("UTF-8", packageParams, 103 | ConfigUtil.API_KEY); 104 | // 组装二维码信息(注意全角和半角:的区别 狗日的腾讯) 105 | StringBuffer qrCode = new StringBuffer(); 106 | qrCode.append("weixin://wxpay/bizpayurl?"); 107 | qrCode.append("appid=" + ConfigUtil.APP_ID); 108 | qrCode.append("&mch_id=" + ConfigUtil.MCH_ID); 109 | qrCode.append("&nonce_str=" + packageParams.get("nonce_str")); 110 | qrCode.append("&product_id=" + product.getProductId()); 111 | qrCode.append("&time_stamp=" + packageParams.get("time_stamp")); 112 | qrCode.append("&sign=" + sign); 113 | String imgPath = Constants.QRCODE_PATH + Constants.SF_FILE_SEPARATOR 114 | + product.getProductId() + ".png"; 115 | // 生成二维码 116 | ZxingUtils.getQRCodeImge(qrCode.toString(), 256, imgPath); 117 | } 118 | 119 | @SuppressWarnings("rawtypes") 120 | @Override 121 | public String weixinRefund(InfoDTO product) { 122 | logger.info("订单号:{}微信退款", product.getOutTradeNo()); 123 | String message = Constants.SUCCESS; 124 | try { 125 | // 账号信息 126 | String mch_id = ConfigUtil.MCH_ID; // 商业号 127 | String key = ConfigUtil.API_KEY; // key 128 | 129 | SortedMap packageParams = new TreeMap(); 130 | ConfigUtil.commonParams(packageParams); 131 | packageParams.put("out_trade_no", product.getOutTradeNo());// 商户订单号 132 | packageParams.put("out_refund_no", product.getOutTradeNo());// 商户退款单号 133 | String totalFee = product.getTotalFee(); 134 | totalFee = CommonUtil.subZeroAndDot(totalFee); 135 | packageParams.put("total_fee", totalFee);// 总金额 136 | packageParams.put("refund_fee", totalFee);// 退款金额 137 | packageParams.put("op_user_id", mch_id);// 操作员帐号, 默认为商户号 138 | String sign = PayCommonUtil.createSign("UTF-8", packageParams, key); 139 | packageParams.put("sign", sign);// 签名 140 | String requestXML = PayCommonUtil.getRequestXml(packageParams); 141 | String weixinPost = ClientCustomSSL.doRefund(ConfigUtil.REFUND_URL, 142 | requestXML).toString(); 143 | Map map = XMLUtil.doXMLParse(weixinPost); 144 | String returnCode = (String) map.get("return_code"); 145 | if ("SUCCESS".equals(returnCode)) { 146 | String resultCode = (String) map.get("result_code"); 147 | if ("SUCCESS".equals(resultCode)) { 148 | logger.info("订单号:{}微信退款成功并删除二维码", product.getOutTradeNo()); 149 | } else { 150 | String errCodeDes = (String) map.get("err_code_des"); 151 | logger.info("订单号:{}微信退款失败:{}", product.getOutTradeNo(), 152 | errCodeDes); 153 | message = Constants.FAIL; 154 | } 155 | } else { 156 | String returnMsg = (String) map.get("return_msg"); 157 | logger.info("订单号:{}微信退款失败:{}", product.getOutTradeNo(), 158 | returnMsg); 159 | message = Constants.FAIL; 160 | } 161 | } catch (Exception e) { 162 | logger.error("订单号:{}微信支付失败(系统异常)", product.getOutTradeNo(), e); 163 | message = Constants.FAIL; 164 | } 165 | return message; 166 | } 167 | 168 | @SuppressWarnings("rawtypes") 169 | @Override 170 | public String weixinCloseorder(InfoDTO product) { 171 | logger.info("订单号:{}微信关闭订单", product.getOutTradeNo()); 172 | String message = Constants.SUCCESS; 173 | try { 174 | String key = ConfigUtil.API_KEY; // key 175 | SortedMap packageParams = new TreeMap(); 176 | ConfigUtil.commonParams(packageParams); 177 | packageParams.put("out_trade_no", product.getOutTradeNo());// 商户订单号 178 | String sign = PayCommonUtil.createSign("UTF-8", packageParams, key); 179 | packageParams.put("sign", sign);// 签名 180 | String requestXML = PayCommonUtil.getRequestXml(packageParams); 181 | String resXml = HttpUtil.postData(ConfigUtil.CLOSE_ORDER_URL, 182 | requestXML); 183 | Map map = XMLUtil.doXMLParse(resXml); 184 | String returnCode = (String) map.get("return_code"); 185 | if ("SUCCESS".equals(returnCode)) { 186 | String resultCode = (String) map.get("result_code"); 187 | if ("SUCCESS".equals(resultCode)) { 188 | logger.info("订单号:{}微信关闭订单成功", product.getOutTradeNo()); 189 | } else { 190 | String errCode = (String) map.get("err_code"); 191 | String errCodeDes = (String) map.get("err_code_des"); 192 | if ("ORDERNOTEXIST".equals(errCode) 193 | || "ORDERCLOSED".equals(errCode)) {// 订单不存在或者已经关闭 194 | logger.info("订单号:{}微信关闭订单:{}", product.getOutTradeNo(), 195 | errCodeDes); 196 | } else { 197 | logger.info("订单号:{}微信关闭订单失败:{}", 198 | product.getOutTradeNo(), errCodeDes); 199 | message = Constants.FAIL; 200 | } 201 | } 202 | } else { 203 | String returnMsg = (String) map.get("return_msg"); 204 | logger.info("订单号:{}微信关闭订单失败:{}", product.getOutTradeNo(), 205 | returnMsg); 206 | message = Constants.FAIL; 207 | } 208 | } catch (Exception e) { 209 | logger.error("订单号:{}微信关闭订单失败(系统异常)", product.getOutTradeNo(), e); 210 | message = Constants.FAIL; 211 | } 212 | return message; 213 | } 214 | 215 | /** 216 | * 商户可以通过该接口下载历史交易清单。比如掉单、系统错误等导致商户侧和微信侧数据不一致,通过对账单核对后可校正支付状态。 注意: 217 | * 1、微信侧未成功下单的交易不会出现在对账单中。支付成功后撤销的交易会出现在对账单中,跟原支付单订单号一致,bill_type为REVOKED; 218 | * 2、微信在次日9点启动生成前一天的对账单,建议商户10点后再获取; 3、对账单中涉及金额的字段单位为“元”。 219 | * 220 | * 4、对账单接口只能下载三个月以内的账单。 221 | */ 222 | @SuppressWarnings("rawtypes") 223 | @Override 224 | public void saveBill() { 225 | try { 226 | String key = ConfigUtil.API_KEY; // key 227 | // 获取两天以前的账单 228 | // String billDate = DateUtil.getBeforeDayDate("2"); 229 | SortedMap packageParams = new TreeMap(); 230 | ConfigUtil.commonParams(packageParams);// 公用部分 231 | packageParams.put("bill_type", "ALL");// ALL,返回当日所有订单信息,默认值SUCCESS,返回当日成功支付的订单REFUND,返回当日退款订单 232 | // packageParams.put("tar_type", "GZIP");//压缩账单 233 | packageParams.put("bill_date", "20161206");// 账单日期 234 | String sign = PayCommonUtil.createSign("UTF-8", packageParams, key); 235 | packageParams.put("sign", sign);// 签名 236 | String requestXML = PayCommonUtil.getRequestXml(packageParams); 237 | String resXml = HttpUtil.postData(ConfigUtil.DOWNLOAD_BILL_URL, 238 | requestXML); 239 | if (resXml.startsWith("")) { 240 | Map map = XMLUtil.doXMLParse(resXml); 241 | String returnMsg = (String) map.get("return_msg"); 242 | logger.info("微信查询订单失败:{}", returnMsg); 243 | } else { 244 | // 入库 245 | } 246 | } catch (Exception e) { 247 | logger.error("微信查询订单异常", e); 248 | } 249 | 250 | } 251 | 252 | @Override 253 | public String weixinPayMobile(InfoDTO product) { 254 | StringBuffer url = new StringBuffer(); 255 | String totalFee = product.getTotalFee(); 256 | // redirect_uri 需要在微信支付端添加认证网址 257 | totalFee = CommonUtil.subZeroAndDot(totalFee); 258 | url.append("http://open.weixin.qq.com/connect/oauth2/authorize?"); 259 | url.append("appid=" + ConfigUtil.APP_ID); 260 | url.append("&redirect_uri=" + server_url + "weixinMobile/dopay?"); 261 | url.append("outTradeNo=" + product.getOutTradeNo() + "&totalFee=" 262 | + totalFee); 263 | url.append("&response_type=code&scope=snsapi_base&state="); 264 | url.append("#wechat_redirect"); 265 | return url.toString(); 266 | } 267 | 268 | @SuppressWarnings("rawtypes") 269 | @Override 270 | public String weixinPayH5(InfoDTO product) { 271 | logger.info("订单号:{}发起H5支付", product.getOutTradeNo()); 272 | String mweb_url = ""; 273 | try { 274 | // 账号信息 275 | String key = ConfigUtil.API_KEY; // key 276 | String trade_type = "MWEB";// 交易类型 H5 支付 277 | SortedMap packageParams = new TreeMap(); 278 | ConfigUtil.commonParams(packageParams); 279 | packageParams.put("product_id", product.getProductId());// 商品ID 280 | packageParams.put("body", product.getBody());// 商品描述 281 | packageParams.put("out_trade_no", product.getOutTradeNo());// 商户订单号 282 | String totalFee = product.getTotalFee(); 283 | totalFee = CommonUtil.subZeroAndDot(totalFee); 284 | packageParams.put("total_fee", totalFee);// 总金额 285 | // H5支付要求商户在统一下单接口中上传用户真实ip地址 spbill_create_ip 286 | packageParams.put("spbill_create_ip", product.getSpbillCreateIp());// 发起人IP地址 287 | packageParams.put("notify_url", notify_url);// 回调地址 288 | packageParams.put("trade_type", trade_type);// 交易类型 289 | // H5支付专用 290 | JSONObject value = new JSONObject(); 291 | value.put("type", "WAP"); 292 | value.put("wap_url", "https://blog.52itstyle.com");// //WAP网站URL地址 293 | value.put("wap_name", "科帮网充值");// WAP 网站名 294 | JSONObject scene_info = new JSONObject(); 295 | scene_info.put("h5_info", value); 296 | packageParams.put("scene_info", scene_info.toString()); 297 | 298 | String sign = PayCommonUtil.createSign("UTF-8", packageParams, key); 299 | packageParams.put("sign", sign);// 签名 300 | 301 | String requestXML = PayCommonUtil.getRequestXml(packageParams); 302 | String resXml = HttpUtil.postData(ConfigUtil.UNIFIED_ORDER_URL, 303 | requestXML); 304 | Map map = XMLUtil.doXMLParse(resXml); 305 | String returnCode = (String) map.get("return_code"); 306 | if ("SUCCESS".equals(returnCode)) { 307 | String resultCode = (String) map.get("result_code"); 308 | if ("SUCCESS".equals(resultCode)) { 309 | logger.info("订单号:{}发起H5支付成功", product.getOutTradeNo()); 310 | mweb_url = (String) map.get("mweb_url"); 311 | } else { 312 | String errCodeDes = (String) map.get("err_code_des"); 313 | logger.info("订单号:{}发起H5支付(系统)失败:{}", 314 | product.getOutTradeNo(), errCodeDes); 315 | } 316 | } else { 317 | String returnMsg = (String) map.get("return_msg"); 318 | logger.info("(订单号:{}发起H5支付(通信)失败:{}", product.getOutTradeNo(), 319 | returnMsg); 320 | } 321 | } catch (Exception e) { 322 | logger.error("订单号:{}发起H5支付失败(系统异常))", product.getOutTradeNo(), e); 323 | } 324 | return mweb_url; 325 | } 326 | } 327 | -------------------------------------------------------------------------------- /src/main/java/com/howie/pay/UnionpayUtils/SDKConstants.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Licensed Property to China UnionPay Co., Ltd. 4 | * 5 | * (C) Copyright of China UnionPay Co., Ltd. 2010 6 | * All Rights Reserved. 7 | * 8 | * 9 | * Modification History: 10 | * ============================================================================= 11 | * Author Date Description 12 | * ------------ ---------- --------------------------------------------------- 13 | * xshu 2014-05-28 MPI插件包常量定义 14 | * ============================================================================= 15 | */ 16 | package com.howie.pay.unionpayUtils; 17 | 18 | public class SDKConstants { 19 | 20 | public final static String COLUMN_DEFAULT = "-"; 21 | 22 | public final static String KEY_DELIMITER = "#"; 23 | 24 | /** memeber variable: blank. */ 25 | public static final String BLANK = ""; 26 | 27 | /** member variabel: space. */ 28 | public static final String SPACE = " "; 29 | 30 | /** memeber variable: unline. */ 31 | public static final String UNLINE = "_"; 32 | 33 | /** memeber varibale: star. */ 34 | public static final String STAR = "*"; 35 | 36 | /** memeber variable: line. */ 37 | public static final String LINE = "-"; 38 | 39 | /** memeber variable: add. */ 40 | public static final String ADD = "+"; 41 | 42 | /** memeber variable: colon. */ 43 | public final static String COLON = "|"; 44 | 45 | /** memeber variable: point. */ 46 | public final static String POINT = "."; 47 | 48 | /** memeber variable: comma. */ 49 | public final static String COMMA = ","; 50 | 51 | /** memeber variable: slash. */ 52 | public final static String SLASH = "/"; 53 | 54 | /** memeber variable: div. */ 55 | public final static String DIV = "/"; 56 | 57 | /** memeber variable: left . */ 58 | public final static String LB = "("; 59 | 60 | /** memeber variable: right. */ 61 | public final static String RB = ")"; 62 | 63 | /** memeber variable: rmb. */ 64 | public final static String CUR_RMB = "RMB"; 65 | 66 | /** memeber variable: .page size */ 67 | public static final int PAGE_SIZE = 10; 68 | 69 | /** memeber variable: String ONE. */ 70 | public static final String ONE = "1"; 71 | 72 | /** memeber variable: String ZERO. */ 73 | public static final String ZERO = "0"; 74 | 75 | /** memeber variable: number six. */ 76 | public static final int NUM_SIX = 6; 77 | 78 | /** memeber variable: equal mark. */ 79 | public static final String EQUAL = "="; 80 | 81 | /** memeber variable: operation ne. */ 82 | public static final String NE = "!="; 83 | 84 | /** memeber variable: operation le. */ 85 | public static final String LE = "<="; 86 | 87 | /** memeber variable: operation ge. */ 88 | public static final String GE = ">="; 89 | 90 | /** memeber variable: operation lt. */ 91 | public static final String LT = "<"; 92 | 93 | /** memeber variable: operation gt. */ 94 | public static final String GT = ">"; 95 | 96 | /** memeber variable: list separator. */ 97 | public static final String SEP = "./"; 98 | 99 | /** memeber variable: Y. */ 100 | public static final String Y = "Y"; 101 | 102 | /** memeber variable: AMPERSAND. */ 103 | public static final String AMPERSAND = "&"; 104 | 105 | /** memeber variable: SQL_LIKE_TAG. */ 106 | public static final String SQL_LIKE_TAG = "%"; 107 | 108 | /** memeber variable: @. */ 109 | public static final String MAIL = "@"; 110 | 111 | /** memeber variable: number zero. */ 112 | public static final int NZERO = 0; 113 | 114 | public static final String LEFT_BRACE = "{"; 115 | 116 | public static final String RIGHT_BRACE = "}"; 117 | 118 | /** memeber variable: string true. */ 119 | public static final String TRUE_STRING = "true"; 120 | /** memeber variable: string false. */ 121 | public static final String FALSE_STRING = "false"; 122 | 123 | /** memeber variable: forward success. */ 124 | public static final String SUCCESS = "success"; 125 | /** memeber variable: forward fail. */ 126 | public static final String FAIL = "fail"; 127 | /** memeber variable: global forward success. */ 128 | public static final String GLOBAL_SUCCESS = "$success"; 129 | /** memeber variable: global forward fail. */ 130 | public static final String GLOBAL_FAIL = "$fail"; 131 | 132 | public static final String UTF_8_ENCODING = "UTF-8"; 133 | public static final String GBK_ENCODING = "GBK"; 134 | public static final String CONTENT_TYPE = "Content-type"; 135 | public static final String APP_XML_TYPE = "application/xml;charset=utf-8"; 136 | public static final String APP_FORM_TYPE = "application/x-www-form-urlencoded;charset="; 137 | 138 | /******************************************** 5.0报文接口定义 ********************************************/ 139 | /** 版本号. */ 140 | public static final String param_version = "version"; 141 | /** 证书ID. */ 142 | public static final String param_certId = "certId"; 143 | /** 签名. */ 144 | public static final String param_signature = "signature"; 145 | /** 编码方式. */ 146 | public static final String param_encoding = "encoding"; 147 | /** 交易类型. */ 148 | public static final String param_txnType = "txnType"; 149 | /** 交易子类. */ 150 | public static final String param_txnSubType = "txnSubType"; 151 | /** 业务类型. */ 152 | public static final String param_bizType = "bizType"; 153 | /** 前台通知地址 . */ 154 | public static final String param_frontUrl = "frontUrl"; 155 | /** 后台通知地址. */ 156 | public static final String param_backUrl = "backUrl"; 157 | /** 接入类型. */ 158 | public static final String param_accessType = "accessType"; 159 | /** 收单机构代码. */ 160 | public static final String param_acqInsCode = "acqInsCode"; 161 | /** 商户类别. */ 162 | public static final String param_merCatCode = "merCatCode"; 163 | /** 商户类型. */ 164 | public static final String param_merType = "merType"; 165 | /** 商户代码. */ 166 | public static final String param_merId = "merId"; 167 | /** 商户名称. */ 168 | public static final String param_merName = "merName"; 169 | /** 商户简称. */ 170 | public static final String param_merAbbr = "merAbbr"; 171 | /** 二级商户代码. */ 172 | public static final String param_subMerId = "subMerId"; 173 | /** 二级商户名称. */ 174 | public static final String param_subMerName = "subMerName"; 175 | /** 二级商户简称. */ 176 | public static final String param_subMerAbbr = "subMerAbbr"; 177 | /** Cupsecure 商户代码. */ 178 | public static final String param_csMerId = "csMerId"; 179 | /** 商户订单号. */ 180 | public static final String param_orderId = "orderId"; 181 | /** 交易时间. */ 182 | public static final String param_txnTime = "txnTime"; 183 | /** 发送时间. */ 184 | public static final String param_txnSendTime = "txnSendTime"; 185 | /** 订单超时时间间隔. */ 186 | public static final String param_orderTimeoutInterval = "orderTimeoutInterval"; 187 | /** 支付超时时间. */ 188 | public static final String param_payTimeoutTime = "payTimeoutTime"; 189 | /** 默认支付方式. */ 190 | public static final String param_defaultPayType = "defaultPayType"; 191 | /** 支持支付方式. */ 192 | public static final String param_supPayType = "supPayType"; 193 | /** 支付方式. */ 194 | public static final String param_payType = "payType"; 195 | /** 自定义支付方式. */ 196 | public static final String param_customPayType = "customPayType"; 197 | /** 物流标识. */ 198 | public static final String param_shippingFlag = "shippingFlag"; 199 | /** 收货地址-国家. */ 200 | public static final String param_shippingCountryCode = "shippingCountryCode"; 201 | /** 收货地址-省. */ 202 | public static final String param_shippingProvinceCode = "shippingProvinceCode"; 203 | /** 收货地址-市. */ 204 | public static final String param_shippingCityCode = "shippingCityCode"; 205 | /** 收货地址-地区. */ 206 | public static final String param_shippingDistrictCode = "shippingDistrictCode"; 207 | /** 收货地址-详细. */ 208 | public static final String param_shippingStreet = "shippingStreet"; 209 | /** 商品总类. */ 210 | public static final String param_commodityCategory = "commodityCategory"; 211 | /** 商品名称. */ 212 | public static final String param_commodityName = "commodityName"; 213 | /** 商品URL. */ 214 | public static final String param_commodityUrl = "commodityUrl"; 215 | /** 商品单价. */ 216 | public static final String param_commodityUnitPrice = "commodityUnitPrice"; 217 | /** 商品数量. */ 218 | public static final String param_commodityQty = "commodityQty"; 219 | /** 是否预授权. */ 220 | public static final String param_isPreAuth = "isPreAuth"; 221 | /** 币种. */ 222 | public static final String param_currencyCode = "currencyCode"; 223 | /** 账户类型. */ 224 | public static final String param_accType = "accType"; 225 | /** 账号. */ 226 | public static final String param_accNo = "accNo"; 227 | /** 支付卡类型. */ 228 | public static final String param_payCardType = "payCardType"; 229 | /** 发卡机构代码. */ 230 | public static final String param_issInsCode = "issInsCode"; 231 | /** 持卡人信息. */ 232 | public static final String param_customerInfo = "customerInfo"; 233 | /** 交易金额. */ 234 | public static final String param_txnAmt = "txnAmt"; 235 | /** 余额. */ 236 | public static final String param_balance = "balance"; 237 | /** 地区代码. */ 238 | public static final String param_districtCode = "districtCode"; 239 | /** 附加地区代码. */ 240 | public static final String param_additionalDistrictCode = "additionalDistrictCode"; 241 | /** 账单类型. */ 242 | public static final String param_billType = "billType"; 243 | /** 账单号码. */ 244 | public static final String param_billNo = "billNo"; 245 | /** 账单月份. */ 246 | public static final String param_billMonth = "billMonth"; 247 | /** 账单查询要素. */ 248 | public static final String param_billQueryInfo = "billQueryInfo"; 249 | /** 账单详情. */ 250 | public static final String param_billDetailInfo = "billDetailInfo"; 251 | /** 账单金额. */ 252 | public static final String param_billAmt = "billAmt"; 253 | /** 账单金额符号. */ 254 | public static final String param_billAmtSign = "billAmtSign"; 255 | /** 绑定标识号. */ 256 | public static final String param_bindId = "bindId"; 257 | /** 风险级别. */ 258 | public static final String param_riskLevel = "riskLevel"; 259 | /** 绑定信息条数. */ 260 | public static final String param_bindInfoQty = "bindInfoQty"; 261 | /** 绑定信息集. */ 262 | public static final String param_bindInfoList = "bindInfoList"; 263 | /** 批次号. */ 264 | public static final String param_batchNo = "batchNo"; 265 | /** 总笔数. */ 266 | public static final String param_totalQty = "totalQty"; 267 | /** 总金额. */ 268 | public static final String param_totalAmt = "totalAmt"; 269 | /** 文件类型. */ 270 | public static final String param_fileType = "fileType"; 271 | /** 文件名称. */ 272 | public static final String param_fileName = "fileName"; 273 | /** 批量文件内容. */ 274 | public static final String param_fileContent = "fileContent"; 275 | /** 商户摘要. */ 276 | public static final String param_merNote = "merNote"; 277 | /** 商户自定义域. */ 278 | // public static final String param_merReserved = "merReserved";//接口变更删除 279 | /** 请求方保留域. */ 280 | public static final String param_reqReserved = "reqReserved";// 新增接口 281 | /** 保留域. */ 282 | public static final String param_reserved = "reserved"; 283 | /** 终端号. */ 284 | public static final String param_termId = "termId"; 285 | /** 终端类型. */ 286 | public static final String param_termType = "termType"; 287 | /** 交互模式. */ 288 | public static final String param_interactMode = "interactMode"; 289 | /** 发卡机构识别模式. */ 290 | // public static final String param_recognitionMode = "recognitionMode"; 291 | public static final String param_issuerIdentifyMode = "issuerIdentifyMode";// 接口名称变更 292 | /** 商户端用户号. */ 293 | public static final String param_merUserId = "merUserId"; 294 | /** 持卡人IP. */ 295 | public static final String param_customerIp = "customerIp"; 296 | /** 查询流水号. */ 297 | public static final String param_queryId = "queryId"; 298 | /** 原交易查询流水号. */ 299 | public static final String param_origQryId = "origQryId"; 300 | /** 系统跟踪号. */ 301 | public static final String param_traceNo = "traceNo"; 302 | /** 交易传输时间. */ 303 | public static final String param_traceTime = "traceTime"; 304 | /** 清算日期. */ 305 | public static final String param_settleDate = "settleDate"; 306 | /** 清算币种. */ 307 | public static final String param_settleCurrencyCode = "settleCurrencyCode"; 308 | /** 清算金额. */ 309 | public static final String param_settleAmt = "settleAmt"; 310 | /** 清算汇率. */ 311 | public static final String param_exchangeRate = "exchangeRate"; 312 | /** 兑换日期. */ 313 | public static final String param_exchangeDate = "exchangeDate"; 314 | /** 响应时间. */ 315 | public static final String param_respTime = "respTime"; 316 | /** 原交易应答码. */ 317 | public static final String param_origRespCode = "origRespCode"; 318 | /** 原交易应答信息. */ 319 | public static final String param_origRespMsg = "origRespMsg"; 320 | /** 应答码. */ 321 | public static final String param_respCode = "respCode"; 322 | /** 应答码信息. */ 323 | public static final String param_respMsg = "respMsg"; 324 | // 新增四个报文字段merUserRegDt merUserEmail checkFlag activateStatus 325 | /** 商户端用户注册时间. */ 326 | public static final String param_merUserRegDt = "merUserRegDt"; 327 | /** 商户端用户注册邮箱. */ 328 | public static final String param_merUserEmail = "merUserEmail"; 329 | /** 验证标识. */ 330 | public static final String param_checkFlag = "checkFlag"; 331 | /** 开通状态. */ 332 | public static final String param_activateStatus = "activateStatus"; 333 | /** 加密证书ID. */ 334 | public static final String param_encryptCertId = "encryptCertId"; 335 | /** 用户MAC、IMEI串号、SSID. */ 336 | public static final String param_userMac = "userMac"; 337 | /** 关联交易. */ 338 | // public static final String param_relationTxnType = "relationTxnType"; 339 | /** 短信类型 */ 340 | public static final String param_smsType = "smsType"; 341 | 342 | /** 风控信息域 */ 343 | public static final String param_riskCtrlInfo = "riskCtrlInfo"; 344 | 345 | /** IC卡交易信息域 */ 346 | public static final String param_ICTransData = "ICTransData"; 347 | 348 | /** VPC交易信息域 */ 349 | public static final String param_VPCTransData = "VPCTransData"; 350 | 351 | /** 安全类型 */ 352 | public static final String param_securityType = "securityType"; 353 | 354 | /** 银联订单号 */ 355 | public static final String param_tn = "tn"; 356 | 357 | /** 分期付款手续费率 */ 358 | public static final String param_instalRate = "instalRate"; 359 | 360 | /** 分期付款手续费率 */ 361 | public static final String param_mchntFeeSubsidy = "mchntFeeSubsidy"; 362 | 363 | 364 | } 365 | -------------------------------------------------------------------------------- /src/main/java/com/howie/pay/UnionpayUtils/SDKConfig.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Licensed Property to China UnionPay Co., Ltd. 4 | * 5 | * (C) Copyright of China UnionPay Co., Ltd. 2010 6 | * All Rights Reserved. 7 | * 8 | * 9 | * Modification History: 10 | * ============================================================================= 11 | * Author Date Description 12 | * ------------ ---------- --------------------------------------------------- 13 | * xshu 2014-05-28 MPI基本参数工具类 14 | * ============================================================================= 15 | */ 16 | package com.howie.pay.unionpayUtils; 17 | 18 | import java.io.BufferedReader; 19 | import java.io.File; 20 | import java.io.FileInputStream; 21 | import java.io.FileNotFoundException; 22 | import java.io.IOException; 23 | import java.io.InputStream; 24 | import java.io.InputStreamReader; 25 | import java.util.Properties; 26 | 27 | import org.apache.commons.lang.StringUtils; 28 | import org.slf4j.Logger; 29 | import org.slf4j.LoggerFactory; 30 | 31 | /** 32 | * 软件开发工具包 配制 33 | * 34 | */ 35 | public class SDKConfig { 36 | private static final Logger logger = LoggerFactory.getLogger(SDKUtil.class); 37 | /** 38 | * 重要:联调接入的时候请务必仔细阅读注释!!! 39 | * 40 | * 功能:从应用的classpath下加载acp_sdk.properties属性文件并将该属性文件中的键值对赋值到SDKConfig类中
41 | * 42 | */ 43 | public static final String FILE_NAME = "acp_sdk.properties"; 44 | 45 | /** 前台请求URL. */ 46 | private String frontRequestUrl; 47 | /** 后台请求URL. */ 48 | private String backRequestUrl; 49 | /** 单笔查询 */ 50 | private String singleQueryUrl; 51 | /** 批量查询 */ 52 | private String batchQueryUrl; 53 | /** 批量交易 */ 54 | private String batchTransUrl; 55 | /** 文件传输 */ 56 | private String fileTransUrl; 57 | /** 签名证书路径. */ 58 | private String signCertPath; 59 | /** 签名证书密码. */ 60 | private String signCertPwd; 61 | /** 签名证书类型. */ 62 | private String signCertType; 63 | /** 加密公钥证书路径. */ 64 | private String encryptCertPath; 65 | /** 验证签名公钥证书目录. */ 66 | private String validateCertDir; 67 | /** 按照商户代码读取指定签名证书目录. */ 68 | private String signCertDir; 69 | /** 磁道加密证书路径. */ 70 | private String encryptTrackCertPath; 71 | /** 磁道加密公钥模数. */ 72 | private String encryptTrackKeyModulus; 73 | /** 磁道加密公钥指数. */ 74 | private String encryptTrackKeyExponent; 75 | /** 有卡交易. */ 76 | private String cardRequestUrl; 77 | /** app交易 */ 78 | private String appRequestUrl; 79 | /** 证书使用模式(单证书/多证书) */ 80 | private String singleMode; 81 | 82 | /*缴费相关地址*/ 83 | private String jfFrontRequestUrl; 84 | private String jfBackRequestUrl; 85 | private String jfSingleQueryUrl; 86 | private String jfCardRequestUrl; 87 | private String jfAppRequestUrl; 88 | 89 | 90 | /** 配置文件中的前台URL常量. */ 91 | public static final String SDK_FRONT_URL = "acpsdk.frontTransUrl"; 92 | /** 配置文件中的后台URL常量. */ 93 | public static final String SDK_BACK_URL = "acpsdk.backTransUrl"; 94 | /** 配置文件中的单笔交易查询URL常量. */ 95 | public static final String SDK_SIGNQ_URL = "acpsdk.singleQueryUrl"; 96 | /** 配置文件中的批量交易查询URL常量. */ 97 | public static final String SDK_BATQ_URL = "acpsdk.batchQueryUrl"; 98 | /** 配置文件中的批量交易URL常量. */ 99 | public static final String SDK_BATTRANS_URL = "acpsdk.batchTransUrl"; 100 | /** 配置文件中的文件类交易URL常量. */ 101 | public static final String SDK_FILETRANS_URL = "acpsdk.fileTransUrl"; 102 | /** 配置文件中的有卡交易URL常量. */ 103 | public static final String SDK_CARD_URL = "acpsdk.cardTransUrl"; 104 | /** 配置文件中的app交易URL常量. */ 105 | public static final String SDK_APP_URL = "acpsdk.appTransUrl"; 106 | 107 | 108 | /** 以下缴费产品使用,其余产品用不到,无视即可 */ 109 | // 前台请求地址 110 | public static final String JF_SDK_FRONT_TRANS_URL= "acpsdk.jfFrontTransUrl"; 111 | // 后台请求地址 112 | public static final String JF_SDK_BACK_TRANS_URL="acpsdk.jfBackTransUrl"; 113 | // 单笔查询请求地址 114 | public static final String JF_SDK_SINGLE_QUERY_URL="acpsdk.jfSingleQueryUrl"; 115 | // 有卡交易地址 116 | public static final String JF_SDK_CARD_TRANS_URL="acpsdk.jfCardTransUrl"; 117 | // App交易地址 118 | public static final String JF_SDK_APP_TRANS_URL="acpsdk.jfAppTransUrl"; 119 | 120 | 121 | /** 配置文件中签名证书路径常量. */ 122 | public static final String SDK_SIGNCERT_PATH = "acpsdk.signCert.path"; 123 | /** 配置文件中签名证书密码常量. */ 124 | public static final String SDK_SIGNCERT_PWD = "acpsdk.signCert.pwd"; 125 | /** 配置文件中签名证书类型常量. */ 126 | public static final String SDK_SIGNCERT_TYPE = "acpsdk.signCert.type"; 127 | /** 配置文件中密码加密证书路径常量. */ 128 | public static final String SDK_ENCRYPTCERT_PATH = "acpsdk.encryptCert.path"; 129 | /** 配置文件中磁道加密证书路径常量. */ 130 | public static final String SDK_ENCRYPTTRACKCERT_PATH = "acpsdk.encryptTrackCert.path"; 131 | /** 配置文件中磁道加密公钥模数常量. */ 132 | public static final String SDK_ENCRYPTTRACKKEY_MODULUS = "acpsdk.encryptTrackKey.modulus"; 133 | /** 配置文件中磁道加密公钥指数常量. */ 134 | public static final String SDK_ENCRYPTTRACKKEY_EXPONENT = "acpsdk.encryptTrackKey.exponent"; 135 | /** 配置文件中验证签名证书目录常量. */ 136 | public static final String SDK_VALIDATECERT_DIR = "acpsdk.validateCert.dir"; 137 | 138 | /** 配置文件中是否加密cvn2常量. */ 139 | public static final String SDK_CVN_ENC = "acpsdk.cvn2.enc"; 140 | /** 配置文件中是否加密cvn2有效期常量. */ 141 | public static final String SDK_DATE_ENC = "acpsdk.date.enc"; 142 | /** 配置文件中是否加密卡号常量. */ 143 | public static final String SDK_PAN_ENC = "acpsdk.pan.enc"; 144 | /** 配置文件中证书使用模式 */ 145 | public static final String SDK_SINGLEMODE = "acpsdk.singleMode"; 146 | /** 操作对象. */ 147 | private static SDKConfig config; 148 | /** 属性文件对象. */ 149 | private Properties properties; 150 | 151 | 152 | /** 153 | * 获取config对象. 154 | * 155 | * @return 156 | */ 157 | public static SDKConfig getConfig() { 158 | if (null == config) { 159 | config = new SDKConfig(); 160 | } 161 | return config; 162 | } 163 | 164 | /** 165 | * 从properties文件加载 166 | * 167 | * @param rootPath 不包含文件名的目录. 168 | */ 169 | public void loadPropertiesFromPath(String rootPath) { 170 | if (StringUtils.isNotBlank(rootPath)) { 171 | File file = new File(rootPath + File.separator + FILE_NAME); 172 | InputStream in = null; 173 | if (file.exists()) { 174 | try { 175 | in = new FileInputStream(file); 176 | BufferedReader bf = new BufferedReader( 177 | new InputStreamReader(in, "utf-8")); 178 | properties = new Properties(); 179 | properties.load(bf); 180 | loadProperties(properties); 181 | } catch (FileNotFoundException e) { 182 | logger.error(e.getMessage(), e); 183 | } catch (IOException e) { 184 | logger.error(e.getMessage(), e); 185 | } finally { 186 | if (null != in) { 187 | try { 188 | in.close(); 189 | } catch (IOException e) { 190 | logger.error(e.getMessage(), e); 191 | } 192 | } 193 | } 194 | } else { 195 | // 由于此时可能还没有完成LOG的加载,因此采用标准输出来打印日志信息 196 | System.out.println(rootPath + FILE_NAME + "不存在,加载参数失败"); 197 | } 198 | } else { 199 | loadPropertiesFromSrc(); 200 | } 201 | 202 | } 203 | 204 | /** 205 | * 从classpath路径下加载配置参数 206 | */ 207 | public void loadPropertiesFromSrc() { 208 | InputStream in = null; 209 | try { 210 | // Properties pro = null; 211 | logger.info("从classpath: " +SDKConfig.class.getClassLoader().getResource("").getPath()+" 获取属性文件"+FILE_NAME); 212 | in = SDKConfig.class.getClassLoader() 213 | .getResourceAsStream(FILE_NAME); 214 | if (null != in) { 215 | BufferedReader bf = new BufferedReader(new InputStreamReader( 216 | in, "utf-8")); 217 | properties = new Properties(); 218 | try { 219 | properties.load(bf); 220 | } catch (IOException e) { 221 | throw e; 222 | } 223 | } else { 224 | logger.info(FILE_NAME + "属性文件未能在classpath指定的目录下 "+SDKConfig.class.getClassLoader().getResource("").getPath()+" 找到!"); 225 | return; 226 | } 227 | loadProperties(properties); 228 | } catch (IOException e) { 229 | logger.error(e.getMessage(), e); 230 | } finally { 231 | if (null != in) { 232 | try { 233 | in.close(); 234 | } catch (IOException e) { 235 | logger.error(e.getMessage(), e); 236 | } 237 | } 238 | } 239 | } 240 | 241 | /** 242 | * 根据传入的 {@link #load(java.util.Properties)}对象设置配置参数 243 | * 244 | * @param pro 245 | */ 246 | public void loadProperties(Properties pro) { 247 | logger.info("开始从属性文件中加载配置项"); 248 | String value = null; 249 | value = pro.getProperty(SDK_SINGLEMODE); 250 | if (SDKUtil.isEmpty(value) || SDKConstants.TRUE_STRING.equals(value)) { 251 | this.singleMode = SDKConstants.TRUE_STRING; 252 | logger.info("单证书模式,使用配置文件配置的私钥签名证书,SingleCertMode:[" + this.singleMode + "]"); 253 | // 单证书模式 254 | value = pro.getProperty(SDK_SIGNCERT_PATH); 255 | 256 | if (!SDKUtil.isEmpty(value)) { 257 | this.signCertPath = value.trim(); 258 | logger.info("配置项:私钥签名证书路径==>"+SDK_SIGNCERT_PATH +"==>"+ value+" 已加载"); 259 | } 260 | value = pro.getProperty(SDK_SIGNCERT_PWD); 261 | if (!SDKUtil.isEmpty(value)) { 262 | this.signCertPwd = value.trim(); 263 | logger.info("配置项:私钥签名证书密码==>"+SDK_SIGNCERT_PWD +" 已加载"); 264 | } 265 | value = pro.getProperty(SDK_SIGNCERT_TYPE); 266 | if (!SDKUtil.isEmpty(value)) { 267 | this.signCertType = value.trim(); 268 | logger.info("配置项:私钥签名证书类型==>"+SDK_SIGNCERT_TYPE +"==>"+ value+" 已加载"); 269 | } 270 | } else { 271 | // 多证书模式 272 | this.singleMode = SDKConstants.FALSE_STRING; 273 | logger.info("多证书模式,不需要加载配置文件中配置的私钥签名证书,SingleMode:[" + this.singleMode + "]"); 274 | } 275 | value = pro.getProperty(SDK_ENCRYPTCERT_PATH); 276 | if (!SDKUtil.isEmpty(value)) { 277 | this.encryptCertPath = value.trim(); 278 | logger.info("配置项:敏感信息加密证书==>"+SDK_ENCRYPTCERT_PATH +"==>"+ value+" 已加载"); 279 | } 280 | value = pro.getProperty(SDK_VALIDATECERT_DIR); 281 | if (!SDKUtil.isEmpty(value)) { 282 | this.validateCertDir = value.trim(); 283 | logger.info("配置项:验证签名证书路径(这里配置的是目录,不要指定到公钥文件)==>"+SDK_VALIDATECERT_DIR +"==>"+ value+" 已加载"); 284 | } 285 | value = pro.getProperty(SDK_FRONT_URL); 286 | if (!SDKUtil.isEmpty(value)) { 287 | this.frontRequestUrl = value.trim(); 288 | } 289 | value = pro.getProperty(SDK_BACK_URL); 290 | if (!SDKUtil.isEmpty(value)) { 291 | this.backRequestUrl = value.trim(); 292 | } 293 | value = pro.getProperty(SDK_BATQ_URL); 294 | if (!SDKUtil.isEmpty(value)) { 295 | this.batchQueryUrl = value.trim(); 296 | } 297 | value = pro.getProperty(SDK_BATTRANS_URL); 298 | if (!SDKUtil.isEmpty(value)) { 299 | this.batchTransUrl = value.trim(); 300 | } 301 | value = pro.getProperty(SDK_FILETRANS_URL); 302 | if (!SDKUtil.isEmpty(value)) { 303 | this.fileTransUrl = value.trim(); 304 | } 305 | value = pro.getProperty(SDK_SIGNQ_URL); 306 | if (!SDKUtil.isEmpty(value)) { 307 | this.singleQueryUrl = value.trim(); 308 | } 309 | value = pro.getProperty(SDK_CARD_URL); 310 | if (!SDKUtil.isEmpty(value)) { 311 | this.cardRequestUrl = value.trim(); 312 | } 313 | value = pro.getProperty(SDK_APP_URL); 314 | if (!SDKUtil.isEmpty(value)) { 315 | this.appRequestUrl = value.trim(); 316 | } 317 | value = pro.getProperty(SDK_ENCRYPTTRACKCERT_PATH); 318 | if (!SDKUtil.isEmpty(value)) { 319 | this.encryptTrackCertPath = value.trim(); 320 | } 321 | 322 | /**缴费部分**/ 323 | value = pro.getProperty(JF_SDK_FRONT_TRANS_URL); 324 | if (!SDKUtil.isEmpty(value)) { 325 | this.jfFrontRequestUrl = value.trim(); 326 | } 327 | 328 | value = pro.getProperty(JF_SDK_BACK_TRANS_URL); 329 | if (!SDKUtil.isEmpty(value)) { 330 | this.jfBackRequestUrl = value.trim(); 331 | } 332 | 333 | value = pro.getProperty(JF_SDK_SINGLE_QUERY_URL); 334 | if (!SDKUtil.isEmpty(value)) { 335 | this.jfSingleQueryUrl = value.trim(); 336 | } 337 | 338 | value = pro.getProperty(JF_SDK_CARD_TRANS_URL); 339 | if (!SDKUtil.isEmpty(value)) { 340 | this.jfCardRequestUrl = value.trim(); 341 | } 342 | 343 | value = pro.getProperty(JF_SDK_APP_TRANS_URL); 344 | if (!SDKUtil.isEmpty(value)) { 345 | this.jfAppRequestUrl = value.trim(); 346 | } 347 | 348 | value = pro.getProperty(SDK_ENCRYPTTRACKKEY_EXPONENT); 349 | if (!SDKUtil.isEmpty(value)) { 350 | this.encryptTrackKeyExponent = value.trim(); 351 | } 352 | 353 | value = pro.getProperty(SDK_ENCRYPTTRACKKEY_MODULUS); 354 | if (!SDKUtil.isEmpty(value)) { 355 | this.encryptTrackKeyModulus = value.trim(); 356 | } 357 | } 358 | 359 | 360 | public String getFrontRequestUrl() { 361 | return frontRequestUrl; 362 | } 363 | 364 | public void setFrontRequestUrl(String frontRequestUrl) { 365 | this.frontRequestUrl = frontRequestUrl; 366 | } 367 | 368 | public String getBackRequestUrl() { 369 | return backRequestUrl; 370 | } 371 | 372 | public void setBackRequestUrl(String backRequestUrl) { 373 | this.backRequestUrl = backRequestUrl; 374 | } 375 | 376 | public String getSignCertPath() { 377 | return signCertPath; 378 | } 379 | 380 | public void setSignCertPath(String signCertPath) { 381 | this.signCertPath = signCertPath; 382 | } 383 | 384 | public String getSignCertPwd() { 385 | return signCertPwd; 386 | } 387 | 388 | public void setSignCertPwd(String signCertPwd) { 389 | this.signCertPwd = signCertPwd; 390 | } 391 | 392 | public String getSignCertType() { 393 | return signCertType; 394 | } 395 | 396 | public void setSignCertType(String signCertType) { 397 | this.signCertType = signCertType; 398 | } 399 | 400 | public String getEncryptCertPath() { 401 | return encryptCertPath; 402 | } 403 | 404 | public void setEncryptCertPath(String encryptCertPath) { 405 | this.encryptCertPath = encryptCertPath; 406 | } 407 | 408 | public String getValidateCertDir() { 409 | return validateCertDir; 410 | } 411 | 412 | public void setValidateCertDir(String validateCertDir) { 413 | this.validateCertDir = validateCertDir; 414 | } 415 | 416 | public String getSingleQueryUrl() { 417 | return singleQueryUrl; 418 | } 419 | 420 | public void setSingleQueryUrl(String singleQueryUrl) { 421 | this.singleQueryUrl = singleQueryUrl; 422 | } 423 | 424 | public String getBatchQueryUrl() { 425 | return batchQueryUrl; 426 | } 427 | 428 | public void setBatchQueryUrl(String batchQueryUrl) { 429 | this.batchQueryUrl = batchQueryUrl; 430 | } 431 | 432 | public String getBatchTransUrl() { 433 | return batchTransUrl; 434 | } 435 | 436 | public void setBatchTransUrl(String batchTransUrl) { 437 | this.batchTransUrl = batchTransUrl; 438 | } 439 | 440 | public String getFileTransUrl() { 441 | return fileTransUrl; 442 | } 443 | 444 | public void setFileTransUrl(String fileTransUrl) { 445 | this.fileTransUrl = fileTransUrl; 446 | } 447 | 448 | public String getSignCertDir() { 449 | return signCertDir; 450 | } 451 | 452 | public void setSignCertDir(String signCertDir) { 453 | this.signCertDir = signCertDir; 454 | } 455 | 456 | public Properties getProperties() { 457 | return properties; 458 | } 459 | 460 | public void setProperties(Properties properties) { 461 | this.properties = properties; 462 | } 463 | 464 | public String getCardRequestUrl() { 465 | return cardRequestUrl; 466 | } 467 | 468 | public void setCardRequestUrl(String cardRequestUrl) { 469 | this.cardRequestUrl = cardRequestUrl; 470 | } 471 | 472 | public String getAppRequestUrl() { 473 | return appRequestUrl; 474 | } 475 | 476 | public void setAppRequestUrl(String appRequestUrl) { 477 | this.appRequestUrl = appRequestUrl; 478 | } 479 | 480 | public String getEncryptTrackCertPath() { 481 | return encryptTrackCertPath; 482 | } 483 | 484 | public void setEncryptTrackCertPath(String encryptTrackCertPath) { 485 | this.encryptTrackCertPath = encryptTrackCertPath; 486 | } 487 | 488 | public String getJfFrontRequestUrl() { 489 | return jfFrontRequestUrl; 490 | } 491 | 492 | public void setJfFrontRequestUrl(String jfFrontRequestUrl) { 493 | this.jfFrontRequestUrl = jfFrontRequestUrl; 494 | } 495 | 496 | public String getJfBackRequestUrl() { 497 | return jfBackRequestUrl; 498 | } 499 | 500 | public void setJfBackRequestUrl(String jfBackRequestUrl) { 501 | this.jfBackRequestUrl = jfBackRequestUrl; 502 | } 503 | 504 | public String getJfSingleQueryUrl() { 505 | return jfSingleQueryUrl; 506 | } 507 | 508 | public void setJfSingleQueryUrl(String jfSingleQueryUrl) { 509 | this.jfSingleQueryUrl = jfSingleQueryUrl; 510 | } 511 | 512 | public String getJfCardRequestUrl() { 513 | return jfCardRequestUrl; 514 | } 515 | 516 | public void setJfCardRequestUrl(String jfCardRequestUrl) { 517 | this.jfCardRequestUrl = jfCardRequestUrl; 518 | } 519 | 520 | public String getJfAppRequestUrl() { 521 | return jfAppRequestUrl; 522 | } 523 | 524 | public void setJfAppRequestUrl(String jfAppRequestUrl) { 525 | this.jfAppRequestUrl = jfAppRequestUrl; 526 | } 527 | 528 | public String getSingleMode() { 529 | return singleMode; 530 | } 531 | 532 | public void setSingleMode(String singleMode) { 533 | this.singleMode = singleMode; 534 | } 535 | 536 | public SDKConfig() { 537 | super(); 538 | } 539 | 540 | public String getEncryptTrackKeyExponent() { 541 | return encryptTrackKeyExponent; 542 | } 543 | 544 | public void setEncryptTrackKeyExponent(String encryptTrackKeyExponent) { 545 | this.encryptTrackKeyExponent = encryptTrackKeyExponent; 546 | } 547 | 548 | public String getEncryptTrackKeyModulus() { 549 | return encryptTrackKeyModulus; 550 | } 551 | 552 | public void setEncryptTrackKeyModulus(String encryptTrackKeyModulus) { 553 | this.encryptTrackKeyModulus = encryptTrackKeyModulus; 554 | } 555 | 556 | 557 | 558 | } 559 | --------------------------------------------------------------------------------