支付接口——WeChat / Alipay

1.微信支付

  a.微信支付官方文档地址:https://pay.weixin.qq.com/wiki/doc/api/index.html

  b.在官网下载SDK

  c.使用IDEA打开 java_sdk_v3.0.9,修改 WXPayConfig 类,将访问修饰符都改为 public

package com.github.wxpay.sdk;

import java.io.InputStream;

public abstract class WXPayConfig {



    /**
     * 获取 App ID
     *
     * @return App ID
     */
    public abstract String getAppID();


    /**
     * 获取 Mch ID
     *
     * @return Mch ID
     */
    public abstract String getMchID();


    /**
     * 获取 API 密钥
     *
     * @return API密钥
     */
    public abstract String getKey();


    /**
     * 获取商户证书内容
     *
     * @return 商户证书内容
     */
    public abstract InputStream getCertStream();

    /**
     * HTTP(S) 连接超时时间,单位毫秒
     *
     * @return
     */
    public int getHttpConnectTimeoutMs() {
        return 6*1000;
    }

    /**
     * HTTP(S) 读数据超时时间,单位毫秒
     *
     * @return
     */
    public int getHttpReadTimeoutMs() {
        return 8*1000;
    }

    /**
     * 获取WXPayDomain, 用于多域名容灾自动切换
     * @return
     */
    public abstract IWXPayDomain getWXPayDomain();

    /**
     * 是否自动上报。
     * 若要关闭自动上报,子类中实现该函数返回 false 即可。
     *
     * @return
     */
    public boolean shouldAutoReport() {
        return true;
    }

    /**
     * 进行健康上报的线程的数量
     *
     * @return
     */
    public int getReportWorkerNum() {
        return 6;
    }


    /**
     * 健康上报缓存消息的最大数量。会有线程去独立上报
     * 粗略计算:加入一条消息200B,10000消息占用空间 2000 KB,约为2MB,可以接受
     *
     * @return
     */
    public int getReportQueueMaxSize() {
        return 10000;
    }

    /**
     * 批量上报,一次最多上报多个数据
     *
     * @return
     */
    public int getReportBatchSize() {
        return 10;
    }

}

 

 

  d.使用 maven 将项目 install 到本地仓库

 

  e.pom文件中引入导入的SDK依赖

    <dependency>
      <groupId>com.github.wxpay</groupId>
      <artifactId>wxpay-sdk</artifactId>
      <version>3.0.9</version>
    </dependency>

 

 

  f.新建 MyConfig 类继承 WXPayConfig

public class MyConfig extends WXPayConfig{
    //公众账号ID
    public static final String APP_ID = "";
    //商户号
    public static final String MCH_ID = "";
    //API密钥
    public static final String KEY = "";
    //异步支付回调地址(外网)
    public static final String NOTIFY_URL = "";
    //异步退款回调地址(外网)
    public static final String REFUND_NOTIFY_URL = "";

    //---------------------------------------

    private byte[] certData;

    public MyConfig() throws Exception {
        String certPath = "/path/to/apiclient_cert.p12";
        File file = new File(certPath);
        InputStream certStream = new FileInputStream(file);
        this.certData = new byte[(int) file.length()];
        certStream.read(this.certData);
        certStream.close();
    }

    public String getAppID() {
        return APP_ID;
    }

    public String getMchID() {
        return MCH_ID;
    }

    public String getKey() {
        return KEY;
    }

    public InputStream getCertStream() {
        ByteArrayInputStream certBis = new ByteArrayInputStream(this.certData);
        return certBis;
    }

    public IWXPayDomain getWXPayDomain() {
        // 这个方法需要这样实现, 否则无法正常初始化WXPay
        IWXPayDomain iwxPayDomain = new IWXPayDomain() {
            public void report(String domain, long elapsedTimeMillis, Exception ex) {
            }

            public DomainInfo getDomain(WXPayConfig config) {
                return new IWXPayDomain.DomainInfo(WXPayConstants.DOMAIN_API, true);
            }
        };
        return iwxPayDomain;
    }

    public int getHttpConnectTimeoutMs() {
        return 8000;
    }

    public int getHttpReadTimeoutMs() {
        return 10000;
    }

}

 

 

  g.统一下单、查询订单、退款

public class WechatUtil {

    /**
     * 统一下单(预支付)
     * @param outTradeNo 外部订单号
     * @param title 标题
     * @param totalFee 总金额
     * @param spbillCreateIp 客户端IP
     * @return
     * @throws Exception
     */
    public static Map<String, String> prepay(String outTradeNo, String title, int totalFee, String spbillCreateIp) throws Exception {
        MyConfig config = new MyConfig();
        WXPay wxpay = new WXPay(config);

        Map<String, String> data = new HashMap<String, String>();
        data.put("body", title);
        data.put("out_trade_no", outTradeNo);
        data.put("fee_type", "CNY");
        data.put("total_fee", String.valueOf(totalFee));
        data.put("spbill_create_ip", spbillCreateIp);
        data.put("notify_url", MyConfig.NOTIFY_URL);
        data.put("trade_type", "NATIVE");  // 此处指定为扫码支付
        data.put("product_id", "12"); //扫码支付时必传参数

        Map<String, String> result = new HashMap<>();
        try {
            Map<String, String> resp = wxpay.unifiedOrder(data);
            System.out.println(resp);
            if("SUCCESS".equals(resp.get("return_code"))){
                if("SUCCESS".equals(resp.get("result_code"))){
                    String prepay_id = resp.get("prepay_id");
                    String code_url = resp.get("code_url");

                    HashMap<String, String> signMap = new HashMap<String, String>();
                    signMap.put("appid", MyConfig.APP_ID);
                    signMap.put("partnerid", MyConfig.MCH_ID);
                    signMap.put("prepayid", prepay_id);
                    signMap.put("package", "Sign=WXPay");
                    signMap.put("noncestr", WXPayUtil.generateNonceStr());
                    signMap.put("timestamp", String.valueOf(System.currentTimeMillis()/1000));
                    String sign = WXPayUtil.generateSignature(signMap, MyConfig.KEY);

                    result.put("prepay_id", prepay_id);
                    result.put("code_url", code_url);
                    result.put("sign", sign);
                    result.putAll(signMap);
                    return result;

                }else{
                    System.out.println(resp.get("err_code_des"));
                }
            }else{
                System.out.println(resp.get("return_msg"));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 查询订单
     * @param outTradeNo 外部订单号
     * @throws Exception
     */
    public static void tradeQuery(String outTradeNo) throws Exception {
        MyConfig config = new MyConfig();
        WXPay wxpay = new WXPay(config);

        Map<String, String> data = new HashMap<String, String>();
        data.put("out_trade_no", outTradeNo);

        try {
            Map<String, String> resp = wxpay.orderQuery(data);
            System.out.println(resp);
            if("SUCCESS".equals(resp.get("return_code"))){
                if("SUCCESS".equals(resp.get("result_code"))){
                    //TODO 根据response中的结果继续业务逻辑处理

                }else{
                    System.out.println(resp.get("err_code_des"));
                }
            }else{
                System.out.println(resp.get("return_msg"));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     *  退款
     * @param outTradeNo 外部订单号
     * @param outRefundNo 外部退款订单号
     * @param totalFee 总金额
     * @param refundFee 退款金额
     * @throws Exception
     */
    public static void refund(String outTradeNo, String outRefundNo, int totalFee, int refundFee) throws Exception {
        MyConfig config = new MyConfig();
        WXPay wxpay = new WXPay(config);

        Map<String, String> data = new HashMap<String, String>();
        data.put("out_trade_no", outTradeNo);
        data.put("out_trade_no", outTradeNo);
        data.put("out_refund_no", outRefundNo);
        data.put("total_fee", String.valueOf(totalFee));
        data.put("refund_fee", String.valueOf(refundFee));
        data.put("notify_url", MyConfig.REFUND_NOTIFY_URL);

        try {
            Map<String, String> resp = wxpay.refund(data);
            System.out.println(resp);
            if("SUCCESS".equals(resp.get("return_code"))){
                if("SUCCESS".equals(resp.get("result_code"))){
                    //TODO 根据response中的结果继续业务逻辑处理

                }else{
                    System.out.println(resp.get("err_code_des"));
                }
            }else{
                System.out.println(resp.get("return_msg"));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

 

 

  h.支付/退款 回调

@RestController
public class WechatController {

    @RequestMapping("/payment/wechatResult")
    public String wechatResult(HttpServletRequest request){
        try{
            InputStream is = request.getInputStream();
            //将InputStream转换成String
            BufferedReader reader = new BufferedReader(new InputStreamReader(is));
            StringBuilder sb = new StringBuilder();
            String line = null;
            try {
                while ((line = reader.readLine()) != null) {
                    sb.append(line);
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            String resXml=sb.toString();
            System.out.println(resXml);

            //-------------------------------------------------------------------

            MyConfig config = new MyConfig();
            WXPay wxpay = new WXPay(config);
            Map<String, String> notifyMap = WXPayUtil.xmlToMap(resXml);         // 转换成map
            if (wxpay.isPayResultNotifySignatureValid(notifyMap)) {
                // 签名正确
                // 进行处理。
                // 注意特殊情况:订单已经退款,但收到了支付结果成功的通知,不应把商户侧订单状态从退款改成支付成功
                String return_code = notifyMap.get("return_code");//状态
                String out_trade_no = notifyMap.get("out_trade_no");//订单号

                if (out_trade_no == null) {
                    return "";
                }
                //TODO 根据notifyMap中的结果继续业务逻辑处理


                return "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml> ";
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        return "";
    }

    @RequestMapping("/payment/wechatRefundResult")
    public String wechatRefundResult(HttpServletRequest request){
        try {
            BufferedReader reader = request.getReader();
            StringBuilder sb = new StringBuilder();
            String line = null;
            try {
                while ((line = reader.readLine()) != null) {
                    sb.append(line);
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            String resXml=sb.toString();
            System.out.println(resXml);

            //---------------------------------------------------

            Map<String, String> notifyMap = WXPayUtil.xmlToMap(resXml);
            if ("SUCCESS".equals(notifyMap.get("return_code"))) {
                String req_info = notifyMap.get("req_info");

                /**
                 * 解密方式
                 * 解密步骤如下:
                 * (1)对加密串A做base64解码,得到加密串B
                 * (2)对商户key做md5,得到32位小写key* ( key设置路径:微信商户平台(pay.weixin.qq.com)-->账户设置-->API安全-->密钥设置 )
                 * (3)用key*对加密串B做AES-256-ECB解密(PKCS7Padding)
                 */
                byte[] byteArray = (new BASE64Decoder()).decodeBuffer(req_info);
                String key = DigestUtils.md5Hex(MyConfig.KEY.getBytes("UTF-8"));

                SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(), "AES");
                Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
                cipher.init(Cipher.DECRYPT_MODE, secretKey);
                String resultStr = new String(cipher.doFinal(byteArray));

                Map<String, String> aesMap = WXPayUtil.xmlToMap(resultStr);
                //TODO 根据aesMap中的结果继续业务逻辑处理


                return "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml> ";
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        return "";
    }
}

 

 

 

2.支付宝支付

  a.官方文档地址:https://docs.open.alipay.com/204/105297/

  b.在pom文件中引入SDK依赖

    <dependency>
      <groupId>com.alipay.sdk</groupId>
      <artifactId>alipay-sdk-java</artifactId>
      <version>3.7.89.ALL</version>
    </dependency>

 

 

  c.统一下单、交易查询、交易退款

public class AlipayUtil {
    //HTTPS请求地址
    public static final String URL = "https://openapi.alipay.com/gateway.do";
    //异步回调地址(外网)
    public static final String NOTIFY_URL = "";
    //应用ID
    public static final String APP_ID = "";
    //应用私钥
    public static final String APP_PRIVATE_KEY = "";
    //支付宝公钥
    public static final String ALIPAY_PUBLIC_KEY = "";
    //编码格式
    public static final String CHARSET = "utf-8";


    /**
     * 生成APP支付订单信息
     * @param outTradeNo 外部订单号
     * @param title 标题
     * @param totalAmount 总金额
     * @return
     */
    public static String getOrderStr(String outTradeNo,String title,String totalAmount){
        //实例化客户端
        AlipayClient alipayClient = new DefaultAlipayClient(URL, APP_ID, APP_PRIVATE_KEY, "json", CHARSET, ALIPAY_PUBLIC_KEY, "RSA2");
        //实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称:alipay.trade.app.pay
        AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
        //SDK已经封装掉了公共参数,这里只需要传入业务参数。以下方法为sdk的model入参方式(model和biz_content同时存在的情况下取biz_content)。
        AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
        model.setSubject(title);
        model.setOutTradeNo(outTradeNo);
        model.setTotalAmount(totalAmount);
        model.setProductCode("QUICK_MSECURITY_PAY");
        request.setBizModel(model);
        request.setNotifyUrl(NOTIFY_URL);
        try {
            //这里和普通的接口调用不同,使用的是sdkExecute
            AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request);
            if(response.isSuccess()){
                String orderStr = response.getBody();//就是orderString 可以直接给客户端请求,无需再做处理。
                System.out.println(orderStr);
                return orderStr;
            }else{
                System.out.println(response.getSubCode());
            }

        } catch (AlipayApiException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 交易查询
     * @param outTradeNo 外部订单号
     */
    public static void tradeQuery(String outTradeNo){
        AlipayClient alipayClient = new DefaultAlipayClient("https://openapi.alipay.com/gateway.do", APP_ID, APP_PRIVATE_KEY, "json", CHARSET, ALIPAY_PUBLIC_KEY, "RSA2"); //获得初始化的AlipayClient
        AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();//创建API对应的request类
        request.setBizContent("{" +
                "   \"out_trade_no\":\"" + outTradeNo + "\"," +
                "  }");//设置业务参数
        try {
            AlipayTradeQueryResponse response = alipayClient.execute(request);//通过alipayClient调用API,获得对应的response类
            if(response.isSuccess()){
                System.out.print(response.getBody());
                //TODO 根据response中的结果继续业务逻辑处理

            }else{
                System.out.println(response.getSubCode());
            }
        } catch (AlipayApiException e) {
            e.printStackTrace();
        }
    }

    /**
     * 交易退款
     * @param outTradeNo 外部订单号
     * @param refundAmount 退款金额
     */
    public static void tradeRefund(String outTradeNo, String refundAmount){
        AlipayClient alipayClient = new DefaultAlipayClient("https://openapi.alipay.com/gateway.do", APP_ID, APP_PRIVATE_KEY, "json", CHARSET, ALIPAY_PUBLIC_KEY, "RSA2"); //获得初始化的AlipayClient
        AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();//创建API对应的request类
        request.setBizContent("{" +
                "    \"out_trade_no\":\"" + outTradeNo + "\"," +
                "    \"refund_amount\":\"" + refundAmount + "\"" +
                "  }");//设置业务参数
        try {
            AlipayTradeRefundResponse response = alipayClient.execute(request);//通过alipayClient调用API,获得对应的response类
            if(response.isSuccess()){
                System.out.print(response.getBody());
                //TODO 根据response中的结果继续业务逻辑处理

            }else{
                System.out.println(response.getSubCode());
            }
        } catch (AlipayApiException e) {
            e.printStackTrace();
        }
    }

}

 

 

  d.支付回调

@RestController
public class AlipayController {

    @RequestMapping("/payment/alipayResult")
    public String alipayResult(HttpServletRequest request){
        //获取支付宝POST过来反馈信息
        Map<String,String> params = new HashMap<String,String>();
        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);
        }
        //切记alipaypublickey是支付宝的公钥,请去open.alipay.com对应应用下查看。
        //boolean AlipaySignature.rsaCheckV1(Map<String, String> params, String publicKey, String charset, String sign_type)
        try {
            boolean flag = AlipaySignature.rsaCheckV1(params, AlipayUtil.ALIPAY_PUBLIC_KEY, AlipayUtil.CHARSET,"RSA2");
            if(flag){
                //TODO 根据params中的结果继续业务逻辑处理

                return "success";
            }else{
                return "failure";
            }
        } catch (AlipayApiException e) {
            e.printStackTrace();
        }
        return "failure";
    }
    
}

 

posted @ 2019-06-17 15:30  晨M风  阅读(1333)  评论(0编辑  收藏  举报