Huang XinPROJECT CASE
PROJECT CASE / REBATE-PLATFORM

Multi-platform Rebate System

Owned the core rebate pipeline across twelve e-commerce and local-life channels, unifying promotion link conversion, user attribution, order feedback reconciliation, commission calculation, fund payout and reverse claw-back. Through channel-strategy onboarding, fund idempotency, distributed scheduling and daily settlement reconciliation, long-cycle asynchronous orders became traceable and compensable, with low-cost onboarding of new channels.

My Role
Core Module Owner
Timeline
2024.11 — Present
Tech
Core Module Owner · Java / Spring · Distributed Scheduling · 12 Channels · Money-flow · 2024.11 — Present

Project Overview

A monetization money-flow system that connects a "user click" to a "real payout": promotion links, user attribution, order feedback, commission calculation, fund payout and reverse claw-back across twelve external channels, all converged into a single model.

Rebate is a CPS distribution business. A user clicks a product on our site, jumps to an external channel to place an order, and days or weeks later the channel feeds the order back. The system then calculates the rebate based on continuously changing order states and pays it into the user's balance. If any step in this chain fails silently, the end result is always that the user does not get paid.

I owned commission calculation and fund payout, the cross-channel attribution and link-conversion system, the channel onboarding framework, and the marketing campaigns built on top of the rebate pipeline.

Dimension Scale
Channels covered 12 e-commerce and local-life channels, including multiple attribution systems and business lines within a single channel
Engineering structure 8 Maven modules
Service interfaces 48 client-facing interfaces and 7 internal RPC services
Async scheduling 25 distributed jobs, of which 19 are channel-sync jobs and 6 are settlement and risk-control jobs
Data model 21 core tables covering orders, message snapshots, after-sales claims, commission sharing, transfers, attribution, revenue aggregation and marketing campaigns

Business Challenges

Channel semantics cannot be unified naturally

Different channels define commission, subsidy, service fee, claim deductions and settlement state differently. Some provide both estimated and actual commission, some have subsidies with an independent settlement state, some deduct a technical service fee first, and some cannot be back-calculated from a rate. The system must let channels keep their differences while preventing those differences from leaking into the end-to-end money flow.

Orders have a long lifecycle

An order flows through "paid -> delivery confirmed -> pending settlement -> settled" over several weeks, with the commission recalculated throughout and possibly refunded at any point. The system must keep up with channel changes without mistaking an intermediate state for an unrecoverable terminal one.

Every number is real money

Overpayment must be clawed back, underpayment triggers complaints, and duplicate payout becomes a financial loss. So every write, calculation and transfer must answer one question: if the same action runs again, is the result still correct?

System Architecture

The whole pipeline is split into six stable stages, chained by hourly scheduling:

Channel order sync
  -> Normalize channel messages into a unified order model
  -> Persist orders and snapshot the raw messages
  -> Real-time commission recalculation and forward/reverse commission records
  -> Fund payout, state update and arrival notification
  -> Refund claw-back, installment deduction and daily settlement reconciliation

Onboarding a new channel only requires adding implementations in the sync, cleaning, calculation and attribution layers, without modifying the existing core orchestration.

Core Design Decisions

1. One calculation model to hold twelve pricing semantics

I separated "channel semantics" from "money flow" completely. The flow layer only decides whether an order is currently eligible for a rebate and how much it should be; each channel's strategy independently handles field priority, subsidy thresholds, service fees and claim deductions.

All channels share the same upfront checks for risk control, refund state, commission-sharing state and ratio configuration. Amounts use "cents" as an integer unit across the whole chain, and ratio calculations round down consistently. When rules change, canary rollout is keyed on the order's placement time rather than the current time, so recalculating a historical order does not drift.

2. Assume every step of the money flow may run twice

Risk Handling
Duplicate transfer Write the transfer record first to obtain a business number, then call payment; retries reuse the same number
Concurrent order writes Use an optimistic lock with a version number; skip directly when amount, state and completion time are unchanged
Multi-instance duplicate payout The calculation, payout and claw-back stages each use a distributed lock with an expiry
Transactions affecting each other The main money flow uses an independent transaction; marketing rewards are isolated in a new transaction
Early payout A transfer is allowed only after the channel settlement cooldown and the risk-control check both pass
Over claw-back A successful forward payout must exist, and the deduction cannot exceed the amount already paid
Insufficient balance Installment deduction records are generated with an increasing sequence until the remaining amount converges
Infinite retries A retry cap is set and wired into end-to-end exception alerting

The commission-sharing state is converged into eight states, with forward, terminal and pending-deduction state sets defined in the enum, so the core flow is not littered with magic state values.

3. Fixing "a temporary failure conclusion permanently freezes a rebate"

To reduce redundant work, the calculation layer once reused an existing failure reason when it found one on an order; the settlement layer then wrote the calculation-failure text back onto the order. Combined, a temporary conclusion like "rebate ratio not yet configured" became permanent once persisted, and even after operations later filled in the configuration, historical orders were still never recalculated.

For long-lifecycle-order channels I explicitly removed this short-circuit and forced a recalculation from the latest state. I also removed the platform-wide terminal judgment of "zero commission means refunded," because a zero commission on an unsettled order is a normal state.

This issue produced a design principle for everything that followed: only a truly stable result may be cached; a failure result is treated as a mutable state by default.

4. Attribution correctness over surface-level availability

Link conversion must carry both the channel and the user attribution identifier. When user-identifier generation fails, degrading to a channel-only identifier still produces a usable link and a completable order, but the feedback cannot be matched to a site user, and the rebate is lost silently.

So attribution-identifier generation is fail-closed: if the identifier is unavailable, the conversion explicitly returns a failure so the user can notice and retry. Risk control uses the opposite strategy: only an explicit rejection blocks the rebate, because a temporarily failing risk-control service must not halt the whole site's money flow.

The two choices look opposite but share one criterion: which failure mode is easier to detect, retry and compensate.

5. Isolating third-party orders in a shared promotion slot

While onboarding one channel, I found the promotion slot was not exclusive. If the attribution suffix collided with the third party's value space, another business's orders could be wrongly attributed to a site user and a real rebate paid out.

I redesigned the attribution-identifier format so the two value spaces do not overlap, and added local-ownership filtering on the order-sync side so third-party orders never enter persistence, message delivery or risk-control reporting. The isolation prefix is kept as a code constraint rather than an ordinary runtime config, to prevent a mistaken edit from stripping attribution off the entire existing order base.

Engineering Governance

Message snapshots as a write gate

Channel order snapshots use append-only, multi-version storage. Before processing, only the fields that affect order state and commission are compared; if the message has no business change, the write and recalculation are skipped.

One channel returned product line items in an unstable order, and the original signature concatenated them in traversal order, so messages with identical content were continuously judged as changed. I generated an entry per line item by content, sorted them and then signed, making the comparison independent of return order; fields that are out of sync with the main state and only add noise are explicitly excluded with a recorded reason.

Migrating from OFFSET paging to cursor pagination

As orders grew, the OFFSET ceiling of the underlying middleware caused the scan job to fail. When migrating to cursor pagination, I additionally distinguished "whether the underlying layer read raw rows" from "whether there was a result after in-memory filtering": even if the filtered result is empty, as long as the underlying layer read data, the cursor keeps advancing, avoiding a job that finishes successfully while later orders are never scanned.

Revenue aggregation and daily reconciliation

A user's cumulative revenue is maintained by an atomic database increment, and a failed aggregation update does not block the main money flow. To resolve brief inconsistency, a daily job back-calculates from the real commission-sharing records and corrects them, scanning only users with recent fund changes, striking a balance between correctness and job cost.

Bidirectional risk-control integration

Key nodes such as orders, commission sharing, payout and claw-back report to risk control asynchronously, and a delivery failure does not affect the main money-flow transaction. On the inbound side, a check is placed before both commission calculation and fund payout, with a compensation entry to recalculate historical orders after a risk-control misjudgment is lifted.

Multi-channel Onboarding Framework

The stable identifier in the channel enum is used for the implementation package, the Bean prefix and channel routing at the same time. The core orchestration finds the matching implementation by convention, so onboarding a new channel only needs an implementation class and one enum entry, without modifying the existing dispatch logic.

On the attribution side, the site user and channel are combined into a channel identifier, with both forward and reverse mappings stored; channels with special authorization requirements are isolated through a dedicated adapter layer. On the product side, an intermediate model with agreed key names connects link conversion, display, product detail and guide persistence, so heterogeneous materials such as hot lists and high-commission items can reuse the same upstream display logic.

I led the from-scratch onboarding of two channels, covering product lists, product detail, product and arbitrary-link conversion, token parsing, campaign materials, and order and claim sync. The onboarding conclusions, production discrepancies and rejected approaches are all captured in handover docs to reduce the cost of future takeover and troubleshooting.

Marketing Campaigns

On top of the core rebate pipeline I built campaigns such as multiplier cards, referral tasks, order lucky draws and newcomer subsidies. Taking the multiplier card as an example:

  • The multiplier is snapshotted at grant time, so later changes to the campaign config do not affect historical rewards;
  • Stock is decremented with an optimistic lock, and a per-order distributed lock prevents duplicate binding;
  • On refund, the reward is unbound or clawed back based on its state;
  • Installment deduction is supported when the balance is insufficient, and failed retries reuse the business number;
  • A failed marketing-reward claw-back does not block the main commission claw-back; the two funds are isolated by risk priority.

Project Results

  • Twelve channels share one calculation, attribution and money-flow pipeline, and a new channel does not modify the existing core orchestration;
  • Led two channels through from-scratch onboarding and production validation;
  • The money flow has idempotency, optimistic locks, distributed locks, installment deduction, balance checks, a retry cap and end-to-end alerting;
  • Fixed the defect where historical orders were permanently never rebated after the configuration was filled in, and the attribution-collision risk that could cause a financial loss;
  • Migrated the scan pipeline to cursor pagination, removing the paging ceiling and avoiding the silent missed orders caused by an empty post-filter result;
  • Reduced wasteful writes and ensured long-cycle fund consistency through snapshot comparison, revenue aggregation and daily reconciliation.

Tech Stack

Java · Spring · MyBatis-Plus · MySQL · Redis · ZooKeeper · Message Queue · Distributed Scheduling · RPC · Dynamic Config Center

This is a public version. Channel names, third-party interface fields, internal systems and code structure, commission rates and specific business figures have all been omitted or desensitized.