Tradtionally, escrow payments are handled by banks or lawyers. A buyer wishing to buy an expensive item would deposit funds with an escrow manager. The manager would hold the funds as a security measure giving the seller confidence that the funds were there so they could then give the item to the buyer.

Today, the role of an escrow manager can be replaced by a smart contract, an unchangeable piece of code that lives on the blockchain. Any rules surrounding the exchange cannot be altered once on the blockchain. This gives both buyer and seller confidence that they won't be cheated during the exchange.

Let's code a very simple contract now.

First we'll create a contract called Escrow.

pragma solidity ^0.6.0;

contract Escrow {
    // Do stuff
}

Next we'll define an "enum" type with three states "Awaiting Payment", "Awaiting Delivery" and "Complete"

We'll also create a variable of that new type as well as variables for the buyer and seller. The seller must be defined as payable because they will actually receive Ether in the end.

pragma solidity ^0.6.0;

contract Escrow {
    //...
    enum State { AWAITING_PAYMENT, AWAITING_DELIVERY, COMPLETE }

    State public currState;

    address public buyer;
    address payable public seller;
    //....
}

Next we'll create a modifier to ensure only the buyer can call certain methods.

pragma solidity ^0.6.0;

contract Escrow {
   //...
  modifier onlyBuyer() {
    require(msg.sender == buyer, "Only buyer can call this method");
     _;
  }
  //...
}

In our constructor we'll initialize the buyer and seller addresses.

pragma solidity ^0.6.0;

contract Escrow {
   //...
  constructor(address _buyer, address payable _seller) public {
       buyer = _buyer;
       seller = _seller;
  }
  //...
}

Then we'll create a deposit method. It's payable so it receives ether. Only the buyer can call it and it can only be called when there are no funds already in the escrow. Once called we set the current state to "Awaiting Delivery".

pragma solidity ^0.6.0;

contract Escrow {
  //...
  function deposit() onlyBuyer external payable {
    require(currState == State.AWAITING_PAYMENT, "Already paid");
    currState = State.AWAITING_DELIVERY;
  }
  //...
}

The second and final method allows the buyer to confirm that they actually received the item. Once again, only the buyer can call it. It can only be called in the "Awaiting Delivery" state. Once called it transfers the funds from the contract to the seller and marks the escrow complete.

pragma solidity ^0.6.0;

contract Escrow {
  //...
  function confirmDelivery() onlyBuyer external {
    require(currState == State.AWAITING_DELIVERY, "Cannot confirm delivery");
    seller.transfer(address(this).balance);
    currState = State.COMPLETE;
  }
  //...
}

The completed contract would look something like this.

pragma solidity ^0.6.0;

contract Escrow {
    enum State { AWAITING_PAYMENT, AWAITING_DELIVERY, COMPLETE }

    State public currState;

    address public buyer;
    address payable public seller;

    modifier onlyBuyer() {
        require(msg.sender == buyer, "Only buyer can call this method");
        _;
    }

    constructor(address _buyer, address payable _seller) public {
        buyer = _buyer;
        seller = _seller;
    }

    function deposit() onlyBuyer external payable {
        require(currState == State.AWAITING_PAYMENT, "Already paid");
        currState = State.AWAITING_DELIVERY;
    }

    function confirmDelivery() onlyBuyer external {
        require(currState == State.AWAITING_DELIVERY, "Cannot confirm delivery");
        seller.transfer(address(this).balance);
        currState = State.COMPLETE;
    }
}

This is a very simple example and there is a lot more you could and probably SHOULD do.