← Hub
Architecture
PDI Reference Services Microservice Design APIs
Customer Layer
Next.js Storefront
Framework: Next.js 15
Port: 3000
New
↕ REST
MedusaJS Backend
Framework: MedusaJS 2.x
Port: 9000
Active
HTTP
Port 3001
Service Layer
loyalty-service
Framework: NestJS
Port: 3001
New
OAuth2 REST
Client Credentials
External Layer
PDI Conexxus API
Protocol: REST + OAuth2
Provider: PDI
External
Side Integrations & Infrastructure
PG
PostgreSQL
Primary database for orders, products, loyalty sessions & configs
Database
R
Redis
Session cache, rate limiting, circuit breaker state, pub/sub events
Cache
S3
S3 Storage
Product images, media assets, static file hosting
Storage
MeiliSearch
Full-text product search, faceted filtering, typo tolerance
Search
$
Stripe
Primary payment processor — cards, ACH, Apple Pay
Payments
F
Finix
Secondary payment processor for fuel & c-store transactions
Payments
🔥
Firebase
Push notifications, customer auth, real-time loyalty updates
Notifications
SendGrid
Transactional emails — order confirmations, loyalty receipts
Email
🐳
Docker
Container orchestration for all services, dev & production parity
DevOps
Nginx
Reverse proxy, SSL termination, load balancing, static serving
DevOps
StorefrontNext.js 15
Medusa BackendMedusaJS 2.x
loyalty-serviceNestJS
PDI APIConexxus REST
IDENTIFY
1
Enter Phone #
Customer enters phone number on checkout page to link loyalty account.
2
POST /loyalty/identify
Medusa receives identify request, creates session record, forwards to loyalty-service.
3
Validate Session
Creates loyalty session, validates phone with PDI, assigns pos_sequence_id. State → ACTIVE.
identifyMember()
PDI Conexxus API validates the member phone number and returns loyalty account data.
REWARDS
4
Display Rewards
UI renders available instant and optional rewards returned from backend.
5
POST /loyalty/rewards
Backend sends cart items to loyalty-service to calculate available rewards and points.
6
Calculate Rewards
Loyalty service calls PDI calculateRewards with cart items and session. Returns instant + optional rewards.
calculateRewards()
PDI engine evaluates cart against promotions, returns eligible rewards and point multipliers.
7
Select Reward
Customer picks optional rewards from the rewards panel. Instant rewards auto-applied.
8
Apply to Cart
Backend applies selected reward as line item discount. Recalculates cart totals with tax.
Update Selection
Session rewards JSONB updated with selected reward IDs. Session state remains ACTIVE.
CHECKOUT
9
Cart Re-eval
If items change, frontend debounces then triggers reward recalculation via backend.
10
Recalculate Totals
Cart totals recalculated: subtotal, loyalty discount, tax (Numeral), delivery fee, tip.
11
Show Updated Totals
Updated breakdown: original, loyalty savings, new total. Customer reviews final amount.
12
Place Order
Customer clicks "Place Order". Payment processed via Finix/Stripe. Order created.
Process Payment
Payment captured via Finix gateway. Order record created. Triggers OrderCompleted event.
FINALIZE
13
Emit OrderCompleted
Backend emits event (SQS recommended). Loyalty service subscribes to finalize transaction.
Finalize Session
Session state → PENDING_FINALIZATION. Calls PDI finalize. On success → FINALIZED.
finalize()
PDI records final transaction: points earned, rewards redeemed. Returns confirmation ID.
14
Confirmation Page
Order confirmation with loyalty summary: points earned, rewards redeemed, new balance.
Return Summary
Backend compiles full order summary with loyalty data for confirmation display.
Points Recorded
Session finalized. Points and rewards permanently recorded. Session state = FINALIZED.
Phase 1: Identify (Steps 1-3)
Phase 2: Rewards (Steps 4-8)
Phase 3: Checkout (Steps 9-12)
Phase 4: Finalize (Steps 13-14)
Hover any step for details
Critical Path
14 Steps
4 phases, 4 service actors
PDI Calls
3 Round-trips
identify → calculateRewards → finalize
Circuit Breaker
Steps 3, 6, 13
Opossum wraps all PDI calls
Failure Mode
Graceful Bypass
Checkout succeeds without loyalty
StoreLoyaltyConfig
store_id UUID
enabled boolean
provider string
client_id string
client_secret string
participant_id string
site_id string
created_at timestamp
LoyaltySession
id UUID
store_id UUID
cart_id UUID
pos_sequence_id string
status enum
rewards JSONB
pdi_transaction_id string
created_at timestamp
LoyaltyCustomer
id UUID
customer_id UUID
membership_id string
phone string
linked_at timestamp
LoyaltyReward
id UUID
session_id UUID
reward_type string
description text
discount_amount decimal
applied boolean
OrderCompleted
Triggered when order is placed and payment confirmed. Contains order details, customer info, and cart contents.
finalizeSession()
CustomerRegistered
New customer account created. Triggers automatic loyalty account creation and linking to PDI.
autoCreateAccount()
CartUpdated
Item added/removed or quantity changed. Re-evaluates available rewards and point eligibility.
reEvaluateRewards()
LoyaltyConfigChanged
Store loyalty settings modified (credentials, provider, features). Clears caches for consistency.
invalidateCache()
MemberLinked
Customer linked to PDI membership. Fetches initial balance and point history.
syncMemberData()
AuthTokenExpired
OAuth2 token from PDI has expired. Must refresh to maintain integration.
refreshToken()