官方文档
需要下载wechatpay-apache-httpclient,很多具体的代码都在这个文档里。
需要简单了解的:
这些是英文和概念需要知道,且其中某些是需要去阿里云获取的:
本文的目的是 帮助读者梳理核心步骤,以至于在开发中不会显得脑瓜子很乱。
没有完完整整的开发步骤,没有完完整整的代码。
官方文档_开发指引
总共有三个大步骤,每个大步骤中都有一个梳理,将大步骤分解为几个小步骤。
为帮助理解,梳理部分是按着代码倒叙方式写的。
可以先看梳理部分。
用户下单发起支付,商户可通过微信支付APP下单API创建支付订单。
商户调用APP下单API后,分正常返回和异常返回情况:
梳理:请结合下方获取预下单id
一起看。
CloseableHttpResponse response = httpClient.execute(httpPost)
。1.execute()
了。String bodyAsString = EntityUtils.toString(response.getEntity())
拿到手。其他函数,类等都是起辅助作用。
商户通过APP调起支付OpenSDK调起微信支付,发起支付请求,有关OpenSDK调起支付的详细说明,请参考2.2.2部分的说明
梳理:请结合代码App调起支付
一起看。
api.sendReq(request)
。调起需要很多参数,参数在那儿呢?参数在 request中。当用户完成支付,微信会把相关支付结果通过异步回调的方式通知商户,商户需要接收处理,并按文档规范返回应答 。
梳理:拿到结果后的步骤梳理,结合回调和验签以及之后的内容
查看
取参数:微信会传给我们一个Json格式的数据,我们要将结果取出来
将JSON内容放入builder中,在代码中有提到,请结合查看
验签:为确保是微信官方发来的数据,需要对其数据进行验签
解密密文:有些数据是加密处理的,解密密文的作用是为了步骤4,代码见“解密密文标题”
验证其他参数:比如我这边的价格和微信发来数据中的价格是否一致等,这一块自己写
发回支付结果成功的消息:这一步是必须的,写一句result.put("code","SECCESS")
,就ok。当然,最后需要将result返回
签名有两个:一个是发送时,需要给微信服务器的。一个是从服务器接收的消息,需要验证是微信服务器,而不是其他黑客分子的。
官方文档地址_APP下单API
配置maven依赖,可能需要下载阿里云镜像,pom中加入junit依赖(请自行配置)
下载wechatpay-apache-httpclient,将README.md中的下单代码( createOrder()函数 )复制,修改下单API的URL,修改其他参数。
HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi");
httpPost.addHeader("Accept", "application/json");
httpPost.addHeader("Content-type","application/json; charset=utf-8");
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectMapper objectMapper = new ObjectMapper();
ObjectNode rootNode = objectMapper.createObjectNode();
// 参数设置都用rootNode.put()来进行设置了
rootNode.put("mchid","1900009191")
.put("appid", "wxd678efh567hg6787")
.put("description", "Image形象店-深圳腾大-QQ公仔")
.put("notify_url", "https://www.weixin.qq.com/wxpay/pay.php")
.put("out_trade_no", "1217752501201407033233368018");
rootNode.putObject("amount")
.put("total", 1);
rootNode.putObject("payer")
.put("openid", "oUpF8uMuAJO_M2pxb1Q9zNjWeS6o");
// rootNode转成bos
objectMapper.writeValue(bos, rootNode);
// bos放进httpPost中
httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));
// execute(),参数都存储在了httpPost中
CloseableHttpResponse response = httpClient.execute(httpPost);
// 执行完之后,要能收到一个prepay_id,你可以认为就是 bodyAsString
String bodyAsString = EntityUtils.toString(response.getEntity());
System.out.println(bodyAsString);
将httpClient和verifier的代码粘贴过来。修改privateKey、mchId,mchId等(这一块可以先不看)
private CloseableHttpClient httpClient;
private AutoUpdateCertificatesVerifier verifier;
@Before
public void setup() throws IOException {
PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(
new ByteArrayInputStream(privateKey.getBytes("utf-8")));
//使用自动更新的签名验证器,不需要传入证书
verifier = new AutoUpdateCertificatesVerifier(
new WechatPay2Credentials(mchId, new PrivateKeySigner(mchSerialNo, merchantPrivateKey)),
apiV3Key.getBytes("utf-8"));
httpClient = WechatPayHttpClientBuilder.create()
.withMerchant(mchId, mchSerialNo, merchantPrivateKey)
.withValidator(new WechatPay2Validator(verifier))
.build();
}
try-catch
运行获得prepay_id
(预支付)即成功
首先需要构造签名串,计算签名值
// 在System.out.println(bodyAsString)之后写
// 1. 构造签名串
String timestamp = System.currentTimeMillis()+""; // 时间戳
String nonce = RandomUtil.randomString(32); // 随机数,使用hutool的工具类
// 预支付交易会话ID
Json node = objectMapper.readTree(bodyAsString);
String presessionid = node.get("prepay_id");
// 应用id,时间戳,随机字符串,预支付交易会话ID,构造签名串
......
// 拼接,执行此函数后签名串就构造好了
StringBuilder builder = new StringBuilder();
builder.append(APP_id).addpend("\n");
....... ;
// 2. 计算签名值
String ciphertext = RsaCryptoUtil.encrytOAEP(builder.toString, verifier.getValidCertificate()); // 加密
将接口参数放入map中返回给前端(这一块可以先不看)
Map map = new Map();
map.put("timestamp",timestamp);
..........
IWXAPI api;
PayReq request = new PayReq();
request.appId = "wxd930ea5d5a258f4f";
request.partnerId = "1900000109";
request.prepayId= "1101000000140415649af9fc314aa427",;
request.packageValue = "Sign=WXPay";
request.nonceStr= "1101000000140429eb40476f8896f4c9";
request.timeStamp= "1398746574";
request.sign= "oR9d8PuhnIc+YZ8cBHFCwfgpaK9gd7vaRvkYD7rthRAZ\/X+QBhcCYL21N7cHCTUxbQ+EAt6Uy+lwSN22f5YZvI45MLko8Pfso0jm46v5hqcVwrk6uddkGuT+Cdvu4WBqDzaDjnNa5UK3GfE1Wfl2gHxIIY5lLdUgWFts17D4WuolLLkiFZV+JSHMvH7eaLdT9N5GBovBwu5yYKUR7skR8Fu+LozcSqQixnlEZUfyE55feLOQTUYzLmR9pNtPbPsu6WVhbNHMS3Ss2+AehHvz+n64GDmXxbX++IOBvm2olHu3PsOUGRwhudhVf7UcGcunXt8cqNjKNqZLhLw4jq\/xDg==";
api.sendReq(request); // sendReq()
梳理部分的大框架:
// 获取Json,返回code
@PostMapping("callback")
public Map callback(HttpServletRequest request ){
//1. 验证签名的需要的参数
//result.getHeader("Wechatpay-Timestamp");
//result.getHeader("Wechatpay-Nonce");
//result.getHeader("Wechatpay-Signature");
//result.getHeader("Wechatpay-Serial");
// 将Json内容放入builder中
Map result = new Map();
resuit.put("code","FAILD");// 默认code失败
try{
BufferedReader br = request.getReader();
String str = null;
StringBuilder builder = new StringBuilder();
while( (str = br.readLine())!=null ){
builder.append(str);
}
// 2. 验证签名
// 3. 解密密文
// 4. 验证订单
// 5. 发回结果
result.put("code","SUCCESS");
} catch(IOException e){
e.printStackTrace();
}
return result;
}
// serial:请求头中携带的序列号, 报文, 签名
public static boolean signVerify(String serial, String message, String signature){
// 获取 verifier
PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(privateKey);
//使用自动更新的签名验证器,不需要传入证书
verifier = new AutoUpdateCertificatesVerifier(
new WechatPay2Credentials(mchId, new PrivateKeySigner(mchSerialNo, merchantPrivateKey)),
apiV3Key.getBytes(StandardCharsets.UTF_8));
// 验证签名
try{
return verifier.verify(serial, message.getBytes(StandardCharsets.UTF_8), signature);
}
catch(UnSupportedEncodingException e) {
e.printStackTrace();
}
return false;
}
node中的数据
public static String decryptOrder(String body){
try{
AesUtil util = new AesUtil( PayConstants.API_V3KEY.getBytes("utf-8") );
ObjectMapper objectMapper = new ObjectMapper();
JsonNode node = objectMapper.readTree(body);
// 从node中拿到resource
JsonNode resource = node.get("resource");
// 拿数据密文
String ciphertext = resouce.get("ciphertext").textValue();// 还是Json类型,需要textValue()转String
String associatedData = resoure.get("ciphertext").textValue();
String nonce = resoure.get("nonce").textValue();
return util.decryToString( associatedData.getBytes("utf-8"), nonce.getBytes("utf-8"), ciphertext );
} catch(UnsupportEncodingException e){
e.printStackTrace();
}
return null;
}
中间步骤不是分开的,写一起吧
// 获取Json,返回code
@PostMapping("callback")
public Map callback(HttpServletRequest request ){
// 1. 验证签名的需要的参数
// result.getHeader("Wechatpay-Timestamp");
// result.getHeader("Wechatpay-Nonce");
// result.getHeader("Wechatpay-Signature");
// result.getHeader("Wechatpay-Serial");
// 将Json内容放入builder中
Map result = new Map();
resuit.put("code","FAILD");// 默认code失败
try{
// 签名构造
StringBuilder signStr = new StringBuiler();
signStr.append( result.getHeader("Wechatpay-Timestamp").append("\n") ); // 时间戳
signStr.append( result.getHeader("Wechatpay-Nonce").append("\n") ); // 随机数
BufferedReader br = request.getReader();
String str = null;
StringBuilder builder = new StringBuilder();
while( (str = br.readLine())!=null ){
builder.append(str);
}
signStr.append( builder.toString().append("\n") ); // 报文主体
// 2. 验证签名
if( !signVerify(result.getHeader("Wechatpay-Serial"), signStr.toString, ), result.getHeader("Wechatpay-Signature") ) {
return result;
}
// 3. 解密密文
decryptOrder(builder.toString());
// 4. 验证订单
...................// 验证回调是否为微信官方发来的
// 5. 发回结果
result.put("code","SUCCESS");
} catch(IOException e){
e.printStackTrace();
}
return result;
}
如果看着还是很凌乱,建议看看这个视频操作一下
本文仅供参数