一:集成步驟
1.引入依賴:
<dependency>
<groupId>com.github.wxpay</groupId>
<artifactId>wxpay-sdk</artifactId>
<version>0.0.3</version>
</dependency>
2.微信app支付參數配置:
#服務器域名地址
server.service-domain = http://127.0.0.1:8080
#微信app支付
pay.wxpay.app.appID = "你的appid"
pay.wxpay.app.mchID = "你的商戶id"
pay.wxpay.app.key = "你的api秘鑰,不是appSecret"
#從微信商戶平台下載的安全證書存放的路徑、我放在resources下面,切記一定要看看target目錄下的class文件下有沒有打包apiclient_cert.p12文件
pay.wxpay.app.certPath = static/cert/wxpay/apiclient_cert.p12
#微信支付成功的異步通知接口
pay.wxpay.app.payNotifyUrl=${server.service-domain}/api/wxPay/notify
3.定義配置類:
package com.annaru.upms.payment.config;
import com.github.wxpay.sdk.WXPayConfig;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.io.InputStream;
/**
* 配置我們自己的信息
*/
@Component
@ConfigurationProperties(prefix = "pay.wxpay.app")
public class WxPayAppConfig implements WXPayConfig {
/**
* appID
*/
private String appID;
/**
* 商戶號
*/
private String mchID;
/**
* API 密鑰
*/
private String key;
/**
* API證書絕對路徑 (本項目放在了 resources/cert/wxpay/apiclient_cert.p12")
*/
private String certPath;
/**
* HTTP(S) 連接超時時間,單位毫秒
*/
private int httpConnectTimeoutMs = 8000;
/**
* HTTP(S) 讀數據超時時間,單位毫秒
*/
private int httpReadTimeoutMs = 10000;
/**
* 微信支付異步通知地址
*/
private String payNotifyUrl;
/**
* 微信退款異步通知地址
*/
private String refundNotifyUrl;
/**
* 獲取商戶證書內容(這里證書需要到微信商戶平台進行下載)
*
* @return 商戶證書內容
*/
@Override
public InputStream getCertStream() {
InputStream certStream =getClass().getClassLoader().getResourceAsStream(certPath);
return certStream;
}
public String getAppID() {
return appID;
}
public void setAppID(String appID) {
this.appID = appID;
}
public String getMchID() {
return mchID;
}
public void setMchID(String mchID) {
this.mchID = mchID;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getCertPath() {
return certPath;
}
public void setCertPath(String certPath) {
this.certPath = certPath;
}
public int getHttpConnectTimeoutMs() {
return httpConnectTimeoutMs;
}
public void setHttpConnectTimeoutMs(int httpConnectTimeoutMs) {
this.httpConnectTimeoutMs = httpConnectTimeoutMs;
}
public int getHttpReadTimeoutMs() {
return httpReadTimeoutMs;
}
public void setHttpReadTimeoutMs(int httpReadTimeoutMs) {
this.httpReadTimeoutMs = httpReadTimeoutMs;
}
public String getPayNotifyUrl() {
return payNotifyUrl;
}
public void setPayNotifyUrl(String payNotifyUrl) {
this.payNotifyUrl = payNotifyUrl;
}
public String getRefundNotifyUrl() {
return refundNotifyUrl;
}
public void setRefundNotifyUrl(String refundNotifyUrl) {
this.refundNotifyUrl = refundNotifyUrl;
}
}
4. 定義controller:
在調用微信服務接口進行統一下單之前,
1、為保證安全性,建議驗證數據庫是否存在訂單號對應的訂單。
package com.annaru.upms.payment.controller;
import com.annaru.common.result.ResultMap;
import com.annaru.upms.payment.service.WxPayService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
@Api(tags = "微信支付接口管理")
@RestController
@RequestMapping("/wxPay")
public class WxPayController{
@Autowired
private WxPayService wxPayService;
/**
* 統一下單接口
*/
@ApiOperation(value = "統一下單", notes = "統一下單")
@GetMapping("/unifiedOrder")
public ResultMap unifiedOrder(
@ApiParam(value = "訂單號") @RequestParam String orderNo,
@ApiParam(value = "訂單金額") @RequestParam double amount,
@ApiParam(value = "商品名稱") @RequestParam String body,
HttpServletRequest request) {
try {
// 1、驗證訂單是否存在
// 2、開始微信支付統一下單
ResultMap resultMap = wxPayService.unifiedOrder(orderNo, orderNo, body);
return resultMap;//系統通用的返回結果集,見文章末尾
} catch (Exception e) {
logger.error(e.getMessage());
return ResultMap.error("運行異常,請聯系管理員");
}
}
/**
* 微信支付異步通知
*/
@RequestMapping(value = "/notify")
public String payNotify(HttpServletRequest request) {
InputStream is = null;
String xmlBack = "<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[報文為空]]></return_msg></xml> ";
try {
is = request.getInputStream();
// 將InputStream轉換成String
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
StringBuilder sb = new StringBuilder();
String line = null;
while ((line = reader.readLine()) != null) {
sb.append(line + "\n");
}
xmlBack = wxPayService.notify(sb.toString());
} catch (Exception e) {
logger.error("微信手機支付回調通知失敗:", e);
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return xmlBack;
}
@ApiOperation(value = "退款", notes = "退款")
@PostMapping("/refund")
public ResultMap refund(@ApiParam(value = "訂單號") @RequestParam String orderNo,
@ApiParam(value = "退款金額") @RequestParam double amount,
@ApiParam(value = "退款原因") @RequestParam(required = false) String refundReason){
return wxPayService.refund(orderNo, amount, refundReason);
}
}
5、定義service接口:
package com.annaru.upms.payment.service;
import com.annaru.common.result.ResultMap;
/**
* 微信支付服務接口
*/
public interface WxPayService {
/**
* @Description: 微信支付統一下單
* @param orderNo: 訂單編號
* @param amount: 實際支付金額
* @param body: 訂單描述
* @Author:
* @Date: 2019/8/1
* @return
*/
ResultMap unifiedOrder(String orderNo, double amount, String body) ;
/**
* @Description: 訂單支付異步通知
* @param notifyStr: 微信異步通知消息字符串
* @Author:
* @Date: 2019/8/1
* @return
*/
String notify(String notifyStr) throws Exception;
/**
* @Description: 退款
* @param orderNo: 訂單編號
* @param amount: 實際支付金額
* @param refundReason: 退款原因
* @Author: XCK
* @Date: 2019/8/6
* @return
*/
ResultMap refund(String orderNo, double amount, String refundReason);
}
6、service實現類
package com.annaru.upms.payment.service.impl;
import com.alibaba.dubbo.config.annotation.Reference;
import com.annaru.common.result.ResultMap;
import com.annaru.common.util.HttpContextUtils;
import com.annaru.upms.payment.config.WxPayAppConfig;
import com.annaru.upms.payment.service.WxPayService;
import com.annaru.upms.service.IOrderPaymentService;
import com.github.wxpay.sdk.WXPay;
import com.github.wxpay.sdk.WXPayUtil;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;
@Service
public class WxPayServiceImpl implements WxPayService {
private final Logger logger = LoggerFactory.getLogger(WxPayServiceImpl.class);
@Reference
private IOrderPaymentService orderPaymentService;
@Autowired
private WxPayAppConfig wxPayAppConfig;
@Override
public ResultMap unifiedOrder(String orderNo, double amount, String body) {
Map<String, String> returnMap = new HashMap<>();
Map<String, String> responseMap = new HashMap<>();
Map<String, String> requestMap = new HashMap<>();
try {
WXPay wxpay = new WXPay(wxPayAppConfig);
requestMap.put("body", body); // 商品描述
requestMap.put("out_trade_no", orderNo); // 商戶訂單號
requestMap.put("total_fee", String.valueOf((int)(amount*100))); // 總金額
requestMap.put("spbill_create_ip", HttpContextUtils.getIpAddr()); // 終端IP
requestMap.put("trade_type", "APP"); // App支付類型
requestMap.put("notify_url", wxPayAppConfig.getPayNotifyUrl()); // 接收微信支付異步通知回調地址
Map<String, String> resultMap = wxpay.unifiedOrder(requestMap);
//獲取返回碼
String returnCode = resultMap.get("return_code");
String returnMsg = resultMap.get("return_msg");
//若返回碼為SUCCESS,則會返回一個result_code,再對該result_code進行判斷
if ("SUCCESS".equals(returnCode)) {
String resultCode = resultMap.get("result_code");
String errCodeDes = resultMap.get("err_code_des");
if ("SUCCESS".equals(resultCode)) {
responseMap = resultMap;
}
}
if (responseMap == null || responseMap.isEmpty()) {
return ResultMap.error("獲取預支付交易會話標識失敗");
}
// 3、簽名生成算法
Long time = System.currentTimeMillis() / 1000;
String timestamp = time.toString();
returnMap.put("appid", wxPayAppConfig.getAppID());
returnMap.put("partnerid", wxPayAppConfig.getMchID());
returnMap.put("prepayid", responseMap.get("prepay_id"));
returnMap.put("noncestr", responseMap.get("nonce_str"));
returnMap.put("timestamp", timestamp);
returnMap.put("package", "Sign=WXPay");
returnMap.put("sign", WXPayUtil.generateSignature(returnMap, wxPayAppConfig.getKey()));//微信支付簽名
return ResultMap.ok().put("data", returnMap);
} catch (Exception e) {
logger.error("訂單號:{},錯誤信息:{}", orderNo, e.getMessage());
return ResultMap.error("微信支付統一下單失敗");
}
}
@Override
public String notify(String notifyStr) {
String xmlBack = "<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[報文為空]]></return_msg></xml> ";
try {
// 轉換成map
Map<String, String> resultMap = WXPayUtil.xmlToMap(notifyStr);
WXPay wxpayApp = new WXPay(wxPayAppConfig);
if (wxpayApp.isPayResultNotifySignatureValid(resultMap)) {
String returnCode = resultMap.get("return_code"); //狀態
String outTradeNo = resultMap.get("out_trade_no");//商戶訂單號
String transactionId = resultMap.get("transaction_id");
if (returnCode.equals("SUCCESS")) {
if (StringUtils.isNotBlank(outTradeNo)) {
/**
* 注意!!!
* 請根據業務流程,修改數據庫訂單支付狀態,和其他數據的相應狀態
*
*/
logger.info("微信手機支付回調成功,訂單號:{}", outTradeNo);
xmlBack = "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return xmlBack;
}
@Override
public ResultMap refund(String orderNo, double amount, String refundReason){
if(StringUtils.isBlank(orderNo)){
return ResultMap.error("訂單編號不能為空");
}
if(amount <= 0){
return ResultMap.error("退款金額必須大於0");
}
Map<String, String> responseMap = new HashMap<>();
Map<String, String> requestMap = new HashMap<>();
WXPay wxpay = new WXPay(wxPayAppConfig);
requestMap.put("out_trade_no", orderNo);
requestMap.put("out_refund_no", UUIDGenerator.getOrderNo());
requestMap.put("total_fee", "訂單支付時的總金額,需要從數據庫查");
requestMap.put("refund_fee", String.valueOf((int)(amount*100)));//所需退款金額
requestMap.put("refund_desc", refundReason);
try {
responseMap = wxpay.refund(requestMap);
} catch (Exception e) {
e.printStackTrace();
}
String return_code = responseMap.get("return_code"); //返回狀態碼
String return_msg = responseMap.get("return_msg"); //返回信息
if ("SUCCESS".equals(return_code)) {
String result_code = responseMap.get("result_code"); //業務結果
String err_code_des = responseMap.get("err_code_des"); //錯誤代碼描述
if ("SUCCESS".equals(result_code)) {
//表示退款申請接受成功,結果通過退款查詢接口查詢
//修改用戶訂單狀態為退款申請中或已退款。退款異步通知根據需求,可選
//
return ResultMap.ok("退款申請成功");
} else {
logger.info("訂單號:{}錯誤信息:{}", orderNo, err_code_des);
return ResultMap.error(err_code_des);
}
} else {
logger.info("訂單號:{}錯誤信息:{}", orderNo, return_msg);
return ResultMap.error(return_msg);
}
}
}
7、定義通用返回結果集 ResultMap
package com.annaru.common.result;
import org.apache.http.HttpStatus;
import java.util.HashMap;
import java.util.Map;
/**
* @Description 通用返回結果集
* @Author
* @Date 2018/6/12 15:13
*/
public class ResultMap extends HashMap<String, Object> {
public ResultMap() {
put("state", true);
put("code", 0);
put("msg", "success");
}
public static ResultMap error(int code, String msg) {
ResultMap r = new ResultMap();
r.put("state", false);
r.put("code", code);
r.put("msg", msg);
return r;
}
public static ResultMap error(String msg) {
return error(HttpStatus.SC_INTERNAL_SERVER_ERROR, msg);
}
public static ResultMap error() {
return error(HttpStatus.SC_INTERNAL_SERVER_ERROR, "未知異常,請聯系管理員");
}
public static ResultMap ok(String msg) {
ResultMap r = new ResultMap();
r.put("msg", msg);
return r;
}
public static ResultMap ok(Map<String, Object> par) {
ResultMap r = new ResultMap();
r.putAll(par);
return r;
}
public static ResultMap ok() {
return new ResultMap();
}
public ResultMap put(String key, Object value) {
super.put(key, value);
return this;
}
}