框架:springDateJPA
对接微信支付首先需要需要在微信商户平台注册微信商户号
微信商户平台入口
微信公众平台入口

这里主要是微信支付的方式,一共有七种,我这里主要对接了3种,App支付,JSAPI支付,H5支付,其实都大同小异,会对接一种就会对接其他的了,只不过是参数的不同.


开通的流程,这里就不描述了,主要是提交一些资料,
APP支付: 需要一个打包到应用商店时获得的应用号.
JSAPI支付: 需要公司的服务号,还需要配置JSAP支付路径,需要获取用户的OpenID
H5支付:没有特别的参数
最后需要配置回调地址,回调地址需要外网可以直接访问到的接口全路径,逻辑其实很简单
例如: http://test.m.xxxxxx.com/jewelrybespoke/weixin/pay/callback/wxorder

开发之前首先要搞清楚,自己公司开通的是普通商户版,还是服务商版这很重要,因为对接的接口是不同的,我就被坑了

APIV3接口文档入口


服务商模式与普通商户的模式的区别就是服务商多一个子商户号参数
服务商类似与加盟商,一个用这个的,做聚合支付的公司用的比较多,
普通商户类似与个体户,其实一个公司用普通商户模式完全满足了,因为子商户会有一些限制
对接流程都是一样的,只不过是多一个子商户号的参数

如何获取OpenId的文档

我对接的就是服务商模式,如果是对接普通商户模式自己去掉子商户的参数就可以了
这里是微信支付涉及到的所有必要参数

 <dependency><groupId>com.github.wechatpay-apiv3</groupId><artifactId>wechatpay-apache-httpclient</artifactId><version>0.2.1</version></dependency><!-- OKHttp3依赖 --><dependency><groupId>com.squareup.okhttp3</groupId><artifactId>okhttp</artifactId><version>3.8.1</version></dependency>

Entity

import lombok.Getter;
import lombok.Setter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;//统一下单参数@Setter
@Getter
@Component
public class WXPayConfigEntity  {public enum SignType {MD5, HMACSHA256}//应用ID@Value(value = "${wx.appid}")private  String apppid;//商户ID@Value(value = "${wx.mchid}")private  String mch_id;//二级应用IDprivate  String sub_apppid = "暂时没有";//二级商户ID@Value(value = "${wx.sub_mchid}")private  String sub_mchid;//签名private  String sign;//终端IP@Value(value = "${wx.payer_client_ip}")private  String payer_client_ip;//API密钥key@Value(value = "${wx.apikey}")private  String apiKey ;//APIV3密钥key@Value(value = "${wx.apiv3key}")private  String apiV3Key;//支付通知回调地址@Value(value = "${wx.notify.url}")private  String notify_url;//支付公钥pash@Value(value = "${v3.certPath}")private  String certPath;//支付支付私钥pash@Value(value = "${v3.keyPath}")private  String keyPath;//支付证书pash@Value(value = "${v3.p12}")private  String p12;//平台证书pash@Value(value = "${v3.platformcertificate}")private  String platformcertificate;//支付证书序列号@Value(value = "${v3.serialNo}")private  String serialNo;//平台证书获取接口路径@Value(value = "${v3.certificates}")private  String certificates;//终端IP@Value(value = "${wx.appserect}")private  String appserect;//退款通知回调地址@Value(value = "${wx.refundnotify.url}")private  String refundnotify_url;//交易类型private final String tradetype = "APP";String authType   ="WECHATPAY2-SHA256-RSA2048";//APP下单支付路径private final String payurl ="https://api.mch.weixin.qq.com/v3/pay/partner/transactions/app";//H5下单支付路径private final String payH5url ="https://api.mch.weixin.qq.com/v3/pay/partner/transactions/h5";//Native下单支付路径private final String payNativeurl ="https://api.mch.weixin.qq.com/v3/pay/partner/transactions/native";//jspapi下单支付路径private final String payJSAPIurl ="https://api.mch.weixin.qq.com/v3/pay/partner/transactions/jsapi";//查询订单private final String paySelectUrl= "https://api.mch.weixin.qq.com/v3/pay/partner/transactions/id/{transaction_id}";//申请退款private final String payRefundurl ="https://api.mch.weixin.qq.com/secapi/pay/refund";//查询退款private final String RefundSelecturl="https://api.mch.weixin.qq.com/pay/refundquery";
}

Controller
微信支付接口,我是根据微信文档自己写的.
V3接口提供自动签名和验签自动获取最新平台证书的方法,算是目前最新的方法,
回调验签有参考这位老哥的微信支付回调参考博文
V2接口参考了我强哥的强哥的微信支付博文

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.mc.common.constant.CommonConstant;
import com.mc.common.exception.BusinessException;
import com.mc.common.utils.ResultUtils;
import com.mc.common.vo.Result;
import com.mc.jewelrybespoke.app.service.AppSkuService;
import com.mc.jewelrybespoke.app.wxutil.*;
import com.mc.jewelrybespoke.app.po.WeiXinPayPo;
import com.mc.jewelrybespoke.app.utils.LocalDateTimeUtil;
import com.mc.jewelrybespoke.app.vo.AppSalesOrderHeaderVo;
import com.mc.jewelrybespoke.entity.SalesOrderDetail;
import com.mc.jewelrybespoke.entity.SalesOrderHeader;
import com.mc.jewelrybespoke.entity.Sku;
import com.mc.jewelrybespoke.query.SalesOrderDetailQuery;
import com.mc.jewelrybespoke.service.ISalesOrderDetailService;
import com.mc.jewelrybespoke.service.ISalesOrderHeaderService;import com.wechat.pay.contrib.apache.httpclient.auth.AutoUpdateCertificatesVerifier;import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.RandomStringUtils;import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.client.config.RequestConfig;
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.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;import java.io.IOException;import java.math.BigDecimal;
import java.security.*;import java.text.ParseException;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;@Slf4j
@Api(tags = "5.0 微信支付对接")
@RestController
@RequestMapping("/weixin/pay")
public class WeiXinPayController {@Autowiredprivate WXPayConfigEntity wxPayConfigEntity; //支付参数@AutowiredWXPayUtil wxPayUtil;@Autowiredprivate ISalesOrderHeaderService salesOrderHeaderService;@Autowiredprivate ISalesOrderDetailService salesOrderDetailService;@Autowiredprivate AppSkuService appSkuService;@Autowiredprivate ClientCustomSSL clientCustomSSL;  //证书private long timestamp = LocalDateTimeUtil.getLocalDateTimeSec();private String nonceStr = RandomStringUtils.randomAlphanumeric(32);private static Lock lock = new ReentrantLock();public static final String POST = "POST";public static final String GET = "GET";@ApiOperation(value = "微信App下单,返回微信预付款订单号", notes = "传入付款金额,订单号 ,商品信息")@PostMapping("/create/wxorder")public Object createWXOrder(@RequestBody WeiXinPayPo weiXinPayPo) {String out_trade_no = weiXinPayPo.getOut_trade_no();//商品订单号SalesOrderHeader salesOrderHeader = salesOrderHeaderService.get(out_trade_no);if (salesOrderHeader == null || StringUtils.isEmpty(salesOrderHeader.getId())) {return new ResultUtils<AppSalesOrderHeaderVo>().setErrorMsg("订单不存在!");}if (!CommonConstant.STATUS_DRAFT.equals(salesOrderHeader.getStatus())) {return new ResultUtils<AppSalesOrderHeaderVo>().setErrorMsg("订单状态异常,请确认订单状态!");}SortedMap<String, Object> sortedMap = new TreeMap<String, Object>();sortedMap.put("sp_appid", wxPayConfigEntity.getApppid()); //应用IDsortedMap.put("sp_mchid", wxPayConfigEntity.getMch_id()); //商户号sortedMap.put("sub_mchid", wxPayConfigEntity.getSub_mchid());//子商户号sortedMap.put("description", "珠宝纯定制-" + weiXinPayPo.getBody()); //商品描述sortedMap.put("out_trade_no", out_trade_no); //商户订单号sortedMap.put("notify_url", wxPayConfigEntity.getNotify_url()); //通知回调地址//总金额int totalFee = weiXinPayPo.getTotal_fee().multiply(new BigDecimal(100)).intValue();//总金额  需要乘以100 ,微信支付默认单位分Map<String, Object> map1 = new HashMap<>();map1.put("total", totalFee);map1.put("currency", "CNY");//订单金额sortedMap.put("amount", map1);JSONObject json = new JSONObject(sortedMap);String reqdata = json.toJSONString();log.info(reqdata);//校验支付路径// HttpUrl httpurl = HttpUrl.parse(wxPayConfigEntity.getPayurl());//String Sign = null;//  Sign = wxPayUtil.getToken(nonceStr,timestamp,POST,httpurl,reqdata);//======设置请求头========//不需要传入微信支付证书,将会自动更新// 建议从Verifier中获得微信支付平台证书,或使用预先下载到本地的平台证书文件中// X509Certificate wechatpayCertificate = verifier.getValidCertificate();AutoUpdateCertificatesVerifier verifier = wxPayUtil.getAutoUpdateCertificatesVerifier();//通过WechatPayHttpClientBuilder构造的HttpClient,会自动的处理签名和验签CloseableHttpClient httpClient = wxPayUtil.getCloseableHttpClient();HttpPost httpPost = new HttpPost(wxPayConfigEntity.getPayurl());RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(30000).setConnectTimeout(30000).build();httpPost.setConfig(requestConfig);httpPost.addHeader("Content-Type", "application/json");httpPost.addHeader("Accept", "application/json");httpPost.addHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36");String authType = wxPayConfigEntity.getAuthType();//String authorization = wxPayUtil.getAuthorization(Sign, authType);//httpPost.addHeader("Authorization",authorization);StringEntity entity = new StringEntity(reqdata, "UTF-8");httpPost.setEntity(entity);//======发起请求========CloseableHttpResponse response = null;String result = null;try {response = httpClient.execute(httpPost);result = EntityUtils.toString(response.getEntity(), "UTF-8");} catch (IOException e) {e.printStackTrace();}Header firstHeader = response.getFirstHeader("Request-ID");String s = firstHeader.toString();log.info("Request-ID:" + s);int statusCode = response.getStatusLine().getStatusCode();Map<String, Object> map = new HashMap<>();if (statusCode == 200) {Map<String, Object> resultMap = JSONObject.parseObject(result, Map.class);String prepayId = (String) resultMap.get("prepay_id");//微信下单号salesOrderHeader.setPayAmount(weiXinPayPo.getTotal_fee());salesOrderHeader.setRefNo(prepayId);salesOrderHeader.setPaid(OrdersOperateEnum.SUBMIT.code);salesOrderHeader.setIsChannel(weiXinPayPo.getIsChannel());salesOrderHeader.setPayType(weiXinPayPo.getPayType());salesOrderHeaderService.update(salesOrderHeader); //根据付款单号修改订单表保存微信需要付款总金额,支付微信下单号String sign2 = null;try {sign2 = wxPayUtil.getToken2(nonceStr, timestamp, prepayId, wxPayConfigEntity.getSub_apppid());} catch (IOException e) {e.printStackTrace();} catch (SignatureException e) {e.printStackTrace();} catch (NoSuchAlgorithmException e) {e.printStackTrace();} catch (InvalidKeyException e) {e.printStackTrace();}map.put("return_code", "操作成功");map.put("appid", wxPayConfigEntity.getSub_apppid());map.put("partnerid", wxPayConfigEntity.getSub_mchid());map.put("prepayid", prepayId);map.put("package", "Sign=WXPay");map.put("noncestr", nonceStr);map.put("timestamp", timestamp);map.put("paySign", sign2);return map;}map.put("return_code", result);return map;}/*** 微信支付回调地址*/@ApiOperation(value = "微信支付回调地址")@PostMapping("/callback/wxorder")public Map<String, String> callBack(HttpServletRequest request) {log.info("这里是微信支付回调!");Map<String, String> map = new HashMap<>(2);try {PrivateKey privateKey = wxPayUtil.getPrivateKey(wxPayConfigEntity.getKeyPath());//微信返回的请求体String body = wxPayUtil.getRequestBody(request);//如果验证签名序列号通过if (wxPayUtil.verifiedSign(request, body, privateKey)) {//微信支付通知实体类WXPayNotifyVO wxPayNotifyVO = JSONObject.parseObject(body, WXPayNotifyVO.class);//如果支付成功if ("TRANSACTION.SUCCESS".equals(wxPayNotifyVO.getEvent_type())) {//通知资源数据WXPayNotifyVO.Resource resource = wxPayNotifyVO.getResource();//解密后资源数据String notifyResourceStr = PayResponseUtils.decryptResponseBody(wxPayConfigEntity.getApiV3Key(), resource.getAssociated_data(), resource.getNonce(), resource.getCiphertext());//通知资源数据对象NotifyResourceVO notifyResourceVO = JSONObject.parseObject(notifyResourceStr, NotifyResourceVO.class);//查询订单 可以优化把订单放入redisSalesOrderHeader salesOrderHeader = salesOrderHeaderService.get(notifyResourceVO.getOut_trade_no());//根据付款单号查询订单表保存微信需要付款总金额//支付完成时间String success_time = notifyResourceVO.getSuccess_time();LocalDateTime localDateTime = LocalDateTimeUtil.string3LocalDateTime(success_time);//微信支付订单号String transactionId = notifyResourceVO.getTransaction_id();if (salesOrderHeader != null) {//如果订单状态是提交状态if (OrdersOperateEnum.SUBMIT.code == salesOrderHeader.getPaid() || OrdersOperateEnum.USERPAYING.code == salesOrderHeader.getPaid()) {//如果付款成功if ("SUCCESS".equals(notifyResourceVO.getTrade_state())) {Integer total = notifyResourceVO.getAmount().getTotal();Integer payer_total = notifyResourceVO.getAmount().getPayer_total();int totalAmount = salesOrderHeader.getTotalAmount().multiply(new BigDecimal(100)).intValue();if (totalAmount == total && payer_total == total) {salesOrderHeader.setPayTime(localDateTime);salesOrderHeader.setRefNo(transactionId);salesOrderHeader.setPaid(OrdersOperateEnum.SUCCESS.code);salesOrderHeader.setStatus(CommonConstant.STATUS_PENDING);BigDecimal payAmount = BigDecimal.valueOf(payer_total).divide(BigDecimal.valueOf(100)).setScale(2, BigDecimal.ROUND_HALF_UP);salesOrderHeader.setPayAmount(payAmount);salesOrderHeader.setModifiedDate(LocalDateTimeUtil.getLocalDateTime());salesOrderHeaderService.update(salesOrderHeader);} else {map.put("code", "ERROR_AMOUNT");map.put("message", "支付金额与订单金额不匹配");return map;}} else {salesOrderHeader.setPayTime(localDateTime);salesOrderHeader.setRefNo(transactionId);//支付状态codeInteger oneCode = OrdersOperateEnum.getOneCode(notifyResourceVO.getTrade_state());salesOrderHeader.setPaid(oneCode);salesOrderHeader.setModifiedDate(LocalDateTimeUtil.getLocalDateTime());salesOrderHeaderService.update(salesOrderHeader);map.put("code", "SUCCESS");map.put("message", "");return map;}}} else {log.info("微信返回支付错误摘要:" + wxPayNotifyVO.getSummary());}//通知微信正常接收到消息,否则微信会轮询该接口map.put("code", "SUCCESS");map.put("message", "");return map;}}} catch (IOException e) {e.printStackTrace();} catch (ParseException e) {e.printStackTrace();} catch (RequestWechatException e) {e.printStackTrace();} catch (GeneralSecurityException e) {e.printStackTrace();}map.put("code", "ERROR_SIGN");map.put("message", "验签不通过");return map;}/*** 微信支付获取用户open_id的回调*/@ApiOperation(value = "微信支付获取用户open_id的回调")@PostMapping("/callback/wxauth")public String wxauth(@RequestParam("code") String code) {String geturl = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=" + wxPayConfigEntity.getApppid() + "&secret=" + wxPayConfigEntity.getAppserect() + "&code=" + code + "&grant_type=authorization_code";RestTemplate restTemplate = new RestTemplate();String result = restTemplate.getForObject(geturl, String.class);log.info(result);return result;}@ApiOperation(value = "微信H5下单,返回微信H5_url", notes = "传入付款金额,订单号 ,商品信息")@PostMapping("/create/wxH5order")public Object createWXH5Order(@RequestBody WeiXinPayPo weiXinPayPo) {String out_trade_no = weiXinPayPo.getOut_trade_no();//商品订单号SalesOrderHeader salesOrderHeader = salesOrderHeaderService.get(out_trade_no);if (salesOrderHeader == null || StringUtils.isEmpty(salesOrderHeader.getId())) {return new ResultUtils<AppSalesOrderHeaderVo>().setErrorMsg("订单不存在!");}if (!CommonConstant.STATUS_DRAFT.equals(salesOrderHeader.getStatus())) {return new ResultUtils<AppSalesOrderHeaderVo>().setErrorMsg("订单状态异常,请确认订单状态!");}SortedMap<String, Object> sortedMap = new TreeMap<String, Object>();sortedMap.put("sp_appid", wxPayConfigEntity.getApppid()); //应用IDsortedMap.put("sp_mchid", wxPayConfigEntity.getMch_id()); //商户号sortedMap.put("sub_mchid", wxPayConfigEntity.getSub_mchid());//子商户号sortedMap.put("description", "珠宝纯定制-H5-" + weiXinPayPo.getBody()); //商品描述sortedMap.put("out_trade_no", out_trade_no); //商户订单号sortedMap.put("notify_url", wxPayConfigEntity.getNotify_url()); //通知回调地址//总金额int totalFee = weiXinPayPo.getTotal_fee().multiply(new BigDecimal(100)).intValue();//总金额  需要乘以100 ,微信支付默认单位分Map<String, Object> map1 = new HashMap<>();map1.put("total", totalFee);map1.put("currency", "CNY");//订单金额sortedMap.put("amount", map1);Map map2 = new HashMap();map2.put("payer_client_ip", wxPayConfigEntity.getPayer_client_ip());Map map3 = new HashMap();map3.put("type", "iOS, Android, Wap");map2.put("h5_info", map3);//场景信息sortedMap.put("scene_info", map2);JSONObject json = new JSONObject(sortedMap);String reqdata = json.toJSONString();log.info(reqdata);//校验支付路径// HttpUrl httpurl = HttpUrl.parse(wxPayConfigEntity.getPayurl());// 请求body参数//String Sign = null;//  Sign = wxPayUtil.getToken(nonceStr,timestamp,POST,httpurl,reqdata);//======设置请求头========AutoUpdateCertificatesVerifier verifier = wxPayUtil.getAutoUpdateCertificatesVerifier();//通过WechatPayHttpClientBuilder构造的HttpClient,会自动的处理签名和验签CloseableHttpClient httpClient = wxPayUtil.getCloseableHttpClient();HttpPost httpPost = new HttpPost(wxPayConfigEntity.getPayH5url());RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(30000).setConnectTimeout(30000).build();httpPost.setConfig(requestConfig);httpPost.addHeader("Content-Type", "application/json");httpPost.addHeader("Accept", "application/json");httpPost.addHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36");String authType = wxPayConfigEntity.getAuthType();//String authorization = wxPayUtil.getAuthorization(Sign, authType);//httpPost.addHeader("Authorization",authorization);StringEntity entity = new StringEntity(reqdata, "UTF-8");httpPost.setEntity(entity);//======发起请求========CloseableHttpResponse response = null;String result = null;try {response = httpClient.execute(httpPost);result = EntityUtils.toString(response.getEntity());} catch (IOException e) {e.printStackTrace();}Header[] allHeaders = response.getAllHeaders();Header firstHeader = response.getFirstHeader("Request-ID");String s = firstHeader.toString();log.info("Request-ID:" + s);int statusCode = response.getStatusLine().getStatusCode();Map<String, Object> map = new HashMap<>();if (statusCode == 200) {Map<String, Object> resultMap = JSONObject.parseObject(result, Map.class);String h5_url = (String) resultMap.get("h5_url");//支付跳转链接log.info("h5_url:" + h5_url);salesOrderHeader.setPayAmount(weiXinPayPo.getTotal_fee());String prepay_id = PayResponseUtils.getParam(h5_url, "prepay_id");salesOrderHeader.setRefNo(prepay_id);salesOrderHeader.setPaid(OrdersOperateEnum.SUBMIT.code);salesOrderHeader.setIsChannel(weiXinPayPo.getIsChannel());salesOrderHeader.setPayType(weiXinPayPo.getPayType());salesOrderHeaderService.update(salesOrderHeader); //根据付款单号修改订单表保存微信需要付款总金额,支付微信下单号map.put("return_code", "操作成功");map.put("h5_url", h5_url);return map;}map.put("return_code", result);return map;}@ApiOperation(value = "微信JSAPI下单", notes = "传入付款金额,订单号 ,商品信息")@PostMapping("/create/wxJSAPIorder")public Object createWXJSAPIOrder(@RequestBody WeiXinPayPo weiXinPayPo) {String out_trade_no = weiXinPayPo.getOut_trade_no();//商品订单号SalesOrderHeader salesOrderHeader = salesOrderHeaderService.get(out_trade_no);if (salesOrderHeader == null || StringUtils.isEmpty(salesOrderHeader.getId())) {return new ResultUtils<AppSalesOrderHeaderVo>().setErrorMsg("订单不存在!");}if (!CommonConstant.STATUS_DRAFT.equals(salesOrderHeader.getStatus())) {return new ResultUtils<AppSalesOrderHeaderVo>().setErrorMsg("订单状态异常,请确认订单状态!");}SortedMap<String, Object> sortedMap = new TreeMap<String, Object>();sortedMap.put("sp_appid", wxPayConfigEntity.getApppid()); //应用IDsortedMap.put("sp_mchid", wxPayConfigEntity.getMch_id()); //商户号sortedMap.put("sub_mchid", wxPayConfigEntity.getSub_mchid());//子商户号sortedMap.put("description", "珠宝纯定制-JSAPI-" + weiXinPayPo.getBody()); //商品描述sortedMap.put("out_trade_no", out_trade_no); //商户订单号sortedMap.put("notify_url", wxPayConfigEntity.getNotify_url()); //通知回调地址//总金额int totalFee = weiXinPayPo.getTotal_fee().multiply(new BigDecimal(100)).intValue();//总金额  需要乘以100 ,微信支付默认单位分Map<String, Object> map1 = new HashMap<>();map1.put("total", totalFee);map1.put("currency", "CNY");//订单金额sortedMap.put("amount", map1);Map map2 = new HashMap();map2.put("sp_openid", weiXinPayPo.getSpOpenid());//支付者sortedMap.put("payer", map2);JSONObject json = new JSONObject(sortedMap);String reqdata = json.toJSONString();log.info(reqdata);//校验支付路径// HttpUrl httpurl = HttpUrl.parse(wxPayConfigEntity.getPayurl());// 请求body参数//String Sign = null;//  Sign = wxPayUtil.getToken(nonceStr,timestamp,POST,httpurl,reqdata);//获取最新的平台证书//AutoUpdateCertificatesVerifier verifier = wxPayUtil.getAutoUpdateCertificatesVerifier();//通过WechatPayHttpClientBuilder构造的HttpClient,会自动的处理签名和验签CloseableHttpClient httpClient = wxPayUtil.getCloseableHttpClient();HttpPost httpPost = new HttpPost(wxPayConfigEntity.getPayJSAPIurl());RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(30000).setConnectTimeout(30000).build();httpPost.setConfig(requestConfig);httpPost.addHeader("Content-Type", "application/json");httpPost.addHeader("Accept", "application/json");httpPost.addHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36");//String authType = wxPayConfigEntity.getAuthType();//String authorization = wxPayUtil.getAuthorization(Sign, authType);//httpPost.addHeader("Authorization",authorization);StringEntity entity = new StringEntity(reqdata, "UTF-8");httpPost.setEntity(entity);//======发起请求========CloseableHttpResponse response = null;String result = null;try {response = httpClient.execute(httpPost);result = EntityUtils.toString(response.getEntity());} catch (IOException e) {e.printStackTrace();}Header firstHeader = response.getFirstHeader("Request-ID");String s = firstHeader.toString();log.info("Request-ID:" + s);int statusCode = response.getStatusLine().getStatusCode();Map<String, Object> map = new HashMap<>();if (statusCode == 200) {Map<String, Object> resultMap = JSONObject.parseObject(result, Map.class);String prepayId = (String) resultMap.get("prepay_id");//微信下单号salesOrderHeader.setPayAmount(weiXinPayPo.getTotal_fee());salesOrderHeader.setRefNo(prepayId);salesOrderHeader.setPaid(OrdersOperateEnum.SUBMIT.code);salesOrderHeader.setIsChannel(weiXinPayPo.getIsChannel());salesOrderHeader.setPayType(weiXinPayPo.getPayType());salesOrderHeaderService.update(salesOrderHeader); //根据付款单号修改订单表保存微信需要付款总金额,支付微信下单号String packages = "prepay_id=" + prepayId;String sign2 = null;try {sign2 = wxPayUtil.getToken3(packages, timestamp, nonceStr, wxPayConfigEntity.getApppid());} catch (IOException e) {e.printStackTrace();} catch (SignatureException e) {e.printStackTrace();} catch (NoSuchAlgorithmException e) {e.printStackTrace();} catch (InvalidKeyException e) {e.printStackTrace();}map.put("return_code", "操作成功");map.put("appId", wxPayConfigEntity.getApppid());map.put("timeStamp", timestamp);map.put("nonceStr", nonceStr);map.put("package", packages);map.put("signType", "RSA");map.put("paySign", sign2);return map;}map.put("return_code", "操作失败");return map;}@ApiOperation(value = "关闭订单")@PostMapping("/create/closeOrder")public Result closeOrder(@RequestParam("orderId") String orderId) throws RequestWechatException {//查询订单 可以优化把订单放入redisSalesOrderHeader salesOrderHeader = salesOrderHeaderService.get(orderId);//如果是付款 返回成功if (salesOrderHeader == null || StringUtils.isEmpty(salesOrderHeader.getId()) ||salesOrderHeader.getStatus().equals(CommonConstant.STATUS_DELETE)) {return new ResultUtils<>().setErrorMsg("订单不存在");} else if (salesOrderHeader != null && OrdersOperateEnum.CLOSED.code == salesOrderHeader.getPaid()) {return new ResultUtils<>().setSuccessMsg("订单已关闭");} else {//未付款 请求微信 获取订单状态String url = String.format("https://api.mch.weixin.qq.com/v3/pay/partner/transactions/out-trade-no/", salesOrderHeader.getId(), "/close");AutoUpdateCertificatesVerifier verifier = wxPayUtil.getAutoUpdateCertificatesVerifier();//通过WechatPayHttpClientBuilder构造的HttpClient,会自动的处理签名和验签CloseableHttpClient httpClient = wxPayUtil.getCloseableHttpClient();HttpPost httpPost = new HttpPost(url);RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(30000).setConnectTimeout(30000).build();httpPost.setConfig(requestConfig);httpPost.addHeader("Content-Type", "application/json");httpPost.addHeader("Accept", "application/json");httpPost.addHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36");Map map = new HashMap();map.put("sp_mchid", wxPayConfigEntity.getMch_id());map.put("sub_mchid", wxPayConfigEntity.getSub_mchid());JSONObject json = new JSONObject(map);String reqdata = json.toJSONString();StringEntity entity = new StringEntity(reqdata, "UTF-8");httpPost.setEntity(entity);HttpResponse response = null;try {response = httpClient.execute(httpPost);} catch (IOException e) {throw new RequestWechatException();}int statusCode = response.getStatusLine().getStatusCode();if (statusCode == 204) {salesOrderHeader.setPaid(OrdersOperateEnum.CLOSED.code);salesOrderHeaderService.update(salesOrderHeader);return new ResultUtils<>().setErrorMsg("订单关闭成功");}return new ResultUtils<>().setErrorMsg("订单关闭失败");}}@ApiOperation(value = "查验订单")@GetMapping("/create/selectOrder")@Transactionalpublic Result selectOrder(@RequestParam("orderId") String orderId) throws RequestWechatException {//        return CallbackResult.getSuccessResult("500");//查询订单 可以优化把订单放入redisSalesOrderHeader salesOrderHeader = salesOrderHeaderService.get(orderId);//如果是付款 返回成功if (salesOrderHeader == null || StringUtils.isEmpty(salesOrderHeader.getId()) ||salesOrderHeader.getStatus().equals(CommonConstant.STATUS_DELETE)) {return new ResultUtils<>().setErrorMsg("订单不存在");} else if (salesOrderHeader != null && OrdersOperateEnum.SUCCESS.code == salesOrderHeader.getPaid()) {return new ResultUtils<>().setSuccessMsg("付款成功");} else {//未付款 请求微信 获取订单状态String url = String.format(wxPayConfigEntity.getPaySelectUrl(), salesOrderHeader.getRefNo(),"?", "sp_mchid=", wxPayConfigEntity.getMch_id(), "&", "sub_mchid=", wxPayConfigEntity.getSub_mchid());AutoUpdateCertificatesVerifier verifier = wxPayUtil.getAutoUpdateCertificatesVerifier();//通过WechatPayHttpClientBuilder构造的HttpClient,会自动的处理签名和验签CloseableHttpClient httpClient = wxPayUtil.getCloseableHttpClient();HttpGet httpGet = new HttpGet(url);httpGet.addHeader("Content-Type", "application/json;charset=UTF-8");httpGet.addHeader("Accept", "application/json");HttpResponse response = null;try {response = httpClient.execute(httpGet);} catch (IOException e) {throw new RequestWechatException();}//如果状态码为200,就是正常返回if (response.getStatusLine().getStatusCode() == 200) {String result = "";try {result = EntityUtils.toString(response.getEntity(), "UTF-8");JSONObject jsonObject = JSON.parseObject(result);NotifyResourceVO notifyResourceVO = JSONObject.parseObject(jsonObject.toString(), NotifyResourceVO.class);//支付完成时间String success_time = notifyResourceVO.getSuccess_time();LocalDateTime localDateTime = LocalDateTimeUtil.string3LocalDateTime(success_time);//微信支付订单号String transactionId = notifyResourceVO.getTransaction_id();//如果付款成功if ("SUCCESS".equals(notifyResourceVO.getTrade_state())) {Integer total = notifyResourceVO.getAmount().getTotal();Integer payer_total = notifyResourceVO.getAmount().getPayer_total();int totalAmount = salesOrderHeader.getTotalAmount().multiply(new BigDecimal(100)).intValue();if (totalAmount == total && payer_total == total) {salesOrderHeader.setPayTime(localDateTime);salesOrderHeader.setRefNo(transactionId);salesOrderHeader.setPaid(OrdersOperateEnum.SUCCESS.code);salesOrderHeader.setStatus(CommonConstant.STATUS_PENDING);BigDecimal payAmount = BigDecimal.valueOf(payer_total).divide(BigDecimal.valueOf(100)).setScale(2, BigDecimal.ROUND_HALF_UP);salesOrderHeader.setPayAmount(payAmount);salesOrderHeader.setModifiedDate(LocalDateTimeUtil.getLocalDateTime());salesOrderHeaderService.update(salesOrderHeader);return new ResultUtils<>().setSuccessMsg(notifyResourceVO.getTrade_state_desc());} else {return new ResultUtils<>().setSuccessMsg("支付金额与订单金额不匹配");}} else {return new ResultUtils<>().setSuccessMsg(notifyResourceVO.getTrade_state_desc());}} catch (IOException e) {e.printStackTrace();}}}return new ResultUtils<>().setSuccessMsg("500");}@ApiOperation(value = "微信退款")@PostMapping("/create/refundOrder")public Object refundOrder(@RequestBody WeiXinPayPo weiXinPayPo) throws Exception {//查询订单 可以优化把订单放入redisSalesOrderHeader salesOrderHeader = salesOrderHeaderService.get(weiXinPayPo.getOut_trade_no());//如果是付款 返回成功if (salesOrderHeader == null || StringUtils.isEmpty(salesOrderHeader.getId()) ||salesOrderHeader.getStatus().equals(CommonConstant.STATUS_DELETE)) {return new ResultUtils<>().setErrorMsg("订单不存在");} else if (salesOrderHeader != null && OrdersOperateEnum.CLOSED.code == salesOrderHeader.getPaid()) {return new ResultUtils<>().setSuccessMsg("订单已关闭");} else {SortedMap<String, String> sortedMap = new TreeMap<String, String>();BigDecimal totalAmount = salesOrderHeader.getTotalAmount();//订单总金额 金额单位为分 *100String totalFee = totalAmount.multiply(new BigDecimal(100)).toString().replace(".00","");sortedMap.put("appid", wxPayConfigEntity.getApppid()); //应用IDsortedMap.put("mch_id", wxPayConfigEntity.getMch_id()); //商户号sortedMap.put("sub_mch_id", wxPayConfigEntity.getSub_mchid());//子商户号sortedMap.put("nonce_str", nonceStr);sortedMap.put("transaction_id", salesOrderHeader.getRefNo());//sortedMap.put("out_trade_no", salesOrderHeader.getId());sortedMap.put("out_refund_no", salesOrderHeader.getId());sortedMap.put("total_fee", totalFee);sortedMap.put("refund_fee", totalFee);sortedMap.put("notify_url",wxPayConfigEntity.getRefundnotify_url());//生成签名String sign = wxPayUtil.generateSignature(sortedMap, wxPayConfigEntity.getApiKey(),WXPayConfigEntity.SignType.MD5);sortedMap.put("sign", sign);log.info("发送给微信的参数:" + sortedMap);String xml = WXPayUtil.getRequestXml(sortedMap);log.info("===>>>发送给微信的xml:" + xml);String string = clientCustomSSL.doRefund(wxPayConfigEntity.getPayRefundurl(), xml);System.out.println("微信返回的xml===>>>" + string);//AutoUpdateCertificatesVerifier verifier = wxPayUtil.getAutoUpdateCertificatesVerifier();//因为退款是V2接口,所以先不用CloseableHttpClient自动签名处理发起请求,就用普通的HttpClient发起请求boolean bool = wxPayUtil.isSignatureValid(string, wxPayConfigEntity.getApiKey());//验证签名是否正确Map<String, String> map = new HashMap<String, String>();if (bool) {Map<String, String> resultMap = wxPayUtil.xmlToMap(string);if ("SUCCESS".equals(resultMap.get("return_code"))) { //返回成功 退款成功map.put("refund_fee", resultMap.get("refund_fee"));//退款金额map.put("out_refund_no", resultMap.get("out_refund_no")); //退款单号map.put("message", "SUCCESS");//退款成功return map;}}map.put("message", "faild"); //退款失败return map;}}@ApiOperation(value = "微信退款回调地址")@PostMapping("/callback/refundOrder")public Object refundCallBack(HttpServletRequest request) throws Exception {String resultxml = wxPayUtil.getRequestBody(request);Map<String,String> resultMap = wxPayUtil.xmlToMap(resultxml);SortedMap<String, String> map = new TreeMap<String, String>(); //返回给微信的mapif ("SUCCESS".equals(resultMap.get("return_code"))) { //返回成功//获取加密信息String reqInfo = resultMap.get("req_info").toString();byte[] asBytes = Base64.getDecoder().decode(reqInfo);String md5Key = wxPayUtil.md5(wxPayConfigEntity.getApiKey()); //md5加密后的keyCipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");SecretKeySpec key = new SecretKeySpec(md5Key.toLowerCase().getBytes(), "AES");cipher.init(Cipher.DECRYPT_MODE, key);byte[] doFinal = cipher.doFinal(asBytes);String result = new String(doFinal, "utf-8");if(org.apache.commons.lang3.StringUtils.isNotEmpty(result)){Map<String, String> res = wxPayUtil.xmlToMap(result);String refundStatus =  res.get("refund_status"); //退款状态log.info("退款状态:-->"+refundStatus);String outRefundNo =  res.get("out_refund_no");//退款单号log.info("退款单号:-->"+outRefundNo);String refund_id = res.get("refund_id"); //微信退款单号 可以保存到数据库
//                String settlementRefundFee = res.get("settlement_refund_fee");//退款金额
//                String refundFee = res.get("refund_fee");//申请退款金额
//                String successTime = res.get("success_time"); // 退款成功时间if("SUCCESS".equals(refundStatus)){  //退款成功Long refund_fee = Long.parseLong(res.get("refund_fee"));log.info("退款金额:(单位:分)-->"+outRefundNo);String refund_success_time = res.get("success_time");log.info("退款时间:-->"+refund_success_time);LocalDateTime refundsuccesstime =LocalDateTimeUtil.string2LocalDateTime(refund_success_time);log.info("退款时间:-->"+refundsuccesstime.toString());BigDecimal refundfee = BigDecimal.valueOf(refund_fee).divide(BigDecimal.valueOf(100)).setScale(2, BigDecimal.ROUND_HALF_UP);SalesOrderHeader salesOrderHeader = salesOrderHeaderService.get(outRefundNo);salesOrderHeader.setRefundPrice(refundfee);salesOrderHeader.setRefundTime(refundsuccesstime);salesOrderHeader.setPaid(OrdersOperateEnum.REFUNDSUCCEE.code);salesOrderHeader.setStatus(CommonConstant.STATUS_RETURN);salesOrderHeaderService.update(salesOrderHeader);SalesOrderDetailQuery sdetailQuery = new SalesOrderDetailQuery();sdetailQuery.setSalesOrderHeader(salesOrderHeader.getId());sdetailQuery.setStatus(CommonConstant.FINISHED_GOODS);List<SalesOrderDetail> orderDetails = salesOrderDetailService.findAll(sdetailQuery);try {lock.lock();for (SalesOrderDetail order : orderDetails){Sku sku = appSkuService.get(order.getSkuId());if (sku != null && !StringUtils.isEmpty(sku.getId())){sku.setStock(sku.getStock()+order.getQty());sku.setSalesCount(sku.getSalesCount()-order.getQty());appSkuService.update(sku);}}}catch (Exception e){throw new BusinessException("库存修改异常!");}finally {lock.unlock();}map.put("return_code", "SUCCESS");map.put("return_msg", "OK");  //正确返回给微信return wxPayUtil.getRequestXml(map);}else if("CHANGE".equals(refundStatus)){  //退款异常}else if ("REFUNDCLOSE".equals(refundStatus)){  //退款关闭}}}map.put("return_code", "FAIL");map.put("return_msg", "return_code不正确");return wxPayUtil.getRequestXml(map);}@ApiOperation(value = "查询退款")@PostMapping("/create/refundquery")@Transactionalpublic Object refundquery(@RequestParam("orderId") String orderId) throws Exception {//查询订单 可以优化把订单放入redisSalesOrderHeader salesOrderHeader = salesOrderHeaderService.get(orderId);//如果是付款 返回成功if (salesOrderHeader == null || StringUtils.isEmpty(salesOrderHeader.getId()) ||salesOrderHeader.getStatus().equals(CommonConstant.STATUS_DELETE)) {return new ResultUtils<>().setErrorMsg("订单不存在");} else if (salesOrderHeader != null && OrdersOperateEnum.REFUNDSUCCEE.code == salesOrderHeader.getPaid()) {return new ResultUtils<>().setSuccessMsg("退款成功");} else {SortedMap<String, String> sortedMap = new TreeMap<String, String>();sortedMap.put("appid", wxPayConfigEntity.getApppid()); //应用IDsortedMap.put("mch_id", wxPayConfigEntity.getMch_id()); //商户号sortedMap.put("sub_mch_id", wxPayConfigEntity.getSub_mchid());//子商户号sortedMap.put("nonce_str", nonceStr);sortedMap.put("transaction_id", salesOrderHeader.getRefNo());//sortedMap.put("out_trade_no", salesOrderHeader.getId());sortedMap.put("out_refund_no", salesOrderHeader.getId());//生成签名String sign = wxPayUtil.generateSignature(sortedMap, wxPayConfigEntity.getApiKey(),WXPayConfigEntity.SignType.MD5);sortedMap.put("sign", sign);log.info("发送给微信的参数:" + sortedMap);String xml = WXPayUtil.getRequestXml(sortedMap);log.info("===>>>发送给微信的xml:" + xml);String string = clientCustomSSL.doRefund(wxPayConfigEntity.getRefundSelecturl(), xml);System.out.println("微信返回的xml===>>>" + string);//因为退款是V2接口,所以先不用CloseableHttpClient自动签名处理发起请求,就用普通的HttpClient发起请求boolean bool = wxPayUtil.isSignatureValid(string, wxPayConfigEntity.getApiKey());//验证签名是否正确Map<String, String> map = new HashMap<String, String>();if (bool) {Map<String, String> resultMap = wxPayUtil.xmlToMap(string);if ("SUCCESS".equals(resultMap.get("return_code"))) { //返回成功 退款成功String result_code = resultMap.get("result_code");//业务结果String refund_id = resultMap.get("refund_id_0");//微信退款单号String refund_status_0 = resultMap.get("refund_status_0");//退款状态Long refund_fee = Long.parseLong(resultMap.get("refund_fee_0"));//退款金额BigDecimal refundfee = BigDecimal.valueOf(refund_fee).divide(BigDecimal.valueOf(100)).setScale(2, BigDecimal.ROUND_HALF_UP);salesOrderHeader.setPaid(OrdersOperateEnum.REFUNDSUCCEE.code);salesOrderHeader.setRefundId(refund_id);salesOrderHeader.setRefundPrice(refundfee);map.put("result_code",result_code);map.put("refund_id",refund_id);map.put("refund_fee", refundfee.toString());//退款金额map.put("out_refund_no", resultMap.get("out_refund_no_0")); //退款单号map.put("refund_status_0",refund_status_0);map.put("message", "SUCCESS");//退款成功return map;}}map.put("message", "faild"); //退款失败return map;}}}

WXPayUtil
特别需要注意的,我在这栽跟头栽了两天,如果你使用了下面的方法,就不需要自己再去生成签名了,一定要记住,不让会报401.
文档第一步就是生成签名,我就是跟着文档开发,后面又发现了下面的方法,也用上了,结果就是一直401,虽然我在用这个方法的时候就在想既然自动签名了那为什么我还自己生成签名呢的疑问下,但是一直都没舍得把自己生成签名的步骤删掉,直到我绝望了.

这个是微信支付提供的自动获取证书和自动签名验证的方法路径,我已经封装在下面的工具类里面了,这里是把地址贴出来

import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.auth.AutoUpdateCertificatesVerifier;
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Validator;
import lombok.extern.slf4j.Slf4j;
import okhttp3.HttpUrl;
import org.apache.http.impl.client.CloseableHttpClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.Base64Utils;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.xml.parsers.DocumentBuilder;
import java.io.*;import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.*;
import java.security.cert.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.text.ParseException;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;@Slf4j
@Component
public class WXPayUtil {@Autowiredprivate  WXPayConfigEntity wxPayConfigEntity; //支付参数private static final int byteLowHalfFlag = 0x0F;private static final int byteHalfShift = 4;private  final char[] hexDigits = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E','F' };/*** 获取证书。** @param filename 证书文件路径  (required)* @return X509证书*/public  X509Certificate getCertificate(String filename) throws IOException {InputStream fis = new FileInputStream(filename);BufferedInputStream bis = new BufferedInputStream(fis);try {CertificateFactory cf = CertificateFactory.getInstance("X509");X509Certificate cert = (X509Certificate) cf.generateCertificate(bis);cert.checkValidity();return cert;} catch (CertificateExpiredException e) {throw new RuntimeException("证书已过期", e);} catch (CertificateNotYetValidException e) {throw new RuntimeException("证书尚未生效", e);} catch (CertificateException e) {throw new RuntimeException("无效的证书文件", e);} finally {bis.close();}}/*** 获取私钥。** @param filename 私钥文件路径  (required)* @return 私钥对象*/public  PrivateKey getPrivateKey(String filename) throws IOException {String content = new String(Files.readAllBytes(Paths.get(filename)), "utf-8");try {String privateKey = content.replace("-----BEGIN PRIVATE KEY-----", "").replace("-----END PRIVATE KEY-----", "").replaceAll("\\s+", "");KeyFactory kf = KeyFactory.getInstance("RSA");return kf.generatePrivate(new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey)));} catch (NoSuchAlgorithmException e) {throw new RuntimeException("当前Java环境不支持RSA", e);} catch (InvalidKeySpecException e) {throw new RuntimeException("无效的密钥格式");}}public  String getToken(String nonceStr,long timestamp,String method, HttpUrl url, String body) throws IOException, SignatureException, NoSuchAlgorithmException, InvalidKeyException {String message = buildMessage(method, url, timestamp, nonceStr, body);String signature = sign(message.getBytes("utf-8"));return "mchid=\"" + wxPayConfigEntity.getMch_id() + "\","+ "nonce_str=\"" + nonceStr + "\","+ "timestamp=\"" + timestamp + "\","+ "serial_no=\"" + wxPayConfigEntity.getSerialNo() + "\","+ "signature=\"" + signature + "\"";}//生成签名public  String sign(byte[] message) throws NoSuchAlgorithmException, IOException, InvalidKeyException, SignatureException {Signature sign = Signature.getInstance("SHA256withRSA");PrivateKey privateKey1 = getPrivateKey(wxPayConfigEntity.getKeyPath());sign.initSign(privateKey1);sign.update(message);return Base64.getEncoder().encodeToString(sign.sign());}//待签名串//生成签名串public  String buildMessage(String method, HttpUrl url, long timestamp, String nonceStr, String body) {String canonicalUrl = url.encodedPath();if (url.encodedQuery() != null) {canonicalUrl += "?" + url.encodedQuery();}return method + "\n"+ canonicalUrl + "\n"+ timestamp + "\n"+ nonceStr + "\n"+ body + "\n";}//生成Authorizationpublic  String getAuthorization(String Sign,String  authType) {return authType.concat(" ").concat(Sign);}//APP支付二次生成签名public  String getToken2(String nonceStr,long timestamp,String prepayid,String subAppid) throws IOException, SignatureException, NoSuchAlgorithmException, InvalidKeyException {String message2 = buildMessage2(prepayid, timestamp, nonceStr, subAppid);String signature2 = sign(message2.getBytes("utf-8"));return signature2;}//APP支付二次生成签名串public  String buildMessage2(String prepayid, long timestamp, String nonceStr, String subAppid) {return subAppid + "\n"+ timestamp + "\n"+ nonceStr + "\n"+ prepayid + "\n";}//JSAPI支付二次生成签名public  String getToken3(String packages, long timestamp, String nonceStr, String appId) throws IOException, SignatureException, NoSuchAlgorithmException, InvalidKeyException {String message2 = buildMessage3(packages, timestamp, nonceStr, appId);String signature2 = sign(message2.getBytes("utf-8"));return signature2;}//JSAPI支付二次生成签名串public  String buildMessage3(String packages, long timestamp, String nonceStr, String appId) {return appId + "\n"+ timestamp + "\n"+ nonceStr + "\n"+ packages + "\n";}/*** 获取请求文体* @param request* @return* @throws IOException*/public  String getRequestBody(HttpServletRequest request) throws IOException {ServletInputStream stream = null;BufferedReader reader = null;StringBuffer sb = new StringBuffer();try {stream = request.getInputStream();// 获取响应reader = new BufferedReader(new InputStreamReader(stream));String line;while ((line = reader.readLine()) != null) {sb.append(line);}} catch (IOException e) {throw new IOException("读取返回支付接口数据流出现异常!");} finally {reader.close();}return sb.toString();}/*** 验证微信签名* @param request* @param body* @return* @throws GeneralSecurityException* @throws IOException* @throws InstantiationException* @throws IllegalAccessException* @throws ParseException*/public boolean verifiedSign(HttpServletRequest request, String body,PrivateKey privateKey) throws GeneralSecurityException, ParseException, RequestWechatException {//微信返回的证书序列号String serialNo = request.getHeader("Wechatpay-Serial");log.info("Wechatpay-Serial:---"+serialNo);//微信返回的随机字符串String nonceStr = request.getHeader("Wechatpay-Nonce");log.info("Wechatpay-Nonce:---"+nonceStr);//微信返回的时间戳String timestamp = request.getHeader("Wechatpay-Timestamp");log.info("Wechatpay-Timestamp:---"+timestamp);//微信返回的签名String wechatSign = request.getHeader("Wechatpay-Signature");log.info("Wechatpay-Signature:---"+wechatSign);//组装签名字符串String signStr = Stream.of(timestamp, nonceStr, body).collect(Collectors.joining("\n", "", "\n"));//自动获取最新平台证书AutoUpdateCertificatesVerifier verifier = getAutoUpdateCertificatesVerifier();X509Certificate certificate = verifier.getValidCertificate();//SHA256withRSA签名Signature signature = Signature.getInstance("SHA256withRSA");signature.initVerify(certificate);signature.update(signStr.getBytes());//返回验签结果return signature.verify(Base64Utils.decodeFromString(wechatSign));}//自动获取最新平台证书public AutoUpdateCertificatesVerifier getAutoUpdateCertificatesVerifier()  {PrivateKey privateKey1 = null;try {privateKey1 = getPrivateKey(wxPayConfigEntity.getKeyPath());} catch (IOException e) {e.printStackTrace();}AutoUpdateCertificatesVerifier verifier = null;try {verifier = new AutoUpdateCertificatesVerifier(new WechatPay2Credentials(wxPayConfigEntity.getMch_id(), new PrivateKeySigner(wxPayConfigEntity.getSerialNo(), privateKey1)),wxPayConfigEntity.getApiV3Key().getBytes("utf-8"));} catch (UnsupportedEncodingException e) {e.printStackTrace();}return verifier;}public CloseableHttpClient  getCloseableHttpClient()  {PrivateKey privateKey1 = null;AutoUpdateCertificatesVerifier verifier = null;try {privateKey1 = getPrivateKey(wxPayConfigEntity.getKeyPath());verifier = getAutoUpdateCertificatesVerifier();} catch (IOException e) {e.printStackTrace();}//通过WechatPayHttpClientBuilder构造的HttpClient,会自动的处理签名和验签CloseableHttpClient httpClient = WechatPayHttpClientBuilder.create().withMerchant(wxPayConfigEntity.getMch_id(), wxPayConfigEntity.getSerialNo(), privateKey1).withValidator(new WechatPay2Validator(verifier)).build();return httpClient;}//=======================微信支付V2工具类 因为V3接口暂时没有退款接口所以暂时还是需要用V2的退款接口=========================/*** 生成签名** @param data 待签名数据* @param key  API密钥* @return 签名*/public  String generateSignature(final Map<String, String> data, String key) throws Exception {return generateSignature(data, key, WXPayConfigEntity.SignType.MD5);}/*** 生成签名. 注意,若含有sign_type字段,必须和signType参数保持一致。** @param data     待签名数据* @param key      API密钥* @param signType 签名方式* @return 签名*/public static String generateSignature(final Map<String, String> data, String key, WXPayConfigEntity.SignType signType) throws Exception {Set<String> keySet = data.keySet();String[] keyArray = keySet.toArray(new String[keySet.size()]);Arrays.sort(keyArray);StringBuilder sb = new StringBuilder();for (String k : keyArray) {if (k.equals("sign")) {continue;}if (data.get(k).toString().trim().length() > 0){// 参数值为空,则不参与签名sb.append(k).append("=").append(data.get(k).toString().trim()).append("&");}}sb.append("key=").append(key);if (WXPayConfigEntity.SignType.MD5.equals(signType)) {return MD5(sb.toString()).toUpperCase();} else if (WXPayConfigEntity.SignType.HMACSHA256.equals(signType)) {return HMACSHA256(sb.toString(), key);} else {throw new Exception(String.format("Invalid sign_type: %s", signType));}}/*** 生成 MD5** @param data 待处理数据* @return MD5结果*/public static String MD5(String data) throws Exception {MessageDigest md = MessageDigest.getInstance("MD5");byte[] array = md.digest(data.getBytes("UTF-8"));StringBuilder sb = new StringBuilder();for (byte item : array) {sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));}return sb.toString().toUpperCase();}/*** 生成 HMACSHA256** @param data 待处理数据* @param key  密钥* @return 加密结果* @throws Exception*/public static String HMACSHA256(String data, String key) throws Exception {Mac sha256_HMAC = Mac.getInstance("HmacSHA256");SecretKeySpec secret_key = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA256");sha256_HMAC.init(secret_key);byte[] array = sha256_HMAC.doFinal(data.getBytes("UTF-8"));StringBuilder sb = new StringBuilder();for (byte item : array) {sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));}return sb.toString().toUpperCase();}/*** 转为xml格式* @param parameters* @return*/public static String getRequestXml(SortedMap<String,String> parameters){StringBuffer sb = new StringBuffer();sb.append("<xml>");Set es = parameters.entrySet();Iterator it = es.iterator();while(it.hasNext()) {Map.Entry entry = (Map.Entry)it.next();String k = (String)entry.getKey();String v = (String)entry.getValue();if ("attach".equalsIgnoreCase(k)||"body".equalsIgnoreCase(k)||"sign".equalsIgnoreCase(k)) {sb.append("<"+k+">"+v+"</"+k+">");}else {sb.append("<"+k+">"+v+"</"+k+">");}}sb.append("</xml>");return sb.toString();}/*** XML格式字符串转换为Map** @param strXML XML字符串* @return XML数据转换后的Map* @throws Exception*/public static Map<String, String> xmlToMap(String strXML) throws Exception {try {Map<String, String> data = new HashMap<String, String>();DocumentBuilder documentBuilder = WXPayXmlUtil.newDocumentBuilder();InputStream stream = new ByteArrayInputStream(strXML.getBytes("UTF-8"));org.w3c.dom.Document doc = documentBuilder.parse(stream);doc.getDocumentElement().normalize();NodeList nodeList = doc.getDocumentElement().getChildNodes();for (int idx = 0; idx < nodeList.getLength(); ++idx) {Node node = nodeList.item(idx);if (node.getNodeType() == Node.ELEMENT_NODE) {org.w3c.dom.Element element = (org.w3c.dom.Element) node;data.put(element.getNodeName(), element.getTextContent());}}try {stream.close();} catch (Exception ex) {// do nothing}return data;} catch (Exception ex) {log.info("Invalid XML, can not convert to map. Error message: {}. XML content: {}", ex.getMessage(), strXML);throw ex;}}public  String md5(String s) throws Exception {try {byte[] btInput = s.getBytes();// 获得MD5摘要算法的 MessageDigest 对象MessageDigest mdInst = MessageDigest.getInstance("MD5");// 使用指定的字节更新摘要mdInst.update(btInput);// 获得密文byte[] md = mdInst.digest();// 把密文转换成十六进制的字符串形式int j = md.length;char[] str = new char[j * 2];int k = 0;for (int i = 0; i < j; i++) {byte byte0 = md[i];str[k++] = hexDigits[(byte0 >>> byteHalfShift) & byteLowHalfFlag];str[k++] = hexDigits[byte0 & byteLowHalfFlag];}return new String(str);} catch (Exception e) {throw e;}}public String md5(byte[] btInput) throws Exception {try {// 获得MD5摘要算法的 MessageDigest 对象MessageDigest mdInst = MessageDigest.getInstance("MD5");// 使用指定的字节更新摘要mdInst.update(btInput);// 获得密文byte[] md = mdInst.digest();// 把密文转换成十六进制的字符串形式int j = md.length;char[] str = new char[j * 2];int k = 0;for (int i = 0; i < j; i++) {byte byte0 = md[i];str[k++] = hexDigits[byte0 >>> byteHalfShift & byteLowHalfFlag];str[k++] = hexDigits[byte0 & byteLowHalfFlag];}return new String(str);} catch (Exception e) {throw e;}}/*** 判断签名是否正确** @param xmlStr XML格式数据* @param key    API密钥* @return 签名是否正确* @throws Exception*/public boolean isSignatureValid(String xmlStr, String key) throws Exception {Map<String, String> data = xmlToMap(xmlStr);if (!data.containsKey("sign")) {return false;}String sign = (String) data.get("sign");return generateSignature(data, key).equals(sign);}/*** 判断签名是否正确,必须包含sign字段,否则返回false。使用MD5签名。** @param data Map类型数据* @param key  API密钥* @return 签名是否正确* @throws Exception*/public static boolean isSignatureValid(Map<String, String> data, String key) throws Exception {return isSignatureValid(data, key, WXPayConfigEntity.SignType.MD5);}/*** 判断签名是否正确,必须包含sign字段,否则返回false。** @param data     Map类型数据* @param key      API密钥* @param signType 签名方式* @return 签名是否正确* @throws Exception*/public static boolean isSignatureValid(Map<String, String> data, String key, WXPayConfigEntity.SignType signType) throws Exception {if (!data.containsKey("sign")) {return false;}String sign = (String) data.get("sign");return generateSignature(data, key, signType).equals(sign);}}

PayResponseUtils
解密回调响应加密数据的工具类

import com.google.common.base.Splitter;
import org.springframework.util.Base64Utils;import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;import java.nio.charset.StandardCharsets;
import java.security.*;
import java.util.Map;/*** 微信付款返回数据工具类* @author shenlu* @date 2020/12/22 13:46*/
public class PayResponseUtils {/*** 用微信V3密钥解密响应体.** @param associatedData  response.body.data[i].encrypt_certificate.associated_data* @param nonce          response.body.data[i].encrypt_certificate.nonce* @param ciphertext     response.body.data[i].encrypt_certificate.ciphertext* @return the string* @throws GeneralSecurityException the general security exception*/public static String decryptResponseBody(String v3key,String associatedData, String nonce, String ciphertext) {try {Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");SecretKeySpec key = new SecretKeySpec(v3key.getBytes(StandardCharsets.UTF_8), "AES");GCMParameterSpec spec = new GCMParameterSpec(128, nonce.getBytes(StandardCharsets.UTF_8));cipher.init(Cipher.DECRYPT_MODE, key, spec);cipher.updateAAD(associatedData.getBytes(StandardCharsets.UTF_8));byte[] bytes;try {bytes = cipher.doFinal(Base64Utils.decodeFromString(ciphertext));} catch (GeneralSecurityException e) {throw new IllegalArgumentException(e);}return new String(bytes, StandardCharsets.UTF_8);} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {throw new IllegalStateException(e);} catch (InvalidKeyException | InvalidAlgorithmParameterException e) {throw new IllegalArgumentException(e);}}public static String getParam(String url, String name) {String params = url.substring(url.indexOf("?") + 1, url.length());Map<String, String> split = Splitter.on("&").withKeyValueSeparator("=").split(params);return split.get(name);}}

OrdersOperateEnum

/*** 订单操作枚举类* @author shenlu* @date 2020/12/15 9:27*/
public enum OrdersOperateEnum {UNPAID(0,"待支付"),SUBMIT(1,"提交支付"),SUCCESS(2,"支付成功"),REFUND(3,"转入退款"),NOTPAY(4,"未支付"),CLOSED(5,"已关闭"),REVOKED(6,"已撤销(付款码支付)"),USERPAYING(7,"用户支付中(付款码支付)"),PAYERROR(8,"支付失败"),REFUNDSUCCEE(9,"退款成功");public int code;public String msg;public Integer getCode() {return code;}public void setCode(Integer code) {this.code = code;}public String getMsg() {return msg;}public void setMsg(String msg) {this.msg = msg;}OrdersOperateEnum(Integer code, String msg){this.code = code;this.msg = msg;}/*** 根据code获得msg* @param code* @return*/@SuppressWarnings("unlikely-arg-type")public static String getValueByCode(Integer code){for(OrdersOperateEnum platformFree:OrdersOperateEnum.values()){if(code.equals(platformFree.getCode())){return platformFree.getMsg();}}return "未知";}public static OrdersOperateEnum getByCode(Integer code){for(OrdersOperateEnum transactType : values()){if (code.equals(transactType.getCode())) {//获取指定的枚举return transactType;}}return null;}public  static Integer getOneCode(String trade_state){switch (trade_state){case "SUBMIT" :return OrdersOperateEnum.SUBMIT.code;case "SUCCESS":return OrdersOperateEnum.SUCCESS.code;case "REFUND" :return OrdersOperateEnum.REFUND.code;case "NOTPAY" :return OrdersOperateEnum.NOTPAY.code;case "CLOSED" :return OrdersOperateEnum.CLOSED.code;case "REVOKED" :return OrdersOperateEnum.REVOKED.code;case "USERPAYING" :return OrdersOperateEnum.USERPAYING.code;default:return OrdersOperateEnum.PAYERROR.code;}}}

ClientCustomSSL
调用退款接口会用的生成请求的工具类

import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;import javax.net.ssl.SSLContext;
import java.io.File;
import java.io.FileInputStream;
import java.security.KeyStore;/*** @ Author :* @ Date   :**/
@Component
public class ClientCustomSSL {@Autowiredprivate WXPayConfigEntity wxPayConfigEntity;@SuppressWarnings("deprecation")public String doRefund(String url,String data) throws Exception {/*** 注意PKCS12证书 是从微信商户平台-》账户设置-》 API安全 中下载的*/KeyStore keyStore = KeyStore.getInstance("PKCS12");/****wxconfig.SSLCERT_PATH : 指向你的证书的绝对路径,带着证书去访问*///你下载的证书,解压后的apiclient_cert.p12这个文件全路径加全名FileInputStream instream = new FileInputStream(new File(wxPayConfigEntity.getP12()));//P12文件目录try {/***** 下载证书时的密码、默认密码是你的MCHID mch_id* */keyStore.load(instream, wxPayConfigEntity.getMch_id().toCharArray());//这里写密码.默认商户id} finally {instream.close();}// Trust own CA and all self-signed certs/**** 下载证书时的密码、默认密码是你的MCHID mch_id* */SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, wxPayConfigEntity.getMch_id().toCharArray())//这里也是写密码的,默认商户id.build();// Allow TLSv1 protocol onlySSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext,new String[] { "TLSv1" },null,SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();try {HttpPost httpost = new HttpPost(url); // 设置响应头信息httpost.addHeader("Connection", "keep-alive");httpost.addHeader("Accept", "*/*");httpost.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");httpost.addHeader("Host", "api.mch.weixin.qq.com");httpost.addHeader("X-Requested-With", "XMLHttpRequest");httpost.addHeader("Cache-Control", "max-age=0");httpost.addHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0) ");httpost.setEntity(new StringEntity(data, "UTF-8"));CloseableHttpResponse response = httpclient.execute(httpost);try {HttpEntity entity = response.getEntity();String jsonStr = EntityUtils.toString(response.getEntity(), "UTF-8");EntityUtils.consume(entity);return jsonStr;} finally {response.close();}} finally {httpclient.close();}}
}

NotifyResourceVO
这里是微信回调通知里面的加密数据解密之后用来封装解密数据的Vo 参考的上面那位老哥的

/*** 微信通知数据解密后对象* @author shenlu* @date 2020/12/18 11:17*/
public class NotifyResourceVO {/*** 服务商公众号ID*/private String sp_appid;/*** 服务商户号*/private String sp_mchid;/*** 子商户号*/private String sub_mchid;/*** 商户订单号*/private String out_trade_no;/*** 微信支付订单号*/private String transaction_id;/*** 交易类型*/private String trade_type;/*** 交易状态*/private String trade_state;/*** 交易状态描述*/private String trade_state_desc;/*** 付款银行*/private String bank_type;/*** 支付完成时间*/private String success_time;/*** 支付者*/private Payer payer;/*** 订单金额*/private Amount amount;/*** 支付者*/public class Payer{/*** 用户标识*/private String openid;public String getOpenid() {return openid;}public void setOpenid(String openid) {this.openid = openid;}}/*** 订单金额*/public class Amount{/*** 总金额*/private Integer total;/*** 用户支付金额*/private Integer payer_total;/*** 货币类型*/private String currency;/*** 用户支付币种*/private String payer_currency;public Integer getTotal() {return total;}public void setTotal(Integer total) {this.total = total;}public Integer getPayer_total() {return payer_total;}public void setPayer_total(Integer payer_total) {this.payer_total = payer_total;}public String getCurrency() {return currency;}public void setCurrency(String currency) {this.currency = currency;}public String getPayer_currency() {return payer_currency;}public void setPayer_currency(String payer_currency) {this.payer_currency = payer_currency;}}public String getSp_appid() {return sp_appid;}public void setSp_appid(String sp_appid) {this.sp_appid = sp_appid;}public String getSp_mchid() {return sp_mchid;}public void setSp_mchid(String sp_mchid) {this.sp_mchid = sp_mchid;}public String getSub_mchid() {return sub_mchid;}public void setSub_mchid(String sub_mchid) {this.sub_mchid = sub_mchid;}public String getOut_trade_no() {return out_trade_no;}public void setOut_trade_no(String out_trade_no) {this.out_trade_no = out_trade_no;}public String getTransaction_id() {return transaction_id;}public void setTransaction_id(String transaction_id) {this.transaction_id = transaction_id;}public String getTrade_type() {return trade_type;}public void setTrade_type(String trade_type) {this.trade_type = trade_type;}public String getTrade_state() {return trade_state;}public void setTrade_state(String trade_state) {this.trade_state = trade_state;}public String getTrade_state_desc() {return trade_state_desc;}public void setTrade_state_desc(String trade_state_desc) {this.trade_state_desc = trade_state_desc;}public String getBank_type() {return bank_type;}public void setBank_type(String bank_type) {this.bank_type = bank_type;}public String getSuccess_time() {return success_time;}public void setSuccess_time(String success_time) {this.success_time = success_time;}public Payer getPayer() {return payer;}public void setPayer(Payer payer) {this.payer = payer;}public Amount getAmount() {return amount;}public void setAmount(Amount amount) {this.amount = amount;}
}

WXPayNotifyVO
这里是接收回调通知的Vo,

/*** 接收微信支付通知VO* @author shenlu* @date*/
public class WXPayNotifyVO {/*** 通知的唯一ID*/private String id;/*** 通知创建时间*/private String create_time;/*** 通知类型 支付成功通知的类型为TRANSACTION.SUCCESS*/private String event_type;/*** 通知数据类型 支付成功通知为encrypt-resource*/private String resource_type;/*** 通知资源数据*/private Resource resource;/*** 回调摘要*/private String summary;/*** 通知资源数据*/public class Resource{/*** 加密算法类型*/private String algorithm;/*** 数据密文*/private String ciphertext;/*** 附加数据*/private String associated_data;/*** 随机串*/private String nonce;public String getAlgorithm() {return algorithm;}public void setAlgorithm(String algorithm) {this.algorithm = algorithm;}public String getCiphertext() {return ciphertext;}public void setCiphertext(String ciphertext) {this.ciphertext = ciphertext;}public String getAssociated_data() {return associated_data;}public void setAssociated_data(String associated_data) {this.associated_data = associated_data;}public String getNonce() {return nonce;}public void setNonce(String nonce) {this.nonce = nonce;}}public String getId() {return id;}public void setId(String id) {this.id = id;}public String getCreate_time() {return create_time;}public void setCreate_time(String create_time) {this.create_time = create_time;}public String getEvent_type() {return event_type;}public void setEvent_type(String event_type) {this.event_type = event_type;}public String getResource_type() {return resource_type;}public void setResource_type(String resource_type) {this.resource_type = resource_type;}public Resource getResource() {return resource;}public void setResource(Resource resource) {this.resource = resource;}public String getSummary() {return summary;}public void setSummary(String summary) {this.summary = summary;}
}
**```
RequestWechatException**
异常类
```java
/*** 请求微信异常,只有在请求微信地址不通时才会抛出该异常* @author shenlu* @date 2020/12/29 9:37*/
public class RequestWechatException extends Exception {private static final long serialVersionUID = -6477104653316285034L;public RequestWechatException() {super("请求微信异常");}}

WXPayXmlUtil
xml转换工具类

/****/
public final class  WXPayXmlUtil {public static DocumentBuilder newDocumentBuilder() throws ParserConfigurationException {DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();documentBuilderFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);documentBuilderFactory.setFeature("http://xml.org/sax/features/external-general-entities", false);documentBuilderFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);documentBuilderFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);documentBuilderFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);documentBuilderFactory.setXIncludeAware(false);documentBuilderFactory.setExpandEntityReferences(false);return documentBuilderFactory.newDocumentBuilder();}public static Document newDocument() throws ParserConfigurationException {return newDocumentBuilder().newDocument();}
}

JPA框架微信支付对接-V3支付接口,V2退款接口对接,复制即用相关推荐

  1. 关于微信小程序云开发,申请退款接口返回“订单不存在”的问题

    关于微信小程序云开发,申请退款接口返回"订单不存在"的问题 微信支付的退款接口返回"订单不存在",有可能是因为传入的商户订单号或微信订单号有误,但本文要讲的是关 ...

  2. SpringBoot微信小程序V3支付

    1.申请商户获取参数或文件 微信小程序appid:xxxx 商户API证书私钥文件:xxxx(微信支付) 商户号:xxxx (微信支付) 商户API证书序列号:xxxx (微信支付) 商户API v2 ...

  3. php ci框架 微信公众号 JSAPI支付

    打开微信商户平台的开发文档,下载php版的SDK与Demo 准备好参数: 1:微信公众号AppID, 2:微信公众号AppSecret, 微信公众平台,商户平台添加好项目授权地址等内容 首先获取用户o ...

  4. 微信V3支付 订单查询 退款查询

    本编在对接V3支付的时候连连撞头,希望后来人能少走点弯路,如果有bug请海涵啊,希望各位大佬也能给我点意见,话不多说上代码: service层 package com.tiyaa.mall.pay.s ...

  5. 支付中心-重复支付问题解决

    支付中心-重复支付问题解决方案 一笔订单,可以做多笔支付,怎么解决? 重复支付的异常背景 一笔订单,在支付中心可以选择多种支付方式.如支付宝扫码,支付宝app,微信扫码,微信小程序,银联.... 用户 ...

  6. 支付中心-重复支付问题解决方案

    支付中心-重复支付问题解决方案 一笔订单,可以做多笔支付,怎么解决? 重复支付的异常背景 一笔订单,在支付中心可以选择多种支付方式.如支付宝扫码,支付宝app,微信扫码,微信小程序,银联- 用户选择支 ...

  7. Python对接微信小程序V3接口进行支付,并使用uwsgi+nginx+django进行https部署

    网上找了很多教程,但是很乱很杂,并且教程资源很少且说的详细.这里就记录一下分享给大家 共分为以下几个步骤: 一.开始前准备信息 二.使用前端code获取用户的openid 三.对接小程序v3接口下单 ...

  8. Python3 微信支付(小程序支付)V3接口

    起因: 因公司项目需要网上充值功能,从而对接微信支付,目前也只对接了微信支付的小程序支付功能,在网上找到的都是对接微信支付V2版本接口,与我所对接的接口版本不一致,无法使用,特此记录下微信支付完成功能 ...

  9. 微信支付API v3接口使用应用篇

    目录 前言 版本 应用 基础配置 1.申请商户API证书 2.设置接口密钥 3.下载平台证书 接口实测 微信支付API官方客户端 1.客户端 2.支付调起参数签名 3.回调通知 参考资料 前言 最近新 ...

最新文章

  1. springJDBC实现查询方法二
  2. 谈谈Java接口Result设计
  3. windows 下 安装mysql 出现 “ ERROR 1045 (28000): Access denied for user ‘root’@‘localhost’ (using password
  4. python嵌套列表法实现树_python – 将嵌套的括号树转换为嵌套列表
  5. 【图像处理】彩色图像自适应对比度增强(OpenCV实现)
  6. Apollo 对表名区分大小写 如何配置MYSQL不区分大小写呢
  7. 51Nod-1179 最大的最大公约数【暴力】
  8. Iocomp 工业仪表盘控件包详细介绍说明
  9. 【Android】用MediaRecorder录制视频太短崩的问题
  10. layui官网地址(备份),layui文档地址(新)
  11. Spring Boot实现QQ邮件发送,用户注册功能——前后端分离版
  12. 批量图片缩小工具,JPG|PNG|BMP图片缩小工具
  13. Java Web 开发后续(二)
  14. readlink /var/lib/docker/overlay2: invalid argument的解决方案
  15. 前端高效开发必备的js库梳理,日常使用中会持续更新
  16. windows 无法完成安装, 若要在此计算机上安装Windows, 请重新启动安装
  17. Oracle19c数据库下载及安装步骤(详细)以及连接Navicat和PLSql
  18. spring-bean生命周期
  19. QT: 程序异常结束 The process was ended forcefully.
  20. 使用idea创建grade项目出现的问题

热门文章

  1. skywalking9.2
  2. Java Date equals()方法具有什么功能呢?
  3. Requests方法 -- 参数关联与JSESSION(上一个接口的返回数据作为下一个接口的请求参数)...
  4. 登录界面全屏+背景图片沉浸式+键盘将输入框顶起来+键盘弹起背景不会被弹起或者压缩
  5. python写的串口助手并连接腾讯云服务器数据库
  6. Gitlab系列八之重置管理员密码
  7. 腾讯 AI Lab 2018年度回顾
  8. DWG格式文件转DWF格式
  9. 分享191个时尚Html模板总有一个适合你
  10. 文献阅读:Improving Language Understanding by Generative Pre-Training