一共須要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; }