自定义银联工具类
package util; import com.unionpay.acp.sdk.SDKConstants; import java.util.Iterator; import java.util.Map; import java.util.TreeMap; public class UnionPayConstantUtil { /** * 版本 5.1.0 */ public static final String VERSION = "5.1.0"; /** * 默认编码 UTF-8 */ public static final String ENCODING = "UTF-8"; /** * 商户号 */ public static final String MER_ID = "777290058156102"; /** * 商户名称 */ public static final String MER_NAME = ""; /** * 商户简称 */ public static final String MER_ABBR = ""; /** * 交易类型 01-消费 */ public static final String TXN_TYPE_CONSUMER = "01"; /** * 交易类型 04-退货 */ public static final String TXN_TYPE_REFUND = "04"; /** * 交易类型 31-消费撤销 */ public static final String TXN_TYPE_CONSUMER_UNDO = "31"; /** * 交易类型 77-发送短信 */ public static final String TXN_TYPE_SMS = "77"; /** * 交易类型 78-交易查询 */ public static final String TXN_TYPE_QUERY = "78"; /** * 首次开通 */ public static final String TXN_TYPE_OPEN_CARD = "79"; /** * 交易类型 95 - 银联加密公钥更新查询 */ public static final String TXN_TYPE_ENCRYPTCER_UPDATE_QUERY = "95"; /** * 交易子类型 00-默认 */ public static final String TXN_SUBTYPE_DEFAULT = "00"; /** * 交易子类型 01-消费 */ public static final String TXN_SUBTYPE_CONSUMER = "01"; /** * 交易子类型 02-消费短信 */ public static final String TXN_SUBTYPE_CONSUMER_SMS = "02"; /** * 交易子类型 03-分期 */ public static final String TXN_SUBTYPE_FQ = "03"; /** * 业务类型 000000-默认 */ public static final String BIZ_TYPE_DEFAULT = "000000"; /** * 业务类型 000201-B2C网关支付 */ public static final String BIZ_TYPE_B2C = "000201"; /** * 业务类型 000301-认证支付2.0 */ public static final String BIZ_TYPE_AUTH = "000301"; /** * 业务类型 000902-TOKEN支付 */ public static final String BIZ_TYPE_TOKEN = "000902"; /** * 渠道类型 07-互联网 */ public static final String CHANNEL_TYPE_PC = "07"; /** * 渠道类型 08-移动 */ public static final String CHANNEL_TYPE_MOBILE = "08"; /** * 商户接入类型 0-商户直接接入 默认为0 1-机构接入 2-平台接入 */ public static final String ACCESS_TYPE_DEFAULT = "0"; /** * 交易币种 默认人民币 */ public static final String CURRENCY_CODE = "156"; /** * 账号类型 01-银行卡 */ public static final String ACC_TYPE_BANK_CARD = "01"; /** * 账号类 03:IC卡 */ public static final String ACC_TYPE_IC_CARD = "03"; /** * 敏感信息加密公钥 01 */ public static final String CERT_TYPE = "01"; /** * 封装数据拆解 * @param data * @return */ public static String genHtmlResult(Map, String> data){ TreeMap , String> tree = new TreeMap , String>(); Iterator , String>> it = data.entrySet().iterator(); while (it.hasNext()) { Map.Entry , String> en = it.next(); tree.put(en.getKey(), en.getValue()); } it = tree.entrySet().iterator(); StringBuffer sf = new StringBuffer(); while (it.hasNext()) { Map.Entry , String> en = it.next(); String key = en.getKey(); String value = en.getValue(); if("respCode".equals(key)){ sf.append(""+key + SDKConstants.EQUAL + value+""); }else sf.append(key + SDKConstants.EQUAL + value+""); } return sf.toString(); } }
银联接口实现
package web; import com.unionpay.acp.sdk.AcpService; import com.unionpay.acp.sdk.LogUtil; import com.unionpay.acp.sdk.SDKConfig; import com.unionpay.acp.sdk.SDKConstants; import org.apache.commons.io.IOUtils; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; import util.UnionPayConstantUtil; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Enumeration; import java.util.HashMap; import java.util.Map; @Controller @RequestMapping("/api/commons/pay") public class UnionPayController { /** * 交易查询 * * @param sessionId * @param accNo * @param orderId * @param txnTime * @param response * @return */ @RequestMapping("/unionPayQuery.do") public @ResponseBody Map, Object> unionPay(String sessionId, String accNo, String orderId, String txnTime, HttpServletResponse response) { //判断参数 //判断是否符合条件 Map , String> contentData = new HashMap , String>(); //配置银联全渠道信息 contentData.put(SDKConstants.param_version, UnionPayConstantUtil.VERSION); //版本号 contentData.put(SDKConstants.param_encoding, UnionPayConstantUtil.ENCODING); //字符集编码 可以使用UTF-8,GBK两种方式 contentData.put(SDKConstants.param_signMethod, SDKConfig.getConfig().getSignMethod()); //签名方法 contentData.put(SDKConstants.param_txnType, UnionPayConstantUtil.TXN_TYPE_QUERY); //交易类型 78-开通查询 contentData.put(SDKConstants.param_txnSubType, UnionPayConstantUtil.TXN_SUBTYPE_DEFAULT); //交易子类型 00-根据账号accNo查询(默认) contentData.put(SDKConstants.param_bizType, UnionPayConstantUtil.BIZ_TYPE_AUTH); //业务类型 认证支付2.0 contentData.put(SDKConstants.param_channelType, UnionPayConstantUtil.CHANNEL_TYPE_PC); //渠道类型07-PC //商户信息 contentData.put(SDKConstants.param_merId, UnionPayConstantUtil.MER_ID); contentData.put(SDKConstants.param_orderId, orderId); contentData.put(SDKConstants.param_txnTime, txnTime); contentData.put(SDKConstants.param_accessType, UnionPayConstantUtil.ACCESS_TYPE_DEFAULT); //买家账户信息 ////////////如果商户号开通了【商户对敏感信息加密】的权限那么需要对 accNo,phoneNo加密使用: String accNo1 = AcpService.encryptData(accNo, UnionPayConstantUtil.ENCODING); //这里测试的时候使用的是测试卡号,正式环境请使用真实卡号 contentData.put(SDKConstants.param_accNo, accNo1); contentData.put(SDKConstants.param_encryptCertId, AcpService.getEncryptCertId()); //加密证书的certId,配置在acp_sdk.properties文件 acpsdk.encryptCert.path属性下 //与银联通信 try { Map , String> reqData = AcpService.sign(contentData, UnionPayConstantUtil.ENCODING); //报文中certId,signature的值是在signData方法中获取并自动赋值的,只要证书配置正确即可。 String requestBackUrl = SDKConfig.getConfig().getBackRequestUrl(); //交易请求url从配置文件读取对应属性文件acp_sdk.properties中的 acpsdk.backTransUrl Map , String> rspData = AcpService.post(reqData, requestBackUrl, UnionPayConstantUtil.ENCODING); //发送请求报文并接受同步应答(默认连接超时时间30秒,读取返回结果超时时间30秒);这里调用signData之后,调用submitUrl之前不能对submitFromData中的键值对做任何修改,如果修改会导致验签不通过 StringBuffer parseStr = new StringBuffer(""); if (!rspData.isEmpty()) { if (AcpService.validate(rspData, UnionPayConstantUtil.ENCODING)) { LogUtil.writeLog("验证签名成功"); String respCode = rspData.get("respCode"); if (("00").equals(respCode)) { //成功 parseStr.append("
解析敏感信息加密信息如下(如果有):
"); String customerInfo = rspData.get("customerInfo"); if (null != customerInfo) { Map, String> cm = AcpService.parseCustomerInfo(customerInfo, UnionPayConstantUtil.ENCODING); parseStr.append("customerInfo明文: " + cm + "
"); } String an = rspData.get("accNo"); if (null != an) { an = AcpService.decryptData(an, UnionPayConstantUtil.ENCODING); parseStr.append("accNo明文: " + an); } //TODO } else { //其他应答码为失败请排查原因或做失败处理 //TODO } } else { LogUtil.writeErrorLog("验证签名失败"); //TODO 检查验证签名失败的原因 } } else { //未返回正确的http状态 LogUtil.writeErrorLog("未获取到返回报文或返回http状态码非200"); } String reqMessage = UnionPayConstantUtil.genHtmlResult(reqData); String rspMessage = UnionPayConstantUtil.genHtmlResult(rspData); response.getWriter().write("请求报文:
" + reqMessage + "
" + "应答报文:" + rspMessage + parseStr); } catch (Exception e) { e.printStackTrace(); return null; } return null; } /** * 首次使用开通 * * @param sessionId * @param orderId * @param accNo * @param phoneNo * @param cvn2 * @param expired * @param response * @return */ @RequestMapping("/unionPayOpenCardBack.do") public @ResponseBody Map, Object> unionPayOpenCardBack(String sessionId, String orderId, String accNo, String phoneNo, String cvn2, String expired, HttpServletResponse response) { //判断参数是否合法 //判断用户是否存在 //判断订单是否存在 订单状态是否合法 //判断银行卡号 手机号 //封装数据 Map , String> contentData = new HashMap , String>(); //银联全渠道默认参数 contentData.put(SDKConstants.param_version, UnionPayConstantUtil.VERSION); //版本号 contentData.put(SDKConstants.param_encoding, UnionPayConstantUtil.ENCODING); //字符集编码 可以使用UTF-8,GBK两种方式 contentData.put(SDKConstants.param_signMethod, SDKConfig.getConfig().getSignMethod()); //签名方法 contentData.put(SDKConstants.param_txnType, UnionPayConstantUtil.TXN_TYPE_OPEN_CARD); //交易类型 79-开通 contentData.put(SDKConstants.param_txnSubType, UnionPayConstantUtil.TXN_SUBTYPE_DEFAULT); //交易子类型 00 contentData.put(SDKConstants.param_bizType, UnionPayConstantUtil.BIZ_TYPE_AUTH); //业务类型 认证支付2.0 contentData.put(SDKConstants.param_channelType, UnionPayConstantUtil.CHANNEL_TYPE_PC); //渠道类型07-PC Date date = new Date(); SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss"); String txnTime = sdf.format(date); //商户接入参数 商户订单内容的补充 contentData.put(SDKConstants.param_merId, UnionPayConstantUtil.MER_ID); contentData.put(SDKConstants.param_accessType, UnionPayConstantUtil.ACCESS_TYPE_DEFAULT); //接入类型,商户接入固定填0,不需修改 contentData.put(SDKConstants.param_orderId, orderId); //商户订单号,8-40位数字字母,不能含“-”或“_”,可以自行定制规则 contentData.put(SDKConstants.param_txnTime, txnTime); //订单发送时间,格式为YYYYMMDDhhmmss,必须取当前时间,否则会报txnTime无效 contentData.put(SDKConstants.param_accType, UnionPayConstantUtil.ACC_TYPE_BANK_CARD); //账号类型 //商户需要开通对银行卡进行开通的权限 否则商户不能再自己的站点开通 //消费交易要素 买家交易的账号进行补充 买家个人信息 Map , String> customInfoData = new HashMap , String>(); //商户号要是开启了对银行卡号进行加密 就需要这一步骤 customInfoData.put(SDKConstants.param_phoneNo, phoneNo); customInfoData.put(SDKConstants.param_cvn2, cvn2); customInfoData.put(SDKConstants.param_expired, expired); String customerInfoWithEncrypt = AcpService.getCustomerInfoWithEncrypt(customInfoData, null, UnionPayConstantUtil.ENCODING); contentData.put(SDKConstants.param_customerInfo, customerInfoWithEncrypt); //根据商户是否开启加密而设置 String accNo1 = AcpService.encryptData(accNo, UnionPayConstantUtil.ENCODING); contentData.put(SDKConstants.param_accNo, accNo1); contentData.put(SDKConstants.param_encryptCertId, AcpService.getEncryptCertId()); //获取加密证书的id //后台通知地址 contentData.put(SDKConstants.param_backUrl, SDKConfig.getConfig().getBackUrl()); //进行交易签名 并应答 try { Map , String> signData = AcpService.sign(contentData, UnionPayConstantUtil.ENCODING); String backRequestUrl = SDKConfig.getConfig().getBackRequestUrl(); //响应回来的数据 这里只能证明与银联连接是否接通 正真的完成交易是在银联给后台地址发送数据 才能证明交易完成 Map , String> resposeData = AcpService.post(signData, backRequestUrl, UnionPayConstantUtil.ENCODING); String reqMessage = UnionPayConstantUtil.genHtmlResult(signData); String rspMessage = UnionPayConstantUtil.genHtmlResult(resposeData); response.getWriter().write("请求报文:
" + reqMessage + "
" + "应答报文:" + rspMessage); if (resposeData == null) { return null; } if (AcpService.validate(resposeData, UnionPayConstantUtil.ENCODING)) { String respCode = resposeData.get("respCode"); if ("00".equals(respCode)) { //处理业务 交易以受理 //保存订单日志 PayRequestLog } else if (("03").equals(respCode) || ("04").equals(respCode) || ("05").equals(respCode)) { //处理没有受理 情况 } else { //这里处理错误 返回给前台提示用户 } } else { return null; } } catch (Exception e) { e.printStackTrace(); } return null; } /** * 银联支付接口 * * @param sessionId * @param orderId * @param smsCode * @param accNo * @param fq_num * @return */ @RequestMapping("/unionPay.do") public @ResponseBody Map, Object> unionPay(String sessionId, String orderId, String smsCode, String accNo, String fq_num, HttpServletResponse response, String prize) { //判断当前用户 //判断参数是否符合要求 //判断订单是否存在 判断订单状态 //判断accno银行卡号是否符合规格 并判断是否是合作的银行所属的卡号 Map , String> contentData = new HashMap , String>(); //银联全渠道默认参数 contentData.put(SDKConstants.param_version, UnionPayConstantUtil.VERSION); //版本号 contentData.put(SDKConstants.param_encoding, UnionPayConstantUtil.ENCODING); //字符集编码 可以使用UTF-8,GBK两种方式 contentData.put(SDKConstants.param_signMethod, SDKConfig.getConfig().getSignMethod()); //签名方法 //txnSubType 01-消费 03-分期 contentData.put(SDKConstants.param_txnType, UnionPayConstantUtil.TXN_TYPE_CONSUMER); //交易类型 01-消费 contentData.put(SDKConstants.param_txnSubType, UnionPayConstantUtil.TXN_SUBTYPE_CONSUMER); //交易子类型 01-消费 03-分期 //numberOfInstallments分期期数03 06 12 instalRate分期期率 mchntFeeSubsidy 商户补贴 //contentData.put("instalTransInfo", "{numberOfInstallments=" + fq_num + "}"); contentData.put(SDKConstants.param_bizType, UnionPayConstantUtil.BIZ_TYPE_AUTH); //业务类型 认证支付2.0 contentData.put(SDKConstants.param_channelType, UnionPayConstantUtil.CHANNEL_TYPE_PC); //渠道类型07-PC Date date = new Date(); SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss"); String txnTime = sdf.format(date); //商户接入参数 商户订单内容的补充 contentData.put(SDKConstants.param_merId, UnionPayConstantUtil.MER_ID); contentData.put(SDKConstants.param_accessType, UnionPayConstantUtil.ACCESS_TYPE_DEFAULT); //接入类型,商户接入固定填0,不需修改 contentData.put(SDKConstants.param_orderId, orderId); //商户订单号,8-40位数字字母,不能含“-”或“_”,可以自行定制规则 contentData.put(SDKConstants.param_txnTime, txnTime); //订单发送时间,格式为yyyyMMddHHmmss,必须取当前时间,否则会报txnTime无效 contentData.put(SDKConstants.param_currencyCode, UnionPayConstantUtil.CURRENCY_CODE); //交易币种(境内商户一般是156 人民币) contentData.put(SDKConstants.param_txnAmt, prize); //交易金额,单位分,不要带小数点 contentData.put(SDKConstants.param_accType, UnionPayConstantUtil.ACC_TYPE_BANK_CARD); //账号类型 //contentData.put("orderDesc", "订单描述"); contentData.put(SDKConstants.param_reqReserved, "orderDesc=订单描述;remark=备注"); //请求保留域 //消费交易要素 买家交易的账号进行补充 Map , String> customInfoData = new HashMap , String>(); customInfoData.put(SDKConstants.param_smsCode, smsCode); //商户号要是开启了对银行卡号进行加密 就需要这一步骤 String customerInfoWithEncrypt = AcpService.getCustomerInfoWithEncrypt(customInfoData, accNo, UnionPayConstantUtil.ENCODING); String accNo1 = AcpService.encryptData(accNo, UnionPayConstantUtil.ENCODING); contentData.put(SDKConstants.param_accNo, accNo1); contentData.put(SDKConstants.param_encryptCertId, AcpService.getEncryptCertId()); //获取加密证书的id contentData.put(SDKConstants.param_customerInfo, customerInfoWithEncrypt); //后台通知地址 contentData.put(SDKConstants.param_backUrl, SDKConfig.getConfig().getBackUrl()); //进行交易签名 并应答 try { Map , String> signData = AcpService.sign(contentData, UnionPayConstantUtil.ENCODING); String backRequestUrl = SDKConfig.getConfig().getBackRequestUrl(); //响应回来的数据 这里只能证明与银联连接是否接通 正真的完成交易是在银联给后台地址发送数据 才能证明交易完成 Map , String> resposeData = AcpService.post(signData, backRequestUrl, UnionPayConstantUtil.ENCODING); String reqMessage = UnionPayConstantUtil.genHtmlResult(signData); String rspMessage = UnionPayConstantUtil.genHtmlResult(resposeData); response.getWriter().write("请求报文:
" + reqMessage + "
" + "应答报文:" + rspMessage); if (resposeData == null) { return null; } if (AcpService.validate(resposeData, UnionPayConstantUtil.ENCODING)) { String respCode = resposeData.get("respCode"); if ("00".equals(respCode)) { //处理业务 交易以受理 //保存订单日志 PayRequestLog LogUtil.writeLog("consume=======》OK"); } else if (("03").equals(respCode) || ("04").equals(respCode) || ("05").equals(respCode)) { //处理没有受理 情况 } else { //这里处理错误 返回给前台提示用户 } } else { return null; } } catch (Exception e) { e.printStackTrace(); return null; } return null; } /** * 银联消费短信 */ @RequestMapping("/unionPayConsumeSMS.do") public @ResponseBody Map, Object> unionPayConsumeSMS(String sessionId, String orderId, String accNo, String phoneNo, HttpServletResponse response, String prize) { //判断参数是否合法 //查看用户是否存在 //查找订单 判断订单状态 //判断电话是否符合要求 //封装数据 Map , String> contentData = new HashMap , String>(); //银联全渠道默认参数 contentData.put(SDKConstants.param_version, UnionPayConstantUtil.VERSION); //版本号 contentData.put(SDKConstants.param_encoding, UnionPayConstantUtil.ENCODING); //字符集编码 可以使用UTF-8,GBK两种方式 contentData.put(SDKConstants.param_signMethod, SDKConfig.getConfig().getSignMethod()); //签名方法 contentData.put(SDKConstants.param_txnType, UnionPayConstantUtil.TXN_TYPE_SMS); //交易类型 77短信 contentData.put(SDKConstants.param_txnSubType, UnionPayConstantUtil.TXN_SUBTYPE_CONSUMER_SMS); //交易子类型 01-消费 03-分期 contentData.put(SDKConstants.param_bizType, UnionPayConstantUtil.BIZ_TYPE_AUTH); //业务类型 认证支付2.0 contentData.put(SDKConstants.param_channelType, UnionPayConstantUtil.CHANNEL_TYPE_PC); //渠道类型07-PC Date date = new Date(); SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss"); String txnTime = sdf.format(date); //商户接入参数 商户订单内容的补充 contentData.put(SDKConstants.param_merId, UnionPayConstantUtil.MER_ID); contentData.put(SDKConstants.param_accessType, UnionPayConstantUtil.ACCESS_TYPE_DEFAULT); //接入类型,商户接入固定填0,不需修改 contentData.put(SDKConstants.param_orderId, orderId); //商户订单号,8-40位数字字母,不能含“-”或“_”,可以自行定制规则 contentData.put(SDKConstants.param_txnTime, txnTime); //订单发送时间,格式为YYYYMMDDhhmmss,必须取当前时间,否则会报txnTime无效 contentData.put(SDKConstants.param_currencyCode, UnionPayConstantUtil.CURRENCY_CODE); //交易币种(境内商户一般是156 人民币) contentData.put(SDKConstants.param_txnAmt, prize); //交易金额,单位分,不要带小数点 contentData.put(SDKConstants.param_accType, UnionPayConstantUtil.ACC_TYPE_BANK_CARD); //账号类型 //contentData.put("orderDesc", "订单描述"); contentData.put(SDKConstants.param_reqReserved, "orderDesc=订单描述;remark=备注"); //请求保留域 //消费交易要素 买家交易的账号进行补充 Map , String> customInfoData = new HashMap , String>(); //添加手机号码 customInfoData.put(SDKConstants.param_phoneNo, phoneNo); String customerInfoWithEncrypt = AcpService.getCustomerInfoWithEncrypt(customInfoData, null, UnionPayConstantUtil.ENCODING); //商户号要是开启了对银行卡号进行加密 就需要这一步骤 String accNo1 = AcpService.encryptData(accNo, UnionPayConstantUtil.ENCODING); contentData.put(SDKConstants.param_accNo, accNo1); contentData.put(SDKConstants.param_encryptCertId, AcpService.getEncryptCertId()); //获取加密证书的id contentData.put(SDKConstants.param_customerInfo, customerInfoWithEncrypt); //后台通知地址 contentData.put(SDKConstants.param_backUrl, SDKConfig.getConfig().getBackUrl()); //进行交易签名 并应答 try { Map , String> signData = AcpService.sign(contentData, UnionPayConstantUtil.ENCODING); String backRequestUrl = SDKConfig.getConfig().getBackRequestUrl(); //响应回来的数据 这里只能证明与银联连接是否接通 正真的完成交易是在银联给后台地址发送数据 才能证明交易完成 Map , String> resposeData = AcpService.post(signData, backRequestUrl, UnionPayConstantUtil.ENCODING); String reqMessage = UnionPayConstantUtil.genHtmlResult(signData); String rspMessage = UnionPayConstantUtil.genHtmlResult(resposeData); response.getWriter().write("请求报文:
" + reqMessage + "
" + "应答报文:" + rspMessage); if (resposeData == null) { return null; } if (AcpService.validate(resposeData, UnionPayConstantUtil.ENCODING)) { String respCode = resposeData.get("respCode"); if ("00".equals(respCode)) { //处理业务 交易以受理 //发送短信成功 LogUtil.writeLog("成功"); } else if (("03").equals(respCode) || ("04").equals(respCode) || ("05").equals(respCode)) { //处理没有受理 情况 } else { //这里处理错误 返回给前台提示用户 } } else { return null; } } catch (Exception e) { e.printStackTrace(); } return null; } /** * 后台接收地址 * * @param request * @param response * @return */ @RequestMapping(value = "/unionPayNotify.do", method = {RequestMethod.POST}) public @ResponseBody Map, Object> unionPayNotify(HttpServletRequest request, HttpServletResponse response) { try { LogUtil.writeLog("===============Notify============="); String encoding = request.getParameter(SDKConstants.param_encoding); Map , String> requestParam = getAllRequestParam(request); //在对数据进行验证的时候 不能在这之前对数据进行修改 if (!AcpService.validate(requestParam, encoding)) { //验证失败 需要做出处理 } else { //验证成功 进行处理业务 //获取请求orderId String orderId = requestParam.get(SDKConstants.param_orderId); //查询订单是否存在 更改订单状态 //获取买家的账号加密信息 String customerInfo = requestParam.get(SDKConstants.param_customerInfo); if (customerInfo != null) { Map , String> parseCustomerInfo = AcpService.parseCustomerInfo(customerInfo, UnionPayConstantUtil.ENCODING); String accNo = parseCustomerInfo.get(SDKConstants.param_accNo); if (accNo != null) { String accNo1 = AcpService.decryptData(accNo, UnionPayConstantUtil.ENCODING); } //可以对账号进行存储 } String respCode = requestParam.get(SDKConstants.param_respCode); //需要保存requestParam 数据 ===》日志 PayResponseLog if ("00".equals(respCode)) { //需要保存交易流水号 queryId //交易成功处理订单 paySuccessAliPay参考支付宝交易成功所需要处理的业务逻辑 response.getWriter().print("ok"); } String rspMessage = UnionPayConstantUtil.genHtmlResult(requestParam); LogUtil.writeLog(rspMessage); } } catch (Exception e) { e.printStackTrace(); } return null; } /** * 银联退款接口 * orderId 不能更原来订单的orderId相同 退款的orderId应该重新定义一个 * txnTime 时间也是一样不能跟原来的一样 */ @RequestMapping("/unionPayRefund.do") public @ResponseBody Map , Object> unionPayRefund(String sessionId, String orderId, String origQryId, String txnTime, HttpServletResponse response) { //判断参数是否合法 //判断当前用户 //查找订单 判断订单是否存在 判断订单状态 //获取退款原因 //需要查找到该订单的流水号 //封装数据 Map , String> contentData = new HashMap , String>(); //银联全渠道参数 contentData.put(SDKConstants.param_version, UnionPayConstantUtil.VERSION); //版本号 contentData.put(SDKConstants.param_encoding, UnionPayConstantUtil.ENCODING); //字符集编码 可以使用UTF-8,GBK两种方式 contentData.put(SDKConstants.param_signMethod, SDKConfig.getConfig().getSignMethod()); //签名方法 contentData.put(SDKConstants.param_txnType, UnionPayConstantUtil.TXN_TYPE_REFUND); //交易类型 04-退货 contentData.put(SDKConstants.param_txnSubType, UnionPayConstantUtil.TXN_SUBTYPE_DEFAULT); //交易子类型 默认00 contentData.put(SDKConstants.param_bizType, UnionPayConstantUtil.BIZ_TYPE_AUTH); //业务类型 contentData.put(SDKConstants.param_channelType, UnionPayConstantUtil.CHANNEL_TYPE_PC); //渠道类型,07-PC,08-手机 //商户数据封装 contentData.put(SDKConstants.param_merId, UnionPayConstantUtil.MER_ID); //商户号码,请改成自己申请的商户号或者open上注册得来的777商户号测试 contentData.put(SDKConstants.param_accessType, UnionPayConstantUtil.ACCESS_TYPE_DEFAULT); //接入类型,商户接入固定填0,不需修改 //订单数据封装 contentData.put(SDKConstants.param_orderId, orderId); //商户订单号,8-40位数字字母,不能含“-”或“_”,可以自行定制规则,重新产生,不同于原消费 contentData.put(SDKConstants.param_txnTime, txnTime); //订单发送时间,格式为YYYYMMDDhhmmss,必须取当前时间,否则会报txnTime无效 contentData.put(SDKConstants.param_currencyCode, UnionPayConstantUtil.CURRENCY_CODE); //交易币种(境内商户一般是156 人民币) contentData.put(SDKConstants.param_txnAmt, "120"); //****退货金额,单位分,不要带小数点。退货金额小于等于原消费金额,当小于的时候可以多次退货至退货累计金额等于原消费金额 contentData.put(SDKConstants.param_backUrl, SDKConfig.getConfig().getBackUrl()); //后台通知地址,后台通知参数详见open.unionpay.com帮助中心 下载 产品接口规范 网关支付产品接口规范 退货交易 商户通知,其他说明同消费交易的后台通知 //订单的流水号 contentData.put(SDKConstants.param_origQryId, origQryId); /**请求参数设置完毕,以下对请求参数进行签名并发送http post请求,接收同步应答报文------------->**/ try { Map , String> reqData = AcpService.sign(contentData, UnionPayConstantUtil.ENCODING); //报文中certId,signature的值是在signData方法中获取并自动赋值的,只要证书配置正确即可。 String url = SDKConfig.getConfig().getBackRequestUrl(); //交易请求url从配置文件读取对应属性文件acp_sdk.properties中的 acpsdk.backTransUrl Map , String> rspData = AcpService.post(reqData, url, UnionPayConstantUtil.ENCODING);//这里调用signData之后,调用submitUrl之前不能对submitFromData中的键值对做任何修改,如果修改会导致验签不通过 if (!rspData.isEmpty()) { if (AcpService.validate(rspData, UnionPayConstantUtil.ENCODING)) { String respCode = rspData.get("respCode"); if ("00".equals(respCode)) { //处理业务 LogUtil.writeLog("refund ==== > ok"); } else if (("03").equals(respCode) || ("04").equals(respCode) || ("05").equals(respCode)) { //其他原因 } } else { //验证失败 } } else { //排查问题 } String reqMessage = UnionPayConstantUtil.genHtmlResult(reqData); String rspMessage = UnionPayConstantUtil.genHtmlResult(rspData); response.getWriter().write("请求报文:
" + reqMessage + "
" + "应答报文:" + rspMessage); } catch (Exception e) { e.printStackTrace(); } return null; } /** * 加密证书 一天一更新 做定时任务 */ public void encryptCerUpdate() { Map, String> contentData = new HashMap , String>(); contentData.put(SDKConstants.param_version, UnionPayConstantUtil.VERSION); //版本号 contentData.put(SDKConstants.param_encoding, UnionPayConstantUtil.ENCODING); //字符集编码 可以使用UTF-8,GBK两种方式 contentData.put(SDKConstants.param_signMethod, SDKConfig.getConfig().getSignMethod()); //签名方法 01:RSA证书方式 11:支持散列方式验证SHA-256 12:支持散列方式验证SM3 contentData.put(SDKConstants.param_txnType, UnionPayConstantUtil.TXN_TYPE_ENCRYPTCER_UPDATE_QUERY); //交易类型 95-银联加密公钥更新查询 contentData.put(SDKConstants.param_txnSubType, UnionPayConstantUtil.TXN_SUBTYPE_DEFAULT); //交易子类型 默认00 contentData.put(SDKConstants.param_bizType, UnionPayConstantUtil.BIZ_TYPE_DEFAULT); //业务类型 默认 contentData.put(SDKConstants.param_channelType, UnionPayConstantUtil.CHANNEL_TYPE_PC); //渠道类型 contentData.put(SDKConstants.param_certType, UnionPayConstantUtil.CERT_TYPE); //01:敏感信息加密公钥(只有01可用) contentData.put(SDKConstants.param_merId, UnionPayConstantUtil.MER_ID); //商户号码(商户号码777290058110097仅做为测试调通交易使用,该商户号配置了需要对敏感信息加密)测试时请改成自己申请的商户号,【自己注册的测试777开头的商户号不支持代收产品】 contentData.put(SDKConstants.param_accessType, UnionPayConstantUtil.ACCESS_TYPE_DEFAULT); //接入类型,商户接入固定填0,不需修改 //----------------------------------------------- Date date = new Date(); SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss"); String txnTime = sdf.format(date); contentData.put(SDKConstants.param_orderId, txnTime + "123"); //商户订单号,8-40位数字字母,不能含“-”或“_”,可以自行定制规则 contentData.put(SDKConstants.param_txnTime, txnTime); //订单发送时间,格式为YYYYMMDDhhmmss,必须取当前时间,否则会报txnTime无效 //账号类型 Map , String> reqData = AcpService.sign(contentData, UnionPayConstantUtil.ENCODING); //报文中certId,signature的值是在signData方法中获取并自动赋值的,只要证书配置正确即可。 String requestBackUrl = SDKConfig.getConfig().getBackRequestUrl(); //交易请求url从配置文件读取对应属性文件acp_sdk.properties中的 acpsdk.backTransUrl Map , String> rspData = AcpService.post(reqData, requestBackUrl, UnionPayConstantUtil.ENCODING); //发送请求报文并接受同步应答(默认连接超时时间30秒,读取返回结果超时时间30秒);这里调用signData之后,调用submitUrl之前不能对submitFromData中的键值对做任何修改,如果修改会导致验签不通过 if (!rspData.isEmpty()) { if (AcpService.validate(rspData, UnionPayConstantUtil.ENCODING)) { LogUtil.writeLog("验证签名成功"); String respCode = rspData.get("respCode"); if (("00").equals(respCode)) { int resultCode = AcpService.updateEncryptCert(rspData, UnionPayConstantUtil.ENCODING); if (resultCode == 1) { LogUtil.writeLog("加密公钥更新成功"); } else if (resultCode == 0) { LogUtil.writeLog("加密公钥无更新"); } else { LogUtil.writeLog("加密公钥更新失败"); } } else { //其他应答码为失败请排查原因 //TODO } } else { LogUtil.writeErrorLog("验证签名失败"); //TODO 检查验证签名失败的原因 } } else { //未返回正确的http状态 LogUtil.writeErrorLog("未获取到返回报文或返回http状态码非200"); } } /** * 获取请求参数中所有的信息 * 当商户上送frontUrl或backUrl地址中带有参数信息的时候, * 这种方式会将url地址中的参数读到map中,会导多出来这些信息从而致验签失败,这个时候可以自行修改过滤掉url中的参数或者使用getAllRequestParamStream方法。 * * @param request * @return */ public static Map , String> getAllRequestParam( final HttpServletRequest request) { Map , String> res = new HashMap , 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; } /** * 获取请求参数中所有的信息。 * 非struts可以改用此方法获取,好处是可以过滤掉request.getParameter方法过滤不掉的url中的参数。 * struts可能对某些content-type会提前读取参数导致从inputstream读不到信息,所以可能用不了这个方法。理论应该可以调整struts配置使不影响,但请自己去研究。 * 调用本方法之前不能调用req.getParameter("key");这种方法,否则会导致request取不到输入流。 * * @param request * @return */ public static Map , String> getAllRequestParamStream( final HttpServletRequest request) { Map , String> res = new HashMap , String>(); try { String notifyStr = new String(IOUtils.toByteArray(request.getInputStream()), UnionPayConstantUtil.ENCODING); LogUtil.writeLog("收到通知报文:" + notifyStr); String[] kvs = notifyStr.split("&"); for (String kv : kvs) { String[] tmp = kv.split("="); if (tmp.length >= 2) { String key = tmp[0]; String value = URLDecoder.decode(tmp[1], UnionPayConstantUtil.ENCODING); res.put(key, value); } } } catch (UnsupportedEncodingException e) { LogUtil.writeLog("getAllRequestParamStream.UnsupportedEncodingException error: " + e.getClass() + ":" + e.getMessage()); } catch (IOException e) { LogUtil.writeLog("getAllRequestParamStream.IOException error: " + e.getClass() + ":" + e.getMessage()); } return res; } public static void main(String[] arg) { Date date = new Date(); SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS"); String format = sdf.format(date); System.out.println(format); } }