A payment address is just the hash of the payment pubkey (and optionally the stake pubkey if that addresses funds will delegate) with some prefixed bytes that indicate what type of address it is. The payment secret keys can either be randomly generated (such as how
cardano-cli address key-gen works), or can be derived from a wallet root private key (which usually has an associated mnemonic to generate the root private key).
If your app doesn't need to use a full wallet, you can generate random bytes, convert that to a pubkey, get the payment key hash and assemble an address.
If your app does use a wallet, you would generate keys under the derivation path:
1852H/1815H/0H/0/$i using cardano-addresses, and then the rest of the steps are exactly the same, but using the bytes derived rather than randomly generated.
If you go the random generation route, please ensure you have many backups of every secret key you generate, otherwise if the keys are lost, those funds are completely unspendable!