引入官方sdk

<!--微信v3支付sdk {https://github.com/wechatpay-apiv3/wechatpay-apache-httpclient}-->
<dependency>
<groupId>com.github.wechatpay-apiv3</groupId>
<artifactId>wechatpay-apache-httpclient</artifactId>
<version>0.4.8</version>
</dependency>

新建配置类

/**
* @Description : 微信支付配置
* @Author : wzkris
* @Version : V1.0.0
* @Date : 2022/11/27 15:41
*/
@ConfigurationProperties("wx.pay")
@Component
@Data
public class WxPayConfig {
/** appid*/
public String appId;
/** appSecret*/
private String appSecret;
/** 商户号 */
public String merchantId;
/** 商户API私钥路径 */
public String privateKeyPath;
/**商户API私钥*/
public PrivateKey privateKey;
/** 商户证书序列号 */
public String merchantSerialNumber;
/**v3密钥*/
public String apiV3key;
}

新建service

/**
* @Description : 微信v3支付服务
* @Author : wzkris
* @Version : V1.0.0
* @Date : 2022/11/27 15:48
* @Description : 微信支付服务,金额一律为分;对账单中的交易金额单位为元
*/
@Component
public class WxPayService { @Resource
private WxPayConfig wxconfig; @Resource
private GtConfig gtConfig; /**
* 请求客户端
*/
private static CloseableHttpClient httpClient; /**
* 微信平台证书验证器
*/
private static Verifier verifier; @PostConstruct
public void init() {
try {
String privateKey = FileUtils.readFileToString(wxconfig.privateKeyPath);
wxconfig.setPrivateKey(PemUtil.loadPrivateKey(privateKey));
//初始化微信平台证书容器,certificatesManager可以放多个商户
CertificatesManager certificatesManager = CertificatesManager.getInstance();
certificatesManager.putMerchant(
wxconfig.merchantId,
new WechatPay2Credentials(wxconfig.merchantId, new PrivateKeySigner(wxconfig.merchantSerialNumber, wxconfig.privateKey)),
wxconfig.getApiV3key().getBytes(StandardCharsets.UTF_8)
);
verifier = certificatesManager.getVerifier(wxconfig.merchantId);
//初始化httpClient
httpClient = WechatPayHttpClientBuilder
.create()
.withMerchant(wxconfig.merchantId, wxconfig.merchantSerialNumber, PemUtil.loadPrivateKey(privateKey))
.withValidator(new WechatPay2Validator(verifier))
.build();
} catch (Exception e) {
e.printStackTrace();
} } /**
* 添加子商户进件申请
*/
public JSONObject addSubMerchant(SubMerchantApplyment applyment) {
HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/applyment4sub/applyment/");
httpPost.addHeader("Accept", "application/json");
httpPost.addHeader("Content-type", "application/json; charset=utf-8");
httpPost.addHeader("Wechatpay-Serial", String.valueOf(verifier.getValidCertificate().getSerialNumber()));
//敏感信息加密
encryptSubMerchantInfo(applyment); httpPost.setEntity(new StringEntity(JSONObject.toJSONString(applyment), StandardCharsets.UTF_8));
try {
CloseableHttpResponse response = httpClient.execute(httpPost);
JSONObject jsonObject = JSONObject.parseObject(EntityUtils.toString(response.getEntity()));
if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
throw new ServiceException(jsonObject);
}
return jsonObject;
} catch (IOException e) {
throw new RuntimeException(e);
}
} /**
* 进件敏感信息加密
*
* @param applyment 特约商户进件
*/
public void encryptSubMerchantInfo(SubMerchantApplyment applyment) {
//1.超级管理员信息加密
ContactInfo contactInfo = applyment.getContactInfo();
contactInfo.setContactName(encrypt(contactInfo.getContactName()))
.setOpenId(encrypt(contactInfo.getOpenId()))
.setMobilePhone(encrypt(contactInfo.getMobilePhone()))
.setContactEmail(encrypt(contactInfo.getContactEmail()));
//1.1经办人类型
if (contactInfo.getContactType().equals(ContactInfo.contactType.SUPER.toString())) {
contactInfo.setContactIdNumber(encrypt(contactInfo.getContactIdNumber()));
}
//2.主体资料
SubjectInfo subjectInfo = applyment.getSubjectInfo();
//2.1受益人信息加密
if (!subjectInfo.getUboInfoList().isEmpty()) {
//每个受益人信息都加密
for (UboInfo uboInfo : subjectInfo.getUboInfoList()) {
uboInfo.setUboIdDocName(encrypt(uboInfo.getUboIdDocName()))
.setUboIdDocNumber(encrypt(uboInfo.getUboIdDocNumber()))
.setUboIdDocAddress(encrypt(uboInfo.getUboIdDocAddress()));
}
}
//3.结算银行账户
BankAccountInfo bankAccountInfo = applyment.getBankAccountInfo();
bankAccountInfo.setAccountName(encrypt(bankAccountInfo.getAccountName()))
.setAccountNumber(encrypt(bankAccountInfo.getAccountNumber()));
} /**
* 查看申请单状态
*
* @param businessCode 业务申请编号
*/
public ApplymentResult searchApplymentStatus(String businessCode) {
HttpGet httpGet = new HttpGet("https://api.mch.weixin.qq.com/v3/applyment4sub/applyment/business_code/" + businessCode);
try {
CloseableHttpResponse response = httpClient.execute(httpGet);
if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
throw new ServiceException(JSONObject.parseObject(EntityUtils.toString(response.getEntity())));
}
return JSONObject.parseObject(EntityUtils.toString(response.getEntity()), ApplymentResult.class);
} catch (IOException e) {
throw new RuntimeException(e);
}
} /**
* @param bo 支付参数
* @param enums 支付渠道
* @return 返回参数
* @Description 服务商付款
*/
public JSONObject pay(WxPayParamsBO bo, PayEnums enums) {
//1.组装参数
JSONObject requestParams = new JSONObject();
requestParams.put("sp_appid", wxconfig.getAppId());
requestParams.put("sp_mchid", wxconfig.getMerchantId());
//子商户appid
requestParams.put("sub_appid", bo.getSubAppId());
//子商户号
requestParams.put("sub_mchid", bo.getSubMchId());
requestParams.put("description", bo.getDescription());
requestParams.put("out_trade_no", bo.getOrderNo());
requestParams.put("notify_url", gtConfig.getUrl() + bo.getNotifyUrl());
JSONObject amount = new JSONObject();
amount.put("total", bo.getAmount());
requestParams.put("amount", amount);
JSONObject payer = new JSONObject();
if (enums.equals(PayEnums.JSAPI)) {
payer.put("sp_openid", bo.getOpenId());
requestParams.put("payer", payer);
} else if (enums.equals(PayEnums.H5)) {
JSONObject sceneInfo = new JSONObject();
//拿到HttpServletRequest解析ip
HttpServletRequest request = ServletUtils.getRequest();
sceneInfo.put("payer_client_ip", IpUtils.getIpAddr(request));
JSONObject h5Info = new JSONObject();
h5Info.put("type", IpUtils.getOperatorSys(request));
sceneInfo.put("h5_info", h5Info);
requestParams.put("scene_info", sceneInfo);
} //2.初始化post请求
HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/partner/transactions/" + enums.getPath());
httpPost.addHeader("Accept", "application/json");
httpPost.addHeader("Content-type", "application/json; charset=utf-8");
httpPost.setEntity(new StringEntity(requestParams.toJSONString(), StandardCharsets.UTF_8));
try {
CloseableHttpResponse response = httpClient.execute(httpPost);
//拿到返回参数
JSONObject resParams = JSONObject.parseObject(EntityUtils.toString(response.getEntity()));
if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
throw new ServiceException(resParams);
}
return resParams;
} catch (IOException e) {
throw new ServiceException(e.getMessage());
} } /**
* @param bo 查询订单详情
* @Description 查询订单信息
*/
public OrderResultInfo queryOrder(QueryOrderBO bo) {
String url = " https://api.mch.weixin.qq.com/v3/pay/partner/transactions/out-trade-no/"
+ bo.getOrderNo()
+ "?" + "sp_mchid=" + bo.getSpMchid()
+ "&" + "sub_mchid=" + bo.getSubMchid();
HttpGet httpGet = new HttpGet(url);
httpGet.addHeader("Accept", "application/json");
try {
CloseableHttpResponse response = httpClient.execute(httpGet);
String bodyAsString = EntityUtils.toString(response.getEntity());
if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
throw new ServiceException(JSONObject.parseObject(bodyAsString));
}
return JSONObject.parseObject(bodyAsString, OrderResultInfo.class);
} catch (IOException e) {
throw new ServiceException(e);
}
} /**
* @param orderNo 订单号
* @param subMchid 子商户号
* @description: 关闭订单
*/
public void closeOrder(String orderNo, String subMchid) {
String url = "https://api.mch.weixin.qq.com/v3/pay/partner/transactions/out-trade-no/" + orderNo + "/close";
//构造参数
JSONObject requestParams = new JSONObject();
requestParams.put("sp_mchid", wxconfig.getMerchantId());
requestParams.put("sub_mchid", subMchid);
//构造请求
HttpPost httpPost = new HttpPost(url);
httpPost.addHeader("Accept", "application/json");
httpPost.addHeader("Content-type", "application/json; charset=utf-8");
httpPost.setEntity(new StringEntity(requestParams.toJSONString(), StandardCharsets.UTF_8));
try {
CloseableHttpResponse response = httpClient.execute(httpPost);
if (response.getStatusLine().getStatusCode() != HttpStatus.SC_NO_CONTENT) {
throw new ServiceException(response.getEntity());
}
} catch (IOException e) {
throw new ServiceException(e.getMessage());
} } /**
* 退款
*/
public RefundResultInfo refund(RefundBO bo) {
//1. 构造参数
JSONObject requestParams = new JSONObject();
//子商户号
requestParams.put("sub_mchid", bo.getSubMchId());
//服务商订单号
requestParams.put("out_trade_no", bo.getOrderNo());
//退款单号
requestParams.put("out_refund_no", bo.getRefundNo());
requestParams.put("reason", bo.getReason());
JSONObject amount = new JSONObject();
//退款金额,单位分
amount.put("refund", bo.getRefund());
//原订单金额,单位分
amount.put("total", bo.getTotal());
amount.put("currency", "CNY");
requestParams.put("amount", amount); //2. 初始化post请求
HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/refund/domestic/refunds");
httpPost.addHeader("Accept", "application/json");
httpPost.addHeader("Content-type", "application/json; charset=utf-8");
httpPost.setEntity(new StringEntity(requestParams.toJSONString(), StandardCharsets.UTF_8));
try {
CloseableHttpResponse httpResponse = httpClient.execute(httpPost);
String resString = EntityUtils.toString(httpResponse.getEntity());
if (httpResponse.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
throw new ServiceException(JSONObject.parseObject(resString));
}
return JSONObject.parseObject(resString, RefundResultInfo.class);
} catch (IOException e) {
throw new ServiceException(e);
}
} /**
* @param request
* @Description 回调请求的验签、解密
*/
public OrderResultInfo asyncCallback(HttpServletRequest request) {
//1.获取请求头
String body = getRequestBody(request);
String serialNumber = request.getHeader("Wechatpay-Serial");
String timeStamp = request.getHeader("Wechatpay-Timestamp");
String nonce = request.getHeader("Wechatpay-Nonce");
String signature = request.getHeader("Wechatpay-Signature");
//2.构造请求
NotificationRequest notificationRequest = new NotificationRequest.Builder().withSerialNumber(serialNumber)
.withNonce(nonce)
.withTimestamp(timeStamp)
.withSignature(signature)
.withBody(body)
.build();
NotificationHandler handler = new NotificationHandler(verifier, wxconfig.getApiV3key().getBytes(StandardCharsets.UTF_8));
// 验签和解析请求体
try {
Notification notification = handler.parse(notificationRequest);
return JSONObject.parseObject(notification.getDecryptData(), OrderResultInfo.class);
} catch (ValidationException | ParseException e) {
throw new ServiceException(e);
}
} /**
* 微信上传图片/视频
*/
public String upload(MultipartFile multipartFile, UploadEnums enums) throws IOException {
String sha256 = DigestUtils.sha256Hex(multipartFile.getBytes());
HttpPost httpPost = new WechatPayUploadHttpPost
.Builder(URI.create("https://api.mch.weixin.qq.com/v3/merchant/media/" + enums.getValue()))
.withImage(multipartFile.getOriginalFilename(), sha256,
new ByteArrayInputStream(multipartFile.getBytes()))
.build();
CloseableHttpResponse response = httpClient.execute(httpPost);
String resString = EntityUtils.toString(response.getEntity());
JSONObject resParams = JSONObject.parseObject(resString);
if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
throw new ServiceException("请求失败", resParams);
}
return resParams.getString("media_id");
} /**
* @param text 明文
* @return 加密后的数据
*/
public String encrypt(String text) {
// 建议从Verifier中获得微信支付平台证书,或使用预先下载到本地的平台证书文件中
X509Certificate certificate = verifier.getValidCertificate();
try {
return RsaCryptoUtil.encryptOAEP(text, certificate);
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
}
return null;
} /**
* @param ciphertext 密文
* @return 解密后端数据
*/
public String decrypt(String ciphertext) {
// 使用商户私钥解密
try {
return RsaCryptoUtil.decryptOAEP(ciphertext, wxconfig.privateKey);
} catch (BadPaddingException e) {
e.printStackTrace();
}
return null;
} /**
* 获取POST请求的json参数
*/
private String getRequestBody(HttpServletRequest request) {
try (BufferedReader streamReader = new BufferedReader(new InputStreamReader(request.getInputStream(), StandardCharsets.UTF_8))) {
StringBuilder responseStrBuilder = new StringBuilder();
String inputStr;
while ((inputStr = streamReader.readLine()) != null) {
responseStrBuilder.append(inputStr);
}
return responseStrBuilder.toString();
} catch (IOException e) {
throw new RuntimeException(e);
}
} }

service里面有个方法是服务商进件的方法,里面的请求参数非常多,建议参考微信支付-开发者文档 (qq.com)

微信支付服务商api对接的更多相关文章

  1. 微信支付服务商模式(受理机构模式)开发注意事项,jsapi支付

    1.首先下载的demo,一般都是有些bug的,先要改一下. 2.微信貌似没有为服务商模式单独开发demo,下载的也都是普通商户的支付demo,其实这里没有必要单独写,因为他们区别就是几个参数的区别. ...

  2. 微信支付JS API使用心得

    微信的接口真的很坑爹,只返回成功或失败,从来不会告诉你为什么失败.这个微信支付的js接口也是调了一个下午才成功,期间踩了不少坑,在这里总结一下,而且把支付接口封装成了一个js文件,这样以后调用就很方便 ...

  3. Java 微信支付分对接记录 (先享后付)

    微信支付分(先享后付)对接记录: 微信支付分对接步骤 填写开通支付分的申请表格 此步骤大概需要审核 1-3 个工作日; (模板-服务信息配置表-[先享后付免确认]-[商户名].xls) 填写商户信息 ...

  4. Java中的微信支付(1):API V3版本签名详解

    1. 前言 最近在折腾微信支付,证书还是比较烦人的,所以有必要分享一些经验,减少你在开发微信支付时的踩坑.目前微信支付的API已经发展到V3版本,采用了流行的Restful风格. 今天来分享微信支付的 ...

  5. C#开发微信门户及应用(32)--微信支付接入和API封装使用

    在微信的应用上,微信支付是一个比较有用的部分,但也是比较复杂的技术要点,在微商大行其道的年代,自己的商店没有增加微信支付好像也说不过去,微信支付旨在为广大微信用户及商户提供更优质的支付服务,微信的支付 ...

  6. 微信支付.NET版开发总结(JS API),好多坑,适当精简

    前2天,做一个手机网页的微信支付的项目,费了好些周折,记录一下.接下来,按照开发步骤,细数一下,我遇到的那些坑. [坑1]官方邮件中下载的demo只有PHP版本,其他版本没有给链接.可能让人误以为只有 ...

  7. 微信支付开发(3) JS API支付

    由于微信支付接口更新,本文档已过期,请查看新版微信支付教程.地址 http://www.cnblogs.com/txw1958/category/624506.html 本文介绍如何使用JS API支 ...

  8. 微信支付开发若干问题总结,API搞死人(谢谢ζั͡ޓއއއ๓http://www.thinkphp.cn/code/1620.html)血淋淋的教训,第二次栽这里了

    近日,我研究了微信支付的API,我是用简化版的API,首先简述一下流程: 1.通过APP_ID,APP_SCRECT获取网页授权码code, 2.利用code获取用户openid/userinfo 3 ...

  9. 微信支付.NET版开发总结(JS API),好多坑,适当精简。

    前2天,做一个手机网页的微信支付的项目,费了好些周折,记录一下.接下来,按照开发步骤,细数一下,我遇到的那些坑. [坑1]官方邮件中下载的demo只有PHP版本,其他版本没有给链接.可能让人误以为只有 ...

  10. 微信支付之h5方式(非微信内置浏览器中支付)

    这两天完成了公司网站手机和PC端的支付对接,就是支付宝和微信. 对接完后有所感触,我们来聊一聊,微信支付的坑,为什么这么说呢,因为我在对接完支付宝后是很愉快的,基本上在demo上稍加修改就ok了, 对 ...

随机推荐

  1. nginx auth_basic uwsgi 目录简易认证。

    mkdir /etc/nginx/conf.d/auth_pwd touch /etc/nginx/conf.d/auth_pwd/xx.pwd htpasswd -c -d /etc/nginx/c ...

  2. Github快速访问

    Github快速访问 1. 国内访问github慢     github是国外网站,用国内的网络很难访问到,也就无法使用github,作为程序猿的我们,无法使用github可太难受了,那么我们有什么办 ...

  3. Django 之 Form

    forms组件 1. 校验字段功能 针对一个实例:注册用户讲解. 模型:models.py class UserInfo(models.Model): name=models.CharField(ma ...

  4. trzcopy

    @echo offcd /d %~dp0setlocal enabledelayedexpansionset aa=伟大的中国!我为你自豪echo 替换前:%aa%echo 替换后:%aa:中国=中华 ...

  5. Django中models下常用Field以及字段参数

    常见的FieldType数据库字段类型     1.AutoField:自增Field域,自动增加的一个数据库字段类型,例如id字段就可以使用该数据类型,参数中必须填入primary_key=True ...

  6. 7种实现web实时消息推送的方案

    做了一个小破站,现在要实现一个站内信web消息推送的功能,对,就是下图这个小红点,一个很常用的功能. 不过他还没想好用什么方式做,这里我帮他整理了一下几种方案,并简单做了实现. 什么是消息推送(pus ...

  7. 第三课 Hello World显示

    HelloWorld 1.新建文件件 2.新建java文件 3.编写代码 public class Hello{ public static void main(String[] args){ Sys ...

  8. scala流程控制

    1.分支控制if-else 分支控制有三种:单分支.双分支.多分支: 1.1 单分支 (1).语法入下: if(条件表达式){ 执行代码块       //当条件表达式为true时,才会执行代码块内容 ...

  9. 【pyqtgraph】pyqtgraph可移动竖线LineSegmentROI的拖拽事件相关

    情景 Python+PyQt+pyqtgraph读取数据绘图,并在图像上添加了LineSegmentROI带handle的竖线(hanlde是为了RectROI的拖动),现要实现竖线可以直接拖动,并在 ...

  10. 2.21(html)