SpringBoot开发银行聚合支付扫码业务,学到就是赚到,不用花钱可以学到精髓
一、文档需求
这是开始的第一步,也是很重要的一步,如果你连文档都看不懂,就别想着继续下一步操作了。
其中还会涉及到与银行技术交接这一过程,话不多说

返回结果:

订单接口:这是轮循时候使用到的,当用户在支付中或者待支付,不断查询订单,直到成功或者超时

返回信息:

字段分析:商户号、终端号、商户key是银行提供的,其中商户key文档没出现,这些测试的数据都是需要对方技术提供的
批次号、系统跟踪号、商户订单号、交易金额、二维码信息、随机字符串、签名,根据文档,这是必须要传的,不要传的像
收银员编号、订单备注、缴费类型、缴费说明、经度、纬度,可以不传
商户订单号:文档没有描述,随机生成32唯一不重复的订单号即可,这些肯定是唯一的
随机字符串:32位不重复的,网上有很多种生成方式
交易金额:银行那边是以分为单位的,我们使用postman测试,输入1,其实他们那边进行了转换,也就是0.1元
二维码信息:我们可以打开支付宝或微信,输入有效的支付码进行付款,必须是有效的,否则按照业务逻辑,也是返回二维码一些错误信息
下面有三个字段是需要注意的,文档中有说明!
批次号:批次号,文档有说明,如下图,使用商户号和终端号调用此接口,在返回结果中会有批次号

系统跟踪号:如下图,详细参考代码

签名:签名如下图,具体什么是签名,根据文档一步一步操作,结合代码会慢慢理解

二、代码部分
1、controller层
package com.example.parkingfees.controller;import com.alibaba.fastjson.JSONObject;
import com.example.parkingfees.service.MicroPayService;
import com.example.parkingfees.service.SignService;
import com.example.parkingfees.service.TraceNoService;
import com.example.parkingfees.utils.HttpsUtil;
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import java.util.*;/*** @author 刘磊* @date 2020/9/3 13:47*/
@RestController
@RequestMapping(value = "/micropay")
public class MicroPayController {@Autowiredprivate SignService signService;@Autowiredprivate MicroPayService microPayService;@Autowiredprivate TraceNoService traceNoService;/*** 需求:商家扫取客户微信、支付宝、银联向客户收款(批次号是必须的属性)* @param cashierNo 收银员编号--否* @param transAmount 交易⾦额--必须* @param payCode ⼆维码信息--必须* @param remark 订单备注--否* @param ymisFlag 缴费类型--否* @param ymisRemark 缴费说明--否* @param longitude 经度--否* @param latitude 纬度--否*/@RequestMapping(value = "/queryMicropay",method = RequestMethod.POST)public Object microPayMethod(String cashierNo, Integer transAmount, String payCode,String remark, String ymisFlag, String ymisRemark, String longitude, String latitude){Map params = new HashMap();Map map = new HashMap<>();// 银行提供:商户号、终端号、商户keyMap bank = microPayService.getBankMation();String newMerchantNo = bank.get("merchantNo").toString(); // map的value是Object类型,转StringString newTerminalNo = bank.get("terminalNo").toString();String newBussinessKe = bank.get("bussinessKey").toString();// 商户订单号(生成一个32位的随机数。防止重复,保留唯一性)UUID uuid = UUID.randomUUID();String OutTradeNo = uuid.toString().replace("-","");// 随机字符串String nonceStr = RandomStringUtils.randomAlphanumeric(32);// 系统跟踪号(1、必须为六位,数字不⾜时,在前补0。。2、系统跟踪号的值从1到999999循环使⽤。。3、同⼀天,同终端下系统跟踪号不可重复)Integer t = traceNoService.getTrace();// 查询系统跟踪号此时是多少,默认为0if (t==999999){t=0;traceNoService.updateResetTrace(t);}traceNoService.updateTraceNo(t+1); // 每访问一次接口加1Integer trc= traceNoService.getTrace(); // 再次查询String traceNo = String.format("%06d",trc); // 转成6位,数字不足时在前补0// 查询sce_sign表中是否存在当天批次号String existenceBatchNo = signService.querySignBatchNo();if ("".equals(existenceBatchNo) || existenceBatchNo==null){params.put("merchantNo",newMerchantNo);params.put("terminalNo",newTerminalNo);// 签到urlString url = "https://银行接口ip:端口号/cposp/pay/signIn";// 请求签到url,获取批次号,每天定时访问一次String result = HttpsUtil.httpJsonMethodPost(url, JSONObject.toJSONString(params),"");if (!"".equals(result) && result !=null){// 获取到key为batchNo的值(batchNo:批次号,当日可以共用批次号)existenceBatchNo = JSONObject.parseObject(result).getString("batchNo");map.put("batchNo",existenceBatchNo);// 将此批次号存到表中signService.addSign(map);}else{map.put("signInResult", HttpStatus.NO_CONTENT); // 签到结果无内容返回return map;}}// 二维码信息Map res = microPayService.microPayMethod(newMerchantNo,newTerminalNo,newBussinessKe,cashierNo,existenceBatchNo,traceNo,OutTradeNo,transAmount,payCode,nonceStr,remark,ymisFlag,ymisRemark,longitude,latitude);return res;}
}
2、service,我备注都写了,查询的功能,每个service对应的作用是不同的,实现的service就不写了,直接写实现类了
MicroPayServiceImpl层:
package com.example.parkingfees.service;import com.alibaba.fastjson.JSONObject;
import com.example.parkingfees.mapper.BankMapper;
import com.example.parkingfees.utils.HttpsUtil;
import com.example.parkingfees.utils.Md5Util;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import java.io.UnsupportedEncodingException;
import java.util.*;/*** @author 刘磊* @date 2020/9/7 10:01*/
@Service
public class MicroPayServiceImpl implements MicroPayService{@Autowiredprivate BankMapper bankMapper;public Map microPayMethod(String merchantNo,String terminalNo,String bussinessKey,String cashierNo,String batchNo,String traceNo,String OutTradeNo,Integer transAmount,String payCode,String nonceStr,String remark,String ymisFlag,String ymisRemark,String longitude,String latitude){Map map = new HashMap<>();SortedMap
其中此Service中使用到的两个工具类,Md5Util和HttpsUtil
Md5Util:
package com.example.parkingfees.utils;import org.apache.commons.lang3.ObjectUtils;
import org.springframework.util.DigestUtils;
import java.io.UnsupportedEncodingException;
import java.util.Iterator;
import java.util.Map;
import java.util.SortedMap;/*** @author 刘磊* @date 2020/9/3 19:20*/
public class Md5Util {// 对传过来的params参数进行md5加密public static String generateSign(SortedMap parameterMap,String bussinessKey) throws UnsupportedEncodingException {ObjectUtils.allNotNull(parameterMap);String url = "";int i = 0;for(Iterator var5 = parameterMap.entrySet().iterator(); var5.hasNext(); ++i) {Map.Entry entry = (Map.Entry)var5.next();String key = entry.getKey();String value = entry.getValue();// 参数的值若为空,则不参与签名if(!"".equals(value) && value!=null){if (i == 0) {url = url + key + "=" + value;} else {url = url + "&" + key + "=" + value;}}}url = url + "&key="+bussinessKey; //对应文档sign第二步骤 拼接keyreturn DigestUtils.md5DigestAsHex(url.getBytes()).toUpperCase();}
}
HttpsUtil:
/*** HTTP协议POST请求方法*/
public static String httpJsonMethodPost(String url, String params, String gb) {if (null == gb || "".equals(gb)) {gb = "UTF-8";}StringBuffer sb = new StringBuffer();URL urls;HttpsURLConnection uc = null;BufferedReader in = null;try {SSLContext sc = SSLContext.getInstance("SSL");sc.init(null, new TrustManager[]{new MyTrustManager()}, new java.security.SecureRandom());urls = new URL(url);uc = (HttpsURLConnection) urls.openConnection();uc.setSSLSocketFactory(sc.getSocketFactory());uc.setHostnameVerifier(new MyHostnameVerifier());uc.setRequestMethod("POST");uc.setDoOutput(true);uc.setDoInput(true);uc.setUseCaches(false);uc.setRequestProperty("Content-Type", "application/json");uc.connect();DataOutputStream out = new DataOutputStream(uc.getOutputStream());out.write(params.getBytes(gb));out.flush();out.close();in = new BufferedReader(new InputStreamReader(uc.getInputStream(), gb));String readLine = "";while ((readLine = in.readLine()) != null) {sb.append(readLine);}if (in != null) {in.close();}if (uc != null) {uc.disconnect();}} catch (Exception e) {log.error(e.getMessage(), e);} finally {if (uc != null) {uc.disconnect();}}return sb.toString();
}
SignServiceImpl层:
package com.example.parkingfees.service;import com.example.parkingfees.mapper.SignMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Map;/*** @author 刘磊* @date 2020/9/4 10:43*/
@Service
public class SignServiceImpl implements SignService{@AutowiredSignMapper signMapper;@Overridepublic void addSign(Map map) {signMapper.addSign(map);}@Overridepublic String querySignBatchNo() {return signMapper.querySignBatchNo();}
}
TraceNoServiceImpl层:
package com.example.parkingfees.service;import com.example.parkingfees.mapper.TraceNoMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;/*** @author 刘磊* @date 2020/9/8 9:41*/
@Service
public class TraceNoServiceImpl implements TraceNoService{@Autowiredprivate TraceNoMapper traceNoMapper;@Overridepublic Integer getTrace() {return traceNoMapper.getTrace();}@Overridepublic void updateTraceNo(Integer traceNo) {traceNoMapper.updateTraceNo(traceNo);}@Overridepublic void updateResetTrace(Integer t) {traceNoMapper.updateResetTrace(t);}
}
3、所有Mapper层
BankMapper层,查询银行提供的商户号,终端号,商户key:
package com.example.parkingfees.mapper;import org.springframework.stereotype.Repository;
import java.util.Map;/*** @author 刘磊* @date 2020/9/7 15:03*/
@Repository
public interface BankMapper {Map getBankMation();
}
SignMapper层:
package com.example.parkingfees.mapper;import org.springframework.stereotype.Repository;
import java.util.Map;/*** @author 刘磊* @date 2020/9/4 10:39*/
@Repository
public interface SignMapper {void addSign(Map map);String querySignBatchNo();
}
TraceNoMapper层:
package com.example.parkingfees.mapper;import org.springframework.stereotype.Repository;/*** @author 刘磊* @date 2020/9/8 9:41*/
@Repository
public interface TraceNoMapper {Integer getTrace();void updateTraceNo(Integer traceNo);void updateResetTrace(Integer t);
}
4、所有xml层
BankMapper.xml层:
SignMapper.xml层:
insert into sce_sign(batchNo,nowtime)values(#{batchNo},now())
TraceNoMapper.xml层:
UPDATE sce_stnumSET traceNo = #{traceNo}; UPDATE sce_stnumSET traceNo = #{t};
5、SQL,至于建表,都是根据业务需求来的,动态自己想要的数据
sce_bank表:
CREATE TABLE `sce_bank` (
`id` int(11) NOT NULL,
`merchantNo` varchar(15) NOT NULL COMMENT '商户号',
`terminalNo` varchar(8) NOT NULL COMMENT '终端号',
`bussinessKey` varchar(100) NOT NULL COMMENT '商户key',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
sce_sign表:
CREATE TABLE `sce_sign` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`batchNo` varchar(8) NOT NULL,
`nowtime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8;
sce_stnum表:
CREATE TABLE `sce_stnum` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`traceNo` int(6) NOT NULL COMMENT '系统跟踪号',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
这些就是从头至尾的所有代码部分,看不懂,给我留言吧。主要是思想,看懂文档才是最重要的,下面目前给出两三种结果
付款成功,返回的结果:
{"payResult": "{\"resultCode\":\"00\",\"resultMessage\":\"交易成功\",\"merchantNo\":\"94734160742A1WD\",\"terminalNo\":\"72001845\",\"batchNo\":\"000071\",\"traceNo\":\"000093\",\"transAmount\":1,\"transTime\":\"20200912105806\",\"cposOrderId\":\"1304617647610544128\",\"referNo\":\"123013051530\",\"itpOrderId\":\"12002020091299301552\",\"chnOrderId\":\"4200000689202009122710474193\",\"orderStatus\":\"3\",\"tradeChannel\":\"01\",\"errorDesc\":\"\",\"outletName\":\"070测试\",\"payUserInfo\":\"070测试\",\"paidTime\":\"2020-09-12\"}", "payStatus": "3", "payMessage": "交易成功"
}
不付款之后返回的结果:
{"payresult": "{\"resultCode\":\"9999\",\"resultMessage\":\"操作失败\"}", "resultCode": "9999", "orderQueryResult": "NO_CONTENT", "resultMessage": "操作失败"
}
不存在的支付码返回的结果:
{"payresult": "{\"resultCode\":\"9999\",\"resultMessage\":\"付款码不可用,请重新支付\"}", "resultCode": "9999", "orderQueryResult": "NO_CONTENT", "resultMessage": "付款码不可用,请重新支付"
}
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
