支付系统设计四:支付核心设计03-快捷发送短信(失败转代扣)

news2024/11/18 8:49:21

文章目录

  • 前言
  • 一、背景
    • 1. 应用架构
    • 2. 分层支撑机制
  • 二、银行卡快捷支付
    • 1. 用户操作流程
    • 2. 系统执行流程--正常
      • 2.1 发送短信
      • 2.2 短信确认
    • 3. 系统执行流程--异常
      • 3.1 异常环节
        • 3.1.1 路由失败
        • 3.1.2 调用支付渠道失败
      • 3.2 异常处理
        • 3.2.1 路由失败
        • 3.2.2 调用支付渠道失败
    • 4. 流程解析
      • 4.1 核心代码实现
        • 4.1.1 Service实现
        • 4.1.2 类图
        • 4.1.3 快捷支付发送领域模型:QuickPayDomain
        • 4.1.4 主动支付领域模型:AbstractActivePayDomain
        • 4.1.5 聚合基类:AggregateBase
        • 4.1.6 标记领域模型:Domain
        • 4.1.7 快捷支付预支付命令链:CardQuickPaySendCommandChain
        • 4.1.8 快捷支付预支付指令:XxxCommand
      • 4.2 涉及表结构
  • 总结


前言

本篇将讲解银行卡快捷支付的实现,正常银行卡快捷支付流程过于简单,没什么好讲的,本篇将讲解如何在银行卡快捷失败后,可以顺滑的不中断内部进行支付方式转换(快捷–>代扣),以提高用户体验和支付成功率,如果觉得失败就失败,没必要想办法提高用户体验和支付成功率,那本篇请忽略,因为在你眼里这些设计都是没必要的。

同时本篇设计实现也可以当做将代扣包装为快捷对外提供。

本篇先讲解银行卡快捷发送短信环节流程实现。

一种实现,仅做参考。


一、背景

1. 应用架构

在这里插入图片描述

2. 分层支撑机制

在这里插入图片描述
本篇主要是讲解在基于paygw系统(见《渠道网关设计》)提供原子化的支付接口的基础上paycore系统进行产品的封装,如对银行卡快捷支付进行包装,在银行卡快捷支付环节失败后不中断平滑的转换为代扣。

二、银行卡快捷支付

1. 用户操作流程

在这里插入图片描述

用户在商城选购商品后,点击支付,进入到收银台页面,展示支付方式,选择银行卡支付,进入已绑定银行卡列表页面,选择已绑定银行卡,点击立即支付,弹出密码框/短信验证码框,本篇将讲解我们点击获取验证码--->输入验证码完成支付这个流程设计,主要是讲解内部支付方式的转换设计,如果有更好的设计实现方案,欢迎交流。

2. 系统执行流程–正常

2.1 发送短信

在这里插入图片描述

2.2 短信确认

在这里插入图片描述
如上两步为正常银行卡快捷支付流程部分系统的交互流程图。

3. 系统执行流程–异常

首先我们看下在发短信流程中的异常环节:

3.1 异常环节

在这里插入图片描述

3.1.1 路由失败

首先是路由失败,失败原因比较多,在路由的时候需要判断限额之类的,如果支付额度超限制等,或者压根就没有接入支持此银行卡快捷支付对应的支付渠道。那么支付路由返回无可用的支付渠道,即没有能够支持此快捷支付的支付渠道。

3.1.2 调用支付渠道失败

网络异常导致的调用支付渠道发送短信失败,或者支付渠道内部处理失败,由于是无资金变动类请求,可以当做失败处理。

3.2 异常处理

3.2.1 路由失败

路由返回无可用通道,则将Detail单状态置为失败,当做渠道发短信失败。

3.2.2 调用支付渠道失败

渠道发短信失败,将Detail单状态置为失败,生成交易类型为“代扣支付”的Detail,将Order交易类型也置为“代扣支付”,并指定短信通道为“短信中心”再次请求路由系统 ,获取到通道信息。如果获取成功,请求支付网关通过短信中心完成短信发送。

4. 流程解析

总体执行流程如下:
在这里插入图片描述
部分代码实现见下:

4.1 核心代码实现

4.1.1 Service实现

/**
 * 银行卡快捷-预支付(发送短信)
 */
@Override
public PayCoreResult<BaseResultVo> prePaySend(final QuickPaySendRequest request) {
	return serviceTemplate.execute(new ServiceCallback<AggregateBase, PayCoreResult>() {
		@Override
		public AbstractActivePayDomain beforeProcess() throws PayCoreException {
			//参数校验
			request.check();
			//创建领域模型
			return quickPayDomainFactory.createDomain(request);
		}

		@Override
		public void execute(AggregateBase domain) throws PayCoreException {
			//业务流程处理
			QuickPayDomain quickPayDomain = (QuickPayDomain) domain;
			quickPaySendCommandChain.execute(quickPayDomain);
		}

		@Override
		public PayCoreResult resultProcess(AggregateBase domain) {
			//返回结果处理
			ActivePayOrder activePayOrder = ((AbstractActivePayDomain) domain).getActivePayOrder();
			String bizOrderNo = StringUtils.isNotBlank(activePayOrder.getBizOrderNo()) ? activePayOrder.getBizOrderNo() : activePayOrder.getOutBizNo();
			return new PayCoreResult(new BaseResultVo(
					bizOrderNo,
					activePayOrder.getPayOrderNo(),
					activePayOrder.getOutBizNo(),
					activePayOrder.getBizStatus(),
					activePayOrder.getRespCode(),
					activePayOrder.getRespMsg()));
		}
	});
}

4.1.2 类图

在这里插入图片描述

4.1.3 快捷支付发送领域模型:QuickPayDomain

/**
 * @author kkk
 * @description 快捷支付发送领域模型
 */
public class QuickPayDomain extends AbstractActivePayDomain implements RouteDomain<ActivePayOrder, CardPayDetail, QuickPaySendRequestBody> {

    /** 状态*/
    private String verifyStatus;

    /**  银行卡子单*/
    protected CardPayOrder cardPayOrder;

    /**  银行卡支付明细*/
    protected CardPayDetail cardPayDetail;
    
    //... ...
}

4.1.4 主动支付领域模型:AbstractActivePayDomain

/**
 * @author kkk
 * @description 主动支付领域模型
 */
public abstract class AbstractActivePayDomain extends AggregateBase{

    /**  主单*/
    protected ActivePayOrder activePayOrder;

    /**  账务明细*/
    protected CcsDetail cssDetail;

    /**  优惠券子单*/
    protected CouponPayOrder couponPayOrder;

    /**  优惠券明细*/
    protected CouponPayDetail couponPayDetail;
    
    //... ...
 }

4.1.5 聚合基类:AggregateBase

/**
 * @author kkk
 * @description 聚合基类
 */
public abstract class AggregateBase implements Domain {

    /** 交易类型 */
    protected String transType;

    /** 是否移除幂等性校验key */
    private boolean removeUnique = false;

    /** 是否外部生成渠道请求流水标识 */
    private boolean outInstReq = false;

    /**
     * 聚合自我检查
     */
    public abstract void selfCheck();

    /**
     * 获取唯一性判断标识
     */
    public abstract String getUniqueKey();

    /**
     * 获取业务订单号
     */
    public abstract String getBizOrderNo();

    /**
     * 是否需要异常幂等性校验缓存key
     */
    public boolean isRemoveUniqueK(){
        return removeUnique;
    }

    /**
     * 是否需要异步MQ通知
     */
    public abstract boolean isNeedNotify();
    
    //... ...
}  

4.1.6 标记领域模型:Domain

/**
 * @author kkk
 * @description 语法堂,标记领域模型
 */
public interface Domain {

}

4.1.7 快捷支付预支付命令链:CardQuickPaySendCommandChain

/**
 * @author kkk
 * @description 快捷支付预支付命令链
 */
@Component
public class CardQuickPaySendCommandChain extends CommandChain {

    /**
     * 构造函数将具体命令加入命令顺序列表
     */
    @Autowired
    public CardQuickPaySendCommandChain(UniqueCheckCommand uniqueCheckCommand,
                                        RepeatPayCheckCommand repeatPayCheckCommand,
                                        CardQuickPaySaveCommand cardQuickPaySaveCommand,
                                        CardQuickPaySendCommand cardQuickPaySendCommand,
                                        QuickPaySmsCommand quickPaySmsCommand){

        // 1.添加幂等性验证指令
        addCommand(uniqueCheckCommand);

        // 2.订单重复支付校验
        addCommand(repeatPayCheckCommand);

        // 3.添加保存数据库命令(收单)
        addCommand(cardQuickPaySaveCommand);

        // 4.添加快捷支付发送指令
        addCommand(cardQuickPaySendCommand);

        // 渠道发短信失败,指定sms发短信
        addCommand(quickPaySmsCommand);
    }
}

4.1.8 快捷支付预支付指令:XxxCommand

4.2 涉及表结构

涉及到的表结构核心字段如下:

在这里插入图片描述

发送短信环节只涉及到active_pay_order、card_pay_order、card_pay_detail、coupon_pay_order(使用优惠券)
、ccs_detail(失败情况下)


总结

本篇主要简略分析了银行卡快捷支付的整体流程,以及在发送短信环节出现问题,如何不中断的将支付方式进行转换,以提高用户支付体验以及成功率。下篇再继续分析,短信确认环节执行流程。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/570042.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

指导实验心得5篇实用技巧

指导实验心得1 我觉得化工原理实验是一门验证性课程&#xff0c;它把我们在化工原理学到的各种单元操作化为实实在在的东西&#xff0c;而让我们把学到的知识认识到它的实在性。流体输送——离心泵、过滤——板框压滤机、对流传热——套管式换热器、吸收蒸馏——填料塔板式塔、…

AF594 NHS,Alexa Fluor594 NHS Ester,AF 594 NHS 活化酯,用于成像和流式细胞术中的稳定信号生成

【产品描述】 陕西新研博美生物科技有限公司供应的​Alexa Fluor594是一种鲜红色染料。Alexa Fluor用于成像和流式细胞术中的稳定信号生成 594染料是水溶性的&#xff0c;并且从pH 4到pH 10对pH不敏感。Alexa Fluor 594染料与多种抗体、肽、蛋白质、示踪剂和扩增底物偶联&#…

java内存问题

各种OOM的情况 1. 堆溢出-java.lang.OutOfMemoryError: Java heap space。 2. 栈溢出-java.lang.OutOfMemorryError。 3. 栈溢出-java.lang.StackOverFlowError。 4. 元信息溢出-java.lang.OutOfMemoryError: Metaspace。 5. 直接内存溢出-java.lang.OutOfMemoryError: Direct …

软件开发工程师个人简历模板3篇

软件开发工程师个人简历模板篇1 姓 名&#xff1a; 张先生 性 别&#xff1a; 男 婚姻状况&#xff1a; 未婚 民 族&#xff1a; 汉族 户 籍&#xff1a; 广东-珠海 年 龄&#xff1a; 28 现所在地&#xff1a; 广东-珠海 身 高&#xff1a; 168cm 希望地区&#xff1a; …

Toolformer and Tool Learning(LLMs如何使用工具)

大模型的能力让学术和工业界都对通用人工智能的未来充满幻想&#xff0c;在前一篇博文中已经粗略介绍&#xff0c; Augmented Language Models&#xff08;增强语言模型&#xff09; ALM的两大思路是推理和工具&#xff0c;本篇博文整理两篇关于Toolformer或Tool Learning的论…

web实现日历、阳历农历之间相互转换、npm、push、unshift、includes、innerHTML

文章目录 1、原生web实现效果图htmlJavaScriptstyle vue2实现htmlJavaScript 1、原生web实现 效果图 html <div class"box"><div class"week"><div>星期日</div><div>星期一</div><div>星期二</div><…

Three.js--》建模软件如何加载外部3D模型?

目录 三维建模软件的介绍 Blender官方文档介绍 Blender软件安装 GLTF格式简介 gltf不同文件形式 看过我之前讲解的three文章的人都知道&#xff0c;我在创建模型的时候都没有使用three.js自带的一些简单模型&#xff0c;而是引入外部的模型并加载到页面上&#xff0c;简言…

数据库基础——1.数据库概述

从这篇文章我们开始学习数据库的相关知识 目录 1.为什么要使用数据库 2.数据库与数据库管理系统 2.1相关概念 2.2数据库与数据库管理系统的关系 ​编辑2.3常见的数据库管理系统 2.4常见的数据库介绍 3.MySQL介绍 3.1概述 3.2关于MySQL8.0 3.3 Oracle vs MySQL 4.RD…

Java 反序列化漏洞

反序列化漏洞是指程序在反序列化期间&#xff0c;通过特殊的调用链引发的一系列安全问题。编程语言中只要存在序列化&#xff0c;反序列化功能就可能存在反序列化的安全问题。这里只针对Java和PHP进行讨论。 序列化漏洞概述 序列化的存在主要是为了存储和传输&#xff0c;将这…

Redis入门篇-初

结束时长 Redis十大数据类型 基本目录 实际的类型是没有被红框框选的10个类型 Strings 1 Lists 2 Sets 3 Hashes 4 Sorted sets 5 Streams 6 Geospatial 7 HyperLogLog 8 Bitmaps 9 Bitfields 10类型展示 Strings --> HelloRedis Lists [A>B>C>C] Sets {A<…

第七章 文件读写

内容框图 7.1 文件读写介绍 文件打开和关闭 用word编写一份简历&#xff0c;应该有哪些流程&#xff1f; 打开word软件&#xff0c;新建一个word文件写入个人简历信息保存文件关闭word软件 同样&#xff0c;编程中操作文件的整体过程类似。 打开文件&#xff0c;或者新建立一个…

chatgpt赋能python:Python中的%z格式化代码

Python中的%z格式化代码 在Python编程中&#xff0c;%z是一个非常有用的格式化代码。它用于表示时区偏移量&#xff0c;并将其与日期时间字符串一起显示。在本文中&#xff0c;我们将深入了解Python中的%z代码&#xff0c;并探讨它如何帮助您处理日期和时间。 什么是%z&#…

chatgpt赋能python:PythonTCP断开连接原因和解决方案

Python TCP 断开连接原因和解决方案 Python 是一种广泛使用的编程语言&#xff0c;它支持网络编程、数据处理、人工智能、机器学习等诸多领域。在网络编程中&#xff0c;Python 通常使用 TCP 连接传输数据。然而&#xff0c;在使用 TCP 连接传输数据的过程中&#xff0c;我们可…

koa2获取HTTP请求参数

HTTP 什么是HTTP HTTP&#xff0c;即超文本传输协议&#xff0c;是一种实现客户端和服务器之间通信的响应协议&#xff0c;它是用作客户端和服务器之间的请求。 客户端&#xff08;浏览器&#xff09;会向服务器提交HTTP请求&#xff1b;然后服务器向客户端返回响应&#xf…

2023 Q1 ZK报告

1. ZK数据 2023年4月4日&#xff0c;ZK社区举办了zkSummit 9。 ZK技术应用场景主要分布情况为&#xff1a; 其中&#xff1a; 1&#xff09;ZK用于隐私场景案例最多&#xff0c;占比12.9%&#xff0c;说明人们对 将ZK技术用于隐私保护 兴趣浓厚。2&#xff09;ZK用于扩容场景…

超长溢出头部省略打点,坑这么大,技巧这么多?

目录 需求 利用 direction 实现头部超长溢出打点 简单介绍一下 direction&#xff1a; 另外两个与排版相关的属性还有&#xff1a; direction: rtl 会导致使用下划线 _ 连接的数字内容排版错误 多方案解决 方案一&#xff1a;两次 direction 反转 当然&#xff0c;这里…

【高级语言程序设计(一)】第 8 章:结构体类型和自定义类型

目录 前言 一、结构体类型定义 &#xff08;1&#xff09;结构体类型定义的一般形式 &#xff08;2&#xff09;结构体类型定义的说明 二、结构体类型变量 &#xff08;1&#xff09;结构体类型变量的定义和初始化 ① 先定义结构体类型、后定义结构体类型的变量&#xf…

今天用AI创作助手写的文章--Docker提问系列介绍 Docker 的基本概念和优势

目录 介绍 Docker 的基本概念和优势&#xff0c;以及在应用程序开发中的实际应用。基本概念&#xff1a;优势&#xff1a; 哪些应用可以部署到docker里面Docker在服务器里面的安装步骤Docker里面的报错如何很仔细的排查解决&#xff1f;总结一下docker 未来发展趋势 介绍 Docke…

安装Arch Linux后要做的十件事

Arch Linux 是一款轻量级、灵活且高度可定制的Linux发行版&#xff0c;被广泛用于个人电脑和服务器。一旦您成功安装了Arch Linux&#xff0c;接下来有一些重要的任务需要完成&#xff0c;以确保系统的稳定性和安全性&#xff0c;并为您的需求做好准备。 本文将详细介绍安装Ar…

C++11 -- 可变参数模板

文章目录 可变参数模板的概念可变模板参数的定义获取可变模板参数包的值递归函数方式展开参数包获取逗号表达式展开参数包获取 STL容器中的emplace相关接口参数emplace_back与STL容器中的push_back的主要区别emplace_back与push_back的差异原理emplate_back与push_back的区别验…