Proposal: Stable Post-Purchase Lifecycle Hooks for Returns, Refunds, and Line-Level Add-Ons #2440
Replies: 6 comments
-
|
Hey there, thanks for the comprehensive write-up. This is a big part of what v2 is looking to solve, so the timing I guess is unfortunate. Some of the events proposed would be fairly straightforward. The order line logic I think is a problem as it simply doesn't exist in v1. Refunds etc are not linked in any way to order lines. So I'm not sure how that would be solved. We also have to consider v2, so that any v1 updates don't go in a different direction. Luckily it's still WIP so we have flexibility there. I don't think we'd want the additional packages in the monorepo, they would be best as community add-ons I think right now for v1. However, some of the concepts may well be appreciated for v2, eg. I wanted the v2 core to have the concept of customer credit and a basic ledger for it. Perhaps if you can propose your approach and we can see what might work? |
Beta Was this translation helpful? Give feedback.
-
|
Thanks for the thoughtful response, and for the time you all are putting into both v1 and v2. That all makes sense. I definitely do not want to push v1 in a direction that fights what you are planning for v2, and I also agree the add-ons themselves are better as community packages for now rather than going into the monorepo. My goal on the v1 side is not to make core own returns, subscriptions, or credits. The goal is to add a small, general-purpose extension surface that lets community add-ons integrate cleanly with checkout, payment success, refunds, and post-purchase line behavior. The main thing I want to avoid is package authors having to bypass Lunar internals or duplicate payment/refund orchestration just to build common commerce add-ons. Without being able to see v2, it's hard to plan from my side or write code for it. I know you're planning a grand reveal, but for open-source and code contributions, it makes it a bit harder. I would like to contribute to the core in a way that doesn't duplicate work or cause headaches for you for v2. Here is the approach I had in mind. 1. Payment success hooksThis is the most broadly useful and seems like the safest starting point for v1. Proposed ideaAdd a stable event when an order is actually considered paid / financially complete. WhyPackages need one reliable place to react when payment has succeeded:
Suggested shape
I understand the concern around order-line logic in v1. I am not imagining line payment state stored separately in core. For v1, So this would be a convenience lifecycle event, not a new persistence model. Example payload:
If 2. Refund authorization + refund lifecycle hooksThis is the biggest need for a returns / RMA package. Proposed ideaAdd:
WhyA returns package needs to be able to require an approved return workflow before money is actually refunded. An event after the refund is useful, but it does not solve the main workflow problem. The key missing piece is a clean veto point before the payment driver is called. Suggested surfaceContract / guard
Purpose:
This would let a returns package say:
That avoids having packages override admin behavior or throw from listeners. Events
Payload could include:
This part seems useful regardless of whether v2 later changes the deeper refund model. 3. Refund allocation as a contract, not core refund persistenceI agree with the concern that v1 does not link refunds to order lines. I am not proposing that core suddenly implement native line-item refunds in v1. Proposed ideaKeep Lunar’s refund truth at the transaction level, but optionally expose a line-allocation contract / payload shape for packages. WhyReturns packages still need line-level refund truth, even if the actual payment refund is order-level. For example:
Suggested shape
Important point:
So in v1, Lunar could still refund at the transaction level exactly as it does now, while packages own their own ledger tables and line-allocation truth. 4. Order-line capability contract rather than full line lifecycle stateI think this may be the better fit for v1 than trying to force deeper order-line lifecycle into core. Proposed ideaExpose a capability resolver contract for order lines. WhyAdmin/storefront code often needs to ask:
That is a real need for returns, subscriptions, and credits, but it does not necessarily require core to model a full line lifecycle state machine. Suggested shape
Purpose:
This feels v1-compatible because it is additive and does not require changing Lunar’s data model. 5. Credits as a secondary concern for v1, but important for v2 directionSince you mentioned v2 may include customer credit and a basic ledger, I think this is worth at least framing now, even if it is not all implemented in v1. Proposed ideaA credit application / eligibility surface that lets stored value reduce the remaining external payment amount without acting like a fake payment driver. WhyA credits package needs to:
Suggested shape
I see this as more directional for v2 and possibly optional for v1. I would not expect the whole customer-credit system to land in v1 core, but if there is a preferred direction for how v2 wants to think about credits, I would like to avoid building the add-on in a way that conflicts with that. 6. What I think core should own vs package should ownCoreA small generalized extension surface:
PackagesAll package-specific workflow and persistence:
So I am not asking core to own the business domains, only to expose the commerce lifecycle points that packages need in order to integrate cleanly. 7. My v1 preferenceIf it helps to keep scope tight, I think the most practical v1 subset is:
That alone would unlock a lot for a returns package and would also help subscription activation. Then, if it still feels aligned with v2 direction, the next layer would be:
And credits can stay mostly in community-package land for v1 unless there is a specific core direction you already know you want to preserve for v2. If this overall direction seems sensible, I can follow up with a tighter proposal showing:
I am happy to follow your lead on naming and shape. My main goal here is to propose a framework that is small enough to fit v1, but not so narrow that community add-ons have to work around core. |
Beta Was this translation helpful? Give feedback.
-
Not so much a grand reveal, just that we may still make fundamental changes and don't want to reveal something that ultimately is not what we deliver. I am happy to see a PR for the first subset, and then we can consider how the order line side of things would work after. |
Beta Was this translation helpful? Give feedback.
-
|
I've broken this down into 3 PRs:
This first PR is intentionally the largest, because it needs to be enough to unblock a reusable returns/RMA package. Refund hooks alone are not sufficient for that. Returns also needs a generalized way to ask line-level questions like isRefundable / requiresPhysicalReturn, and a clean way to attach package-owned allocation data to the order-level refund flow without introducing line-level refund persistence into core.
This is mainly for subscriptions and other line-oriented post-purchase activation use cases. It stays additive and explicitly does not introduce independent line-level financial state in core.
I think this split gives us the best balance of usefulness and reviewability:
Across all 3 PRs, the boundary stays the same:
Once i get the 1st PR working along with my returns / RMA package, then I'll post the 1st PR. |
Beta Was this translation helpful? Give feedback.
-
|
I've made PR #2443 as a start. Let me know what you think and if i should change any of the elements / function calls. |
Beta Was this translation helpful? Give feedback.
-
|
Any feedback on #2443? Thanks. @glennjacobs |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
I’m looking at building a reusable
Returns / RMApackage for Lunar, as well as subscription and credit packages. I chatted briefly about this on Discord, and as much as I want to wait for version 2.x in hopes these features will be available, I need to get the e-commerce portion going sooner than later.The main gap I’m hitting is post-purchase extensibility. Lunar already has good extension points around carts, orders, and payment drivers, but add-ons still need to duplicate some lifecycle logic once an order has been paid, refunded, cancelled, or handled at the line level.
What would help most is a small set of stable lifecycle hooks/contracts that packages can build on. I'm willing to code these and update the docs repo, but I just want some guidance if these would be accepted into the mainline. I'm also willing to contribute the returns / RMA package, credit package, and subscriptions package too, but as separate PRs.
If this seems reasonable, I can put together a concrete proposal for the hooks, events, contracts, and listener flow before I start implementation.
Sleepy time details
Requested hooks/contracts:
OrderPaid/OrderLinePaid: a reliable signal that payment actually succeeded, so add-ons can start post-purchase behavior from the correct lifecycle point.RefundRequested: fires before refund execution, so add-ons can run approval checks, eligibility logic, and prepare their own accounting/workflow state.RefundCompleted: fires after a successful refund, so add-ons can finalize workflow state and link their records to the Lunar refund transaction.RefundFailed: fires when refund execution fails, so packages can keep state accurate and recover cleanly.created,paid,cancelled,refunded, andcompleted: this would let packages react to purchased lines directly instead of inferring everything from the order aggregate.isRefundable,isCancellable,requiresPhysicalReturn,createsEntitlement,supportsEndOfTerm, orallowsAccountCreditwithout hardcoding package-specific rules.DB / migrations:
return_requests,return_lines,refund_allocations, plus optional audit/receipt records.Beta Was this translation helpful? Give feedback.
All reactions