148 lines
6.6 KiB
Java
148 lines
6.6 KiB
Java
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;
|
||
/**
|
||
* type:h5、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;
|
||
}
|
||
|
||
|
||
|
||
}
|