历时两周,终于把银联支付退款搞定了。由于没人指导,走了不少弯路,博主在此贴出相关代码,希望能帮到像我一样没人指导的小伙伴。
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类中
*
*/
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 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("");
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\">span>");
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\">3b>秒钟之后<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("");
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\">span>");
stringBuffer.append(" dd>");
stringBuffer.append(" <dt id=\"outSuccess\">");
stringBuffer.append(" <b id=\"spanSecond\">3b>秒钟之后<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());
}
}
1:开发环境分为测试环境和生产环境
2:不管什么环境,银联相关证书一定要一一对应。
3:若是生产环境的证书有四个,缺一不可,测试环境有两个
4:开发过程中测试环境无法测试回调接口,必须为外网测试,因为这里的回调接口是银联通过外网来回调的。
5:支付发起所需的参数缺一不可,格式一定要规范,符合规则,只有这样才能生成正确的请求报文。
6:支付的两个回调接口,一个回调接口一般叫做frontUrl,这个地址是由用户点击返回商户的时候银联才会调用。另外一个回调接口叫做backUrl,这个地址是银联接到支付请求后异步回调的接口,一般网站的业务逻辑在此处处理。
7:如果要做退款,一定要有退款流水号,支付流水号。
8:退款时,需要拿着接口对应的参数,以及对应的支付流水号去发起退款申请。退款分为交易撤销和交易退货,撤销只能处理当日的订单。
9:不管是支付,退款,回调接口,当出现问题时,一定要仔细产看银联的响应报文,只要请求报文格式正确,银联收到请求,它一定会给响应,通过响应respCode和respMsg来判断接口的问题出自哪里。
10:交易金额都是以分为单位,不能有小数点,金额参数一定要做去小数点处理。
11:银联官网测试环境提供了测试参数,项目没有用生产环境的时候,可以用测试参数来测试。
在开发过程中可能需要的网址整理。
官网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
以上博文是自己做完支付后的总结,有不正确的地方,还望各位路过的大牛批评指正。针对以上代码若有疑问,请加群:qq群号 451232132 找博主。