赞
踩
官方文档
需要下载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返回
签名有两个:一个是发送时,需要给微信服务器的。一个是从服务器接收的消息,需要验证是微信服务器,而不是其他黑客分子的。
配置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; }
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; }
本文仅供参数
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。