微信支付JSAPI的V3版本
JSAPI下单
https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_1.shtml
准备:
一个微信公众号,一个微信商户号,一套前后台系统
AppSecret:在上面也能看到,是微信公众号里的开发者密码
APPID:指的是公众号的开发者ID
mchid:这个就是传说中的商户号id,商户平台
API证书:商户平台-账户中心-API安全页面
API秘钥:商家秘钥,功能是解密商家发起访问时用商户证书加密的数据
APIv3秘钥:微信支付平台秘钥,功能是下载平台证书和回调的时候解密平台返回参数用的。
流程:
(1)后台生成订单,调用统一下单接口,拿到返回数据加密给前台
(2)前台拿到数据拉起微信支付
(3)支付成功回调后台接口,后台检验结果完成支付成功后的业务处理,整个流程结束
代码实现:
说明:傻瓜式复制一下三大步代码,完成微信公众号H5支付功能,由于工具类为辅助封装内容,下面代码中个别工具类下载地址如下:
链接:https://pan.baidu.com/s/1ZSIEf3fmAW_Wr9RagcPVAg?pwd=dpn0
提取码:dpn0
统一下单接口调用
import org.apache.http.client.methods.CloseableHttpResponse;
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;
/** WeixinPayService
* H5支付
*
* @param body:说明语,如XX充值
* @param out_trade_no:订单号
* @param total:订单金额,分
* @return data.prepay_id
WeChatConstant:常量类,自行建立
*/
public String h5wxpay(String body, String out_trade_no, int total,String openid) throws IOException, GeneralSecurityException, NotFoundException, HttpCodeException {
Verifier verifier = this.verify();
PrivateKey merchantPrivateKey = getPrivateKey();
WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
.withMerchant(WeChatConstant.mch_id, WeChatConstant.merchantSerialNumber, merchantPrivateKey)
.withValidator(new WechatPay2Validator(verifier));
HttpPost httpPost = new HttpPost(WeChatConstant.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", WeChatConstant.mch_id)
.put("appid", WeChatConstant.app_id)
.put("description", body)
.put("notify_url", WeChatConstant.notify_wurl)
.put("out_trade_no", out_trade_no);
rootNode.putObject("amount")
.put("total", total);
rootNode.putObject("payer").put("openid", openid);
objectMapper.writeValue(bos, rootNode);
CloseableHttpClient httpClient = builder.build();
httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));
CloseableHttpResponse response = httpClient.execute(httpPost);
String bodyAsString = EntityUtils.toString(response.getEntity());
System.out.println(bodyAsString);
return bodyAsString;
}
/**
* 定时更新平台证书功能
* @return
* @throws NotFoundException
* @throws IOException
* @throws GeneralSecurityException
* @throws HttpCodeException
*/
private Verifier verify() throws NotFoundException, IOException, GeneralSecurityException, HttpCodeException {
// 获取证书管理器实例
CertificatesManager certificatesManager = CertificatesManager.getInstance();
PrivateKey merchantPrivateKey = getPrivateKey();
// 向证书管理器增加需要自动更新平台证书的商户信息
certificatesManager.putMerchant(WeChatConstant.mch_id, new WechatPay2Credentials(WeChatConstant.mch_id,
new PrivateKeySigner(WeChatConstant.merchantSerialNumber, merchantPrivateKey)), WeChatConstant.api_key_3.getBytes(StandardCharsets.UTF_8));
// ... 若有多个商户号,可继续调用putMerchant添加商户信息
// 从证书管理器中获取verifier
Verifier verifier = certificatesManager.getVerifier(WeChatConstant.mch_id);
return verifier;
}
/**
* 加载商户私钥
apiclient_key_2.pem:商户证书
* @return
*/
private PrivateKey getPrivateKey() {
String path = this.getClass().getClassLoader().getResource("").getPath();
PrivateKey merchantPrivateKey = null;
try {
merchantPrivateKey = PemUtil.loadPrivateKey(
new FileInputStream(path +"/cert/apiclient_key_2.pem"));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
return merchantPrivateKey;
}
唤起移动端微信支付
/**唤起移动端微信支付页面接口**/
@GetMapping("/wx/sign")
public Object wxsign(@RequestParam String prepay_id) {
//String appId, long timestamp, String nonceStr, String pack
if(StringUtil.isEmpty(prepay_id)){
return Rets.failure("prepay_id不允许为空");
}
Map token = null;
try {
Map token = createSign.getToken(WeChatConstant.app_id, prepayId, WeChatConstant.CERT_URL);
} catch (Exception e) {
e.printStackTrace();
}
return Rets.success(token );
}
import org.springframework.stereotype.Repository;
import java.io.IOException;
import java.security.*;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
/**
* @author :wt
* @date :Created in 2023/5/6 11:10
* @description:辅助签名类
* @modified By:
*/
@Repository
public class CreateSign {
public Map getToken(String appid,String prepay_id, String privateKeyPath) throws IOException, SignatureException, NoSuchAlgorithmException, InvalidKeyException {
String randomOnce = RandomStringGenerator.getRandomStringByLength(32);
//随机字符串
String nonceStr = randomOnce;//真!随机字符串
//时间戳
long timestamp = System.currentTimeMillis() / 1000;
prepay_id = "prepay_id="+prepay_id;
//从下往上依次生成
String message = buildMessage(appid, timestamp, nonceStr, prepay_id);
String signature = sign(message.getBytes("utf-8"), privateKeyPath);
Map param = new HashMap<>();
param.put("appId",appid);
param.put("timeStamp",String.valueOf(timestamp));
param.put("nonceStr",randomOnce);
param.put("package",prepay_id);
param.put("signType","RSA");
param.put("paySign",signature);
return param ;
}
public String sign(byte[] message, String privateKeyPath) throws NoSuchAlgorithmException, SignatureException, IOException, InvalidKeyException {
//签名方式
Signature sign = Signature.getInstance("SHA256withRSA");
//在本地环境运行使用 私钥,通过MyPrivateKey来获取,这是个静态类可以接调用方法 ,需要的是_key.pem文件的绝对路径配上文件名
sign.initSign(MyPrivatekey.getPrivateKey(privateKeyPath));
//在服务器中使用这种方式
// FileReader fileReader = new FileReader(privateKeyPath);
// sign.initSign(MyPrivatekey.getPrivateKey(fileReader.readString()));
sign.update(message);
return Base64.getEncoder().encodeToString(sign.sign());
}
/**
* 按照前端签名文档规范进行排序,\n是换行
* @param appid
* @param timestamp
* @param nonceStr
* @param packag
* @return
*/
public String buildMessage(String appid, long timestamp,String nonceStr,String packag ) {
return appid + "\n"
+ timestamp + "\n"
+ nonceStr + "\n"
+packag + "\n";
}
}
public class RandomStringGenerator {
/**
* 获取一定长度的随机字符串
* @param length 指定字符串长度
* @return 一定长度的字符串
*/
public static String getRandomStringByLength(int length) {
String base = "abcdefghijklmnopqrstuvwxyz0123456789";
Random random = new Random();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < length; i++) {
int number = random.nextInt(base.length());
sb.append(base.charAt(number));
}
return sb.toString();
}
}
回调后台接口,处理业务逻辑
/**
* 移动微信支付回调
* @param request
* @return
* @throws Exception
*/
@RequestMapping(value = "wxin/notify",method = RequestMethod.POST)
public Object wxinNotify(HttpServletRequest request, HttpServletResponse response) throws Exception {
String reqParams = StreamUtils.read(request.getInputStream());
logger.info("微信回调-------支付结果:"+reqParams);
JSONObject jsonObject = JSONObject.parseObject(reqParams);
String eventType = (String) jsonObject.get("event_type");
JSONObject resource = (JSONObject) jsonObject.get("resource");
String associated_data = (String) resource.get("associated_data");
String nonce = (String) resource.get("nonce");
String ciphertext = (String) resource.get("ciphertext");
//如果微信返回状态为TRANSACTION.SUCCESS
if (eventType.equals("TRANSACTION.SUCCESS")) {
response.setStatus(200);
try {
//业务处理
String result = new AesUtil(WeChatConstant.api_key_3.getBytes()).decryptToString(associated_data.getBytes(), nonce.getBytes(), ciphertext);
logger.info("微信支付回调解密结果:" + result);
JSONObject jsonObject1 = JSONObject.parseObject(result);
String orderSn = jsonObject1.getString("out_trade_no");
Order order = orderService.getByOrderSn(orderSn);
if (order == null) {
return WxPayNotifyResponse.fail("订单不存在 sn=" + orderSn);
}
// 检查这个订单是否已经处理过
if (order.hasPayed()) {
return WxPayNotifyResponse.success("订单已经处理成功!");
}
// order.setPayId(payId);
//支付成功,修改订单状态OrderEnum.PayTypeEnum.UN_SEND为已支付
orderService.paySuccess(order, OrderEnum.PayTypeEnum.UN_SEND.getKey());
//业务处理结束
} catch (Exception e) {
throw new RuntimeException("更新付款状态失败!");
}
}else{
return WxPayNotifyResponse.fail("支付失败");
}
return WxPayNotifyResponse.success("支付成功");
}
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
/**
* @author :wt
* @date :Created in 2023/5/11 10:17
* @description:
* @modified By:
*/
public class StreamUtils {
public static String read(InputStream is){
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int len = 0;
byte[] buffer = new byte[512];
while((len = is.read(buffer)) != -1){
baos.write(buffer, 0, len);
}
return new String(baos.toByteArray(), 0, baos.size(), "utf-8");
}catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return "";
}
}
1、所有文章未经授权禁止转载、摘编、复制或建立镜像,如有违反,追究法律责任。
2、本站文章部分来源注册用户发布或互联网收集而来,若有侵权,请邮件联系作者。
邮箱地址:wtao219@qq.com
2、本站文章部分来源注册用户发布或互联网收集而来,若有侵权,请邮件联系作者。
邮箱地址:wtao219@qq.com
THE END
二维码