开发前的准备:(必须)
1.小程序标识(appid) 2.商户号(mch_id)3.商户密钥(key)
apiclient_cert.p12)这个证书根据自己需求添加
微信商户平台下载,自己百度
我们使用官网的SDK进行开发: https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=11_1
我们将官网的SDK用IDEA打开看看
v3.0.9版本
点击README.md可以看到官方给的Demo
直接将SDK的java类复制到项目中,注意也要讲pom文件中的依赖也要添加到自己的项目中
我的API证书放在了这里
第一步:配置MyWXConfig类 继承WXPayConfig 抽象类
/**
* @author: zhuhualian
* @date: 2019-11-15 10:46
* @description:
*/
public class MyWXConfig extends WXPayConfig {
private byte[] certData;
public MyWXConfig() throws Exception {
String path = "cert/apiclient_cert.p12";
File file = new File(this.getClass().getClassLoader().getResource(path).getFile());
//File file = new File("/data/project/cert/apiclient_cert.p12");//这个是你API证书放在服务器上的位置(上传服务器时记得换这里)
InputStream certStream = new FileInputStream(file);
this.certData = new byte[(int) file.length()];
certStream.read(this.certData);
certStream.close();
}
@Override
public String getAppID() {
return "xxxxx";//你自己的小程序AppID
}
@Override
public String getMchID() {
return "xxxxx";//你自己的微信商户ID
}
@Override
public String getKey() {
return "xxxxx";//你自己的微信商户密钥
}
@Override
public InputStream getCertStream() {
ByteArrayInputStream certBis = new ByteArrayInputStream(this.certData);
return certBis;
}
@Override
public int getHttpConnectTimeoutMs() {
return 8000;
}
@Override
public int getHttpReadTimeoutMs() {
return 10000;
}
@Override
IWXPayDomain getWXPayDomain() {
IWXPayDomain iwxPayDomain=new IWXPayDomain() {
@Override
public void report(String domain, long elapsedTimeMillis, Exception ex) {
}
@Override
public DomainInfo getDomain(WXPayConfig config) {
return new IWXPayDomain.DomainInfo(WXPayConstants.DOMAIN_API,true);//微信工具常量类有 "api.mch.weixin.qq.com"; wxpay.unifiedorder() /pay/unifiedorder
}
};
return iwxPayDomain;
}
}
第二步:找到 SDK 中的 WxPay 类 修改里面的代码
public WXPay(final WXPayConfig config, final String notifyUrl, final boolean autoReport, final boolean useSandbox) throws Exception {
this.config = config;
this.notifyUrl = notifyUrl;
this.autoReport = autoReport;
this.useSandbox = useSandbox;
if (useSandbox) {
this.signType = SignType.MD5; // 沙箱环境
}
else {
//this.signType = SignType.HMACSHA256;
this.signType = SignType.MD5; //注意:这点是个坑! 默认是HMACSHAS56加密 一定要修改成MD5 不然无论如何都会报 “微信签名失败” 的错误!
} this.wxPayRequest = new WXPayRequest(config); }
第三步:编写统一下单和返回给小程序需要的参数即可
openid:就是微信用户登录你的小程序授权你获得的openid
outtradeno:订单号
IpUtils:获取IP的工具类,后面会将代码贴出来
交易类型trade_type:
-- 原生支付
JSAPI -- 公众号支付 - 小程序支付
MWEB -- H5支付
APP -- app支付
public Object pay2(String openid,String outtradeno, HttpServletRequest request) throws Exception {
//统一下单支付
HashMap<String, String> map = new HashMap<>();
Map<String, String> data = new HashMap<>();
data.put("body", "微信支付"); //商品描述
data.put("total_fee", "1"); // 标价金额 单位:分
data.put("openid", openid); //用户标识 trade_type=JSAPI,此参数必传,用户在商户appid下的唯一标识
data.put("out_trade_no", outtradeno + ""); //商户系统内部订单号
data.put("nonce_str", WXPayUtil.generateNonceStr()); //随机字符串,长度要求在32位以内。推荐随机数生成算法
data.put("spbill_create_ip",IpUtils.getIpAddr(request)); //支持IPV4和IPV6两种格式的IP地址。调用微信支付API的机器IP 自定获取ip
data.put("notify_url", "120.25.214.5:8082/wx/notify"); // 没用到.通知地址:通知url必须为外网可访问的url,不能携带参数。
data.put("trade_type", "JSAPI");//交易类型
data.put("sign_type", WXPayConstants.MD5); //签名类型//MyWxPayConfig 配置了一些默认信息 appid,商户号,商户秘钥,请求域名 ..
MyWXConfig myWxPayConfig = new MyWXConfig();
WXPay wxpay = new WXPay(myWxPayConfig);
Map<String, String> rMap = wxpay.unifiedOrder(data);//生成一次签名 sign
System.out.println(rMap);
// 下面只是为了生成第二次签名 仅此而已
String return_code = rMap.get("return_code");//返回状态码
String result_code = rMap.get("result_code");//结果状态码
String nonce_str = rMap.get("nonce_str"); //随即字符串
Long s = System.currentTimeMillis() / 1000;//获取时间戳除以千变字符串
String timeStamp = String.valueOf(s);
if ("SUCCESS".equals(return_code) && return_code.equals(result_code)) {
map.put("appId", myWxPayConfig.getAppID());//你的appid
map.put("timeStamp", timeStamp);//这边要将返回的时间戳转化成字符串,不然小程序端调用wx.requestPayment方法会报签名错误
map.put("nonceStr", nonce_str);
map.put("package", "prepay_id=" + rMap.get("prepay_id"));
map.put("signType", "MD5");
System.out.println("二次签名参数 : " + map);//需要生成二次签名 所用的参数
//再次签名sign,这个签名用于小程序端调用wx.requesetPayment方法
String sign = WXPayUtil.generateSignature(map, myWxPayConfig.getKey());//你的商户号key
map.put("paySign", sign); // 生成签名 重要
System.out.println("生成的签名paySign : " + sign);
return map; //将map响应给前端 微信支付接口需要的参数
}
return new Result<>("0",Result.ERROR);
}
IpUtils类
public class IpUtils {
/* IpUtils工具类方法
* 获取真实的ip地址
* @param request
* @return
*/
public static String getIpAddr(HttpServletRequest request) {
String ip = request.getHeader("X-Forwarded-For");
if(StringUtils.isNotEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)){
//多次反向代理后会有多个ip值,第一个ip才是真实ip
int index = ip.indexOf(",");
if(index != -1){
return ip.substring(0,index);
}else{
return ip;
}
}
ip = request.getHeader("X-Real-IP");
if(StringUtils.isNotEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)){
return ip;
}
return request.getRemoteAddr();
}
}
容易遇到的错误 ! 容易遇到的错误 ! 容易遇到的错误 !
1 商户号key不要与 appid的secret弄混淆了
2 SDK工具类中 Wxpay类中 this.signType = SignType.HMACSHA256; HMACSHA256改成 MD5
3 第二次签名需要的五个参数一个不能少 appId,nonceStr,package,signType,timeStamp。 注意 都是以 驼峰命名 不然也会报错
OK,搞定了!!!!!!!!!!!!!!!!!!