Skip to main content

Overview

This document outlines the complete lifecycle of patient app requests, from submission through the mobile app to prescription creation in the clinical system. It includes the duplicate prevention mechanisms implemented to ensure data integrity.

Architecture Flow

Request States

Core States

  • submitted: Initial request created from mobile app
  • review_required: Clinical review needed (default)
  • prescription_created: Prescription created from request
  • completed: Request fully processed

Clinical Decision Values

  • review_required: Default state requiring clinical review
  • prescription_created: Prescription successfully created
  • rejected: Request rejected by clinician
  • cancelled: Request cancelled by patient

Process Flow

1. Mobile App Request Creation

When a patient submits a reorder request:
// checkout-submit-order function
const appRequest = await supabase
  .from('patient_app_requests')
  .insert({
    patient_id: patient.id,
    request_type: 'reorder',
    status: 'submitted',
    clinical_decision: 'review_required',
    stripe_payment_intent_id: payment_intent_id,
    request_number: generateRequestNumber(),
    // ... other fields
  })
  .select()
  .single();

2. Clinical Review Process

Clinicians see the request in the clinical system:
  • Request appears in patient detail page
  • Shows patient information and requested medications
  • Allows creation of prescription from request

3. Prescription Creation

When clinician creates prescription from request:
// create-prescription-order function
await supabase
  .from('patient_app_requests')
  .update({
    clinical_decision: 'prescription_created',
    clinical_decision_made_at: new Date().toISOString(),
    prescription_order_id: prescriptionOrder.id,
    status: 'completed'
  })
  .eq('id', requestId);

4. Duplicate Prevention Logic

The system prevents duplicate requests during webhook processing:
// checkout-stripe-webhook function
const { data: existingRequest } = await supabase
  .from('patient_app_requests')
  .select('id, clinical_decision, prescription_order_id')
  .eq('stripe_payment_intent_id', paymentIntent.id)
  .single();

if (existingRequest?.clinical_decision === 'prescription_created') {
  // Skip duplicate creation - just update payment status
  await updatePaymentStatusOnly(checkout.id);
  return;
}

// Continue with normal processing for new requests
await invokeCheckoutSubmitOrder(checkout);

Integration Points

Mobile App Integration

  • Order Submission: Creates initial request via checkout-submit-order
  • Payment Processing: Triggers webhook validation
  • Status Updates: Real-time sync of request status

Clinical System Integration

  • Request Display: Shows pending requests on patient detail page
  • Prescription Creation: Links prescriptions to original requests
  • UI Refresh: Automatically updates when prescription dialog closes

Payment System Integration

  • Stripe Webhooks: Validates payment completion
  • Duplicate Prevention: Checks for existing processed requests
  • Payment Linking: Associates payments with requests

Error Handling

Duplicate Prevention

  • Webhook Retry Safety: Multiple webhook calls don’t create duplicates
  • Race Condition Protection: Handles concurrent prescription/payment processing
  • Data Integrity: Ensures one-to-one request-prescription relationship

Failed Requests

  • Payment Failures: Request remains in submitted state
  • Clinical Rejection: Request marked as rejected with reason
  • System Errors: Automatic retry logic with exponential backoff

Database Schema

patient_app_requests Table

CREATE TABLE patient_app_requests (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  patient_id UUID NOT NULL REFERENCES patients(id),
  request_number VARCHAR(20) UNIQUE NOT NULL,
  request_type VARCHAR(50) NOT NULL,
  status VARCHAR(50) NOT NULL DEFAULT 'submitted',
  clinical_decision VARCHAR(50) NOT NULL DEFAULT 'review_required',
  clinical_decision_made_at TIMESTAMPTZ,
  prescription_order_id UUID REFERENCES prescription_orders(id),
  stripe_payment_intent_id VARCHAR(255),
  created_at TIMESTAMPTZ DEFAULT NOW(),
  updated_at TIMESTAMPTZ DEFAULT NOW()
);

Key Indexes

  • stripe_payment_intent_id - For webhook duplicate prevention
  • patient_id - For patient request lookup
  • clinical_decision - For filtering by status

Monitoring and Logging

Key Metrics

  • Request Creation Rate: Requests per hour/day
  • Clinical Processing Time: Time from submission to decision
  • Duplicate Prevention Hits: Number of prevented duplicates
  • Payment Success Rate: Successful payment to request ratio

Logging Points

  • Request creation and updates
  • Prescription linking events
  • Duplicate prevention triggers
  • Payment webhook processing

Security Considerations

Data Protection

  • PHI Handling: All patient data encrypted at rest
  • Access Control: RLS policies restrict access to patient’s own data
  • Audit Trail: All changes logged for compliance

Payment Security

  • Webhook Validation: Stripe signature verification
  • Idempotency: Duplicate prevention prevents financial issues
  • PCI Compliance: No card data stored in application
All patient app requests follow a strict one-to-one relationship with prescriptions to ensure accurate clinical tracking and billing