user934: Below is my attempt to implement the homework assignment.
Is the way I call the endpoint twice in payTrace with minor differences appropriate?
Since payTest2 would not execute properly without modifying the original payContract, is the way I handle the contract error appropriate?
payContract :: Contract () PaySchema Text ()
payContract = do
    pp <- endpoint @"pay"
    let tx = mustPayToPubKey (ppRecipient pp) $ lovelaceValueOf $ ppLovelace pp
    Contract.handleError (\err -> Contract.logError $ "Caught error: " ++ unpack err) $ (void $ submitTx tx)
    payContract

-- A trace that invokes the pay endpoint of payContract on Wallet 1 twice, each time with Wallet 2 as
-- recipient, but with amounts given by the two arguments. There should be a delay of one slot
-- after each endpoint call.
payTrace :: Integer -> Integer -> EmulatorTrace ()
payTrace x y = do
    h1 <- activateContractWallet (Wallet 1) payContract
    callEndpoint @"pay" h1 $ PayParams
        { ppRecipient = pubKeyHash $ walletPubKey $ Wallet 2
        , ppLovelace  = x
        }
    void $ Emulator.waitNSlots 1
    xs <- observableState h1
    Extras.logInfo $ show xs

    callEndpoint @"pay" h1 $ PayParams
        { ppRecipient = pubKeyHash $ walletPubKey $ Wallet 2
        , ppLovelace  = y
        }
    void $ Emulator.waitNSlots 1
    ys <- observableState h1
    Extras.logInfo $ show ys

I retracted the surrounding code that has not been modified.
The start code can be found here.

user2107: I did a similar solution as @Mankeh - I added a second function to catch the error on the first transaction. Theirs is cleaner than mine.
And after checking the solution, I see that I got the order of submitTx and Contract.handleError mixed up in the payContract function. If I had put them in the solution's order, then the first error would be caught and the second 'wrapper' function wouldn't have been needed.
-- my solution
payContract :: Contract () PaySchema Text ()
payContract = do
    pp <- endpoint @"pay"
    let tx = mustPayToPubKey (ppRecipient pp) $ lovelaceValueOf $ ppLovelace pp
    void $ submitTx tx
    Contract.handleError (\err -> Contract.logError $ "Caught error inner1: " ++ unpack err) 
        payContract
    

payContractErrHandler :: Contract () PaySchema Text ()
payContractErrHandler = do
    Contract.handleError (\err -> Contract.logError $ "Caught error: " ++ unpack err)
        payContract
    payContractErrHandler


user249: The exception handler does not cover the first payment contract run. I've created a separate function for this that handles this error:
payContract' :: Contract () PaySchema Void ()
payContract' = Contract.handleError
    (\err -> Contract.logError $ "Caught error: " ++ unpack err)
    payContract

Then of course you have to call the payContract' in your emulator tracer as follows:
h1 <- activateContractWallet (Wallet 1) payContract'

You can also combine everything together like this:
payContract :: Contract () PaySchema Text ()
payContract = Contract.handleError
    (\err -> Contract.logError $ "Caught error: " ++ unpack err) $ do
    pp <- endpoint @"pay"
    let tx = mustPayToPubKey (ppRecipient pp) $ lovelaceValueOf $ ppLovelace pp
    void $ submitTx tx
    payContract


934: <p>Below is my attempt to implement the homework assignment.</p>
<p>Is the way I call the endpoint twice in <code>payTrace</code> with minor differences appropriate?</p>
<p>Since <code>payTest2</code> would not execute properly without modifying the original <code>payContract</code>, is the way I handle the contract error appropriate?</p>
<pre><code>payContract :: Contract () PaySchema Text ()
payContract = do
    pp &lt;- endpoint @&quot;pay&quot;
    let tx = mustPayToPubKey (ppRecipient pp) $ lovelaceValueOf $ ppLovelace pp
    Contract.handleError (\err -&gt; Contract.logError $ &quot;Caught error: &quot; ++ unpack err) $ (void $ submitTx tx)
    payContract

-- A trace that invokes the pay endpoint of payContract on Wallet 1 twice, each time with Wallet 2 as
-- recipient, but with amounts given by the two arguments. There should be a delay of one slot
-- after each endpoint call.
payTrace :: Integer -&gt; Integer -&gt; EmulatorTrace ()
payTrace x y = do
    h1 &lt;- activateContractWallet (Wallet 1) payContract
    callEndpoint @&quot;pay&quot; h1 $ PayParams
        { ppRecipient = pubKeyHash $ walletPubKey $ Wallet 2
        , ppLovelace  = x
        }
    void $ Emulator.waitNSlots 1
    xs &lt;- observableState h1
    Extras.logInfo $ show xs

    callEndpoint @&quot;pay&quot; h1 $ PayParams
        { ppRecipient = pubKeyHash $ walletPubKey $ Wallet 2
        , ppLovelace  = y
        }
    void $ Emulator.waitNSlots 1
    ys &lt;- observableState h1
    Extras.logInfo $ show ys
</code></pre>
<p>I retracted the surrounding code that has not been modified.
The start code can be found <a href="https://raw.githubusercontent.com/input-output-hk/plutus-pioneer-program/main/code/week04/src/Week04/Homework.hs" rel="noreferrer">here</a>.</p>
 2107: <p>I did a similar solution as @Mankeh - I added a second function to catch the error on the first transaction. Theirs is cleaner than mine.</p>
<p>And after checking the solution, I see that I got the order of submitTx and Contract.handleError mixed up in the payContract function. If I had put them in the solution's order, then the first error would be caught and the second 'wrapper' function wouldn't have been needed.</p>
<pre><code>-- my solution
payContract :: Contract () PaySchema Text ()
payContract = do
    pp &lt;- endpoint @&quot;pay&quot;
    let tx = mustPayToPubKey (ppRecipient pp) $ lovelaceValueOf $ ppLovelace pp
    void $ submitTx tx
    Contract.handleError (\err -&gt; Contract.logError $ &quot;Caught error inner1: &quot; ++ unpack err) 
        payContract
    

payContractErrHandler :: Contract () PaySchema Text ()
payContractErrHandler = do
    Contract.handleError (\err -&gt; Contract.logError $ &quot;Caught error: &quot; ++ unpack err)
        payContract
    payContractErrHandler
</code></pre>
 249: <p>The exception handler does not cover the first payment contract run. I've created a separate function for this that handles this error:</p>
<pre><code>payContract' :: Contract () PaySchema Void ()
payContract' = Contract.handleError
    (\err -&gt; Contract.logError $ &quot;Caught error: &quot; ++ unpack err)
    payContract
</code></pre>
<p>Then of course you have to call the <code>payContract'</code> in your emulator tracer as follows:</p>
<pre><code>h1 &lt;- activateContractWallet (Wallet 1) payContract'
</code></pre>
<p>You can also combine everything together like this:</p>
<pre><code>payContract :: Contract () PaySchema Text ()
payContract = Contract.handleError
    (\err -&gt; Contract.logError $ &quot;Caught error: &quot; ++ unpack err) $ do
    pp &lt;- endpoint @&quot;pay&quot;
    let tx = mustPayToPubKey (ppRecipient pp) $ lovelaceValueOf $ ppLovelace pp
    void $ submitTx tx
    payContract
</code></pre>
