Preventing payment to be processed twice

I have app with paymemt/subscription service, pretty much how the payment works is:

Initial payment

  1. User initiate payment on the web ( click on “subscribe”)
  2. Back-End send init request to the gateway – it returns payment id ( also used for subscribe payments )
  3. Back-End send process request to the gateway
  4. Gateway returns url to redirect
  5. User is redirected to the gateway, fills credit card etc and pays
  6. Gateway redirects back to the back end

In 6th step, after gateway redirects back to the back-end with POST request, i match returned payment id with payment id stored in DB, mark it as done and add membership to the user.

For membership i use the bable

create table membership
(
    id         bigserial primary key,
    start_date date not null,
    end_date   date,
    resub_date date,
    client_id  bigserial
    active     boolean default false
);

Now my resub mechanism is following, i have task that executes every midnight and does:

  1. Retrieves all memberships that have resub_date for today and are active
  2. init payment
  3. process payment

I am using ThreadPool for steps 2 and 3, to in, so each thread init and process payment for membership, then takes another etc etc.

My question/problem is with “unlucky” timing during canceling membership and subscribing again

Imagine following scenario

  1. Midnight of 1.10.2024
  2. Periodic task executes
  3. Task retrieves all user that should be resubscribed – including user A
  4. Task starts to process membership of user A – check if he is subscribed again – yes and inits payment
  5. User A on web cancel membership
  6. User A on web resubscribe again – thus leading to **Initial payment** flow
  7. Task is finished with inits payment and start process payment

In this case the user would pay twice.

In both init and process i can check if the user should be resubscrbed again ( but this would mean 2 additional database hits per resubscribe ), but with some unlucky timing the payment could still be processed twice.

What are the best ways to prevent these types of scenarios? Some kind of locking on row at db level, or some kind of custom lock that would lock on user id – in this case the performance? Somehow integrate redis into this? I know the chances for this are very slim but it is still problem.

I am using java so i am tagging this question as java but the answer doesnt need to be java specific.

Thanks for the help!

1

There is no way to fully prevent a double payment. The best way to deal with it in my view is to detect the situation after it happened and pay back the payment that should not have happened.

With the scenario you described, I would deal with it in the following way:

  1. Any subscription, even a re-subscription, creates a new membership record.
  2. When the payment process for continued subscription is initiated, it will not be stopped by changes to the corresponding membership record.
  3. If a subscription is cancelled within X time of initiating payment for continuing the subscription, once the payment processes have completed, a credit-payment will be performed on the recently cancelled subscriptions.

This does not prevent someone from paying twice, or paying for a cancelled subscription, but it tries to correct for the situation. This is a form of eventual consistency: the amount payed by the customer is not at all time equal to the amount due, but eventually the two match.

3

I have doubts your logic is complete around steps 5 and 6:

  1. User A on web cancel membership
  2. User A on web resubscribe again – thus leading to Initial payment flow

When a user cancels membership, don’t you REFUND the user?

You can ensure data consistency by wrapping all the relevant steps in a transaction.
As you’re describing an interplay of multiple components, this will likely have to be a distributed transaction.

Not all technology stacks support distributed transactions. In the past, I had good success getting .Net code to participate in a transaction with a SQL Server database and an MSMQ (message queue) via msDTC (distributed transaction coordinator). This ensures that all components either commit or roll back together, maintaining consistency.

Outside the Microsoft stack, I read about (but did not use yet) a number of distributed transaction coordinators, such as Atomikos, Narayana, Bitronix Transaction Manager (BTM), SEATA (Simple Extensible Autonomous Transaction Architecture), Apache Kafka Transactions (with Kafka Streams) and OpenXA. Many of these are Open Source.
Each of these transaction coordinators can manage distributed transactions across heterogeneous systems, including databases, message queues, and services.

New contributor

Radu Popa is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.

1

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật