maven項目整合官方微信支付v3.0.9(Springboot示例版)

前由: 最近作java的微信支付,查看微信支付sdk的maven倉庫,發現最新一次更新在2017年3月16日,版本號停留在0.0.3:php

<!-- https://mvnrepository.com/artifact/com.github.wxpay/wxpay-sdk -->
<dependency>
    <groupId>com.github.wxpay</groupId>
    <artifactId>wxpay-sdk</artifactId>
    <version>0.0.3</version>
</dependency>

同時,微信官方SDK(下載地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=11_1) 內容版本以下:java

<groupId>com.github.wxpay</groupId>
<artifactId>wxpay-sdk</artifactId>
<version>3.0.9</version>

官方SDK爲3.0.9版本一樣也是基於maven構建,只不過沒有上傳到maven的公共倉庫,那麼咱們就能夠本身動手打成jar包從而導入本地maven項目。git

打包步驟:github

  1. 修改配置文件WXPayConfig的抽象屬性。原版的缺省(default)抽象屬性只能在同一個package下可見,在不一樣package下面缺省屬性是沒有訪問權限的,跨包必須改爲公共public屬性才能夠。
public abstract class WXPayConfig {

    /**
     * 獲取 App ID
     *
     * @return App ID
     */
    public abstract String getAppID();

    /**
     * 獲取 Mch ID
     *
     * @return Mch ID
     */
    public abstract String getMchID();
   
   /**
     * 獲取 API 密鑰
     *
     * @return API密鑰
     */
    public abstract String getKey();
   
   /**
     * 獲取商戶證書內容
     *
     * @return 商戶證書內容
     */
    public abstract InputStream getCertStream();
   
   /**
     * HTTP(S) 鏈接超時時間,單位毫秒
     *
     * @return
     */
    public int getHttpConnectTimeoutMs() {
        return 6*1000;
    }
   
   /**
     * HTTP(S) 讀數據超時時間,單位毫秒
     *
     * @return
     */
    public int getHttpReadTimeoutMs() {
        return 8*1000;
    }
   
   /**
     * 獲取WXPayDomain, 用於多域名容災自動切換
     * @return
     */
    public abstract IWXPayDomain getWXPayDomain();
   
   /**
     * 是否自動上報。
     * 若要關閉自動上報,子類中實現該函數返回 false 便可。
     *
     * @return
     */
   public boolean shouldAutoReport() {
        return true;
    }
	
    /**
     * 進行健康上報的線程的數量
     *
     * @return
     */
    public int getReportWorkerNum() {
        return 6;
    }
    
	/**
     * 健康上報緩存消息的最大數量。會有線程去獨立上報
     * 粗略計算:加入一條消息200B,10000消息佔用空間 2000 KB,約爲2MB,能夠接受
     *
     * @return
     */
    public int getReportQueueMaxSize() {
        return 10000;
    }
    
	/**
     * 批量上報,一次最多上報多個數據
     *
     * @return
     */
    public int getReportBatchSize() {
        return 10;
    }
	
}

2.自行打包完成,安裝本地jar包到本地倉庫須要以下命令(注意不要換行):web

mvn install:install-file -Dfile=E:\wxpay-sdk-3.0.9.jar -DgroupId=com.github.wxpay -DartifactId=wxpay-sdk -Dversion=3.0.9 -Dpackaging=jar

3.maven的pom.xml引入wxpay-sdk依賴。算法

<dependency>
            <groupId>com.github.wxpay</groupId>
            <artifactId>wxpay-sdk</artifactId>
            <version>3.0.9</version>
        </dependency>

同時須要注意打包wxpay-sdk-3.0.9.jar並無把wxpay依賴的包打進去,咱們還需引入wxpay-sdk依賴的包:spring

<dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.5.12</version>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.30</version>
        </dependency>

wxpay-sdk依賴的slf4j與spring-boot-starter-web所依賴的的spring-boot-starter-logging有衝突,請注意依賴排除,本人排除的是spring-boot-starter-logging的依賴。數據庫

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <exclusions><!-- 去掉springboot默認配置的日誌 -->
               <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

SDK使用:apache

1.微信支付yml配置文件api

# 微信app支付
pay:
  wxpay:
    app:
      appID: "xxxxx"
      mchID: "xxxx"
      key: "xxxxxxxxxxxxx"
      #從微信商戶平臺下載的安全證書存放的路徑、放在resources下面,切記必定要看看target目錄下的class文件下有沒有打包apiclient_cert.p12文件
      certPath: static/cert/wxpay/apiclient_cert.p12
      #微信支付成功的異步通知接口
      #payNotifyUrl: ${server.service-domain}/wxPay/notify
      payNotifyUrl: http://xxx.com/wxPay/notify

2.讀取配置信息

@Component
@ConfigurationProperties(prefix = "pay.wxpay.app")
public class WxPayAppConfig extends 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;
    }


    public IWXPayDomain getWXPayDomain() {
        IWXPayDomain iwxPayDomain = new IWXPayDomain() {
            public void report(String domain, long elapsedTimeMillis, Exception ex) {
            }

            public DomainInfo getDomain(WXPayConfig config) {
                return new IWXPayDomain.DomainInfo(WXPayConstants.DOMAIN_API, true);
            }
        };
        return iwxPayDomain;
    }
	
}

3.通用返回結果集

/**
 * @Description 通用返回結果集
 * @Author
 */
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.METHOD_NOT_ALLOWED.value(), msg);
    }

    public static ResultMap error() {
        return error(HttpStatus.METHOD_NOT_ALLOWED.value(), "未知異常,請聯繫管理員");
    }

    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;
    }

}

4.微信支付服務接口

/**
 * 微信支付服務接口
 */
public interface WxPayService {

    /**
     * @Description: 微信支付統一下單
     * @param orderNo: 訂單編號
     * @param amount: 實際支付金額
     * @param body: 訂單描述
     * @Author:
     * @return
     */
    ResultMap unifiedOrder(String orderNo, double amount, String body) ;

    /**
     * @Description: 訂單支付異步通知
     * @param notifyStr: 微信異步通知消息字符串
     * @Author:
     * @return
     */
    String notify(String notifyStr) throws Exception;

    /**
     * @Description: 退款
     * @param orderNo: 訂單編號
     * @param amount: 實際支付金額
     * @param refundReason: 退款緣由
     * @Author:
     * @return
     */
    ResultMap refund(String orderNo, double amount, String refundReason) throws Exception;

}

5.服務接口實現類

@Service
public class WxPayServiceImpl implements WxPayService {

    private final Logger logger = LoggerFactory.getLogger(WxPayServiceImpl.class);

    @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);
            for (String resultKey : resultMap.keySet()) {
                logger.info("訂單key:{}", resultMap.get(resultKey));
            }

            //獲取返回碼
            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("獲取預支付交易會話標識失敗");
            }
            // 三、簽名生成算法
            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.isEmpty(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) throws Exception {

        if(StringUtils.isEmpty(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);
        System.out.println(wxPayAppConfig.toString());
        requestMap.put("out_trade_no", orderNo);
        requestMap.put("out_refund_no", "xxxx自行手動生成");//商戶系統內部的退款單號,商戶系統內部惟一,只能是數字、大小寫字母_-|*@ ,同一退款單號屢次請求只退一筆。
        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();
        }
        for (String responseKey : responseMap.keySet()) {
            logger.info("訂單key:{}", responseMap.get(responseKey));
        }
        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);
        }
    }

}

6.微信支付對外REST接口(用到Swagger2)

@Api(tags = "微信支付接口管理")
@RestController
@RequestMapping("/wxPay")
public class WxPayController{

    @Autowired
    private WxPayService wxPayService;
    private final Logger logger = LoggerFactory.getLogger(WxPayController.class);
    /**
     * 統一下單接口
     */
    @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 {
            // 一、驗證訂單是否存在

            // 二、開始微信支付統一下單
            ResultMap resultMap = wxPayService.unifiedOrder(orderNo, amount, 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) throws Exception {

        return wxPayService.refund(orderNo, amount, refundReason);
    }

}
相關文章
相關標籤/搜索