java 微信支付、退款V3
此方法为V3版本
位置支付配置文件:wechat_pay_v3.properties
v3.keyPath=/opt/wechat_pay_v3_key/apiclient_key.pem
v3.certPath=/opt/wechat_pay_v3_key/apiclient_cert.pem
v3.certP12Path=/opt/wechat_pay_v3_key/apiclient_cert.p12
v3.platformCertPath=/opt/wechat_pay_v3_key/wx_cert.pem
v3.mchId=
v3.apiKey3=
#v3.apiKey= Api \u5BC6\u94A5
v3.domain= https://newzdc.jinckji.com/prod-api#v3.passengerAppId=
v3.passengerAppId=
#v3.passengerMiniappSecret=
v3.passengerMiniappSecret=v3.driverAppId=
v3.driverMiniappSecret=
微信配置类:
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;/**** <p>不依赖任何第三方 mvc 框架,仅仅作为工具使用简单快速完成支付模块的开发,可轻松嵌入到任何系统里。 </p>*** <p>Node.js 版: https://gitee.com/javen205/TNWX</p>** <p>微信配置</p>** @author Javen*/
@Component
@PropertySource("classpath:wechat/wechat_pay_v3.properties")
@ConfigurationProperties(prefix = "v3")
public class WechatPayV3Bean {private String keyPath;private String certPath;private String certP12Path;private String platformCertPath;private String mchId;private String apiKey;private String apiKey3;private String domain;private String passengerAppId;//乘客小程序private String passengerMiniappSecret; //乘客小程序秘钥private String driverAppId;//司机小程序private String driverMiniappSecret; //司机小程序秘钥public String getDriverAppId() {return driverAppId;}public void setDriverAppId(String driverAppId) {this.driverAppId = driverAppId;}public String getDriverMiniappSecret() {return driverMiniappSecret;}public void setDriverMiniappSecret(String driverMiniappSecret) {this.driverMiniappSecret = driverMiniappSecret;}public String getPassengerMiniappSecret() {return passengerMiniappSecret;}public void setPassengerMiniappSecret(String passengerMiniappSecret) {this.passengerMiniappSecret = passengerMiniappSecret;}public String getPassengerAppId() {return passengerAppId;}public void setPassengerAppId(String passengerAppId) {this.passengerAppId = passengerAppId;}public String getKeyPath() {return keyPath;}public void setKeyPath(String keyPath) {this.keyPath = keyPath;}public String getCertPath() {return certPath;}public void setCertPath(String certPath) {this.certPath = certPath;}public String getCertP12Path() {return certP12Path;}public void setCertP12Path(String certP12Path) {this.certP12Path = certP12Path;}public String getPlatformCertPath() {return platformCertPath;}public void setPlatformCertPath(String platformCertPath) {this.platformCertPath = platformCertPath;}public String getMchId() {return mchId;}public void setMchId(String mchId) {this.mchId = mchId;}public String getApiKey() {return apiKey;}public void setApiKey(String apiKey) {this.apiKey = apiKey;}public String getApiKey3() {return apiKey3;}public void setApiKey3(String apiKey3) {this.apiKey3 = apiKey3;}public String getDomain() {return domain;}public void setDomain(String domain) {this.domain = domain;}@Overridepublic String toString() {return "WxPayV3Bean{" +"keyPath='" + keyPath + '\'' +", certPath='" + certPath + '\'' +", certP12Path='" + certP12Path + '\'' +", platformCertPath='" + platformCertPath + '\'' +", mchId='" + mchId + '\'' +", apiKey='" + apiKey + '\'' +", apiKey3='" + apiKey3 + '\'' +", domain='" + domain + '\'' +'}';}
}
微信支付V3接口:
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.file.FileWriter;
import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.ContentType;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.ijpay.core.IJPayHttpResponse;
import com.ijpay.core.enums.RequestMethod;
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.WxApiType;
import com.ijpay.wxpay.enums.WxDomain;
import com.ijpay.wxpay.model.v3.*;import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayInputStream;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.security.cert.X509Certificate;
import java.util.*;/*** 微信支付V3接口**/
@Slf4j
@Controller
@RequestMapping("wechat/operation/pay")
public class WechatPayV3Controller {@ResourceWechatPayV3Bean wechatPayV3Bean;String serialNo;String platSerialNo;/*** 获取证书序列号** @return*/private String getSerialNumber() {if (StrUtil.isEmpty(serialNo)) {// 获取证书序列号X509Certificate certificate = PayKit.getCertificate(FileUtil.getInputStream(wechatPayV3Bean.getCertPath()));
// X509Certificate certificate = PayKit.getCertificate(FileUtil.getInputStream("D:\\Documents\\wechat\\apiclient_cert.pem"));serialNo = certificate.getSerialNumber().toString(16).toUpperCase();}System.out.println("serialNo:" + serialNo);return serialNo;}/*** 获取平台证书序列号** @return*/private String getPlatSerialNumber() {if (StrUtil.isEmpty(platSerialNo)) {// 获取平台证书序列号X509Certificate certificate = PayKit.getCertificate(FileUtil.getInputStream(wechatPayV3Bean.getPlatformCertPath()));platSerialNo = certificate.getSerialNumber().toString(16).toUpperCase();}System.out.println("platSerialNo:" + platSerialNo);return platSerialNo;}/*** 保存平台证书** @param associatedData* @param nonce* @param cipherText* @param certPath* @return*/private String savePlatformCert(String associatedData, String nonce, String cipherText, String certPath) {try {AesUtil aesUtil = new AesUtil(wechatPayV3Bean.getApiKey3().getBytes(StandardCharsets.UTF_8));// 平台证书密文解密// encrypt_certificate 中的 associated_data nonce ciphertextString publicKey = aesUtil.decryptToString(associatedData.getBytes(StandardCharsets.UTF_8),nonce.getBytes(StandardCharsets.UTF_8),cipherText);// 保存证书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();}}/*** 刷新平台证书** @return*/@RequestMapping("/get")@ResponseBodypublic String v3Get() {// 获取平台证书列表try {IJPayHttpResponse response = WxPayApi.v3(RequestMethod.GET,WxDomain.CHINA.toString(),WxApiType.GET_CERTIFICATES.toString(),wechatPayV3Bean.getMchId(),getSerialNumber(),null,wechatPayV3Bean.getKeyPath(),"");String timestamp = response.getHeader("Wechatpay-Timestamp");String nonceStr = response.getHeader("Wechatpay-Nonce");String serialNumber = response.getHeader("Wechatpay-Serial");String signature = response.getHeader("Wechatpay-Signature");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, wechatPayV3Bean.getPlatformCertPath());log.info("平台证书序列号: {} serialNo: {}", platSerialNo, serialNo);}// 根据证书序列号查询对应的证书来验证签名结果boolean verifySignature = WxPayKit.verifySignature(response, wechatPayV3Bean.getPlatformCertPath());System.out.println("verifySignature:" + verifySignature);return body;} catch (Exception e) {e.printStackTrace();return null;}}/*** 统一下单* @param openid* @param orderInfoId* @return*/@GetMapping("/jsApiPay")@ResponseBody@ApiOperation(value = "统一下单")@ApiImplicitParams({@ApiImplicitParam(name = "openid", value = "微信用户openid", dataType = "string", required = true),@ApiImplicitParam(name = "orderInfoId", value = "订单id", dataType = "int", required = true),@ApiImplicitParam(name = "payType", value = "支付类型:1-零钱;2-微信", dataType = "Long", required = false)})@Transactionalpublic AjaxResult jsApiPay(@RequestParam("openid") String openid,@RequestParam("orderInfoId") Long orderInfoId,@RequestParam(value = "payType", required = false) Long payType) {if (StringUtils.isEmpty(openid)) {return AjaxResult.error("微信用户openid不能为空");}if (orderInfoId==null) {return AjaxResult.error("订单编号不能为空");}try {//交易结束时间,暂定10年String timeExpire = DateTimeZoneUtil.dateToTimeZone(System.currentTimeMillis() + 1000 * 60 * 31530);UnifiedOrderModel unifiedOrderModel = new UnifiedOrderModel().setAppid(wechatPayV3Bean.getPassengerAppId()).setMchid(wechatPayV3Bean.getMchId()).setDescription("支付").setOut_trade_no(payOrderId).setTime_expire(timeExpire).setAttach("众人").setNotify_url(wechatPayV3Bean.getDomain().concat("/wechat/operation/pay/payNotify"))//金额的单位是分.setAmount(new Amount().setTotal("100").setPayer(new Payer().setOpenid(openid));log.info("统一下单参数 {}", JSONUtil.toJsonStr(unifiedOrderModel));IJPayHttpResponse response = WxPayApi.v3(RequestMethod.POST,WxDomain.CHINA.toString(),WxApiType.JS_API_PAY.toString(),wechatPayV3Bean.getMchId(),getSerialNumber(),null,wechatPayV3Bean.getKeyPath(),JSONUtil.toJsonStr(unifiedOrderModel));log.info("统一下单响应 {}", response);if (response.getStatus() == HttpStatus.SUCCESS) {// 根据证书序列号查询对应的证书来验证签名结果boolean verifySignature = WxPayKit.verifySignature(response, wechatPayV3Bean.getPlatformCertPath());log.info("verifySignature: {}", verifySignature);if (verifySignature) {String body = response.getBody();JSONObject jsonObject = JSONUtil.parseObj(body);String prepayId = jsonObject.getStr("prepay_id");Map<String, String> map = WxPayKit.jsApiCreateSign(wechatPayV3Bean.getPassengerAppId(), prepayId, wechatPayV3Bean.getKeyPath());log.info("唤起支付参数:{}", map);return AjaxResult.success(map);}}return AjaxResult.error(response.toString());} catch (Exception e) {e.printStackTrace();return AjaxResult.error(e.getMessage());}}/*** 申请交易账单** @param billDate 2020-06-14 当天账单后一天出,不然会出现「账单日期格式不正确」错误* @return 交易账单下载地址*/@RequestMapping("/tradeBill")@ResponseBodypublic String tradeBill(@RequestParam(value = "billDate", required = false) String billDate) {try {if (StrUtil.isEmpty(billDate)) {Calendar calendar = Calendar.getInstance();calendar.setTime(new Date());calendar.add(Calendar.DATE, -1);billDate = DateUtil.format(calendar.getTime(), "YYYY-MM-dd");}Map<String, String> params = new HashMap<>(12);params.put("bill_date", billDate);params.put("bill_type", "ALL");params.put("tar_type", "GZIP");IJPayHttpResponse result = WxPayApi.v3(RequestMethod.GET,WxDomain.CHINA.toString(),WxApiType.TRADE_BILL.toString(),wechatPayV3Bean.getMchId(),getSerialNumber(),null,wechatPayV3Bean.getKeyPath(),params);// 根据证书序列号查询对应的证书来验证签名结果boolean verifySignature = WxPayKit.verifySignature(result, wechatPayV3Bean.getPlatformCertPath());log.info("verifySignature: {}", verifySignature);log.info("result:{}", result);return JSONUtil.toJsonStr(result);} catch (Exception e) {e.printStackTrace();return e.getMessage();}}/*** 申请资金账单** @param billDate 2020-06-14 当天账单后一天出,不然会出现「账单日期格式不正确」错误* @return 资金账单下载地址*/@RequestMapping("/fundFlowBill")@ResponseBodypublic String fundFlowBill(@RequestParam(value = "billDate", required = false) String billDate) {try {if (StrUtil.isEmpty(billDate)) {Calendar calendar = Calendar.getInstance();calendar.setTime(new Date());calendar.add(Calendar.DATE, -1);billDate = DateUtil.format(calendar.getTime(), "YYYY-MM-dd");}Map<String, String> params = new HashMap<>(12);params.put("bill_date", billDate);params.put("account_type", "BASIC");IJPayHttpResponse result = WxPayApi.v3(RequestMethod.GET,WxDomain.CHINA.toString(),WxApiType.FUND_FLOW_BILL.toString(),wechatPayV3Bean.getMchId(),getSerialNumber(),null,wechatPayV3Bean.getKeyPath(),params);// 根据证书序列号查询对应的证书来验证签名结果boolean verifySignature = WxPayKit.verifySignature(result, wechatPayV3Bean.getPlatformCertPath());log.info("verifySignature: {}", verifySignature);log.info("result:{}", result);return JSONUtil.toJsonStr(result);} catch (Exception e) {e.printStackTrace();return e.getMessage();}}@RequestMapping("/billDownload")@ResponseBodypublic String billDownload(@RequestParam(value = "token") String token,@RequestParam(value = "tarType", required = false) String tarType) {try {Map<String, String> params = new HashMap<>(12);params.put("token", token);if (StrUtil.isNotEmpty(tarType)) {params.put("tartype", tarType);}IJPayHttpResponse result = WxPayApi.v3(RequestMethod.GET,WxDomain.CHINA.toString(),WxApiType.BILL_DOWNLOAD.toString(),wechatPayV3Bean.getMchId(),getSerialNumber(),null,wechatPayV3Bean.getKeyPath(),params);log.info("result:{}", result);return JSONUtil.toJsonStr(result);} catch (Exception e) {e.printStackTrace();return e.getMessage();}}/*** 退款接口* @param transactionId* @param outTradeNo* @return*/@RequestMapping("/refund")@ResponseBody@Transactionalpublic String refund(@RequestParam(required = false) String transactionId,@RequestParam(required = false) String outTradeNo) {return estOrderCancelFund(transactionId, outTradeNo);}/*** 退款*/public String estOrderCancelFund(String transactionId, String outTradeNo){try {log.info("transactionId: {}", transactionId);log.info("outTradeNo: {}", outTradeNo);String outRefundNo = PayKit.generateStr();log.info("商户退款单号: {}", outRefundNo);List<RefundGoodsDetail> list = new ArrayList<>();RefundGoodsDetail refundGoodsDetail = new RefundGoodsDetail().setMerchant_goods_id(orderInfo1.getOrderId()).setGoods_name("取消订单").setUnit_price("100") //金额,单位为分.setRefund_amount("100").setRefund_quantity(1);list.add(refundGoodsDetail);RefundModel refundModel = new RefundModel().setOut_refund_no(outRefundNo).setReason("取消订单")//.setNotify_url(wechatPayV3Bean.getDomain().concat("/wechat/operation/pay/refundNotify")) //退款异步回调通知.setAmount(new RefundAmount().setRefund("100").setGoods_detail(list);if (StrUtil.isNotEmpty(transactionId) && StringUtils.isNotNull(transactionId)) {refundModel.setTransaction_id(transactionId);}if (StrUtil.isNotEmpty(outTradeNo) && StringUtils.isNotNull(outTradeNo)) {refundModel.setOut_trade_no(orderInfo1.getPayOrderId());}log.info("退款参数 {}", JSONUtil.toJsonStr(refundModel));IJPayHttpResponse response = WxPayApi.v3(RequestMethod.POST,WxDomain.CHINA.toString(),WxApiType.DOMESTIC_REFUNDS.toString(),wechatPayV3Bean.getMchId(),getSerialNumber(),null,wechatPayV3Bean.getKeyPath(),
// "D:\\Documents\\wechat\\apiclient_key.pem",JSONUtil.toJsonStr(refundModel));// 根据证书序列号查询对应的证书来验证签名结果boolean verifySignature = WxPayKit.verifySignature(response, wechatPayV3Bean.getPlatformCertPath());
// boolean verifySignature = WxPayKit.verifySignature(response, "D:\\Documents\\wechat\\wx_cert.pem");log.info("verifySignature: {}", verifySignature);log.info("退款响应 {}", response);String body = response.getBody();//转换为JSONJSONObject jsonObject = JSONUtil.parseObj(body);if(response.getStatus()==200){//退款成功,处理业务逻辑}if (verifySignature) {return response.getBody();}} catch (Exception e) {e.printStackTrace();return e.getMessage();}return null;}/*** 支付成功回调通知,微信有可能多次回调,业务要做幂等性** @param request* @param response*/@RequestMapping(value = "/payNotify", method = {org.springframework.web.bind.annotation.RequestMethod.POST, org.springframework.web.bind.annotation.RequestMethod.GET})@ResponseBodypublic void payNotify(HttpServletRequest request, HttpServletResponse response) {Map<String, String> 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,wechatPayV3Bean.getApiKey3(), wechatPayV3Bean.getPlatformCertPath());log.info("支付通知明文 {}", plainText);if (StrUtil.isNotEmpty(plainText)) {response.setStatus(200);map.put("code", "SUCCESS");map.put("message", "SUCCESS");//处理自己的业务HashMap hashMap = JSON.parseObject(plainText, HashMap.class);if (hashMap.containsKey("out_trade_no")) {//业务逻辑处理} else {//数据有问题,没有订单编号order_idlog.error("支付通知明文解析失败");}} 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();}}}
pom.xml依赖:
<!-- 微信支付工具 --><dependency><groupId>com.github.binarywang</groupId><artifactId>weixin-java-pay</artifactId><version>4.0.0</version></dependency>
微信支付开发文档:微信支付开发者文档
java 微信支付、退款V3相关推荐
- java 微信转账 ca_error_java,微信支付退款_微信支付退款接口调用证书出现错误,java,微信支付退款,ssl - phpStudy...
微信支付退款接口调用证书出现错误 PS:代码是copy腾讯提供的demo,但运行有问题,望大拿能够帮忙解决 加载证书时间出现如下错误: java.io.IOException: DER input, ...
- Java 微信支付APP V3示例思路
这篇文章只是说一下个人的实现思路,大家了解一下实现的思路就行了,并不是全流程可以粘贴复制就能用的案例,个人觉得整体比较麻烦的地方就是前期准备工作,你要去注册账号获取 商户ID 商户证书,商户证书下载后 ...
- java微信支付v3系列——7.微信支付之申请退款
目录 java微信支付v3系列--1.微信支付准备工作 java微信支付v3系列--2.微信支付基本配置 java微信支付v3系列--3.订单创建准备操作 java微信支付v3系列--4.创建订单的封 ...
- java微信支付v3系列——8.微信支付之退款成功回调
目录 java微信支付v3系列--1.微信支付准备工作 java微信支付v3系列--2.微信支付基本配置 java微信支付v3系列--3.订单创建准备操作 java微信支付v3系列--4.创建订单的封 ...
- java微信支付v3系列——6.微信支付查询订单API
目录 java微信支付v3系列--1.微信支付准备工作 java微信支付v3系列--2.微信支付基本配置 java微信支付v3系列--3.订单创建准备操作 java微信支付v3系列--4.创建订单的封 ...
- java微信支付v3系列——1.微信支付准备工作
目录 java微信支付v3系列--1.微信支付准备工作 java微信支付v3系列--2.微信支付基本配置 java微信支付v3系列--3.订单创建准备操作 java微信支付v3系列--4.创建订单的封 ...
- java微信支付v3系列——4.创建订单的封装及使用
目录 java微信支付v3系列--1.微信支付准备工作 java微信支付v3系列--2.微信支付基本配置 java微信支付v3系列--3.订单创建准备操作 java微信支付v3系列--4.创建订单的封 ...
- java微信支付v3系列——5.微信支付成功回调
目录 java微信支付v3系列--1.微信支付准备工作 java微信支付v3系列--2.微信支付基本配置 java微信支付v3系列--3.订单创建准备操作 java微信支付v3系列--4.创建订单的封 ...
- java微信支付v3系列——3.订单创建准备操作
目录 java微信支付v3系列--1.微信支付准备工作 java微信支付v3系列--2.微信支付基本配置 java微信支付v3系列--3.订单创建准备操作 java微信支付v3系列--4.创建订单的封 ...
- 微信接口java解密_java使用AES-256-ECB(PKCS7Padding)解密——微信支付退款通知接口指定解密方式...
1.场景 在做微信支付退款通知接口时,微信对通知的内容做了加密,并且指定用 AES256 解密,官方指定的解密方式如下: 2.导包 org.bouncycastle bcprov-jdk15on 1. ...
最新文章
- 【GoLang】GoLang GOPATH 工程管理 最佳实践
- python中3or5什么意思_示例详解Python3 or Python2 两者之间的差异
- ONVIF网络摄像头(IPC)客户端开发—RTSP RTCP RTP加载H264视频流
- .net 服务器自动执行,自动检测服务器使用流量并执行命令脚本
- Mac 终端连接linux程服务器并相互传输文件
- c语言二叉树的构造输出,C语言数据结构树状输出二叉树,谁能给详细的解释一下...
- go语言的安装、环境变量配置及简单使用
- 理解$watch ,$apply 和 $digest --- 理解数据绑定过程
- 伪静态页面在iis7.0中的配置
- Windows 2012部署Exchange2013
- 管理感情:精力有限,要么干活,要么内斗
- 带你快速读懂ITIL4
- STM32单片机(一).相关的开发工具软件
- 嵌入式 | 51 单片机《手把手教你51单片机-C语言版》
- 2、恩智浦-车规级-MCU :S32K11X GPIO实验
- java 汽车加油问题_贪心算法---汽车加油问题
- 以衍复为例,聊聊当下的沪深300指数增强
- 【机器学习】数据驱动方法在电网稳定分析应用浅谈
- springboot项目在外部tomcat运行出现的问题
- sicily 4379 bicoloring