银联支付之在线网关支付
银联支付之在线网关支付
- 准备
- 下载SDK
- 下载证书
- SDK示例Demo
- 修改acp_sdk.properties
- 启动项目
- 测试
- 项目集成
- 添加依赖
- 集成SDK
- 支付类型与支付信息对象
- Mapper接口
- 支付接口
- 支付信息
- 真正支付接口
- 回调接口
- 加载配置信息
- 添加OR修改配置
- 添加数据库表
- 添加支付类型信息
- 执行测试
- 生成支付信息
- 开始支付
- 支付状态查询
- 商户测试中心查询
准备
中国银联.开放平台 https://open.unionpay.com/tjweb/index
选择业务产品.全渠道.在线网关支付
下载SDK
在线网关支付页面下载SDK
选择合适的SDK,导入开发工具
下载证书
在线网关支付页面选择网关测试进入商户入网测试中心,在测试参数项下载证书
SDK示例Demo
SDK示例Demo导入开发工具 ,并进行相应配置
修改acp_sdk.properties
配置项目访问路径配置4个证书存放路径
#前台通知地址,填写银联前台通知的地址,必须外网能访问
acpsdk.frontUrl=http://localhost:9999/ACPSample_B2C/frontRcvResponse#########################入网测试环境签名证书配置 ################################
# 签名证书路径,必须使用绝对路径,如果不想使用绝对路径,可以自行实现相对路径获取证书的方法;测试证书所有商户共用开发包中的测试签名证书,生产环境请从cfca下载得到。
acpsdk.signCert.path=D:/certs/acp_test_sign.pfx##########################加密证书配置################################
# 敏感信息加密证书路径(商户号开通了商户对敏感信息加密的权限,需要对 卡号accNo,pin和phoneNo,cvn2,expired加密(如果这些上送的话),对敏感信息加密使用)
acpsdk.encryptCert.path=d:/certs/acp_test_enc.cer##########################验签证书配置################################
# 验签中级证书路径(银联提供)
acpsdk.middleCert.path=D:/certs/acp_test_middle.cer# 验签根证书路径(银联提供)
acpsdk.rootCert.path=D:/certs/acp_test_root.cer
启动项目
配置后启动项目,示例中包含了各种Demo,后续项目集成可参考
测试
测试消费样例,卡号与支付密码在测试参数处
返回商户
项目集成
参考示例Demo集成项目即可
添加依赖
<!-- 集成web--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><scope>runtime</scope></dependency><!-- 集成redis --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><!-- 集成mysql--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope></dependency><!-- 集成lombok框架 --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><!-- 集成mybatis-plus框架 --><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.4.2</version></dependency><!-- 集成fastjson框架 --><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.72</version></dependency><!-- 集成commons工具类 --><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId><version>3.10</version></dependency><!-- SDK中SecureUtil类需要 --><dependency><groupId>org.bouncycastle</groupId><artifactId>bcprov-jdk15on</artifactId><version>1.68</version></dependency>
集成SDK
将SDK,如Java Version SDK (通用版)\ACPSample_B2C\src\com\unionpay\acp\sdk目录下的类拷贝至项目
支付类型与支付信息对象
@Getter
@Setter
public class PaymentType {/*** 主键*/@TableId(type = IdType.AUTO)private Long id;/*** 支付平台*/private String typeName;/*** 同步通知*/private String frontUrl;/*** 异步通知*/private String backUrl;/*** 商户id*/private String merchantId;/*** 创建时间*/private Timestamp createDate;/*** 修改时间*/private Timestamp updateDate;
}
@Getter
@Setter
public class PaymentInfo {/*** 主键*/@TableId(type = IdType.AUTO)private Long id;/*** 支付类型*/private Long typeId;/*** 订单编号*/private String orderId;/*** 第三方平台支付id*/private String platformorderId;/*** 价格 以分为单位*/private Long price;/*** 支付来源*/private String source;/*** 支付*/private Integer state;/*** 支付报文*/private String payMessage;/*** 创建时间*/private Timestamp createDate;/*** 修改时间*/private Timestamp updateDate;
}
Mapper接口
@Mapper
public interface PaymentInfoDao extends BaseMapper<PaymentInfo> {}
@Mapper
public interface PaymentTypeDao extends BaseMapper<PaymentType> {}
支付接口
@Controller
public class PayController {@Autowiredprivate PaymentInfoServiceImpl paymentInfoService;@Autowiredprivate PayImplService payService;/*** 生成订单支付信息,返回与此支付信息关联的随机token** @param paymentInfo* @return*/@RequestMapping("/generatePaymentInfo")@ResponseBodypublic Object generatePaymentInfo(@RequestBody PaymentInfo paymentInfo) {return paymentInfoService.generatePaymentInfo(paymentInfo);}/*** 跳转支付网关** @param request* @param token 订单支付信息关联的token* @param resp* @throws IOException*/@RequestMapping("/payGateway")public void payGateway(HttpServletRequest request, String token, HttpServletResponse resp) {paymentInfoService.getPaymentInfoToken(token,request,resp);}/*** 查询订单支付状态* @param orderId* @param txnTime* @param resp*/@RequestMapping("/queryPayStatus")public void queryPayStatus(String orderId, String txnTime, HttpServletResponse resp) {payService.queryPayStatus(orderId, txnTime, resp);}}
public interface PayService {/*** 支付接口* @param paymentInfo* @return*/public String pay(PaymentInfo paymentInfo);/*** 查询订单支付状态* @param orderId* @param txnTime* @param resp*/void queryPayStatus(String orderId,String txnTime, HttpServletResponse resp);
}
@Slf4j
@Service
public class PayImplService implements PayService {@Autowiredprivate PaymentTypeDao paymentTypeDao;@Autowiredprivate YinLianPay yinLianPay;@Overridepublic String pay(PaymentInfo paymentInfo) {Long typeId = paymentInfo.getTypeId();PaymentType paymentType = paymentTypeDao.selectById(typeId);if (paymentType == null) {log.error("PaymentType is null");return null;}String typeName = paymentType.getTypeName();PayAdaptService payAdaptService = null;//依据支付类型分发支付接口switch (typeName) {case "yinlianPay":payAdaptService = yinLianPay;break;
// case "zhifubaoPay":
// payAdaptService = zhifubaoPay;
// break;default:break;}return payAdaptService.pay(paymentInfo, paymentType);}@Overridepublic void queryPayStatus(String orderId, String txnTime, HttpServletResponse resp) {Map<String, String> data = new HashMap<String, String>();/***银联全渠道系统,产品参数,除了encoding自行选择外其他不需修改***/data.put("version", DemoBase.version); //版本号data.put("encoding", DemoBase.encoding); //字符集编码 可以使用UTF-8,GBK两种方式data.put("signMethod", SDKConfig.getConfig().getSignMethod()); //签名方法data.put("txnType", "00"); //交易类型 00-默认data.put("txnSubType", "00"); //交易子类型 默认00data.put("bizType", "000201"); //业务类型 B2C网关支付,手机wap支付/***商户接入参数***/data.put("merId", "777290058110048"); //商户号码,请改成自己申请的商户号或者open上注册得来的777商户号测试data.put("accessType", "0"); //接入类型,商户接入固定填0,不需修改/***要调通交易以下字段必须修改***/data.put("orderId", orderId); //****商户订单号,每次发交易测试需修改为被查询的交易的订单号data.put("txnTime", txnTime); //****订单发送时间,每次发交易测试需修改为被查询的交易的订单发送时间/**请求参数设置完毕,以下对请求参数进行签名并发送http post请求,接收同步应答报文------------->**/Map<String, String> reqData = AcpService.sign(data, DemoBase.encoding);//报文中certId,signature的值是在signData方法中获取并自动赋值的,只要证书配置正确即可。String url = SDKConfig.getConfig().getSingleQueryUrl();// 交易请求url从配置文件读取对应属性文件acp_sdk.properties中的 acpsdk.singleQueryUrl//这里调用signData之后,调用submitUrl之前不能对submitFromData中的键值对做任何修改,如果修改会导致验签不通过Map<String, String> rspData = AcpService.post(reqData, url, DemoBase.encoding);/**对应答码的处理,请根据您的业务逻辑来编写程序,以下应答码处理逻辑仅供参考------------->**///应答码规范参考open.unionpay.com帮助中心 下载 产品接口规范 《平台接入接口规范-第5部分-附录》if (!rspData.isEmpty()) {if (AcpService.validate(rspData, DemoBase.encoding)) {LogUtil.writeLog("验证签名成功");if ("00".equals(rspData.get("respCode"))) {//如果查询交易成功//处理被查询交易的应答码逻辑String origRespCode = rspData.get("origRespCode");if ("00".equals(origRespCode)) {//交易成功,更新商户订单状态//TODOlog.info("交易成功,更新商户订单状态");} else if ("03".equals(origRespCode) || "04".equals(origRespCode) || "05".equals(origRespCode)) {//需再次发起交易状态查询交易//TODOlog.info("需再次发起交易状态查询交易");} else {////TODOlog.info("其他应答码为失败请排查原因");}} else {//查询交易本身失败,或者未查到原交易,检查查询交易报文要素//TODOlog.info("查询交易本身失败,或者未查到原交易,检查查询交易报文要素");}} else {log.info("验证签名失败");//TODO 检查验证签名失败的原因}} else {//未返回正确的http状态log.info("未获取到返回报文或返回http状态码非200");}String reqMessage = DemoBase.genHtmlResult(reqData);String rspMessage = DemoBase.genHtmlResult(rspData);try {resp.setContentType("text/html;charset=utf-8");resp.getWriter().write("</br>请求报文:<br/>" + reqMessage + "<br/>" + "应答报文:</br>" + rspMessage + "");} catch (IOException e) {e.printStackTrace();}}
}
支付信息
public interface PaymentInfoService {/*** 生成订单支付信息,返回与此支付信息关联的随机token* @param paymentInfo* @return*/public Map<String, Object> generatePaymentInfo(@RequestBody PaymentInfo paymentInfo);/*** 通过token获取支付信息* @param token* @return*/public void getPaymentInfoToken(String token, HttpServletRequest request, HttpServletResponse resp);/*** 使用orderId查找支付信息** @param orderId* @return*/public Map<String, Object> getByOrderIdPayInfo(@RequestParam("orderId") String orderId);/*** 使用token查找支付信息** @param paymentInfo* @return*/public Map<String, Object> updatePayInfo(@RequestBody PaymentInfo paymentInfo);
}
@Slf4j
@Service
public class PaymentInfoServiceImpl implements PaymentInfoService {@Autowiredprivate PaymentInfoDao paymentInfoDao;@Autowiredprivate PayImplService payService;@Autowiredprivate StringRedisTemplate stringRedisTemplate;@Overridepublic Map<String, Object> generatePaymentInfo(@RequestBody PaymentInfo paymentInfo) {paymentInfo.setCreateDate(new Timestamp(System.currentTimeMillis()));paymentInfo.setUpdateDate(new Timestamp(System.currentTimeMillis()));int insert = paymentInfoDao.insert(paymentInfo);if (insert != 1 || paymentInfo.getId() == null) {return ResponseUtils.back(0, "系统错误");}String token = "pay-" + UUID.randomUUID();stringRedisTemplate.opsForValue().set(token, paymentInfo.getId() + "");stringRedisTemplate.expire(token, 900, TimeUnit.SECONDS);return ResponseUtils.back(1, token);}@Overridepublic void getPaymentInfoToken(String token, HttpServletRequest request, HttpServletResponse resp) {resp.setContentType("text/html;charset=utf-8");PrintWriter out = null;//从redis获取支付信息String payInfoId = stringRedisTemplate.opsForValue().get(token);Long newPayInfoId = Long.parseLong(payInfoId);// redis信息中取出支付id查询数据库PaymentInfo paymentInfo = paymentInfoDao.selectById(newPayInfoId);//开始支付String html = payService.pay(paymentInfo);try {out = resp.getWriter();} catch (IOException e) {e.printStackTrace();} finally {out.println(html);out.close();}}@Overridepublic Map<String, Object> getByOrderIdPayInfo(@RequestParam("orderId") String orderId) {QueryWrapper<PaymentInfo> queryWrapper = new QueryWrapper<>();queryWrapper.eq("order_id", orderId);PaymentInfo paymentInfo = paymentInfoDao.selectOne(queryWrapper);if (paymentInfo == null) {return ResponseUtils.back(0, "未查询到支付信息");}return ResponseUtils.back(1, paymentInfo);}@Overridepublic Map<String, Object> updatePayInfo(@RequestBody PaymentInfo paymentInfo) {paymentInfoDao.updateById(paymentInfo);return ResponseUtils.back(1, "");}}
真正支付接口
public interface PayAdaptService {/*** 通用支付接口适配器* @param paymentInfo* @param paymentType* @return*/public String pay(PaymentInfo paymentInfo,PaymentType paymentType );
}
@Slf4j
@Service
public class YinLianPay implements PayAdaptService {@Overridepublic String pay(PaymentInfo paymentInfo, PaymentType paymentType) {return setPay(paymentType, paymentInfo);}public String setPay(PaymentType paymentType, PaymentInfo paymentInfo) {Map<String, String> requestData = new HashMap<String, String>();/***银联全渠道系统,产品参数,除了encoding自行选择外其他不需修改***/requestData.put("version", DemoBase.version); //版本号,全渠道默认值requestData.put("encoding", DemoBase.encoding); //字符集编码,可以使用UTF-8,GBK两种方式requestData.put("signMethod", SDKConfig.getConfig().getSignMethod()); //签名方法requestData.put("txnType", "01"); //交易类型 ,01:消费requestData.put("txnSubType", "01"); //交易子类型, 01:自助消费requestData.put("bizType", "000201"); //业务类型,B2C网关支付,手机wap支付requestData.put("channelType", "07"); //渠道类型,这个字段区分B2C网关支付和手机wap支付;07:PC,平板 08:手机/***商户接入参数***/requestData.put("merId", paymentType.getMerchantId()); //商户号码,请改成自己申请的正式商户号或者open上注册得来的777测试商户号requestData.put("accessType", "0"); //接入类型,0:直连商户requestData.put("orderId", paymentInfo.getOrderId()); //商户订单号,8-40位数字字母,不能含“-”或“_”,可以自行定制规则requestData.put("txnTime", DemoBase.getCurrentTime()); //订单发送时间,取系统时间,格式为yyyyMMddHHmmss,必须取当前时间,否则会报txnTime无效requestData.put("currencyCode", "156"); //交易币种(境内商户一般是156 人民币)requestData.put("txnAmt", paymentInfo.getPrice().toString()); //交易金额,单位分,不要带小数点//requestData.put("reqReserved", "透传字段"); //请求方保留域,如需使用请启用即可;透传字段(可以实现商户自定义参数的追踪)本交易的后台通知,对本交易的交易状态查询交易、对账文件中均会原样返回,商户可以按需上传,长度为1-1024个字节。出现&={}[]符号时可能导致查询接口应答报文解析失败,建议尽量只传字母数字并使用|分割,或者可以最外层做一次base64编码(base64编码之后出现的等号不会导致解析失败可以不用管)。requestData.put("riskRateInfo", "{commodityName=测试商品名称}");//前台通知地址 (需设置为外网能访问 http https均可),支付成功后的页面 点击“返回商户”按钮的时候将异步通知报文post到该地址//如果想要实现过几秒中自动跳转回商户页面权限,需联系银联业务申请开通自动返回商户权限//异步通知参数详见open.unionpay.com帮助中心 下载 产品接口规范 网关支付产品接口规范 消费交易 商户通知requestData.put("frontUrl", paymentType.getFrontUrl());//后台通知地址(需设置为【外网】能访问 http https均可),支付成功后银联会自动将异步通知报文post到商户上送的该地址,失败的交易银联不会发送后台通知//后台通知参数详见open.unionpay.com帮助中心 下载 产品接口规范 网关支付产品接口规范 消费交易 商户通知//注意:1.需设置为外网能访问,否则收不到通知 2.http https均可 3.收单后台通知后需要10秒内返回http200或302状态码// 4.如果银联通知服务器发送通知后10秒内未收到返回状态码或者应答码非http200,那么银联会间隔一段时间再次发送。总共发送5次,每次的间隔时间为0,1,2,4分钟。// 5.后台通知地址如果上送了带有?的参数,例如:http://abc/web?a=b&c=d 在后台通知处理程序验证签名之前需要编写逻辑将这些字段去掉再验签,否则将会验签失败requestData.put("backUrl", paymentType.getBackUrl());// 订单超时时间。// 超过此时间后,除网银交易外,其他交易银联系统会拒绝受理,提示超时。 跳转银行网银交易如果超时后交易成功,会自动退款,大约5个工作日金额返还到持卡人账户。// 此时间建议取支付时的北京时间加15分钟。// 超过超时时间调查询接口应答origRespCode不是A6或者00的就可以判断为失败。requestData.put("payTimeout", new SimpleDateFormat("yyyyMMddHHmmss").format(System.currentTimeMillis() + 15 * 60 * 1000));////// 报文中特殊用法请查看 special_use_purchase.txt/////**请求参数设置完毕,以下对请求参数进行签名并生成html表单,将表单写入浏览器跳转打开银联页面**/Map<String, String> submitFromData = AcpService.sign(requestData, DemoBase.encoding); //报文中certId,signature的值是在signData方法中获取并自动赋值的,只要证书配置正确即可。String requestFrontUrl = SDKConfig.getConfig().getFrontRequestUrl(); //获取请求银联的前台地址:对应属性文件acp_sdk.properties文件中的acpsdk.frontTransUrlString html = AcpService.createAutoFormHtml(requestFrontUrl, submitFromData, DemoBase.encoding); //生成自动跳转的Html表单log.info("-------------------------报文开始------------------------");LogUtil.writeLog("打印请求HTML,此为请求报文,为联调排查问题的依据:" + html);log.info("-------------------------报文结束------------------------");//将生成的html写到浏览器中完成自动跳转打开银联支付页面;这里调用signData之后,将html写到浏览器跳转到银联页面之前均不能对html中的表单项的名称和值进行修改,如果修改会导致验签不通过return html;}
}
回调接口
@RequestMapping("/pay/callback")
@RestController
public class YinLianCallbackController {@Autowiredprivate YinLianCallbackService yinLianCallbackService;/*** 功能说明:同步回调** @return*/@RequestMapping("/syn")public String syn(HttpServletRequest request) {Map<String, String> synResultMap = yinLianCallbackService.syn(request);String encoding = request.getParameter(SDKConstants.param_encoding);if (!AcpService.validate(synResultMap, encoding)) {LogUtil.writeLog("验证签名结果[失败].");return "支付失败,支付金额: " + Double.parseDouble(synResultMap.get("txnAmt")) / 100 + " , 订单号: " + synResultMap.get("orderId");}LogUtil.writeLog("验证签名结果[成功].");return "支付金额: " + Double.parseDouble(synResultMap.get("txnAmt")) / 100 + " , 订单号: " + synResultMap.get("orderId");}/*** 功能说明:异步回调** @return*/@RequestMapping("/asyn")public String asyn(HttpServletRequest request) {return yinLianCallbackService.asyn(request);}}
public interface CallbackService {/*** 同步回调* * @return*/public Map<String, String> syn(HttpServletRequest request);/*** 异步回调* * @return*/public String asyn(HttpServletRequest request);
}
@Slf4j
@Service
public class YinLianCallbackService implements CallbackService {private static final String PAY_SUCCESS = "ok";private static final String PAY_FAIL = "fail";@Autowiredprivate PaymentInfoDao paymentInfoDao;public static Map<String, String> valideData(final HttpServletRequest req, String encoding) {// 获取银联通知服务器发送的后台通知参数Map<String, String> reqParam = getAllRequestParam(req);LogUtil.printRequestLog(reqParam);Map<String, String> valideData = null;if (null != reqParam && !reqParam.isEmpty()) {Iterator<Entry<String, String>> it = reqParam.entrySet().iterator();valideData = new HashMap<>(reqParam.size());while (it.hasNext()) {Entry<String, String> e = it.next();String key = e.getKey();String value = e.getValue();try {value = new String(value.getBytes(encoding), encoding);} catch (Exception e2) {// TODO: handle exception}valideData.put(key, value);}}return valideData;}/*** 获取请求参数中所有的信息** @param request* @return*/public static Map<String, String> getAllRequestParam(final HttpServletRequest request) {Map<String, String> res = new HashMap<>();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;}@Overridepublic Map<String, String> syn(HttpServletRequest req) {log.info("FrontRcvResponse前台接收报文返回开始");try {String encoding = req.getParameter(SDKConstants.param_encoding);Map<String, String> valideData = valideData(req, encoding);return valideData;} catch (Exception e) {log.info("FrontRcvResponse前台接收报文返回ERROR:{}", e);return null;} finally {log.info("FrontRcvResponse前台接收报文返回结束");}}@Overridepublic String asyn(HttpServletRequest req) {log.info("BackRcvResponse接收后台通知开始");String encoding = req.getParameter(SDKConstants.param_encoding);Map<String, String> valideData = valideData(req, encoding);// 重要!验证签名前不要修改reqParam中的键值对的内容,否则会验签不过if (!AcpService.validate(valideData, encoding)) {log.info("验证签名结果[失败]");// 验签失败,需解决验签问题return PAY_FAIL;}log.info("验证签名结果[成功]");// 【注:为了安全验签成功才应该写商户的成功处理逻辑】交易成功,更新商户订单状态String orderId = valideData.get("orderId"); // 获取后台通知的数据,其他字段也可用类似方式获取QueryWrapper<PaymentInfo> queryWrapper = new QueryWrapper<>();queryWrapper.eq("order_id", orderId);PaymentInfo paymentInfo = paymentInfoDao.selectOne(queryWrapper);if (paymentInfo == null) {log.error("异步通知,订单id:{},无此订单", orderId);return PAY_FAIL;}Integer state = paymentInfo.getState();if (state.equals(1)) {log.error("异步通知,订单id:{},状态已经为已支付,不能在修改为已支付.", orderId);return PAY_SUCCESS;}paymentInfo.setPlatformorderId(valideData.get("queryId"));paymentInfo.setUpdateDate(new Timestamp(System.currentTimeMillis()));paymentInfo.setPayMessage(JSON.toJSONString(valideData));paymentInfo.setState(1);paymentInfoDao.updateById(paymentInfo);log.error("异步通知,订单id:{},状态已经为已支付,修改成功。", orderId);// 返回给银联服务器http 200 状态码return PAY_SUCCESS;}}
加载配置信息
@Component
@Slf4j
public class AutoLoadRunner implements CommandLineRunner {@Overridepublic void run(String... args) throws Exception {SDKConfig.getConfig().loadPropertiesFromSrc();log.info(">>>>>>>>>>>>>>>服务启动执行,执行加载数据等操作<<<<<<<<<<<<<");}
}
INFO 21948 --- [ restartedMain] cn.ybzy.pay.PayApplication : Started PayApplication in 4.861 seconds (JVM running for 6.515)INFO 21948 --- [ restartedMain] ACP_SDK_LOG : 从classpath: /C:/Users/admin/Desktop/pay/target/classes/ 获取属性文件acp_sdk.propertiesINFO 21948 --- [ restartedMain] ACP_SDK_LOG : 开始从属性文件中加载配置项INFO 21948 --- [ restartedMain] ACP_SDK_LOG : 配置项:私钥签名证书路径==>acpsdk.signCert.path==>D:/certs/acp_test_sign.pfx 已加载INFO 21948 --- [ restartedMain] ACP_SDK_LOG : 配置项:私钥签名证书密码==>acpsdk.signCert.pwd 已加载INFO 21948 --- [ restartedMain] ACP_SDK_LOG : 配置项:私钥签名证书类型==>acpsdk.signCert.type==>PKCS12 已加载INFO 21948 --- [ restartedMain] ACP_SDK_LOG : 配置项:敏感信息加密证书==>acpsdk.encryptCert.path==>d:/certs/acp_test_enc.cer 已加载INFO 21948 --- [ restartedMain] cn.ybzy.pay.load.AutoLoadRunner : >>>>>>>>>>>>>>>服务启动执行,执行加载数据等操作<<<<<<<<<<<<<
添加OR修改配置
修改acp_sdk.properties
将SDK示例项目配置文件复制到项目中,修改外网能访问的前台通知地址
#前台通知地址,填写银联前台通知的地址,必须外网能访问
acpsdk.frontUrl=http://jackchen.imwork.net
修改application.yml
server:port: 8888spring:application:name: pay-serviceredis:host: 127.0.0.1password:port: 6379#数据库连接信息datasource:url: jdbc:mysql://127.0.0.1:3306/pay?serverTimezone=UTCusername: rootpassword: 123456driver-class-name: com.mysql.jdbc.Driver
添加数据库表
CREATE TABLE `payment_info` (`id` int(11) NOT NULL AUTO_INCREMENT,`type_id` int(11) DEFAULT NULL,`order_id` varchar(255) DEFAULT NULL,`platformorder_id` varchar(255) DEFAULT NULL,`price` decimal(10,4) DEFAULT NULL,`source` varchar(255) DEFAULT NULL,`state` int(255) DEFAULT NULL,`pay_message` text,`create_date` datetime DEFAULT NULL,`update_date` datetime DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=34 DEFAULT CHARSET=utf8
CREATE TABLE `payment_type` (`id` int(11) NOT NULL AUTO_INCREMENT,`type_name` varchar(255) DEFAULT NULL,`front_url` varchar(255) DEFAULT NULL,`back_url` varchar(255) DEFAULT NULL,`merchant_id` varchar(255) DEFAULT NULL,`create_date` datetime DEFAULT NULL,`update_date` datetime DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8
添加支付类型信息
支付类型表添加数据作为支付类型的配置, 回调地址需被外网访问
INSERT INTO `pay`.`payment_type`(`id`, `type_name`, `front_url`, `back_url`, `merchant_id`, `create_date`, `update_date`) VALUES (1, 'yinlianPay', 'http://jackchen.imwork.net/pay/callback/syn', 'http://jackchen.imwork.net/pay/callback/asyn', '777290058110048', '2021-08-30 16:25:51', NULL);
执行测试
生成支付信息
请求http://jackchen.imwork.net/generatePaymentInfo
接口,生成支付信息,得到token
开始支付
以得到的token访问http://jackchen.imwork.net/payGateway?token=pay-3bb87b52-35e9-4592-ae72-1516bebd8754
接口跳转到支付网关
支付成功,返回商户
支付状态查询
访问https://jackchen.imwork.net/queryPayStatus?orderId=qwesdfsfdfds431&txnTime=20210830180004
接口查询订单状态
商户测试中心查询
商户测试中心查询交易信息
可看到很重要的2个信息:回调地址
银联支付之在线网关支付相关推荐
- 银联在线网关支付,快速接入指南
银联在线支付网关是中国银联联合各商业银行为持卡人提供的集成化.综合性互联网支付工具,主要支持输入卡号付款.用户登录支付.网银支付.迷你付(IC卡支付)等多种支付方式,为持卡人提供境内外网上购物支付服务 ...
- PHP接入银联在线网关支付
网站支付:一般接入微信支付.支付宝支付.银联支付,本文介绍的是如何接入中国银联在线网关支付 银联介绍 银联在线支付网关是中国银联联合各商业银行为持卡人提供的集成化.综合性互联网支付工具,主要支持输入卡 ...
- 银联支付(一)申请测试环境,并运行测试demo(在线网关支付)
1.注册账号 点击进入银联支付开放平台地址 注册账号 选择普通用户登录账号 登录后点击前往商户测试中心 进入后产品列表中选择在线网关支付,然后要点击操作中的集成测试申请支付权限,10分钟后生效(这里如 ...
- 银联批量结算 java_银联在线 网关支付 (JAVA版)
这一版本的编写是在我上一次博客的基础上写的,有不懂得童鞋可以先看下我的原先在线支付的博客,熟悉下:http://blog.csdn.net/yulei_qq/article/details/45197 ...
- java对接银联在线网关支付(已测试)
访问连接下载银联SDK https://open.unionpay.com/tjweb/acproduct/list?apiSvcId=448&index=5 1.拿到测试证书(下载银联的SD ...
- 银联在线 网关支付(中国银联全渠道系统商户接入 测试指引-银联网关支付产品)
https://blog.csdn.net/yulei_qq/article/details/49025045 中国银联全渠道系统商户接入 测试指引-银联网关支付产品 https://blog.csd ...
- 银联在线支付 java_银联在线 网关支付 (JAVA版)
这一版本的编写是在我上一次博客的基础上写的,有不懂得童鞋可以先看下我的原先在线支付的博客,熟悉下:http://blog.csdn.net/yulei_qq/article/details/45197 ...
- 西米支付:支付宝/微信支付/银联支付通道的接入介绍
本文以电脑网站支付为例,着重对第三方支付通道的接入进行了分析,包括支付宝支付接入.微信支付接入及银联支付接入. 1.支付宝支付接入 支付宝支付能力主要有当面付.刷脸付.App支付.手机网站支付.电脑网 ...
- 银联网关支付demo
对接银联支付接口,通过一个例子向商家支付金额. 1.首先在银联开放平台下载支付Demo,点击链接 或者直接拿我修改好的demo,链接: https://pan.baidu.com/s/1FywgOf5 ...
- springMvc之银联网关支付(详解)
一.申请与配置 1.了解产品和申请测试账号 无论接入什么平台,首先就是了解自己的需求,然后从开发文档中查看接入方式. 进入银联技术开放平台 点击 点击我是商户,然后进行注册.(注意:注册或登录时用IE ...
最新文章
- Android Handler杂记(结合别人的blog)
- 为什么本地sqlservr.exe进程占用内存如此之大?
- BZOJ 2115 [Wc2011] Xor ——线性基
- 飞鸽传书2011看到一篇国外的博客
- 收藏 | 10个重要问题概览Transformer全部内容
- maven asm 依赖配置_Maven教程_v20201119
- python数据库去重复_python string 去重复数据库
- springboot添加webmagic_SpringBoot+Solr + webmagic JD商品爬取数据,放入solr中做搜索
- 采购与供应链管理 读书笔记一
- spring data jpa 使用@Query 不确定参数查询
- matlab coder 安装,MATLAB Coder
- Nagios下载安装配置
- win10触屏输入法_触摸屏笔记本Win10电脑的校准技巧
- Python Cannot open E:\Python36\Scripts\pip-script.py
- OSWatcher的安装使用
- 什么是Excel的快速编号功能?
- 最全的硬盘问题及修复方法
- 查找新式BADI的方法
- Leetcode917:仅仅反转字母(simple)
- 概率公理化定义的理解