支付宝或者微信支付业务

3,170 阅读9分钟

这是上一周甘肃的一个面试官问的一个业务场景,问的是我们现在嵌入一个支付的接口,你知道应该怎么做嘛?在实际开发之中可能会遇到什么问题。昨天晚上我在搜集资料后整理出来的个人见解(2025.2.16).如果转载请私信我并附上我原文的链接,谢谢。

支付业务大致流程

支付流程.png

  1. 用户选择商品提交订单
    用户在平台选择自己想要的商品,并提交订单。
  2. 商家服务器与支付宝交互生成订单
    商家服务器将商品信息和所需金额发给支付宝,生成支付宝订单。
  3. 支付宝返回支付页面
    支付宝订单返回成功后,生成支付页面,方便用户进行手机支付或网页支付。
  4. 手机调起支付宝 App 进行支付
    用户通过手机调起支付宝 App 进行支付操作。
  5. 用户输入支付密码并发送给支付宝
    用户输入支付密码,通过支付宝服务器发送支付请求。
  6. 支付宝转账成功并通知商家服务器
    支付宝确认转账成功后,通知商家服务器,表示订单金额已经转账成功。
  7. 商户验证支付结果并处理订单
    商户服务器验证支付宝回调通知,确认支付结果后处理订单。
  8. 用户收到支付成功反馈,商户开始发货或提供服务
    用户收到支付成功的反馈后,商户开始发货或提供服务。

支付流程中出现的问题以及解决方案

  1. 在第二步和第六步之中。商户的服务器和支付宝或者微信的服务器在进行交互的过程之中,传输的数据是异常的敏感的,所以,在交互时必须防止中间人对于信息的篡改。怎么防止进行纂改呢?这就需要我们使用到加密算法对交互的数据进行加密。
  2. 在第六步和第七步会之中,如果QPS过高会出现“超卖的问题”
  3. 支付失败或者是超时的时候,我们应该怎么处理?

数据加密方案

为了防止数据篡改,保障交易的安全性,使用加密算法保护商户与支付宝之间的通信。常见的加密算法如下:

  1. 单向加密(不可逆)
    • 算法: 一般有MD5、SHAD算法 , 这种算法只能加密,不能解密。但是MD5加密可能会出现哈希碰撞的情况,比如一个字符串"123456"和另外一个字符串"234567"经过MD5加密后可能会出现加密值相等的情况 , ,这时候我们就需要通过MD5+盐来解决哈希碰撞。
    • 应用: 这种单向加密的发生的业务场景一般是在一张用户表里面存放用户的隐私数据,比如用户的密码,在数据库中不能明文存储。
  1. 对称加密(可逆)
    • 算法:AES、DES 等
    • 加密流程:这个是服务端和用户端使用同一对密钥。密钥又分为私钥和公钥,私钥是用来加密的(签名),公钥是用来解密的(验签)。流程是客户端用私钥进行加密传输给服务端,服务端拿着公钥解密,然后将响应的数据在通过私钥进行加密返回给服务端,服务端在通过公钥解密响应数据。
    • 应用: 这种对称加密最常用的经常就是用到了HTTPS协议之中了。
  1. 非对称加密(可逆)
    • 算法:RSA2 等
    • 加密流程: 这个是服务端和客户端分别使用一对密钥。服务端拿着的是服务端的私钥和客户端的公钥,客户端拿着的是客户端的私钥和服务端的公钥。流程是:客户端用自己本地的私钥将交互的数据进行加密传输给服务端,服务端用客户端的公钥来解密传输的数据,然后将响应的数据用服务端本地的私钥进行加密,响应给客户端,客户端在得到响应的数据后,会拿着服务端的公钥进行解密,从而完成在数据交互时候,数据的加密处理。
    • 应用: 见的即使我们在支付宝支付或者微信支付的时候,我们商家的服务器和支付宝或者微信的服务器在进行数据交互的时候,就用非对称加密进行数据防纂该的。

超卖问题背景以及方案

超卖问题的背景:

现在主要的问题就是在用户支付成功的时候,我们应该如何处理订单?如果我们有一个支付业务场景就是,我们商家平台做了一个秒杀的优惠活动,在面对大量的支付请求打过来的时候,我们怎么才能确保我们的商品不会出现“超卖的问题”?或者是我们这个商家平台平时的常规流量很大,我们怎么预防这个超卖问题呢?

解决方案 :

1. 使用悲观锁

  • .对于刚入门的后端开发者来说,可能就很简单呗,我们直接在MySQL层面加一个悲观锁呗,,比如在查询库存的时候使用SELECT ... FOR UPDATE,锁定这一行数据,直到事务结束。这样其他事务必须等待,直到锁释放。这种方式虽然可以防止超卖,但会降低系统的并发性能,尤其是在高并发场景下,可能会导致大量请求阻塞,影响用户体验。

2. 使用乐观锁

  • 对于我们已经有工作经验的后端开发者,就会考虑性能的问题了,比如说我可以实现一个基于版本号或者时间戳的乐观锁,更新库存时检查版本号或者时间戳,确保在更新之前库存没有被其他事务修改过,如果被其他的事务更新过就进行回滚操作。但是单纯的依靠MySQL还是不能应对QPS较高的情况,在QPS较高的时候,读写的操作全在MySQL层面,会给MySQL造成很大的压力,从而影响用户的体验。这时候我们就需要引入redis来进行预扣库存,从而分担MySQL的压力。

3. 使用 Redis+乐观锁

  • 我们用乐观锁加上redis来应对QPS过高的情况,在redis之中我们缓存库存数量和版本号(或者是时间戳)。首先的话我们用户在下单时,先通过 Redis 检查库存是否充足。若库存不足,直接返回“已售罄”,避免大量无效的请求直接打到MySQL里面,如果现在有库存,通过Redis 的单线程特性,- 如果库存足够,使用 Redis 的 MULTIEXEC*事务来更新库存,确保操作的原子性。如果在 WATCH 后,库存数据被其他请求修改(例如另一个请求在此时也试图修改库存),那么 Redis 的 EXEC 会回滚,表示操作失败。系统可以重新尝试操作。然后设置一个定时器来定时的更细MySQL里面的库存数量。

4. Redis+乐观锁+异步队列

  • 这时候其实对于常规流量来说已经是完全足够了,但是我们要考虑一种极端的情况就是秒杀情况,这时候尽管 Redis 的乐观锁可以确保库存的原子性,但当有大量并发请求时,可能会频繁进行 Redis 的 WATCHEXEC 操作,导致MySQL或 Redis 的操作压力过大,如何缓解呢?这时候我们要引入--异步队列。
  • 请求流程: 用户发起秒杀请求,系统首先进行初步校验(例如是否在秒杀时间、库存是否足够等);
  • 如果秒杀条件满足,系统会将该请求放入 Redis 队列中,而不是立即处理;
  • 这样,所有的请求会被放入队列中,并由后端消费者逐一处理。队列的作用是将请求按照顺序排队,保证系统按顺序执行库存扣减和订单生成;
  • 请求被处理完后,用户会收到支付成功或秒杀失败的反馈。即使大量用户同时请求秒杀,使用异步队列后,系统依然能够有序地处理每个请求,避免了多个请求同时并发处理导致的 “超卖问题”。

自我疑问以及解答: 写到这里的时候,我突然想到了另一种问题,现在我们引入redis的异步队列之后,请求不就是按照先进先出的形式被消费者(在这里我们可以简单的理解为处理订单商品库存的接口)进行处理嘛?你这样不就是按照串行的方式处理了嘛?别忘了我们GO语言最大的优势就是可以创建多个用户态的轻量级线程(就是协程)。我们只需要通过 增加消费者数量 来提高并发处理能力。例如,系统可以设置多个消费者并行消费队列中的请求,从而加速请求处理。消费者并发数 可以根据服务器的处理能力和队列中的积压情况动态调整,避免过度并行带来的资源消耗。 经过上面的总结,我们可以通过乐观锁+redis+异步队列来解决超卖的问题。

支付失败或超时方案

1.使用 Redis 的缓存过期策略 TTL:

  • 现在总结已经接近末尾了,但是又想到一种情况就是用户在支付的时候,如果因为支付超时或者是支付失败的话是怎么处理的?我想到的就是redis里的一种缓存过期策略TTL(设置键值对的过期时间),比如说在我们购买的时候在Redis 中标记库存为“预占”状态,支付成功后再同步到数据库,失败则自动释放。

总结

通过使用乐观锁、Redis 缓存、异步队列等技术,可以有效避免支付业务中的超卖问题。异步队列还能提高系统的处理能力,避免在高并发场景下造成性能瓶颈。此外,支付时的加密保障了数据的安全性,确保了用户和商家的信息不被篡改。