微信支付适用于许多场合,如小程序、网页支付、但微信支付相对于其他支付方式略显麻烦,我们使用IJpay框架进行整合
JPay 让支付触手可及,封装了微信支付、支付宝支付、银联支付常用的支付方式以及各种常用的接口。不依赖任何第三方 mvc 框架,仅仅作为工具使用简单快速完成支付模块的开发,可轻松嵌入到任何系统里。
com.github.javen205 IJPay-WxPay2.9.6
package cn.cnvp.web.api.wechart; import cn.cnvp.web.token.message.JsonResult; import cn.cnvp.web.utils.WxUtils; import cn.hutool.core.date.DatePattern; import cn.hutool.core.date.DateUtil; import cn.hutool.core.io.file.FileWriter; import cn.hutool.core.util.StrUtil; import cn.hutool.http.ContentType; import cn.hutool.http.HttpStatus; import cn.hutool.json.JSONArray; import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; import com.ijpay.core.IJPayHttpResponse; import com.ijpay.core.enums.RequestMethodEnum; import com.ijpay.core.kit.AesUtil; import com.ijpay.core.kit.HttpKit; import com.ijpay.core.kit.PayKit; import com.ijpay.core.kit.WxPayKit; import com.ijpay.core.utils.DateTimeZoneUtil; import com.ijpay.wxpay.WxPayApi; import com.ijpay.wxpay.enums.WxDomainEnum; import com.ijpay.wxpay.enums.v3.BasePayApiEnum; import com.ijpay.wxpay.enums.v3.OtherApiEnum; import com.ijpay.wxpay.model.v3.Amount; import com.ijpay.wxpay.model.v3.Payer; import com.ijpay.wxpay.model.v3.UnifiedOrderModel; import io.swagger.annotations.Api; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Scope; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.ByteArrayInputStream; import java.nio.charset.StandardCharsets; import java.security.cert.X509Certificate; import java.util.HashMap; import java.util.Map; /** * @Author: Scoot * @Date: 2023/3/16 15:52 * @Description: 微信支付控制器 */ @Slf4j @Api(tags = "微信支付控制器") @RestController @RequestMapping("/api/wx/pay/v1") @Scope("prototype") public class WxPayApiController { /**微信小程序appid**/ @Value("${wechat.ma.appId}") String appid; /**微信小程序secretId**/ @Value("${wechat.ma.secret}") String secret; /**商户号**/ @Value("${wechat.ma.mchid}") String mchid; /**商户密钥**/ @Value("${wechat.ma.mchKey}") String mchKey; /**回调地址**/ @Value("${wechat.ma.notifyUrl}") String notifyUrl; /**证书地址**/ @Value("${wechat.ma.certPath}") String certPath; /**证书密钥地址**/ @Value("${wechat.ma.certKeyPath}") String certKeyPath; /**微信平台证书**/ @Value("${wechat.ma.platFormPath}") String platFormPath; /** * 微信支付 * @param openId 用户openId * @return */ @RequestMapping("/jsApiPay") @ResponseBody public JsonResult jsApiPay(@RequestParam(value = "openId", required = false, defaultValue = "o-_-itxuXeGW3O1cxJ7FXNmq8Wf8") String openId) { try { String timeExpire = DateTimeZoneUtil.dateToTimeZone(System.currentTimeMillis() + 1000 * 60 * 3); UnifiedOrderModel unifiedOrderModel = new UnifiedOrderModel() // APPID .setAppid(appid) // 商户号 .setMchid(mchid) .setDescription("IJPay 让支付触手可及") .setOut_trade_no(PayKit.generateStr()) .setTime_expire(timeExpire) .setAttach("微信系开发脚手架 https://gitee.com/javen205/TNWX") .setNotify_url(notifyUrl) .setAmount(new Amount().setTotal(1)) .setPayer(new Payer().setOpenid(openId)); log.info("统一下单参数 {}", JSONUtil.toJsonStr(unifiedOrderModel)); IJPayHttpResponse response = WxPayApi.v3( RequestMethodEnum.POST, WxDomainEnum.CHINA.toString(), BasePayApiEnum.JS_API_PAY.toString(), mchid, getSerialNumber(), null, certKeyPath, JSONUtil.toJsonStr(unifiedOrderModel) ); log.info("统一下单响应 {}", response); // 根据证书序列号查询对应的证书来验证签名结果 boolean verifySignature = WxPayKit.verifySignature(response, platFormPath); log.info("verifySignature: {}", verifySignature); if (response.getStatus() == HttpStatus.HTTP_OK && verifySignature) { String body = response.getBody(); JSONObject jsonObject = JSONUtil.parseObj(body); String prepayId = jsonObject.getStr("prepay_id"); Mapmap = WxPayKit.jsApiCreateSign(appid, prepayId, certKeyPath); log.info("唤起支付参数:{}", map); return JsonResult.success("获取成功",JSONUtil.toJsonStr(map)); } } catch (Exception e) { e.printStackTrace(); } return JsonResult.error("唤起失败"); } /** * 微信支付回调 * @param request * @param response */ @RequestMapping(value = "/payNotify", method = {org.springframework.web.bind.annotation.RequestMethod.POST, org.springframework.web.bind.annotation.RequestMethod.GET}) public void payNotify(HttpServletRequest request, HttpServletResponse response) { Map map = new HashMap<>(12); try { String timestamp = request.getHeader("Wechatpay-Timestamp"); String nonce = request.getHeader("Wechatpay-Nonce"); String serialNo = request.getHeader("Wechatpay-Serial"); String signature = request.getHeader("Wechatpay-Signature"); log.info("timestamp:{} nonce:{} serialNo:{} signature:{}", timestamp, nonce, serialNo, signature); String result = HttpKit.readData(request); log.info("支付通知密文 {}", result); // 需要通过证书序列号查找对应的证书,verifyNotify 中有验证证书的序列号 String plainText = WxPayKit.verifyNotify(serialNo, result, signature, nonce, timestamp, mchKey, platFormPath); log.info("支付通知明文 {}", plainText); if (StrUtil.isNotEmpty(plainText)) { response.setStatus(200); map.put("code", "SUCCESS"); map.put("message", "SUCCESS"); } else { response.setStatus(500); map.put("code", "ERROR"); map.put("message", "签名错误"); } response.setHeader("Content-type", ContentType.JSON.toString()); response.getOutputStream().write(JSONUtil.toJsonStr(map).getBytes(StandardCharsets.UTF_8)); response.flushBuffer(); } catch (Exception e) { e.printStackTrace(); } } private String getSerialNumber() { // 获取证书序列号 X509Certificate certificate = PayKit.getCertificate(certPath); if (null != certificate) { String serialNo = certificate.getSerialNumber().toString(16).toUpperCase(); // 提前两天检查证书是否有效 boolean isValid = PayKit.checkCertificateIsValid(certificate, mchid, -2); log.info("证书是否可用 {} 证书有效期为 {}", isValid, DateUtil.format(certificate.getNotAfter(), DatePattern.NORM_DATETIME_PATTERN)); System.out.println("serialNo:" + serialNo); return serialNo; } return null; } @RequestMapping("/get") @ResponseBody public String v3Get() { // 获取平台证书列表 try { IJPayHttpResponse response = WxPayApi.v3( RequestMethodEnum.GET, WxDomainEnum.CHINA.toString(), OtherApiEnum.GET_CERTIFICATES.toString(), mchid, getSerialNumber(), null, certKeyPath, "" ); String serialNumber = response.getHeader("Wechatpay-Serial"); String body = response.getBody(); int status = response.getStatus(); log.info("serialNumber: {}", serialNumber); log.info("status: {}", status); log.info("body: {}", body); int isOk = 200; if (status == isOk) { JSONObject jsonObject = JSONUtil.parseObj(body); JSONArray dataArray = jsonObject.getJSONArray("data"); // 默认认为只有一个平台证书 JSONObject encryptObject = dataArray.getJSONObject(0); JSONObject encryptCertificate = encryptObject.getJSONObject("encrypt_certificate"); String associatedData = encryptCertificate.getStr("associated_data"); String cipherText = encryptCertificate.getStr("ciphertext"); String nonce = encryptCertificate.getStr("nonce"); String serialNo = encryptObject.getStr("serial_no"); final String platSerialNo = savePlatformCert(associatedData, nonce, cipherText, platFormPath); log.info("平台证书序列号: {} serialNo: {}", platSerialNo, serialNo); } // 根据证书序列号查询对应的证书来验证签名结果 boolean verifySignature = WxPayKit.verifySignature(response, platFormPath); System.out.println("verifySignature:" + verifySignature); return body; } catch (Exception e) { e.printStackTrace(); return null; } } /** * 保存平台证书 * @param associatedData associated_data * @param nonce nonce * @param cipherText cipherText * @param certPath 证书保存路径 * @return */ private String savePlatformCert(String associatedData, String nonce, String cipherText, String certPath) { try { AesUtil aesUtil = new AesUtil(mchKey.getBytes(StandardCharsets.UTF_8)); // 平台证书密文解密 // encrypt_certificate 中的 associated_data nonce ciphertext String publicKey = aesUtil.decryptToString( associatedData.getBytes(StandardCharsets.UTF_8), nonce.getBytes(StandardCharsets.UTF_8), cipherText ); // 保存证书 cn.hutool.core.io.file.FileWriter writer = new FileWriter(certPath); writer.write(publicKey); // 获取平台证书序列号 X509Certificate certificate = PayKit.getCertificate(new ByteArrayInputStream(publicKey.getBytes())); return certificate.getSerialNumber().toString(16).toUpperCase(); } catch (Exception e) { e.printStackTrace(); return e.getMessage(); } } }
调用上面get方法下载证书下载完成后会在yml配置的路径生成platForm.pem证书
接口传入openID(后续可换其他方式),返回如下json字符串表示成功
在yml配置的异步回调地址(需要外网访问)
以上就是今天要讲的内容,本文仅仅简单介绍了springboot整合IJpay进行微信支付的使用,关于调用支付jspi缺少参数:total_fee主要是因为1.金额为空 2.订单号重复 3.订单号为空