微信开放平台(分享)
微信支付 - 中国领先的第三方支付平台 | 微信支付提供安全快捷的支付方式
微信公众平台(公众号,订阅号)
1.注册小程序
2.注册微信支付账号(由于需要营业执照等信息,暂无法注册)
一,模块架构
分为两部分 1.小程序(公众号) 2.微信支付(中)
?
?二,关键概念
1.首先注册成为商户
2.小程序APPID 登录小程序-开发-开发管理-开发设置
3.商家的商户ID,就是注册的用户名
4.商家的API私钥,就是下面设置的秘钥?
?三,接入准备
微信支付-开发者文档
?
?四,签约产品
即购买什么支付方法,二维码,还是JSAPI等
产品中心 - 微信支付商户平台
五,设置回调地址
支付完成之后,程序服务端接收微信支付成功的回调信息,之后进行其他业务逻辑处理,比如更新订单状态
?
?六,绑定微信支付与小程序
七,签名
类似token,调用接口传参。注意签名并不需要apiV3密钥,构建客户端需要。
微信支付-普通下单开发者文档
微信支付-开发者文档
八,构建客户端
GitHub - wechatpay-apiv3/wechatpay-apache-httpclient: 微信支付 APIv3 Apache HttpClient装饰器(decorator)
?重要概念:
/**
* merchantId商户号。
* merchantSerialNumber商户API证书的证书序列号。
* merchantPrivateKey商户API私钥,如何加载商户API私钥请看常见问题。
* wechatpayCertificates微信支付平台证书。你也可以使用后面章节提到的“自动更新证书功能”,而不需要关心平台证书的来龙去脉。
* apiV3Key是String格式的API v3密钥 参考: https://kf.qq.com/faq/180830E36vyQ180830AZFZvu.html
* <p>
* 参考文档 开发指引:https://pay.weixin.qq.com/wiki/doc/apiv3/open/pay/chapter2_7_2.shtml
*/
客户端:
@Slf4j
public class WechatPayHttpClientUtil {
/**
* merchantId商户号。
* merchantSerialNumber商户API证书的证书序列号。
* merchantPrivateKey商户API私钥,如何加载商户API私钥请看常见问题。
* wechatpayCertificates微信支付平台证书。你也可以使用后面章节提到的“自动更新证书功能”,而不需要关心平台证书的来龙去脉。
* apiV3Key是String格式的API v3密钥 参考: https://kf.qq.com/faq/180830E36vyQ180830AZFZvu.html
* <p>
* 参考文档 开发指引:https://pay.weixin.qq.com/wiki/doc/apiv3/open/pay/chapter2_7_2.shtml
*/
@Value("${wxpay.merchantId:merchantId}")
private String merchantId;
@Value("${wxpay.merchantSerialNumber:merchantSerialNumber}")
private String merchantSerialNumber;
@Value("${wxpay.merchantPrivateKey:merchantPrivateKey}")
private String merchantPrivateKey;
@Value("${wxpay.apiV3Key:apiV3Key}")
private String apiV3Key;
public HttpClient createHttpClient() {
try {
// 加载商户私钥(privateKey:私钥字符串)
PrivateKey privateKey = PemUtil
.loadPrivateKey(new ByteArrayInputStream(merchantPrivateKey.getBytes("utf-8")));
// 加载平台证书(mchId:商户号,mchSerialNo:商户证书序列号,apiV3Key:V3密钥)
//https://github.com/wechatpay-apiv3/wechatpay-apache-httpclient 自动更新证书功能
AutoUpdateCertificatesVerifier verifier = new AutoUpdateCertificatesVerifier(
new WechatPay2Credentials(merchantId,
new PrivateKeySigner(merchantSerialNumber, privateKey)),
apiV3Key.getBytes("utf-8"));
return WechatPayHttpClientBuilder.create().withMerchant(merchantId, merchantSerialNumber, privateKey)
.withValidator(new WechatPay2Validator(verifier)).build();
} catch (Exception e) {
log.error("createHttpClient exception ", e);
}
return null;
}
public String httpPost(String url, String param, Map<String, Object> headers) throws IOException {
BufferedReader bufferedReader = null;
try {
HttpPost httpPost = new HttpPost(url);
httpPost.setHeader("Content-Type", "application/json;charset=utf-8");
httpPost.addHeader("Accept", "application/json");
if (headers != null) {
headers.keySet().forEach(key -> {
httpPost.setHeader(key, headers.get(key).toString());
});
}
if (StringUtils.isNotBlank(param)) {
HttpEntity httpEntity = new StringEntity(param, "utf-8");
httpPost.setEntity(httpEntity);
}
HttpResponse httpResponse = createHttpClient().execute(httpPost);
if (httpResponse.getStatusLine().getStatusCode() != 200) {
log.error("httpPost failed: url: {}, httpResponse: {}", url, JSON.toJSONString(httpResponse));
}
String output;
bufferedReader = new BufferedReader(new InputStreamReader(httpResponse.getEntity().getContent(), "utf-8"));
StringBuilder stringBuilder = new StringBuilder();
while ((output = bufferedReader.readLine()) != null) {
stringBuilder.append(output);
}
return stringBuilder.toString();
} catch (IOException e) {
e.printStackTrace();
throw e;
} finally {
if (bufferedReader != null)
bufferedReader.close();
}
}
public String httpPost2(String url, String body) {
// HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi");
HttpPost httpPost = new HttpPost(url);
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("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");
String bodyAsString = null;
try {
objectMapper.writeValue(bos, rootNode);
// httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));
httpPost.setEntity(new StringEntity(body, "UTF-8"));
HttpResponse response = createHttpClient().execute(httpPost);
bodyAsString = EntityUtils.toString(response.getEntity());
} catch (IOException e) {
e.printStackTrace();
}
return bodyAsString;
}
}
签名:
public class TokenUtil {
/**
* merchantId商户号。
* merchantSerialNumber商户API证书的证书序列号。
* merchantPrivateKey商户API私钥,如何加载商户API私钥请看常见问题。
* wechatpayCertificates微信支付平台证书。你也可以使用后面章节提到的“自动更新证书功能”,而不需要关心平台证书的来龙去脉。
* apiV3Key是String格式的API v3密钥 参考: https://kf.qq.com/faq/180830E36vyQ180830AZFZvu.html
* <p>
* 参考文档 开发指引:https://pay.weixin.qq.com/wiki/doc/apiv3/open/pay/chapter2_7_2.shtml
*/
@Value("${wxpay.merchantId:merchantId}")
private String merchantId;
@Value("${wxpay.merchantSerialNumber:merchantSerialNumber}")
private String merchantSerialNumber;
@Value("${wxpay.merchantPrivateKey:merchantPrivateKey}")
private String merchantPrivateKey;
@Value("${wxpay.apiV3Key:apiV3Key}")
private String apiV3Key;
/**
* 签名生成
* https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay4_0.shtml
*
* @param method
* @param url
* @param body
* @return
*/
String getToken(String method, HttpUrl url, String body) {
String nonceStr = "your nonce string";
long timestamp = System.currentTimeMillis() / 1000;
String message = buildMessage(method, url, timestamp, nonceStr, body);
String signature = null;
try {
signature = sign(message.getBytes("utf-8"));
} catch (Exception e) {
e.printStackTrace();
}
return "mchid=\"" + merchantId + "\"," + "nonce_str=\"" + nonceStr + "\"," + "timestamp=\"" + timestamp + "\"," + "serial_no=\"" + merchantSerialNumber + "\"," + "signature=\"" + signature + "\"";
}
private String sign(byte[] message) throws SignatureException {
Signature sign = null;
try {
// 加载商户私钥(privateKey:私钥字符串)
PrivateKey privateKey = PemUtil
.loadPrivateKey(new ByteArrayInputStream(merchantPrivateKey.getBytes("utf-8")));
sign = Signature.getInstance("SHA256withRSA");
sign.initSign(privateKey);
sign.update(message);
return Base64.getEncoder().encodeToString(sign.sign());
} catch (Exception e) {
e.printStackTrace();
}
return "";
}
private String buildMessage(String method, HttpUrl url, long timestamp, String nonceStr, String body) {
String canonicalUrl = url.encodedPath();
if (url.encodedQuery() != null) {
canonicalUrl += "?" + url.encodedQuery();
}
return method + "\n" + canonicalUrl + "\n" + timestamp + "\n" + nonceStr + "\n" + body + "\n";
}
/**
* 获取私钥。
*
* @param filename 私钥文件路径 (required)
* @return 私钥对象
*/
public static PrivateKey getPrivateKey(String filename) throws IOException {
String content = new String(Files.readAllBytes(Paths.get(filename)), "utf-8");
try {
String privateKey = content.replace("-----BEGIN PRIVATE KEY-----", "")
.replace("-----END PRIVATE KEY-----", "").replaceAll("\\s+", "");
KeyFactory kf = KeyFactory.getInstance("RSA");
return kf.generatePrivate(new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey)));
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("当前Java环境不支持RSA", e);
} catch (InvalidKeySpecException e) {
throw new RuntimeException("无效的密钥格式");
}
}
}
|