步骤:
1.注册微信商户,开通小程序支付业务,获得必要接入参数。(Certificate、PrivateKey、merchantId、SerialNumbe、apiV3Key)
2.微信商户号关联小程序(需目标小程序审核)
3.java使用接入参数发起下单,获取下单参数。
4.小程序使用下单参数,调用微信支付,拉起微信支付。
5.客户支付完成后,java后台接收微信支付回调,完成最后的业务。
6.在商户平台下载微信支付账单与系统订单进行对账。
一、微信商户注册
注册地址
通过上传企业信息,法人信息等,成为商户
开通小程序支付业务
产品中心--我的产品--JSAPI支付
merchantId获取
账户中心-商户信息-微信支付商户号
其他参数获取
Certificate、PrivateKey、SerialNumbe、apiV3Key参数获取:账户中心-API安全。具体步骤,在申请的时候官方会提醒的。
二、关联小程序
产品中心--AppID账号管理--关联APPID
三、发起下单
java使用接入参数发起下单,直接上代码
package com.ancun.netsign;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
import com.wechat.pay.contrib.apache.httpclient.auth.ScheduledUpdateCertificatesVerifier;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Validator;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.nio.charset.StandardCharsets;
import java.security.PrivateKey;
public class Test {
private static final String appId="";
private static final String merchantId="";
private static final String merchantSerialNumber="";
private static final String apiV3Key="";
private static final String merchantPrivateKey="";
public static void main(String[] args) throws Exception {
payNomal();
searchOrder();
}
public static void payNomal() throws Exception {
PrivateKey privateKey = PemUtil.loadPrivateKey(
new ByteArrayInputStream(merchantPrivateKey.getBytes("utf-8")));
PrivateKeySigner signer=new PrivateKeySigner(merchantSerialNumber, privateKey);
byte[] bs=apiV3Key.getBytes(StandardCharsets.UTF_8);
WechatPay2Credentials cred=new WechatPay2Credentials(merchantId, signer);
ScheduledUpdateCertificatesVerifier verifier = new ScheduledUpdateCertificatesVerifier(cred, bs);
WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create();
builder.withMerchant(merchantId, merchantSerialNumber, privateKey);
builder.withValidator(new WechatPay2Validator(verifier));
CloseableHttpClient httpClient = builder.build();
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("mchid",merchantId)
.put("appid", appId)
.put("description", "测试公司")
.put("notify_url", "http://回调地址")
.put("attach","自定义参数")
.put("out_trade_no", "订单id");
rootNode.putObject("amount")
.put("total", 3);
rootNode.putObject("payer")
.put("openid", "微信openid");
objectMapper.writeValue(bos, rootNode);
httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));
CloseableHttpResponse response = httpClient.execute(httpPost);
String bodyAsString = EntityUtils.toString(response.getEntity());
JSONObject jo=JSONObject.parseObject(bodyAsString);
System.out.println("prepay_id = " + jo.getString("prepay_id"));
}
public static void searchOrder() throws Exception {
PrivateKey privateKey = PemUtil.loadPrivateKey(
new ByteArrayInputStream(merchantPrivateKey.getBytes("utf-8")));
PrivateKeySigner signer=new PrivateKeySigner(merchantSerialNumber, privateKey);
byte[] bs=apiV3Key.getBytes(StandardCharsets.UTF_8);
WechatPay2Credentials cred=new WechatPay2Credentials(merchantId, signer);
ScheduledUpdateCertificatesVerifier verifier = new ScheduledUpdateCertificatesVerifier(cred, bs);
WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create();
builder.withMerchant(merchantId, merchantSerialNumber, privateKey);
builder.withValidator(new WechatPay2Validator(verifier));
CloseableHttpClient httpClient = builder.build();
HttpGet httpget = new HttpGet("https://api.mch.weixin.qq.com/v3/pay/transactions/id/交易号?mchid=");
httpget.addHeader("Accept", "application/json");
httpget.addHeader("Content-type","application/json; charset=utf-8");
CloseableHttpResponse response = httpClient.execute(httpget);
String bodyAsString = EntityUtils.toString(response.getEntity());
System.out.println(bodyAsString);
JSONObject jo=JSONObject.parseObject(bodyAsString);
System.out.println(jo);
}
}
下单接口返回:
{"prepay_id":"wx2310065247629669c55d9ec38dxxxxxx"}
//组装小程序需要的参数:packageInfo、NonceStr、AppId、PaySign、TimeStamp
String NonceStr= RandomUtil.enUuId();
Long timeStamp = System.currentTimeMillis() / 1000;
String packageInfo = "prepay_id=" + prepayId;
CreateSign02 sign = new CreateSign02();
String paySign = sign.getToken(appId, packageInfo, NonceStr, timeStamp, merchantPrivateKey);
String signType="RSA"
package jnpf.utils;
import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.Signature;
import java.security.SignatureException;
import java.util.Base64;
public class CreateSign02 {
public String getToken(String appid,String prepay_id,String nonceStr,long timestamp,String privateKey) throws IOException, SignatureException, NoSuchAlgorithmException, InvalidKeyException {
//从下往上依次生成
String message = buildMessage(appid, timestamp, nonceStr, prepay_id);
//签名
String signature = sign(message.getBytes("utf-8"),privateKey);
return signature;
}
String sign(byte[] message,String privateKey) throws NoSuchAlgorithmException, SignatureException, IOException, InvalidKeyException {
//签名方式
Signature sign = Signature.getInstance("SHA256withRSA");
//私钥,通过MyPrivateKey来获取,这是个静态类可以接调用方法 ,需要的是_key.pem文件的绝对路径配上文件名
sign.initSign(MyPrivatekey.getPrivateKey(privateKey));
sign.update(message);
return Base64.getEncoder().encodeToString(sign.sign());
}
/**
* 按照前端签名文档规范进行排序,\n是换行
* @param appid
* @param timestamp
* @param nonceStr
* @param prepay_id
* @return
*/
String buildMessage(String appid, long timestamp,String nonceStr,String prepay_id) {
return appid + "\n"
+ timestamp + "\n"
+ nonceStr + "\n"
+ prepay_id + "\n";
}
}
四、小程序拉起支付
//小程序拉起支付方法
uni.requestPayment({
provider: 'wxpay',
timeStamp: timeStamp,
nonceStr: nonceStr,
package: package,
signType: signType,
paySign: paySign,
success: res => {
//成功后的操作
},fail: err => {
},complete: () => {
}
})
效果
五、接收回调
业务代码就不贴了,直接上验签和解密代码
private String checkSignAndDecrypt(HttpServletRequest request) throws Exception {
StringBuffer xmlStr = new StringBuffer();
BufferedReader reader = request.getReader();
String line;
while ((line = reader.readLine()) != null) {
xmlStr.append(line);
}
//支付回调通知:
// {
// "id":"ff9ce7d0-6d60-520a-bb79-ba98aadaef58",
// "create_time":"2022-01-26T10:07:46+08:00",
// "resource_type":"encrypt-resource",
// "event_type":"TRANSACTION.SUCCESS",
// "summary":"支付成功",
// "resource":{
// "original_type":"transaction",
// "algorithm":"AEAD_AES_256_GCM",
// "ciphertext":"bHsdM/qkwb+dU0aani3s0TO+HzD4W0AbQ1TyOBL4VFKDN2IEJx9FPFWWpAwywi/5llfPkf4DoyMMc6KDSkVf3U1bk1y6rKcC+XTFg6jPpsMj/H9kqmrLTYXohtJ6PtmUijnJyEKtyjr7Z6scMEY0oRAAOMZlz3IxheXwNc1AO14zUohS3jppF7wS8lgasVTRiGnk8WRzGLyJTH7lo0bUKYtL2Asq9ARESZbDzJAUcfl8ywZTVFGIlItTXte4CT529cRl+oQtbOFXhlW4kRM7k1QVFhQ3I5FOX58+oPf5aOMaUTzh5HMbpxOAHNB6Yr0JR0QmX1qMwRbeaS45aWhd8BBYJPRFk2MuPalAK3oWqHHYTwa9lEHEJHofSRMZDdUVD3gCCEyzDJpeR0bmQlm1oxnkV4CzBQQeoPg/7Dn2VBUjyX2kKpdVMSdiCb7h1HGesl1BuFurcbZ6rayMfX1Nbq9a7C+oofGauKtdAY1tfQOkee2+NfWTKTc3cfwrWB90aYV2uV9G3tCXeD/XRS2VkXsXiU2UljeVnI1Qv7+9eHWtQDpboQ8q2G2pc4MalhXV2g==",
// "associated_data":"transaction",
// "nonce":"EzVTGRqlF6GM"
// }
// }
log.info("支付回调通知接收的body参数:" + xmlStr);
try {
log.info("开始验签===========================");
// 验签
String Signature = request.getHeader("Wechatpay-Signature");
String Serial = request.getHeader("Wechatpay-Serial");
String Timestamp = request.getHeader("Wechatpay-Timestamp");
String Nonce = request.getHeader("Wechatpay-Nonce");
PrivateKey privateKey = PemUtil.loadPrivateKey(
new ByteArrayInputStream(pro.getMerchantPrivateKey().getBytes("utf-8")));
//加载官方自动更新证书
AutoUpdateCertificatesVerifier verifier = new AutoUpdateCertificatesVerifier(
//商户平台查看 //不是API密钥
new WechatPay2Credentials(pro.getMerchantId(), new PrivateKeySigner(pro.getMerchantSerialNumbe(), privateKey)), pro.getApiV3Key().getBytes("utf-8"));
String s1 = xmlStr.toString();
//按照文档要求拼接验签串
String VerifySignature = Timestamp + "\n" + Nonce + "\n" + s1 + "\n";
log.info("拼接后的验签串=" + VerifySignature);
//使用官方验签工具进行验签
boolean verify1 = verifier.verify(Serial, VerifySignature.getBytes(), Signature);
log.info("验签结果=" + verify1);
if (!verify1) {
return "验签失败";
}
} catch (Exception e) {
e.printStackTrace();
}
log.info("开始解密=========================");
//解密
JSONObject jo = JSONObject.parseObject(xmlStr.toString());
JSONObject jodata = jo.getJSONObject("resource");
String associated_data = jodata.getString("associated_data");
String nonce = jodata.getString("nonce");
String ciphertext = jodata.getString("ciphertext");
AesUtil aesUtil = new AesUtil(pro.getApiV3Key().getBytes());
String aes = aesUtil.decryptToString(associated_data.getBytes(), nonce.getBytes(), ciphertext);
log.info("解密结果=========================" + aes);
return aes;
}
回调方法定义:
/**
* 微信支付回调
*
* @param request
* @param response
* @throws Exception
*/
@PostMapping(value = "/wxPayNotify")
public String wxPayNotify(HttpServletRequest request, HttpServletResponse response) throws Exception {
log.info("进入微信支付回调");
String aes="";
try {
WeCharPayProperties pro = new WeCharPayProperties();
aes = this.checkSignAndDecrypt(pro, request);
log.info("解密后参数:" + aes);
if (aes.equals("验签失败")) {
return "{" +
'"' + "code" + '"' + ":" + '"' + "FAIL" + '"' + "," +
'"' + "message" + '"' + ":" + '"' + "失败,验签失败" + '"' +
"}";
}
//=============解析参数开始=======================================
省略代码
//============解析参数结束=======================================
业务代码
} catch (Exception e) {
log.error("微信支付回调出错", e);
}
log.info("微信支付回调完成====================================================");
return "{" +
'"' + "code" + '"' + ":" + '"' + "SUCCESS" + '"' + "," +
'"' + "message" + '"' + ":" + '"' + "成功" + '"' +
"}";
}
至此完成开发,后续的对账就不贴了。根据自己的业务进行对账。