Payment Change Settlement Gap Mining
Payment Change Settlement Gap Mining
Section titled “Payment Change Settlement Gap Mining”Scope: add source-feature checklist evidence without touching
source-feature-checklist.md or existing gap-mining pages. This pass covers
payment change, reservation change, payment closeout, settlement recalculation,
customer-visible frozen amount, internal settlement adjustment, refund/cancel,
virtual account, and Kill Bill migration gaps.
2026-07-05 Party And Recipient Authority Edge Pass
Section titled “2026-07-05 Party And Recipient Authority Edge Pass”| Priority | Missing backend-owned policy | Source evidence | Modern target |
|---|---|---|---|
| P0 | Payment authority must separate login actor, mother/reservation beneficiary, payer, and communication recipient. Source payment callbacks reject non-owner access by comparing reservation owner to the login user, while some virtual-account instructions send to the login user’s phone and payment completion paths send to reservation/mother phone. | source-refs/sanmopia_web/application/controllers/api/Payment.php:66-219, source-refs/sanmopia_web/application/controllers/api/Payment.php:326-413, source-refs/sanmopia_web/application/controllers/api/Payment.php:509-654, source-refs/sanmopia_web/application/controllers/api/Payment.php:681-762 | PayerSnapshot, PaymentAuthorityDecision, PaymentCommunicationRecipientResolution, PaymentVirtualAccountInstructionPayload |
| P0 | Delegated or family payer authority cannot be inferred from emergency-contact relation. Source intake stores emergency contact/relation, but payment methods still require reservation ownership. Modern delegation must be an explicit grant with scope, booking, product-plan applicability, effective window, revocation state, and ambiguity handling. | source-refs/sanmopia_web/application/controllers/api/Service.php:57-81, source-refs/sanmopia_web/application/controllers/api/Payment.php:66-219, source-refs/sanmopia_web/application/controllers/api/Payment.php:681-762 | FamilyPaymentDelegationGrant, PaymentDelegationScopeDecision, ReservationPartyAuthoritySnapshot |
| P0 | Gift coupon buyer, receiver, and contact-only recipient require separate snapshots. Source coupon order defaults buyer fields from the login user when present, accepts free-form receiver name/phone, allows nullable user id at payment prepare, sends fulfillment to receiver phone, sends virtual-account instructions to buyer phone, and later registers coupon ownership by matching receiver phone to a user. Resend/cancel verify the original receiver name/phone for the buyer-owned payment row; resend can send to a new phone without updating stored coupon data. | source-refs/sanmopia_web/application/views/shop/coupon_order.php:60-90, source-refs/sanmopia_web/application/controllers/api/Shop.php:20-47, source-refs/sanmopia_web/application/controllers/api/Shop.php:125-245, source-refs/sanmopia_web/application/models/Coupon_model.php:280-317, source-refs/sanmopia_web/application/models/Coupon_model.php:344-396, source-refs/sanmopia_web/application/controllers/api/Payment.php:223-303, source-refs/sanmopia_web/application/models/Payment_model.php:451-466, source-refs/sanmopia_web/application/models/Payment_model.php:707-728 | GiftCouponBuyerSnapshot, GiftCouponReceiverSnapshot, DirectRecipientContactSnapshot, ContactOnlyCommunicationDispatchRequest, CouponFulfillmentDispatchRoute, CouponRecipientMutationPolicy |
| P0 | Refund recipient and payout eligibility are missing source policies. Source reservation refund decodes provider context and requests KCP refund by order id/transaction id, with the virtual-account skip commented out; coupon cancel marks refund/delete flags and calls provider refund; admin pre-reservation cancel reads provider context and refunds by method/order/transaction. None record payer identity, refund recipient, refund account, payout destination, or recipient-facing side effects. | source-refs/sanmopia_web/application/models/Payment_model.php:384-440, source-refs/sanmopia_web/application/controllers/api/Shop.php:197-245, source-refs/sanmopia-admin/application/models/Reservation_model.php:5558-5595 | RefundRecipientSnapshot, PaymentRefundIntent, ProviderRefundRecipientDecision, VirtualAccountRefundEligibilityDecision, PaymentRefundLedgerEntry, RefundRecipientAndSideEffectOrdering |
| P1 | Branch payment notice exceptions must be versioned routing policy. Source sends primary branch notices to BRANCH_PHONE_NO and hardcodes branch id 20 to an additional secondary phone across deposit, pre-reservation, change-payment, and virtual-account deposit completion paths. | source-refs/sanmopia_web/application/controllers/api/Payment.php:196-210, source-refs/sanmopia_web/application/controllers/api/Payment.php:621-643, source-refs/sanmopia_web/application/controllers/api/Payment.php:748-754, source-refs/sanmopia_web/application/models/Payment_model.php:571-700 | BranchPaymentNoticeContactPolicy, BranchPaymentNoticeRecipientSnapshot, PaymentProviderTemplatePayload |
2026-07-05 Refund And Settlement Crossover Pass
Section titled “2026-07-05 Refund And Settlement Crossover Pass”| Priority | Missing backend-owned policy | Source evidence | Modern target |
|---|---|---|---|
| P0 | Cancellation refund must be a saga with ordered side effects. Customer instant cancel uses a 14-day rule, calls reservation refund, reopens used coupons, deletes Momfirst rows, writes a request row, then controller sends branch Kakao and customer FCM. Admin cancel refunds reservation payment contexts, refunds latest payment-change context, reopens used coupons, resets product coupons, while stop status only resets product coupons when state changes. | source-refs/sanmopia_web/application/controllers/api/Reservation.php:87-240, source-refs/sanmopia_web/application/models/Reservation_model.php:990-1066, source-refs/sanmopia_web/application/models/Coupon_model.php:530-552, source-refs/sanmopia-admin/application/models/Reservation_model.php:1769-1916, source-refs/sanmopia-admin/application/models/Coupon_model.php:37-161, source-refs/sanmopia-admin/application/models/Coupon_model.php:936-959 | ReservationCancellationRefundSaga, RefundRecipientAndSideEffectOrdering, ReservationBenefitRollbackPolicy, CouponReinstatementLedgerEntry, PartnerBenefitCancellationPolicy, RefundNotificationRecipientResolution |
| P0 | Virtual-account refund handling is source-ambiguous. Web refund has a commented skip for pay_method == "vcnt" or account context, then still requests provider refund by order id and transaction id; admin pre-reservation cancel passes PAY_METHOD, TNO, and ORDR_IDXX to KCP. Pay method readers only translate method labels for display. | source-refs/sanmopia_web/application/models/Payment_model.php:384-440, source-refs/sanmopia-admin/application/models/Reservation_model.php:5559-5595, source-refs/sanmopia-admin/application/models/Reservation_model.php:5296-5537 | VirtualAccountRefundEligibilityDecision, RefundPayoutDestination, ProviderRefundRecipientDecision, PaymentProviderRefundCapabilitySnapshot |
| P0 | Payment-change refund and settlement visibility need linked lifecycle. Admin cancellation refunds the latest PAYMENT_CHANGE_TB provider context but does not mark a refund state on that row; payment-change list intentionally includes cancelled reservations, and manual status changes only set status/completed date. | source-refs/sanmopia-admin/application/models/Reservation_model.php:1861-1870, source-refs/sanmopia-admin/application/models/Payment_model.php:4431-4521 | PaymentChangeRefundLedgerEntry, PaymentChangeSettlementVisibilityPolicy, RefundAdjustedCustomerChargeSnapshot, PaymentChangeStatusTransitionAudit |
| P1 | Coupon refund/delete must separate buyer refund, receiver entitlement, and settlement side effects. Customer coupon cancel marks payment REFUND_FL, deletes coupon, and calls provider refund; admin delete-coupon can refund by coupon payment context, mark REFUND_FL, and delete the coupon. Gift/product coupon reset after stop/cancel can delete and republish receiver-facing coupons based on actual service days. | source-refs/sanmopia_web/application/controllers/api/Shop.php:197-245, source-refs/sanmopia-admin/application/models/Coupon_model.php:164-209, source-refs/sanmopia-admin/application/models/Coupon_model.php:37-161 | CouponRefundIntent, GiftCouponReceiverEntitlementEvent, CouponSettlementReversalLine, CouponRecipientMutationPolicy |
Payment Change Adjustment
Section titled “Payment Change Adjustment”-
Payment product plan catalog. Status:
Partial. Evidence:source-refs/sanmopia_web/application/controllers/api/Payment.php:65-219,source-refs/sanmopia_web/application/controllers/api/Payment.php:222-304,source-refs/sanmopia_web/application/controllers/api/Payment.php:325-382,source-refs/sanmopia_web/application/controllers/api/Payment.php:416-506,source-refs/sanmopia_web/application/controllers/api/Payment.php:508-583,source-refs/sanmopia_web/application/controllers/api/Payment.php:657-735,source-refs/sanmopia_web/application/models/Payment_model.php:283-324,source-refs/sanmopia_web/application/models/Payment_model.php:707-728,source-refs/sanmopia-admin/application/models/Payment_model.php:3923-4042,source-refs/sanmopia-admin/application/models/Payment_model.php:4422-4521. Source behavior: separate controller methods sell customer deposit, customer balance, gift coupon, pre-reservation deposit, and change-payment flows, while payment type ids and gateway method codes later double as settlement, fee, receipt, and operator-board meaning. Modern gap:PaymentProductPlanmust separate the billable product shown to a payer from the accounting obligation recorded in the ledger. The plan catalog needs stable product codes, product family, allowed payment methods, provider routing key, ledger obligation mapping, virtual-account expiry policy, refund/conversion policy, settlement participation, and customer/operator visibility.ReservationPaymentPurposecan remain an obligation vocabulary, but it must not be treated as the product catalog. Kill Bill pre-registration should validate product plan, method, purpose, and amount before the gateway call, so provider migration, conversion payment, pre-reservation, and service-balance workflows can evolve without adding another hardcoded payment-type branch. Virtual-account notification is also product-plan behavior: source branches the message and waiting-state mutation by coupon, customer balance, and pre-reservation, and even hardcodes the pre-reservation deposit notice amount. Modern plans carryPaymentProductCommunicationRouterows with trigger, recipient, provider workflow key, SMS fallback, message purpose, and optional applicability condition; displayed amounts must still come from the frozen payment request, not from controller string branches. Gift-coupon purchase is also a first-class product family: successful payment issues/registers the coupon, links the gift-coupon type, notifies the receiver, and later appears in settlement fee/coupon lines. Migrated proof: backend now stores seeded versioned product-plan rows insanmopia_payment_product_plan_catalog_itemswith explicit Supabase grants, RLS, active/effective dates, and catalog version labels. A Pydantic adapter validates the rows, derivesPaymentProductPlanCatalog/PaymentProductPlanBook, and wires that same Supabase-backed catalog into Kill Bill pre-registration validation andGET /payment-product-plans. 2026-07-04 follow-up:PaymentProviderRouteFeeSnapshotnow binds each pre-registration to the selected provider route, settlement participation, ledger recognition timing, fee basis, settlement line targets, frozen customer-visible behavior, andPaymentMethodFeePolicyresult. The public contract now requirespaymentProductPlanCodeon the command and returnsproviderFeeSnapshot, while Supabase stores nullable snapshot columns onsanmopia_payment_recordsfor new pre-registered rows. Catalog v2 migrated gift/voucher fee-band settlement lines intoPaymentSettlementLineTemplate; catalog v3 migrates notification routing intoPaymentProductCommunicationRouteand exposes it through backend, HTTP, and TS contracts.ReservationPaymentWorkflownow carries a typed payment communication dispatch command through Restate input replay and invokesPlanPaymentCommunicationDispatchHandlerafter successful closeout. Dispatch planning failures become workflow failures so Restate can retry without rolling back the approved payment status.SupabasePaymentCommunicationDispatchCommandResolvernow derives the command before Kill Bill workflow start from the payment record, selected product plan, reservation mother, branch-operator membership facts, and active family payment-delegation facts. Delegated payer resolution uses the mother member profile, reservation booking id, product-plan-to-delegation scope mapping, grant window, revocation state, and booking-specific precedence; ambiguous multiple payer grants stay unresolved instead of sending a notice to an arbitrary family member. Catalog v4 now adds delegated payer payment-complete routes and matching Novu workflow keys for customer-share deposit/balance, pre-reservation deposit, and payment-change completion, so resolved delegated payer facts can produce durable dispatch requests. Gift-coupon receiver authority now has user-backedsanmopia_payment_coupon_receiver_snapshotsand acoupon_fulfilleddispatch resolver path; phone-only gift receiver contacts now route throughsanmopia_payment_direct_recipient_contact_snapshotsand contact-onlysanmopia_direct_contact_communication_dispatch_requestswithout fake user ids. Primary and secondary branch payment notices now use versionedsanmopia_branch_payment_notice_contactsrows keyed by branch, recipient kind, condition code, effective window, and user id instead of source branch id hardcoding; primary branch routes fall back to branch-operator membership only when no policy row exists.PaymentProviderTemplatePayloadnow freezes workflow, recipient, amount, payment, subject, and message-purpose variables inproviderTemplatePayloadbefore provider handoff.PaymentVirtualAccountInstructionPayloadnow adds backend-rendered virtual-account bank/account/depositor/deadline/amount variables from frozenpayment_provider_context, preferring Kill Bill RESTmethod.virtualAccountfields and falling back to legacy KCP keys only as migration evidence. Remaining proof: virtual-account-issued dispatch path, queue/provider result reconciliation, and hidden source payment-type adapter edges. -
Frontend charge math migration. Status:
Partial. Evidence:source-refs/sanmopia_web/static/js/step3.js:198-474,source-refs/sanmopia_web/static/js/step3.js:766-831,source-refs/sanmopia_web/static/js/step3.js:1148-1179,source-refs/sanmopia_web/application/views/reservation/retc.php:45-200,source-refs/sanmopia_web/application/models/Reservation_model.php:1115-1373. Source behavior: booking and reservation views write row arrays frominputList/data-priceand recompute payable totals in JavaScript and PHP views. Modern authority must move these lines intoChargeAdjustmentPolicy,DiscountAdjustment,AdditionalChargeAdjustment, andPaymentAdjustmentCatalogprojections so UI renders only frozen totals and snapshot IDs. Backend progress:GET /reservation-financial-lifecycles/{reservationBookingId}/customer-visible-charge-payment-summarynow composes finalized charge snapshot rows with Supabasesanmopia_payment_recordsand returns display lines, payment state, payable-now purpose, paid/refund amounts, receipt handoff refs, projection revisions, andcallerObservedTotalMismatch. UI-observed totals are diagnostic input only; backend-computedcomputedCustomerTotalKrwis the authority. Remaining work: bind the booking/payment frontend to this projection, persist charge snapshots from every reservation creation/change path, and remove legacyPRICE_TABLE/DOM total compatibility paths. 2026-07-05 persistent-worker addendum: source reservation review and admin-add screens still contain display amounts, charge names, support item ids, pre-reservation credit math, delivery line amounts, voucher add-on display, regional discounts, and weekend/holiday option row math in view/JavaScript layers. Evidence:source-refs/sanmopia_web/static/js/step3.js:198-560,source-refs/sanmopia_web/static/js/step3.js:766-831,source-refs/sanmopia_web/application/views/reservation/retc.php:1-205,source-refs/sanmopia-admin/application/views/reservation_status/pre_reservation_price_table.php:1-124,source-refs/sanmopia-admin/static/js/reservation_status/reservation_add.js:927-948, andsource-refs/sanmopia-admin/static/js/reservation_status/reservation_add.js:1518-1542. Modern target names:ReservationChargeLineCatalogProjection,PreReservationCreditPolicy,RegionalBenefitRule, andCustomerChargeSnapshot. Backend must freeze amount source, label key, support profile, adjustment reason, deposit/balance split, and policy version before any customer or operator screen renders. -
Product settlement realization policy. Status:
Partial. Evidence:source-refs/sanmopia_web/application/models/Payment_model.php:101-230,source-refs/sanmopia_web/application/models/Payment_model.php:780-843,source-refs/sanmopia-admin/application/controllers/Calculate.php:150-235,source-refs/sanmopia-admin/application/models/Payment_model.php:767-820, andsource-refs/sanmopia-admin/application/models/Payment_model.php:4265-4305. Source behavior: customer deposit, customer balance, service balance, pre-reservation deposit, gift coupon, delivery fee, membership due, and coupon commission become settlement facts at different moments. Some lines are created when a payment is approved, some stay unpaid until branch/HQ settlement board commands, some wait for coupon fulfillment, and some are marked complete only after board acknowledgement. Modern gap:PaymentProductPlanneeds a separatePaymentProductSettlementPolicyor equivalent value object. It must define realization trigger, ledger recognition timing, fee basis, settlement line target, customer-visible freeze rule, reprice protection, conversion/refund aftermath, and operator-review needs. Product family alone is too shallow because two products in one family can recognize into different ledgers at different lifecycle events. -
Mother-payable adjustment lifecycle. Status:
Partial. Evidence:source-refs/sanmopia-admin/application/controllers/ajax/Reservation.php:68-178,source-refs/sanmopia-admin/application/models/Payment_model.php:4423-4521,source-refs/sanmopia_web/application/controllers/api/Payment.php:657-764,source-refs/sanmopia_web/application/models/Payment_model.php:856-900,backend-repo/src/sanmopia_modernization/application/pricing_settlement/features/payment/payment_change_adjustment.py:18-161,contract-repo/packages/ts-client/src/types/reservation-payment.ts:373-408. Source behavior: customer flow creates the latest payable adjustment, replaces unpaid prior adjustment rows, then gateway payment stores provider context and paid time. Operator flow lists paid adjustment rows, shows recalculated reservation amount and prior deposit dates, then edits adjustment amount or completion status. Modern gap:PaymentChangeAdjustmentneeds one lifecycle across mother payment, operator completion, cancellation of superseded unpaid adjustments, settlement continuation, and customer-visible charge refresh. Current backend and contract cover the ledger shape plus protected public command APIs: request and operator completion require Supabase bearer auth and booking-scoped SpiceDB permission, while mother payment requires the authenticated Supabase user id to matchmotherUserId. 2026-07-03 progress: backend and contract now also expose protected read projection endpoints for latest adjustment by reservation and durable adjustment detail by id, using mother ownership or booking-scopedviewpermission. The remaining proof is operator paid-row list/search/status UI, workflow-status polling around adjustment payment ids and settlement continuation from the paid adjustment. 2026-07-03 progress: backend and contract now exposePaymentChangeOperatorBoardfor paid/completed adjustment rows with operator-only access, row-level bookingviewfiltering, search text, visible/returned row counts, payment deposited time, completion time, and completion-editable state. Remaining proof moved to frontend board UI, profile/reservation enrichment for mother name and prior deposit dates, status projection invalidation, and explicit settlement continuation. 2026-07-03 progress: protected operator completion and amount-correction commands now appendPaymentChangeOperatorAuditrows with actor, reason, idempotency key, previous/new status, previous paid amount, requested amount, and recorded time. The command intentionally does not mutate the paid adjustment total, preventing mother-visible paid amounts from drifting after provider closeout. Operator inline edit also needs a command ledger: sourcereservation_status/payment_change.js:93-170sends raw amount/status on change,ajax/Reservation.php:167-177forwards rawtype/value, andPayment_model.php:4500-4518overwrites price/status/completion time by id. Modern names:PaymentChangeOperatorCommand,PaymentChangeCompletionAudit, andPaymentChangeProjectionInvalidation. 2026-07-03 operator/status read evidence: legacy admin table headers expose reservation id, applicant, request date, reservation payment amount, change request amount, prior deposit dates, paid date, completed date, and processing status (table_payment_change.php:5-14), while DataTables maps those fields fromPAYMENT_CHANGE_TB, enriched reservation/payment facts, and an inline amount/status editor (payment_change.js:40-123). Search fires only on Enter and backend search is mother name like-match or exact reservation id (payment_change.js:127-135,ajax/Reservation.php:121-140,Payment_model.php:4471-4476). Status read needs DB status options plus synthetic0=대기중(예약취소), cancel+pending display remap, disallow operator setting0, and ID2completion timestamp (ajax/Reservation.php:68-75,payment_change.js:109-149,Payment_model.php:4500-4518). Settlement continuation evidence remains indirect: paid change rows are listable only afterDEPOSIT_DT, customer copy says operations cancels the old card amount after confirmation, cancellation refunds the latest change-payment provider context, and reservation settlement jobs continue through진행 -> 정산 -> 완료using ordinary payment rows rather than an explicitPAYMENT_CHANGE_TBcontinuation (Payment_model.php:4451-4461,modal_change_payment.php:30-31,90-92,Reservation_model.php:1861-1870,cli/Reservation.php:160-221,227-280). Additional source evidence: admin nav exposes전환결제only for admin type (nav_side.php:60-69); controller parses DataTables draw/start/length and search (ajax/Reservation.php:101-140); model still orders byPAYMENT_CHANGE_ID_PK DESCand limits server-side (Payment_model.php:4478,4485-4488). Modern board intentionally avoids hardcoding unknown source status option names for IDs 1 and 3 until seed/live proof exists. 2026-07-03 migration progress:PaymentChangeOperatorBoardnow enriches paid/completed rows withreservationId,branchProfileId,motherDisplayName, andreservationChargeAmountKrwfrom normalized Supabase reservation booking and member profile tables. Search now covers those human fields in addition to durable ids. 2026-07-03 migration progress:sanmopia_payment_recordsnow has nullablepayment_purposeanddeposited_atfacts plus an indexed reservation/purpose lookup.PaymentChangeOperatorBoardexposescustomerShareDepositPaidAt,customerShareBalancePaidAt,serviceBalancePaidAt, andpreReservationDepositPaidAt, covering legacyBEFORE_PAYMENT_DEPOSITED_DTandBALANCE_PAYMENT_DEPOSITED_DTwhile also preserving the other source payment purposes. 2026-07-03 follow-up: direct booking writes now mark the initial customer-share deposit purpose and paid timestamp; reservation payment pre-registration requirespaymentPurpose, rejects idempotent reuse with a different purpose, and stores waiting records with purpose; Kill Bill status synchronization writesdeposited_atwhen a payment becomes approved/completed. Remaining gap: import/backfill of existing null-purpose payment rows into purpose-tagged customer-share-balance and service-balance receipt facts, plus frontend/operator UX implementation of the protected manual receipt command for operator-confirmeddepositedAtdates. -
Adjustment amount breakdown. Status:
Backend-backed foundation. Evidence:source-refs/sanmopia-admin/application/controllers/ajax/Reservation.php:142-161,source-refs/sanmopia_web/application/controllers/api/Payment.php:672-723,backend-repo/src/sanmopia_modernization/domain/pricing_settlement/features/payment/payment_change_adjustment.py,backend-repo/supabase/migrations/20260705092016_payment_change_amount_snapshots.sql,contract-repo/packages/ts-client/src/types/reservation-payment.ts. Source behavior: operator display recomputes a payable amount from current reservation price and coupon facts, while customer payment accepts a posted adjustment amount. Modern backend now freezesPaymentChangeAdjustmentAmountBreakdownSnapshot,MotherVisibleChargeSnapshot, coupon allocation evidence, discount source, approved timestamp, and source evidence. Supabase stores queryable displayed charge/discount-source columns; operator board and public TS/JSON schema expose the snapshot keys and displayed amount. Remaining runtime gap: bind every payment-change approval path to create the snapshot and backfill legacy rows from source evidence. 2026-07-05 follow-up: the backend domain/application slice now addsPaymentChangeRequestedAmountAuthorityso a customer-entered payment-change amount can be accepted only when it matches frozen amount, charge, and coupon facts. Replacing an unpaid request now emits an explicitPaymentChangeSupersessionFactinstead of relying only on the cancelled row. 2026-07-06 follow-up: the public reservation payment interface and contract now require nestedrequestedAmountAuthorityand reject the legacy scalar request shape before handler execution. Regional benefit charge impacts are also acknowledged through finalized charge snapshot id plus frozen impact event ids, so payment-change approval cannot reuse stale locality math after a finalized charge snapshot has already frozen the mother-visible amount.
Adjustment Catalog Centralization
Section titled “Adjustment Catalog Centralization”- Discount, additional charge, service-balance override, and partial-refund
catalog. Status:
Partial. Evidence:source-refs/sanmopia_web/static/js/step3.js:198-474,source-refs/sanmopia_web/static/js/step3.js:766-831,source-refs/sanmopia_web/static/js/step3.js:1148-1179,source-refs/sanmopia_web/application/models/Reservation_model.php:1115-1373,source-refs/sanmopia_web/application/views/reservation/retc.php:45-200,source-refs/sanmopia_web/application/views/modal/modal_change_payment.php:1-92,source-refs/sanmopia-admin/application/views/reservation_status/pre_reservation_price_table.php:1-120,source-refs/sanmopia-admin/application/controllers/ajax/Reservation.php:142-161, andsource-refs/sanmopia_web/application/models/Payment_model.php:383-441. Source behavior: reservation intake JavaScript asks/api/service/price, walks the browserinputList, writes service/government/coupon/add-on rows withdata-price, then sums DOM.minorrows incalculationTotal. Reservation price views calculate additional service charges, regional support, coupon/pre-reservation deductions, voucher in-home/special add-ons, rental and delivery fees, remaining service balance, and customer-share display in read paths; payment-change and cancellation/refund paths then recalculate or mutate related amounts separately. Admin service-balance confirmation posts/reservation/confirmServiceBalance, setsSERVICE_BALANCE_PRICE,SERVICE_BALANCE_CONFIRM_FL = 1, andSERVICE_BALANCE_PRICE_USE_FL = 1; stale/reservation/serviceBalanceConfirmJavaScript has no verified route alias and only represents a confirm-flag compatibility edge. SourceinsertServiceBalanceInfoalso creates the branch-manager서비스잔금receivable row; when the service-balance amount is zero it sets branch payment and caregiver receipt-confirmation flags complete immediately, while positive balances remain payment-required. Manager-side status changes later split branch-paid/awaiting-caregiver-confirmation from caregiver-confirmed receipt for main and sub caregivers. Modern gap:ChargeAdjustmentPolicy,PaymentAdjustmentCatalog,DiscountAdjustment,AdditionalChargeAdjustment, andPartialRefundmust be the only authority for these amount decisions. Every quote/execution should freeze catalog version, adjustment kind, source evidence key, actor/reason, idempotency key, refundable balance, settlement impact, and customer-visible explanation snapshot. UI tables and modals render those frozen rows only; they must not recompute discount/additional-charge/refund totals fromPRICE_TABLE,PAYMENT_INFO, current coupon rows, or DOMdata-pricetotals. Any mismatch between browser-displayed historical totals and backend ledger lines is a migration diagnostic, not payment authority. Backend now hasReservationServiceBalanceConfirmationcommand/contract foundation that freezes the actual three-column closeout effect. 2026-07-05 follow-up:ServiceBalancePaymentCloseoutandCaregiverServiceBalanceReceiptCloseoutnow replace source primary/sub columns with tuple-based caregiver recipients, andCloseReservationServiceBalanceHandlerbuilds the backend-owned closeout command for positive-balance waiting state or zero-balance automatic completion.sanmopia_save_service_balance_closeoutnow persists the idempotent event, current closeout row, and caregiver receipt-state rows through a service-role RPC while RLS read policies expose only mother, caregiver, branch, and HQ-admin projections.POST /reservation-service-balance-closeoutsexposes the command through the backend API with the existing payment-record finalize authorization path. Remaining work is settlement-completion workflow binding, branch/HQ repair UX, conflict/audit logging, and frontend/E2E proof.
Service-Balance Closeout Flow
Section titled “Service-Balance Closeout Flow”flowchart LR Mother[Mother] --> Booking[Booking workflow] Booking --> Payment[Payment closeout] Payment --> ServiceBalance[Service-balance closeout] Branch[Branch Operator] --> ServiceBalance ServiceBalance --> Receipt[Caregiver receipt state] Caregiver[Caregiver] --> Receipt Receipt --> Settlement[Branch/HQ settlement continuation] HQ[HQ Admin] --> Settlement ServiceBalance -. persists .-> Supabase[(Supabase RLS/RPC ledgers)] Settlement -. reads .-> Supabase
Unchecked legacy PHP gaps still blocking closeout parity:
- Persist source
SERVICE_BALANCE_*flag meaning without leaking source column names into domain contracts. - Split primary/sub caregiver receipt into stable assignment-key recipients
for every source
SORT == 0andSORT > 0row. - Reconcile zero-balance auto-complete with positive-balance branch payment and caregiver receipt waiting states.
- Bind closeout to branch/HQ settlement continuation and repair history before frontend boards treat the flow as complete.
Reservation Change Request
Section titled “Reservation Change Request”-
Customer-initiated reservation change. Status:
Pending. Evidence:source-refs/sanmopia_web/application/controllers/api/Reservation.php:86-239,source-refs/sanmopia_web/application/models/Reservation_model.php:989-1112. Source behavior: customer command stores a request message, changes reservation state, and notifies branch users. Cancellation can become an immediate cancellation depending on service timing, while change and stop requests only record the request state. Modern gap:ReservationChangeRequestneeds request kind, actor, submitted reason, immediate-vs-review path, branch notification attempt, affected payment facts, and later operator decision. Payment change should reference this request when it is caused by a reservation change. -
Operator-applied reservation change. Status:
Partial. Evidence:source-refs/sanmopia-admin/application/controllers/Reservation.php:675-870,source-refs/sanmopia-admin/application/controllers/Reservation.php:1025-1130,source-refs/sanmopia-admin/application/models/Reservation_model.php:1536-1679,source-refs/sanmopia-admin/application/models/Reservation_model.php:5597-5870. Source behavior: operator edit saves service dates, payment dates, addresses, care-team assignment, service balance, and price version changes in one broad form. It also rewrites payment row prices and logs version changes separately. Modern gap: split this into command-specific changes with a sharedReservationChangeImpactread model. Each change should emit impacted charge, payment, care-team, and settlement effects before any amount is exposed to mother or settlement screens.2026-07-06 progress:
OperatorPaymentFactEditCommandseparates payment-date and manual payment correction from the broad reservation edit path. Payment fact edits now require payment purpose, explicit paid/unpaid intent, recorded deposit date, expected payment revision, actor capability evidence, reason, idempotency key, immutable amount snapshot, correction audit, shared audit fingerprint helper, and projection invalidation keys. Persistence follow-up5a2ac5cadds the application handler/store port, Supabase payment-fact read adapter, idempotent correction lookup, RPC-backed persistence,sanmopia_operator_payment_fact_corrections, payment-record revision/snapshot columns, and restrict FK audit retention. API binding follow-upff4e996exposes protected HTTP/API contracts backed by that persistence path, derives operator actor/capability evidence from Supabase Auth plus SpiceDB, wires the handler in the main app, and returns correction audit/projection facts. Broad-save guard follow-up4f7b672rejects existing payment fact overwrite through the manual receipt path and requires payment-date/manual payment corrections to useOperatorPaymentFactEditCommand. Historical amount snapshot backfill9de2d68fills legacy payment-row amount evidence and makes new receipt/pre-registration rows carry deterministic amount snapshot keys. Projection follow-up49245dccarries those amount snapshot keys onto payment-change operator board rows and invalidates settlement board read models for manual payment fact edits. Reservation-detail adapter follow-up183f8f4blocks collaboration broad-save of payment-date facts, exposespayment_fact.operator_edit_targetswith payment fact id/revision/amount snapshot evidence, and removes unsafe security-definer use from payment fact correction RPCs.2026-07-13 authority follow-up: contract
f230493fixes the operator target projection, immutable amount snapshot, capability, allowed intent, and stable disabled reason codes without exposing raw payment or settlement status. Backend130269drechecks actor authority, booking association, purpose, revision, refund state, settlement lock, intent, and the complete frozen amount snapshot before one atomic RPC update. Backend5653e85makes the database assign deterministic snapshot keys to new payment facts and backfills 62 stage rows that still had no key. Backend8374589adds the repeatablesupabase:smoke:stage:operator-payment-fact-editproof. The latest run created one tracked booking and waiting payment, denied forged branch scope, read the backend target, changedwaitingtopaid, advanced revision0to1, returned the same result for exact idempotent replay, refreshed the target to allow onlymark_unpaid, and reaped the booking, correction, snapshot, DB rows, and SpiceDB relationships.Frontend
fb7f16drenders only backend targets, keeps amount, booking, revision, capability, and idempotency out of operator inputs, preserves the exact failed payload for retry, and refreshes the projection after success. Its 390px and 1280px browser harness is synthetic component evidence, not live stage workflow evidence. Remaining proof: bind the currently empty/pricing-settlementreservation-operation list to a backend projection, execute the editor with a real authenticated stage operator, capture the successful browser workflow as governed WebP evidence, then promote that evidence from provisional to canonical.
Payment Closeout
Section titled “Payment Closeout”-
Approved payment closeout. Status:
Partial. Evidence:source-refs/sanmopia_web/application/controllers/api/Payment.php:65-219,source-refs/sanmopia_web/application/models/Payment_model.php:101-230,backend-repo/src/sanmopia_modernization/application/pricing_settlement/features/payment/reservation_payment_workflow.py:219-334,backend-repo/src/sanmopia_modernization/application/pricing_settlement/features/payment/reservation_payment_workflow.py:350-397. Source behavior: successful payment immediately mutates reservation state, payment status, deposit timestamp, payment context, coupon use, notifications, and branch messages inside the customer request. Modern gap:PaymentCloseoutmust be an idempotent event with provider result, amount, method, closeout kind, finalized charge snapshot id, and settlement continuation request id. The workflow already verifies paid payment and can finalize charges, but migration still needs proof that every closeout path writes one closeout event and never relies on request-time side effects. -
Settlement continuation after closeout. Status:
Partial. Evidence:backend-repo/src/sanmopia_modernization/application/pricing_settlement/features/payment/reservation_payment_workflow.py:240-312,backend-repo/src/sanmopia_modernization/application/pricing_settlement/features/payment/reservation_payment_workflow.py:374-397. Source behavior: customer payment closeout and settlement effects are coupled indirectly by mutable payment rows and later settlement-board readers. Modern gap:SettlementContinuationRequestshould start only after charge finalization succeeds, carry payment closeout id and finalized charge snapshot id, and expose blocked reasons when downstream settlement evidence is missing.
Customer-Visible Frozen Amount
Section titled “Customer-Visible Frozen Amount”-
Mother-visible charge snapshot. Status:
Partial. Evidence:source-refs/sanmopia_web/application/controllers/MyReservation.php:120-155,source-refs/sanmopia_web/application/views/mypage/item/reservation_fee_table.php:1-190,source-refs/sanmopia_web/application/views/mypage/reservation_detail.php:519-533,frontend-repo/src/features/mother-booking/booking-payment-view-model.ts:151-219. Source behavior: customer reservation display mixes current price table values, stored payment row values, coupons, pre-reservation amount, and operator-entered service balance. The payment form also chooses between current balance and stored payment amount at render time. Backend now exposes the customer-visible charge/payment projection throughGET /reservation-financial-lifecycles/{reservationBookingId}/customer-visible-charge-payment-summaryso the customer-facing surface can render frozen charge lines, payment state, payable-now amount, paid/refund totals, and mismatch diagnostics without recomputing from mutable UI rows. Remaining gap: every charge finalization and payment-closeout path must persist snapshot ids, captured time, calculation version, direction, amount, and source evidence keys so later price version, settlement, or caregiver changes cannot silently change already-shown customer totals. -
Customer receipt handoff. Status:
Pending. Evidence:source-refs/sanmopia_web/application/views/mypage/reservation_receipt.php:35-128. Source behavior: customer receipt screen opens gateway receipt URL from stored provider context for paid or refunded payment rows. Modern gap:CustomerReceiptArtifactshould reference payment closeout or refund event and expose a signed artifact or provider receipt pointer. Receipt availability must follow ledger state, not template logic in the customer page.
Internal Settlement Adjustment
Section titled “Internal Settlement Adjustment”-
Service-balance adjustment ledger. Status:
Partial. Evidence:source-refs/sanmopia-admin/application/controllers/Reservation.php:461-511,source-refs/sanmopia-admin/application/models/Reservation_model.php:606-638,source-refs/sanmopia-admin/application/models/Reservation_model.php:3507-3555,source-refs/sanmopia-admin/application/models/Reservation_model.php:3667-3688,source-refs/sanmopia-admin/application/models/Reservation_model.php:5903-5913. Source behavior: operator can override service balance, confirm it, derive related caregiver balance state, and write a custom-price log. Modern gap:InternalSettlementAdjustmentneeds reason, direction, amount, branch scope, actor, reservation change request link, effective date, and affected payout or branch settlement line ids. A custom amount log alone is not enough to replay settlement or explain customer and branch totals. Source service balance and voucher price version changes are additional mutation entrypoints:Reservation.php:461-510recalculates/logs/writes a posted service balance,Reservation.php:1819-1847changes the voucher price version, andReservation_model.php:3667-3678,5608-5617,5857-5866,5904-5912writes critical price flags/version with branch JSON logs. Modern names:SettlementRecalculationCommand,ReservationPriceVersionChange,ServiceBalanceAdjustment, andSettlementPreviewRefresh. -
Service-balance recalculation policy. Status:
Partial. Evidence:source-refs/sanmopia-admin/application/models/Reservation_model.php:1536-1679,source-refs/sanmopia-admin/application/models/Payment_model.php:4156-4222,source-refs/sanmopia-admin/application/config/constants.php:289-313. Source behavior: service balance is recalculated from reservation service type, service days, options, price version, prior pay amount, coupon, pre-reservation amount, and local discount rules. Modern gap:SettlementRecalculationAuditshould record input snapshot hash, price policy version, amount components, previous frozen amount, new amount, and whether the delta creates mother payment change, internal branch adjustment, or both.
Settlement Recalculation
Section titled “Settlement Recalculation”-
Branch/HQ settlement recalculation. Status:
Partial. Evidence:source-refs/sanmopia-admin/application/controllers/Calculate.php:150-315,source-refs/sanmopia-admin/application/models/Payment_model.php:828-980,source-refs/sanmopia-admin/application/models/Payment_model.php:1124-1300,source-refs/sanmopia-admin/application/models/Payment_model.php:4312-4402. Source behavior: branch/HQ board first calculates monthly lines from mutable reservation, branch, coupon, delivery, membership, manual extra, and board rows. After a board exists, display switches to board detail rows and status flags. Modern gap:BranchSettlementStatementmust freeze line items at opening time andBranchSettlementRecalculationAuditmust explain any rerun. Board display should compare frozen totals with recomputed diagnostic totals instead of mixing live source readers with board-state readers. -
Other-office settlement adjustment. Status:
Pending. Evidence:source-refs/sanmopia-admin/application/controllers/Calculate.php:337-430. Source behavior: operator action calculates fee from posted total and result amount, then updates settlement status or branch acknowledgement depending on actor. Modern gap:OtherOfficeSettlementAdjustmentneeds service office, other office, line direction, fee basis, actor, idempotency key, and separate acknowledgement state for each side.
Refund And Cancellation
Section titled “Refund And Cancellation”- Reservation cancellation and refund policy. Status:
Partial. Evidence:source-refs/sanmopia_web/application/controllers/api/Reservation.php:86-239,source-refs/sanmopia_web/application/models/Reservation_model.php:989-1066,source-refs/sanmopia_web/application/models/Reservation_model.php:1091-1112,source-refs/sanmopia_web/application/models/Reservation_model.php:2012-2033,source-refs/sanmopia_web/application/models/Coupon_model.php:529-550,source-refs/sanmopia_web/application/models/Payment_model.php:140-150,source-refs/sanmopia_web/application/models/Payment_model.php:758-768,source-refs/sanmopia-admin/application/models/Reservation_model.php:1822-1891,source-refs/sanmopia-admin/application/models/Coupon_model.php:33-135,source-refs/sanmopia-admin/application/models/Coupon_model.php:935-957,source-refs/sanmopia_web/application/models/Payment_model.php:383-435,backend-repo/src/sanmopia_modernization/application/pricing_settlement/features/payment/reservation_payment_workflow.py:31-44,backend-repo/src/sanmopia_modernization/application/pricing_settlement/features/payment/reservation_payment_workflow.py:315-348. Source behavior: customer immediate cancellation triggers refund processing, restores used coupons, updates reservation state, and notifies customer/branch. The web restore path readsRESERVATION_COUPON_USED_TB, flips eachUSER_COUPON_TB.USE_FLto0, clearsUSE_DT, and writesUSER_COUPON_REUSE_LOGwith the mother actor text. Admin cancellation status9performs provider refund, cancels change-payment context when present, runs the same used-coupon restore with the admin actor text, then resets product gift coupons. Stop request only writes status12and a request row; admin stop transition to status13resets product gift coupons but does not restore already-used reservation coupons. Coupon selection recordsRESERVATION_COUPON_USED_TB, and payment confirmation later validates the coupon and marksUSER_COUPON_TB.USE_FL/USE_DTas used. Payment refund loops over stored provider contexts and marks rows refunded after provider success. Backend side-effect planning now preserves that distinction:PlanReservationPromotionSideEffectHandlerplans product coupon reset for cancellation and stop, but only plans used reservation coupon restore for cancellation commands. Service-stop commands that carry reservation coupon restore ids are rejected before persistence.SupabaseReservationPromotionSideEffectPlanStorenow persists those plans and entitlement outbox rows with idempotency-key conflict protection; actual entitlement execution still belongs to the worker binding. Backend foundation:ReservationCancellationRefundPolicynow produces aRefundQuotefrom immutable reservation facts, applies the 14-day and 24-hour full-refund thresholds, calculates late-cancellation penalty, post-start provided-service charge, gift benefit deduction, delivery fee deduction, and customer shortfall, then turns the quote into catalog-backedPaymentRefundIntentdata. The application command no longer accepts caller-provided paid amount; it derives paid/refundable amount fromPaymentLedger.refundable_amount_krwbefore building the Kill Bill cancellation command.SupabasePaymentRefundLedgerStorenow reconstructs current refund ledgers fromsanmopia_payment_recordsand append-onlysanmopia_payment_refund_ledger_entries, whileSupabasePaymentAdjustmentCatalogReaderreads the latest active catalog fromsanmopia_payment_adjustment_catalog_items.POST /reservation-cancellation-refundsis wired through the backend runtime and rejects UI-suppliedpaidAmountKrw. Gateway-cancellable cancellation refunds now persistReservationCancellationRefundExplanationSnapshotintorefund_explanation_snapshot, freezing the policy inputs, quote result, deduction lines, and adjustment catalog version that the mother saw. 2026-07-05 contract follow-up: the public OpenAPI/TS client now exposesPOST /reservation-cancellation-refundswith optionalpaymentChangeAdjustmentId; backend HTTP/API maps that id intoPaymentChangeRefundLink, but still derives refund amount from the immutable ledger rather than client totals. 2026-07-05 orchestration follow-up:refund_paid_paymentsnow expands into bothpayment_refund_closeoutandpayment_refund_completion_barrier, with preconditions for terminal provider refund event or zero-refund fact before benefit, coupon, settlement, or receipt side effects can complete. Remaining gap: cancellation request orchestration must still link reservation state transition, coupon/benefit restoration, provider event ingestion, zero-gateway-refund quote persistence, customer/branch notification, customer receipt state, settlement reversal, provider partial-cancel capability/status events, and provider-event ordering states so Kill Bill/KCP refund outcomes cannot race coupon or settlement side-effects.
Virtual Account Payment
Section titled “Virtual Account Payment”- Virtual-account issue, deadline, deposit, and expiry. Status:
Partial. Evidence:source-refs/sanmopia_web/application/controllers/api/Payment.php:123-158,source-refs/sanmopia_web/application/controllers/api/Payment.php:416-506,source-refs/sanmopia_web/application/models/Payment_model.php:328-380,source-refs/sanmopia_web/application/models/Payment_model.php:512-705,source-refs/sanmopia_web/application/controllers/cli/Reservation.php:557-737,source-refs/sanmopia_web/application/controllers/cli/Reservation.php:739-825,source-refs/sanmopia_web/application/models/Reservation_model.php:2960-3031. Source behavior: virtual account issue stores provider context and waiting status, sends account/deadline notice, later deposit callback marks payment paid and may restore reservation state, while scheduled jobs cancel expired waiting reservations and send expiry notices. Modern gap:VirtualAccountPaymentLifecycleneeds issued, notice-sent, deposit-closed, expiring-soon, expired, cancellation-requested, and cancelled states. Kill Bill webhooks currently classify paid closeout as virtual-account deposit, but migration still needs explicit issued/expiry events, deadline clock, duplicate deposit handling, and customer/branch notification attempts.
2026-07-10 Virtual-Account Commit And Expiry Deepening
Section titled “2026-07-10 Virtual-Account Commit And Expiry Deepening”Backend 5c66e2d establishes the first replay-safety foundation: an accepted
issue may open waiting payment only from the unissued state; an exact replay of
the same provider order and deadline does not advance the payment revision or
redispatch instructions; provider-order mismatch and issue-after-deposit are
rejected without notification. The Supabase decision constraint accepts the
new non-mutating replay action, and colocated tests cover the invariants. This
does not yet provide the atomic issuance commit barrier, dispatch outbox,
superseded-account alias, or orphan repair flow described below.
- Replay can replace an active account and lose old deposit lookup. Normal
payment gates only paid state, while coupon and pre-reservation paths lack a
waiting-state gate; source rewrites provider context by reservation/type and
callbacks resolve only the current context. Evidence:
source-refs/sanmopia_web/application/controllers/api/Payment.php:95-117,:246-267,:523-545,source-refs/sanmopia_web/application/models/Payment_model.php:57-70,:531-532,:654-655, and:707-708. Target:VirtualAccountIssueReplayPolicy,ActiveVirtualAccountInstruction,VirtualAccountInstructionSupersessionSaga, andSupersededVirtualAccountDepositAlias. - Expiry scheduling covers initial reservation deposit only. Balance,
pre-reservation, and coupon waiting payments do not enter the source query.
Evidence:
source-refs/sanmopia_web/application/models/Reservation_model.php:2960-2985,source-refs/sanmopia_web/application/models/Payment_model.php:353-366,:784-810, andsource-refs/sanmopia_web/application/controllers/api/Payment.php:441-449. Target:VirtualAccountExpiryScopePolicy,PaymentProductVirtualAccountExpiryRule, andVirtualAccountExpiryWorkItem. - Source expiry changes reservation state but leaves payment/account/instruction
live. Detail reads still return waiting instructions after status
17. Evidence:source-refs/sanmopia_web/application/models/Reservation_model.php:2998-3025,source-refs/sanmopia_web/application/controllers/MyReservation.php:149-160, andsource-refs/sanmopia_web/application/views/mypage/reservation_detail.php:133-138,:458-466. Target:VirtualAccountExpiryCloseout,ProviderVirtualAccountCloseCommand, andExpiredVirtualAccountInstructionRevocation. - Midnight expiry mutates status before the later notice job runs the same
status-14 query, so transitioned rows disappear from notice eligibility.
Evidence:
source-refs/sanmopia_web/application/controllers/cli/Reservation.php:562-598,:629-650, andsource-refs/sanmopia_web/application/models/Reservation_model.php:2968-2981. Target: atomicVirtualAccountExpiryNotificationOutboxemitted fromVirtualAccountExpiryTransitionEvent. - Pre-reservation issue commits one waiting write, runs a helper that rewrites
and exits, then leaves caller validation unreachable. Evidence:
source-refs/sanmopia_web/application/models/Payment_model.php:784-823,:368-380, andsource-refs/sanmopia_web/application/controllers/api/Payment.php:456-496,:558-596. Target: onePreReservationVirtualAccountIssueCommitbehindVirtualAccountIssuanceCommitBarrierplusPaymentInstructionDispatchOutbox. - Unmatched funded deposit has retry but no durable claim path. Callback log is
raw-only; missing/superseded current context returns failure forever.
Evidence:
source-refs/sanmopia_web/application/controllers/api/Kcp.php:48-100,source-refs/sanmopia_web/application/models/Kcp_model.php:8-13, andsource-refs/sanmopia_web/application/models/Payment_model.php:707-734. Target:UnmatchedVirtualAccountDeposit,OrphanedVirtualAccountDepositRepairCase, andClaimVirtualAccountDepositCommand. - Issue compensation uses the same full-cancel primitive for unfunded close and
funded refund. Evidence:
source-refs/sanmopia_web/application/controllers/api/Payment.php:126-179andsource-refs/sanmopia_web/application/libraries/Kcppayment.php:418-437. Target:VirtualAccountIssueCompensationPolicy,CloseUnfundedVirtualAccountCommand, andVirtualAccountCompensationOutcome; unknown outcomes require lookup before retry.
Kill Bill Migration Gaps
Section titled “Kill Bill Migration Gaps”-
Kill Bill pre-registration and closeout parity. Status:
Partial. Evidence:source-refs/sanmopia_web/application/controllers/api/Payment.php:1-62,source-refs/sanmopia_web/application/controllers/api/Payment.php:65-219,backend-repo/src/sanmopia_modernization/application/pricing_settlement/features/payment/reservation_payment_pre_registration.py:30-169,backend-repo/src/sanmopia_modernization/adapters/pricing_settlement/features/payment/killbill_billing_client.py:82-193,contract-repo/packages/ts-client/src/types/reservation-payment.ts:49-123,contract-repo/packages/ts-client/src/types/reservation-payment.ts:349-435. Source behavior: legacy payment flow directly requests gateway approval and later stores raw provider context. Modern gap: every Kill Bill payment entry point must pre-register amount, verify paid amount/store, persist closeout, and expose workflow progress. Amount mismatch, store mismatch, status mismatch, provider timeout, duplicate webhook, and retry outcome need contract examples and operator repair paths. -
Kill Bill administrator operations parity. Status:
Partial. Evidence: Kill Bill exposes REST payment lookup, invoice-payment refund, chargeback, account, subscription, invoice, and Kaui back-office operations. The backend now usesKillBillBillingClientAdapterinstead of a provider SDK and keeps Sanmopia-specific retries, event ingress, repair, and audit policy outside Kill Bill. Source behavior: old PHP/KCP flows often forced operators to enter PG consoles or inspect raw gateway payloads. Modern gap: branch/HQ operator APIs must expose safe admin actions for payment lookup, webhook replay, virtual-account close, manual/API virtual-account issue, refund/cancel status inspection, and provider error capture, all behind RBAC/SpiceDB, expected revision, idempotency key, append-only audit, and Kill Bill official SDK/REST contracts. 2026-07-04 progress: backend/contract now exposePOST /killbill-payment-admin-operationsfor authenticated branch/HQ operators. Supported audited actions arelookup_payment,search_payments,close_virtual_account,inspect_refund_status,inspect_chargeback_status, andrecord_chargeback. The API derivesactorUserIdfrom Supabase Auth, gates branch managers through SpiceDBfinalize_charge, stores idempotency command fingerprints insanmopia_killbill_admin_operation_audits, captures provider error evidence, and keeps raw provider snapshots backend-only. Remaining gap: webhook replay, manual/API virtual-account issue, frontend repair screen, and operational runbook examples. -
Kill Bill webhook ingestion coverage. Status:
Partial. Evidence:backend-repo/src/sanmopia_modernization/application/pricing_settlement/features/payment/killbill_payment_event.py:24-197,backend-repo/src/sanmopia_modernization/application/pricing_settlement/features/payment/killbill_payment_event.py:210-271. Source behavior: virtual-account deposit and cancel/failed outcomes were spread across payment callbacks, source DB status mutation, and daily jobs. Modern gap: webhook ingestion should persist every event, classify closeout kind, start paid verification only for paid events, synchronize cancel/partial refund/fail statuses, and leave ignored events with reasons. Add tests for event ordering: issued before paid, paid before local record exists, duplicate paid, cancel after completed closeout, and partial refund after branch settlement opened. Backend31e5d83now accepts official Kill BillExtBusEventpayment callbacks only.PAYMENT_SUCCESS/PAYMENT_FAILEDandPaymentMetadata.transactionType/statusdistinguish purchase, capture, refund, void, and failed operations. Supabase stores account, tenant, transaction, amount, currency, operation, and status evidence under an ExtBus-specific constraint. Synthetic stage callback, queue dispatch, worker, and ignored-event ledger persistence pass; invented legacy event shapes fail with400. Backend072b2e0then atomically binds inbox insert to PGMQ send, deduplicates canonical JSON replay, quarantines changed payload under the same stable event identity, preserves repair evidence, and rejects canonical-row mutation. Stage proves one queue effect across duplicate/conflicting deliveries. Backend stage proof now also posts an official-shapePAYMENT_SUCCESS/REFUNDcallback through the hosted callback route, dispatchessanmopia_payment_webhook_processing, records the provider refund result from the refund ledger, mutates the paid row topartial_refunded, persists settlement reversal, requests six reporting refresh targets, and reaps the registered stage batch. Backend now also resolves multi-refund results by explicit refund intent or provider-enriched correlation key matching the ledger refund intent/idempotency key, and rejects uncorrelated multi-refund callbacks as repairable failures. Remaining gap: live Kill Bill-originated callback through the restricted Traefik admission route, live transaction lookup/admission enrichment for correlation keys, per-effect replay state, and the ordering scenarios above. -
Payment-change Kill Bill UX. Status:
Pending. Evidence:backend-repo/src/sanmopia_modernization/interfaces/reservation_payment_http.py:289-378,contract-repo/packages/ts-client/src/types/reservation-payment.ts:202-222,frontend-repo/src/pages/mother/booking.astro:560-590,frontend-repo/src/ui/organisms/PaymentPreRegistrationPanel.astro:26-102. Source behavior: legacy customer payment-change flow lets mother pay an adjustment and then notifies branch/operator. Modern backend and contract expose adjustment endpoints, while current frontend payment UI evidence is booking pre-registration only. Modern backend/contract now freeze adjustment payment pre-registration before mother payment UI, verify paid adjustment payments server-side through Kill Bill, and request settlement continuation after paid adjustment receipts.PaymentChangeAdjustmentandPaymentChangeOperatorBoardresponses now expose nullablesettlementContinuationStatus,settlementContinuationWorkflowRequestId,settlementContinuationReason, andsettlementContinuationRecordedAtfields. Backend/contract now exposeGET /reservation-payment-change-adjustments/{paymentChangeAdjustmentId}/settlement-continuation-workflow-statusso operator screens can poll the shared financial lifecycle projection by durable adjustment id instead of by source latest-row lookup. Remaining gap: mother payment-change UI, operator completion UI, status projection invalidation, and reservation/profile enrichment. Frontend/docs audit note (2026-07-03): current TS contract now exportspaymentChangeAdjustmentMotherPaymentVerificationsPath()forPOST /reservation-payment-change-adjustments/{reservationBookingId}/mother-payment-verificationsandPaymentChangeAdjustmentPaidVerificationPayload, but frontend still only imports/uses bookingPreRegisterReservationPaymentPayload,reservationPaymentPreRegistrationsPath(), andpreRegisterReservationPayment(). Later frontend work needs adjustment request, mother pre-registration, paid verification, operator completion, operator amount-correction audit, and workflow/projection polling surfaces;contracts/public-api.mdxnow lists the payment-change pre-registration, paid-verification, operator board, completion, and amount-correction audit endpoints explicitly. Backendfe4a3f8adds a separate secure foundation command,AuthorizeAndPreRegisterPaymentChangeCommand. It requires a backend-generated payer/booking-bound authorization snapshot, validates frozen amount and product route, and atomically writes payment revision, handoff session, adjustment pre-registration, and authorization audit through a service-role Supabase RPC. Existing HTTP behavior remains compatible and does not yet call this secure command. Stage proves exact replay without duplicate audit and denies directauthenticatedexecution. Remaining runtime work: interface SpiceDB resolution, different-payment-id unpaid-draft supersession/provider compensation, generated contract, and mother/operator UI. -
Source status evidence (2026-07-03): legacy mother page has no payment-change progress polling after AJAX/KCP success; admin status starts only from paid rows where
DEPOSIT_DT is not null, while callback and refund paths fetch the latest reservation row instead of a durable request id. This creates stale-tab and overwrite risk aroundPAYMENT_CHANGE_TB. Evidence:source-refs/sanmopia_web/static/js/mypage/reservation_changePayment.js:43-143,source-refs/sanmopia_web/application/models/Payment_model.php:856-900,source-refs/sanmopia-admin/application/models/Payment_model.php:4431-4521, andsource-refs/sanmopia-admin/application/controllers/ajax/Reservation.php:68-178.
2026-07-05 Settlement Source Delta
Section titled “2026-07-05 Settlement Source Delta”This CaveCrew pass adds settlement/payment gaps that must stay backend-owned. Legacy browser totals and Korean labels are evidence only.
| Modern target | Legacy evidence | Required migration |
|---|---|---|
SettlementBoardAmountDecision, SettlementAmountRecalculationPolicy | source-refs/sanmopia-admin/static/js/calculate_detail.js:81-119, source-refs/sanmopia-admin/static/js/calculate_detail.js:155-274, source-refs/sanmopia-admin/static/js/calculate_detail.js:900-1045, source-refs/sanmopia-admin/application/controllers/Calculate.php:155-189, source-refs/sanmopia-admin/application/models/Payment_model.php:534-620, source-refs/sanmopia-admin/application/models/Payment_model.php:2816-2840 | Backend must recompute/freeze total, fee, result, selected lines, coupon fee count/price, delivery, monthly dues, manual lines, and status-specific pending lines; UI renders frozen quote only. |
GatewayReceiptEvidence, PaymentGatewayFeeAssessment | source-refs/sanmopia-admin/application/models/Payment_model.php:3188-3214, source-refs/sanmopia-admin/application/models/Payment_model.php:3339-3371, source-refs/sanmopia-admin/application/views/calculate_detail.php:583-601, source-refs/sanmopia-admin/application/views/calculate_detail.php:674-692, source-refs/sanmopia-admin/application/models/Payment_model.php:3916-4044 | Provider receipt references and fee rates become catalog/provider snapshots. Hardcoded KCP admin URLs, method labels, and rates 0.05, 0.04, 0.025 stay adapter evidence. |
SettlementLineAcknowledgementScope, DataRoomBranchAcknowledgement | source-refs/sanmopia-admin/application/controllers/Calculate.php:279-310, source-refs/sanmopia-admin/application/models/Payment_model.php:4265-4308, source-refs/sanmopia-admin/static/js/calculate_detail.js:340-360, source-refs/sanmopia-admin/application/models/Payment_model.php:2526-2563 | Branch acknowledgement must require board-scoped line ids, expected revision, idempotency, actor, and audit. Data-room branch acknowledgement is a distinct flow from branch-settlement POST. |
CouponSettlementLifecycle, SettlementBoardStatusLifecycle | source-refs/sanmopia-admin/application/models/Payment_model.php:26-31, source-refs/sanmopia-admin/application/models/Payment_model.php:670-693, source-refs/sanmopia-admin/application/models/Payment_model.php:768-823, source-refs/sanmopia-admin/application/models/Payment_model.php:2526-2563, source-refs/sanmopia-admin/application/models/Payment_model.php:2977-3028, source-refs/sanmopia-admin/application/models/Payment_model.php:3643-3648, source-refs/sanmopia-admin/application/models/Payment_model.php:4382-4420, source-refs/sanmopia-admin/application/controllers/Common.php:109-124 | Coupon settlement needs request/branch-ack/complete states separate from board status. Board status must transition through waiting, branch-paid, complete, arrears-paid, and arrears with logs. |
ManualPaymentDateCloseout, InterOfficeSettlementCloseout | source-refs/sanmopia-admin/application/controllers/Reservation.php:680-687, source-refs/sanmopia-admin/application/controllers/Reservation.php:848-859, source-refs/sanmopia-admin/application/models/Reservation_model.php:1536-1678, source-refs/sanmopia-admin/application/controllers/Calculate.php:337-369, source-refs/sanmopia-admin/application/models/Payment_model.php:2917-2962 | Manual payment-date commands target one payment purpose per command and keep refund status immutable. Inter-office closeout needs separate Danbee/other-office completion gates and cannot infer completion from exactly three normal rows. |
2026-07-05 CaveCrew Explorer A Payment-Change Delta
Section titled “2026-07-05 CaveCrew Explorer A Payment-Change Delta”These rows narrow customer-side payment-change behavior not covered by operator board rows above.
| Modern target | Legacy evidence | Required migration |
|---|---|---|
PaymentChangeRequestedAmountDecision, SupersededPaymentChangeRequest | source-refs/sanmopia_web/application/views/modal/modal_change_payment.php:1-25, source-refs/sanmopia_web/application/views/modal/modal_change_payment.php:43-79, source-refs/sanmopia_web/static/js/mypage/reservation_changePayment.js:43-75, source-refs/sanmopia_web/application/controllers/api/Payment.php:657-677, source-refs/sanmopia_web/application/models/Payment_model.php:856-875 | Source calculates convertible amount in PHP view, accepts customer-entered amount, posts it before gateway start, and deletes unpaid prior PAYMENT_CHANGE_TB rows for the reservation. Backend must validate requested amount against frozen charge/payment facts, write a supersession event for replaced unpaid requests, and expose customer-visible amount/reason before provider pre-registration. |
PaymentChangeProviderCloseoutAttempt, PaymentChangeCloseoutCompensationSaga | source-refs/sanmopia_web/static/js/mypage/reservation_changePayment.js:78-143, source-refs/sanmopia_web/application/controllers/api/Payment.php:680-762, source-refs/sanmopia_web/application/models/Payment_model.php:877-900 | Source handles KCP cancel code 3001, posts encrypted provider form data, stores raw provider context and deposit time, refunds provider payment if DB update fails, then sends mother and branch AlimTalk after transaction completion. Modern closeout needs one idempotent attempt with provider result, DB-write compensation, refund-on-failure ordering, dispatch plan, and retry-safe state transitions. |
PaymentChangeCompletionNoticeRoutingPolicy, PaymentClientCacheInvalidationSignal | source-refs/sanmopia_web/static/js/mypage/reservation_changePayment.js:116-143, source-refs/sanmopia_web/application/controllers/api/Payment.php:740-755 | Source clears Android app cache after successful payment and sends branch TR_6009 with amount, including branch 20 secondary recipient. Modern workflow should return a client invalidation signal and enqueue versioned mother/branch notice dispatches from backend policy instead of relying on page-local success handlers and branch-id conditions. |
To Merge Into Source Checklist
Section titled “To Merge Into Source Checklist”- Add
PaymentChangeAdjustmentproof: request, superseded-unpaid cancellation, mother payment pre-registration, server-side paid verification, raw receipt capture fallback, operator completion audit, operator amount-correction audit, zero-payable backend closeout without provider wait, paid-adjustment settlement continuation, and adjustment-id workflow projection/status polling. Negative customer-credit cases stay routed to refund workflow rather than the zero-payable closeout. Evidence:source-refs/sanmopia-admin/application/controllers/ajax/Reservation.php:68-178,source-refs/sanmopia-admin/application/models/Payment_model.php:4423-4521,source-refs/sanmopia_web/application/controllers/api/Payment.php:657-764,source-refs/sanmopia_web/application/models/Payment_model.php:856-900. - Add
ReservationChangeImpactproof: customer request, operator decision, service/date/price/payment impact, and affected adjustment ids. Evidence:source-refs/sanmopia_web/application/controllers/api/Reservation.php:86-239,source-refs/sanmopia_web/application/models/Reservation_model.php:989-1112,source-refs/sanmopia-admin/application/controllers/Reservation.php:675-870,source-refs/sanmopia-admin/application/models/Reservation_model.php:5597-5870. - Add
PaymentCloseoutandVirtualAccountPaymentLifecycleproof: amount pre-registration, paid verification, virtual-account issue/deposit/expiry, receipt, cancellation/refund, notification attempts, and Kill Bill admin repair operations. Evidence:source-refs/sanmopia_web/application/controllers/api/Payment.php:65-219,source-refs/sanmopia_web/application/models/Payment_model.php:101-230,source-refs/sanmopia_web/application/models/Payment_model.php:512-705,source-refs/sanmopia_web/application/controllers/cli/Reservation.php:557-825,backend-repo/src/sanmopia_modernization/application/pricing_settlement/features/payment/killbill_payment_event.py:24-271. - Add
MotherVisibleChargeSnapshotproof: customer-visible amounts read only frozen charge lines after closeout and payment change. Evidence:source-refs/sanmopia_web/application/views/mypage/item/reservation_fee_table.php:1-190,source-refs/sanmopia_web/application/views/mypage/reservation_detail.php:519-533,frontend-repo/src/features/mother-booking/booking-payment-view-model.ts:151-219. - Add
InternalSettlementAdjustmentandBranchSettlementRecalculationAuditproof: service-balance override, policy recalculation, settlement board opening, board detail freeze, rerun diagnostics, and reversal after refund. Evidence:source-refs/sanmopia-admin/application/controllers/Reservation.php:461-511,source-refs/sanmopia-admin/application/models/Payment_model.php:4156-4222,source-refs/sanmopia-admin/application/controllers/Calculate.php:150-315,source-refs/sanmopia-admin/application/models/Payment_model.php:828-1300,source-refs/sanmopia-admin/application/models/Payment_model.php:4312-4402.
Top Gaps For Migration Backlog
Section titled “Top Gaps For Migration Backlog”- Wire payment-change mother payment through Kill Bill pre-registration, workflow start, paid verification, receipt capture, operator completion, and workflow/projection polling.
- Replace mixed customer price-table/payment-row display with
MotherVisibleChargeSnapshotfor every post-closeout and post-adjustment customer amount. - Turn service-balance override and price-version rerun into
InternalSettlementAdjustmentplusSettlementRecalculationAuditinstead of mutable amount edits. - Freeze branch/HQ settlement statements at opening time and keep later recalculation as diagnostic/reversal events, not display-time recomputation.
- Model virtual-account lifecycle explicitly: issue, backend-rendered deposit instruction, deadline warning, paid deposit closeout, expiry cancellation, duplicate event handling, notices, and operator repair actions backed by Kill Bill lookup/webhook replay/virtual-account close.
- Link reservation cancellation to cancel/refund workflow, coupon restoration, customer receipt state, and settlement reversal.
- Add frontend/operator Kill Bill repair and payment-change screens; current visible frontend proof covers booking pre-registration only.