Skip to content

Customer Engagement Commerce Gap Mining

Scope: customer-facing commerce and engagement features from the legacy web source. This is not a UI-copy exercise. Source paths are evidence only; modern implementation should use Supabase, Kill Bill, Strapi, Novu, Storage, Cron/Queues, and DDD feature slices.

  • PromotionEntitlement: customer-owned coupon, gift coupon, product coupon, or service credit with ownership, expiry, use, cancellation, and restoration state.
  • GiftCouponPurchase: buyer/receiver gift coupon payment intent, payment proof, issued coupon, SMS handoff, resend, and cancellation state.
  • GiftFulfillmentRequest: product gift order using an entitlement, with recipient, address, fulfillment state, and operator repair history.
  • PromotionUseLedger: immutable coupon use/restoration journal for reservation payment, gift fulfillment, cancellation, and expiry.
  • CampaignPublication: event, promotion, alliance, popup, sponsor benefit, result page, and campaign landing content authored through CMS.
  • NotificationInboxItem: customer-visible notification with show window, action target, read state, and delivery audit linkage.
  • PartnerBenefitProgram: configurable external benefit program such as milk, lovebox, or cleaning service.
  • PartnerBenefitLead: customer application, eligibility proof, cancellation, export batch, result attachment, and partner handoff audit.
flowchart LR
  Customer[Customer portal]
  Kill Bill[Kill Bill]
  CMS[Strapi CMS]
  Novu[Novu]
  Cron[Supabase Cron/Queues]
  Storage[(Supabase Storage)]

  Promotion[PromotionEntitlement]
  GiftPurchase[GiftCouponPurchase]
  GiftFulfillment[GiftFulfillmentRequest]
  Campaign[CampaignPublication]
  Inbox[NotificationInboxItem]
  PartnerProgram[PartnerBenefitProgram]
  PartnerLead[PartnerBenefitLead]
  ExportBatch[PartnerBenefitExportBatch]

  Customer --> GiftPurchase
  GiftPurchase --> Kill Bill
  GiftPurchase --> Promotion
  Promotion --> GiftFulfillment
  GiftFulfillment --> Promotion
  Customer --> Campaign
  CMS --> Campaign
  Campaign --> Promotion
  Customer --> PartnerLead
  PartnerProgram --> PartnerLead
  Cron --> ExportBatch
  PartnerLead --> ExportBatch
  ExportBatch --> Storage
  Promotion --> Inbox
  PartnerLead --> Inbox
  Inbox --> Novu

Coupon Purchase, Registration, Resend, Cancellation

Section titled “Coupon Purchase, Registration, Resend, Cancellation”

Source behavior:

  • Gift coupon payment prepare accepts buyer/receiver information, price, gift coupon type, and message, then creates a pending coupon payment row. Evidence: source-refs/sanmopia_web/application/controllers/api/Shop.php:10-55 and source-refs/sanmopia_web/application/models/Coupon_model.php:280-319.
  • Source deletes stale unpaid coupon payment rows from previous days before creating a new pending payment. Evidence: source-refs/sanmopia_web/application/models/Coupon_model.php:289-294.
  • Coupon issuance uses generated coupon numbers, buyer/receiver fields, price, and publish date. Evidence: source-refs/sanmopia_web/application/models/Coupon_model.php:396-453.
  • Gift coupon registration can happen by coupon id or by receiver phone after signup/profile changes. Evidence: source-refs/sanmopia_web/application/models/Coupon_model.php:328-394.
  • Gift coupon resend sends SMS to a new receiver phone but source update of receiver fields is commented out. Evidence: source-refs/sanmopia_web/application/controllers/api/Shop.php:109-177.
  • Gift coupon cancellation marks coupon payment refunded and coupon deleted only after checking whether the gift coupon was used. Evidence: source-refs/sanmopia_web/application/controllers/api/Shop.php:179-230 and source-refs/sanmopia_web/application/models/Coupon_model.php:561-583.

Modern gap:

  • GiftCouponPurchase should use Kill Bill pre-registration and verification, then issue PromotionEntitlement only from confirmed payment state.
  • PromotionEntitlement needs owner/receiver separation. Anonymous buyer and future signup claim should be explicit, not inferred by mutable phone lookup.
  • Resend must be a GiftCouponDeliveryAttempt with recipient snapshot, provider response, idempotency key, and delivery audit. It must not silently change ownership.
  • Cancellation must distinguish payment refund, entitlement revocation, already-used rejection, and customer-visible refund status.

Product Gift Fulfillment And Coupon Restoration

Section titled “Product Gift Fulfillment And Coupon Restoration”

Source behavior:

  • Gift product order validates login, coupon ownership, usable coupon state, product id, recipient, phone, address, and comment, then inserts a product application. Evidence: source-refs/sanmopia_web/application/controllers/api/Shop.php:57-107.
  • Product coupon use checks for existing product apply rows and old product coupon kinds. Evidence: source-refs/sanmopia_web/application/models/Coupon_model.php:260-278.
  • Shopping mall coupons can be created from reservation context and attached to a user coupon row. Evidence: source-refs/sanmopia_web/application/models/Coupon_model.php:495-528.
  • Reservation cancellation restores used coupons and appends a reuse log with reservation id. Evidence: source-refs/sanmopia_web/application/models/Coupon_model.php:530-553.
  • Admin stop/cancel status changes reset product coupons only for normal-service reservations whose status is 13 or 9. Source hard-deletes existing COUPON_TB rows, counts attended days from MANAGER_ATTENDANCE_TB, republishes no coupon for <=2 days, a one-week PRODUCT1 20,000 coupon for 3-7 days, or a two-week PRODUCT2 30,000 coupon for >=8 days, then immediately inserts a USER_COUPON_TB row with +6 month expiry. Evidence: source-refs/sanmopia-admin/application/models/Reservation_model.php:1873-1880, source-refs/sanmopia-admin/application/models/Reservation_model.php:1888-1892, source-refs/sanmopia-admin/application/models/Coupon_model.php:63-87, source-refs/sanmopia-admin/application/models/Coupon_model.php:97-142, and source-refs/sanmopia-admin/application/models/Coupon_model.php:144-157. Backend domain now owns the replacement as ProductCouponResetPolicy: it deduplicates stale product entitlement ids into auditable revocations, plans no replacement for <=2 actual service days, and uses the versioned promotion catalog for 3-7 and >=8 actual-service-day replacement entitlements instead of source PRODUCT1/PRODUCT2 literals. Application command PlanReservationPromotionSideEffectHandler now composes product reset and cancellation-only reservation coupon restore in one persistable plan, so stop status 13 cannot accidentally restore used reservation coupons. Remaining migration is outbox command execution, workflow binding, repair commands, and actor-visible ledger/read models. Supabase persistence now stores sanmopia_reservation_promotion_side_effect_plans plus sanmopia_promotion_entitlement_outbox_entries so replay can use idempotency-key conflict protection instead of controller deletes or flag flips.
  • Initial product coupon issue is separate from reset/reissue: normal reservations can receive one-week or two-week product coupons based on service term and publish date, voucher-only service details are excluded, and an existing coupon may have only its publish date updated for compatibility. Evidence: source-refs/sanmopia-admin/application/models/Coupon_model.php:210-323.
  • Used reservation coupons can also be reopened by cancellation logic. Web cancellation writes mother actor text; admin cancellation writes admin actor text. Both clear USE_FL and USE_DT and append USER_COUPON_REUSE_LOG. Backend domain now owns this as ReservationCouponRestorePolicy, with actor-aware restore reasons and existing PromotionEntitlement.restore transitions instead of direct flag updates. The application side-effect command defaults customer, operator, and system restore reasons from actor kind and rejects reservation coupon restore ids on service-stop commands. Evidence: source-refs/sanmopia_web/application/models/Coupon_model.php:530-553, and source-refs/sanmopia-admin/application/models/Coupon_model.php:935-958.
  • Coupon expiry processing scans coupons whose expiry is today and writes expiry history. Evidence: source-refs/sanmopia_web/application/models/Coupon_model.php:624-640.

Modern gap:

  • GiftFulfillmentRequest should consume a PromotionEntitlement through a command that creates a PromotionUseLedger entry. Fulfillment, entitlement use, and address snapshot must commit atomically.
  • Reservation cancellation should execute PromotionEntitlementRestored facts from the persisted side-effect plan through the payment/reservation workflow instead of directly toggling use flags.
  • Stop/cancel product-coupon reset now has an application-level ReservationPromotionSideEffectPlan with actual-service-day input, original entitlement lineage, idempotency key, cancellation/stop trigger, Supabase plan snapshot, and entitlement outbox rows. Remaining acceptance: execute entitlement cancellation/issue/restore from the outbox, add repair state, and prove stopped or cancelled normal reservations reproduce the source bands (<=2 none, 3-7 one-week 20,000, >=8 two-week 30,000) without duplicate grants on command replay or repeated edits while already stopped.
  • Used-coupon restoration should append ledger facts and customer-visible history instead of only flipping USER_COUPON_TB flags.
  • Expiry should be a scheduled Supabase Cron/Queue job over entitlement policy versions with idempotent ledger entries.
  • Product coupon source codes such as product buckets remain adapter mappings, not domain enums.

Source behavior:

  • Event controller maps many hardcoded slugs to static view templates for review events, app review events, culture events, sponsor benefits, campaign results, and one-off partner pages. Evidence: source-refs/sanmopia_web/application/controllers/Event.php:14-184.
  • Additional service benefits are another switch over sponsor slugs, with deleted/deprecated cases left in comments and some image-only pages. Evidence: source-refs/sanmopia_web/application/controllers/Event.php:186-227.
  • Alliance page and event result page are separate switch-style routes. Evidence: source-refs/sanmopia_web/application/controllers/Event.php:229-264.

Modern gap:

  • CampaignPublication should replace static view switches. Strapi should own campaign page body, media, CTA, active windows, result pages, partner sponsor pages, and deprecated state.
  • CampaignPlacement should own popup/banner/entry-slot placement rules and dismissal keys.
  • Review-event application, winner announcement, and reward issuance should be modeled as campaign participation/award facts if any operational workflow is required; static content alone is insufficient.

Source behavior:

  • Notification rows store user id, notification type, optional show date, and optional action data payload. Evidence: source-refs/sanmopia_web/application/models/Notification_model.php:16-39.
  • Notifications can be inserted from FCM token lookup, resolving token to user before inserting an inbox row. Evidence: source-refs/sanmopia_web/application/models/Notification_model.php:42-53.
  • Inbox reads notification type template data plus row-specific data where show date is due, ordered newest first. Evidence: source-refs/sanmopia_web/application/models/Notification_model.php:56-69.

Modern gap:

  • NotificationInboxItem should separate content template, row action payload, show window, read/archive state, and delivery channel audit.
  • Novu dispatch should write CommunicationDispatchEvent; inbox item should not be the delivery proof.
  • Token-based insertion is a legacy adapter concern. Modern commands should target customer profile ids and let communication delivery resolve devices.

Source behavior:

  • Momfirst submit validates user login, applicant fields, program types, eligible reservation, then inserts one or more benefit application rows and sends customer notifications by program. Evidence: source-refs/sanmopia_web/application/controllers/api/Momfirst.php:10-95.
  • Momfirst cancel loops selected product types and deletes application info. Evidence: source-refs/sanmopia_web/application/controllers/api/Momfirst.php:97-146.
  • Momfirst model owns program type validation, eligible reservation lookup, insert/cancel, export data, export sent flags, SMS reservation, application status reads, and delete flows. Evidence: source-refs/sanmopia_web/application/models/Momfirst_model.php:21-859.
  • Clean service submit validates housing/service fields, eligible reservation, duplicate application state, and inserts a lead. Evidence: source-refs/sanmopia_web/application/controllers/api/Clean.php:9-91.
  • Clean service has export data, export sent flags, cancellation, eligibility, and application status paths. Evidence: source-refs/sanmopia_web/application/models/Clean_model.php:50-673.
  • CLI schedulers build and send CSV exports for Momfirst/Clean programs. Evidence: source-refs/sanmopia_web/application/controllers/cli/Momfirst.php:22-174 and source-refs/sanmopia_web/application/controllers/cli/Clean.php:21-142.

Modern gap:

  • PartnerBenefitProgram must be configurable by code, eligibility policy, required fields, cancellation window, export cadence, partner destination, notification workflow, and active dates.
  • PartnerBenefitLead should keep applicant snapshot, reservation eligibility proof, submitted fields, cancellation state, and customer-visible status.
  • PartnerBenefitExportBatch should be generated by workflow/Cron, write CSV or partner payload artifacts to private Supabase Storage, and track sent state, retry, missing rows, and partner response.
  • Lead deletion should become cancellation/anonymization policy. Do not hard delete rows when legal/audit retention or partner export state exists.
  • Backend/contract: PromotionEntitlement read model with ownership, receiver, expiry, use, restoration, and cancellation state.
  • Backend/contract: GiftCouponPurchase Kill Bill workflow with payment verification, entitlement issuance, resend attempt, and refund/revoke state.
  • Backend/contract: GiftFulfillmentRequest command and operator repair audit.
  • Backend: PromotionUseLedger idempotent restore/expire/use ledger.
  • Backend/contract/frontend-worker: customer coupon wallet projection and sent coupon projection.
  • Backend/docs: CampaignPublication and CampaignPlacement through Strapi, including campaign result pages and sponsor benefit slots.
  • Backend/contract: NotificationInboxItem read model plus Novu dispatch audit linkage.
  • Backend/contract: PartnerBenefitProgram, PartnerBenefitLead, and PartnerBenefitExportBatch with Supabase Storage artifacts.
  • Docs: map each legacy coupon/product/program source code at adapter boundary only; no legacy product code as domain language.