首页 > 其他分享 >微信支付服务商api对接

微信支付服务商api对接

时间:2022-12-22 14:46:27浏览次数:58  
标签:requestParams String 微信 服务商 api httpPost JSONObject put new

引入官方sdk

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

新建配置类

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

新建service

/**
 * @Description : 微信v3支付服务
 * @Author : wzkris
 * @Version : V1.0.0
 * @Date : 2022/11/27 15:48
 * @Description : 微信支付服务,金额一律为分;对账单中的交易金额单位为元
 */
@Component
public class WxPayService {

    @Resource
    private WxPayConfig wxconfig;

    @Resource
    private GtConfig gtConfig;


    /**
     * 请求客户端
     */
    private static CloseableHttpClient httpClient;

    /**
     * 微信平台证书验证器
     */
    private static Verifier verifier;


    @PostConstruct
    public void init() {
        try {
            String privateKey = FileUtils.readFileToString(wxconfig.privateKeyPath);
            wxconfig.setPrivateKey(PemUtil.loadPrivateKey(privateKey));
            //初始化微信平台证书容器,certificatesManager可以放多个商户
            CertificatesManager certificatesManager = CertificatesManager.getInstance();
            certificatesManager.putMerchant(
                    wxconfig.merchantId,
                    new WechatPay2Credentials(wxconfig.merchantId, new PrivateKeySigner(wxconfig.merchantSerialNumber, wxconfig.privateKey)),
                    wxconfig.getApiV3key().getBytes(StandardCharsets.UTF_8)
            );
            verifier = certificatesManager.getVerifier(wxconfig.merchantId);
            //初始化httpClient
            httpClient = WechatPayHttpClientBuilder
                    .create()
                    .withMerchant(wxconfig.merchantId, wxconfig.merchantSerialNumber, PemUtil.loadPrivateKey(privateKey))
                    .withValidator(new WechatPay2Validator(verifier))
                    .build();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }


    /**
     * 添加子商户进件申请
     */
    public JSONObject addSubMerchant(SubMerchantApplyment applyment) {
        HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/applyment4sub/applyment/");
        httpPost.addHeader("Accept", "application/json");
        httpPost.addHeader("Content-type", "application/json; charset=utf-8");
        httpPost.addHeader("Wechatpay-Serial", String.valueOf(verifier.getValidCertificate().getSerialNumber()));
        //敏感信息加密
        encryptSubMerchantInfo(applyment);

        httpPost.setEntity(new StringEntity(JSONObject.toJSONString(applyment), StandardCharsets.UTF_8));
        try {
            CloseableHttpResponse response = httpClient.execute(httpPost);
            JSONObject jsonObject = JSONObject.parseObject(EntityUtils.toString(response.getEntity()));
            if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
                throw new ServiceException(jsonObject);
            }
            return jsonObject;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 进件敏感信息加密
     *
     * @param applyment 特约商户进件
     */
    public void encryptSubMerchantInfo(SubMerchantApplyment applyment) {
        //1.超级管理员信息加密
        ContactInfo contactInfo = applyment.getContactInfo();
        contactInfo.setContactName(encrypt(contactInfo.getContactName()))
                .setOpenId(encrypt(contactInfo.getOpenId()))
                .setMobilePhone(encrypt(contactInfo.getMobilePhone()))
                .setContactEmail(encrypt(contactInfo.getContactEmail()));
        //1.1经办人类型
        if (contactInfo.getContactType().equals(ContactInfo.contactType.SUPER.toString())) {
            contactInfo.setContactIdNumber(encrypt(contactInfo.getContactIdNumber()));
        }
        //2.主体资料
        SubjectInfo subjectInfo = applyment.getSubjectInfo();
        //2.1受益人信息加密
        if (!subjectInfo.getUboInfoList().isEmpty()) {
            //每个受益人信息都加密
            for (UboInfo uboInfo : subjectInfo.getUboInfoList()) {
                uboInfo.setUboIdDocName(encrypt(uboInfo.getUboIdDocName()))
                        .setUboIdDocNumber(encrypt(uboInfo.getUboIdDocNumber()))
                        .setUboIdDocAddress(encrypt(uboInfo.getUboIdDocAddress()));
            }
        }
        //3.结算银行账户
        BankAccountInfo bankAccountInfo = applyment.getBankAccountInfo();
        bankAccountInfo.setAccountName(encrypt(bankAccountInfo.getAccountName()))
                .setAccountNumber(encrypt(bankAccountInfo.getAccountNumber()));
    }


    /**
     * 查看申请单状态
     *
     * @param businessCode 业务申请编号
     */
    public ApplymentResult searchApplymentStatus(String businessCode) {
        HttpGet httpGet = new HttpGet("https://api.mch.weixin.qq.com/v3/applyment4sub/applyment/business_code/" + businessCode);
        try {
            CloseableHttpResponse response = httpClient.execute(httpGet);
            if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
                throw new ServiceException(JSONObject.parseObject(EntityUtils.toString(response.getEntity())));
            }
            return JSONObject.parseObject(EntityUtils.toString(response.getEntity()), ApplymentResult.class);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }


    /**
     * @param bo    支付参数
     * @param enums 支付渠道
     * @return 返回参数
     * @Description 服务商付款
     */
    public JSONObject pay(WxPayParamsBO bo, PayEnums enums) {
        //1.组装参数
        JSONObject requestParams = new JSONObject();
        requestParams.put("sp_appid", wxconfig.getAppId());
        requestParams.put("sp_mchid", wxconfig.getMerchantId());
        //子商户appid
        requestParams.put("sub_appid", bo.getSubAppId());
        //子商户号
        requestParams.put("sub_mchid", bo.getSubMchId());
        requestParams.put("description", bo.getDescription());
        requestParams.put("out_trade_no", bo.getOrderNo());
        requestParams.put("notify_url", gtConfig.getUrl() + bo.getNotifyUrl());
        JSONObject amount = new JSONObject();
        amount.put("total", bo.getAmount());
        requestParams.put("amount", amount);
        JSONObject payer = new JSONObject();
        if (enums.equals(PayEnums.JSAPI)) {
            payer.put("sp_openid", bo.getOpenId());
            requestParams.put("payer", payer);
        } else if (enums.equals(PayEnums.H5)) {
            JSONObject sceneInfo = new JSONObject();
            //拿到HttpServletRequest解析ip
            HttpServletRequest request = ServletUtils.getRequest();
            sceneInfo.put("payer_client_ip", IpUtils.getIpAddr(request));
            JSONObject h5Info = new JSONObject();
            h5Info.put("type", IpUtils.getOperatorSys(request));
            sceneInfo.put("h5_info", h5Info);
            requestParams.put("scene_info", sceneInfo);
        }

        //2.初始化post请求
        HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/partner/transactions/" + enums.getPath());
        httpPost.addHeader("Accept", "application/json");
        httpPost.addHeader("Content-type", "application/json; charset=utf-8");
        httpPost.setEntity(new StringEntity(requestParams.toJSONString(), StandardCharsets.UTF_8));
        try {
            CloseableHttpResponse response = httpClient.execute(httpPost);
            //拿到返回参数
            JSONObject resParams = JSONObject.parseObject(EntityUtils.toString(response.getEntity()));
            if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
                throw new ServiceException(resParams);
            }
            return resParams;
        } catch (IOException e) {
            throw new ServiceException(e.getMessage());
        }

    }


    /**
     * @param bo 查询订单详情
     * @Description 查询订单信息
     */
    public OrderResultInfo queryOrder(QueryOrderBO bo) {
        String url = " https://api.mch.weixin.qq.com/v3/pay/partner/transactions/out-trade-no/"
                + bo.getOrderNo()
                + "?" + "sp_mchid=" + bo.getSpMchid()
                + "&" + "sub_mchid=" + bo.getSubMchid();
        HttpGet httpGet = new HttpGet(url);
        httpGet.addHeader("Accept", "application/json");
        try {
            CloseableHttpResponse response = httpClient.execute(httpGet);
            String bodyAsString = EntityUtils.toString(response.getEntity());
            if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
                throw new ServiceException(JSONObject.parseObject(bodyAsString));
            }
            return JSONObject.parseObject(bodyAsString, OrderResultInfo.class);
        } catch (IOException e) {
            throw new ServiceException(e);
        }
    }


    /**
     * @param orderNo  订单号
     * @param subMchid 子商户号
     * @description: 关闭订单
     */
    public void closeOrder(String orderNo, String subMchid) {
        String url = "https://api.mch.weixin.qq.com/v3/pay/partner/transactions/out-trade-no/" + orderNo + "/close";
        //构造参数
        JSONObject requestParams = new JSONObject();
        requestParams.put("sp_mchid", wxconfig.getMerchantId());
        requestParams.put("sub_mchid", subMchid);
        //构造请求
        HttpPost httpPost = new HttpPost(url);
        httpPost.addHeader("Accept", "application/json");
        httpPost.addHeader("Content-type", "application/json; charset=utf-8");
        httpPost.setEntity(new StringEntity(requestParams.toJSONString(), StandardCharsets.UTF_8));
        try {
            CloseableHttpResponse response = httpClient.execute(httpPost);
            if (response.getStatusLine().getStatusCode() != HttpStatus.SC_NO_CONTENT) {
                throw new ServiceException(response.getEntity());
            }
        } catch (IOException e) {
            throw new ServiceException(e.getMessage());
        }

    }


    /**
     * 退款
     */
    public RefundResultInfo refund(RefundBO bo) {
        //1. 构造参数
        JSONObject requestParams = new JSONObject();
        //子商户号
        requestParams.put("sub_mchid", bo.getSubMchId());
        //服务商订单号
        requestParams.put("out_trade_no", bo.getOrderNo());
        //退款单号
        requestParams.put("out_refund_no", bo.getRefundNo());
        requestParams.put("reason", bo.getReason());
        JSONObject amount = new JSONObject();
        //退款金额,单位分
        amount.put("refund", bo.getRefund());
        //原订单金额,单位分
        amount.put("total", bo.getTotal());
        amount.put("currency", "CNY");
        requestParams.put("amount", amount);

        //2. 初始化post请求
        HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/refund/domestic/refunds");
        httpPost.addHeader("Accept", "application/json");
        httpPost.addHeader("Content-type", "application/json; charset=utf-8");
        httpPost.setEntity(new StringEntity(requestParams.toJSONString(), StandardCharsets.UTF_8));
        try {
            CloseableHttpResponse httpResponse = httpClient.execute(httpPost);
            String resString = EntityUtils.toString(httpResponse.getEntity());
            if (httpResponse.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
                throw new ServiceException(JSONObject.parseObject(resString));
            }
            return JSONObject.parseObject(resString, RefundResultInfo.class);
        } catch (IOException e) {
            throw new ServiceException(e);
        }
    }


    /**
     * @param request
     * @Description 回调请求的验签、解密
     */
    public OrderResultInfo asyncCallback(HttpServletRequest request) {
        //1.获取请求头
        String body = getRequestBody(request);
        String serialNumber = request.getHeader("Wechatpay-Serial");
        String timeStamp = request.getHeader("Wechatpay-Timestamp");
        String nonce = request.getHeader("Wechatpay-Nonce");
        String signature = request.getHeader("Wechatpay-Signature");
        //2.构造请求
        NotificationRequest notificationRequest = new NotificationRequest.Builder().withSerialNumber(serialNumber)
                .withNonce(nonce)
                .withTimestamp(timeStamp)
                .withSignature(signature)
                .withBody(body)
                .build();
        NotificationHandler handler = new NotificationHandler(verifier, wxconfig.getApiV3key().getBytes(StandardCharsets.UTF_8));
        // 验签和解析请求体
        try {
            Notification notification = handler.parse(notificationRequest);
            return JSONObject.parseObject(notification.getDecryptData(), OrderResultInfo.class);
        } catch (ValidationException | ParseException e) {
            throw new ServiceException(e);
        }
    }

    /**
     * 微信上传图片/视频
     */
    public String upload(MultipartFile multipartFile, UploadEnums enums) throws IOException {
        String sha256 = DigestUtils.sha256Hex(multipartFile.getBytes());
        HttpPost httpPost = new WechatPayUploadHttpPost
                .Builder(URI.create("https://api.mch.weixin.qq.com/v3/merchant/media/" + enums.getValue()))
                .withImage(multipartFile.getOriginalFilename(), sha256,
                        new ByteArrayInputStream(multipartFile.getBytes()))
                .build();
        CloseableHttpResponse response = httpClient.execute(httpPost);
        String resString = EntityUtils.toString(response.getEntity());
        JSONObject resParams = JSONObject.parseObject(resString);
        if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
            throw new ServiceException("请求失败", resParams);
        }
        return resParams.getString("media_id");
    }


    /**
     * @param text 明文
     * @return 加密后的数据
     */
    public String encrypt(String text) {
        // 建议从Verifier中获得微信支付平台证书,或使用预先下载到本地的平台证书文件中
        X509Certificate certificate = verifier.getValidCertificate();
        try {
            return RsaCryptoUtil.encryptOAEP(text, certificate);
        } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * @param ciphertext 密文
     * @return 解密后端数据
     */
    public String decrypt(String ciphertext) {
        // 使用商户私钥解密
        try {
            return RsaCryptoUtil.decryptOAEP(ciphertext, wxconfig.privateKey);
        } catch (BadPaddingException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 获取POST请求的json参数
     */
    private String getRequestBody(HttpServletRequest request) {
        try (BufferedReader streamReader = new BufferedReader(new InputStreamReader(request.getInputStream(), StandardCharsets.UTF_8))) {
            StringBuilder responseStrBuilder = new StringBuilder();
            String inputStr;
            while ((inputStr = streamReader.readLine()) != null) {
                responseStrBuilder.append(inputStr);
            }
            return responseStrBuilder.toString();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

}

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

标签:requestParams,String,微信,服务商,api,httpPost,JSONObject,put,new
From: https://www.cnblogs.com/wzkris/p/16998665.html

相关文章

  • k8s yaml 文件中的api版本如何选择?
    在创建不同的资源时,发现yaml文件用到的api版本不一样。本文主要讲如何api版本的选择。在你的k8s集群执行命令,查看你的k8s提供的api和版本信息,输出列格式:NAME资源名称SHO......
  • react中的api获取数组排序
    [javascript-SortanarrayofobjectsinReactandrenderthem-StackOverflow](https://stackoverflow.com/questions/43572436/sort-an-array-of-objects-in-reac......
  • 企业微信自建应用如何获取用户信息
    框架类型:springboot框架开发前提:企业微信开发者文档 https://developer.work.weixin.qq.com/document/path/90556我们阅读企业微信开发文档发现,获取userid必需知道两个......
  • python之调用高德、百度api解析经纬度地址
    调用高德#高德地图根据经纬度反查地址,每天只能调用5000次defgaode_excute_single_query(coordStrings,currentkey='你自己的api-key'):#1-将coordList中的经纬......
  • MyBatis的相应API与传统和代理开发的Dao层实现
    MyBatis的相应API1、SqlSession工厂构建器SqlSessionFactoryBuilder常用API:SqlSessionFactorybuild(InputStreaminputStream)通过加载mybatis的核心文件的输入流的形式构......
  • .net core web api 路由约束
    路由约束在传入URL发生匹配时执行,URL路径标记为路由值。路径约束通常检查通过路径模板关联的路径值,并对该值是否为可接受做出对/错决定。某些路由约束使用路由值以外......
  • day01 微信小程序
    day01微信小程序1.问题什么是微信小程序?-移动互联网时代,手机。-手机软件,在手机上中安装很多软件。-腾讯和阿里(只安装自己不用别人)-腾讯:微信+N小程序......
  • #yyds干货盘点#【愚公系列】2022年12月 微信小程序-three.js绘制球体
    前言Three.js是一款运行在浏览器中的3D引擎,你可以用它创建各种三维场景,包括了摄影机、光影、材质等各种对象。一个典型的Three.js程序至少要包括渲染器(Renderer)、场......
  • ptrade 量化交易接口 API接口文档
    http://ptradeapi.com  更新了多个可转债数据接口比如:ptradeapi #可转债溢价率规模数据 ......
  • Flink:DataStreamAPI
    执行环境获取的执行环境是StreamExecutionEnvironment类的对象。在代码中创建执行环境的方法,就是调用这个类的静态方法。getExecutionEnvironment根据上下文直接得到......