银联支付之在线网关支付
准备
中国银联.开放平台 https://open.unionpay.com/tjweb/index
下载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 {
@Autowired
private PaymentInfoServiceImpl paymentInfoService;
@Autowired
private PayImplService payService;
/**
* 生成订单支付信息,返回与此支付信息关联的随机token
*
* @param paymentInfo
* @return
*/
@RequestMapping("/generatePaymentInfo")
@ResponseBody
public 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 {
@Autowired
private PaymentTypeDao paymentTypeDao;
@Autowired
private YinLianPay yinLianPay;
@Override
public 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);
}
@Override
public 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"); //交易子类型 默认00
data.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)) {
//交易成功,更新商户订单状态
//TODO
log.info("交易成功,更新商户订单状态");
} else if ("03".equals(origRespCode) || "04".equals(origRespCode) || "05".equals(origRespCode)) {
//需再次发起交易状态查询交易
//TODO
log.info("需再次发起交易状态查询交易");
} else {
//
//TODO
log.info("其他应答码为失败请排查原因");
}
} else {//查询交易本身失败,或者未查到原交易,检查查询交易报文要素
//TODO
log.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 {
@Autowired
private PaymentInfoDao paymentInfoDao;
@Autowired
private PayImplService payService;
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Override
public 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);
}
@Override
public 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();
}
}
@Override
public 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);
}
@Override
public 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 {
@Override
public 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.frontTransUrl
String 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 {
@Autowired
private 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";
@Autowired
private 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;
}
@Override
public 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前台接收报文返回结束");
}
}
@Override
public 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 {
@Override
public 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.properties
INFO 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: 8888
spring:
application:
name: pay-service
redis:
host: 127.0.0.1
password:
port: 6379
#数据库连接信息
datasource:
url: jdbc:mysql://127.0.0.1:3306/pay?serverTimezone=UTC
username: root
password: 123456
driver-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
接口查询订单状态
商户测试中心查询
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/137039.html