原创文章:对接微信支付(二)统一下单API – 编程屋
大家可以先想一下:大家平时在PC端发起的支付都需要什么,是不是你选好商品之后,点击支付,然后PC端弹出来一个二维码,你扫码付款,付款完成之后就OK了。当然这只是针对我们用户来说的,对于我们的一个后台应该是如何来实现的呢?
用户扫码后:1)后台生成订单 2)调用统一下单API 3)返回微信支付链接code_url 4)将链接生成的二维码展示给用户。5)用户扫码后提交扫码链接6)微信验证链接有效性7)返回需要用户支付授权7)用户输入支付密码,提交支付授权8)验证授权,完成支付交易。
相关依赖:
<?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> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.7.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.liubujun</groupId> <artifactId>payment-demo</artifactId> <version>0.0.1-SNAPSHOT</version> <name>payment-demo</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--Swagger--> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>2.7.0</version> </dependency> <!--Swagger ui--> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> <version>2.7.0</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.20</version> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.3.0</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!--生成自定义配置的元数据信息--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency> <!--微信支付的sdk--> <dependency> <groupId>com.github.wechatpay-apiv3</groupId> <artifactId>wechatpay-apache-httpclient</artifactId> <version>0.3.0</version> </dependency> <!--json处理器--> <dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId> </dependency> </dependencies> <build> <!--项目打包时会将java目录中的*.xml文件进行打包--> <resources> <resource> <directory>src/main/java</directory> <includes> <include>/*.xml</include> </includes> <filtering>false</filtering> </resource> </resources> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
讯享网
枚举类:

讯享网@AllArgsConstructor @Getter public enum WxApiType { / * Native下单 */ NATIVE_PAY("/v3/pay/transactions/native"), / * 查询订单 */ ORDER_QUERY_BY_NO("/v3/pay/transactions/out-trade-no/%s"), / * 关闭订单 */ CLOSE_ORDER_BY_NO("/v3/pay/transactions/out-trade-no/%s/close"), / * 申请退款 */ DOMESTIC_REFUNDS("/v3/refund/domestic/refunds"), / * 查询单笔退款 */ DOMESTIC_REFUNDS_QUERY("/v3/refund/domestic/refunds/%s"), / * 申请交易账单 */ TRADE_BILLS("/v3/bill/tradebill"), / * 申请资金账单 */ FUND_FLOW_BILLS("/v3/bill/fundflowbill"); / * 类型 */ private final String type; }
@AllArgsConstructor @Getter public enum WxNotifyType { / * 支付通知 */ NATIVE_NOTIFY("/api/wx-pay/native/notify"), / * 退款结果通知 */ REFUND_NOTIFY("/api/wx-pay/refunds/notify"); / * 类型 */ private final String type; }
主要配置类:WxPayConfig
讯享网package com.atguigu.paymentdemo.config; 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.ScheduledUpdateCertificatesVerifier; import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials; import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Validator; import com.wechat.pay.contrib.apache.httpclient.util.PemUtil; import lombok.Data; import lombok.extern.slf4j.Slf4j; import org.apache.http.impl.client.CloseableHttpClient; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.nio.charset.StandardCharsets; import java.security.PrivateKey; @Configuration @PropertySource("classpath:wxpay.properties") //读取配置文件 @ConfigurationProperties(prefix="wxpay") //读取wxpay节点 @Data //使用set方法将wxpay节点中的值填充到当前类的属性中 @Slf4j public class WxPayConfig { // 商户号 private String mchId; // 商户API证书序列号 private String mchSerialNo; // 商户私钥文件 private String privateKeyPath; // APIv3密钥 private String apiV3Key; // APPID private String appid; // 微信服务器地址 private String domain; // 接收结果通知地址 private String notifyDomain; / * 获取商户的私钥文件 * @param filename * @return */ private PrivateKey getPrivateKey(String filename){ try { return PemUtil.loadPrivateKey(new FileInputStream(filename)); } catch (FileNotFoundException e) { throw new RuntimeException("私钥文件不存在",e); } } / * 获取签名验证器 * @return */ @Bean public ScheduledUpdateCertificatesVerifier getVerifier(){ //获取商户私钥 PrivateKey privateKey = getPrivateKey(privateKeyPath); //私钥签名对象 PrivateKeySigner privateKeySigner = new PrivateKeySigner(mchSerialNo, privateKey); //身份认证对象 WechatPay2Credentials wechatPay2Credentials = new WechatPay2Credentials(mchId, privateKeySigner); //使用定时更新的签名验证器,不需要传入证书 ScheduledUpdateCertificatesVerifier verifier = new ScheduledUpdateCertificatesVerifier( wechatPay2Credentials, apiV3Key.getBytes(StandardCharsets.UTF_8)); return verifier; } / * 获取http请求对象 * @param verifier * @return */ @Bean(name = "wxPayClient") public CloseableHttpClient getWxPayClient(ScheduledUpdateCertificatesVerifier verifier){ //获取商户私钥 PrivateKey privateKey = getPrivateKey(privateKeyPath); WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create() .withMerchant(mchId, mchSerialNo, privateKey) .withValidator(new WechatPay2Validator(verifier)); //通过WechatPayHttpClientBuilder构造的httpClient,会自动处理签名和验签,并进行证书的自动更新 CloseableHttpClient httpClient = builder.build(); return httpClient; } / * 获取HttpClient,无需进行应答签名验证,跳过验签的流程 */ @Bean(name = "wxPayNoSignClient") public CloseableHttpClient getWxPayNoSignClient(){ //获取商户私钥 PrivateKey privateKey = getPrivateKey(privateKeyPath); //用于构造HttpClient WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create() //设置商户信息 .withMerchant(mchId, mchSerialNo, privateKey) //无需进行签名验证、通过withValidator((response) -> true)实现 .withValidator((response) -> true); // 通过WechatPayHttpClientBuilder构造的HttpClient,会自动的处理签名和验签,并进行证书自动更新 CloseableHttpClient httpClient = builder.build(); log.info("== getWxPayNoSignClient END =="); return httpClient; } }
下单controller:
@RestController @RequestMapping("/api/wx-pay") @Api(tags = "网站微信支付API") @Slf4j public class WxPayController { @Resource private WxPayService wxPayService; @Resource private Verifier verifier; @ApiOperation("调用统一下单API,生成支付二维码") @PostMapping("/native/{productId}") public R nativePay(@PathVariable Long productId) throws Exception { //返回支付二维码链接和订单号 Map<String, Object> map = wxPayService.nativePay(productId); return R.ok().setData(map); } / * 接收微信服务器发来的请求 * @param request * @param response * @return */ @PostMapping("/native/notify") public String nativeNotify(HttpServletRequest request, HttpServletResponse response){ Gson gson = new Gson(); //应答对象 Map<String, String> map = new HashMap<>(); try { //处理通知参数 String body = HttpUtils.readData(request); Map<String,Object> bodyMap = gson.fromJson(body, HashMap.class); String requestId = (String)bodyMap.get("id"); log.info("支付通知的id====》{}",bodyMap.get("id")); // log.info("支付通知的完整数据====》",body); // int a = 9 / 0; //TODO 签名的验证 WechatPay2ValidatorForRequest wechatPay2ValidatorForRequest = new WechatPay2ValidatorForRequest(verifier, requestId,body); if(!wechatPay2ValidatorForRequest.validate(request)){ log.error("通知验签失败"); response.setStatus(500); map.put("code","ERROR"); map.put("message","通知验签失败"); return gson.toJson(map); } log.info("通知验签成功"); //TODO 处理订单 wxPayService.processOrder(bodyMap); //模拟接收微信端的重复通知 TimeUnit.SECONDS.sleep(5); //成功应答 response.setStatus(200); map.put("code","SUCCESS"); map.put("message","成功"); return gson.toJson(map); } catch (Exception e) { e.printStackTrace(); //失败应答 response.setStatus(500); map.put("code","ERROR"); map.put("message","失败"); return gson.toJson(map); } }
下单service:
讯享网 @Override public Map<String, Object> nativePay(Long productId) throws Exception { log.info("生成订单"); //生成订单 OrderInfo orderInfo = orderInfoService.createOrderByProductId(productId); String codeUrl = orderInfo.getCodeUrl(); if (!StringUtils.isEmpty(codeUrl)){ log.info("二维码订单已经存在"); //返回二维码 Map<String, Object> map = new HashMap<>(); map.put("codeUrl",codeUrl); map.put("orderNo",orderInfo.getOrderNo()); return map; } log.info("调用统一下单API"); //调用统一下单API HttpPost httpPost = new HttpPost(wxPayConfig.getDomain().concat(WxApiType.NATIVE_PAY.getType())); // 请求body参数 Gson gson = new Gson(); Map paramMap = new HashMap<>(); paramMap.put("appid",wxPayConfig.getAppid()); paramMap.put("mchid",wxPayConfig.getMchId()); paramMap.put("description",orderInfo.getTitle()); paramMap.put("out_trade_no",orderInfo.getOrderNo()); //微信支付成功之后,向该地址发送通知 paramMap.put("notify_url",wxPayConfig.getNotifyDomain().concat(WxNotifyType.NATIVE_NOTIFY.getType())); Map amountMap = new HashMap<>(); amountMap.put("total",orderInfo.getTotalFee()); amountMap.put("currency","CNY"); paramMap.put("amount",amountMap); //将参数转为json字符串 String jsonParams = gson.toJson(paramMap); log.info("请求参数"+jsonParams); StringEntity entity = new StringEntity(jsonParams,"utf-8"); entity.setContentType("application/json"); httpPost.setEntity(entity); httpPost.setHeader("Accept", "application/json"); //完成签名并执行请求 CloseableHttpResponse response = wxPayClient.execute(httpPost); try { String bodyAsString = EntityUtils.toString(response.getEntity()); //响应体 int statusCode = response.getStatusLine().getStatusCode(); //响应状态码 if (statusCode == 200) { //处理成功 log.info("成功 返回结果 = " + bodyAsString); } else if (statusCode == 204) { //处理成功,无返回Body log.info("成功"); } else { log.info("Native 下单失败,响应码 " + statusCode+ ",返回结果 = " + bodyAsString); throw new IOException("request failed"); } //响应结果 HashMap<String,String> resultMap = gson.fromJson(bodyAsString, HashMap.class); //二维码 codeUrl = resultMap.get("code_url"); //保存二维码 String orderNo = orderInfo.getOrderNo(); orderInfoService.saveCodeUrl(orderNo,codeUrl); //返回二维码 Map<String, Object> map = new HashMap<>(); map.put("codeUrl",codeUrl); map.put("orderNo",orderInfo.getOrderNo()); return map; } finally { response.close(); } }
这是在B站上观看尚硅谷的视屏进行学习的,退款和下单都可以正常进行,但是验证签名一直失败??
以上只是部分内容,为了维护方便,本文已迁移到新地址:
对接微信支付(二)统一下单API – 编程屋

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/16566.html