一共需要5处
1.pom,添加如下依赖
<dependency> <groupId>com.github.wxpaygroupId> <artifactId>wxpay-sdkartifactId> <version>0.0.3version> dependency>
2.配置文件(文件夹):
WxpayConfig.properties
#微信支付配置 #移动端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****
同时可以再同目录文件夹放入证书,微信支付部分功能需要证书
3. 生成sign的util类:WxMD5Util
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:WxAppPayController
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; }