intermediate
billing
wallet
payments

Wallet & Payments

Complete guide to the customer wallet system, payment methods, auto top-up functionality, and payment processing via Stripe.

Levy Fleets Team25 de diciembre de 202516 min read

Wallet & Payments

This guide covers the customer wallet system, payment methods, auto top-up functionality, and payment processing. Understanding these systems is essential for operators troubleshooting billing issues and for customers managing their payment preferences.


Overview

The payment system consists of three key components:

  1. Customer Wallet - A stored-value account for ride payments
  2. Payment Methods - Saved credit/debit cards linked to Stripe
  3. Auto Top-Up - Automatic wallet funding when balance is low

Rides are paid from the wallet balance. When the balance is insufficient, the system can automatically charge the customer's default payment method.


Customer Wallet

How the Wallet Works

Each customer has a wallet balance that:

  • Stores credit in USD
  • Is used to pay for rides
  • Can be funded manually or automatically
  • Receives credits from promo codes, referrals, and loyalty redemption

Wallet Balance Flow:

Credits IN:
  • Manual top-up (customer adds funds)
  • Auto top-up (automatic when low)
  • Promo code redemption
  • Referral rewards
  • Loyalty points redemption
  • Customer service credits

Debits OUT:
  • Ride charges
  • Subscription purchases
  • Package purchases

Wallet Balance Display

Customers see their wallet balance:

  • In the mobile app home screen
  • Before starting a ride
  • On ride receipts
  • In account settings

Format: Always displayed in dollars with 2 decimal places (e.g., "$25.00")

Minimum Balance Requirements

To start a ride, customers must have:

  • Positive wallet balance, OR
  • Valid payment method for auto top-up, OR
  • Active subscription/package that covers the ride

Important

If a customer's wallet is empty and auto top-up is disabled, they cannot start a ride.


Manual Top-Up

Customer Top-Up Flow

  1. Customer opens wallet/payment section
  2. Selects or enters top-up amount
  3. Chooses payment method
  4. Confirms payment
  5. Wallet balance updates instantly

Top-Up Amounts

Operators can configure preset amounts:

SettingDescriptionExample
Preset amountsQuick-select options$10, $25, $50, $100
Custom amountAllow any amount$15.00 (min $5, max $500)
Minimum top-upLowest allowed amount$5.00
Maximum top-upHighest allowed amount$500.00

Top-Up Processing

Top-ups are processed via Stripe:

  1. Create PaymentIntent with amount
  2. Confirm with customer's payment method
  3. On success: Increment wallet balance
  4. Create wallet transaction record
  5. Send confirmation (email/push)

Auto Top-Up

How Auto Top-Up Works

Auto top-up automatically charges the customer when their wallet balance falls below a threshold:

If wallet_balance ≤ threshold:
  Charge default payment method for top-up amount
  Add funds to wallet

When It Triggers:

  • Before ride starts (if balance insufficient)
  • After ride ends (if balance went negative)
  • During ride (for real-time billing)

Configuration Requirements

Auto top-up requires BOTH:

  1. Customer opt-in - Customer enables auto top-up in their settings
  2. Subaccount enabled - Operator enables auto top-up for the location
LevelSettingWho Controls
Customerauto_topup_enabledCustomer in app
Subaccountauto_topup_enabledOperator in dashboard
Subaccountauto_topup_amount_centsOperator in dashboard
Subaccountauto_topup_threshold_centsOperator in dashboard

Configuration Options

Subaccount Settings:

SettingDescriptionDefaultExample
auto_topup_enabledEnable for this locationfalsetrue
auto_topup_amount_centsAmount to charge15002000 ($20)
auto_topup_threshold_centsTrigger threshold5001000 ($10)

Example Configuration:

  • Threshold: $5.00 (500 cents)
  • Top-up amount: $15.00 (1500 cents)

When wallet drops to $5 or below, the system automatically charges $15.

Auto Top-Up Flow

1. Check if customer allows auto top-up (customer.auto_topup_enabled)
2. Check if subaccount allows auto top-up (subaccount.auto_topup_enabled)
3. Get threshold and amount from subaccount settings
4. If wallet_balance ≤ threshold:
   a. Acquire lock (prevent duplicate charges)
   b. Get customer's default payment method
   c. Create Stripe PaymentIntent
   d. Confirm payment (off_session)
   e. Increment wallet balance
   f. Record transaction
   g. Release lock

Concurrency Protection

The system prevents duplicate charges using a database lock:

FieldDescription
auto_topup_lock_idUnique lock identifier
auto_topup_lock_expires_atLock expiration time

Lock Flow:

  1. Attempt to acquire lock (2-minute TTL)
  2. If lock acquired, proceed with charge
  3. If lock exists (another process is charging), wait and check for wallet increase
  4. Release lock when done

Error Handling

ErrorCauseResolution
Card declinedInsufficient funds, expired cardNotify customer to update payment
Authentication required3DS challenge neededPrompt customer for authentication
No payment methodNo card on filePrompt customer to add card
Lock conflictAnother charge in progressWait for other charge to complete

Payment Methods

Storing Payment Methods

Payment methods are stored via Stripe:

  1. Customer enters card details
  2. Card tokenized by Stripe.js (PCI compliant)
  3. PaymentMethod attached to Stripe Customer
  4. Reference saved to local database

Payment Method Table

FieldDescription
stripe_payment_method_idStripe PM identifier
last_fourLast 4 digits of card
brandCard brand (Visa, Mastercard, etc.)
exp_monthExpiration month
exp_yearExpiration year
is_defaultDefault for charges

Default Payment Method

One payment method is marked as default:

  • Used for auto top-up
  • Used for manual top-up (unless another selected)
  • Used for subscription charges

Setting Default:

  1. Customer selects "Make Default" in app
  2. System updates is_default flag
  3. Syncs with Stripe's invoice_settings.default_payment_method

Managing Payment Methods

Adding a Card:

  1. Customer taps "Add Payment Method"
  2. Stripe Elements collects card details
  3. Card validated and tokenized
  4. PaymentMethod created and saved

Removing a Card:

  1. Customer selects card to remove
  2. System checks it's not the only card (if auto top-up enabled)
  3. Detaches PaymentMethod from Stripe Customer
  4. Deletes local record

Updating a Card: Cards cannot be updated - customer must add new card and remove old one.


Wallet Transactions

Transaction Types

TypeDescriptionAmount Sign
creditFunds added to walletPositive
debitFunds deducted from walletNegative
topupManual top-upPositive
auto_topupAutomatic top-upPositive
rideRide chargeNegative
refundCharge refundPositive
promoPromo code redemptionPositive
referralReferral rewardPositive
loyaltyLoyalty points redemptionPositive
adjustmentManual adjustmentEither

Transaction Record

FieldDescription
customer_idCustomer reference
amountAmount in dollars
typeTransaction type
descriptionHuman-readable description
reference_typeSource type (ride, promo, etc.)
reference_idSource identifier
balance_afterWallet balance after transaction
stripe_payment_intent_idStripe PI (for payments)
created_atTimestamp

Viewing Transaction History

Customers can view their transaction history:

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

  WALLET HISTORY

  Today
  ─────
  Ride completed              -$7.35
  Balance: $17.65

  Yesterday
  ─────────
  Auto top-up                 +$15.00
  Balance: $25.00

  Ride completed              -$8.50
  Balance: $10.00

  Dec 23
  ─────────
  Promo code HOLIDAY10        +$10.00
  Balance: $18.50

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Ride Payment Flow

Start-of-Ride Balance Check

When a customer starts a ride:

  1. Check wallet balance - Is it above $0?
  2. Check auto top-up eligibility - Is it enabled and has payment method?
  3. Check coverage - Does subscription/package cover the ride?

If none of the above are met, ride cannot start.

End-of-Ride Payment

When a ride ends:

1. Calculate final ride charge
2. Check current wallet balance
3. If balance >= charge:
   - Deduct from wallet
   - Record transaction
4. If balance < charge AND auto top-up enabled:
   - Process auto top-up first
   - Then deduct ride charge
5. If balance < charge AND no auto top-up:
   - Deduct available balance
   - Record outstanding amount
   - Schedule payment retry

Payment Retry System

When a customer has an outstanding balance:

  1. Schedule retry - Add to background retry queue
  2. Retry attempts - Try to collect over time
  3. Notification - Alert customer of failed payment
  4. Account restriction - Block new rides until balance cleared

Retry Schedule:

AttemptDelayAction
1ImmediateFirst charge attempt
21 hourRetry with notification
324 hoursRetry with warning
472 hoursFinal attempt
5+ManualRequires operator intervention

Operator Dashboard

Viewing Customer Wallet

In customer detail view:

FieldDescription
Wallet BalanceCurrent balance in dollars
Auto Top-UpEnabled/Disabled status
Payment MethodsList of saved cards
Transaction HistoryRecent wallet activity

Manual Wallet Adjustments

Operators can adjust wallet balances:

Adding Credit:

  • Customer service gestures
  • Refunds for service issues
  • Promotional credits

Removing Credit:

  • Fraud corrections
  • Error corrections

All adjustments require:

  • Amount
  • Reason/description
  • Operator authentication

Transaction Export

Export wallet transactions for:

  • Accounting reconciliation
  • Customer disputes
  • Audit purposes

Stripe Integration

Test vs Live Mode

The system supports both Stripe modes:

ModeKey TypeUse Case
Livesk_live_*Production transactions
Testsk_test_*Internal testing

Test Mode Selection:

  • @levyelectric.com emails use test mode
  • All other customers use live mode

Stripe Objects Used

ObjectPurpose
CustomerLinks local customer to Stripe
PaymentMethodStored card details
PaymentIntentIndividual payment
SetupIntentCard setup without charge

Webhooks

Stripe webhooks update local records:

EventAction
payment_intent.succeededConfirm wallet credit
payment_intent.failedLog failure, notify customer
payment_method.attachedSave payment method locally
payment_method.detachedRemove payment method locally

Security Considerations

PCI Compliance

  • Card numbers never touch our servers
  • All card collection via Stripe.js/Elements
  • Only store tokenized references

Data Protection

  • Wallet balances encrypted at rest
  • Transaction history access controlled
  • Payment method access restricted

Fraud Prevention

  • Velocity limits on top-ups
  • Unusual activity detection
  • Manual review thresholds

Technical Reference

Database Tables

customers (wallet fields)

ColumnTypeDescription
wallet_balanceDecimalCurrent balance in dollars
stripe_customer_idTextStripe Customer ID
auto_topup_enabledBooleanCustomer's auto top-up preference
auto_topup_lock_idUUIDConcurrency lock ID
auto_topup_lock_expires_atTimestampLock expiration

payment_methods

ColumnTypeDescription
customer_uuidUUIDReference to customer
stripe_payment_method_idTextStripe PM ID
last_fourTextLast 4 card digits
brandTextCard brand
exp_monthIntegerExpiration month
exp_yearIntegerExpiration year
is_defaultBooleanDefault payment method

wallet_transactions

ColumnTypeDescription
customer_idUUIDReference to customer
amountDecimalAmount in dollars
typeTextTransaction type
descriptionTextDescription
reference_typeTextSource type
reference_idUUIDSource ID
balance_afterDecimalBalance after transaction
stripe_payment_intent_idTextStripe PI ID

subaccounts (auto top-up fields)

ColumnTypeDescription
auto_topup_enabledBooleanEnable for location
auto_topup_amount_centsIntegerAmount to charge
auto_topup_threshold_centsIntegerTrigger threshold

Core Functions

FunctionLocationPurpose
processAutoTopupsrc/lib/auto-topup.tsExecute auto top-up charge
canProcessAutoTopupsrc/lib/auto-topup.tsCheck if auto top-up possible
incrementWalletCentsRPC functionAtomically update wallet
acquireAutoTopupLockRPC functionGet concurrency lock
releaseAutoTopupLockRPC functionRelease concurrency lock

API Endpoints

MethodEndpointAction
GET/api/mobile/paymentGet wallet & payment methods
POST/api/mobile/payment/topupManual top-up
POST/api/mobile/payment/methodsAdd payment method
DELETE/api/mobile/payment/methods/:idRemove payment method
PUT/api/mobile/payment/methods/:id/defaultSet default

Troubleshooting

Auto Top-Up Not Working

  1. Check customer setting - Is auto_topup_enabled true?
  2. Check subaccount setting - Is auto top-up enabled for location?
  3. Verify payment method - Is there a default card?
  4. Check Stripe customer - Is stripe_customer_id valid?
  5. Review lock status - Is there a stale lock blocking?

Payment Failed

  1. Check Stripe error - Card declined? Expired? 3DS required?
  2. Verify payment method - Is it still valid in Stripe?
  3. Review account status - Is Stripe account active?
  4. Check idempotency - Was the same charge attempted twice?

Wallet Balance Incorrect

  1. Review transaction history - What transactions occurred?
  2. Check for pending charges - Any in-flight payments?
  3. Verify ride charges - Were rides billed correctly?
  4. Check for duplicates - Any duplicate transactions?

Cannot Start Ride

  1. Check wallet balance - Is it $0 or negative?
  2. Verify auto top-up - Is it enabled and functional?
  3. Check payment method - Is there a valid card?
  4. Review outstanding balance - Any unpaid charges?

Need Help?

For wallet and payment assistance, contact support@levyelectric.com.