Security//4 min

Stop Repeat Notices from Charging or Crediting Twice

Payment and booking services may send the same notice more than once. Your app must recognize repeats before they cause a second customer action.

before you start

One customer event should produce one result, even when the same notice arrives several times.

Understand why one event can produce two actions

in plain words

An outside service may send the same notice again when it does not receive a quick confirmation from your app.

A customer can pay once yet receive two store credits, two welcome emails, or two orders. This can happen because a payment, booking, form, or delivery service sends your app a notice when something occurs. The technical name for this automatic service-to-app notice is a webhook. It is useful because your app can react without someone checking the other service manually, but every reaction must be designed to handle repeated delivery safely.

A repeated notice does not automatically mean someone is attacking your app. The sending service may have waited for a reply, lost the connection, or restarted after maintenance. It then sends the notice again because it cannot be sure the first copy worked. Your app should treat the next copy as a reminder to confirm the existing result, not as permission to charge, credit, reserve, email, or update the customer again.

  • ▸One payment confirmation should create one order.
  • ▸One booking confirmation should reserve one time slot.
  • ▸One approved refund should return money once.

common risk

A payment service sends the same approved-payment notice twice. The app treats both copies as new and adds two credits to the customer's balance.

what to do now

List every outside service that can start a customer action and write down exactly what one accepted notice is allowed to do.

ask your AI

Inspect my app and list every outside service that sends automatic notices to it. For each notice, identify the unique reference supplied by the service, the customer action it starts, where that action happens, and what currently prevents the same notice from causing the action twice. Do not change the app yet; give me a clear report first.

Give every notice a one-time claim

in plain words

Before doing anything for a customer, your app must check whether that exact notice has already been handled.

Each incoming notice should contain a unique reference created by the sending service. Before creating an order, adding credit, changing a membership, or sending an email, your app should claim that reference in its records. If the claim already exists, it should return a normal confirmation without repeating the customer action. Developers call this behavior idempotency: processing the same instruction again leaves the app with the same final result instead of producing another result.

The organized storage used by an app is technically called a database. Save the service name, unique notice reference, arrival time, work status, and final outcome there. Developers call a rule that forbids a second matching reference a unique constraint. Use the reference supplied by the service rather than a customer email, name, amount, or current time. One customer can have several legitimate purchases, while two different customers can share a name or payment amount.

  • ▸Save the service name together with its notice reference.
  • ▸Track whether work is new, started, completed, rejected, or awaiting review.
  • ▸Let the app's storage enforce the one-reference rule.

common risk

The app uses a customer's email address to identify repeats, so a legitimate second purchase is ignored while some true duplicates still pass through.

what to do now

Add a permanent record for each notice and a storage rule that refuses a second claim for the same service and reference.

ask your AI

Implement idempotent handling for every incoming service notice. Before any order, charge, refund, credit, membership change, booking, customer update, or email, save the service name and the service's unique notice reference. Add a database unique constraint covering both values. If that claim already exists, return a successful acknowledgement without repeating the customer action. Store new, started, completed, rejected, and needs-review states.

Confirm the sender and reject old copies

in plain words

A notice must prove where it came from before your app treats it as an instruction.

A message can claim that a payment succeeded without coming from the payment company. Trusted services attach protected proof that your app can check before changing customer information or money. The technical name for checking that proof is signature verification. The service gives your app a signing password, often called a signing secret, which allows the app to distinguish a genuine notice from an invented or altered one. Missing or invalid proof should cause no customer action.

The computer process that performs protected app work away from visitors is what developers call a server. Keep the signing password in server-only settings, never in files delivered to a visitor's device. When the service includes a trustworthy sending time, accept only a short, documented age window. Developers call stopping an old valid notice from being submitted again replay protection. Sender checking and repeat detection are both necessary because a genuine service can also resend a genuine notice.

  • ▸Check the service's protected proof before acting.
  • ▸Reject missing, invalid, altered, or excessively old notices.
  • ▸Keep the service signing password away from visitor-delivered files.

common risk

An app accepts any message sent to its payment-notice address and marks an account as paid without confirming who sent it.

what to do now

Use the sending service's official checking method and apply its recommended age window before reading the notice as an instruction.

ask your AI

Review every place where my app receives automatic notices from outside services. Before changing any customer information, order, payment, refund, credit, booking, membership, or email, verify the notice with the service's official signature method. Keep each signing secret in server-only environment settings, reject missing or invalid signatures, enforce the service's recommended timestamp tolerance, and do not record signing secrets in logs or error messages.

Prepare for two copies arriving together

in plain words

Two matching notices can arrive so close together that a simple check followed by a save lets both continue.

Imagine two workers checking the same list at the same moment. Both see an empty line, both write their names, and both perform the job. An app can make the same mistake if it first asks whether a notice reference exists and only saves it later. Both copies may pass the check before either copy saves its claim. This timing problem matters most for money, limited bookings, inventory, account credits, and messages sent to customers.

The claim must be completed as one indivisible storage step. The technical name for an indivisible step is an atomic operation. Let the database accept one claim and reject the other through its unique constraint. Mark the accepted work as started before performing the customer action. If work stops halfway through, keep an unfinished status and enough non-sensitive detail for controlled review. Do not blindly repeat a charge or credit when the earlier outcome is uncertain.

  • ▸Claim the notice reference in one indivisible step.
  • ▸Allow only the winning claim to perform the customer action.
  • ▸Send uncertain or unfinished work to review instead of guessing.

common risk

Two identical notices arrive within milliseconds. Both pass a separate existence check, and each adds the same monthly credit.

what to do now

Test two matching notices at the same time in a safe test setup and confirm that exactly one customer action occurs.

ask your AI

Make notice claiming safe when two identical copies arrive at the same time. Use one atomic database operation and a unique constraint on service name plus notice reference. Only the request that creates the claim may perform the customer action. A duplicate must acknowledge receipt without repeating the action. If the winning request stops midway, mark it needs-review and do not automatically repeat an uncertain charge, refund, credit, booking, or email.

Keep records and test after every important change

in plain words

Clear records and repeat tests help you find a missing safeguard before customers see duplicate results.

Keep a small history showing the service name, notice reference, arrival time, checking result, work status, and outcome. Developers call these records logs. Do not put customer passwords, full payment card numbers, signing passwords, or unnecessary message contents in them. Review repeated, rejected, failed, and unfinished work regularly. An unusual change in those patterns can reveal a broken connection, a sender-checking problem, or a recent app change that removed the one-time claim.

After changing payment, signup, booking, delivery, or automation behavior, send the same harmless approved test notice twice and confirm that only one result appears. Then test an invalid notice and an old notice, confirming that neither changes customer information. Developers call repeated checking over time continuous monitoring. VibeCodeWall checks the public app from the outside and watches for important changes over time. It does not see private app instructions or replace the records and tests inside your app.

  • ▸Test approved repeats, invalid notices, old notices, and simultaneous copies.
  • ▸Review unfinished work before retrying anything that affects money or customer access.
  • ▸Run the checks again after related app changes.

common risk

A payment-flow update replaces the old notice handler, and the new version creates orders before checking whether the notice was already completed.

what to do now

Create a repeatable safety test for each important service and run it after every related change.

ask your AI

Create and run a safe automated test plan for every outside service that sends notices to my app. Test one valid notice, the same valid notice twice, two matching copies sent concurrently, an invalid signature, an expired timestamp, and a failure after work starts. Confirm that valid input creates exactly one result, rejected input changes nothing, unfinished work is marked for review, and no password, signing secret, full payment card number, or unnecessary customer information appears in logs.

Quick checklist

  1. 01List every outside service that sends notices to your app.
  2. 02Write down the customer action each kind of notice can cause.
  3. 03Save the unique reference included with every notice.
  4. 04Make one storage rule prevent the same reference from being claimed twice.
  5. 05Confirm who sent a notice before changing an order, payment, credit, or customer record.
  6. 06Keep the service signing password where visitors cannot download it.
  7. 07Reject genuine but outdated notices when the service provides a sending time.
  8. 08Test two matching notices arriving at nearly the same moment.
  9. 09Record accepted, repeated, rejected, and unfinished work without storing passwords or full payment card numbers.
  10. 10Repeat the tests after payment, booking, signup, or automation changes.

FAQ

Are repeated service notices always an attack?

No. Services commonly try again when they do not receive a quick confirmation. Your app should safely handle both ordinary retries and deliberately repeated messages.

Can the customer's email address identify a repeated notice?

No. One customer may make several valid purchases or bookings. Use the unique notice reference created by the sending service.

What if the first attempt stops halfway through?

Record that the work is unfinished and review it in a controlled way. Do not automatically repeat a charge, refund, credit, booking, or email when the first outcome is uncertain.

Is checking the sender enough?

No. A genuine service can send the same genuine notice more than once. Your app must confirm the sender and also prevent a previously claimed reference from causing another action.