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 transactions(String type, Long orderNo, String payType) throws SignatureException, NoSuchAlgorithmException, InvalidKeyException, IOException { SysUser user = SecurityUtils.getLoginUser().getUser(); // 统一参数封装 Map 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 amountMap = new HashMap<>(4); // 金额单位为分 amountMap.put("total", amount.intValue()); //人民币 amountMap.put("currency", "CNY"); params.put("amount", amountMap); // 场景信息 Map 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 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 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 resMap = JSONObject.parseObject(resStr, new TypeReference>(){}); return paySignMsg(resMap.get("prepay_id").toString(), wechatPayConfig.getAppId(),null); } @ApiOperation(value = "支付回调", notes = "支付回调") @PostMapping("/payNotify") public Map 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 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 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 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; } }