Back to BlogIndustry Guides

Building Online Rent Collection: Stripe, Late Fees and Reminders

Rupak Amin

Founder & Lead Engineer, RAITHub

11 min read

An online rent collection system needs four parts: a payment provider such as Stripe, a charge per lease per period that each payment settles, reminders that send once per charge, and late fees that respect the grace period and never hit a payment still in transit. With ACH bank debits taking up to 4 business days to confirm, that last rule needs the most care.

This post is for founders and teams building rent collection into a property management or PropTech product. It draws on PropDesk, the property management platform RAITHub built, which collects rent through Stripe with automated late fees on a configurable grace period, rent reminders, payment plans for tenants in hardship and scheduled rent increases, and runs 1,024 automated tests. The ledger, roles and wider product are covered in property management software development; this post stays on the money moving in. Where something is engineering guidance rather than PropDesk proof, it says so.

Should tenants pay rent by card or by bank debit?

Offer both, and make bank debit the default for recurring rent. On Stripe's published US pricing, a domestic card costs 2.9% + 30¢ and ACH Direct Debit costs 0.8% capped at $5.00 (Stripe pricing). For rent, the cap is what matters.

FactorCardACH Direct Debit
Fee on $1,200 rent$35.10$5.00 (0.8% would be $9.60; the cap applies)
Fees over 12 months$421.20$60.00
ConfirmationAt paymentUp to 4 business days
Disputes$15.00 fee per card disputeUp to 60 calendar days on a personal account, and final through the ACH network
Set-upCard detailsA mandate the tenant accepts, and a verified bank account

Sources: Stripe pricing and Stripe: ACH Direct Debit, checked 29 September 2026. Who pays that fee, the landlord or the tenant, is a product decision, and passing card fees to tenants is regulated differently from place to place. That is general information; confirm the rules for your market with your adviser.

How does an ACH rent payment move through Stripe?

Slowly, and not always in one direction. Stripe describes ACH Direct Debit as a "delayed notification payment method that can take up to 4 business days to receive acknowledgement of success or failure" (Stripe: ACH Direct Debit). Your system has to model the wait, not just the result.

What happenedStripe webhook eventWhat your system should do
Tenant submitted a bank paymentpayment_intent.processingRecord the payment as processing against the charge; show "payment in progress" to the tenant and landlord
The bank debit clearedpayment_intent.succeededMark the payment succeeded; send the receipt
The debit failed (insufficient funds, closed account)payment_intent.payment_failedMark it failed, reopen the charge, notify the tenant, let the late-fee rules run
A failure or dispute arrived after successcharge.dispute.createdReverse the payment in the ledger with a reason; do not delete it

The last row surprises people. Stripe's documentation says that, in rare situations, an ACH failure can arrive after a payment has moved to succeeded, in which case Stripe opens a dispute with a reason such as insufficient_funds. A disputed payment also invalidates the mandate, so the tenant must authorise again before the next debit. A rent system that treats "succeeded" as permanent will show a paid month that was never paid.

Two set-up details are worth designing for early. The tenant must accept a mandate, the authorisation to debit their account, and Stripe displays and records it for you when you use its hosted Checkout or Payment Element. And if a tenant enters bank details by hand instead of logging in to their bank, Stripe verifies them with microdeposits, which the tenant has 10 days to confirm. Collect bank details well before the first due date.

Every one of those webhooks can arrive twice or out of order. Handle them idempotently, as described in how to build webhooks.

How should automated late fees work?

A late fee should be applied once per charge, only after the grace period ends, only if the charge is still uncovered, and never on money that is already on its way. PropDesk applies late fees automatically after a configurable grace period; the rules below are the engineering behind that kind of job.

  • Grace period in the landlord's time zone. A due date is a calendar day where the property is, not a UTC timestamp.
  • A pending bank debit counts as paid, for now. A tenant who started an ACH payment on the due date should not be charged a fee because the bank took four days. If the debit later fails, the fee can apply then.
  • One fee per charge. Enforce it in the database with a unique constraint, so a job that runs twice cannot double-charge.
  • Configurable amounts, with caps. Many places limit late fees. In New York, for example, a late fee may not be charged until rent is five days late, and may not exceed $50 or 5% of the monthly rent, whichever is less (New York Real Property Law § 238-A). This is general information, not legal advice: the rules for each property come from the landlord and their adviser, and the software should make them settings, not code.

A minimal version of the decision, in TypeScript:

type Cents = number

interface RentCharge { id: string; amount: Cents; dueDate: string } // YYYY-MM-DD, property's time zone
interface Payment {
  chargeId: string
  amount: Cents
  initiatedOn: string // YYYY-MM-DD, property's time zone
  status: 'processing' | 'succeeded' | 'failed'
}
interface LateFeePolicy { graceDays: number; fee: Cents; maxFee?: Cents }

function addDays(isoDay: string, days: number): string {
  const d = new Date(isoDay + 'T00:00:00Z')
  d.setUTCDate(d.getUTCDate() + days)
  return d.toISOString().slice(0, 10)
}

export function lateFeeDue(charge: RentCharge, payments: Payment[], today: string, policy: LateFeePolicy): Cents {
  const lastOnTimeDay = addDays(charge.dueDate, policy.graceDays)
  if (today <= lastOnTimeDay) return 0
  // Money started on time and not failed counts, even if it is still processing.
  const covered = payments
    .filter((p) => p.chargeId === charge.id && p.status !== 'failed' && p.initiatedOn <= lastOnTimeDay)
    .reduce((sum, p) => sum + p.amount, 0)
  if (covered >= charge.amount) return 0
  return policy.maxFee === undefined ? policy.fee : Math.min(policy.fee, policy.maxFee)
}

The function decides; the job that calls it writes the fee as its own ledger entry, and the database's unique constraint makes the write happen once. A fee applied in error is corrected with a reversing credit, never by editing the fee.

How do you send rent reminders that go out exactly once?

Claim the reminder in the database before you send it. PropDesk sends automated reminders to tenants before rent is due; the pattern below is the engineering guidance for any reminder job that might run twice, run late, or run on two servers at once.

CREATE TABLE rent_reminder (
  charge_id uuid NOT NULL REFERENCES rent_charge (id),
  kind      text NOT NULL CHECK (kind IN ('before_due', 'due_today', 'overdue')),
  sent_at   timestamptz NOT NULL DEFAULT now(),
  PRIMARY KEY (charge_id, kind)
);

-- Send only if this returns a row: a second run finds the row and returns nothing.
INSERT INTO rent_reminder (charge_id, kind)
VALUES ($1, 'before_due')
ON CONFLICT DO NOTHING
RETURNING charge_id;

Three rules around it:

  • Skip charges already covered, including by a payment still processing. A "rent is due" message after the tenant has paid erodes trust quickly.
  • Catch up after a missed day. Select reminders that are due on or before today and not yet claimed, so a job that failed yesterday still sends today.
  • Record send failures. If the email provider rejects the message, mark the row for retry rather than losing it. An outbox table does this cleanly.

What should happen when a rent payment fails?

Reopen the charge, tell the tenant in plain words, and give them a way to pay that does not wait another four days. A failed bank debit is common enough that it should be a designed journey, not an error page.

  1. Tell both sides. The tenant gets the reason and a link to pay; the landlord sees the charge back in arrears.
  2. Offer a faster method. A card payment settles at once, at a higher fee, which a tenant near the late-fee line may prefer.
  3. Retry carefully. If you bill through Stripe invoices, Stripe can retry a failed ACH debit caused by insufficient funds a maximum of 2 times, within 40 days of the original attempt (Stripe: ACH Direct Debit). Make sure a retry and a manual payment cannot both succeed for the same charge.
  4. Payment plans for hardship. PropDesk supports payment plans for tenants in hardship. Model a plan as instalments that together settle the original charge, and decide explicitly whether late fees pause while the plan is kept.

How do you test a rent collection system?

Test the dates and the replays, because that is where rent systems break. PropDesk's 1,024 automated tests are 932 unit and 92 end to end, run in CI (PropDesk case study). For rent collection specifically, these are the cases worth writing first:

Test caseWhat must hold
Payment started on the last grace day, still processingNo late fee
That payment fails two days laterCharge reopens; late fee applies once
Late-fee job runs twice on the same dayOne fee
Same webhook delivered twice, or succeeded before processingOne payment, correct final status
ACH failure arrives after successReversing ledger entry; charge back in arrears
Due date at month end, property in a different time zone from the serverFee day computed in the property's time zone
Reminder job missed a dayReminder still sent, once

The method behind this is in how RAITHub tests software, and it is available on its own as QA and test automation.

Why RAITHub for this, and when to use a tool instead

RAITHub built PropDesk's rent collection on Stripe, with late fees on a configurable grace period, rent reminders, payment plans and scheduled rent increases, inside a platform with 4 roles and 1,024 tests. Payment work is quoted as fixed scope after a free 15-minute technical audit, and you own the code.

Use a tool instead if:

  • You are a landlord collecting your own rent. Property management tools already include rent collection; building it for one portfolio will not pay back.
  • You only need a way for a few tenants to pay. Recurring invoices or payment links from your payment provider may be enough.
  • You would hold rent for other owners. That brings client-money or trust-accounting rules that need specialist advice before any code. RAITHub has not shipped a trust-accounting system.
  • You want developers placed in your team, or a certified vendor. RAITHub does not offer staff augmentation and is not SOC 2 or ISO 27001 certified.

If rent collection is part of the product you are building, book the free 15-minute technical audit with your payment methods, grace rules and reminder schedule. The PropTech page sets out what a build includes.

Stripe pricing and documentation checked on 29 September 2026.

Frequently asked questions

What does it cost to collect rent online with Stripe?

On Stripe's published US pricing, 2.9% + 30 cents per domestic card payment and 0.8% capped at $5.00 for ACH Direct Debit. On $1,200 rent, that is $35.10 by card and $5.00 by bank debit.

How long does an ACH rent payment take to clear?

Stripe says ACH Direct Debit can take up to 4 business days to confirm success or failure. During that time the payment is processing, and your system should treat it as covering the rent unless it fails.

Should a late fee apply if the tenant's bank payment is still processing?

Not if the payment was started on time. Count processing payments as covering the charge, and apply the fee only if the payment later fails. The fee amount and grace period should be settings, because local rules differ.

How do you stop duplicate rent reminders?

Record each reminder in the database with a unique key per charge and reminder type, insert it before sending, and send only if the insert succeeded. A second run of the job finds the row and sends nothing.

Can a successful ACH rent payment be reversed later?

Yes. Stripe notes that in rare cases an ACH failure arrives after success, and tenants can dispute debits from a personal account for up to 60 calendar days. Record the reversal as a new ledger entry and reopen the charge.

Does PropDesk collect rent through Stripe?

Yes. PropDesk, built by RAITHub, collects rent through Stripe with automated late fees on a configurable grace period, rent reminders, payment plans for hardship and scheduled rent increases.

Online rent collectionRent collection softwareStripe ACHLate feesRent remindersPropTechProperty management software

Ready to discuss your project?

Book a free 15-minute technical audit with our engineering team.