Hey Kevin, long reply ahead… But I’m gonna share some screenshots of the app we’re building currently so you’ll have a visual rep in your head. The design is still very raw because we’re ironing out the logic first haha.
Recording payment/credits to the balance of the user’s account:
For us, when the Stripe payment is successful, we’ll add the amount of credits they bought to their existing balance. Because there’s a validity date too, we have to add to the user’s existing credits’ expiry too.
Apart from that, we’ll create a payment record in a separate database that has a relationship with the users, so it appears in their transaction history:
And there must be some feedback to the user that the payment went through, so it’s good to link them to a payment successful screen:
For the second part of your question on how to restrict users from using only credits, here’s how we’re doing it. When a user clicks on the “confirm booking” button, a credit will be deducted from the account:
But it must only happen “sometimes”, ie. when the user has more than 0 credits:
For your case, if there’s a specific cost to the product, you can use the magic text. Eg. You’ll only allow users to purchase/checkout the product if their balance is greater than or equal to the product cost.
If they don’t have enough balance in the account, they’ll be sent to a screen that prompts them to top-up. In our case, it’s insufficient class credits and they’ll have to purchase a class pack before continuing:
Hope this makes sense and helps you with figuring out the logic. As mentioned, when our app is ready to test, I’d be happy to send you the preview to help you with your own app’s logic and user flow (if it’s useful). In return, you can send any feedback or bug reports (hopefully, minimal) 
Cheers mate
UPDATE: I used a list for the “Book Class” page, which is for the user to confirm the booking. That shouldn’t be the case. It should be a group of elements by itself, instead of a list, to prevent more than 1 record appearing on this page.