Forráskód Böngészése

完成微信支付回调

杨杰 1 éve
szülő
commit
a7ca2a3ecf

+ 36 - 7
src/main/java/com/llisoft/pay/controller/CallbackWxPayController.java

@@ -1,5 +1,6 @@
 package com.llisoft.pay.controller;
 
+import java.io.IOException;
 import java.nio.charset.StandardCharsets;
 
 import javax.servlet.http.HttpServletRequest;
@@ -7,13 +8,17 @@ import javax.servlet.http.HttpServletRequest;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpHeaders;
 import org.springframework.http.ResponseEntity;
 import org.springframework.stereotype.Controller;
 import org.springframework.util.StreamUtils;
+import org.springframework.web.bind.annotation.PathVariable;
 import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestHeader;
 import org.springframework.web.bind.annotation.RequestMapping;
 
-import com.llisoft.pay.service.PayWxV3Service;
+import com.llisoft.pay.service.ApiService;
+import com.wechat.pay.java.core.notification.RequestParam;
 
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
@@ -26,15 +31,39 @@ public class CallbackWxPayController {
 	private Logger logger = LoggerFactory.getLogger(CallbackWxPayController.class);
 
 	@Autowired
-	private PayWxV3Service payWxV3Service;
+	private ApiService apiService;
 	
 	
-	@ApiOperation(value="异步通知")
-	@PostMapping(value="/notify")
-	public ResponseEntity.BodyBuilder notify(HttpServletRequest request) throws Exception {
-		String xml = StreamUtils.copyToString(request.getInputStream(), StandardCharsets.UTF_8);
-		logger.debug("收到微信异步通知: {}", xml);
+	@ApiOperation(value="异步通知 栋科")
+	@PostMapping(value="/dongke/{pid}")
+	public ResponseEntity.BodyBuilder dongke(@PathVariable String pid, @RequestHeader HttpHeaders headers, HttpServletRequest request) throws Exception {
+		if(apiService.wxNotifyDongke(pid, this.notify(headers, request))) {
+			return ResponseEntity.ok();
+		}
 		return ResponseEntity.badRequest();
 	}
 	
+	@ApiOperation(value="异步通知 青谷")
+	@PostMapping(value="/qinggu/{pid}")
+	public ResponseEntity.BodyBuilder qinggu(@PathVariable String pid, @RequestHeader HttpHeaders headers, HttpServletRequest request) throws Exception {
+		if(apiService.wxNotifyDongke(pid, this.notify(headers, request))) {
+			return ResponseEntity.ok();
+		}
+		return ResponseEntity.badRequest();
+	}
+	
+	// https://github.com/wechatpay-apiv3/wechatpay-java/README.md
+	private RequestParam notify(@RequestHeader HttpHeaders headers, HttpServletRequest request) throws IOException {
+		String signature = headers.getFirst("Wechatpay-Signature"); // - HTTP 头 `Wechatpay-Signature`。应答的微信支付签名。
+		String serial = headers.getFirst("Wechatpay-Serial");		// - HTTP 头 `Wechatpay-Serial`。微信支付平台证书的序列号,验签必须使用序列号对应的微信支付平台证书。
+		String nonce = headers.getFirst("Wechatpay-Nonce");			// - HTTP 头 `Wechatpay-Nonce`。签名中的随机数。
+		String timestamp = headers.getFirst("Wechatpay-Timestamp"); // - HTTP 头 `Wechatpay-Timestamp`。签名中的时间戳。
+//		String signatureType = headers.getFirst("Wechatpay-Signature-Type"); // - HTTP 头 `Wechatpay-Signature-Type`。签名类型。
+		// **切记使用原始报文**,不要用 JSON 对象序列化后的字符串,避免验签的 body 和原文不一致。
+		String body = StreamUtils.copyToString(request.getInputStream(), StandardCharsets.UTF_8);
+		logger.debug("收到微信支付异步通知: {}", body);
+		return new RequestParam.Builder().serialNumber(signature)
+			.nonce(serial).signature(nonce).timestamp(timestamp).body(body).build();
+	}
+	
 }

+ 28 - 0
src/main/java/com/llisoft/pay/service/ApiService.java

@@ -15,6 +15,7 @@ import com.llisoft.pay.util.CodeUtil;
 import com.llisoft.pay.util.JsonUtil;
 import com.llisoft.pay.vo.ApiInfoResponseVo;
 import com.llisoft.pay.vo.ApiPayWxAppResponseVo;
+import com.wechat.pay.java.core.notification.RequestParam;
 
 @Service
 public class ApiService {
@@ -82,6 +83,33 @@ public class ApiService {
 		return ApiInfoResponseVo.Build(pid, payed);
 	}
 	
+	/**
+	 * 微信通知 栋科
+	 * @param pid
+	 * @param requestParam
+	 * @return
+	 * @throws Exception
+	 */
+	public boolean wxNotifyDongke(String pid, RequestParam requestParam) throws Exception {
+		if(!this.payed(pid) && payWxV3Service.notifyDongke(requestParam)) {
+			return payDao.update(pid, Constant.STATUS_PAYED, Constant.FLAG_NOTIFY);
+		}
+		return false;
+	}
+	
+	/**
+	 * 微信通知 青谷
+	 * @param pid
+	 * @param requestParam
+	 * @return
+	 * @throws Exception
+	 */
+	public boolean wxNotifyQinggu(String pid, RequestParam requestParam) throws Exception {
+		if(!this.payed(pid) && payWxV3Service.notifyQinggu(requestParam)) {
+			return payDao.update(pid, Constant.STATUS_PAYED, Constant.FLAG_NOTIFY);
+		}
+		return false;
+	}
 	
 
 	/**

+ 63 - 16
src/main/java/com/llisoft/pay/service/PayWxV3Service.java

@@ -13,6 +13,9 @@ import com.llisoft.pay.vo.ApiPayWxAppResponseVo;
 import com.llisoft.pay.vo.ApiPayWxJsApiResponseVo;
 import com.wechat.pay.java.core.Config;
 import com.wechat.pay.java.core.RSAAutoCertificateConfig;
+import com.wechat.pay.java.core.notification.NotificationConfig;
+import com.wechat.pay.java.core.notification.NotificationParser;
+import com.wechat.pay.java.core.notification.RequestParam;
 
 /**
  * 微信支付v3
@@ -29,9 +32,6 @@ public class PayWxV3Service {
 	@Value("${wxpay.keyPathQinggu}")
 	private String keyPathQinggu;
 	
-	private static String callbackWx = "/callback/wxpay/notify";
-	
-	
 	// 商户号
 	private static String mchidDongke = "1570392471";
 	private static String mchidQinggu = "1304201901";
@@ -49,6 +49,18 @@ public class PayWxV3Service {
 				.merchantSerialNumber("2BA51F3AB21E440FD129752829003952E765B1AE")
 				.apiV3Key("POIUYTREWQAS1q2w3e4r5t6y7u8i9o0p").build());
 	}
+	private static NotificationConfig notificationConfigDongke;
+	private static NotificationConfig notificationConfigQinggu;
+	private NotificationConfig getNotificationConfigDongke() {
+		return Objects.nonNull(notificationConfigDongke) ? notificationConfigDongke : (notificationConfigDongke = new RSAAutoCertificateConfig.Builder()
+				.merchantId(mchidDongke).privateKeyFromPath(keyPathDongke).merchantSerialNumber("").apiV3Key("").build());
+	}
+	private NotificationConfig getNotificationConfigQinggu() {
+		return Objects.nonNull(notificationConfigQinggu) ? notificationConfigQinggu : (notificationConfigQinggu = new RSAAutoCertificateConfig.Builder()
+				.merchantId(mchidQinggu).privateKeyFromPath(keyPathQinggu)
+				.merchantSerialNumber("2BA51F3AB21E440FD129752829003952E765B1AE")
+				.apiV3Key("POIUYTREWQAS1q2w3e4r5t6y7u8i9o0p").build());
+	}
 	
 	
 	/**
@@ -57,7 +69,7 @@ public class PayWxV3Service {
 	 * @throws Exception 
 	 */
 	public String pcDongke(String appid, String pid, int money, String body) throws Exception {
-		return this.pc(this.getConfigDongke(), appid, mchidDongke, pid, money, body);
+		return this.pc(this.getConfigDongke(), appid, mchidDongke, pid, money, body, domain+"/callback/wxpay/qinggu/"+pid);
 	}
 	
 	/**
@@ -66,7 +78,7 @@ public class PayWxV3Service {
 	 * @throws Exception 
 	 */
 	public String pcQinggu(String appid, String pid, int money, String body) throws Exception {
-		return this.pc(this.getConfigQinggu(), appid, mchidQinggu, pid, money, body);
+		return this.pc(this.getConfigQinggu(), appid, mchidQinggu, pid, money, body, domain+"/callback/wxpay/qinggu/"+pid);
 	}
 	
 	/**
@@ -75,7 +87,7 @@ public class PayWxV3Service {
 	 * @return 返回用于生成二维码的url
 	 * @throws Exception 
 	 */
-	private String pc(Config config, String appid, String mchid, String pid, int money, String body) throws Exception {
+	private String pc(Config config, String appid, String mchid, String pid, int money, String body, String notifyUrl) throws Exception {
 		com.wechat.pay.java.service.payments.nativepay.NativePayService service = new com.wechat.pay.java.service.payments.nativepay.NativePayService.Builder().config(config).build();
         com.wechat.pay.java.service.payments.nativepay.model.PrepayRequest request = new com.wechat.pay.java.service.payments.nativepay.model.PrepayRequest();
         com.wechat.pay.java.service.payments.nativepay.model.Amount amount = new com.wechat.pay.java.service.payments.nativepay.model.Amount();
@@ -85,7 +97,7 @@ public class PayWxV3Service {
         request.setMchid(mchid);
         request.setDescription(body);
         request.setOutTradeNo(pid);
-        request.setNotifyUrl(domain + callbackWx);
+        request.setNotifyUrl(notifyUrl);
         try {
         	logger.debug("微信Native支付下单请求参数: {}", JsonUtil.toJson(request));
         	com.wechat.pay.java.service.payments.nativepay.model.PrepayResponse response = service.prepay(request);
@@ -104,7 +116,7 @@ public class PayWxV3Service {
 	 * @throws Exception 
 	 */
 	public ApiPayWxAppResponseVo appDongke(String appid, String pid, int money, String body) throws Exception {
-		return this.app(this.getConfigDongke(), appid, mchidDongke, pid, money, body);
+		return this.app(this.getConfigDongke(), appid, mchidDongke, pid, money, body, domain+"/callback/wxpay/qinggu/"+pid);
 	}
 	
 	/**
@@ -113,14 +125,14 @@ public class PayWxV3Service {
 	 * @throws Exception 
 	 */
 	public ApiPayWxAppResponseVo appQinggu(String appid, String pid, int money, String body) throws Exception {
-		return this.app(this.getConfigQinggu(), appid, mchidQinggu, pid, money, body);
+		return this.app(this.getConfigQinggu(), appid, mchidQinggu, pid, money, body, domain+"/callback/wxpay/qinggu/"+pid);
 	}
 	
 	/**
 	 * App支付是指商户通过在移动端应用App中集成开放SDK调起微信支付模块来完成支付。目前微信支付支持手机系统有:IOS(苹果)、Android(安卓)和WP(Windows Phone)。
 	 * @return
 	 */
-	private ApiPayWxAppResponseVo app(Config config, String appid, String mchid, String pid, int money, String body) {
+	private ApiPayWxAppResponseVo app(Config config, String appid, String mchid, String pid, int money, String body, String notifyUrl) {
 		com.wechat.pay.java.service.payments.app.AppServiceExtension service = new com.wechat.pay.java.service.payments.app.AppServiceExtension.Builder().config(config).build();
 		com.wechat.pay.java.service.payments.app.model.PrepayRequest request = new com.wechat.pay.java.service.payments.app.model.PrepayRequest();
 		com.wechat.pay.java.service.payments.app.model.Amount amount = new com.wechat.pay.java.service.payments.app.model.Amount();
@@ -130,7 +142,7 @@ public class PayWxV3Service {
         request.setMchid(mchid);
         request.setDescription(body);
         request.setOutTradeNo(pid);
-        request.setNotifyUrl(domain + callbackWx);
+        request.setNotifyUrl(notifyUrl);
         try {
         	logger.debug("微信APP支付下单请求参数: {}", JsonUtil.toJson(request));
         	com.wechat.pay.java.service.payments.app.model.PrepayWithRequestPaymentResponse response = service.prepayWithRequestPayment(request);
@@ -152,7 +164,7 @@ public class PayWxV3Service {
 	 * @throws Exception 
 	 */
 	public ApiPayWxJsApiResponseVo jsapiDongke(String appid, String pid, int money, String body) throws Exception {
-		return this.jsapi(this.getConfigDongke(), appid, mchidDongke, pid, money, body);
+		return this.jsapi(this.getConfigDongke(), appid, mchidDongke, pid, money, body, domain+"/callback/wxpay/qinggu/"+pid);
 	}
 	
 	/**
@@ -161,14 +173,14 @@ public class PayWxV3Service {
 	 * @throws Exception 
 	 */
 	public ApiPayWxJsApiResponseVo jsapiQinggu(String appid, String pid, int money, String body) throws Exception {
-		return this.jsapi(this.getConfigQinggu(), appid, mchidQinggu, pid, money, body);
+		return this.jsapi(this.getConfigQinggu(), appid, mchidQinggu, pid, money, body, domain+"/callback/wxpay/qinggu/"+pid);
 	}
 	
 	/**
 	 * JSAPI支付是指商户通过调用微信支付提供的JSAPI接口,在支付场景中调起微信支付模块完成收款。
 	 * @return
 	 */
-	private ApiPayWxJsApiResponseVo jsapi(Config config, String appid, String mchid, String pid, int money, String body) {
+	private ApiPayWxJsApiResponseVo jsapi(Config config, String appid, String mchid, String pid, int money, String body, String notifyUrl) {
 		com.wechat.pay.java.service.payments.jsapi.JsapiServiceExtension service = new com.wechat.pay.java.service.payments.jsapi.JsapiServiceExtension.Builder().config(config).build();
 		com.wechat.pay.java.service.payments.jsapi.model.PrepayRequest request = new com.wechat.pay.java.service.payments.jsapi.model.PrepayRequest();
 		com.wechat.pay.java.service.payments.jsapi.model.Amount amount = new com.wechat.pay.java.service.payments.jsapi.model.Amount();
@@ -178,7 +190,7 @@ public class PayWxV3Service {
         request.setMchid(mchid);
         request.setDescription(body);
         request.setOutTradeNo(pid);
-        request.setNotifyUrl(domain + callbackWx);
+        request.setNotifyUrl(notifyUrl);
         try {
         	logger.debug("微信JSAPI支付下单请求参数: {}", JsonUtil.toJson(request));
         	com.wechat.pay.java.service.payments.jsapi.model.PrepayWithRequestPaymentResponse response = service.prepayWithRequestPayment(request);
@@ -216,7 +228,7 @@ public class PayWxV3Service {
 	 * @return
 	 * @throws Exception
 	 */
-	public boolean query(Config config, String mchid, String pid) throws Exception {
+	private boolean query(Config config, String mchid, String pid) throws Exception {
 		com.wechat.pay.java.service.payments.nativepay.NativePayService service = new com.wechat.pay.java.service.payments.nativepay.NativePayService.Builder().config(config).build();
 		com.wechat.pay.java.service.payments.nativepay.model.QueryOrderByOutTradeNoRequest request = new com.wechat.pay.java.service.payments.nativepay.model.QueryOrderByOutTradeNoRequest();
 		request.setMchid(mchid);
@@ -232,4 +244,39 @@ public class PayWxV3Service {
 		return false;
 	}
 	
+	
+	/**
+	 * 异步通知 栋科
+	 * @param requestParam
+	 * @return
+	 */
+	public boolean notifyDongke(RequestParam requestParam) {
+		return this.notify(this.getNotificationConfigDongke(), requestParam);
+	}
+	
+	/**
+	 * 异步通知 青谷
+	 * @param requestParam
+	 * @return
+	 */
+	public boolean notifyQinggu(RequestParam requestParam) {
+		return this.notify(this.getNotificationConfigQinggu(), requestParam);
+	}
+	
+	/**
+	 * 异步通知
+	 * @param requestParam
+	 * @return
+	 */
+	private boolean notify(NotificationConfig config, RequestParam requestParam) {
+		NotificationParser parser = new NotificationParser(config);
+		try { // 以支付通知回调为例,验签、解密并转换成 Transaction
+			com.wechat.pay.java.service.payments.model.Transaction transaction = parser.parse(requestParam, com.wechat.pay.java.service.payments.model.Transaction.class);
+			return transaction.getTradeState() == com.wechat.pay.java.service.payments.model.Transaction.TradeStateEnum.SUCCESS;
+		} catch (Exception exception) {
+			logger.error("处理微信支付异步通知失败", exception);
+		}
+		return false;
+	}
+	
 }