支付宝公钥证书和微信v3支付,退款,和转账

项目有转账到个人账户的需求.所以支付宝秘钥要用公钥证书的方式,如果不用转账可以用普通的公私钥就可以了.刚开始支付宝的转账文档里面没有用到证书,是错误的,加上习惯看文档一般都直奔目的地.结果接口就是调试不好.后来问了客服,让参考接入指南,这里面的介绍和代码是正确的.参考地址:接入指南 | 网页&移动应用.过了2天回头看文档已经都改正;微信的v3支付就是个坑.感觉文档写的是真菜.我喜欢支付宝这样的直接告诉你这么做,代码怎么写.微信就喜欢让你猜.找客服问个验签的问题,客服直接甩出文档说代码你自己实现.文档能看通谁还找客服.等个客服好点的十几分钟.有次有快1小时了.最后实在验签不了放那等第二天吧.第二天一早想到自动更新证书里面可以获取证书.用这个试试.毕竟支付的时候验签就ton过了,没理由回调的时候验签不通过.一式果然成了.

支付宝

    支付宝api还比较清楚.如果用到转账.余额查询等敏感接口需要使用公钥证书的加密方式.公钥证书和普通加密方式只能同时存在一种.普通加密方式切换到公钥正式会有个过渡期.两者可共存.新版api退款不用在设置notifyurl,会使用支付时传入的地址,在支付回调的时候分别处理下就可以了.下面代码都是使用公钥正式加密方式.证书可以用提供的工具生成.不做介绍.

1.创建证书client

建议直接复制代码.手写可能会遗漏,亲身经历.证书地址为磁盘绝对路径.用/

  private AlipayClient getAlipayClient() throws AlipayApiException {
        /** 初始化 **/
        CertAlipayRequest certAlipayRequest = new CertAlipayRequest();
        /** 支付宝网关 **/
        certAlipayRequest.setServerUrl("https://openapi.alipay.com/gateway.do");
        /** 应用id,如何获取请参考:https://opensupport.alipay.com/support/helpcenter/190/201602493024 **/
        certAlipayRequest.setAppId(appId);
        /** 应用私钥, 如何获取请参考:https://opensupport.alipay
         * .com/support/helpcenter/207/201602471154?ant_source=antsupport  **/
        certAlipayRequest.setPrivateKey(privateKey);
        /** 应用公钥证书路径,下载后保存位置的绝对路径  **/
        certAlipayRequest.setCertPath(appPublicCert);
        /** 支付宝公钥证书路径,下载后保存位置的绝对路径 **/
        certAlipayRequest.setAlipayPublicCertPath(alipayPublicCert);
        /** 支付宝根证书路径,下载后保存位置的绝对路径 **/
        certAlipayRequest.setRootCertPath(aliRootCert);
        /** 设置签名类型 **/
        certAlipayRequest.setSignType("RSA2");
        /** 设置请求格式,固定值json **/
        certAlipayRequest.setFormat("json");
        /** 设置编码格式 **/
        certAlipayRequest.setCharset("UTF-8");
        return new DefaultAlipayClient(certAlipayRequest);
    }

  2.支付

虽然用了证书.支付的时候也是调用sdkExecute方法.需要参数有订单编号.订单描述,订单金额,另外一个值是固定值.

 /**
     * 支付宝支付
     * @param shopName 订单描述
     * @param unionSn 订单编号
     * @param payPrice 支付金额
     * @param notifyUrl 回调地址,退款回调地址也用这个 不要求必须https
     * @return
     */
    public AlipayTradeAppPayResponse alipay(String shopName, String unionSn, double payPrice, String notifyUrl) {
        try {
            AlipayClient alipayClient = getAlipayClient();
            AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
            request.setNotifyUrl(notifyUrl);
            JSONObject bizContent = new JSONObject();
            bizContent.put("out_trade_no", unionSn);//订单编号
            bizContent.put("total_amount", payPrice);//支付金额
//            bizContent.put("timeout_express", "30m");
            bizContent.put("subject", shopName);//订单描述
            bizContent.put("product_code", "QUICK_MSECURITY_PAY");//固定值
            request.setBizContent(bizContent.toString());
            //这里和普通的接口调用不同,使用的是sdkExecute
            AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request);
            if (response.isSuccess()) {
                System.out.println("调用成功");
            } else {
                System.out.println("调用失败");
            }
            return response;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

  3.退款

回调使用的是订单支付的回调地址.可使用返回值out_biz_no判断.如果不为空就是退款回调.为空就是支付回调.

/**
 * 退款通知走下单时候传的notifyurl,商城定单需要传入小订单号
 *
 * @param orderId       订单号
 * @param transactionId 支付宝流水号
 * @param money         退还金额
 * @param reason        退款原因
 * @return
 * @throws AlipayApiException
 */
public AlipayTradeRefundResponse refund(String orderId, String transactionId, float money, String reason) throws AlipayApiException {
    AlipayClient alipayClient = getAlipayClient();
    AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
    JSONObject bizContent = new JSONObject();
    bizContent.put("trade_no", transactionId);//支付宝交易号。和商户订单号 out_trade_no 不能同时为空。
    bizContent.put("refund_amount", money);
    bizContent.put("refund_reason", reason);
    bizContent.put("out_request_no", orderId);//支付大单,退单针对小单,该参数必传.如果退款失败.需要使用这个参数重新发起退款
    // 返回参数选项,按需传入
    JSONArray queryOptions = new JSONArray();
    queryOptions.add("refund_detail_item_list");
    bizContent.put("query_options", queryOptions);
    request.setBizContent(bizContent.toString());
    return alipayClient.certificateExecute(request);
}

4.回调 

 可使用返回值out_biz_no判断.如果不为空就是退款回调.为空就是支付回调.

回调参数中out_trade_no是创建订单或者退款时传入的自己系统内的订单id,如果是退款,out_biz_no

返回值不为空.退款的时候如果是部分退款成功trade_status返回:TRADE_SUCCESS,全部退款返:TRADE_CLOSED ,项目里面分大小单.大单对应对个小单.大单支付小单退款.收到回调后根据自己业务处理,最终要输出success字符,支付宝收到后不在回调.

   @ApiOperation("项目阿里支付,退款回调")
    @ResponseBody
    String jectpayali(HttpServletRequest request) {
        HashMap<String, String> params = new HashMap<>();
        //获取支付宝POST过来反馈信息
        Map requestParams = request.getParameterMap();
        for (Iterator iter = requestParams.keySet().iterator(); iter.hasNext(); ) {
            String name = (String) iter.next();
            String[] values = (String[]) 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("ISO-8859-1"), "utf-8");
            params.put(name, valueStr);
        }
        //切记alipayPublicCertPath是应用公钥证书路径,请去open.alipay.com对应应用下载。
        //boolean AlipaySignature.rsaCertCheckV1(Map<String, String> params, String publicKeyCertPath, String
        // charset,String signType)
        String sign_type = params.get("sign_type");
        try {
            if (AlipaySignature.rsaCertCheckV1(params, ALiPay.alipayPublicCert, "utf-8", sign_type)) {
                String out_trade_no = params.get("out_trade_no");
                JzlProjectOrder projectOrder = mProjectOrderService.findBy("unionSn", out_trade_no);
                String out_biz_no = params.get("out_biz_no");//不为空就是退款,为空就是支付回调
                if (projectOrder != null) {
                    if (StringUtils.isEmpty(out_biz_no)) {//,退款流水号为空,走支付回调
                        if (projectOrder.getStatus() != null && projectOrder.getStatus() == 1) {
                            return "success";
                            //支付成功.并且金额相等
                        } else if (params.get("trade_status").equals("TRADE_SUCCESS") && new BigDecimal(params.get(
                                "buyer_pay_amount")).compareTo(projectOrder.getPrice()) == 0) {
                            projectOrder.setStatus(1);
                            projectOrder.setTransactionId(params.get("trade_no"));
                            projectOrder.setTimeEnd(params.get("gmt_payment"));
                            projectOrder.setPayType(1);
                            mProjectOrderService.update(projectOrder);
                            return "success";
                        }
                    } else {//走 退款回调
                        if (projectOrder.getStatus() != null && projectOrder.getStatus() == 3) {//已退款
                            return "success";
                            //支付成功.并且金额相等,部分退款状态为TRADE_SUCCESS,全额退款为TRADE_CLOSED
                        } else if (params.get("trade_status").equals("TRADE_CLOSED") && new BigDecimal(params.get(
                                "refund_fee")).compareTo(projectOrder.getPrice()) == 0) {
                            projectOrder.setStatus(3);
                            projectOrder.setTransactionId(params.get("trade_no"));//支付宝交易号
                            projectOrder.setTimeEnd(params.get("gmt_refund"));
                            mProjectOrderService.update(projectOrder);
                            return "success";
                        }
                    }
                }
            }
        } catch (AlipayApiException e) {
            e.printStackTrace();
        }
        return "error";
    }

5.转账

不用异步回调.可直接拿到结果.根据api返回值.需要判断网关是否调用成功.还有转账的状态是否为SUCCESS

    /**
     * 转账到用户支付宝账户余额
     *
     * @param orderId    订单di.
     * @param money      金额 单位元 最少0.1,2位小数
     * @param aliAccount 收款账号.不是用户id,登录支付宝的账号
     * @param realName   真实姓名
     * @return
     */
    public AlipayFundTransUniTransferResponse transfer(String orderId, String money, String aliAccount,
                                                       String realName) {
        try {
            AlipayClient alipayClient = getAlipayClient();
            /** 实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称:alipay.fund.trans.uni.transfer(单笔转账接口) **/
            AlipayFundTransUniTransferRequest request = new AlipayFundTransUniTransferRequest();
            /** 设置业务参数,具体接口参数传值以文档说明为准:https://opendocs.alipay.com/apis/api_28/alipay.fund.trans.uni.transfer/ **/
            AlipayFundTransUniTransferModel model = new AlipayFundTransUniTransferModel();
            /** 商户端的唯一订单号,对于同一笔转账请求,商户需保证该订单号唯一 **/
            model.setOutBizNo(orderId);
            /** 转账金额,TRANS_ACCOUNT_NO_PWD产品取值最低0.1  **/
            model.setTransAmount(money);
            /** 产品码,单笔无密转账到支付宝账户固定为:TRANS_ACCOUNT_NO_PWD **/
            model.setProductCode("TRANS_ACCOUNT_NO_PWD");
            /** 场景码,单笔无密转账到支付宝账户固定为:DIRECT_TRANSFER  **/
            model.setBizScene("DIRECT_TRANSFER");
            /** 转账业务的标题,用于在支付宝用户的账单里显示 **/
            model.setOrderTitle("佣金提现");
            Participant participant = new Participant();
            /** 参与方的唯一标识,收款支付宝账号或者支付宝吧账号唯一会员ID **/
            participant.setIdentity(aliAccount);
            /** 参与方的标识类型:ALIPAY_USER_ID 支付宝的会员ID  **/
            participant.setIdentityType("ALIPAY_LOGON_ID");
            /** 参与方真实姓名,如果非空,将校验收款支付宝账号姓名一致性。当identity_type=ALIPAY_LOGON_ID时,本字段必填 **/
            participant.setName(realName);
            model.setPayeeInfo(participant);
            /** 业务备注  **/
            model.setRemark("单笔转账");
            request.setBizModel(model);
            AlipayFundTransUniTransferResponse response = alipayClient.certificateExecute(request);
            System.out.println(response.getBody());
            return response;
        } catch (AlipayApiException e) {
            e.printStackTrace();
        }
        /** 获取接口调用结果,如果调用失败,可根据返回错误信息到该文档寻找排查方案:https://opensupport.alipay.com/support/helpcenter/107 **/
        return null;
    }

微信

很不喜欢微信的文档.要参考好几个.sdk文档:GitHub - wechatpay-apiv3/wechatpay-apache-httpclient: 微信支付 APIv3 Apache HttpClient装饰器(decorator)

.api说明文档:微信支付-开发者文档.

还有个apiv3文档:简介 - WechatPay-API-v3 这个比较重要不好找,里面有签名,验签,加密解密方法

微信也是一样,需要证书.有证书生成工具.生成后自动下载.下载后证书双击安装.里面有个apiclient_key.pem就是证书的私钥.

先说下要用到的参数.

1:appid;

2:商户id;

3apikey,跟老版本一样支付秘钥,

4:v3apikey,v3支付的标志行参数在第三个参数的下面设置,

5:证书序列号,在第三个参数的上面设置证书.可查看证书序列号.

微信的证书不支持网站下载,只能生成的时候下载,保存不好就只能作废了.

1.自动更新证书的client,大概每1小时更新1次,不用管证书,它会自己下载

privateKeyPath:私钥绝对路径,目录之间用/ 

 private HttpClient getHttpClient() throws UnsupportedEncodingException {
        try {
            PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(new FileInputStream(privateKeyPath));
            // 使用定时更新的签名验证器,不需要传入证书
            PrivateKeySigner privateKeySigner = new PrivateKeySigner(mPayConfig.getMchCertSN(), merchantPrivateKey);
            WechatPay2Credentials wechatPay2Credentials = new WechatPay2Credentials(mPayConfig.getMchID(),
                    privateKeySigner);
            ScheduledUpdateCertificatesVerifier verifier =
                    new ScheduledUpdateCertificatesVerifier(wechatPay2Credentials,
                            mPayConfig.getV3Key().getBytes(StandardCharsets.UTF_8));
            WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
                    .withMerchant(mPayConfig.getMchID(), mPayConfig.getMchCertSN(), merchantPrivateKey)
                    .withValidator(new WechatPay2Validator(verifier));
// ... 接下来,你仍然可以通过builder设置各种参数,来配置你的HttpClient
// 通过WechatPayHttpClientBuilder构造的HttpClient,会自动的处理签名和验签,并进行证书自动更新
            return builder.build();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }

添加请求的header参数.v3的好处就是已json传送数据.更易于封装使用. 

    private HttpPost getHttpPost(String url, String params) {
        HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com" + url);
        RequestConfig requestConfig =
                RequestConfig.custom().setSocketTimeout(mPayConfig.getHttpReadTimeoutMs()).setConnectTimeout(mPayConfig.getHttpConnectTimeoutMs()).build();
        httpPost.setConfig(requestConfig);
        StringEntity postEntity = new StringEntity(params, "UTF-8");
        httpPost.addHeader("Content-Type", "application/json; charset=utf-8");
        httpPost.addHeader("Accept", "application/json");
//        httpPost.addHeader("User-Agent", WXPayConstants.USER_AGENT + " " + mPayConfig.getMchID());
        httpPost.setEntity(postEntity);
        System.out.println(httpPost.getURI().getRawPath());
        return httpPost;
    }

为了方便调用.参考支付宝.吧请求参数和返回值都添加的实体bean.属性值就是文档中说明的请求参数key,响应参数key.此处不提供bean代码

 用到的方法

2.签名:

  public String sign(byte[] message) {
        try {
            PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(new FileInputStream(privateKeyPath));
            Signature sign = Signature.getInstance("SHA256withRSA");
            sign.initSign(merchantPrivateKey);
            sign.update(message);
            return Base64.getEncoder().encodeToString(sign.sign());
        } catch (NoSuchAlgorithmException | SignatureException | FileNotFoundException | InvalidKeyException e) {
            e.printStackTrace();
        }
        return null;
    }

3.验签:

这个比较坑.如果用ScheduledUpdateCertificatesVerifier对象直接验签的话会不通过.不知道是不是商户平台证书设置的配置.跟踪代码发现去证书的证书id不一样.导致验签不通过.创建Signature对象传入获取到的证书可以验证通过,ScheduledUpdateCertificatesVerifier对象的验证方法就是Signature实现.

  /**
     * 验证签名 带证书验证
     *
     * @param message
     * @param sign
     * @return
     * @throws UnsupportedEncodingException
     */
    public boolean verify(String message, String sign) {
        try {
            PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(new FileInputStream(privateKeyPath));
            // 使用定时更新的签名验证器,不需要传入证书
            PrivateKeySigner privateKeySigner = new PrivateKeySigner(mPayConfig.getMchCertSN(), merchantPrivateKey);
            WechatPay2Credentials wechatPay2Credentials = new WechatPay2Credentials(mPayConfig.getMchID(),
                    privateKeySigner);
            ScheduledUpdateCertificatesVerifier verifier =
                    new ScheduledUpdateCertificatesVerifier(wechatPay2Credentials,
                            mPayConfig.getV3Key().getBytes(StandardCharsets.UTF_8));
            X509Certificate certificate = verifier.getLatestCertificate();
            Signature signature = Signature.getInstance("SHA256withRSA");
            signature.initVerify(certificate);
            signature.update(message.getBytes(StandardCharsets.UTF_8));
            return signature.verify(Base64.getDecoder().decode(sign));

        } catch (FileNotFoundException | NoSuchAlgorithmException | InvalidKeyException | SignatureException e) {
            e.printStackTrace();
        }
        return false;
    }

 4.回调密文解密.敏感信息加密解密

package com.jianlianlian.api.utils.wxpaysdk.v3;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.security.*;
import java.security.cert.X509Certificate;
import java.util.Base64;

public class WxAesUtil {
    static final int KEY_LENGTH_BYTE = 32;
    static final int TAG_LENGTH_BIT = 128;
    private final byte[] aesKey;

    /**
     * @param key v3秘钥
     */
    public WxAesUtil(byte[] key) {
        if (key.length != KEY_LENGTH_BYTE) {
            throw new IllegalArgumentException("无效的ApiV3Key,长度必须为32个字节");
        }
        this.aesKey = key;
    }

    /**
     * 回调报文解密
     *
     * @param associatedData
     * @param nonce
     * @param ciphertext
     * @return
     * @throws GeneralSecurityException
     * @throws IOException
     */
    public String decryptToString(byte[] associatedData, byte[] nonce, String ciphertext)
            throws GeneralSecurityException, IOException {
        try {
            Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
            SecretKeySpec key = new SecretKeySpec(aesKey, "AES");
            GCMParameterSpec spec = new GCMParameterSpec(TAG_LENGTH_BIT, nonce);
            cipher.init(Cipher.DECRYPT_MODE, key, spec);
            cipher.updateAAD(associatedData);
            return new String(cipher.doFinal(Base64.getDecoder().decode(ciphertext)), "utf-8");
        } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
            throw new IllegalStateException(e);
        } catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
            throw new IllegalArgumentException(e);
        }
    }

    /**
     * 敏感信息加密
     *
     * @param message
     * @param certificate
     * @return
     * @throws IllegalBlockSizeException
     * @throws IOException
     */
    public static String rsaEncryptOAEP(String message, X509Certificate certificate)
            throws IllegalBlockSizeException, IOException {
        try {
            Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding");
            cipher.init(Cipher.ENCRYPT_MODE, certificate.getPublicKey());

            byte[] data = message.getBytes("utf-8");
            byte[] cipherdata = cipher.doFinal(data);
            return Base64.getEncoder().encodeToString(cipherdata);
        } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
            throw new RuntimeException("当前Java环境不支持RSA v1.5/OAEP", e);
        } catch (InvalidKeyException e) {
            throw new IllegalArgumentException("无效的证书", e);
        } catch (IllegalBlockSizeException | BadPaddingException e) {
            throw new IllegalBlockSizeException("加密原串的长度不能超过214字节");
        }
    }

    /**
     * 敏感信息解密
     *
     * @param ciphertext
     * @param privateKey
     * @return
     * @throws BadPaddingException
     * @throws IOException
     */
    public static String rsaDecryptOAEP(String ciphertext, PrivateKey privateKey)
            throws BadPaddingException, IOException {
        try {
            Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding");
            cipher.init(Cipher.DECRYPT_MODE, privateKey);

            byte[] data = Base64.getDecoder().decode(ciphertext);
            return new String(cipher.doFinal(data), "utf-8");
        } catch (NoSuchPaddingException | NoSuchAlgorithmException e) {
            throw new RuntimeException("当前Java环境不支持RSA v1.5/OAEP", e);
        } catch (InvalidKeyException e) {
            throw new IllegalArgumentException("无效的私钥", e);
        } catch (BadPaddingException | IllegalBlockSizeException e) {
            throw new BadPaddingException("解密失败");
        }
    }
}

  5.支付

需要注意微信支付单位是分.如果是doubel类型最好转string用BigDecimal拿值.

 /**
     * @param desc         订单描述
     * @param Out_trade_no 订单编号
     * @param payAmount    支付进度 单位元
     * @param notifyUrl    通知url 可为null
     * @return
     */
    public WxAppPayParams pay(String desc, String Out_trade_no, double payAmount, String notifyUrl) {
        try {
            WxPayParams params = new WxPayParams();
            params.setOut_trade_no(Out_trade_no);
            params.setDescription(desc);
            if (!StringUtils.isEmpty(notifyUrl)) {
                params.setNotify_url(notifyUrl);
            }
            WxAmount amount = new WxAmount();
            amount.setTotal(new BigDecimal(String.valueOf(payAmount)).multiply(new BigDecimal(100)).intValue());
            params.setAmount(amount);
            params.setAppid(mPayConfig.getAppID());
            params.setMchid(mPayConfig.getMchID());
            String payParams = JSON.toJSONString(params);
            System.out.println(payParams);
            HttpResponse httpResponse =
                    getHttpClient().execute(getHttpPost(WXPayConstants.V3_PLACE_ORDER, payParams));
            String text = EntityUtils.toString(httpResponse.getEntity());
            System.out.println(text);
            WXPayResult wxPayResult = JSON.parseObject(text, WXPayResult.class);
            if (wxPayResult != null) {
                WxAppPayParams wxAppPayParams = new WxAppPayParams();
                wxAppPayParams.setAppid(mPayConfig.getAppID());
                wxAppPayParams.setPartnerid(mPayConfig.getMchID());
                wxAppPayParams.setPrepayid(wxPayResult.getPrepay_id());
                StringBuffer sb = new StringBuffer();
                sb.append(wxAppPayParams.getAppid()).append("\n").append(wxAppPayParams.getTimestamp()).append("\n").append(wxAppPayParams.getNoncestr()).append("\n").append(wxPayResult.getPrepay_id()).append("\n");
                wxAppPayParams.setSign(sign(sb.toString().getBytes("UTF-8")));
                return wxAppPayParams;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

6.回调 

 支付和退款回调可分开设置,分别传入notifyurl,也可以一样自己处理.先验签以保证接口是微信调用.项目里面分大小单.大单对应对个小单.大单支付小单退款,unionSn(大单号),orderSn(小单号)

 @ApiOperation("项目微信支付回调")
    @ResponseBody
    String jectpay(HttpServletRequest request) {
        String resultSign = request.getHeader("Wechatpay-Signature");  //回调验签
        String resultNonce = request.getHeader("Wechatpay-Nonce");//回调随机串
        String resultTimestamp = request.getHeader("Wechatpay-Timestamp");//回调时间搓
        try {
            ServletInputStream inputStream = request.getInputStream();
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
            StringBuffer resultBuffer = new StringBuffer();
            String line;
            while ((line = bufferedReader.readLine()) != null) {
                resultBuffer.append(line);
            }
            inputStream.close();
            bufferedReader.close();
            String resultBody = resultBuffer.toString();
            String originStr = Stream.of(resultTimestamp, resultNonce, resultBody).collect(Collectors.joining("\n",
                    "", "\n"));
            if (mWxV3Pay.verify(originStr, resultSign)) {
                WxNotify notify = JSON.parseObject(resultBody, WxNotify.class);
                WxAesUtil aesUtil = new WxAesUtil(mWxV3Pay.getPayConfig().getV3Key().getBytes(StandardCharsets.UTF_8));
                String decrypt = aesUtil.decryptToString(notify.getResource().getAssociated_data().getBytes(),
                        notify.getResource().getNonce().getBytes(), notify.getResource().getCiphertext());
                WXNotifyDecode notifyDecode = JSON.parseObject(decrypt, WXNotifyDecode.class);
                String out_trade_no = notifyDecode.getOut_trade_no();
                JzlProjectOrder projectOrder = mProjectOrderService.findBy("unionSn", out_trade_no);
                if (projectOrder != null) {
                    if (projectOrder.getStatus() != null && projectOrder.getStatus() == 1) {
                        JSONObject object = new JSONObject();
                        object.put("code", "SUCCESS");
                        object.put("message", "成功");
                        return object.toJSONString();
                        //支付成功.并且金额相等
                    } else if (notifyDecode.getTrade_state().equals("SUCCESS") && new BigDecimal(notifyDecode.getAmount().getTotal()).compareTo(projectOrder.getPrice().multiply(new BigDecimal(100))) == 0) {
                        projectOrder.setStatus(1);
                        projectOrder.setTransactionId(notifyDecode.getTransaction_id());
                        String success_time =
                                new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new SimpleDateFormat("yyyy-MM" +
                                        "-ddTHH:mm:ss").parse(notifyDecode.getSuccess_time()));
                        projectOrder.setTimeEnd(success_time);
                        mProjectOrderService.update(projectOrder);
                        JSONObject object = new JSONObject();
                        object.put("code", "SUCCESS");
                        object.put("message", "成功");
                        return object.toJSONString();
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
        JSONObject object = new JSONObject();
        object.put("code", "error");
        object.put("message", "失败");
        return object.toJSONString();
    }

  

退款

需要传入支付流水号.退款单号.订单总金额和退款金额.还有一个币种默认CNY.参考api参入必传参数,退款回调需要验证Refund_status是否为SUCCESS,处理完之后返回一个json传:

{
    "code": "SUCCESS",
    "message": "成功"
}

  /**
     * 退款,商城订单需要传入小订单号
     *
     * @param refundParams
     * @return
     */
    public WXRefundResult refund(WXRefundParams refundParams) {
        try {
            HttpPost httpPost = getHttpPost(WXPayConstants.V3_REFUND, JSON.toJSONString(refundParams));
// 后面跟使用Apache HttpClient一样
            HttpResponse httpResponse = getHttpClient().execute(httpPost);
            String text = EntityUtils.toString(httpResponse.getEntity());
            System.out.println(text);
            return JSON.parseObject(text, WXRefundResult.class);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

  转账

微信转账要求开通微信的90天之后的前30天每天都有正常的交易流水.暂时不满足条件.

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值