微信支付wxpay -- 移動端app第二章節 -- java 後端代碼

一共須要5處php

1.pom,添加以下依賴前端

<dependency>
   <groupId>com.github.wxpaygroupId>
   <artifactId>wxpay-sdkartifactId>
   <version>0.0.3version>
dependency>

2.配置文件(文件夾):java

WxpayConfig.propertiesgit

#微信支付配置
#移動端appid
APP_ID = wx3d3c125230d2ba**

#商戶號
BUSINESS_CODE = 153982***

#祕鑰--sign獲取須要的,在  帳戶中心-->api安全--api祕鑰設置
API_KEY = gin201907***********

APIV3_KEY = gin2*******

#簽名類型   HMAC-SHA256和MD5,默認爲MD5
SIGN_TYPE = MD5

#證書地址
PEM_ADDRESS = /wxConfig/**/apiclient_cert.p12

#異步通知地址(請注意必須是外網)
NOTIFY_URL =http://***/wxAppPay/notify


#公衆號的appID --待肯定---
#GZH_APP_ID = wxcfe45ec****

同時能夠再同目錄文件夾放入證書,微信支付部分功能須要證書github

3. 生成sign的util類:WxMD5Utilweb

import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Map;
import java.util.Set;

/**
 * Created by Mafy on 2019/7/9.
 * 該類是生成簽名用
 */
public class WxMD5Util {

    public static String getSign(Map data) throws Exception {
        WXConfigUtil config = WXConfigUtil.initWXconfigUtil();
        Set keySet = data.keySet();
        String[] keyArray = keySet.toArray(new String[keySet.size()]);
        Arrays.sort(keyArray);
        StringBuilder sb = new StringBuilder();
        for (String k : keyArray) {
            if (k.equals(WXPayConstants.FIELD_SIGN)) {
                continue;
            }
            if (data.get(k).trim().length() > 0) // 參數值爲空,則不參與簽名
                sb.append(k).append("=").append(data.get(k).trim()).append("&");
        }
        sb.append("key=").append(config.getKey());
        MessageDigest md = null;
        try {
            md = MessageDigest.getInstance("MD5");
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        byte[] array = new byte[0];
        try {
            array = md.digest(sb.toString().getBytes("UTF-8"));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        StringBuilder sb2 = new StringBuilder();
        for (byte item : array) {
            sb2.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
        }
        return sb2.toString().toUpperCase();
    }
}

4.獲取配置文件的配置類--該處使用單例模式算法

import com.github.wxpay.sdk.WXPayConfig;
import org.springframework.util.ObjectUtils;

import java.io.*;
import java.util.Properties;

/**
 * Created by Mafy on 2019/7/9.
 */
public class WXConfigUtil implements WXPayConfig{
    Properties properties= null;
    private byte[] certData;

    private static WXConfigUtil wxConfigUtil = new WXConfigUtil();

    public static WXConfigUtil initWXconfigUtil(){
        return wxConfigUtil;
    }

    private WXConfigUtil() {
        String certPath = PropertiesUtil.class.getResource(getProperties().getProperty("PEM_ADDRESS")).getPath();//從微信商戶平臺下載的安全證書存放的路徑
        InputStream certStream = null;
        try {
            File file = new File(certPath);
            certStream = new FileInputStream(file);
            this.certData = new byte[(int) file.length()];
            certStream.read(this.certData);

        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(!ObjectUtils.isEmpty(certStream)){
                try {
                    certStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    @Override
    public String getAppID() {
        return getProperties().getProperty("APP_ID");
    }

    //parnerid,商戶號
    @Override
    public String getMchID() {
        return getProperties().getProperty("BUSINESS_CODE");
    }

    @Override
    public String getKey() {
        return getProperties().getProperty("API_KEY");
    }

    @Override
    public InputStream getCertStream() {
        ByteArrayInputStream certBis = new ByteArrayInputStream(this.certData);
        return certBis;
    }

    @Override
    public int getHttpConnectTimeoutMs() {
        return 8000;
    }

    @Override
    public int getHttpReadTimeoutMs() {
        return 10000;
    }

    Properties getProperties() {
        if(null == properties) {
            properties = PropertiesUtil.getProperties("/wxConfig/WxpayConfig.properties");
        }
        return properties;
    }
}

5.調用支付接口的controller:WxAppPayControllerspring

import com.alibaba.fastjson.JSONObject;
import com.github.wxpay.sdk.WXPay;
import com.github.wxpay.sdk.WXPayUtil;
import com.xinlianpu.util.PropertiesUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

/**
 * Created by Mafy on 2019/7/9.
 */
@Controller
@ResponseBody
@RequestMapping("/wxAppPay")
public class WxAppPayController {

    static Properties properties= null;

    final static Logger logger = LoggerFactory.getLogger(WxAppPayController.class);

//    @Resource
//    private WxPayService wxPayService;

    //異步通知地址(請注意必須是外網)
    public static final String NOTIFY_URL = getProperties().getProperty("NOTIFY_URL");
    //交易類型,JSAPI--JSAPI支付(或小程序支付)、NATIVE--Native支付、APP--app支付,MWEB--H5支付
    public static final String TRADE_TYPE_APP = "APP";


    /**
     * 統一下單--不須要證書
     * 包括生成預支付訂單-->訂單生成-->返回訂單數據到前端
     * 官方文檔:https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_1
     * @param jsonObject
     * @return
     * @throws Exception
     */
    @RequestMapping(value = "/pay",method = RequestMethod.POST)
//    public Map wxPay(@RequestParam(value = "userId") String user_id, @RequestParam(value = "totalFee") String total_fee) throws Exception {
    public DataResponse wxPayUnEncryption(@RequestBody JSONObject jsonObject) throws Exception {
        logger.info("下單支付,app傳過來的參數:{}",jsonObject);

        DataResponse dataResponse = DataResponse.success();
//        String user_id = TokenManager.getUserId();
        String user_id = "123";
//        String total_fee = jsonObject.getString("totalFee");//總金額
        String total_fee = "1";//總金額--分
        String spbillCreateIp = jsonObject.getString("spbillCreateIp");//點擊支付按鈕的機器的ip
//        String spbillCreateIp = "0.01";//點擊支付按鈕的機器的ip


        Map map = null;
        try {
//            String attach = "{\"user_id\":\"" + user_id + "\"}";//擴展字段,原樣返回
            String attach = "擴展字段";//擴展字段,原樣返回
            //請求預支付訂單
            Map result = dounifiedOrder(attach, total_fee,spbillCreateIp);
            logger.info("下單以前,預支付返回信息:{}",JSONObject.toJSON(result));
            //可判斷改預支付訂單的數據是否正確
            //預支付訂單以後,生成帶簽名的客戶端您支付信息
            map = new HashMap<>();

            //返回APP端的數據
            //參加調起支付的簽名字段有且只能是6個,分別爲 appid、partnerid、prepayid、package、noncestr 和 timestamp,並且都必須是小寫---start--
            map.put("appid", result.get("appid"));
            map.put("partnerid", result.get("mch_id"));
            map.put("prepayid", result.get("prepay_id"));
            map.put("package", "Sign=WXPay");
            map.put("noncestr", result.get("nonce_str"));
            String signTimstamp = String.valueOf(System.currentTimeMillis() / 1000);
            map.put("timestamp", signTimstamp);//單位爲秒
            //參加調起支付的簽名字段有且只能是6個,分別爲appid、partnerid、prepayid、package、noncestr和timestamp,並且都必須是小寫---end--


            Map mobileParam = new HashMap<>();
            mobileParam.put("appId", result.get("appid"));
            mobileParam.put("partnerId", result.get("mch_id"));
            mobileParam.put("prepayId", result.get("prepay_id"));
            mobileParam.put("packageValue", "Sign=WXPay");//???
            mobileParam.put("nonceStr", result.get("nonce_str"));
            mobileParam.put("timeStamp", signTimstamp);//單位爲秒
            //----這裏不要使用請求預支付訂單時返回的簽名-------
            mobileParam.put("sign", WxMD5Util.getSign(map));
            mobileParam.put("extData", attach);
            logger.info("支付返回參數:{}",JSONObject.toJSONString(map));
            logger.info("返回移動端的參數:{}",JSONObject.toJSONString(mobileParam));
            dataResponse.setData(mobileParam);
        } catch (Exception e) {
            e.printStackTrace();
            return DataResponse.failure();
        }
        return dataResponse;
    }


    /**
     * 微信退款接口--申請退款
     * @param jsonObject
     * @return
     */
    public DataResponse wxRefund(@RequestBody JSONObject jsonObject){
        DataResponse model= DataResponse.success();
        String orderId = jsonObject.getString("orderId");//訂單ID
        try {
            WXConfigUtil config = WXConfigUtil.initWXconfigUtil();
            WXPay wxpay = new WXPay(config);
            Map data = new HashMap<>();


            Map refund = wxpay.refund(data);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return model;
    }



    /**
     *   支付異步結果通知,咱們在請求預支付訂單時傳入的地址--無參數
     *   官方文檔 :https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_7&index=3
     */
    @RequestMapping(value = "/notify", method = {RequestMethod.GET, RequestMethod.POST})
    public String wxPayNotifyUnEncryption(HttpServletRequest request, HttpServletResponse response) {
        String resXml = "";
        try {
            InputStream inputStream = request.getInputStream();
            //將InputStream轉換成xmlString
            BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
            StringBuilder sb = new StringBuilder();
            String line = null;
            try {
                while ((line = reader.readLine()) != null) {
                    sb.append(line + "\n");
                }
            } catch (IOException e) {
                System.out.println(e.getMessage());
            } finally {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            resXml = sb.toString();
            String result = payBack(resXml);
            return result;
        } catch (Exception e) {
            logger.info("微信手機支付失敗:" + e.getMessage());
            System.out.println("微信手機支付失敗:" + e.getMessage());
            String result = "" + "" + "" + " ";
            return result;
        }
    }


    /**
     * 調用官方SDK 獲取預支付訂單等參數
     *
     * @param attach    額外參數
     * @param total_fee 總價
     * @return
     * @throws Exception
     */
    public Map dounifiedOrder(String attach, String total_fee,String spbillCreateIp) throws Exception {
        logger.info("進入調用官方sdk方法,參數:{},{}",attach,total_fee);
        Map returnMap = new HashMap<>();
        WXConfigUtil config = WXConfigUtil.initWXconfigUtil();
        WXPay wxpay = new WXPay(config);
        Map data = new HashMap<>();
        //生成商戶訂單號,不可重複
        String out_trade_no = "wxpay" + System.currentTimeMillis();

        data.put("appid", config.getAppID());//應用ID
        data.put("mch_id", config.getMchID());//商戶號
        data.put("nonce_str", WXPayUtil.generateNonceStr());//隨機字符串,不長於32位。推薦隨機數生成算法
        String body = "新臉譜微信訂單支付-測試-"+ System.currentTimeMillis();
        data.put("body", body); //商品描述
        data.put("out_trade_no", out_trade_no); //商戶訂單號
        data.put("total_fee", total_fee); //總金額
        data.put("spbill_create_ip", spbillCreateIp);//本身的服務器IP地址 ---點擊支付的機器IP
        data.put("notify_url", NOTIFY_URL);//異步通知地址(請注意必須是外網)
        data.put("trade_type", TRADE_TYPE_APP);//交易類型
        data.put("attach", attach);//附加數據,在查詢API和支付通知中原樣返回,該字段主要用於商戶攜帶訂單的自定義數據
        String sign1 = WxMD5Util.getSign(data);
        data.put("sign", sign1); //簽名
        logger.info("生成預訂單的參數,{}", JSONObject.toJSON(data));
        try {
            //使用官方API請求預付訂單
            Map response = wxpay.unifiedOrder(data);
            logger.info("預訂單的返回結果,{}", JSONObject.toJSON(response));
            String returnCode = response.get("return_code");    //獲取返回碼
            //若返回碼爲SUCCESS,則會返回一個result_code,再對該result_code進行判斷
            if (returnCode.equals("SUCCESS")) {//主要返回如下5個參數
                logger.info("預訂單的返回結果SUCCESS,{}", JSONObject.toJSON(response));
                String resultCode = response.get("result_code");
                returnMap.put("appid", response.get("appid"));
                returnMap.put("mch_id", response.get("mch_id"));
                returnMap.put("nonce_str", response.get("nonce_str"));
                returnMap.put("sign", response.get("sign"));
                if ("SUCCESS".equals(resultCode)) {//resultCode 爲SUCCESS,纔會返回prepay_id和trade_type
                    //獲取預支付交易回話標誌
                    returnMap.put("trade_type", response.get("trade_type"));
                    returnMap.put("prepay_id", response.get("prepay_id"));
                    return returnMap;
                } else {
                    logger.info("預訂單的返回結果ERROR,{}", JSONObject.toJSON(response));
                    //此時返回沒有預付訂單的數據
                    return returnMap;
                }
            } else {
                return returnMap;
            }
        } catch (Exception e) {
            System.out.println(e);
            //系統等其餘錯誤的時候
        }
        return returnMap;
    }


    /**
     * @param notifyData 異步通知後的XML數據
     * @return
     */
    public String payBack(String notifyData) {
        logger.info("異步通知進入方法數據:{}",notifyData);
        WXConfigUtil config = null;
        try {
            config = WXConfigUtil.initWXconfigUtil();
        } catch (Exception e) {
            e.printStackTrace();
        }
        WXPay wxpay = new WXPay(config);
        String xmlBack = "";
        Map notifyMap = null;
        try {
            notifyMap = WXPayUtil.xmlToMap(notifyData);         // 調用官方SDK轉換成map類型數據
            logger.info("支付異步結果通知:{}",JSONObject.toJSONString(notifyMap));
            if (wxpay.isPayResultNotifySignatureValid(notifyMap)) {//驗證簽名是否有效,有效則進一步處理

                String return_code = notifyMap.get("return_code");//狀態
                String out_trade_no = notifyMap.get("out_trade_no");//商戶訂單號
                if (return_code.equals("SUCCESS")) {
                    if (out_trade_no != null) {
                        // 注意特殊狀況:訂單已經退款,但收到了支付結果成功的通知,不該把商戶的訂單狀態從退款改爲支付成功
                        // 注意特殊狀況:微信服務端一樣的通知可能會屢次發送給商戶系統,因此數據持久化以前須要檢查是否已經處理過了,處理了直接返回成功標誌
                        //業務數據持久化

                        System.err.println("支付成功");

                        logger.info("微信手機支付回調成功訂單號:{}", out_trade_no);
                        xmlBack = "" + "" + "" + " ";
                    } else {
                        logger.info("微信手機支付回調失敗訂單號:{}", out_trade_no);
                        xmlBack = "" + "" + "" + " ";
                    }
                }
                return xmlBack;
            } else {
                // 簽名錯誤,若是數據裏沒有sign字段,也認爲是簽名錯誤
                //失敗的數據要不要存儲?
                logger.error("手機支付回調通知簽名錯誤");
                xmlBack = "" + "" + "" + " ";
                return xmlBack;
            }
        } catch (Exception e) {
            logger.error("手機支付回調通知失敗", e);
            xmlBack = "" + "" + "" + " ";
        }
        return xmlBack;
    }

    static Properties getProperties() {
        if(null == properties) {
            properties = PropertiesUtil.getProperties("/wxConfig/WxpayConfig.properties");
        }
        return properties;
    }
相關文章
相關標籤/搜索