Payments System
Ricoya supports three payment methods: card (CyberSource), PayPal, and cash on delivery.
Payment Methods
| Method | Provider | Flow | Service Fee |
|---|---|---|---|
| Card | CyberSource | 3DS authentication + tokenization | 2.5% of subtotal + delivery |
| PayPal | PayPal | OAuth redirect → capture | 2.5% of subtotal + delivery |
| Cash | Manual | Driver collects at door | 0% |
Service fee percentages are configurable via the config table keys: payment-fee-card, payment-fee-paypal, payment-fee-cash.
Order Total Calculation
total = subtotal + tax + delivery_fee + service_fee + tip - discount
- subtotal: Sum of (item price × quantity) for all order items
- tax: Calculated per item from category tax mappings or direct tax_id
- delivery_fee: Distance-based ($3 base + $0.50/km beyond 1km, converted to HNL)
- service_fee: Percentage of (subtotal + delivery_fee), varies by payment method
- tip: User-selected (preset 10/15/20% or custom amount)
- discount: From discount code if applied
Server recalculates total before processing payment. If client total differs by more than 50 cents, payment is rejected (AmountMismatchError).
CyberSource Card Payment (3DS Flow)
Four-step process:
Step 1: Setup — POST /api/payments/setup
- Detects card type from BIN (Visa, Mastercard, AMEX)
- Calls CyberSource
/risk/v1/authentication-setups - Returns
accessTokenfor device data collection (DDC)
Step 2: Device Data Collection (client-side)
- Hidden iframe posts JWT to CyberSource DDC URL
- Waits for
profile.completedmessage (10s timeout) - Collects device fingerprint for fraud detection
Step 3: Payment — POST /api/payments
- Sends card details + device info + 3DS reference
- CyberSource processes with
TOKEN_CREATE+CONSUMER_AUTHENTICATION - If 3DS challenge required: returns
stepUpUrlfor bank redirect - If no challenge: returns
AUTHORIZEDwith payment ID
Step 4: Verification — POST /api/payments/verify
- Called after bank 3DS redirect
- Validates authentication with
VALIDATE_CONSUMER_AUTHENTICATION - Completes payment, returns final status
Card tokenization creates a saved_cards entry for future 1-click payments.
PayPal Flow
Create — POST /api/payments/paypal/create
- Converts HNL to USD using exchange rate from config
- Creates PayPal order with
intent: CAPTURE - Returns
approvalUrl— user redirected to PayPal
Capture — POST /api/payments/paypal/capture
- Called after user approves on PayPal
- Captures payment, extracts capture ID
- Updates order:
payment_status: "paid",status: "placed" - Sends push notification to restaurant staff
Cancel — POST /api/payments/paypal/cancel
- Marks order
payment_status: "failed"locally - No PayPal API call needed
Cash on Delivery
- Customer selects cash, enters expected amount (
cash_payment_amount_cents) - Order created with
payment_status: "pending",payment_method: "cash" - Driver collects cash on delivery
- Staff marks
payment_status: "cash_completed"via admin panel
Refunds — POST /api/payments/refund
Tries reversal first (faster, full amount), falls back to refund (partial OK):
- Reversal:
POST /pts/v2/payments/{id}/reversals— cancels original transaction - Refund:
POST /pts/v2/payments/{id}/refunds— returns funds (partial or full)
Payment Status Flow
(new) → pending → authorized → paid → refunded
↗
pending → paid ------
↘
pending → cash_completed
pending/authorized → failed
Key Files
| File | Purpose |
|---|---|
src/app/api/payments/route.ts |
CyberSource card payment |
src/app/api/payments/setup/route.ts |
3DS setup |
src/app/api/payments/verify/route.ts |
Post-3DS verification |
src/app/api/payments/refund/route.ts |
Refund/reversal |
src/app/api/payments/paypal/create/route.ts |
Create PayPal order |
src/app/api/payments/paypal/capture/route.ts |
Capture PayPal payment |
src/app/api/payments/paypal/cancel/route.ts |
Cancel PayPal order |
src/app/api/saved-cards/route.ts |
Saved card CRUD |
src/app/api/orders/[orderId]/payment-status/route.ts |
Update payment status |
src/lib/cybersource.ts |
CyberSource signature builder |
src/lib/orderTotalCalculator.ts |
Server-side total recalculation |
src/services/paymentApi.ts |
Client-side payment API wrapper |
src/services/paypalApi.ts |
Client-side PayPal API wrapper |
Environment Variables
| Variable | Purpose |
|---|---|
CYBERSOURCE_MERCHANT_ID |
CyberSource account ID |
CYBERSOURCE_KEY_ID |
API key ID |
CYBERSOURCE_SECRET_KEY |
Base64-encoded signing key |
CYBERSOURCE_API_BASE_URL |
API endpoint (prod vs test) |
CYBERSOURCE_3DS_REQUESTOR_ID |
3DS processing ID |
PAYPAL_CLIENT_ID |
PayPal OAuth client |
PAYPAL_CLIENT_SECRET |
PayPal OAuth secret |
PAYPAL_API_BASE_URL |
PayPal endpoint |