银行软件开发工资一般多少seopc流量排行榜企业
目录
- 支付模块
- 需求分析
- 表设计
- 支付单表
- 支付宝账号信息表-商家账号
- 微信支付账号信息表-商家账号
- 银行账号表-商家
- 资金账号表
- 支付流水表
- 流程分析
- 支付基础模块继承
- 加密算法
- 沙箱环境准备
- 支付宝支付-流程分析
- 根据demo封装工具类
- 导入依赖
- AlipayConfig
- AlipayInfo
- AlipayUtil
- 内网穿透
- 领养订单-支付流程
- 后端生成支付单和进行支付
- AdoptOrderServiceImpl
- PayConstants
- PayBill
- PayBillServiceImpl
- 测试
- 前台显示支付form表单
- 前台完成支付
- 异步回调通知后完成后续逻辑
- 订单与支付汇总流程图
每种订单都有对应类型的支付
支付模块
需求分析
资金账号(支付账号)
支付方式
支付单
支付流水
表设计
三种类型:支付单表,支付流水表,支付方式表
支付单表
CREATE TABLE `t_pay_bill` (`id` bigint(20) NOT NULL AUTO_INCREMENT,`digest` varchar(100) DEFAULT NULL COMMENT '交易摘要',`money` decimal(11,2) NOT NULL COMMENT '金额',`state` tinyint(4) NOT NULL DEFAULT '0' COMMENT '支付状态 0待支付1 已支付-1 取消',`lastPayTime` datetime DEFAULT NULL,`payChannel` tinyint(4) NOT NULL COMMENT '支付方式 余额 三方支付(支付宝,微信,银联)',`createTime` datetime DEFAULT NULL,`updateTime` datetime DEFAULT NULL,`unionPaySn` char(15) DEFAULT NULL COMMENT '统一支付单号',`businessType` varchar(255) NOT NULL COMMENT '业务类型',`businessKey` bigint(20) NOT NULL COMMENT '关联业务键',`user_id` bigint(20) DEFAULT NULL,`nickName` varchar(255) DEFAULT '',`shopName` varchar(255) DEFAULT NULL,`shop_id` bigint(11) DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=30 DEFAULT CHARSET=utf8 COMMENT='支付单';
1.订单模块生成订单
2.支付模块生成支付单
3.支付模块生成统一支付单号,调三方接口进行支付
4.支付成功返回统一支付单号给支付模块,支付模块根据统一支付单号找到订单修改状态
5.订单模块根据支付模块的订单类型和订单号找到订单,修改订单状态
支付宝账号信息表-商家账号
CREATE TABLE `t_pay_alipay_info` (`id` bigint(20) NOT NULL AUTO_INCREMENT,`shop_id` bigint(20) DEFAULT NULL,`shopName` varchar(255) DEFAULT NULL,`appid` varchar(255) DEFAULT NULL COMMENT '应用id',`merchant_private_key` varchar(2500) DEFAULT NULL COMMENT '商家私钥',`alipay_public_key` varchar(2500) DEFAULT NULL COMMENT '支付宝公钥',PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
微信支付账号信息表-商家账号
CREATE TABLE `t_pay_wechat_info` (`merchant_private_key` varchar(2500) DEFAULT NULL,`appid` varchar(255) DEFAULT NULL,`id` bigint(20) NOT NULL AUTO_INCREMENT,`alipay_public_key` varchar(2500) DEFAULT NULL,`shop_id` bigint(20) DEFAULT NULL,`shopName` varchar(255) DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
银行账号表-商家
CREATE TABLE `t_pay_bank_info` (`merchant_private_key` varchar(2500) DEFAULT NULL,`appid` varchar(255) DEFAULT NULL,`id` bigint(20) NOT NULL AUTO_INCREMENT,`alipay_public_key` varchar(2500) DEFAULT NULL,`shop_id` bigint(20) DEFAULT NULL,`shopName` varchar(255) DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
资金账号表
CREATE TABLE `t_pay_account` (`id` bigint(20) NOT NULL AUTO_INCREMENT,`createTime` datetime DEFAULT NULL,`updateTime` datetime DEFAULT NULL,`user_id` bigint(20) NOT NULL COMMENT '用户ID',`useableBalance` int(11) NOT NULL DEFAULT '0' COMMENT '可用金额',`frozenBalance` int(11) NOT NULL DEFAULT '0' COMMENT '冻结金额',`creditBanance` int(11) NOT NULL DEFAULT '0' COMMENT '积分',`salt` varchar(255) DEFAULT NULL,`payPassword` varchar(32) NOT NULL COMMENT '支付密码 md5加密+加盐',PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户账户';
支付流水表
CREATE TABLE `t_pay_account_flow` (`id` bigint(20) NOT NULL AUTO_INCREMENT,`createTime` bigint(20) DEFAULT NULL,`user_id` bigint(20) NOT NULL COMMENT '用户ID',`nickName` varchar(20) DEFAULT NULL COMMENT '用户姓名',`money` decimal(11,2) NOT NULL COMMENT '金额',`type` tinyint(4) NOT NULL COMMENT '支付方式',`businessType` varchar(4) NOT NULL COMMENT '业务类型',`businessName` varchar(20) NOT NULL COMMENT '业务名',`businessKey` bigint(20) NOT NULL COMMENT '关联业务键',`payChannel` tinyint(4) NOT NULL,`payChannelName` varchar(20) NOT NULL,`note` varchar(50) DEFAULT NULL COMMENT '备注',`digest` varchar(100) DEFAULT NULL,`unionPaySeq` char(15) DEFAULT NULL COMMENT '统一支付单号',PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户支付流水';
流程分析
结合上面内容
https://opendocs.alipay.com/open/00a0ut
电脑网站支付 - 产品介绍 -> 流程介绍
支付基础模块继承
拷贝java和xml代码,配置yml扫描别名
加密算法
沙箱环境准备
在线加密生成自己的公钥和私钥
把自己的公钥告诉支付宝并得到支付宝的公钥
保存自己的 appid 私钥 和支付宝的公钥到数据库表里
支付宝支付-流程分析
https://opendocs.alipay.com/open/00a0ut
电脑网站支付 - 接入指南 - 电脑网站支付快速接入 -> 流程介绍
根据订单生成支付单后调支付接口要传同步通知地址和异步通知地址
支付平台返回string(内容是一个支付表单)给后端,后端将这个String交给前端到浏览器显示,
中间的步骤由用户与支付宝他们自己完成
支付完成后三重返回保证
1.同步通知,只做重定向页面通知
2.异步通知,以此为准,收到后分别去更改支付单和订单的状态
3.主动用统一支付单号去支付宝查询状态
根据demo封装工具类
导入依赖
<!-- https://mvnrepository.com/artifact/com.alipay.sdk/alipay-sdk-java -->
<dependency><groupId>com.alipay.sdk</groupId><artifactId>alipay-sdk-java</artifactId><version>3.7.4.ALL</version>
</dependency>
AlipayConfig
public class AlipayConfig {// 服务器异步通知路径 需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问public static String notify_url = "http://c84pns.natappfree.cc/notify";// 页面跳转同步通知页面路径 需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问public static String return_url = "http://localhost/success.html";// 签名方式public static String sign_type = "RSA2";// 字符编码格式public static String charset = "utf-8";// 测试环境支付宝网关public static String gatewayUrl = "https://openapi.alipaydev.com/gateway.do";
}
AlipayInfo
@Data
public class AlipayInfo extends BaseDomain {private String merchant_private_key;//我的私钥private String appid;//我的AppIDprivate String alipay_public_key;//支付宝公钥private Long shop_id;private String shopName;}
AlipayUtil
public class AlipayUtil {public static String pay(AlipayInfo info, PayBill payBill){try {//获得初始化的AlipayClientAlipayClient alipayClient = new DefaultAlipayClient(cn.ming.pay.utils.AlipayConfig.gatewayUrl,info.getAppid(),info.getMerchant_private_key(),"json",cn.ming.pay.utils.AlipayConfig.charset,info.getAlipay_public_key(),cn.ming.pay.utils.AlipayConfig.sign_type);//设置请求参数AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest();alipayRequest.setReturnUrl(cn.ming.pay.utils.AlipayConfig.return_url);alipayRequest.setNotifyUrl(cn.ming.pay.utils.AlipayConfig.notify_url);//商户订单号,商户网站订单系统中唯一订单号,必填String out_trade_no = payBill.getUnionPaySn();//付款金额,必填String total_amount = payBill.getMoney().toString();//订单名称,必填String subject = payBill.getDigest();//商品描述,可空String body = payBill.getDigest();alipayRequest.setBizContent("{\"out_trade_no\":\""+ out_trade_no +"\","+ "\"total_amount\":\""+ total_amount +"\","+ "\"subject\":\""+ subject +"\","+ "\"body\":\""+ body +"\","+ "\"product_code\":\"FAST_INSTANT_TRADE_PAY\"}");//请求String result = alipayClient.pageExecute(alipayRequest).getBody();return result;} catch (AlipayApiException e) {e.printStackTrace();}return null;}}
内网穿透
使用内网穿透将本地的异步通知地址暴露到公网上供外网访问
官网首页:https://natapp.cn/
NATAPP1分钟快速新手图文教程:https://natapp.cn/article/natapp_newbie
购买隧道 - 免费
名称:pethome,隧道协议:web,本地端口:8080, - 免费购买
查看我的隧道,得到authtoken
下载客户端,解压至任意目录,得到natapp.exe
同文件夹下新建config.ini文件,输入authtoken=xxx保存
双击natapp.exe得到公网可用域名
配置以下两个回调地址
// 服务器异步通知路径 需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问
public static String notify_url = "http://254fde.natappfree.cc/notify";
// 页面跳转同步通知页面路径 需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问
public static String return_url = "http://localhost/success.html";
ph-web拷贝success.html到根目录,编码异步回调notify接口,
AlipayController
@PostMapping("/notify")public void notify(HttpServletRequest request){System.out.println("支付宝异步回调!");}
领养订单-支付流程
后端生成支付单和进行支付
AdoptOrderServiceImpl
/*** 领养订单结算*/@Override@Transactionalpublic String submit(Map<String, Object> params, Logininfo loginIn) {Long petId = Long.valueOf(params.get("pet_id").toString());Long addressId = Long.valueOf(params.get("address_id").toString());//收货地址:t_user_address的idInteger payMethod = Integer.valueOf(params.get("pay_method").toString());//1 支付宝 2 微信 3 银联 0 余额Integer serviceMethod = Integer.valueOf(params.get("service_method").toString());//送货方式//1.修改状态 准备下架Pet pet = petMapper.loadById(petId);pet.setState(0);pet.setOffsaletime(new Date());//2.绑定用户User user = userMapper.loadByloginInfoId(loginIn.getId());pet.setUser(user);pet.setUser_id(user.getId());pet.setShop_id(pet.getShop().getId());petMapper.update(pet);//3.生成订单 一次性AdoptOrder order = initAdoptOrder(pet, user);//骚操作:为了后边操作支付单,不用再来修改订单,先生成统一的支付单号String unionPaySn = CodeGenerateUtils.generateUnionPaySn();order.setPaySn(unionPaySn);adoptOrderMapper.save(order);// 3.1 生成订单地址UserAddress userAddress = userAddressMapper.loadById(addressId);OrderAddress orderAddress = userAddress2OrderAddress(order, userAddress);orderAddressMapper.save(orderAddress);// 4.支付单PayBill payBill = initPayBill(payMethod, pet, user, order);payBillMapper.save(payBill);//调用统一支付接口(老杨定义的)return payBillService.pay(payBill);// 5.订单定时取消任务 @TODO}private AdoptOrder initAdoptOrder(Pet pet, User user) {AdoptOrder order = new AdoptOrder();order.setDigest("【摘要】" + pet.getName());order.setPrice(pet.getSaleprice());order.setOrderSn(CodeGenerateUtils.generateOrderSn(user.getId()));order.setLastConfirmTime(new Date(System.currentTimeMillis() + 15*60*1000));//最后确认时间order.setPet_id(pet.getId());order.setUser_id(user.getId());order.setShop_id(pet.getShop().getId());return order;}private OrderAddress userAddress2OrderAddress(AdoptOrder order, UserAddress userAddress) {OrderAddress orderAddress = new OrderAddress();BeanUtils.copyProperties(userAddress, orderAddress);orderAddress.setId(null);orderAddress.setOrder_id(order.getId());orderAddress.setOrderSn(order.getOrderSn());return orderAddress;}@Overridepublic PageList<AdoptOrder> queryAdmin(AdoptOrderQuery query, Long loginInfoId) {//1.通过loginInfoID查询出EmployeeEmployee employee = employeeMapper.loadByLoginInfoId(loginInfoId);//2.如果employee中的shopID不为null,就是店铺。否则就是平台员工if (employee.getShop_id() != null) {query.setShopId(employee.getShop_id());}return super.queryPage(query);}@Overridepublic PageList<AdoptOrder> queryUser(AdoptOrderQuery query, Long loginInfoId) {User user = userMapper.loadByloginInfoId(loginInfoId);query.setUserId(user.getId());return super.queryPage(query);}private PayBill initPayBill(Integer payMethod, Pet pet, User user, AdoptOrder order) {PayBill payBill = new PayBill();payBill.setDigest(order.getDigest()+"支付单");payBill.setMoney(order.getPrice());//重点:支付唯一表示payBill.setUnionPaySn(order.getPaySn());payBill.setLastPayTime(new Date(System.currentTimeMillis() + PayConstants.LAST_TIME));payBill.setPayChannel(Long.valueOf(payMethod));//0 余额 1 支付宝 2 微信 3 银联payBill.setBusinessType(PayConstants.BUSINESS_TYPE_ADOPT);payBill.setBusinessKey(order.getId());payBill.setUser_id(user.getId());payBill.setShop_id(pet.getShop().getId());payBill.setNickName(user.getUsername());return payBill;}
PayConstants
public class PayConstants {public static final Integer LAST_TIME = 15*60*1000;//领养订单public static final String BUSINESS_TYPE_ADOPT = "business_type_adopt";//服务订单public static final String BUSINESS_TYPE_PRODUCT = "business_type_product";//收购订单public static final String BUSINESS_TYPE_ACQUISITION = "business_type_acquisition";//商品订单public static final String BUSINESS_TYPE_GOODS = "business_type_goods";//充值订单public static final String BUSINESS_TYPE_RECHARGE = "business_type_recharge";}
PayBill
@Data
public class PayBill extends BaseDomain {private String digest;private BigDecimal money;private String unionPaySn;private Integer state = 0;//0待支付1 已支付-1 取消private Date lastPayTime;private Long payChannel; //0 余额 1 支付宝 2 微信 3 银联private String businessType;private Long businessKey;private Date updateTime;private Date createTime = new Date();private Long user_id;private Long shop_id;private String nickName;
}
PayBillServiceImpl
@Overridepublic String pay(PayBill payBill) {if(payBill == null){throw new BusinessException("请生成支付单后,在进行支付!!!");}PayBill payBill1 = payBillMapper.loadByUnionPaySn(payBill.getUnionPaySn());if(payBill1 == null){throw new BusinessException("请生成支付单后,在进行支付!!!");}Long payChannel = payBill1.getPayChannel();String resultData = "";switch(payChannel.intValue()){case 1 ://支付宝AlipayInfo info = alipayInfoMapper.loadByShopId(payBill1.getShop_id());resultData = AlipayUtil.pay(info, payBill1);break;case 2 ://微信//TODObreak;case 3 ://银联//TODObreak;default : //0 余额//TODO}return resultData;}
测试
adoptOrder.html
methods: {orderSubmit() {this.$http.post("/adopt/submit", this.order).then(result => {console.log(result.data);// $("#alipayForm").html(result.data.resultObj);// location.href = "personCenter.html";}).catch(result => {alert("系统错误!!");})}},
前台显示支付form表单
<div id="alipayForm"></div>
methods: {orderSubmit() {this.$http.post("/adopt/submit", this.order).then(result => {console.log(result.data);$("#alipayForm").html(result.data.resultObj);// location.href = "personCenter.html";}).catch(result => {alert("系统错误!!");})}},
前台完成支付
form表单显示后会展示支付宝扫码或登录页面,完成支付后跳转到success回调页面,可从url取到unionPaySn查询数据显示到success页面。
异步回调通知后完成后续逻辑
AlipayController
@PostMapping("/notify")public void notify(HttpServletRequest request){System.out.println("支付宝异步回调!");//获取支付宝POST过来反馈信息try {Map<String,String> params = new HashMap<String,String>();Map<String,String[]> requestParams = request.getParameterMap();for (Iterator<String> iter = requestParams.keySet().iterator(); iter.hasNext();) {String name = (String) iter.next();String[] values = (String[]) requestParams.get(name);String valueStr = "";for (int i = 0; i < values.length; i++) {valueStr = (i == values.length - 1) ? valueStr + values[i]: valueStr + values[i] + ",";}valueStr = new String(valueStr);params.put(name, valueStr);}String unionPaySn = params.get("out_trade_no");PayBill payBill = payBillService.loadByUnionPaySn(unionPaySn);if(payBill != null){AlipayInfo info = alipayInfoService.getByShopId(payBill.getShop_id());boolean signVerified = AlipaySignature.rsaCheckV1(params,info.getAlipay_public_key(),AlipayConfig.charset,AlipayConfig.sign_type); //调用SDK验证签名if(signVerified) {//验证成功//商户订单号String out_trade_no = unionPaySn;//支付宝交易号String trade_no = request.getParameter("trade_no");//交易状态String trade_status = request.getParameter("trade_status");if(trade_status.equals("TRADE_FINISHED")){//用户确认}else if (trade_status.equals("TRADE_SUCCESS")){//1. 改支付单状态payBill.setState(1);payBill.setUpdateTime(new Date());payBillService.update(payBill);String businessType = payBill.getBusinessType();//2.再修改对应(领养)的订单状态 businessType businessKey 订单if(PayConstants.BUSINESS_TYPE_ADOPT.equals(businessType)){//领养订单Long orderId = payBill.getBusinessKey();AdoptOrder order = adoptOrderService.getById(orderId);order.setState(1);adoptOrderService.update(order);}}}else {//验证失败System.out.println("老宋,不要搞事");//调试用,写文本函数记录程序运行情况是否正常//String sWord = AlipaySignature.getSignCheckContentV1(params);//AlipayConfig.logResult(sWord);}}} catch (AlipayApiException e) {e.printStackTrace();} catch (Exception e) {e.printStackTrace();}}