A regular transaction is just a collection of inputs (coins spent) and outputs (coins produced):
payload: {
inputs: [...],
outputs: [...]
}
All of that is public, and everybody sees who spent what and to whom. Also, everybody can validate the transaction.
Now, Alice and Bod don't want to show their transactions to the entire world, and Alice hashes the above payload and stores only the hash on the public database. At the same time, Alice sends the payload itself directly to Bob. Along with payload hash, she also stores the spend proof, which is a hash of the input. So, this is what she posts to the public database:
private_transaction: {
payload_hash: ".....",
spend_proof: "......"
}
Both hashes posted to the public database are obviously meaningless for all third parties, they can learn nothing from Alice's transaction except the fact that she sent _some_ transaction.
By making the spend proof public, we enable everybody to verify that the same input is not spent twice, as the same input will produce the same spend proof.
Alice creates a txn that has her 10(could be hidden) input and a 10 output to Bob.
Alice generates a random number, that she will need to share with Bob, along with ALL the previous proofs in the chain(if her 10 is hidden), to decode the spend proofs.
Previous proofs (spend proofs) are already public, they don't need to be sent directly. What is sent directly from Alice to Bob is previous payloads (plaintext inputs and outputs), which allow Bob to verify that the corresponding payload hashes and spend proofs do exist in the public database.