Just-In-Time Payment Contract

What it does:
Automatically releases payments to suppliers or partners when predefined conditions, milestones, or delivery confirmations are met, supporting just-in-time supply chains.

Why it matters:
Optimizes cash flow, reduces idle capital, ensures timely supplier payments, and enforces contractual obligations transparently on-chain.

How it works:

  • Buyers or manufacturers deposit funds into the contract

  • Milestones or delivery events are defined in the smart contract (time-based, sensor-based, or off-chain verified)

  • Smart contract automatically releases payments when milestones are achieved

  • Supports partial payments for multi-stage deliveries or production cycles

  • Historical payment events are stored for auditing and reconciliation

  • Integrates with Supply Chain Traceability, Cold Chain Monitoring, and Inventory Management

  • Public dashboards provide visibility into milestone progress, payments, and pending actions

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

import "@openzeppelin/contracts/access/Ownable.sol";

/**
 * @title JustInTimePayment
 * @author Nam
 * @notice Automates payments based on delivery or milestone fulfillment in supply chains
 */
contract JustInTimePayment is Ownable {

    struct Milestone {
        string description;
        uint256 amount;
        bool completed;
        bool paid;
    }

    struct PaymentContract {
        address payable buyer;
        address payable supplier;
        Milestone[] milestones;
        uint256 totalAmount;
    }

    mapping(uint256 => PaymentContract) public paymentContracts;
    uint256 public contractCount;

    mapping(address => bool) public authorizedVerifiers;

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

    event ContractCreated(uint256 indexed contractId, address buyer, address supplier, uint256 totalAmount);
    event MilestoneCompleted(uint256 indexed contractId, uint256 milestoneIndex);
    event PaymentReleased(uint256 indexed contractId, uint256 milestoneIndex, uint256 amount);
    event VerifierApproved(address verifier);

    // -------------------- VERIFIER MANAGEMENT --------------------

    function approveVerifier(address _verifier) external onlyOwner {
        authorizedVerifiers[_verifier] = true;
        emit VerifierApproved(_verifier);
    }

    function revokeVerifier(address _verifier) external onlyOwner {
        authorizedVerifiers[_verifier] = false;
    }

    modifier onlyVerifier() {
        require(authorizedVerifiers[msg.sender], "Not an authorized verifier");
        _;
    }

    // -------------------- CONTRACT CREATION --------------------

    function createContract(address payable _supplier, string[] calldata _milestoneDescriptions, uint256[] calldata _milestoneAmounts) external payable {
        require(_milestoneDescriptions.length == _milestoneAmounts.length, "Mismatched milestones and amounts");

        uint256 total = 0;
        for (uint256 i = 0; i < _milestoneAmounts.length; i++) {
            total += _milestoneAmounts[i];
        }
        require(msg.value == total, "Incorrect payment amount");

        contractCount += 1;
        PaymentContract storage c = paymentContracts[contractCount];
        c.buyer = payable(msg.sender);
        c.supplier = _supplier;
        c.totalAmount = total;

        for (uint256 i = 0; i < _milestoneDescriptions.length; i++) {
            c.milestones.push(Milestone({
                description: _milestoneDescriptions[i],
                amount: _milestoneAmounts[i],
                completed: false,
                paid: false
            }));
        }

        emit ContractCreated(contractCount, msg.sender, _supplier, total);
    }

    // -------------------- MILESTONE MANAGEMENT --------------------

    function completeMilestone(uint256 _contractId, uint256 _milestoneIndex) external onlyVerifier {
        PaymentContract storage c = paymentContracts[_contractId];
        Milestone storage m = c.milestones[_milestoneIndex];
        require(!m.completed, "Milestone already completed");

        m.completed = true;
        emit MilestoneCompleted(_contractId, _milestoneIndex);

        // Release payment automatically
        c.supplier.transfer(m.amount);
        m.paid = true;
        emit PaymentReleased(_contractId, _milestoneIndex, m.amount);
    }

    // -------------------- VIEW FUNCTIONS --------------------

    function getMilestones(uint256 _contractId) external view returns (Milestone[] memory) {
        return paymentContracts[_contractId].milestones;
    }

    function getTotalPaid(uint256 _contractId) external view returns (uint256 totalPaid) {
        PaymentContract storage c = paymentContracts[_contractId];
        for (uint256 i = 0; i < c.milestones.length; i++) {
            if (c.milestones[i].paid) {
                totalPaid += c.milestones[i].amount;
            }
        }
    }

    // -------------------- FUNDING --------------------

    receive() external payable {}
}