这里使用的是springboot框架进行测试,比较方便简单,代码仅供参考
原理:java 的前后端通信
注意:1、域名一般需要备案:如果没有备案可能会被拦截,无法正常应答验签(申请域名的平台都会提供备案的攻略,按照攻略正常备案即可)
2、一般无法本地测试(也是就是个人pc电脑),因为没有固定的ip地址,需要申请购买一个服务器后,有固定的对外ip(记得把对应默认端口后80打开,不然会被拦截),把对应的ip映射到对应的域名后(映射其实就是绑定的意思,在申请到域名后设置一下,比较简单),看看外网能不能ping通,如果无法ping通就说明没有映射成功,或者映射的ip不是外网可以访问
pox文件:
4.0.0org.springframework.bootspring-boot-starter-parent2.4.3 cn.hzldemo0.0.1-SNAPSHOTjardemoDemo project for Spring Boot1.82.9.2org.springframework.bootspring-boot-starterorg.springframework.bootspring-boot-starter-testtest
org.springframework.bootspring-boot-starter-web
com.github.wechatpay-apiv3wechatpay-apache-httpclient0.2.3org.projectlomboklombokcom.squareup.okhttp3okhttp3.14.9org.springframeworkspring-tx5.3.1io.springfoxspringfox-swagger2${swagger2.version}io.springfoxspringfox-swagger-ui${swagger2.version}com.alibabafastjson1.2.60
com.github.wechatpay-apiv3wechatpay-apache-httpclient0.2.3
org.springframework.bootspring-boot-maven-plugin
启动类:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
配置文件:(application.yml)只配置了默认的端口号
server:
port: 80
回调主要代码:
import com.alibaba.fastjson.JSON;
import com.wechat.pay.contrib.apache.httpclient.auth.AutoUpdateCertificatesVerifier;
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;
import com.wechat.pay.contrib.apache.httpclient.util.AesUtil;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import io.swagger.annotations.ApiOperation;
import org.springframework.util.Base64Utils;
import org.springframework.web.bind.annotation.*;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.security.cert.X509Certificate;
import java.security.*;
import java.util.*;
@RestController
@RequestMapping("/weixin/pay")
public class WeiXinPayController {
//商户api证书私钥文件的私钥串,apiclient_key.pem文本形式打开即可获取,不需要前后缀
static String s ="商户api证书私钥文件的私钥串";
//验签方法,非微信官方的,微信支付官方的基本也查不到,封装的方式不一样,下面也有
public boolean verify(String srcData, X509Certificate certificate, String sign, String Serial) throws Exception {
//先验证证书序列号是否正确
if (certificate.getSerialNumber().toString(16).toUpperCase().equals(Serial)){
Signature sha256withRSA = Signature.getInstance("SHA256withRSA");
sha256withRSA.initVerify(certificate.getPublicKey());
sha256withRSA.update(srcData.getBytes());
return sha256withRSA.verify(Base64Utils.decodeFromString(sign));
}else {
return false;
}
}
/**
* 微信支付回调地址
*/
@ApiOperation(value = "微信支付回调地址")
@PostMapping(value = "/callback/test")//域名后面的路径,可以根据自己的喜好或者业务需求设置,前面是域名,不能携带端口号,比如https://www.baidu.com/weixin/pay/callback/test, https://www.baidu.com找到你的服务器,/weixin/pay/callback/test服务器的哪个方法进行处理
/**
* request 请求体
* response 响应体
*/
public String callBack(HttpServletRequest request, HttpServletResponse response) throws IOException, GeneralSecurityException {
String characterEncoding = request.getCharacterEncoding();
System.out.println("characterEncoding=" + characterEncoding);
//从请求头获取验签字段
String Timestamp = request.getHeader("Wechatpay-Timestamp");
String Nonce = request.getHeader("Wechatpay-Nonce");
String Signature = request.getHeader("Wechatpay-Signature");
String Serial = request.getHeader("Wechatpay-Serial");
System.out.println("开始读取请求头的信息");
//请求头
System.out.println("Wechatpay-Timestamp=" + Timestamp);
System.out.println("Wechatpay-Nonce=" + Nonce);
System.out.println("Wechatpay-Signature=" + Signature);
System.out.println("Wechatpay-Serial=" + Serial);
System.out.println("=================");
//加载平台证书,官方的sdk,s为商户api证书私钥
PrivateKey merchantPrivateKey = PemUtil
.loadPrivateKey(new ByteArrayInputStream(s.getBytes("utf-8")));
//加载官方自动更新证书
AutoUpdateCertificatesVerifier verifier = new AutoUpdateCertificatesVerifier(
//商户平台查看 //不是API密钥
new WechatPay2Credentials("商户号", new PrivateKeySigner("商户api证书序列号", merchantPrivateKey)), "APIv3密钥".getBytes("utf-8"));
//读取请求体的信息
System.out.println("开始读取请求体的信息");
ServletInputStream inputStream = request.getInputStream();
StringBuffer stringBuffer = new StringBuffer();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
String s;
//读取回调请求体
while ((s = bufferedReader.readLine()) != null) {
stringBuffer.append(s);
}
String s1 = stringBuffer.toString();
System.out.println("请求体" + s1);
Map requestMap = (Map) JSON.parse(s1);
//开始按照验签进行拼接
String id = requestMap.get("id");
System.out.println("id=" + id);
String resource = String.valueOf(requestMap.get("resource"));
System.out.println("resource=" + resource);
Map requestMap2 = (Map) JSON.parse(resource);
String associated_data = requestMap2.get("associated_data");
String nonce = requestMap2.get("nonce");
String ciphertext = requestMap2.get("ciphertext");
//按照文档要求拼接验签串
String VerifySignature = Timestamp + "\n" + Nonce + "\n" + s1 + "\n";
System.out.println("拼接后的验签串=" + VerifySignature);
//使用官方验签工具进行验签
boolean verify1 = verifier.verify(Serial, VerifySignature.getBytes(), Signature);
System.out.println("官方工具验签=" + verify1);
//使用自己写的验签方法进行验签
//赋一个默认值
boolean verify = false;
try {
verify = verify(VerifySignature, verifier.getValidCertificate(), Signature, Serial);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("验签方法进行验签=" + verify);
//判断验签的结果
System.out.println("=======判断验签结果=======");
if (verify == false) {
System.out.println("验签失败,应答接口");
Map map = new HashMap<>();
//响应接口
//设置状态码
response.setStatus(500);
return "{"+
'"'+ "code"+'"'+":"+'"'+"FAIL"+'"'+" "+
'"'+ "message"+'"'+":"+'"'+"失败"+'"'+
"}";
}
System.out.println("验签成功后,开始进行解密");
//解密,如果这里报错,就一定是APIv3密钥错误
AesUtil aesUtil = new AesUtil("APIv3密钥".getBytes());
String aes = aesUtil.decryptToString(associated_data.getBytes(), nonce.getBytes(), ciphertext);
System.out.println("解密后=" + aes);
Map map = new HashMap<>();
//响应接口
map.put("code", "SUCCESS");
map.put("message", "成功");
//设置状态码
response.setStatus(200);
return "{"+
'"'+ "code"+'"'+":"+'"'+"SUCCESS"+'"'+" "+
'"'+ "message"+'"'+":"+'"'+"成功"+'"'+
"}";
}
}
可以试试云开发工作流来接收微信支付回调:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/guide/wechatpay/workflow.html
微信支付的文档跟屎一样 通知也没找到示例代码 蚂蚁支付都有响应的示例
PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(new ByteArrayInputStream(properties.getApiv3Key().getBytes("utf-8"))); //加载官方自动更新证书 AutoUpdateCertificatesVerifier verifier = new AutoUpdateCertificatesVerifier( //商户平台查看 //不是API密钥 new WechatPay2Credentials("商户号", new PrivateKeySigner("商户api证书序列号", merchantPrivateKey)), "APIv3密钥".getBytes("utf-8")); 这些是哪个包中的,PemUtil AutoUpdateCertificatesVerifier 无法引入
微信文档太垃圾了,没有一个是全面的
本地调试的兄弟,你们参数是咋整的啊
哥们 用的就是你的实例代码 支付回调验签过了 但是退款的回调为啥一直失败啊,同样的验签代码
还有这个问题 "java.net.SocketException: Socket Closed"
哥么 问一下 微信支付的native下单那里 需要给请求头生成auth签名的东西吗 他那个文档上没写要 但我看了很多demo都要那个签名 我们现在在替换公司名所以appid掉了没法用 我自己测不了....
我用微信maven包中的wxPayService.parseOrderNotifyV3Result方法来进行解析传来的参数,一直在包null但是看源码看他报错的地方是360 但是360又是一个方法结束“}”号
javax.crypto.AEADBadTagException: Tag mismatch!
这个什么原因呢
请求头里的四个参数为空是为什么呀