소스 검색

添加微信支付

杨杰 5 년 전
부모
커밋
87972ae53a

+ 5 - 7
pom.xml

@@ -13,7 +13,6 @@
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-parent</artifactId>
         <version>2.0.3.RELEASE</version>
-        <relativePath/> <!-- lookup parent from repository -->
     </parent>
 
     <properties>
@@ -25,6 +24,11 @@
 
 	<dependencies>
 		<dependency>
+			<groupId>com.llisoft</groupId>
+			<artifactId>mta-common</artifactId>
+			<version>3.9.2-SNAPSHOT</version>
+		</dependency>
+		<dependency>
 			<groupId>org.springframework.cloud</groupId>
 			<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
 		</dependency>
@@ -65,12 +69,6 @@
 			<artifactId>springfox-swagger-ui</artifactId>
 			<version>2.7.0</version>
 		</dependency>
-		
-		<dependency>
-			<groupId>com.llisoft</groupId>
-			<artifactId>mta-common</artifactId>
-			<version>0.0.1-SNAPSHOT</version>
-		</dependency>
 
 		<dependency>
 			<groupId>org.springframework.boot</groupId>

+ 4 - 2
src/main/java/com/llisoft/pay/service/PayService.java

@@ -92,8 +92,10 @@ public class PayService {
 			throw new MtaException("支付记录不存在: " + payNum);
 		}
 		Order order = orderService.get(item.getOrderId());
-		switch (item.getPayType()) {
-		case TYPE_ALI: // 支付宝
+		if (item.getPayType() == TYPE_ALI) { // 支付宝
+			return isMobile ? aliPayService.paym(payNum, order.getOrderMoney(), order.getOrderTitle()) : 
+				aliPayService.pay(payNum, order.getOrderMoney(), order.getOrderTitle());
+		}else if (item.getPayType() == TYPE_WX) { // 微信
 			return isMobile ? aliPayService.paym(payNum, order.getOrderMoney(), order.getOrderTitle()) : 
 				aliPayService.pay(payNum, order.getOrderMoney(), order.getOrderTitle());
 		}

+ 335 - 0
src/main/java/com/llisoft/pay/service/WxPayService.java

@@ -0,0 +1,335 @@
+package com.llisoft.pay.service;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+import org.apache.commons.codec.digest.DigestUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+
+import com.llisoft.common.util.HttpUtil;
+import com.llisoft.common.util.JsonUtil;
+import com.llisoft.common.util.MoneyUtil;
+import com.llisoft.common.util.XmlUtil;
+
+/**
+ * 微信支付
+ */
+@Service
+public class WxPayService {
+	
+	private Logger logger = LoggerFactory.getLogger(WxPayService.class);
+	
+	/** 微信接口 统一下单 */
+	public static final String WX_API_ORDER = "https://api.mch.weixin.qq.com/pay/unifiedorder";
+	/** 微信接口 查询订单 */
+	public static final String WX_API_ORDERQUERY = "https://api.mch.weixin.qq.com/pay/orderquery";
+	
+	@Value("${mta.domain}")
+	private String domain;
+	@Value("${mta.wx.appid}")
+	private String appid;
+	@Value("${mta.wx.secret}")
+	private String secret;
+	@Value("${mta.wx.mchid}")
+	private String mchid;
+	@Value("${mta.wx.mchkey}")
+	private String mchkey;
+	
+	@Autowired
+	private PayService payService;
+	
+	
+	/**
+	 * 支付
+	 * @param paynum
+	 * @param money
+	 * @param body
+	 * @return
+	 */
+	public String pay(String paynum, int money, String body) {
+		Map<String, String> paramMap = new HashMap<String ,String>();
+		paramMap.put("appid", appid);	// 公众账号ID
+		paramMap.put("mch_id", mchid);	// 商户号
+		paramMap.put("nonce_str", getNonceStr());				// 随机字符串
+		paramMap.put("out_trade_no", paynum);					// 商户订单号
+		paramMap.put("body", body);								// 商品描述
+		paramMap.put("total_fee", String.valueOf(money));		// 支付金额, 单位为【分】
+		paramMap.put("spbill_create_ip", "127.0.0.1"); 			// 终端IP, APP和网页支付提交用户端ip
+		paramMap.put("notify_url", domain+"/wxpay/notify");		// 通知地址
+		paramMap.put("trade_type", "NATIVE");	// 交易类型, 取值如下:JSAPI,NATIVE,APP
+		paramMap.put("sign", getSign(paramMap));	// 签名
+		String xmlParamString = mapToXml(paramMap);			// 封装xml格式参数字符串
+		logger.info("微信统一下单api请求参数: {}", xmlParamString);
+		String result = HttpUtil.postXml(WX_API_ORDER, xmlParamString);
+		logger.info("微信统一下单api返回结果: {}", result);
+		HashMap<String, String> resultMap = XmlUtil.toObject(result, HashMap.class, String.class, String.class);
+		if ("SUCCESS".equals(resultMap.get("return_code"))) {
+			return resultMap.get("code_url");
+		}
+		logger.error("微信统一下单api调用失败: {}", resultMap);
+		return null;
+	}
+	
+	/**
+	 * 支付 移动端
+	 * @param paynum
+	 * @param money
+	 * @param body
+	 * @return
+	 */
+	public JsApiPayConfig paym(String paynum, int money, String body, String openid) throws Exception {
+		Map<String, String> paramMap = new HashMap<String ,String>();
+		paramMap.put("appid", appid);	// 公众账号ID
+		paramMap.put("mch_id", mchid);	// 商户号
+		paramMap.put("nonce_str", getNonceStr());				// 随机字符串
+		paramMap.put("out_trade_no", paynum);					// 商户订单号
+		paramMap.put("body", body);								// 商品描述
+		paramMap.put("total_fee", String.valueOf(money));		// 支付金额, 单位为【分】
+		paramMap.put("spbill_create_ip", "127.0.0.1"); 			// 终端IP, APP和网页支付提交用户端ip
+		paramMap.put("notify_url", domain+"/wxpay/notify");		// 通知地址
+		paramMap.put("trade_type", "JSAPI");	// 交易类型, 取值如下:JSAPI,NATIVE,APP
+		paramMap.put("openid", openid);	// 用户标识, trade_type=JSAPI,此参数必传,用户在商户appid下的唯一标识。
+		paramMap.put("sign", getSign(paramMap));	// 签名
+		String xmlParamString = mapToXml(paramMap);			// 封装xml格式参数字符串
+		logger.info("微信统一下单api请求参数: {}", xmlParamString);
+		String result = HttpUtil.postXml(WX_API_ORDER, xmlParamString);
+		logger.info("微信统一下单api返回结果: {}", result);
+		HashMap<String, String> resultMap = XmlUtil.toObject(result, HashMap.class, String.class, String.class);
+		if ("SUCCESS".equals(resultMap.get("return_code"))) {
+			String prepayid = resultMap.get("prepay_id");
+			if (prepayid!=null && !prepayid.trim().isEmpty()) {
+				// 微信js支付参数
+				paramMap = new HashMap<String, String>();
+				paramMap.put("appId", appid);
+				paramMap.put("nonceStr", getNonceStr());
+				paramMap.put("timeStamp", getTimeStamp());
+				paramMap.put("package", "prepay_id=" + prepayid);
+				paramMap.put("signType", "MD5");
+				paramMap.put("paySign", getSign(paramMap));
+				paramMap.put("packageStr", paramMap.get("package")); // 解决java中package关键字问题
+				JsApiPayConfig jsApiPayConfig = JsonUtil.toObject(JsonUtil.toJson(paramMap), JsApiPayConfig.class);
+				// 微信jsapi初始化参数 // TODO 未完待续,需要集成微信公众号网页授权
+//				String result = HttpUtil.get(authurl + "/jsapi/config");
+//				logger.info("调用微信授权服务: 获取jsapi初始化参数: {}", result);
+//				jsApiPayConfig.setInitConfig(JsonUtil.toObject(result, JsApiConfig.class));
+//				logger.info("返回微信js支付需要的参数信息: {}", JsonUtil.toJson(jsApiPayConfig));
+				return jsApiPayConfig;
+			}
+		}
+		logger.error("微信统一下单api调用失败: {}", resultMap);
+		return null;
+	}
+	
+	/**
+	 * 查询
+	 * @param paynum
+	 * @return
+	 */
+	public Map<String, String> query(String paynum) {
+		Map<String, String> paramMap = new HashMap<String ,String>();
+		paramMap.put("appid", appid);				// 公众账号ID
+		paramMap.put("mch_id", mchid);			// 商户号
+		paramMap.put("nonce_str", getNonceStr());		// 随机字符串
+		paramMap.put("out_trade_no", paynum);			// 商户订单号
+		paramMap.put("sign", getSign(paramMap));	// 签名
+		// 封装xml格式参数字符串
+		String xmlParamString = mapToXml(paramMap);
+		logger.info("微信订单查询api请求参数: {}", xmlParamString);
+		String result = HttpUtil.postXml(WX_API_ORDERQUERY, xmlParamString);
+		logger.info("微信订单查询api返回结果: {}", result);
+		Map<String, String> resultMap = XmlUtil.toObject(result, HashMap.class, String.class, String.class);
+		return resultMap;
+	}
+	
+	/**
+	 * 处理微信支付返回结果
+	 * @param resultMap
+	 * @return
+	 * @author YangJie
+	 * @throws Exception 
+	 * @throws Throwable 
+	 * @createTime 2015年3月31日 下午5:36:27
+	 */
+	public boolean doResult(Map<String, String> resultMap, byte payFlag) throws Exception {
+		// 判断通信标识
+		if (!"SUCCESS".equals(resultMap.get("return_code"))) {
+			logger.error("微信支付处理: 通信标识异常: {}", resultMap.get("return_code"));
+			return false;
+		}
+		// 验证签名
+		if (!getSign(resultMap).equals(resultMap.get("sign"))) {
+			logger.error("微信支付处理: 签名验证失败: {}", resultMap.get("sign"));
+			return false;
+		}
+		// 判断交易状态
+		if (!"SUCCESS".equals(resultMap.get("result_code"))) {
+			logger.error("微信支付处理: 交易状态异常: {}", resultMap.get("result_code"));
+			return false;
+		}
+		// 完成支付状态
+		String paynum = resultMap.get("out_trade_no"); // paynum
+		String tradenum = resultMap.get("trade_no"); // 支付宝交易号
+		int totalAmount = MoneyUtil.toFen(resultMap.get("total_amount"));	// 实际支付金额
+		return payService.finish(paynum, tradenum, totalAmount, PayService.FLAG_NOTIFY);
+	}
+	
+	
+	/**
+	 * 获取随机字符串
+	 * @return 32字符以内
+	 * @author YangJie
+	 * @createTime 2015年3月26日 下午5:12:47
+	 */
+	private String getNonceStr(){
+		return UUID.randomUUID().toString().replace("-", "");
+	}
+	
+	/**
+	 * 获取时间戳
+	 * @return 精确到秒
+	 * @author YangJie
+	 * @createTime 2015年3月26日 下午5:12:47
+	 */
+	private String getTimeStamp(){
+		return String.valueOf(System.currentTimeMillis()/1000);
+	}
+	
+	/**
+	 * 获取微信认证签名(md5)
+	 * @param paramMap
+	 * @author YangJie
+	 * @createTime 2015年3月26日 下午2:48:17
+	 */
+	private String getSign(Map<String, String> paramMap) {
+        List<String> keyList = new ArrayList<String>(paramMap.keySet());	// 获取参数key
+        Collections.sort(keyList);	// key 排序
+        StringBuffer paramBuffer = new StringBuffer();
+        String value = null;
+        for (String key : keyList) {	// 循环封装非空参数
+        	value = paramMap.get(key);
+        	if (key != null && !key.equals("sign") && value!=null && !value.isEmpty()) {
+        		paramBuffer.append(key).append("=").append(value).append("&");
+        	}
+        } // 添加私钥
+        paramBuffer.append("key=").append(mchkey);   
+        return DigestUtils.md5Hex(paramBuffer.toString()).toUpperCase();
+    }
+	
+	/**
+	 * 获取xml格式参数字符串
+	 * @param paramMap
+	 * @return
+	 * @author YangJie
+	 * @createTime 2015年3月26日 下午3:06:26
+	 */
+	private String mapToXml(Map<String, String> paramMap) {
+		StringBuilder xmlBuilder = new StringBuilder("<xml>");
+		for (String key : paramMap.keySet()) {
+			if (key != null && paramMap.get(key)!=null && !paramMap.get(key).isEmpty()) {
+				xmlBuilder.append("<").append(key).append(">").append(paramMap.get(key))
+					.append("</").append(key).append(">");
+			}
+		}
+		xmlBuilder.append("</xml>");
+		return xmlBuilder.toString();
+	}
+	
+	/**
+	 * 微信js api 初始化参数
+	 */
+	public class JsApiConfig{
+		private String appId;
+		private String timestamp;
+		private String nonceStr;
+		private String signature;
+		
+		public String getAppId() {
+			return appId;
+		}
+		public void setAppId(String appId) {
+			this.appId = appId;
+		}
+		public String getTimestamp() {
+			return timestamp;
+		}
+		public void setTimestamp(String timestamp) {
+			this.timestamp = timestamp;
+		}
+		public String getNonceStr() {
+			return nonceStr;
+		}
+		public void setNonceStr(String nonceStr) {
+			this.nonceStr = nonceStr;
+		}
+		public String getSignature() {
+			return signature;
+		}
+		public void setSignature(String signature) {
+			this.signature = signature;
+		}
+	}
+	
+	/**
+	 * 微信js api 支付参数
+	 */
+	public class JsApiPayConfig{
+		private String appId;
+		private String timeStamp;
+		private String nonceStr;
+		private String packageStr;
+		private String signType;
+		private String paySign;
+		private JsApiConfig initConfig;
+		
+		public String getAppId() {
+			return appId;
+		}
+		public void setAppId(String appId) {
+			this.appId = appId;
+		}
+		public String getTimeStamp() {
+			return timeStamp;
+		}
+		public void setTimeStamp(String timeStamp) {
+			this.timeStamp = timeStamp;
+		}
+		public String getNonceStr() {
+			return nonceStr;
+		}
+		public void setNonceStr(String nonceStr) {
+			this.nonceStr = nonceStr;
+		}
+		public String getPackageStr() {
+			return packageStr;
+		}
+		public void setPackageStr(String packageStr) {
+			this.packageStr = packageStr;
+		}
+		public String getSignType() {
+			return signType;
+		}
+		public void setSignType(String signType) {
+			this.signType = signType;
+		}
+		public String getPaySign() {
+			return paySign;
+		}
+		public void setPaySign(String paySign) {
+			this.paySign = paySign;
+		}
+		public JsApiConfig getInitConfig() {
+			return initConfig;
+		}
+		public void setInitConfig(JsApiConfig initConfig) {
+			this.initConfig = initConfig;
+		}
+	}
+}

+ 3 - 3
src/main/java/com/llisoft/pay/service/feign/FeignMailService.java

@@ -3,13 +3,13 @@ package com.llisoft.pay.service.feign;
 import org.springframework.cloud.openfeign.FeignClient;
 import org.springframework.web.bind.annotation.PostMapping;
 
-import com.llisoft.common.vo.MailSendVo;
+import com.llisoft.common.vo.feign.FeignMailSendRequestVo;
 
 // 通过@FeignClient注解来指定服务名进而绑定服务
-@FeignClient(value="mta-service-sms", fallback=FeignMailServiceHystric.class)
+@FeignClient("mta-service-sms")
 public interface FeignMailService {
 
 	@PostMapping("/feign/mail/send")
-	public void send(MailSendVo mailSendVo);
+	public void send(FeignMailSendRequestVo requestVo);
 	
 }

+ 0 - 13
src/main/java/com/llisoft/pay/service/feign/FeignMailServiceHystric.java

@@ -1,13 +0,0 @@
-package com.llisoft.pay.service.feign;
-
-import org.springframework.stereotype.Component;
-
-import com.llisoft.common.vo.MailSendVo;
-
-@Component // 配置断路器
-public class FeignMailServiceHystric implements FeignMailService{
-
-	@Override
-	public void send(MailSendVo mailSendVo) {}
-	
-}

+ 6 - 1
src/main/resources/application.yml

@@ -1,12 +1,17 @@
 ##自定义配置
 mta: 
   debug: true #是否测试
-  domain: http://localhost  #本工程域名(用于支付回调 需要在支付宝开放平台配置)
+  domain: http://localhost  #本工程域名(用于支付回调 需要在支付宝开放平台和微信商户平台配置)
   ali: #支付宝
     appid: 2016080300154811
     serverUrl: https://openapi.alipaydev.com/gateway.do
     publicKey: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuHV05fB6aafE1lMvFtU/eawP0s4uFa2zZThPFGx80l1JZNWaMfQoa8+Gu8reRc/PRnx1N9syTc/zeZYZUfstPn22y8qWPLYwqxyJH53knCj9263M8P6Q4n+MF7pUVUQbkn0FTlyo8eWZlpCOJe9MDTTIhPdIMlrDn31HiFZ8TY0pySh5tEZzhYIaaJjmvQUdQuCWMwdq9uzVtHu++QFmbHCApZbekJcQ1ebaGv76DOiGpPPtlH3mrSGqR/1z0uVwC5iRn5vuax/73a/OPvQGHOVTBq2QBiZHRf23DPO7SMuCpampEVaXIwIW2weCfNnvLCWMQ+v0dxpOVIUx1BlLjwIDAQAB
     privateKey: MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCZ2eFSd0f+SC3Djep7v3PwNludUmrPaiJNf6Wf3+5XVIVQGeomsFCIF1qS3hecj6x9k/JEayxQTDjMnwADZLMUcqsMm/OuOa+36kzSg/yFTs2Jq23GjjsHI6/g4ioj9inl9t4QfimBN6RczGzLWBUe1trhmNICFVJqbPomUgz19ey8uCaMbttIaHi8yNLTflFEHysl9LZSmNNBlz50MtLhIc77/UKbah2ot3xllHnVnvz0jXVTdMqIP//H4Oitcucy018EcMctgvdmFNG5VIste6yOmd+uox82QyFRRAngmw+66EpYHzmbwKpGHD0KKi/35ti4IGA+uoXxY50Xcm+tAgMBAAECggEACxvMRSQPsrCLfV812xGL0YBbRRS6WZeUOnxI/S4yC2Qy7AGzAnAgoHLvO0OF+Ov2zGpuR7plTLb0MvIYEianN4bACv0Hr0DDC0iDm3+kMAvsk1ApcPi8ElvlSxuJZ2dSxyP4DMDPRmE6IOkxwczuWXex/jAGmdfoR37u4O1zuh6Wu+JpKbYfTiwmhw0OAJBq1gm1wiTN9thDr1Qf4Ifr/k0Mv9TVnOWQLAg9qGGfQHEU/UtU92Rlp4S2WB2UMl8gpq0ZgC7brxeegS7OW2uv8LtTrdekhVzC62zyE+V47xQ1hukAv06Sw9+XgjI0fLHS3xxNCUOVWqwiOnhluJjHgQKBgQDinyhksk7pHKra9ryy03hGTPZt2LYGNCogfdIuG8ShNrXva6MV4TuGcgjcJUNed+w3QW/F9C/kRUP7VQvEL/j/1lfKy4FRWvui7Mr9LyouaTlANXyVc0KzoS3u3NIy5eKTOE0QrUNs7G4x16R84Gsx3GQOGUhmFi+WPhQ0SqkiiQKBgQCty7NoCjQ7g6DMIE7vcGuRAACAEZi0P9qLZvHmwujdJlyPctYX2/wcpT1bnn1FqyavSDKNrtSIIfa5GNCUzcv4ysBiYZlXNGDw4wleQ/ENG/DRj0A4AAOR3/bR5TaAY9QnFInuj2mlKvmQWuYvc6EnLDfo1OjuWc0bDkWpO6nrBQKBgQCzJ6OlR5k+jJ09kUXIEYnJ+dnvKR1tdhu/p/ha3zTpRfk6l1tMVszaEpiSgRrrOd8SJ7cXRV3/FgsSTD+LD2nHx0mMVqmbCVRZjt1CnuC35BOQgThGZeJbY5aOeR/rgPVH8YBrKK5fE+JhoNq2pivYNSUcSDyGCBACtH0Age2IEQKBgQCQMg1VSfoSUuDL3BaB9O1abfz8RR1EmCIUPUKBKsAYFKcBYc8eFqgzgCnLZVEcx6ceETHYefDeTgethVgxzhno/xflyIJ4Zv3zfvub2ZUbQk5pIIiwrdpIYuEfPyUcGze/gPv8EcMehexwB+sp/a78mGR+6n+kmp9hTlMf2V/enQKBgQDPH5exzUjktHYwlEhEsXOGCUh1gs7KI5d7FcWWubZiWgz3I2F4iVMW9yd1godxr0XUJMuWJqft0c0A/ayPDOCRwxvnxMDEB9qhA4hdfyBdUYKU4sDxn8NmT7/srMbSUyQIzbprA6UPg4Hh68nunvMwf/vX8VK4641y+W3rYYyDjw==
+  wx: #微信支付
+    appid: wx42fe30efc65cf74b
+    secret: ff241718fc4d61919f45734411aaa9f6
+    mchid: 1570392471
+    mchkey: 1q2w3e4r5t6y7u8i9o0pPOIUYTREWQAS
   
 ##端口配置
 server.port: 8003 #监听端口

+ 8 - 0
src/test/java/com/llisoft/pay/ServiceTest.java

@@ -2,16 +2,24 @@ package com.llisoft.pay;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.boot.test.context.SpringBootTest;
 import org.springframework.test.context.junit4.SpringRunner;
 
+import com.llisoft.common.util.CodeUtil;
+import com.llisoft.pay.service.WxPayService;
+
 @SpringBootTest
 @RunWith(SpringRunner.class)
 public class ServiceTest {
 	
+	@Autowired
+	WxPayService wxPayService;
+	
 	@Test
 	public void test() throws Exception{
 		long begin = System.currentTimeMillis();
+		System.out.println(wxPayService.pay(CodeUtil.time(), 1, "测试"));
 		System.out.println("耗时:"+ (System.currentTimeMillis() - begin));
 	}