银联网关支付,退款java实现详解编程语言

历时两周,终于把银联支付退款搞定了。由于没人指导,走了不少弯路,博主在此贴出相关代码,希望能帮到像我一样没人指导的小伙伴。

银联支付

1:支付,退款流程。

2:支付的相关准备

  1. 去官网下载sdk,官网相关地址会在本文结尾出提供

  2. 下载官网的测试配置文件acp_sdk.properties,测试相关证书acp_test_enc.cer,acp_test_sign.pfx,修改acp_sdk.properties后放在classpath下,如果要用生产环境,相关证书文件有四个,配置文件也有对应的生产环境证书。

  3. 如果支付成功后是跳转页面,则需要准备相关页面,如果是java代码拼接字符串,需要准备相关的java类,用来动态生产html页面。

3:支付,退款相关代码

1:代码结构

2:AutoLoadServlet

import com.kemile.common.pay.chinapay.sdk.SDKConfig; 
 
import javax.servlet.ServletConfig; 
import javax.servlet.ServletException; 
import javax.servlet.http.HttpServlet; 
 
 
/** 
 * 功能:从应用的classpath下加载acp_sdk.properties属性文件并将该属性文件中的键值对赋值到SDKConfig类中 <br> 
 *  
 */ public class AutoLoadServlet extends HttpServlet { 
 @Override public void init(ServletConfig config) throws ServletException { 
 
        SDKConfig.getConfig().loadPropertiesFromSrc(); 
 super.init(); 
    } 
}

3:CharsetEncodingFilter

import com.kemile.common.pay.chinapay.config.ChinapayConfig; 
 
import java.io.IOException; 
 
import javax.servlet.Filter; 
import javax.servlet.FilterChain; 
import javax.servlet.FilterConfig; 
import javax.servlet.ServletException; 
import javax.servlet.ServletRequest; 
import javax.servlet.ServletResponse; 
 
public class CharsetEncodingFilter implements Filter { 
 @Override public void destroy() { 
    } 
 @Override public void doFilter(ServletRequest request, ServletResponse response, 
            FilterChain chain) throws IOException, ServletException { 
 
        request.setCharacterEncoding(ChinapayConfig.encoding_UTF8); 
        response.setContentType("text/html; charset="+ ChinapayConfig.encoding_UTF8); 
        chain.doFilter(request, response); 
    } 
 @Override public void init(FilterConfig filterConfig) throws ServletException { 
    } 
}

4:ChinapayConfig

import java.text.SimpleDateFormat; 
import java.util.Date; 
 
public class ChinapayConfig { 
 //商家号 //public static String MERID = "777290058137116";//填自己网站相关的商家号 //前台请求地址 public static String FRONTURL = "fontrev"; //后台请求地址 public static String BACKURL = "backrev"; //默认配置的是UTF-8 public static String encoding_UTF8 = "UTF-8"; 
 //  public static String encoding_GBK = "GBK"; 
 //全渠道固定值 public static String version = "5.0.0"; 
 // 商户发送交易时间 格式:YYYYMMDDhhmmss public static String getCurrentTime() { return new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()); 
    } 
 // AN8..40 商户订单号,不能含"-"或"_" public static String getOrderId() { return new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()); 
    } 
 /** 
     招商银行借记卡:6226090000000048 
     手机号:18100000000 
     密码:111101 
     短信验证码:123456(手机)/111111(PC)(先点获取验证码之后再输入) 
     证件类型:01 
     证件号:510265790128303 
     姓名:张三 
     */ }

5:CYChinaPayController

@Controller 
@RequestMapping(value = "/CYChinapay") 
public class CYChinaPayController { 
    Logger logger = Logger.getLogger(AliPayController.class); 
 
    @Autowired private IMobileCyDishorderService mobileCyDishorderService;//注入service 
    @Autowired private IMobileServiceStyleService mobileServiceStyleService; 
 //支付 
    @RequestMapping("/{order_nbr}") public String pay(@PathVariable String order_nbr, HttpServletRequest request, HttpServletResponse response) { 
 String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort(); 
 
        CyDishorderEntity order = (CyDishorderEntity) mobileCyDishorderService.findOne("orderNum", order_nbr); Map<String, String> requestData = new HashMap<String, String>(); 
        requestData.put("version", ChinapayConfig.version);              //版本号,全渠道默认值 
        requestData.put("encoding", ChinapayConfig.encoding_UTF8);              //字符集编码,可以使用UTF-8,GBK两种方式 
        requestData.put("signMethod", "01");                          //签名方法,只支持 01:RSA方式证书加密 
        requestData.put("txnType", "01");                          //交易类型 ,01:消费 
        requestData.put("txnSubType", "01");                          //交易子类型, 01:自助消费 
        requestData.put("bizType", "000201");                      //业务类型,B2C网关支付,手机wap支付 
        requestData.put("channelType", "08");                      //渠道类型,这个字段区分B2C网关支付和手机wap支付;07:PC,平板  08:手机 /***商户接入参数***/ 
        requestData.put("merId", ChinapayConfig.MERID);                  //商户号码,请改成自己申请的正式商户号或者open上注册得来的777测试商户号 
        requestData.put("accessType", "0");                          //接入类型,0:直连商户 
        requestData.put("orderId", order_nbr);             //商户订单号,8-40位数字字母,不能含“-”或“_”,可以自行定制规则 
        requestData.put("txnTime", ChinapayConfig.getCurrentTime());        //订单发送时间,取系统时间,格式为YYYYMMDDhhmmss,必须取当前时间,否则会报txnTime无效 
        requestData.put("currencyCode", "156");                      //交易币种(境内商户一般是156 人民币) 
        BigDecimal totPrice = null; //==========价格计算========= 
        totPrice = new BigDecimal(order.getPayMoney());//查询价格 
        requestData.put("txnAmt", String.valueOf((totPrice.multiply(new BigDecimal(100))).longValue())); //交易金额,单位分,不要带小数点 
        try { //=============================== //:TODO: //============================= 
            requestData.put("frontUrl", basePath + "/CYChinapay/" + ChinapayConfig.FRONTURL); //前台请求地址 
            requestData.put("backUrl", basePath + "/CYChinapay/" + ChinapayConfig.BACKURL);  //后台请求地址 
            logger.info("回调地址"+basePath + "/CYChinapay/" + ChinapayConfig.FRONTURL); 
            logger.info("回调地址"+basePath + "/CYChinapay/" + ChinapayConfig.BACKURL); Map<String, String> submitFromData = AcpService.sign(requestData, ChinapayConfig.encoding_UTF8); //签名 String requestFrontUrl = SDKConfig.getConfig().getFrontRequestUrl();  //获取请求银联的前台地址:对应属性文件acp_sdk.properties文件中的acpsdk.frontTransUrl String html = AcpService.createAutoFormHtml(requestFrontUrl, submitFromData, ChinapayConfig.encoding_UTF8);   //生成自动跳转的Html表单 //将生成的html写到浏览器中完成自动跳转打开银联支付页面;这里调用signData之后,将html写到浏览器跳转到银联页面之前均不能对html中的表单项的名称和值进行修改,如果修改会导致验签不通过 
            response.getWriter().write(html); 
        } catch (Exception e) { 
            e.printStackTrace(); 
        } return null; 
    } 
 private static Map<String, String> valideData(HttpServletRequest req, HttpServletResponse resp) throws IOException { String encoding = req.getParameter(SDKConstants.param_encoding); // 获取银联通知服务器发送的后台通知参数 Map<String, String> reqParam = getAllRequestParam(req); Map<String, String> valideData = null; if (null != reqParam && !reqParam.isEmpty()) { 
            Iterator<Map.Entry<String, String>> it = reqParam.entrySet().iterator(); 
            valideData = new HashMap<String, String>(reqParam.size()); while (it.hasNext()) { Map.Entry<String, String> e = it.next(); String key = (String) e.getKey(); String value = (String) e.getValue(); 
                value = new String(value.getBytes(encoding), encoding); 
                valideData.put(key, value); 
            } 
        } return valideData; 
    } 
 //后台请求地址 
    @RequestMapping(value = "/backrev") public void BackRcv(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //Map<String,String> map=getAllRequestParam(request); 
        logger.info("[进入银联支付回调方法]"); String encoding = req.getParameter(SDKConstants.param_encoding); // 获取银联通知服务器发送的后台通知参数 Map<String, String> reqParam = getAllRequestParam(req); //   LogUtil.printRequestLog(reqParam); Map<String, String> valideData = valideData(req, resp); 
        PrintWriter out = null; 
        out = resp.getWriter(); //重要!验证签名前不要修改reqParam中的键值对的内容,否则会验签不过 if (!AcpService.validate(valideData, encoding)) { 
            logger.info("验证签名结果[失败]."); 
            out.write(CyReturnPayEndHtml.failedHtml(valideData.get("orderId"), "验证签名失败", Const.validateFaliUrl)); 
        } else { 
            logger.info("验证签名结果[成功]."); //【注:为了安全验签成功才应该写商户的成功处理逻辑】交易成功,更新商户订单状态 String respCode = valideData.get("respCode"); if ("00".equals(respCode)) {//银联返回00代表支付成功 
               //:TODO:该方法在用户支付成功后银联自动异步回调。此处可以写订单支付成功后的业务逻辑 if (订单支付成功后) { 
                    resp.getWriter().print("ok");//这里一定要写响应。否则银联会认为商户后台没有收到回调信息。 
                } 
            } 
        } 
        LogUtil.writeLog("BackRcvResponse接收后台通知结束"); 
    } 
 //前台请求地址 
    @RequestMapping("/fontrev") public void fontrev(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 
        response.setCharacterEncoding("utf-8"); 
        response.setContentType("text/html; charset=utf-8");//设置编码 
        PrintWriter out = null; 
        out = response.getWriter();//获取响应输出流 
        logger.info("FrontRcvResponse前台接收报文返回开始"); String encoding = request.getParameter(SDKConstants.param_encoding); 
        logger.info("返回报文中encoding=[" + encoding + "]"); Map<String, String> respParam = getAllRequestParam(request);//获取银联回调的参数 // 打印请求报文 
        LogUtil.printRequestLog(respParam); 
 Map<String, String> valideData = valideData(request, response);//验证签名 
 if (!AcpService.validate(valideData, encoding)) {//验证签名失败 
            logger.info("验证签名结果[失败]."); 
//            return "redirect:/pay-failed.html"; 
            out.write(CyReturnPayEndHtml.failedHtml(valideData.get("orderId"), "验证签名失败", Const.validateFaliUrl)); 
        } else { String respCode = valideData.get("respCode");//验证成功后获取银联响应码 if ("00".equals(respCode)) {//响应码为00表示支付成功。 
                logger.info("验证签名结果[成功]"); //===================== 
               //TODO:这个方法在用户支付成功后点击返回商户时,银联回调,这里写回调成功后的一些业务逻辑。 
            } else { 
                out.write(CyReturnPayEndHtml.failedHtml(valideData.get("orderId"), "银联支付失败", Const.validateFaliUrl)); 
            } 
        } 
        out.flush(); 
        out.close(); 
    } 
 /** 
     * 更新订单数据(状态)此方法根据自己的业务需求编写,最后要返回验证签名后的订单编号。 
     * 
     * @param valideData 
     * @return 
     */ private String updateOrder(Map<String, String> valideData) { 
 return valideData.get("orderId");//返回验证签名成功后的订单编号 
    } 
 //退款 
    @ResponseBody 
    @RequestMapping("/Refund/{order_nbr}") public String doPost(@PathVariable String order_nbr, HttpServletRequest req, HttpServletResponse resp) 
            throws ServletException, IOException { String basePath = req.getScheme() + "://" + req.getServerName() + ":" + req.getServerPort(); 
        CyDishorderEntity order = (CyDishorderEntity) mobileCyDishorderService.findOne("orderNum", order_nbr); 
 Map<String, String> data = new HashMap<String, String>(); 
 /***银联全渠道系统,产品参数 ,除了encoding自行选择外其他不需修改***/ data.put("version", ChinapayConfig.version);               //版本号 data.put("encoding", ChinapayConfig.encoding_UTF8);             //字符集编码 可以使用UTF-8,GBK两种方式 data.put("signMethod", "01");                        //签名方法 目前只支持01-RSA方式证书加密 data.put("txnType", "04");                           //交易类型 04-退货 data.put("txnSubType", "00");                        //交易子类型  默认00 data.put("bizType", "000201");                       //业务类型 B2C网关支付,手机wap支付 data.put("channelType", "07");                       //渠道类型,07-PC,08-手机 
 /***商户接入参数***/ /*ChinapayConfig.MERID*/ data.put("merId", ChinapayConfig.MERID);                //商户号码,请改成自己申请的商户号或者open上注册得来的777商户号测试 data.put("accessType", "0");                         //接入类型,商户接入固定填0,不需修改 data.put("orderId", ChinapayConfig.getOrderId());          //商户订单号,8-40位数字字母,不能含“-”或“_”,可以自行定制规则,重新产生,不同于原消费 data.put("txnTime", ChinapayConfig.getCurrentTime());      //订单发送时间,格式为YYYYMMDDhhmmss,必须取当前时间,否则会报txnTime无效 data.put("currencyCode", "156");                     //交易币种(境内商户一般是156 人民币) //================================= data.put("txnAmt",String.valueOf((new BigDecimal(order.getPayMoney()).multiply(new BigDecimal(100))).intValue()));                          //****退货金额,单位分,不要带小数点。退货金额小于等于原消费金额,当小于的时候可以多次退货至退货累计金额等于原消费金额 //data.put("reqReserved", "透传信息");                  //请求方保留域,如需使用请启用即可;透传字段(可以实现商户自定义参数的追踪)本交易的后台通知,对本交易的交易状态查询交易、对账文件中均会原样返回,商户可以按需上传,长度为1-1024个字节 data.put("backUrl", basePath + "/CYChinapay/refundBack");               //后台通知地址,后台通知参数详见open.unionpay.com帮助中心 下载  产品接口规范  网关支付产品接口规范 退货交易 商户通知,其他说明同消费交易的后台通知 
 /***要调通交易以下字段必须修改***/ //======================== data.put("origQryId", order.getRefund_queryid());      //****原消费交易返回的的queryId,可以从消费交易后台通知接口中或者交易状态查询接口中获取 /**请求参数设置完毕,以下对请求参数进行签名并发送http post请求,接收同步应答报文------------->**/ Map<String, String> reqData = AcpService.sign(data, ChinapayConfig.encoding_UTF8);//报文中certId,signature的值是在signData方法中获取并自动赋值的,只要证书配置正确即可。 String url = SDKConfig.getConfig().getBackRequestUrl();//交易请求url从配置文件读取对应属性文件acp_sdk.properties中的 acpsdk.backTransUrl 
        System.out.print("backurl:" + url); Map<String, String> rspData = AcpService.post(reqData, url, ChinapayConfig.encoding_UTF8);//这里调用signData之后,调用submitUrl之前不能对submitFromData中的键值对做任何修改,如果修改会导致验签不通过 //   System.out.print("rspData值:"+"sasasasasaaaaaaaaaaaaasasasasasasa sasasas///////"+rspData+"------------"+!rspData.isEmpty()); /**对应答码的处理,请根据您的业务逻辑来编写程序,以下应答码处理逻辑仅供参考------------->**/ //应答码规范参考open.unionpay.com帮助中心 下载  产品接口规范  《平台接入接口规范-第5部分-附录》 if (!rspData.isEmpty()) { if (AcpService.validate(rspData, ChinapayConfig.encoding_UTF8)) { 
                LogUtil.writeLog("验证签名成功"); String respCode = rspData.get("respCode"); 
                LogUtil.writeLog("respCode-------------------------------------------------"+respCode); if ("00".equals(respCode)) { //交易已受理,等待接收后台通知更新订单状态,也可以主动发起 查询交易确定交易状态。 //TODO:处理退款申请发送成功后的业务逻辑 //设置退款状态为正在受理 return "申请退款成功!等待银联处理"; 
                } else if ("03".equals(respCode) || "04".equals(respCode) || "05".equals(respCode)) { 
 return "退款失败"; 
                } else { //其他应答码为失败请排查原因,根据银联响应报文的,respCode,respMsg来排查原因。 
 return "退款失败"; 
                } 
            } else { 
                LogUtil.writeErrorLog("验证签名失败"); 
 return "退款失败"; 
            } 
        } else { //未返回正确的http状态 
            LogUtil.writeErrorLog("未获取到返回报文或返回http状态码非200"); return "退款失败"; 
        } 
    } 
     //退款回调方法 
    @RequestMapping(value = "/refundBack", method = RequestMethod.POST) public void refundBack(HttpServletRequest req, HttpServletResponse resp) throws Exception { String encoding = req.getParameter(SDKConstants.param_encoding); 
        LogUtil.writeLog("进入退款后台回调-------------------------------------------------"); Map<String, String> reqParam = getAllRequestParam(req); 
 
        LogUtil.printRequestLog(reqParam); 
 Map<String, String> valideData = null; if (null != reqParam && !reqParam.isEmpty()) { 
            Iterator<Map.Entry<String, String>> it = reqParam.entrySet().iterator(); 
            valideData = new HashMap<String, String>(reqParam.size()); while (it.hasNext()) { Map.Entry<String, String> e = it.next(); String key = e.getKey(); String value = e.getValue(); 
                value = new String(value.getBytes(encoding), encoding); 
                valideData.put(key, value); 
            } 
        } //重要!验证签名前不要修改reqParam中的键值对的内容,否则会验签不过 if (!AcpService.validate(valideData, encoding)) { 
            LogUtil.writeLog("验证签名结果[失败]."); //验签失败,需解决验签问题 
 
        } else { 
            LogUtil.writeLog("验证签名结果[成功]."); //【注:为了安全验签成功才应该写商户的成功处理逻辑】交易成功,更新商户订单状态 
 
 String respCode = valideData.get("respCode"); //获取应答码,收到后台通知了respCode的值一般是00,可以不需要根据这个应答码判断。 
            LogUtil.writeLog("respCode:-------" + respCode); 
            LogUtil.writeLog("respCodeflg:-------" + respCode.equals("00")); if (respCode.equals("00")) { 
               //:TODO:退款回调成功后的业务逻辑 
            } 
        } 
        LogUtil.writeLog("BackRcvResponse接收后台通知结束"); //返回给银联服务器http 200  状态码 
        resp.getWriter().print("ok"); 
    } 
 /** 
     * 获取请求参数中所有的信息 
     * 
     * @param request 
     * @return 
     */ public static Map<String, String> getAllRequestParam( 
            final HttpServletRequest request) { Map<String, String> res = new HashMap<String, String>(); 
        Enumeration<?> temp = request.getParameterNames(); if (null != temp) { while (temp.hasMoreElements()) { String en = (String) temp.nextElement(); String value = request.getParameter(en); 
                res.put(en, value); // 在报文上送时,如果字段的值为空,则不上送<下面的处理为在获取所有参数数据时,判断若值为空,则删除这个字段> if (res.get(en) == null || "".equals(res.get(en))) { // System.out.println("======为空的字段名===="+en); 
                    res.remove(en); 
                } 
            } 
        } return res; 
    } 
}

6.订单状态枚举类

/**订单状态枚举类 
 */ public enum CyOrderStatusEnum { 
 //1未支付 2已支付 3退款 4取消订单 5过期 
 
    NO_PAYMENT("0","未支付"), 
    PREPAID("1","已支付"), 
    APPLYCANCEL("2","取消订单"), 
    CANCEL("3","已取消"), 
    AWARDING("4","正在授理..."), 
    REFUND_SUCCESS("5","退款完成"), 
    NET_ERROR("net_error", "网络异常,请稍后重试"); 
 private String code; private String desc; 
 private CyOrderStatusEnum(String code, String desc) { this.code = code; this.desc = desc; 
    } 
 public static CyOrderStatusEnum codeOf(String code) { if(code == null) { return NET_ERROR; 
        } else { 
            CyOrderStatusEnum[] arr$ = values(); int len$ = arr$.length; 
 for(int i$ = 0; i$ < len$; ++i$) { 
                CyOrderStatusEnum rtnCodeEnum = arr$[i$]; if(code.equals(rtnCodeEnum.getCode())) { return rtnCodeEnum; 
                } 
            } return NET_ERROR; 
        } 
    } 
 public String getCode() { return code; 
    } 
 public void setCode(String code) { this.code = code; 
    } 
 public String getDesc() { return desc; 
    } 
 public void setDesc(String desc) { this.desc = desc; 
    } 
}

7.支付成功后,java动态生成成功跳转页面,和失败跳转页面。

/** 
 * @author hff 
 * 点餐预定支付返回 
 */ 
public class CyReturnPayEndHtml { 
 
    private static StringBuffer stringBuffer = null; 
 
    private static StringBuffer headHtml(boolean isSuccess){ 
        if (isSuccess){ 
            stringBuffer.append("<head>"); 
            stringBuffer.append("   <meta name=/"viewport/" content=/"width=device-width, initial-scale=1, maximum-scale=1/">"); 
            stringBuffer.append("   <meta charset=/"UTF-8/">"); 
            stringBuffer.append("   <link rel=/"stylesheet/" href=/"http://code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css/">"); 
            stringBuffer.append("   <link rel=/"stylesheet/" href=/"http://资源路径/">"); 
            stringBuffer.append("   <style type=/"text/css/">"); stringBuffer.append("       .paySuccess dd,.paySuccess dt{text-align: center;line-height: 1.6;}.paySuccess dd .i_icon{display: inline-block;width: 130px;height: 130px;font-size: 100px;color: green;}.paySuccess dd b,.paySuccess dt b{color: #ff5409;font-weight: normal;}.paySuccess dt .i_icon{display: inline-block;font-size: 20px;color: #555;padding-right: 5px;}"); stringBuffer.append("   </style>"); 
            stringBuffer.append("   <title>支付成功</title>"); 
            stringBuffer.append("</head>"); 
        } 
        return stringBuffer; 
    } 
 
    private static StringBuffer javascript(String orderNum, String orderno, String url, boolean b){ 
        stringBuffer.append("<script src=/"https://code.jquery.com/jquery-1.9.1.min.js/"></script>"); 
        stringBuffer.append("<script src=/"js,css等资源路径"></script>"); 
        stringBuffer.append("<script>"); 
        stringBuffer.append("   function waitToIndex(){"); 
        stringBuffer.append("       var second = 3;"); 
        stringBuffer.append("       setInterval(function(){"); 
        stringBuffer.append("           if(second > 0){"); 
        stringBuffer.append("               $(/"#spanSecond/").html(second);"); 
        stringBuffer.append("               second--;"); 
        stringBuffer.append("           }else{"); 
        stringBuffer.append("               gotoIndex();"); 
        stringBuffer.append("           }"); 
        stringBuffer.append("       },1000);"); 
        stringBuffer.append("   }"); 
        stringBuffer.append("   function gotoIndex(){"); 
        stringBuffer.append("       window.location.href='"+url+"'"+";"); 
        stringBuffer.append("   }"); if (b){ 
            stringBuffer.append("   var num = 10;"); 
            stringBuffer.append("   function isSuccess(){"); 
            stringBuffer.append("       $.post('查询订单的路径"+orderno+"',{},function(res){"); 
            stringBuffer.append("           if(res.rtnCode == '0000000'){"); 
            stringBuffer.append("               var resutl = res.bizData;"); 
            stringBuffer.append("                   if (resutl.orderStatus == '1') {"); 
            stringBuffer.append("                   var timer1 = setInterval(function () {"); 
            stringBuffer.append("                       if (num > 0) {"); 
            stringBuffer.append("                           num--;"); 
            stringBuffer.append("                           isSuccess();"); 
            stringBuffer.append("                       } else {"); 
            stringBuffer.append("                           clearInterval(timer1);"); 
            stringBuffer.append("                           $('#outing').hide();"); 
            stringBuffer.append("                           $('#outSuccess').show();"); 
            stringBuffer.append("                           waitToIndex();"); 
            stringBuffer.append("                       }"); 
            stringBuffer.append("                   },1000);"); 
            stringBuffer.append("                 }"); 
            stringBuffer.append("           }"); 
            stringBuffer.append("       });"); 
            stringBuffer.append("   }"); 
            stringBuffer.append("   isSuccess();"); 
        }else { 
            stringBuffer.append("   waitToIndex();"); 
        } 
        stringBuffer.append("</script>"); 
        return stringBuffer; 
    } 
 
    public static String successHtml(String orderNum, String orderNo, String url){ 
        stringBuffer = new StringBuffer(); 
        stringBuffer.append("<!DOCTYPE html>"); 
        stringBuffer.append("<html>"); 
        headHtml(true); 
        stringBuffer.append("<script src=/"https://code.jquery.com/jquery-1.9.1.min.js/"></script>"); 
        stringBuffer.append("<script src=/"相关js,css的路径"></script>"); 
        stringBuffer.append("<body>"); 
        stringBuffer.append("   <div class=/"p10 paySuccess/">"); 
        stringBuffer.append("       <dd><i class=/"ion-checkmark-circled i_icon/"></i></dd>"); 
        stringBuffer.append("       <dd id=/"paySuccess/">"); 
        stringBuffer.append("           支付成功"); 
        stringBuffer.append("           <span id=/"scores/">"); 
        stringBuffer.append("       </dd>"); 
        stringBuffer.append("       <dt id=/"outing/"><i class=/"ion-load-a i_icon/"></i>正在点餐预定....</dt>"); 
        stringBuffer.append("       <dt id=/"outSuccess/" style=/"display: none;/">"); 
        stringBuffer.append("           <b id=/"spanSecond/">3</b>秒钟之后<a href=/"javascript:gotoIndex()/"><b>跳转</b></a>回首页"); 
        stringBuffer.append("       </dt>"); 
        stringBuffer.append("   </div>"); 
        javascript(orderNum,orderNo, url, true); 
        stringBuffer.append("</body>"); 
        stringBuffer.append("</html>"); 
        return stringBuffer.toString(); 
    } 
 
    public static String failedHtml(String orderNo, String errMsg, String url){ 
        stringBuffer = new StringBuffer(); 
        stringBuffer.append("<!DOCTYPE html>"); 
        stringBuffer.append("<html>"); 
        headHtml(true); 
        stringBuffer.append("<body>"); 
        stringBuffer.append("   <div class=/"p10 paySuccess/">"); 
        stringBuffer.append("       <dd><i class=/"ion-checkmark-circled i_icon/"></i></dd>"); 
        stringBuffer.append("       <dd id=/"paySuccess/">"); 
        stringBuffer.append("           支付失败["+errMsg+"]"); 
        stringBuffer.append("           <span id=/"scores/">"); 
        stringBuffer.append("       </dd>"); 
        stringBuffer.append("       <dt id=/"outSuccess/">"); 
        stringBuffer.append("           <b id=/"spanSecond/">3</b>秒钟之后<a href=/"javascript:gotoIndex()/"><b>跳转</b></a>回首页"); 
        stringBuffer.append("       </dt>"); 
        stringBuffer.append("   </div>"); 
        javascript(null,orderNo,url, false); 
        stringBuffer.append("</body>"); 
        stringBuffer.append("</html>"); 
        return stringBuffer.toString(); 
    } 
 
    public static void main(String[] args) { 
        BigDecimal b1 = new BigDecimal("0.12"); 
        BigDecimal b2 = new BigDecimal("0.01"); 
        System.out.println(b1.subtract(b2).toString()); 
    } 
 
}

4.注意事项(可能会出错的地方)

1:开发环境分为测试环境和生产环境 
2:不管什么环境,银联相关证书一定要一一对应。 
3:若是生产环境的证书有四个,缺一不可,测试环境有两个 
4:开发过程中测试环境无法测试回调接口,必须为外网测试,因为这里的回调接口是银联通过外网来回调的。 
5:支付发起所需的参数缺一不可,格式一定要规范,符合规则,只有这样才能生成正确的请求报文。 
6:支付的两个回调接口,一个回调接口一般叫做frontUrl,这个地址是由用户点击返回商户的时候银联才会调用。另外一个回调接口叫做backUrl,这个地址是银联接到支付请求后异步回调的接口,一般网站的业务逻辑在此处处理。 
7:如果要做退款,一定要有退款流水号,支付流水号。 
8:退款时,需要拿着接口对应的参数,以及对应的支付流水号去发起退款申请。退款分为交易撤销和交易退货,撤销只能处理当日的订单。 
9:不管是支付,退款,回调接口,当出现问题时,一定要仔细产看银联的响应报文,只要请求报文格式正确,银联收到请求,它一定会给响应,通过响应respCode和respMsg来判断接口的问题出自哪里。 
10:交易金额都是以分为单位,不能有小数点,金额参数一定要做去小数点处理。 
11:银联官网测试环境提供了测试参数,项目没有用生产环境的时候,可以用测试参数来测试。

5.银联官网相关网址

在开发过程中可能需要的网址整理。 
官网demo,文档及sdk下载链接: 
https://open.unionpay.com/ajweb/help/file/techFile?productId=1 
官网api链接: 
https://open.unionpay.com/ajweb/help/api  
常见问题查看平台: 
https://open.unionpay.com/ajweb/help/faq/list?id=140&level=0&from=1 
银联社区链接: 
https://bbs.unionpay.com/upbbs/forum/topic?id=4

以上博文是自己做完支付后的总结,有不正确的地方,还望各位路过的大牛批评指正。

原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/13987.html

(0)
上一篇 2021年7月19日
下一篇 2021年7月19日

相关推荐

发表回复

登录后才能评论