APP绑定微信商户平台获取商户id(mchID)、证书(商户后台下载)、支付签名密钥(商户后台设置api密钥)、退款签名密钥(商户后台设置api密钥ipv3)等
1.导入微信支付SDK
com.github.wxpay
wxpay-sdk
0.0.3
2.配置微信参数类
@Component
@ConfigurationProperties
@PropertySource("classpath:config/wxconfig.properties") --需自己创建相应的配置文件
@Data
public class WxConfig implements WXPayConfig {
/**
* appid
*/
public String appID;
/**
* 商户id
*/
public String mchID;
/**
* 密钥
*/
public String key;
/**
* 退款密钥
*/
public String refundKey;
/**
* 连接超时时间
*/
public int httpConnectTimeoutMs;
/**
* 读取超时时间
*/
public int httpReadTimeoutMs;
/**
* 证书
*/
public InputStream certStream;
/**
* 证书路径
*/
public String certPath;
/**
* 微信异步通知地址
*/
public String wxNotifyUrl;
public InputStream getCertStream() {
byte [] certData=null;
InputStream certStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(this.getCertPath());
try {
certData = IOUtils.toByteArray(certStream);
certStream.close();
} catch (IOException e) {
e.printStackTrace();
}
return new ByteArrayInputStream(certData);
}
}
需注入第一步种的配置类
/*微信统一下单接口*/
public Map wxPayOrder(HashMap param) throws Exception{
Double price = Double.valueOf(param.get("price").toString());
String paymentDetailNo = param.get("paymentDetailNo").toString();
/*转换金额为分*/
Map respData = null;
BigDecimal totalPrice = new BigDecimal(price.toString()); //单位是元
String totalFee = totalPrice.multiply(new BigDecimal(100)).toBigInteger().toString();
WXPay wxPay = new WXPay(wxconfig, WXPayConstants.SignType.MD5, false);
/*调起支付一次签名 得到第一个sign进行下单*/
Map data = new TreeMap<>();
data.put("appid",wxconfig.getAppID());
data.put("mch_id",wxconfig.getMchID()); //商户号
data.put("trade_type","APP"); //支付场景 APP 微信app支付 JSAPI 公众号支付 NATIVE 扫码支付
data.put("notify_url",wxconfig.getWxNotifyUrl()); //回调地址
data.put("spbill_create_ip","127.0.0.1"); //终端ip
data.put("total_fee",totalFee); //订单总金额
data.put("fee_type","CNY"); //默认人民币
data.put("out_trade_no",paymentDetailNo); //交易号
data.put("body","**APP-购买业务"); //购买显示信息
data.put("nonce_str",WXPayUtil.generateNonceStr()); // 随机字符串小于32位
String s=WXPayUtil.generateSignature(data, wxconfig.getKey());
data.put("sign",s);
/** wxPay.unifiedOrder 这个方法中调用微信统一下单接口 */
respData = wxPay.unifiedOrder(data);
if (respData.get("result_code").equals("SUCCESS")){
//返回给APP端的参数并进行签名得到第二个sign,APP端再调起支付接口
Map repData = new TreeMap<>();
repData.put("appid",wxconfig.getAppID());
repData.put("partnerid",wxconfig.getMchID());
repData.put("prepayid",respData.get("prepay_id"));
repData.put("package","Sign=WXPay");
repData.put("noncestr",respData.get("nonce_str"));
repData.put("timestamp",String.valueOf(System.currentTimeMillis()/1000));
String sign = WXPayUtil.generateSignature(repData,wxconfig.getKey()); //调用sdk方法签名
respData.put("sign",sign);
respData.put("timestamp",repData.get("timestamp"));
respData.put("package","Sign=WXPay");
}
return respData;
}
/*微信支付异步回调 支付异常的情况回滚并调用退款接口进行退款*/
@Override
@Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW, rollbackFor=Exception.class)
public Boolean wxPayNotifyUrl(Map map) throws Exception {
log.info(">>>>微信支付回调...<<<<");
String out_trade_no = map.get("out_trade_no");
String return_code = map.get("return_code");
if(return_code.equals("SUCCESS")){
log.info(">>>>微信调起成功...<<<<");
PaymentDetailVo entity = paymentDao.queryPaymentDetail(out_trade_no);
/*微信重复调起-->校验如果已经支付则直接返回*/
if(entity.getDetailStatus() == 1){
return true;
}
/*判断sign签名是否一致*/
if(WXPayUtil.isSignatureValid(map, wxconfig.getKey())){
log.info(">>>>微信签名通过...<<<<");
/*校验api返回的金额与流水单的金额是否一致*/
if(map.get("total_fee") == null){
log.error("API返回无金额!");
/*调用退款接口 */
wxRefund(out_trade_no,entity.getUserId(), entity.getPaymentStatus());
throw new Exception("API返回无金额!");
}
/*转换金额为元*/
BigDecimal totalPrice = new BigDecimal(entity.getPaymentPrice().toString()); //单位是元
String paymentPrive = totalPrice.multiply(new BigDecimal(100)).toBigInteger().toString();
String total_fee = map.get("total_fee").toString();
if(!total_fee.equals(paymentPrive)){
log.error("API返回的交易金额与流水单的金额不一致,存在假通知的危险!");
wxRefund(out_trade_no,entity.getUserId(), entity.getPaymentStatus());
throw new Exception("API返回的交易金额与流水单的金额不一致,存在假通知的危险!");
}
HashMap param = new HashMap();
param.put("no", entity.getPaymentNo());
/*异步回调成功,修改订单状态*/
...
log.info(">>>>微信异步回调完毕...<<<<");
}else{
log.error("API返回的数据签名验证不通过,有可能被第三方篡改!!!");
wxRefund(out_trade_no,entity.getUserId(), Integer.valueOf(map.get("type").toString()));
throw new Exception("API返回的数据签名验证不通过,有可能被第三方篡改!!!");
}
}else {
return false;
}
return true;
}
/*微信退款*/
private Boolean wxDoingRefund(HashMap param) {
Double price = Double.valueOf(param.get("price").toString());
String paymentDetailNo = param.get("paymentDetailNo").toString();
String out_trade_no = param.get("out_trade_no").toString();
Map respData = null;
BigDecimal totalPrice = new BigDecimal(price); //此时的单位是元
String totalFee = totalPrice.multiply(new BigDecimal(100)).toBigInteger().toString();
try {
WXPay wxPay = new WXPay(wxconfig, WXPayConstants.SignType.MD5, false);
Map data = new TreeMap<>();
data.put("appid",wxconfig.getAppID());
data.put("mch_id",wxconfig.getMchID()); //商户号
data.put("total_fee",totalFee); //订单总金额
data.put("refund_fee",totalFee); //退款总金额
data.put("out_trade_no",out_trade_no); //商户订单号
data.put("out_refund_no",paymentDetailNo); //商户退款单号
data.put("refund_desc","支付异常");
data.put("nonce_str",WXPayUtil.generateNonceStr()); // 随机字符串小于32位
String s=WXPayUtil.generateSignature(data, wxconfig.getRefundKey());
data.put("sign",s);
/** wxPay.refund 这个方法中调用微信退款接口 */
respData = wxPay.refund(data);
if (respData.get("return_code").equals("SUCCESS")){
log.info(">>>>微信退款成功...>>>>");
return true;
}
} catch (Exception e) {
e.printStackTrace();
return false;
}
return false;
}
关闭订单——以下情况需要调用关单接口:
* 商户订单支付失败需要生成新单号重新发起支付,要对原订单号调用关单,避免重复支付;
* 系统下单后,用户支付超时,系统退出不再受理,避免用户继续,请调用关单接口。
* 订单生成后不能马上调用关单接口,最短调用时间间隔为5分钟
/*微信关闭订单*/
public Boolean cloaseOrder(String out_trade_no) throws Exception{
WXPay wxPay = new WXPay(wxconfig, WXPayConstants.SignType.MD5, false);
Map respData = null;
Map data = new TreeMap<>();
data.put("appid",wxconfig.getAppID());
data.put("mch_id",wxconfig.getMchID()); //商户号
data.put("out_trade_no",out_trade_no); //商户订单号
data.put("nonce_str",WXPayUtil.generateNonceStr()); // 随机字符串小于32位
String s=WXPayUtil.generateSignature(data, wxconfig.getRefundKey());
data.put("sign",s);
/** wxPay.cloaseOrder 这个方法中调用微信退款接口 */
respData = wxPay.closeOrder(data);
if (respData.get("return_code").equals("SUCCESS")){
log.info(">>>>微信关闭订单成功...>>>>");
return true;
}
return false;
}