支付宝退款java实现

//config
public class AlipayRefundConfig {

    private final String charset    = "UTF-8";                              //参数编码字符集
    private final String signType   = "MD5";                                //签名方式
    private String       service    = "refund_fastpay_by_platform_nopwd";   //接口名称
    private String       gatewayUrl = "https://mapi.alipay.com/gateway.do"; //支付宝网关
    private String       partner;                                           //合作者身份ID
    private String       notifyUrl;                                         //服务器异步通知页面路径
    private String       sellerId;                                          //收款支付宝账号,以2088开头由16位纯数字组成的字符串,一般情况下收款账号就是签约账号
    private String       signKey;                                           // MD5密钥,安全检验码,由数字和字母组成的32位字符串,查看地址

    public void setService(String service) {
        this.service = service;
    }

    public String getService() {
        return service;
    }

    public String getCharset() {
        return charset;
    }

    public String getSignType() {
        return signType;
    }

    public String getGatewayUrl() {
        return gatewayUrl;
    }

    public void setGatewayUrl(String gatewayUrl) {
        this.gatewayUrl = gatewayUrl;
    }

    public String getPartner() {
        return partner;
    }

    public void setPartner(String partner) {
        this.partner = partner;
    }

    public String getNotifyUrl() {
        return notifyUrl;
    }

    public void setNotifyUrl(String notifyUrl) {
        this.notifyUrl = notifyUrl;
    }

    public String getSellerId() {
        return sellerId;
    }

    public void setSellerId(String sellerId) {
        this.sellerId = sellerId;
    }

    public String getSignKey() {
        return signKey;
    }

    public void setSignKey(String signKey) {
        this.signKey = signKey;
    }
}

//??????
public class AlipayRefundRequest {

    private String     refundDate; // 退款请求时间 yyyy-MM-dd HH:mm:ss
    private String     batchNo;    // 退款批次号,退款日期(8位)+流水号(3~24位)
    private String     tradeNo;    // 原付款支付宝交易号
    private BigDecimal amount;     // 退款总金额
    private String     reason;     // 退款理由
    private String     notifyUrl;  //服务器异步通知页面路径,优先使用

    public String getRefundDate() {
        return refundDate;
    }

    public void setRefundDate(String refundDate) {
        this.refundDate = refundDate;
    }

    public String getBatchNo() {
        return batchNo;
    }

    public void setBatchNo(String batchNo) {
        this.batchNo = batchNo;
    }

    public String getTradeNo() {
        return tradeNo;
    }

    public void setTradeNo(String tradeNo) {
        this.tradeNo = tradeNo;
    }

    public BigDecimal getAmount() {
        return amount;
    }

    public void setAmount(BigDecimal amount) {
        this.amount = amount;
    }

    public String getReason() {
        return reason;
    }

    public void setReason(String reason) {
        this.reason = reason;
    }

    public String getNotifyUrl() {
        return notifyUrl;
    }

    public void setNotifyUrl(String notifyUrl) {
        this.notifyUrl = notifyUrl;
    }
}

//??????
public class AlipayRefundHelper {

    public static String createGateway(AlipayRefundConfig config, AlipayRefundRequest request) {

        TreeMap<String, String> parameter = new TreeMap<>();

        parameter.put("_input_charset", config.getCharset());
        parameter.put("notify_url", request.getNotifyUrl() == null ? config.getNotifyUrl() : request.getNotifyUrl());
        parameter.put("partner", config.getPartner());
        parameter.put("seller_id", config.getSellerId());
        parameter.put("service", config.getService());
        parameter.put("sign_type", config.getSignType());

        parameter.put("batch_no", request.getBatchNo());
        parameter.put("refund_date", request.getRefundDate());
        parameter.put("batch_num", "1");
        StringBuilder sb = new StringBuilder(80);
        sb.append(request.getTradeNo()).append("^");
        sb.append(request.getAmount()).append("^");
        sb.append(request.getReason());
        parameter.put("detail_data", sb.toString());

        String queryString = AlipayUtil.buildQueryString(parameter, config.getSignKey());

        return config.getGatewayUrl() + queryString;
    }

    public static AlipayRefundReturn parseReturn(String xml) {
        AlipayRefundReturn r = new AlipayRefundReturn();
        r.setIsSuccess(extractXml(xml, "is_success"));
        r.setError(extractXml(xml, "error"));
        return r;
    }

    private static String extractXml(String xml, String tag) {
        int p = xml.indexOf(tag);
        if (p < 0) return null;
        int p1 = -1;
        int p2 = -1;
        for (int i = p + tag.length(); i < xml.length(); i++) {
            char c = xml.charAt(i);
            if (c == ‘ ‘ || c == ‘\t‘) continue;
            if (c != ‘>‘) return null;
            p1 = i + 1;
            p2 = xml.indexOf(‘<‘, p1);
            break;
        }
        if (p1 < 0 || p2 < 0 || p2 < p1) return null;
        return xml.substring(p1, p2);
    }
}

/**
 */
public class AlipayUtil {

    public static boolean isSuccess(String str) {
        return str != null && "T".equalsIgnoreCase(str.trim());
    }

    public static boolean isFailed(String str) {
        return str != null && "F".equalsIgnoreCase(str.trim());
    }

    public static boolean isProcess(String str) {
        return str != null && "P".equalsIgnoreCase(str.trim());
    }

    public static boolean signSkip(String k, String v) {
        return v == null || v.length() == 0 || k == null || "sign_type".equalsIgnoreCase(k) || "sign".equalsIgnoreCase(k);
    }

    public static String buildQueryString(TreeMap<String, String> parameter, String signKey) {
        StringBuilder sign = new StringBuilder();
        StringBuilder query = new StringBuilder();
        for (Map.Entry<String, String> entry : parameter.entrySet()) {
            String k = entry.getKey();
            String v = entry.getValue();
            if (!AlipayUtil.signSkip(k, v)) {
                sign.append("&").append(k).append("=").append(v);
                try {
                    v = URLEncoder.encode(v, "UTF8");
                } catch (Exception e) {
                    // ignore
                }
                query.append("&").append(k).append("=").append(v);
            }
        }

        sign.append(signKey);
        String digest = Md5Util.sumUtf8(sign.substring(1));

        query.append("&sign=").append(digest);
        query.setCharAt(0, ‘?‘);

        return query.toString();
    }

    /**
     * @return
     */

    public enum VerifyResult {
        UNSUPPORTED,
        NOT_PASS,
        MD5_PASS,
        ALL_PASS
    };

    public static VerifyResult verifyNotifyMd5(TreeMap<String, String> params, String signKey, String partnerId) {
        if (params == null || params.isEmpty()) return VerifyResult.UNSUPPORTED;
        if (!"md5".equalsIgnoreCase(params.get("sign_type"))) return VerifyResult.UNSUPPORTED;

        StringBuilder sign = new StringBuilder(300);
        for (Map.Entry<String, String> entry : params.entrySet()) {
            String k = entry.getKey();
            if ("sign".equalsIgnoreCase(k) || "sign_type".equalsIgnoreCase(k)) {
                continue;
            }
            sign.append("&").append(k).append("=").append(entry.getValue());
        }
        sign.append(signKey);
        String signStr = sign.substring(1);
        String signMd5 = Md5Util.sumUtf8(signStr);
        String paraMd5 = params.get("sign");
        if (!signMd5.equalsIgnoreCase(paraMd5)) {
            return VerifyResult.NOT_PASS;
        }

        // chick notify_id
        String verifyUrl = "https://mapi.alipay.com/gateway.do?service=notify_verify&partner=";
        StringBuilder queryUrl = new StringBuilder(100);
        queryUrl.append(verifyUrl);
        queryUrl.append(partnerId);
        queryUrl.append("&notify_id=");
        queryUrl.append(params.get("notify_id"));

        BufferedReader in = null;
        try {
            URL url = new URL(queryUrl.toString());
            URLConnection urlConnection = url.openConnection();
            in = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));
            String inputLine = in.readLine();
            if (inputLine.equalsIgnoreCase("true")) {
                return VerifyResult.ALL_PASS;
            } else {
                return VerifyResult.MD5_PASS;
            }
        } catch (Exception e) {
            return VerifyResult.MD5_PASS;
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                    // ignore
                }
            }
        }
    }
}
/** * 生成支付宝批次号 * * @return */public String getRandomBatchNum() {    SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd");    String format = dateFormat.format(new Date());    int max = 24;    int min = 3;    Random random = new Random();    int s = random.nextInt(max) % (max - min + 1) + min;    StringBuffer buffer = new StringBuffer();    for (int i = 0; i < s; i++) {        Integer val = (int) (Math.random() * 9 + 1);        buffer.append(val.toString());    }    return format + buffer.toString();}

//测试
public static void main(String[] args) {
AlipayRefundConfig config = new AlipayRefundConfig();
config.setNotifyUrl("http://127.0.0.1:9966/alipay.html");
config.setPartner(PayConstants.PARTNER_ID);
config.setSellerId(PayConstants.PARTNER_ID);
config.setSignKey(PayConstants.SECURITY_KEY);

AlipayRefundRequest req = new AlipayRefundRequest();
req.setBatchNo(getRandomBatchNum()); //退款流水号
SimpleDateFormat dt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
req.setRefundDate(dt.format(new Date())); //退款请求时间
req.setAmount(new BigDecimal("0.01"));
req.setReason("测试");
req.setTradeNo("888888888");  //交易号
String payUrl = AlipayRefundHelper.createGateway(config, req);
System.out.println(payUrl);
String xml = PayUtil.request(payUrl);
AlipayRefundReturn r = AlipayRefundHelper.parseReturn(xml);
System.out.println(xml);
System.out.println(r.getIsSuccess());
System.out.println(r.getError());

}

  

原文地址:https://www.cnblogs.com/dreammyone/p/8575104.html

时间: 2024-10-24 22:02:49

支付宝退款java实现的相关文章

支付宝退款 (新版)

实现支付宝退款 http://www.upwqy.com/details/85.html 想要实现退款,前提是有支付的订单.所以这里也列举出来了支付 1 支付宝配置 2 支付宝支付 3 支付宝退款 1 支付宝配置 可以查看 支付宝(新版)配置 2 支付 文章待更新.. 3 退款 官方文档 https://docs.open.alipay.com/api_1/alipay.trade.refund 把配置弄好 基本是直接成功了. 需要注意的是  在文档上说的 商户的操作编号还有下面的几个参数 是可

支付宝支付java模块接入,开发

准备 1.蚂蚁金服开放平台商户注册->https://open.alipay.com/platform/home.htm 2.创建应用或者沙箱应用 3.创建应用或者沙箱应用 4.配置相关参数 回调,公钥私钥等,然后签约开发的移动端类型,签约应用,包括但不限于APP,网页支付.留存appid, 支付宝公钥,应用私钥,支付宝公钥由上传的应用私钥获取而来. 可以下载阿里提供的sdk进行集成,免去重写造轮子https://docs.open.alipay.com/54/103419/ 代码 如果使用sd

alipay.trade.refund (统一收单交易退款接口)[支付宝退款]

首页官网退款的api: https://doc.open.alipay.com/docs/api.htm?spm=a219a.7395905.0.0.UTBitT&docType=4&apiId=759 直接测试调用: public function gis() { $aop = new AopClient(); //$plukey="MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAhvyIazymefqylZ3OXf43SXEzxsb2V

小黑式烂代码之微信APP支付 + 退款(JAVA实现)

首先,你得先有微信开发平台账号密码还需要开通应用,然后还有微信服务商平台商户版账号(这些我都是给产品经理拿的) 其次我认为你先去看一看微信开发平台的文档!  https://pay.weixin.qq.com/wiki/doc/api/index.html 这里有很多种支付,我就采用APP支付来说了(会了APP支付其实H5支付都差不多的!) 进来后是这样的,随便看看'APP支付那几篇文章'讲的流程!,看完后知道大概了就可以看看'API列表了' 我们后台开发需要关注的就是这三个API了! 1 /*

java支付宝无密退款

支付宝API提供了两处退款的接口文档: ①统一收单退款接口:https://doc.open.alipay.com/doc2/apiDetail.htm?spm=a219a.7395905.0.0.0ej0mo&docType=4&apiId=759 ②及时到账有密退款接口:https://doc.open.alipay.com/doc2/detail?treeId=66&articleId=103571&docType=1 两者的区别的是:如果你是alipay.trade

nopCommerce 3.9 大波浪系列 之 可退款的支付宝插件(上)

一.简介 nop通过插件机制可以支持更多的支付扩展,我们通过编写支持退款的支付宝插件来更好的理解支付插件的扩展. 先分享下支付宝插件源码点击下载,由于时间原因,本篇只介绍使用该插件,下一篇结合插件进行代码分析.       注意:测试时将项目部署在公网,这样才能接收来自支付宝的回调通知,否则无法接收支付宝回调会导致订单状态无法处理. 二.插件使用 1.将插件DaBoLang.Nop.Plugin.Payments.AliPay项目放置在"nopCommerce_3.90_Source\Plugi

asp.net mvc 接入最新支付宝支付+退款 alipay-sdk-NET-20170615110549

第1步: https://openhome.alipay.com/developmentDocument.htm 第2步:下载sdk和demo https://docs.open.alipay.com/270/106291/ https://docs.open.alipay.com/54/103419 第3步:将SDK放到解决方案下并在解决方案下打开下载下来的SDK项目 第4步:新建项目,项目中新建一个类存放支付宝配置相关信息 登录支付宝进入开发者中心 https://openhome.alip

app微信支付宝支付后台的插件模式+回调通过spring广播处理后续业务(已亲测可用)

写在前面的话:每当我们做一个项目,基本上都会涉及到支付的业务,最常用的莫过于微信和支付宝的支付了,项目有bug,有问题,都不叫问题,可一旦钱出了问题,那就是大问题了,所以在支付业务上我们必须慎之又慎! 但是我们做开发的,并不是在一个项目中完成支付模块就万事大吉了,在下一个项目中,我们是不是又要将支付模块的代码复制粘贴一遍,然后再重改支付模块?这样的坏处是频繁修改支付模块难免出现一些我们自己都意识不到的问题,一旦暴露在一些不怀好心的又懂技术的人面前,那我们哭都不知道去找谁. 所以,我试着通过利用s

支付宝支付接口的调用(转)

支付宝支付接口的调用 2018年04月07日 17:54:51 Forward_duyu 阅读数:15167 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/zoroduyu/article/details/79825880 应公司业务要求,需要调用支付宝的支付接口进行支付的操作,于是将整个调用过程用博客形式记录下来,以供以后使用. 本次调用支付宝采用的是电脑支付,官方文档页面如下: 电脑端调用支付宝,流程很简单,在页面有一个立即支付的按钮,点击