GAE / P:API调用的交易安全性

gae*_*fan 8 python google-app-engine transactions app-engine-ndb

假设您使用交易来处理Stripe付款并更新用户实体:

@ndb.transactional
def process_payment(user_key, amount):
    user = user_key.get()
    user.stripe_payment(amount) # API call to Stripe
    user.balance += amount
    user.put()
Run Code Online (Sandbox Code Playgroud)

Stripe API调用有可能成功,但put由于争用而失败。然后会向用户收费,但他的帐户不会反映付款。

您可以将Stripe API调用从事务中拉出,然后再执行事务,但是看来您仍然遇到相同的问题。收费成功,但交易失败,并且未记入用户的帐户。

这似乎是一个非常常见的情况。如何正确处理呢?

Dan*_*scu 3

为了正确操作,事务功能需要是幂等的。因此,您不能在此类函数内进行条带调用,因为这会使其成为非幂等的。

我会将条带调用分开,并且在 API 成功后,我将调用事务函数来更新用户的帐户余额(在发生争用时可以安全地重试)。

甚至可能创建一个单独的、独立的实体来反映条带 API 调用结果?这样的实体应该没有争用的空间,因为它只写入一次 - 当条带事务发生时。这将使您能够:

  • 保留账户交易历史记录 - 指向这些实体
  • 进行一些健全性检查,查找孤立条带交易(如果由于某种原因帐户交易调用即使在重试后仍失败)并对此采取措施。

@thebjorn 的评论很好:多步骤方法可以使该过程非常可靠:

  • 更新帐户的事务函数,旨在执行条带事务,该函数还会以事务方式将推送任务排入队列以执行条带 API 调用。仅当事务成功时任务才会入队
  • 推送队列任务进行 Stripe API 调用(最终创建 Stripe 交易实体),并在成功后调用交易函数来更新账户余额