I think you can understand this code by walking through an informal proof by induction that it works for trees of depth d.
For depth 1 you just have a single node and both Min-Value and Max-Value return the utility of this node.
For depth d > 1 consider Min-Value first. v starts off as infinity, so at the first call to Min(v, Max-Value(s)) v gets set to the utility of a child node, as computed by Max, because it is Max's turn after Min, and we know it is correct by induction because it is of depth d-1. (This call amounts to an assignment because v <= infinity), Later calls to Min(v, Max-Value(s)) in this routine end up computing the Min of Max-Value(s) over all children of the node passed in. So Min-Value ends up computing the minimum utility over all children of the node passed in, which is the value of that node to Min when it is Min's turn to move.
The argument for Max-Value is much the same as for Min-Value, so the induction tells you that, for trees of any depth, both Min-Value and Max-Value return to you the value of the node passed to them, when it is Max or Min's turn to move and make the choices associated with that node.
You could also show by induction that what this code does is equivalent to what you have described.
So the double recursion arises because it allows Max and Min to take alternate turns as you walk up the tree from the leaves, and v is a temporary which is used to work out the min or max value of all the child nodes of a tree.