;; lib/commerce/refund.sx — refund lifecycle as a second flow-on-sx flow. ;; ;; A refund is request → approve → settle, with TWO genuine suspension points: ;; approval (a human/policy decision) and settlement (the provider issuing the ;; refund). Like order.sx the flow is pure orchestration carrying only the ;; order-id; the SX driver does all ledger IO and reuses order.sx's generic flow ;; helpers (order-flow-waiting/-resume/-status, order-susp-id). ;; ;; refund-begin! → ledger :refund-requested, flow suspends at 'approve ;; refund-approve! → resume past approval, flow suspends at 'settle ;; refund-settle! → ledger :refunded (idempotent), flow completes ;; refund-reject! → ledger :refund-rejected, flow cancelled ;; ;; Only :refunded moves the books (recon.sx), so a requested-but-unsettled or ;; rejected refund leaves reconciliation unchanged. (define refund-flow-src "(defflow refund-lifecycle (lambda (oid) (begin (request (quote approve) oid) (request (quote settle) oid))))") (define refund-make-env (fn () (let ((env (flow-make-env))) (begin (flow-run-in env refund-flow-src) env)))) ;; Register the refund flow into an existing (e.g. order) env. (define refund-flow-load! (fn (env) (begin (flow-run-in env refund-flow-src) env))) (define refund-flow-start (fn (env oid) (flow-run-in env (str "(flow/start refund-lifecycle \"" oid "\")")))) ;; --- ledger writes --- (define refund-request (fn (b oid ref at amount) (persist/append-once b (order-stream oid) (str "refund-req/" ref) :refund-requested at {:amount amount :ref ref}))) ;; --- lifecycle --- ;; Open a refund: record the request, start the flow, suspend at approval. (define refund-begin! (fn (env b oid ref at amount) (begin (refund-request b oid ref at amount) (order-susp-id (refund-flow-start env oid))))) (define refund-approve! (fn (env id) (if (= (order-flow-waiting env id) "approve") (begin (order-flow-resume env id :approved) :approved) :not-pending-approval))) (define refund-reject! (fn (env b oid id at reason) (if (= (order-flow-waiting env id) "approve") (begin (persist/append b (order-stream oid) :refund-rejected at {:reason reason}) (flow-run-in env (str "(flow/cancel " id ")")) :rejected) :not-pending-approval))) ;; Settle (provider issued the refund): idempotent — only acts while waiting on ;; settle, so a replayed provider callback returns :already-settled. (define refund-settle! (fn (env b id oid ref at amount) (if (= (order-flow-waiting env id) "settle") (begin (order-refund b oid ref at amount) (order-flow-resume env id :settled) :settled) :already-settled)))