Reciprocity & Stacking
Deep dive into reciprocity agreements, defaults, and how stacking works.
Agreement types
- Bilateral (
type: BILATERAL): between two clubs (clubAId,clubBId) with directionA_TO_B,B_TO_A, orBOTH. - Network (
type: NETWORK): any clubs sharingnetworkCode(e.g.,SAGA_NETWORK) can honor the network agreement. Membership is tracked inClubNetworkMembership.
Rate configuration (ReciprocityRateConfig)
| Field | Meaning | Defaults |
|---|---|---|
discountType | PERCENT, FIXED_AMOUNT, FIXED_RATE, RATE_TIER | required |
discountValue | % (0–100) or cents off (for PERCENT/FIXED_AMOUNT) | 0 |
fixedRateCents | Final price in cents (FIXED_RATE) | falls back to base price |
rateTierCode | Tier code for RATE_TIER passthrough | undefined |
priority | Lower = chosen first (ties break on updatedAt) | 10_000 |
validDaysOfWeek | Array of MON..SUN (case-insensitive) | all days |
validTimeStart / validTimeEnd | HH:MM window (24h) | none |
blackoutDates | YYYY-MM-DD list | none |
minHandicap / maxHandicap | Inclusive range | none |
Restrictions block the agreement with reasons DAY_RESTRICTED, TIME_RESTRICTED, BLACKOUT_DATE, HANDICAP_OUT_OF_RANGE.
Resolution flow (ReciprocityService)
- Require
membershipNumber; ensure club policyreciprocityEnabled. - Resolve home club (provider code → membership/homeClubCode hint). Home club == target → no reciprocity.
- Gather applicable agreements:
- Bilateral(s): home ↔ target.
- Network: only if both clubs share
networkCode.
- Apply rate config(s) respecting restrictions.
- Return pricing +
appliedAgreements[]audit trail.
Stacking behavior
stackingMode(input):BEST_PRICE(default) orSTACK.- BEST_PRICE: pick the first applicable agreement (bilateral preferred, otherwise network). Matches previous behavior.
- STACK: apply all eligible bilaterals (priority-sorted) then the network agreement if present. Discounts are applied sequentially to the running price. The response includes
appliedAgreementswith each discount delta. - If every candidate is blocked (no rate config/restriction), the service returns the last reason and no discount.
Defaults & safeguards
- Provider default:
SAGA_NETWORKwhen noproviderCodeis supplied. - Policy fetch failure → reciprocity treated as enabled with visitor access (logs a warning, never fails the request).
- Unknown discountType falls back to base price (no-op).
- Errors in home-club lookup degrade to
NO_AGREEMENTwithout failing pricing/streams.
Inputs you must forward
Send these in pricing/search calls to activate reciprocity:
{
"tenantId": "tenant-uuid",
"membershipNumber": "ABC123",
"providerCode": "SAGA_NETWORK",
"homeClubCode": "HOME123", // optional hint
"handicap": 9 // optional, for handicap-gated deals
}
Outputs to expect
Response fields (when applicable):
finalPriceCents,discountCents,agreementId,rateType,source(BILATERAL|NETWORK)appliedAgreements[]: ordered list of all applied agreements with per-stepdiscountCentsreasonwhen not applicable (see denial reasons above)
Operational checklist
- Create bilateral/network agreements with priorities where multiple should stack.
- Ensure
ClubNetworkMembershipexists for every club expected to use a network agreement. - Populate day/time/handicap limits to match business rules; leave empty to allow all.
- Prefer
STACKmode when you want bilateral overrides + network fallback together.