Telemedicine Payment Escrow

What it does:
Holds patient payments in escrow for telemedicine sessions and automatically releases funds to healthcare providers once the consultation is completed.

Why it matters:
Protects both patients and providers by ensuring fair payment, reducing disputes, and enforcing transparent, milestone-based healthcare service delivery.

How it works:

  • Telemedicine session terms are defined at deployment

  • Patient deposits consultation fee into escrow

  • Provider conducts the remote medical session

  • Provider marks session as completed

  • Patient confirms service completion or timeout triggers release

  • Escrow automatically pays the provider or refunds if conditions fail

      // SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

/**
 * @title TelemedicinePaymentEscrow
 * @author Nam
 * @notice Escrow contract for telemedicine consultation payments
 */
contract TelemedicinePaymentEscrow {

    // -------------------- ROLES --------------------

    address public patient;
    address public provider;

    // -------------------- PAYMENT TERMS --------------------

    uint256 public consultationFee;
    uint256 public sessionDeadline; // timestamp
    uint256 public confirmationTimeout; // seconds

    // -------------------- STATE --------------------

    bool public funded;
    bool public sessionCompleted;
    bool public patientConfirmed;
    bool public paid;

    // -------------------- EVENTS --------------------

    event Funded(address indexed patient, uint256 amount);
    event SessionCompleted(address indexed provider);
    event PatientConfirmed(address indexed patient);
    event ProviderPaid(address indexed provider, uint256 amount);
    event Refunded(address indexed patient, uint256 amount);

    // -------------------- MODIFIERS --------------------

    modifier onlyPatient() {
        require(msg.sender == patient, "Not patient");
        _;
    }

    modifier onlyProvider() {
        require(msg.sender == provider, "Not provider");
        _;
    }

    // -------------------- CONSTRUCTOR --------------------

    constructor(
        address _patient,
        address _provider,
        uint256 _consultationFee,
        uint256 _sessionDuration,
        uint256 _confirmationTimeout
    ) {
        require(_patient != address(0), "Invalid patient");
        require(_provider != address(0), "Invalid provider");

        patient = _patient;
        provider = _provider;
        consultationFee = _consultationFee;
        sessionDeadline = block.timestamp + _sessionDuration;
        confirmationTimeout = _confirmationTimeout;
    }

    // -------------------- ESCROW FLOW --------------------

    /**
     * @notice Patient funds the escrow
     */
    function fund() external payable onlyPatient {
        require(!funded, "Already funded");
        require(msg.value == consultationFee, "Incorrect fee");

        funded = true;
        emit Funded(msg.sender, msg.value);
    }

    /**
     * @notice Provider marks session as completed
     */
    function completeSession() external onlyProvider {
        require(funded, "Not funded");
        require(!sessionCompleted, "Already completed");
        require(block.timestamp = sessionDeadline + confirmationTimeout,
            "Confirmation window open"
        );
        require(!paid, "Already settled");

        _releasePayment();
    }

    // -------------------- INTERNAL --------------------

    function _releasePayment() internal {
        require(!paid, "Already paid");

        paid = true;
        payable(provider).transfer(consultationFee);
        emit ProviderPaid(provider, consultationFee);
    }

    // -------------------- REFUND LOGIC --------------------

    /**
     * @notice Refund patient if session not completed
     */
    function refund() external onlyPatient {
        require(funded, "Not funded");
        require(!sessionCompleted, "Session completed");
        require(block.timestamp > sessionDeadline, "Session still valid");
        require(!paid, "Already settled");

        paid = true;
        payable(patient).transfer(consultationFee);
        emit Refunded(patient, consultationFee);
    }
}