dl_admin/ruoyi-admin/src/main/java/com/ruoyi/api/PayApi.java
2025-04-15 17:41:00 +08:00

148 lines
6.6 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package com.ruoyi.api;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.TypeReference;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.payConfig.WechatPayConfig;
import com.ruoyi.payConfig.WechatPayRequest;
import com.ruoyi.payConfig.WechatPayUrlEnum;
import com.wechat.pay.contrib.apache.httpclient.util.AesUtil;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/payApi")
public class PayApi {
@Resource
private WechatPayConfig wechatPayConfig;
@Resource
private WechatPayRequest wechatPayRequest;
/**
* typeh5、jsapi、app、native、sub_jsapi
* @param type
* @return
*/
@ApiOperation(value = "统一下单-统一接口", notes = "统一下单-统一接口")
@GetMapping("/prepayment")
public Map<String,Object> transactions(String type, Long orderNo, String payType) throws SignatureException, NoSuchAlgorithmException, InvalidKeyException, IOException {
SysUser user = SecurityUtils.getLoginUser().getUser();
// 统一参数封装
Map<String, Object> params = new HashMap<>(8);
params.put("appid", wechatPayConfig.getAppId());
params.put("mchid", wechatPayConfig.getMchId());
params.put("description", driveSchoolCourse.getName());
params.put("out_trade_no", orderNo.toString());
params.put("notify_url", wechatPayConfig.getNotifyUrl());
Map<String, Object> amountMap = new HashMap<>(4);
// 金额单位为分
amountMap.put("total", amount.intValue());
//人民币
amountMap.put("currency", "CNY");
params.put("amount", amountMap);
// 场景信息
Map<String, Object> sceneInfoMap = new HashMap<>(4);
// 客户端IP
sceneInfoMap.put("payer_client_ip", "127.0.0.1");
// 商户端设备号门店号或收银设备ID
sceneInfoMap.put("device_id", "127.0.0.1");
// 除H5与JSAPI有特殊参数外其他的支付方式都一样
if (type.equals(WechatPayUrlEnum.H5.getType())) {
Map<String, Object> h5InfoMap = new HashMap<>(4);
// 场景类型:iOS, Android, Wap
h5InfoMap.put("type", "IOS");
sceneInfoMap.put("h5_info", h5InfoMap);
} else if (type.equals(WechatPayUrlEnum.JSAPI.getType()) || type.equals(WechatPayUrlEnum.SUB_JSAPI.getType())) {
Map<String, Object> payerMap = new HashMap<>(4);
payerMap.put("openid", user.getWxOpenId());
params.put("payer", payerMap);
}
params.put("scene_info", sceneInfoMap);
String paramsStr = JSON.toJSONString(params);
String resStr = wechatPayRequest.wechatHttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi",paramsStr);
Map<String, Object> resMap = JSONObject.parseObject(resStr, new TypeReference<Map<String, Object>>(){});
return paySignMsg(resMap.get("prepay_id").toString(), wechatPayConfig.getAppId(),null);
}
@ApiOperation(value = "支付回调", notes = "支付回调")
@PostMapping("/payNotify")
public Map<String, String> payNotify(@RequestBody JSONObject jsonObject) throws GeneralSecurityException, IOException {
String key = wechatPayConfig.getApiV3Key();
String json = jsonObject.toString();
String associated_data = (String) JSONUtil.getByPath(JSONUtil.parse(json), "resource.associated_data");
String ciphertext = (String) JSONUtil.getByPath(JSONUtil.parse(json), "resource.ciphertext");
String nonce = (String) JSONUtil.getByPath(JSONUtil.parse(json), "resource.nonce");
String decryptData = new AesUtil(key.getBytes(StandardCharsets.UTF_8)).decryptToString(associated_data.getBytes(StandardCharsets.UTF_8), nonce.getBytes(StandardCharsets.UTF_8), ciphertext);
//验签成功
JSONObject decryptDataObj = JSONObject.parseObject(decryptData, JSONObject.class);
String orderNo = decryptDataObj.get("out_trade_no").toString();
//调用业务系统
Map<String, String> res = new HashMap<>();
res.put("code", "SUCCESS");
res.put("message", "成功");
return res;
}
String buildMessage(String appId, String timestamp,String nonceStr,String prepay_id) {
return appId + "\n"
+ timestamp + "\n"
+ nonceStr + "\n"
+ prepay_id + "\n";
}
String sign(byte[] message,String privateKeyStr) throws NoSuchAlgorithmException, SignatureException, IOException, InvalidKeyException {
//签名方式
Signature sign = Signature.getInstance("SHA256withRSA");
//私钥通过MyPrivateKey来获取这是个静态类可以接调用方法 需要的是_key.pem文件的绝对路径配上文件名
PrivateKey privateKey =null;
if (StringUtils.isNotEmpty(privateKeyStr)){
privateKey = PemUtil.loadPrivateKey(privateKeyStr);
}else {
privateKey = wechatPayConfig.getPrivateKey(wechatPayConfig.getKeyPemPath());
}
sign.initSign(privateKey);
sign.update(message);
return Base64.getEncoder().encodeToString(sign.sign());
}
private Map<String, Object> paySignMsg(String prepayId,String appId,String privateKeyStr) throws IOException, NoSuchAlgorithmException, InvalidKeyException, SignatureException {
long timeMillis = System.currentTimeMillis();
String timeStamp = timeMillis/1000+"";
String nonceStr = timeMillis+"";
String packageStr = "prepay_id="+prepayId;
// 公共参数
Map<String, Object> resMap = new HashMap<>();
resMap.put("nonceStr",nonceStr);
resMap.put("timeStamp",timeStamp);
resMap.put("appId",appId);
resMap.put("package", packageStr);
// 使用字段appId、timeStamp、nonceStr、package进行签名
//从下往上依次生成
String message = buildMessage(appId, timeStamp, nonceStr, packageStr);
//签名
String paySign = sign(message.getBytes("utf-8"), privateKeyStr);
resMap.put("paySign", paySign);
resMap.put("signType", "RSA");
return resMap;
}
}