首页 > 编程知识 正文

怎么做第三方支付接口,接口设计原则

时间:2023-05-04 15:05:32 阅读:112746 作者:3392

你是一个从事重要工作的人,前言的安全性、稳定性、事务的连贯性、保守性

前言

阅读本文大概需要6分钟

最近,项目对接了第三方支付,但对第三方支付来说,复杂的功能是支付、退款和结算。

本文仅介绍有关支付的接口设计。

一笔支付流水可能涉及到的节点包括:支付、支付结果查询、支付结果通知、撤单、关单、退款、对账。

以“支付宝”为例,“支付宝”提供了非常丰富的支付能力。 包括APP支付、扫描支付、网站支付等。 不同的支付方法之间的差异不大。

支付给第三方的过程很相似。 由于根据官方提供的文档可以快速完成对接,因此本文不讨论如何支付给第三方。 谈谈对接以外的事情。

你是怎么设计接口的? 据介绍,设计一个接口要考虑五点:安全性、稳定性、高效性、可维护性、可读性。

在此,以这些特性为中心,介绍支付接口的设计方法。

因为安全性支付界面涉及资金的流动,所以其安全性是不言而喻的。

在支付宝(Alipay )中,规定当访问支付能力时,数据传输接口用公开密钥方式进行加密。

那么,我们自己的接口之间是如何保证安全性的呢?

SHA256 或者RSA2。

具体的加密算法在这里不详细说明,但网上有一大堆。

本文简要介绍了加密的流程。 下图:

具体而言:

在APP端首先用SHA256或RSA2加密支付消息,然后传递到后台服务; 在后台分析和验证密文。 分析通过时进入下一个逻辑,否则表示密文分析失败。 稳定性对于支付接口来说幂等性是极为重要的。

与支付业务相关的数据库操作为保存支付流水、同步订单状态、更新库存数量等等。

为了新操作天然的非幂等性,我们需要在设计水平上进行保证。

我在项目上做了使用Redis分布式锁实现支付接口的幂等

有关使用Redis实现分布式锁的原理,我会在下一篇文章和大家分享。我在项目中实现的方法: Redisson。

Redisson原理:

线程将获得锁定并获得成功。 运行lua脚本,将数据保存到redis数据库中。 线程去获取锁定,获取失败:一直尝试在while循环中获取锁定,获取成功后运行lua脚本,将数据保存到redis数据库。 支持看门狗自动延期机制。 代码示例:

ependencygroupidorg.redis son/groupidartifactidredisson/artifactidversion3. 13.6/version/ependencypublicvoidtestrestred try{ //1.最常用的用法//lock.lock (; //2 .支持过期解锁功能,10秒后自动解锁。 不需要调用unlock方法手动解锁//lock.lock(10,TimeUnit.SECONDS )。 //3 .尝试锁定,最多等待3秒钟,锁定后10秒钟自动解锁boolean RES=lock.trylock (3,10,TimeUnit.SECONDS )。 if(RES ) (/成功(/doyourbusiness ) ) catch ) interruptedexceptione ).printstacktrace ); } finally { lock.unlock (); )除幂等性外,支付界面需要考虑的一个问题是订单超时关闭这个问题首先请大家考虑,我们将在后期的文章中详细介绍。

交易一致支付成功后,应同时更新订单情况、库存数量。

在微服务背景下,每个业务的数据库都是独立的,必须使用分布式事务才能确保事务的一致性。

典型的分布式事务解决方案:

XA两级提交TCC模式:支持TCC事务的开源框架是字节TCC、Himly和TCC-transaction。 Saga事务的基于消息的分布式事务:基于事务消息的方案、基于本地消息的方案分布式事务中间件: Seata可维护性目前在我们的项目中称为支付宝(Alipay )

接银联等等。支付方式会随着业务的增长不断增加。
但是每个支付方式的流程大致都是一样的:支付信息解密、支付、修改订单、修改库存。

如此一来,使用if else判断就会导致支付功能和系统业务功能高度耦合。

if (payType.equals ("WeiXin")) {//dosomething} ​else if (payType.equals ("AliPay")) {//dosomething} else if ​(payType.equals ("UnionPay")) {//dosomething}

所以我在项目中用到了策略模式,来为不同的支付方式定义不同的实现。

首先定义一个抽象类,封装公共的方法。

然后自定义注解ServiceRoute,标注在具体的支付实现接口,项目启动时自动把标注了ServiceRoute注解的服务注入到容器;

在支付的时候根据具体的通道编号,调用不同的支付实现功能。

自定义注解:

@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Inheritedpublic @interface ServiceRoute { /** * 支付通道编号 * * @return */ String value();}

服务注册:

public class RegisterService implements ApplicationContextAware { private static final Logger logger = LoggerFactory.getLogger(RegisterService.class);​ private Map<String, Object> servicesMap = new ConcurrentHashMap<String, Object>();​ private static ApplicationContext applicationCtx = null;​ /** * 注册服务接口 */ @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { applicationCtx = applicationContext;​ //扫描添加了ServiceRoute注解的类 Map<String, Object> allWebResBeans = applicationCtx.getBeansWithAnnotation(ServiceRoute.class); for (Object bean : allWebResBeans.values()) { String routeName = getServiceRoute(bean); if (routeName != null) { servicesMap.put(routeName, bean); logger.debug("register route,routeName={},bean={}", new Object[] {routeName,bean}); } } }​ private String getServiceRoute(Object bean) { if (bean != null) { Annotation anno = AnnotationUtils.getAnnotation(bean.getClass(), ServiceRoute.class); if (anno != null) { return anno.getClass().getAnnotation(ServiceRoute.class).value(); } } return null; } public Object getServiceByAnnoName(String name) { if (StringUtils.isNotEmpty(name)) { return servicesMap.get(name); } return null; }}

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。