项目实战—springboot整合微信、支付宝NATIVE扫码支付

微信支付

申请微信支付

接入微信支付前需要再微信开放平台、公众号平台申请appid和商家号并绑定
微信支付申请操作流程

添加依赖

		<!--    微信支付SDK     -->
        <dependency>
            <groupId>com.github.wechatpay-apiv3</groupId>
            <artifactId>wechatpay-apache-httpclient</artifactId>
            <version>0.4.5</version>
        </dependency>

WechatConstant

public class WechatPayConstant {

    public static final String CANCEL_PAY_URL="/v3/pay/transactions/out-trade-no/%s/close";
    public static final String CREATE_PAY_URL="/v3/pay/transactions/native";
    public static final String QUERY_PAY_URL="/v3/pay/transactions/out-trade-no/%s?mchid=%s";
    public static final String CREATE_REFUND_URL="/v3/refund/domestic/refunds";
    public static final String QUERY_REFUND_URL="/v3/refund/domestic/refunds/%s";
    public static final String TRADE_BILL_URL="/v3/bill/tradebill?bill_date=%s&bill_type=%s";
    public static final String FLOW_BILL_URL="/v3/bill/fundflowbill?bill_date=%s";
    public static final String TRADE_STATE_SUCCESS="SUCCESS";
    public static final String REFUND_STATE_SUCCESS="SUCCESS";
}

配置httpClient

/**
 * @Author:
 * @Description:
 **/
@Component
@Data
@Slf4j
@ConfigurationProperties(prefix = "wxpay")
public class WechatPayConfig {
    /**
     * 应用编号
     */
    private String appId;
    /**
     * 商户号
     */
    private String mchId;
    /**
     * 服务商商户号
     */
    private String slMchId;
    /**
     * APIv2密钥
     */
    private String apiKey;
    /**
     * APIv3密钥
     */
    private String apiV3Key;
    /**
     * 支付通知回调地址
     */
    private String notifyUrl;
    /**
     * 退款回调地址
     */
    private String refundNotifyUrl;

    /**
     * API 证书中的 key.pem
     */
    private String keyPemPath;
	/**
     * key.pem
     */
    private String privateKey;
    /**
     * 证书序列号
     */
    private String merchantSerialNumber;

    /**
     * 微信支付V3-url前缀
     */
    private String baseUrl;

    public PrivateKey getPrivateKey(String keyPemPath){
        InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(keyPemPath);
        if(inputStream == null){
            throw new RuntimeException("私钥文件不存在");
        }
        return PemUtil.loadPrivateKey(inputStream);
    }



    /**
     * 获取签名验证器
     */
    public Verifier getVerifier() {
        // 获取商户私钥
       PrivateKey privateKey = getPrivateKey(keyPemPath);
        // 私钥签名对象
        PrivateKeySigner privateKeySigner = new PrivateKeySigner(merchantSerialNumber, privateKey);
        // 身份认证对象
        WechatPay2Credentials wechatPay2Credentials = new WechatPay2Credentials(mchId, privateKeySigner);
        // 获取证书管理器实例
        CertificatesManager certificatesManager = CertificatesManager.getInstance();
        try {
            // 向证书管理器增加需要自动更新平台证书的商户信息
            certificatesManager.putMerchant(mchId, wechatPay2Credentials, apiV3Key.getBytes(StandardCharsets.UTF_8));
        } catch (IOException | GeneralSecurityException | HttpCodeException e) {
            e.printStackTrace();
        }
        try {
            return certificatesManager.getVerifier(mchId);
        } catch (NotFoundException e) {
            e.printStackTrace();
            throw new RuntimeException("获取签名验证器失败");
        }
    }

    /**
     * 给容器中加入WechatPay的HttpClient,虽然它是WechatPay的,
     * 但可以用它给任何外部发请求,因为它只对发给WechatPay的请求做处理而不对发给别的的请求做处理.
     */
    @Bean
    public CloseableHttpClient httpClient(){
        //私钥
        PrivateKey merchantPrivateKey = getPrivateKey(keyPemPath);
        Verifier verifier = getVerifier();
        WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
                .withMerchant(mchId,merchantSerialNumber,merchantPrivateKey)
                .withValidator(new WechatPay2Validator(verifier));
// 				  不需要签名验证
//                withValidator(response->true);
        CloseableHttpClient httpClient = builder.build();
        return httpClient;
    }
   
}

生成支付二维码

public void genQRCode(HttpServletResponse httpResponse, PayDTO payDTO) throws Exception {

        String key = RedisContents.WX_QR_PREFIX + payDTO.getOrderId();

        SimpleDateFormat sdfTime = commonUtils.getSdfTime();
        Date openDate = sdfTime.parse(payDTO.getTimeExpire());
        long nowTime = new Date().getTime();
        long openTime = openDate.getTime();
        long sur = openTime - nowTime;
        String qrCode = (String) redisTemplate.opsForValue().get(key);
        if (qrCode != null){
            QrCodeUtil.generate(qrCode, 300, 300,"png", httpResponse.getOutputStream());
            return ;
        }

        //请求构造
        HttpPost httpPost = new HttpPost(wechatPayConfig.getBaseUrl()
                                            + WechatPayConstant.CREATE_PAY_URL);
        String orderId = payDTO.getOrderId();
        Map<String, Object> bodyData = new HashMap<>();
        bodyData.put("appid", wechatPayConfig.getAppId());
        bodyData.put("mchid", wechatPayConfig.getMchId());
        bodyData.put("description", payDTO.getSubject());
        bodyData.put("out_trade_no", orderId);
        bodyData.put("notify_url", wechatPayConfig.getNotifyUrl());
        // 设置绝对超时时间,即开标时间
        SimpleDateFormat wxpaySdfDate = commonUtils.getWxpaySdfDate();
        bodyData.put("time_expire",wxpaySdfDate.format(openDate));
        // 金额单位是分
        HashMap<String,Integer> amount = new HashMap<>();
        //单位是分
        BigDecimal bigDecimal = new BigDecimal(payDTO.getOrderAmount()).multiply(BigDecimal.valueOf(100));
        amount.put("total",bigDecimal.intValue());
        bodyData.put("amount",amount);
        String jsonReqData = new Gson().toJson(bodyData);
        StringEntity entity = new StringEntity(jsonReqData,"utf-8");
        entity.setContentType("application/json");
        httpPost.setEntity(entity);
        //请求头
        httpPost.setHeader("Accept","application/json");
        //完成签名并执行请求
        CloseableHttpResponse wxpayResponse =  httpClient.execute(httpPost);

        Map<String,String> dataMap = null;
        try{
            int statusCode = wxpayResponse.getStatusLine().getStatusCode();
            //成功
            if(statusCode==200){
                String body= EntityUtils.toString(wxpayResponse.getEntity());
                dataMap = new Gson().fromJson(body,HashMap.class);
                String qrCodeUrl = dataMap.get("code_url");
                redisTemplate.opsForValue().set(key,qrCodeUrl,sur, TimeUnit.MILLISECONDS);
                QrCodeUtil.generate(qrCodeUrl, 300, 300,"png", httpResponse.getOutputStream());

            }
            //失败
            else{
                if(statusCode!=204){
                    String body = EntityUtils.toString(wxpayResponse.getEntity());
                    log.error(body);
                    return ;
                }
            }
        }
        finally{
            wxpayResponse.close();
        }

    }

支付成功回调

	@PostMapping("/wxpay/notify")
    public HashMap<String,String> wxPayNotify(@RequestBody Map<String,Object> signalRes, HttpServletResponse response){
        log.debug("收到微信回调");
        try{
            //验签,用密文解密出明文
            Map<String,String> resource = (Map<String,String>)signalRes.get("resource");
            String ciphertext = resource.get("ciphertext");
            String associatedData = resource.get("associated_data");
            String nonce = resource.get("nonce");
            String plainText = new AesUtil(wechatPayConfig.getApiV3Key().getBytes(StandardCharsets.UTF_8))
                                    .decryptToString(associatedData.getBytes(StandardCharsets.UTF_8)
                                            ,nonce.getBytes(StandardCharsets.UTF_8)
                                            ,ciphertext);
            //转换
            HashMap<String,Object> data = new Gson().fromJson(plainText,HashMap.class);
            // 查询是否支付
            LambdaQueryWrapper<PaymentInfo> pWrapper = new LambdaQueryWrapper<>();
            pWrapper.eq(PaymentInfo::getTradeNo,(String)data.get("transaction_id"));
            PaymentInfo paymentInfoDb = paymentService.getOne(pWrapper);
            if (paymentInfoDb != null){
                return null;
            }
            synchronized(this){
                    // 保存支付信息
                    PaymentInfo paymentInfo = new PaymentInfo();
                    long id = commonUtils.nextId("wx:pay");
                    paymentInfo.setId(id);
                    paymentInfo.setOrderId((String)data.get("out_trade_no"));
                    paymentInfo.setTradeNo((String)data.get("transaction_id"));
                    paymentInfo.setPaymentType("wechat");
                    paymentInfo.setTradeType((String)data.get("trade_type"));
                    paymentInfo.setTradeState((String)data.get("trade_state"));
                    // 获取金额
                    LinkedTreeMap<String,Object> amountMap = (LinkedTreeMap<String, Object>) data.get("amount");
                    BigDecimal totalAmount = new BigDecimal((Double) amountMap.get("total"))
                                                    .divide(new BigDecimal(100))
                                                    .setScale(2, BigDecimal.ROUND_DOWN);
                    paymentInfo.setTotalAmount(totalAmount);
                    Date date = new Date();
                    paymentInfo.setTradeTime(date);
                    paymentInfo.setCreateTime(date);
                    paymentInfo.setUpdateTime(date);
                    //交易类型(扫码 刷脸等等)
                    paymentInfo.setTradeType((String)data.get("trade_type"));
                    paymentInfo.setTradeState((String)data.get("trade_state"));
                    //存放全部数据(json)以备不时之需
                    paymentInfo.setContent(plainText);
                    paymentService.save(paymentInfo);
                    log.info("订单{}的支付记录添加成功,支付记录id为{}.",(String)data.get("out_trade_no"),paymentInfo.getId());

            }
            return null;
        } catch(Exception e){
            log.error(e.getMessage());
            response.setStatus(HttpStatus.SC_INTERNAL_SERVER_ERROR);
            HashMap<String,String> map=new HashMap<>();
            map.put("code","FAIL");
            map.put("message","支付失败");
            return map;
        }
    }

订单退款

public boolean wechatRefund(String orderId,String tradeNo,String refundAmount) throws IOException {

        // 请求构造
        HttpPost httpPost = new HttpPost(wechatPayConfig.getBaseUrl()
                                    + WechatPayConstant.CREATE_REFUND_URL );
        // 构造请求体
        HashMap<String,Object> reqData = new HashMap<>();
        reqData.put("out_trade_no",orderId);//订单编号
        reqData.put("out_refund_no",tradeNo);//退款单编号
        reqData.put("reason","xxxx");//退款原因
        reqData.put("notify_url",wechatPayConfig.getRefundNotifyUrl());//退款通知地址
        HashMap<String,Object> amount = new HashMap<>();
        // 单位是分
        BigDecimal bigDecimal = new BigDecimal(refundAmount)
                                    .multiply(BigDecimal.valueOf(100));
        //退款金额
        amount.put("refund",bigDecimal.intValue());
        //原订单金额
        amount.put("total",bigDecimal.intValue());
        amount.put("currency","CNY");//币种
        reqData.put("amount",amount);
        //将参数转换成json字符串
        String jsonData = new Gson().toJson(reqData);
        log.info("请求参数 ===> {}"+jsonData);
        StringEntity entity = new StringEntity(jsonData,"utf-8");
        httpPost.setEntity(entity);//将请求报文放入请求对象
        //请求头
        httpPost.setHeader("content-type","application/json");
        httpPost.setHeader("Accept","application/json");//设置响应报文格式
        //完成签名并执行请求
        CloseableHttpResponse response = httpClient.execute(httpPost);
        try{
            //解析响应结果
            String bodyAsString = EntityUtils.toString(response.getEntity());
            int statusCode = response.getStatusLine().getStatusCode();
            if(statusCode == 200){
                log.info("成功, 退款返回结果 = "+bodyAsString);
                return true;
            } else{
                if(statusCode!=204){
                    log.warn("退款异常:"+bodyAsString);
                }
                return false;
            }
        }
        finally{
            response.close();
        }
    }

支付宝支付

申请支付宝支付

接入支付宝支付前需要在支付宝开放平台创建应用和申请商户,并将appid和商家号并绑定
支付宝支付申请流程

添加依赖

      <!--    支付宝支付SDK    -->
        <dependency>
            <groupId>com.alipay.sdk</groupId>
            <artifactId>alipay-sdk-java</artifactId>
            <!-- 对接正式环境需要这个版本及以上-->
            <version>4.38.183.ALL</version>
        </dependency>

配置AlipayClient

支付宝接口参数

@Data
@Component
@ConfigurationProperties(prefix = "alipay")
public class AlipayProperty {
	// 应用id
    private String appId;
	// 应用私钥
    private String appPrivateKey;
	// 支付宝公钥
    private String alipayPublicKey;
	// 支付宝异步通知地址
    private String notifyUrl;
	// 支付宝网关
    private String gatewayUrl;
}

配置AlipayClient

/**
     * 支付宝支付客户端
     * 验签时 charset要一致,不然会验签失败
     * @return
     */
    @Bean
    public AlipayClient alipayClient(){
        return new DefaultAlipayClient(
                alipayProperty.getGatewayUrl(),
                alipayProperty.getAppId(),
                alipayProperty.getAppPrivateKey(),
                "json",
                AlipayConstants.CHARSET_UTF8,
                alipayProperty.getAlipayPublicKey(),
                AlipayConstants.SIGN_TYPE_RSA2
        );
    }

生成支付二维码

/**
     * 生成支付二维码
     * @return
     */
    public void genQRCode(HttpServletResponse httpResponse,PayDTO payDTO) throws Exception {
        String key = RedisContents.ZFB_QR_PREFIX + payDTO.getOrderId();
        String timeExpire = payDTO.getTimeExpire();
        Date nowDate = new Date();
        SimpleDateFormat sdfTime = commonUtils.getSdfTime();
        Date openDate = sdfTime.parse(timeExpire);

        long nowTime = nowDate.getTime();
        long openTime = openDate.getTime();
        String qrCode = (String) redisTemplate.opsForValue().get(key);
        if (qrCode != null){
            QrCodeUtil.generate(qrCode, 300, 300,"png", httpResponse.getOutputStream());
            return ;
        }
        AlipayTradePrecreateRequest request = new AlipayTradePrecreateRequest();
        // 异步通知支付结果
        request.setNotifyUrl(alipayProperty.getNotifyUrl());
        //将商品的属性填写进去
        JSONObject bizContent = new JSONObject();
        bizContent.put("out_trade_no", payDTO.getOrderId());
        bizContent.put("total_amount", payDTO.getOrderAmount());
        bizContent.put("subject", payDTO.getSubject());
        // 设置绝对超时时间,即开标时间
        bizContent.put("time_expire",timeExpire);
        request.setBizContent(bizContent.toString());
        AlipayTradePrecreateResponse response = null;
        try {
            response = alipayClient.execute(request);
        } catch (AlipayApiException e) {
            throw new RuntimeException(e);
        }
        if(response.isSuccess()){
            log.info("++++++++++++++++调用成功++++++++++++++++");
            String respQrCode = response.getQrCode();
            // 保存respQrCode到redis
            redisTemplate.opsForValue().set(key,respQrCode,sur, TimeUnit.MILLISECONDS);
            // 生成支付二维码并输出到响应
            QrCodeUtil.generate(respQrCode, 300, 300,"png", httpResponse.getOutputStream());

            // JSAPI支付宝收银台
//            String form = "";
//            form = alipayClient.pageExecute(request).getBody();
//            //设置响应结果,将返回的内容写出到浏览器
//            httpResponse.setContentType("text/html;charset=UTF-8");
//            httpResponse.getWriter().write(form);//直接将完整的表单html输出到页面
//            httpResponse.getWriter().flush();
//            httpResponse.getWriter().close();

        }

支付成功回调

 /**
     * 支付宝异步回调通知
     * @return
     */
    @AutoLog(value = "支付宝支付通知")
    @PostMapping("/alipay/notify")
    public void alipayNotify(HttpServletRequest request, HttpServletResponse response) throws Exception {
        request.setCharacterEncoding("UTF-8");
        response.setCharacterEncoding("UTF-8");
        log.info("+++++++++++支付宝异步通知+++++++++++");
        String outTradeNo = request.getParameter("out_trade_no");
        String tradeNo = request.getParameter("trade_no");
        String totalAmount = request.getParameter("total_amount");
        // TODO 业务处理
//        商户订单号(out_trade_no):商户网站生成的唯一订单号。
//        支付宝交易号(trade_no):支付宝交易凭证号。
//        交易状态(trade_status):交易的当前状态,包括支付成功、支付失败等。
//        通知时间(notify_time):支付宝服务器发送通知的时间。
//        交易金额(total_amount):本次交易支付的订单金额。
        Map<String, String[]> requestParams = request.getParameterMap();
        PrintWriter out = response.getWriter();
        try {
            // 这里首先验签 验证是支付宝发来的请求
            HashMap<String, String> params = new HashMap<>();
            for (Iterator<String> iter = requestParams.keySet().iterator(); iter.hasNext(); ) {
                String name = iter.next();
                String[] values = requestParams.get(name);
                String valueStr = "";
                for (int i = 0; i < values.length; i++) {
                    valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ",";
                }
//                    valueStr = new String(valueStr.getBytes(),"UTF-8");
                params.put(name, valueStr);
            }
//                params.put("subject","保费");
            log.info("sign params:{}",params);
            log.info("alipay params:{}",alipayProperty);
            // 验证签名,确保是支付宝发送的
            // 验签失败可以原因
            // 1、参数2为支付宝公钥
            // 2、字符编码不统一  AlipayClient的字符编码要与参数3的字符编码统一
            boolean signVerified = AlipaySignature.rsaCheckV1(params,
                                        alipayProperty.getAlipayPublicKey(),
                                        AlipayConstants.CHARSET_UTF8,
                                        AlipayConstants.SIGN_TYPE_RSA2);
            if (!signVerified) {
                log.error("验签失败");
                out.print("fail");
                return;
            }
            // 查询是否支付
            LambdaQueryWrapper<PaymentInfo> pWrapper = new LambdaQueryWrapper<>();
            pWrapper.eq(PaymentInfo::getTradeNo,tradeNo);
            PaymentInfo paymentInfoDb = paymentService.getOne(pWrapper);
            if (paymentInfoDb != null){
                return;
            }
            // 保存支付信息
            PaymentInfo paymentInfo = new PaymentInfo();
            long id = commonUtils.nextId("ali:pay");
            paymentInfo.setId(id);
            paymentInfo.setOrderId(outTradeNo);
            paymentInfo.setTradeNo(tradeNo);
            paymentInfo.setPaymentType("alipay");
            paymentInfo.setTradeType("NATIVE");
            paymentInfo.setTradeState(request.getParameter("trade_status"));
            paymentInfo.setTotalAmount(new BigDecimal(totalAmount));
            Date date = new Date();
            paymentInfo.setTradeTime(date);
            paymentInfo.setCreateTime(date);
            paymentInfo.setUpdateTime(date);
            paymentService.save(paymentInfo);
        }catch (Exception e){

        }
        // 获取支付订单号
        log.info("异步通知参数outTradeNo:{}",outTradeNo);
        log.info("异步通知参数tradeNo:{}",tradeNo);
    }

订单退款

/**
     * 支付宝退款
     * @return
     */
    public boolean alipayRefund(String tradeNo,String refundAmount) {
        AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
        // TODO 业务代码查询订单信息
        JSONObject bizContent = new JSONObject();
        bizContent.put("trade_no", tradeNo);
        bizContent.put("refund_amount", refundAmount);
        bizContent.put("out_request_no", "HZ01RF001");
        request.setBizContent(bizContent.toString());
        try{
            AlipayTradeRefundResponse response = alipayClient.execute(request);
            if(response.isSuccess()){
                System.out.println("退款成功");
                return true;
            } else {
                System.out.println("退款失败");
                return false;
            }
        }catch (Exception e){
            e.printStackTrace();
            return false;
        }
    }

可能出现的问题

1、解决微信支付 java.security.InvalidKeyException: Illegal key size

2、关于支付宝、微信支付成功回调验签失败问题,仔细检查公钥,字符集编码,参数等

  • 10
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,以下是一个springboot整合微信扫码登录的示例: 1. 首先,在微信公众平台上创建一个应用,并获取到AppID和AppSecret。 2. 在Spring Boot项目中添加以下依赖: ``` <dependency> <groupId>com.github.binarywang</groupId> <artifactId>weixin-java-mp</artifactId> <version>3.4.</version> </dependency> ``` 3. 在application.properties文件中配置AppID和AppSecret: ``` wechat.mp.appId=your_app_id wechat.mp.secret=your_app_secret ``` 4. 创建一个Controller,用于处理微信扫码登录的请求: ``` @RestController @RequestMapping("/wechat") public class WechatController { @Autowired private WxMpService wxMpService; @GetMapping("/login") public String login(HttpServletRequest request) throws WxErrorException { String redirectUrl = "http://your_domain.com/wechat/callback"; String state = UUID.randomUUID().toString(); String url = wxMpService.oauth2buildAuthorizationUrl(redirectUrl, WxConsts.OAuth2Scope.SNSAPI_USERINFO, state); return "redirect:" + url; } @GetMapping("/callback") public String callback(HttpServletRequest request) throws WxErrorException { String code = request.getParameter("code"); WxMpOAuth2AccessToken accessToken = wxMpService.oauth2getAccessToken(code); WxMpUser user = wxMpService.oauth2getUserInfo(accessToken, null); // TODO: 处理用户信息 return "success"; } } ``` 5. 在启动类中添加以下代码,初始化WxMpService: ``` @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } @Bean public WxMpService wxMpService() { WxMpService wxMpService = new WxMpServiceImpl(); wxMpService.setWxMpConfigStorage(wxMpConfigStorage()); return wxMpService; } @Bean public WxMpConfigStorage wxMpConfigStorage() { WxMpInMemoryConfigStorage configStorage = new WxMpInMemoryConfigStorage(); configStorage.setAppId("your_app_id"); configStorage.setSecret("your_app_secret"); return configStorage; } } ``` 以上就是一个简单的springboot整合微信扫码登录的示例,希望对你有帮助。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值