Skip to content

simplestore: expired (unpaid) orders are marked as paid #766

Description

@karamble

Summary

In client/resources/simplestore, when an order's invoice expires without
being paid, the order is set to status paid. An unpaid, expired order is
therefore indistinguishable from a completed sale, and any consumer that reacts
to the paid status (via the StatusChanged callback) registers a false payment.

Details

invoiceExpired is a near-verbatim copy of invoiceSettled and still sets the
order status to StatusPaid. In client/resources/simplestore/simplestore.go
(invoiceExpired):

// Mark order as paid. First, reload the full order from disk.   // copied comment
...
// Now update status.
order.Status = StatusPaid                                        // wrong for an expired/unpaid order

Orders expire one hour after placement (ExpiresTS = time.Now().Add(time.Hour)
in handlers.go), so any order not paid within that window flips to paid.

The rest of invoiceExpired is already correct: it does not send the digital
files, it logs the order "as expired", and it DMs the buyer that the order "has
been identified as expired". Only the status assignment (and the copied
// Mark order as paid. comment) is wrong.

Impact

  • An expired, unpaid order shows as paid to the merchant.
  • A StatusChanged consumer keying on StatusPaid (e.g. to notify "payment
    received") fires a false positive.
  • Since the file-send loop is skipped, the order is paid with no delivery,
    which is itself inconsistent.

Expected

An expired, unpaid order should be a distinct, non-paid terminal state.

Suggested fix

Minimal: set the existing (currently unused) StatusCanceled instead of
StatusPaid, and fix the copied comment:

-   // Mark order as paid. First, reload the full order from disk.
+   // Mark order as canceled. First, reload the full order from disk.
    ...
-   order.Status = StatusPaid
+   order.Status = StatusCanceled

Alternatively, introduce a dedicated StatusExpired to distinguish an expired
order from a merchant/buyer cancelation. Happy to open a PR for whichever you
prefer.

Related (separate, optional)

handleAdminUpdateOrderStatus in admin.go writes a paid status without
running the file-send loop, so the admin "set paid" path never delivers digital
downloads. Noting in case it is of interest; can file separately.

Version

Present on master and in the latest release (v0.2.4).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions