com.github.wxpay
wxpay-sdk
0.0.3
org.jodd
jodd-core
5.1.5
com.github.wechatpay-apiv3
wechatpay-apache-httpclient
0.2.1
yml中的相关的配置
实现类里的具体内容,(一般流程是,要调用预下单接口,然后返回给安卓的二次签名,安卓拿着二次签名去拉起微信支付,如果成功拉起支付,就可以付款,付款成功以后,就会主动回调咱们自己写的回调接口,修改业务逻辑,最后完成此次支付)
预下单接口
@Value("${ayj.wechat.notifyUrlMall}")
private String notifyUrlMall;
@Value("${ayj.wechat.notifyUrlMiniMall}")
private String notifyUrlMiniMall;
@Value("${ayj.wechat.appId}")
private String appId;
@Value("${ayj.wechat.miniAppId}")
private String miniAppId;
@Value("${ayj.wechat.mchId}")
private String mchId;
@Value("${ayj.wechat.bodyMall}")
private String bodyMall;
@Value("${ayj.wechat.bodyCharity}")
private String bodyCharity;
@Value("${ayj.wechat.key}")
private String key;
@Value("${ayj.wechat.feeType}")
private String feeType;
@Value("${ayj.wechat.tradeType}")
private String tradeType;
@Value("${ayj.wechat.miniTradeType}")
private String miniTradeType;
@Value("${ayj.wechat.package}")
private String wxPackage;
@Override
public R prePayMall(Integer userId, Integer orderId,BigDecimal money) {
int penny = money.multiply(new BigDecimal("100")).intValue();//已分为单位
Map resp = new HashMap<>();
try {
WXPay wxpay = appConfigService.myWXPay();
SortedMap data = new TreeMap();
data.put("appid", appId);//微信支付id
data.put("mch_id", mchId);//商户号
data.put("nonce_str", WXPayUtil.generateNonceStr());
data.put("sign_type", "MD5");
data.put("body", bodyMall);
data.put("out_trade_no", OrderNoUtil.getOrderId());//订单号
data.put("fee_type", feeType);// 金额类型 默认CNY
data.put("total_fee", penny+"");//订单处理金额
data.put("notify_url", notifyUrlMall);//支付成功以后调用的地址
data.put("trade_type", tradeType); // 此处指定为app支付
// data.put("profit_sharing","Y");//分账
//生成签名--一次签名,在预下单之前
String characterEncoding = "UTF-8";
String oneSign= createSign(characterEncoding,data,key);
data.put("sign",oneSign);
resp = wxpay.unifiedOrder(data);
log.info("微信预下单返回的对象:", resp);
if (resp.get("return_code").equals("SUCCESS")) {
log.info("微信预下单成功!");
//生成微信预下单对象
WXPrePay prePay = new WXPrePay();
prePay.setMemberId(userId);//用户id
prePay.setOrderNo(data.get("out_trade_no"));//支付订单单号
prePay.setOrderId(orderId);//项目自增id
prePay.setOrderType(MallEmums.PRE_PAY_TYPE_MALL.getCode());//订单类型
prePay.setTotalAmount(penny);//金额
baseMapper.insert(prePay);
//返回WXPaymentResponseDto对象给前端
WXPaymentResponseDto dto = new WXPaymentResponseDto();
dto.setNonceStr(resp.get("nonce_str"));
dto.setPrepayId(resp.get("prepay_id"));
dto.setPartnerId(mchId); //商户id
dto.setAppId(appId); //商户id
dto.setWxPackage(wxPackage);
String time = new Date().getTime()+"";
dto.setTimestamp(time.substring(0,10));
//统一下单以后,生成的二次签名
Map signmap = new TreeMap<>();
signmap.put("appid",appId);
signmap.put("noncestr",resp.get("nonce_str"));
signmap.put("package",wxPackage);
signmap.put("partnerid",mchId);
signmap.put("prepayid",resp.get("prepay_id"));
// signmap.put("signtype","MD5");
signmap.put("timestamp",time.substring(0,10));//官网上写到,时间戳要十位并且精确到秒,平常使用的时间戳都是毫秒13位
String sign = WxUtils.createSign(characterEncoding,signmap,key);
System.out.println("二次签名:"+sign);
dto.setSign(sign.substring(0,30)); //注意,就是这里,为什么是截取签名的前30位,正常生成的签名是32位,这是个坑,我被困扰很久,一会附上图片,告诉大家
return R.success(dto);
}
log.info("微信预下单失败:",resp.get("return_msg"));
return R.fail("微信预下单失败:"+resp.get("return_msg"));
} catch (Exception e) {
e.printStackTrace();
return R.fail("微信预下单失败:"+resp.get("return_msg"));
}
}
在这里给大家附上图片,我被这个二次签名困扰48小时,每次安卓一拉起微信支付,就报错,说是签名错误,我就百度,看到有个大神说,截取前30位就好了,
安卓拉起微信支付以后,并支付成功,这个时候,微信会去自动调用咱们的写的回调接口 public String callBackMall(HttpServletRequest request, HttpServletResponse response) {
response.setHeader("Content-Type", "application/xml"); //设置响应数据格式为xml
Map returnMap = new HashMap<>();
returnMap.put("return_code", "SUCCESS");
returnMap.put("return_msg", "");
Long hxcode =null;
// 读取参数,解析Xml为map
Map map = null;
try {
map = WXPayUtil.xmlToMap(wxUtils.readRequest(request));
// 转换为有序 map,判断签名是否正确
boolean isSignSuccess = WXPayUtil.isSignaturevalid(new TreeMap(map), key, WXPayConstants.SignType.HMACSHA256);
// 签名校验成功,说明是微信服务器发出的数据
if (isSignSuccess) {
//拿出订单支付编号
String orderNo = map.get("out_trade_no");
//根据订单支付编号去查找订单id,等相关的数据
WXPrePay pay = prePayService.getWxPrePayByOrderNo(orderNo).getData();
System.out.println("预下单对象:" + pay);
Integer orderId = pay.getOrderId();
String transactionId = map.get("transaction_id");
System.out.println("微信订单支付单号" + transactionId);
Boolean b = orderFeignService.hasPayByOrderId(orderId).getData();
System.out.println(b);
if (!b) { //false,已支付------一定要在这里去判断是否支付成功,因为微信支付的回调,是分时间,一直在调用,这个具体看官网怎么写的
returnMap.put("hx_code", hxcode.toString());
return WXPayUtil.mapToXml(returnMap);
}
if (map.get("return_code").equals("SUCCESS")) {
if (map.get("result_code").equals("SUCCESS")) {
System.out.println("微信回调:支付成功,orderNo为:" + orderNo);
//------------------自己处理业务逻辑
pay.setTransactionId(transactionId);
prePayService.updateById(pay);
System.out.println(map);
returnMap.put("hx_code", hxcode.toString());
return WXPayUtil.mapToXml(returnMap);
} else {
System.out.println("微信回调:支付失败,orderNo为:" + orderNo);
}
}
// 签名校验失败(可能不是微信服务器发出的数据)
returnMap.put("return_code", "FAIL");
returnMap.put("return_msg", "");
return WXPayUtil.mapToXml(returnMap);
} else {
// 签名校验失败(可能不是微信服务器发出的数据)
returnMap.put("return_code", "FAIL");
returnMap.put("return_msg", "");
return WXPayUtil.mapToXml(returnMap);
}
} catch (IOException e) {
e.printStackTrace();
return "n" +
" n" +
" n" +
" ";
} catch (Exception e) {
e.printStackTrace();
return "n" +
" n" +
" n" +
" ";
}
}
这里附上两个工具类,上面会用到
package com.ayjmall.thirdparty.wechat.utils;
import jodd.util.ResourcesUtil;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;
import javax.net.ssl.SSLContext;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
public class WxCertHttpUtil {
private static int socketTimeout = 10000;// 连接超时时间,默认10秒
private static int connectTimeout = 30000;// 传输超时时间,默认30秒
private static RequestConfig requestConfig;// 请求配置
private static CloseableHttpClient httpClient;// HTTP请求
public static String postData(String url, String xml, String mchId, String certPath) {
// 加载证书
try {
loadCert(mchId, certPath);
} catch (Exception e) {
e.printStackTrace();
}
String result = null;
HttpPost httpPost = new HttpPost(url);
StringEntity postEntity = new StringEntity(xml, "UTF-8");
httpPost.addHeader("Content-Type", "application/xml");
httpPost.setEntity(postEntity);
requestConfig = RequestConfig.custom().setSocketTimeout(socketTimeout).setConnectTimeout(connectTimeout).build();
httpPost.setConfig(requestConfig);
try {
HttpResponse response = null;
try {
response = httpClient.execute(httpPost);
} catch (IOException e) {
e.printStackTrace();
}
HttpEntity entity = response.getEntity();
try {
result = EntityUtils.toString(entity, "UTF-8");
} catch (IOException e) {
e.printStackTrace();
}
} finally {
httpPost.abort();
}
return result;
}
private static void loadCert(String mchId, String certPath) throws Exception {
// 证书密码,默认为服务商商戶ID
String key = mchId;
// 证书路径
String path = certPath;
if (!path.startsWith("/")) {
path = "/" + path;
}
// 指定证书格式为PKCS12
KeyStore keyStore = KeyStore.getInstance("PKCS12");
// 读取PKCS12证书文件
InputStream instream = ResourcesUtil.getResourceAsStream(path);
try {
// 指定PKCS12的密碼(商戶ID)
keyStore.load(instream, key.toCharArray());
} finally {
instream.close();
}
SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, key.toCharArray()).build();
SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(sslcontext, NoopHostnameVerifier.INSTANCE);
httpClient = HttpClients.custom().setSSLSocketFactory(socketFactory).build();
}
}
package com.ayjmall.thirdparty.wechat.utils;
import com.ayjmall.common.utils.MD5Util;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.HttpClients;
import org.jdom.document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;
@Service
public class WxUtils {
public static String createSign(String characterEncoding, Map parameters, String key) {
StringBuffer sb = new StringBuffer();
Set es = parameters.entrySet();// 所有参与传参的参数按照accsii排序(升序)
Iterator it = es.iterator();
while (it.hasNext()) {
@SuppressWarnings("rawtypes")
Map.Entry entry = (Map.Entry) it.next();
String k = (String) entry.getKey();
Object v = entry.getValue();
if (null != v && !"".equals(v) && !"sign".equals(k)
&& !"key".equals(k)) {
sb.append(k + "=" + v + "&");
}
}
sb.append("key=" + key); //KEY是商户秘钥
String sign = MD5Util.MD5Encode(sb.toString(), characterEncoding)
.toUpperCase();
return sign;
}
public String readRequest(HttpServletRequest request) throws IOException {
InputStream inputStream;
StringBuffer sb = new StringBuffer();
inputStream = request.getInputStream();
String str;
BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
while ((str = in.readLine()) != null) {
sb.append(str);
}
in.close();
inputStream.close();
return sb.toString();
}
}
小程序支付
小程序支付跟app支付有不一样的地方,但是区别不大,我已经在代码中注释了,小程序有单独的小程序id.还要在单独指定小程序的支付方式,而且还有单独的小程序的openid,请大家注意
public RMiniPrePayMall(Integer userId, Integer orderId, BigDecimal money) { AyjMallMembers members = memberFeignService.getMembersById(userId).getData(); int penny = money.multiply(new BigDecimal("100")).intValue();//已分为单位 Map resp = new HashMap<>(); try { MiniWXPay miniWXPay = miniUserMyConfig.miniMyWXPay(); SortedMap data = new TreeMap (); data.put("appid", miniAppId);//微信小程序支付id data.put("mch_id", mchId);//商户号 data.put("nonce_str", MiniWXPayUtil.generateNonceStr()); data.put("sign_type", "MD5"); data.put("body", bodyMall); data.put("out_trade_no", OrderNoUtil.getOrderId());//订单号 data.put("fee_type", feeType);// 金额类型 默认CNY data.put("total_fee", penny+"");//订单处理金额 data.put("notify_url", notifyUrlMiniMall);//支付成功以后调用的地址 data.put("trade_type", miniTradeType); // 此处指定为小程序支付 data.put("openid",members.getOpenidMiniUserId());//小程序支付必须使用小程序登录的openid //生成签名--一次签名,在预下单之前 String characterEncoding = "UTF-8"; String oneSign= createSign(characterEncoding,data, key); data.put("sign",oneSign); resp = miniWXPay.unifiedOrder(data); log.info("微信预下单返回的对象-----------------:", resp); if (resp.get("return_code").equals("SUCCESS")) { log.info("微信预下单成功!"); //生成微信预下单对象 WXPrePay prePay = new WXPrePay(); prePay.setMemberId(userId);//用户id prePay.setOrderNo(data.get("out_trade_no"));//支付订单单号 prePay.setOrderId(orderId);//项目自增id prePay.setOrderType(4);//订单类型 prePay.setTotalAmount(penny);//金额 baseMapper.insert(prePay); //返回WXPaymentResponseDto对象给前端 WXPaymentResponseDto dto = new WXPaymentResponseDto(); dto.setNonceStr(resp.get("nonce_str")); dto.setPrepayId(resp.get("prepay_id")); dto.setPartnerId(mchId); //商户id dto.setAppId(miniAppId); //商户id dto.setWxPackage(wxPackage); String time = new Date().getTime()+""; dto.setTimestamp(time.substring(0,10)); //统一下单以后,生成的二次签名 Map signmap = new TreeMap<>(); signmap.put("appId",miniAppId); signmap.put("nonceStr",resp.get("nonce_str")); signmap.put("package","prepay_id="+resp.get("prepay_id")); signmap.put("signType","MD5"); signmap.put("timeStamp",time.substring(0,10)); //signmap.put("mchId",mchId); String sign = WxUtils.createSign(characterEncoding,signmap,key);//商户的key System.out.println(signmap+"---------------"); System.out.println("二次签名:"+sign); dto.setSign(sign); System.out.println(resp); return R.success(dto); } log.info("微信预下单失败:",resp.get("return_msg")); return R.fail("微信预下单失败:"+resp.get("return_msg")); } catch (Exception e) { e.printStackTrace(); return R.fail("微信预下单失败:"+resp.get("return_msg")); } }



