赞
踩
河南循中网络科技有限公司 - 精心创作,详细分解,按照步骤,均可成功!
科技应该以人为本,为人服务为宗旨,API的对接应该是很轻松的,就像支付宝一样,傻瓜式对接,哪怕是腾讯系的腾讯云相关的API接口服务,都非常容易对接,无论是文档还是Demo都非常舒服,哪怕小白也可以对接成功,但微信支付就很奇葩…之前对接v2版本支付系列接口几乎没有一次轻松的,现在的V3接口虽然好很多,但官方Demo写的…啥也不是。
不适合小白入门,网上关于微信支付V3的文章又少之又少,而且坑巨多…尤其是使用了某些工具类,然后少放一些,是让人呕血?搞不懂!!!,在此我推出如下傻瓜式对接,小白也可完成微信支付V3相关API的对接!!!
实现的接口:JSAPI下单、小程序支付、申请退款
其余接口,可通过对应的微信支付API文档,根据不同的请求方式,调用对应的POST或GET请求工具方法,传入JSON对象即可完成,无需担心证书相关问题,证书封装在请求体内,直接调用封装好的POST或GET请求工具方法即可。
“商户证书”是指由商户申请的,包含商户的商户号、公司名称、公钥信息的证书。
”平台证书”是指由微信支付负责申请的,包含微信支付平台标识、公钥信息的证书。
通过完成接入前准备,可获得3个API证书:apiclient_cert.p12、apiclient_cert.pem、apiclient_key.pem,但此证书为商户证书,跟平台证书是不一样的。
大多数人在接入时,由于官方文档写的很…令人无语会忽略此步,博主就是忽略了此步骤,从而在调用API的时候,错误的使用了商户证书,导致报错:“应答的微信支付签名验证失败”。
java -jar CertificateDownloader-1.2.0-jar-with-dependencies.jar -f 商户私钥文件路径 -k 证书解密的密钥 -m 商户号 -o 证书保存路径 -s 商户证书序列号
商户私钥文件:apiclient_key.pem
证书解密的密钥:登陆商户平台【API安全】->【APIv3密钥】
商户证书序列号:登陆商户平台【API安全】->【API证书】->【查看证书】,可查看商户API证书序列号。
实践:
java -jar CertificateDownloader-1.2.0-jar-with-dependencies.jar -f 商户私钥文件路径 -k 证书解密的密钥 -m 商户号 -o 证书保存路径 -s 商户证书序列号
,则可得到微信支付平台证书。 <!-- 微信支付 -->
<wechatpay-apache-httpclient.version>0.4.8</wechatpay-apache-httpclient.version>
<!-- Hutool小而全的Java工具类库 -->
<hutool-all.version>5.8.5</hutool-all.version>
<!-- alibaba JSON -->
<fastjson.version>2.0.11</fastjson.version>
<!-- apache公共基础类 -->
<commons-lang3.version>3.12.0</commons-lang3.version>
<!-- 微信支付 --> <dependency> <groupId>com.github.wechatpay-apiv3</groupId> <artifactId>wechatpay-apache-httpclient</artifactId> <version>${wechatpay-apache-httpclient.version}</version> </dependency> <!-- Hutool小而全的Java工具类库 --> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>${hutool-all.version}</version> </dependency> <!-- alibaba JSON --> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>${fastjson.version}</version> </dependency> <!-- apache公共基础类 --> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>${commons-lang3.version}</version> </dependency>
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.xz</groupId> <artifactId>common</artifactId> <version>0.0.1-SNAPSHOT</version> <name>common</name> <description>河南循中网络科技有限公司 - 通用工具</description> <!-- 子模块打包类型必须为jar --> <packaging>jar</packaging> <!-- parent指明继承关系,给出被继承的父项目的具体信息 --> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.7.1</version> <relativePath/> <!-- lookup parent from repository --> </parent> <!-- 版本控制 --> <properties> <java.version>1.8</java.version> <!-- 实体类注解 --> <lombok.version>1.18.24</lombok.version> <!-- swagger --> <springfox-boot-starter.version>3.0.0</springfox-boot-starter.version> <!-- 腾讯云cos存储 --> <cos_api.version>5.6.89</cos_api.version> <!-- alibaba JSON --> <fastjson.version>2.0.11</fastjson.version> <!-- apache公共基础类 --> <commons-lang3.version>3.12.0</commons-lang3.version> <!-- 微信支付 --> <wechatpay-apache-httpclient.version>0.4.8</wechatpay-apache-httpclient.version> <!-- Hutool小而全的Java工具类库 --> <hutool-all.version>5.8.5</hutool-all.version> </properties> <!-- 引入的jar包 --> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!-- 实体类注解 --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>${lombok.version}</version> </dependency> <!-- swagger --> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-boot-starter</artifactId> <version>${springfox-boot-starter.version}</version> </dependency> <!-- spring boot内置redis --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <!-- 腾讯云cos存储 --> <dependency> <groupId>com.qcloud</groupId> <artifactId>cos_api</artifactId> <version>${cos_api.version}</version> </dependency> <!-- alibaba JSON --> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>${fastjson.version}</version> </dependency> <!-- apache公共基础类 --> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>${commons-lang3.version}</version> </dependency> <!-- 微信支付 --> <dependency> <groupId>com.github.wechatpay-apiv3</groupId> <artifactId>wechatpay-apache-httpclient</artifactId> <version>${wechatpay-apache-httpclient.version}</version> </dependency> <!-- Hutool小而全的Java工具类库 --> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>${hutool-all.version}</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
weChatPay: # 支付回调地址 notifyUrl: 支付回调地址 # 退款回调地址 refundNotifyUrl: 退款回调地址 # 商户号 mchId: 商户号 # 公众号appId appid: 公众号appId # 公众号secret secret: 公众号secret # APIv3密钥 apiV3Key: API v3密钥 # 小程序appId miniProgramAppid: 小程序appId # 小程序secret miniProgramSecret: 小程序secret # 商户证书-apiclient_cert.p12路径 apiclientCertp12: 商户证书-apiclient_cert.p12路径 # 商户证书公钥-apiclient_cert.pem路径 apiclientCert: 商户证书公钥-apiclient_cert.pem路径 # 商户证书私钥-apiclient_key.pem路径 apiclientKey: 商户证书私钥-apiclient_key.pem路径 # 微信支付平台证书,有效期五年,xxxx年xx月份之前进行替换 wechatPayCert: 微信支付平台证书,有效期五年,xxxx年xx月份之前进行替换 # 商户证书序列号 wechatPaySerial: 商户证书序列号
package com.xz.weChat; import cn.hutool.core.io.resource.ClassPathResource; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder; import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner; import com.wechat.pay.contrib.apache.httpclient.auth.Verifier; import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials; import com.wechat.pay.contrib.apache.httpclient.cert.CertificatesManager; import com.wechat.pay.contrib.apache.httpclient.notification.Notification; import com.wechat.pay.contrib.apache.httpclient.notification.NotificationHandler; import com.wechat.pay.contrib.apache.httpclient.notification.NotificationRequest; import com.wechat.pay.contrib.apache.httpclient.util.PemUtil; import com.xz.redis.RedisUtil; import com.xz.util.NumberUtil; import org.apache.commons.lang3.RandomStringUtils; import org.apache.commons.lang3.StringUtils; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.util.EntityUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import javax.annotation.Resource; import javax.servlet.ServletInputStream; import javax.servlet.http.HttpServletRequest; import java.io.*; import java.math.BigDecimal; import java.net.HttpURLConnection; import java.net.URL; import java.nio.charset.StandardCharsets; import java.security.PrivateKey; import java.security.Signature; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Arrays; import java.util.Base64; import static org.apache.http.HttpHeaders.ACCEPT; import static org.apache.http.HttpHeaders.CONTENT_TYPE; import static org.apache.http.entity.ContentType.APPLICATION_JSON; /** * 微信V3工具类 */ @Component public class WeChatPayUtil { private Logger logger = LoggerFactory.getLogger(this.getClass()); @Value("${weChatPay.notifyUrl}") private String notifyUrl; @Value("${weChatPay.refundNotifyUrl}") private String refundNotifyUrl; @Value("${weChatPay.mchId}") private String mchId; @Value("${weChatPay.apiV3Key}") private String apiV3Key; @Value("${weChatPay.miniProgramAppid}") private String miniProgramAppid; @Value("${weChatPay.miniProgramSecret}") private String miniProgramSecret; @Value("${weChatPay.apiclientKey}") private String apiclientKey; @Value("${weChatPay.wechatPayCert}") private String wechatPayCert; @Value("${weChatPay.wechatPaySerial}") private String wechatPaySerial; @Resource private RedisUtil redisUtil; /** * 普通get请求 * @param url * @return */ public String doGet(String url){ BufferedReader read = null; try { URL URL = new URL(url); read = new BufferedReader(new InputStreamReader(URL.openStream(),"utf-8")); StringBuffer str = new StringBuffer(); String buf = ""; buf = read.readLine(); while (buf != null && !buf.equals("")) { str.append(buf); buf = read.readLine(); } return str.toString(); } catch (Exception e) { e.printStackTrace(); } finally{ try{ if(read!=null) read.close(); } catch(IOException ex){ ex.printStackTrace(); } } return null; } /** * 普通post请求 * @param url * @param param * @return * @throws Exception */ public String doPost(String url, String param)throws Exception { PrintWriter out = null; BufferedReader read = null; String result = ""; try { URL URL = new URL(url); HttpURLConnection conn = (HttpURLConnection)URL.openConnection(); conn.setRequestProperty("Accept","application/json"); conn.setRequestProperty("Content-type","application/json"); conn.setDoOutput(true); conn.setDoInput(true); conn.setUseCaches(false);// 忽略缓存 conn.setRequestMethod("POST"); conn.setConnectTimeout(20000); conn.setReadTimeout(300000); out = new PrintWriter(conn.getOutputStream()); out.print(param); out.flush(); // 获得响应状态 int responseCode = conn.getResponseCode(); if (HttpURLConnection.HTTP_OK == responseCode) {// 连接成功 read = new BufferedReader( new InputStreamReader(conn.getInputStream())); String line; while ((line = read.readLine()) != null) { result += line; } }else{ throw new Exception("请求链接失败"); } } catch (IOException e) { throw e; } finally{ try{ if(out!=null) out.close(); if(read!=null) read.close(); } catch(IOException ex){ ex.printStackTrace(); } } return result; } /** * 微信v3 post请求 * @param url * @param body * @return * @throws Exception */ public String v3Post(String url,String body) throws Exception { logger.info("微信v3 post请求参数:" + body); CloseableHttpClient httpClient = getClient(); HttpPost httpPost = new HttpPost(url); httpPost.addHeader(ACCEPT, APPLICATION_JSON.toString()); httpPost.addHeader(CONTENT_TYPE, APPLICATION_JSON.toString()); httpPost.addHeader("Wechatpay-Serial", wechatPaySerial); httpPost.setEntity(new StringEntity(body, "UTF-8")); CloseableHttpResponse response = httpClient.execute(httpPost); try { String bodyAsString = EntityUtils.toString(response.getEntity()); logger.info("微信v3 post请求返回信息:"+bodyAsString); return bodyAsString; } finally { httpClient.close(); response.close(); } } /** * 微信v3 get请求 * @param url * @return * @throws Exception */ public String v3Get(String url) throws Exception { logger.info("微信v3 get请求URL:" + url); CloseableHttpClient httpClient = getClient(); HttpGet httpGet = new HttpGet(url); httpGet.addHeader(ACCEPT, APPLICATION_JSON.toString()); httpGet.addHeader(CONTENT_TYPE, APPLICATION_JSON.toString()); httpGet.addHeader("Wechatpay-Serial", wechatPaySerial); CloseableHttpResponse response = httpClient.execute(httpGet); try { String bodyAsString = EntityUtils.toString(response.getEntity()); logger.info("微信v3 get请求返回信息:"+bodyAsString); return bodyAsString; } finally { httpClient.close(); response.close(); } } /** * 小程序快捷登录 * @param jsCode * @return * @throws Exception */ public String miniJscode2session(String jsCode) throws Exception { String url = "https://api.weixin.qq.com/sns/jscode2session?appid=" + miniProgramAppid + "&secret=" + miniProgramSecret + "&js_code=" + jsCode + "&grant_type=authorization_code"; return doGet(url); } /** * 小程序获取手机号 * @param code * @return * @throws Exception */ public JSONObject miniGetuserphonenumber(String code) throws Exception { String accessToken = getAccessTokenMini(); String Url = "https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token="+accessToken; String getPhoneNumber = doPost(Url,"{\"code\": \""+code+"\"}"); JSONObject getPhoneNumberJSON = JSON.parseObject(getPhoneNumber); return getPhoneNumberJSON; } /** * 微信通讯client * @return CloseableHttpClient */ public CloseableHttpClient getClient() { /**商户证书私钥文件*/ ClassPathResource apiclientKeyFile = new ClassPathResource(apiclientKey); InputStream apiclientKeyInputStream = apiclientKeyFile.getStream(); /**微信支付平台证书文件*/ ClassPathResource wechatPayCertFile = new ClassPathResource(wechatPayCert); InputStream wechatPayCertInputStream = wechatPayCertFile.getStream(); PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(apiclientKeyInputStream); WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create() .withMerchant(mchId, wechatPaySerial, merchantPrivateKey) .withWechatPay(Arrays.asList(PemUtil.loadCertificate(wechatPayCertInputStream))); CloseableHttpClient httpClient = builder.build(); return httpClient; } /** * 微信敏感数据加密公钥 * 使用方法: * X509Certificate x509Certificate = getSaveCertificates(); * RsaCryptoUtil.encryptOAEP(要加密的数据, x509Certificate) * @return */ public X509Certificate getSaveCertificates() { ClassPathResource wechatPayCertFile = new ClassPathResource(wechatPayCert); InputStream platformCertInputStream = wechatPayCertFile.getStream(); return PemUtil.loadCertificate(platformCertInputStream); } /** * 转换为分数,money小数长度不允许超过2位,如果超过2位小数,自动直接舍弃 * @param money * @return */ public int conversionFraction(BigDecimal money){ int fractionMonery = 0; if(money!=null){ fractionMonery = money.multiply(new BigDecimal(100)).intValue(); } return fractionMonery; } /** * 获取时间戳 * @return */ private String getTimeStamp() { return String.valueOf(System.currentTimeMillis() / 1000); } /** * 生成签名 * @param message * @return * @throws Exception */ public String sign(byte[] message) throws Exception { Signature sign = Signature.getInstance("SHA256withRSA"); // 商户证书私钥 sign.initSign(getPrivateKey()); sign.update(message); return Base64.getEncoder().encodeToString(sign.sign()); } /** * 获取商户证书私钥 * @return 私钥对象 */ public PrivateKey getPrivateKey() throws IOException { ClassPathResource apiclientKeyFile = new ClassPathResource(apiclientKey); PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(apiclientKeyFile.getStream()); return merchantPrivateKey; } /** * 构造签名串 * @param signMessage 待签名的参数 * @return 构造后带待签名串 */ public String buildSignMessage(ArrayList<String> signMessage) { if (signMessage == null || signMessage.size() <= 0) { return null; } StringBuilder sbf = new StringBuilder(); for (String str : signMessage) { sbf.append(str).append("\n"); } return sbf.toString(); } /** * 获取微信回调结果 * @param request * @return * @throws Exception */ public JSONObject getCallback(HttpServletRequest request) throws Exception { //获取报文 String body = getRequestBody(request); //随机串 String nonce = request.getHeader("Wechatpay-Nonce"); //微信传递过来的签名 String signature = request.getHeader("Wechatpay-Signature"); //证书序列号(微信平台) String wechatPaySerial = request.getHeader("Wechatpay-Serial"); //时间戳 String timestamp = request.getHeader("Wechatpay-Timestamp"); // 验签 NotificationRequest notificationRequest = new NotificationRequest.Builder() .withSerialNumber(wechatPaySerial) .withNonce(nonce) .withTimestamp(timestamp) .withSignature(signature) .withBody(body) .build(); NotificationHandler handler = new NotificationHandler(verifier(), apiV3Key.getBytes(StandardCharsets.UTF_8)); // 验签和解析请求体 Notification notification = handler.parse(notificationRequest); return JSON.parseObject(notification.getDecryptData()); } /** * 获取报文 * @param request * @return * @throws Exception */ private String getRequestBody(HttpServletRequest request) throws Exception { StringBuffer stringBuffer = new StringBuffer(); ServletInputStream inputStream = request.getInputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); String line; while ((line = reader.readLine()) != null) { stringBuffer.append(line); } return stringBuffer.toString(); } /** * 获得验签器 * @return * @throws Exception */ public Verifier verifier() throws Exception { //获取证书管理器实例 CertificatesManager certificatesManager = CertificatesManager.getInstance(); certificatesManager.putMerchant(mchId, new WechatPay2Credentials(mchId,new PrivateKeySigner(wechatPaySerial, getPrivateKey())), apiV3Key.getBytes(StandardCharsets.UTF_8)); // 从证书管理器中获取verifier Verifier verifier = certificatesManager.getVerifier(mchId); return verifier; } /** * JSAPI下单 * @param appid 对应openid的应用ID * @param description 商品描述 * @param orderNum 商户订单号 * @param total 总金额 * @param openid 个人用户openId * @throws Exception */ public String transactionsJsapi(String appid, String description, String orderNum, BigDecimal total, String openid) throws Exception { JSONObject transactionsJsapiJSON = new JSONObject(); transactionsJsapiJSON.put("appid", appid); transactionsJsapiJSON.put("mchid", mchId); transactionsJsapiJSON.put("description", description); transactionsJsapiJSON.put("out_trade_no", orderNum); transactionsJsapiJSON.put("notify_url",notifyUrl); JSONObject amount = new JSONObject(); amount.put("total",conversionFraction(total)); amount.put("currency","CNY"); transactionsJsapiJSON.put("amount",amount); JSONObject payer = new JSONObject(); payer.put("openid",openid); transactionsJsapiJSON.put("payer",payer); String body = transactionsJsapiJSON.toJSONString(); return v3Post("https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi",body); } /** * 小程序支付 * @param orderNum 商户订单号 * @param total 总金额 * @param openid 个人用户openId * @throws Exception */ public JSONObject payMini(String orderNum,BigDecimal total,String openid) throws Exception { String transactionsJsapiBody = transactionsJsapi(miniProgramAppid,"么丢",orderNum,total,openid); JSONObject transactionsJsapiBodyJSON = JSON.parseObject(transactionsJsapiBody); String prepayId = transactionsJsapiBodyJSON.getString("prepay_id"); if(StringUtils.isNotBlank(prepayId)){ String timeStamp = getTimeStamp(); String nonceStr = RandomStringUtils.randomNumeric(32); ArrayList<String> list = new ArrayList<>(); list.add(miniProgramAppid); list.add(timeStamp); list.add(nonceStr); list.add("prepay_id=" + prepayId); //二次签名,调起支付需要重新签名,注意这个是buildSignMessage() String packageSign = sign(buildSignMessage(list).getBytes()); JSONObject jsonObject = new JSONObject(); jsonObject.put("timeStamp", timeStamp); jsonObject.put("nonceStr", nonceStr); jsonObject.put("package", "prepay_id=" + prepayId); jsonObject.put("signType", "RSA"); jsonObject.put("paySign", packageSign); return jsonObject; }else{ return transactionsJsapiBodyJSON; } } /** * 申请退款 * @param transactionId 微信支付订单号 * @param outRefundNo 商户退款单号 * @param refund 退款金额 * @param total 原订单金额 * @return * @throws Exception */ public String domesticRefunds(String transactionId,String outRefundNo,BigDecimal refund,BigDecimal total) throws Exception { JSONObject domesticRefundsJSON = new JSONObject(); domesticRefundsJSON.put("transaction_id", transactionId); domesticRefundsJSON.put("out_refund_no", outRefundNo); JSONObject amount = new JSONObject(); amount.put("refund",conversionFraction(refund)); amount.put("total",conversionFraction(total)); amount.put("currency","CNY"); domesticRefundsJSON.put("amount",amount); domesticRefundsJSON.put("notify_url", refundNotifyUrl); String body = domesticRefundsJSON.toJSONString(); String domesticRefundsBody = v3Post("https://api.mch.weixin.qq.com/v3/refund/domestic/refunds",body); JSONObject domesticRefundsBodyJSON = JSON.parseObject(domesticRefundsBody); if(StringUtils.isNotBlank(domesticRefundsBodyJSON.getString("status")) && (domesticRefundsBodyJSON.getString("status").equals("SUCCESS") || domesticRefundsBodyJSON.getString("status").equals("PROCESSING"))){ return "200"; }else{ return "code="+domesticRefundsBodyJSON.getString("code")+",message="+domesticRefundsBodyJSON.getString("message"); } } /** * 发起商家转账 * @param appid 对应openid的应用ID * @param outBatchNo 转账单号 * @param batchName 转账的名称 * @param batchRemark 转账说明 * @param totalAmount 转账金额 * @param openid 个人用户openId * @return * @throws Exception */ public String transferBatches(String appid,String outBatchNo,String batchName,String batchRemark,BigDecimal totalAmount,String openid) throws Exception { JSONObject transactionsJsapiJSON = new JSONObject(); transactionsJsapiJSON.put("appid", appid); transactionsJsapiJSON.put("out_batch_no", outBatchNo); transactionsJsapiJSON.put("batch_name", batchName); transactionsJsapiJSON.put("batch_remark", batchRemark); transactionsJsapiJSON.put("total_amount", NumberUtil.conversionFraction(totalAmount)); transactionsJsapiJSON.put("total_num", 1); JSONArray transferDetailList = new JSONArray(); JSONObject transferDetail = new JSONObject(); transferDetail.put("out_detail_no", outBatchNo); transferDetail.put("transfer_amount", NumberUtil.conversionFraction(totalAmount)); transferDetail.put("transfer_remark", batchRemark); transferDetail.put("openid", openid); transferDetailList.add(transferDetail); transactionsJsapiJSON.put("transfer_detail_list", transferDetailList); String body = transactionsJsapiJSON.toJSONString(); return v3Post("https://api.mch.weixin.qq.com/v3/transfer/batches",body); } /** * 小程序发起商家转账 * @param outBatchNo 转账单号 * @param batchName 转账的名称 * @param batchRemark 转账说明 * @param totalAmount 转账金额 * @param openid 个人用户openId * @return * @throws Exception */ public String miniTransferBatches(String outBatchNo,String batchName,String batchRemark,BigDecimal totalAmount,String openid) throws Exception { String transferBatchesStr = transferBatches(miniProgramAppid,outBatchNo,batchName,batchRemark,totalAmount,openid); JSONObject transferBatchesJSON = JSON.parseObject(transferBatchesStr); if(StringUtils.isNotBlank(transferBatchesJSON.getString("out_batch_no")) && StringUtils.isNotBlank(transferBatchesJSON.getString("batch_id")) && StringUtils.isNotBlank(transferBatchesJSON.getString("create_time"))){ return "200"; }else{ return transferBatchesStr; } } /** * 获取基础access_token * @param appid 对应openid的应用ID * @param secret 对应openid的secret * @return * @throws Exception */ public JSONObject getAccessToken(String appid,String secret) throws Exception { // 获取accres_token String Url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + appid + "&secret=" + secret; String doGet = doGet(Url); // 结果转为json对象 if(StringUtils.isNotBlank(doGet)){ JSONObject json = JSONObject.parseObject(doGet); if(json!=null){ return json; }else{ return null; } }else{ return null; } } /** * 获取小程序基础access_token * @return * @throws Exception */ public String getAccessTokenMini() throws Exception { String accessToken = null; if(redisUtil.get("accessToken")!=null){ accessToken = redisUtil.get("accessToken").toString(); } if(StringUtils.isBlank(accessToken)){ JSONObject accessTokenJSON = getAccessToken(miniProgramAppid,miniProgramSecret); if(StringUtils.isBlank(accessTokenJSON.getString("errcode"))){ accessToken = accessTokenJSON.getString("access_token"); redisUtil.set("accessToken",accessToken); int expires_in = accessTokenJSON.getInteger("expires_in")-120;//基础token的时间减去2分钟 redisUtil.expire("accessToken",expires_in);//将基础token存入redis return accessToken; }else{ logger.info(accessTokenJSON.getString("errmsg")); return null; } }else{ return accessToken; } } /** * 检测内容安全方法 * 文档:https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/sec-check/security.msgSecCheck.html * @param openid 用户的openid(用户需在近两小时访问过小程序) * @param scene 场景枚举值(1 资料;2 评论;3 论坛;4 社交日志) * @param content 需检测的文本内容,文本字数的上限为2500字,需使用UTF-8编码 * @param accessToken 接口调用凭证 * @return */ public String msgSecCheck(String openid,Integer scene,String content, String accessToken) throws Exception { JSONObject msgSecCheckJSON = new JSONObject(); msgSecCheckJSON.put("version", "2"); msgSecCheckJSON.put("openid", openid); msgSecCheckJSON.put("scene", scene); msgSecCheckJSON.put("content", content); return v3Post("https://api.weixin.qq.com/wxa/msg_sec_check?access_token="+accessToken, msgSecCheckJSON.toJSONString()); } /** * 小程序检测内容安全方法 * 文档:https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/sec-check/security.msgSecCheck.html * @param openid 用户的openid(用户需在近两小时访问过小程序) * @param scene 场景枚举值(1 资料;2 评论;3 论坛;4 社交日志) * @param content 需检测的文本内容,文本字数的上限为2500字,需使用UTF-8编码 * @return */ public JSONObject msgSecCheckMini(String openid,Integer scene,String content) throws Exception { JSONObject data = new JSONObject(); String returnData = msgSecCheck(openid, scene, content, getAccessTokenMini()); JSONObject returnDataJSON = JSON.parseObject(returnData); if(returnDataJSON!=null && returnDataJSON.getString("errcode").equals("0")){ JSONObject resultJSON = returnDataJSON.getJSONObject("result"); if(resultJSON!=null && resultJSON.getString("suggest").equals("pass")){ data.put("code", 200); data.put("msg", "正常"); return data; }else{ data.put("code", 400); switch (resultJSON.getString("label")){ case "100": data.put("msg", "正常"); break; case "10001": data.put("msg", "广告"); break; case "20001": data.put("msg", "时政"); break; case "20002": data.put("msg", "色情"); break; case "20003": data.put("msg", "辱骂"); break; case "20006": data.put("msg", "违法犯罪"); break; case "20008": data.put("msg", "欺诈"); break; case "20012": data.put("msg", "低俗"); break; case "20013": data.put("msg", "版权"); break; case "21000": data.put("msg", "其他"); break; default: data.put("msg", "其他"); break; } return data; } }else{ data.put("code", 400); data.put("msg", "接口请求异常"+returnDataJSON.toJSONString()); return data; } } }
package com.xz.controller.pay; import com.alibaba.fastjson.JSONObject; import com.xz.weChat.WeChatPayUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * 微信支付回调 */ @RestController public class WeChatPayController { private Logger logger = LoggerFactory.getLogger(this.getClass()); @Resource private WeChatPayUtil weChatPayUtil; /** * v3处理微信支付异步回调 * @param request * @param response */ @RequestMapping("/clientWeChatNotify") public void clientWeChatNotify(HttpServletRequest request, HttpServletResponse response) throws Exception { try{ //获取微信回调结果 JSONObject dataJSON = weChatPayUtil.getCallback(request); logger.info("v3处理微信支付异步回调=============================================================="+dataJSON); String out_trade_no = dataJSON.getString("out_trade_no");//商户平台订单号 String transaction_id = dataJSON.getString("transaction_id");//微信支付交易号 //处理业务逻辑 //返回给微信 response.setStatus(200); }catch (Exception e){ e.printStackTrace(); logger.error("v3处理微信支付异步回调:错误-"+e.getMessage()); } } /** * v3处理微信退款异步回调 * @param request * @param response */ @RequestMapping("/refundWeChatNotify") public void refundWeChatNotify(HttpServletRequest request, HttpServletResponse response) throws Exception { try{ //获取微信回调结果 JSONObject dataJSON = weChatPayUtil.getCallback(request); logger.info("v3处理微信退款异步回调=============================================================="+dataJSON); String outRefundNo = dataJSON.getString("out_refund_no");//商户退款单号 String refundStatus = dataJSON.getString("refund_status");//退款状态,枚举值:SUCCESS:退款成功、CLOSED:退款关闭、ABNORMAL:退款异常,退款到银行发现用户的卡作废或者冻结了,导致原路退款银行卡失败,可前往【商户平台—>交易中心】,手动处理此笔退款 //处理业务逻辑 //返回给微信 response.setStatus(200); }catch (Exception e){ e.printStackTrace(); logger.error("v3处理微信退款异步回调:错误-"+e.getMessage()); } } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。