Payments are the most sensitive part of any system where money moves. The regulator wants to know who paid what and when, the customer wants certainty that nothing extra leaves their account, and the operator must not lose or duplicate a single transaction. This article is for architects and development leads who design or sign off a payment interface.

Between 2017 and 2022, working through a partner company as part of the win2day development team at Austrian Lotteries, we designed and implemented payment transaction security: digital signatures on requests, key management and rotation, replay protection, and integration into the Java backend and the web, Android, iOS and Huawei apps. What follows is not a description of that implementation but a general summary of the rules we would apply today to any comparable system.

What HTTPS protects and what it does not

TLS 1.3 (RFC 8446) gives a connection three properties: the server is always authenticated (the client only optionally), data is visible only to the endpoints, and an attacker cannot modify it without detection. HTTPS therefore protects both the confidentiality and the integrity of the transfer. These are properties of the channel, though, not of the transaction. Protection ends wherever TLS is terminated, for example at a load balancer, a CDN or an API gateway. RFC 9421 states explicitly that behind such a gateway end-to-end integrity and authenticity between client and application cannot be guaranteed. TLS also says nothing about whether the sender may perform the operation or whether the request repeats an earlier one.

Take particular care with early data (0-RTT) in TLS 1.3: according to RFC 8446 it has no guarantee of non-replay between connections, and clients should only send data that is safe to replay. Payment calls do not qualify.

1. Sign the transaction, not just the connection

Every payment request should carry an application-level digital signature that the server verifies before writing anything. The signature stays with the request behind the gateway, can be stored and verified during an audit years later, and in a dispute is strong evidence of what the client sent, used alongside server records and the payment provider’s data, not instead of them. For HTTP there is a standard for this, HTTP Message Signatures (RFC 9421, February 2024).

SHA256withRSA, which we worked with, is in Java the RSASSA-PKCS1-v1_5 signature over a SHA-256 digest. RFC 8017 lists no known attacks against that scheme but requires RSASSA-PSS for new applications. ECDSA is an alternative with smaller keys. What matters is that the algorithm is part of a versioned contract and that the verifier accepts only the expected algorithm for a given key. Changing the algorithm then does not mean rewriting the system, which will help with the move to post-quantum signatures (standardised by NIST in 2024 in FIPS 204 and 205).

Mutual TLS (mTLS), where the client also proves itself with a certificate, is a good choice for the channel between two servers, for example with a payment provider or a game supplier, and OAuth can bind access tokens to it (RFC 8705). It authenticates and protects the connection, including the integrity of transmitted data, but leaves no separate signature attached to each message, so it does not make the signature unnecessary. The two layers complement each other: mTLS for the channel with a partner, the signature for the content of the transaction.

2. A precise contract: what is signed, in what order and encoding

Most signature bugs are not cryptographic. They arise because client and server assemble the signed content differently. If web, Android, iOS and Huawei each implement signing their own way, you end up with four security models and four sets of bugs. The answer is one written contract and a reference implementation with test vectors (input, expected signed string, verification result) that every client is checked against in automated tests. The contract defines:

  • What is signed: everything that changes the meaning of the operation (amount, currency, account or payee, operation type, request identifier, time, key identifier and contract version), plus the method and path, so that a signature for one operation cannot be reused for another.
  • Order: JSON does not guarantee key order, so either sign the exact bytes of the body together with a digest (the Content-Digest field from RFC 9530, which RFC 9421 also recommends) or use a defined canonicalisation such as the JSON Canonicalization Scheme (informational RFC 8785).
  • Encoding: UTF-8 and an unambiguous format for time and amounts (an integer in the smallest currency unit or a decimal string, not a floating-point number).
  • Processing: the server works with exactly what it verified. If it verifies one representation of the data and processes another (a different parser, a value from an unsigned header), the signature protects nothing.

3. Every request counts once: time, nonce and idempotency

A signed request can be captured and sent again, and the signature will still be valid. RFC 9421 says so explicitly and offers a unique value (nonce) and signature creation and expiry times as countermeasures. The server rejects requests outside a short validity window (with a margin for inaccurate clocks on mobile devices) and remembers nonces only for that window, so the store does not grow indefinitely. Checking and recording the nonce must be atomic, for example through a unique constraint in the database, otherwise two concurrent copies will both get through.

A more common problem is a double tap on a weak connection, or a client that resends the same payment with a fresh nonce after a dropped connection. Only an idempotency key solves that, because it identifies the business operation rather than the message. Under RFC 9110 POST is not idempotent, so the interface needs its own mechanism. The IETF is working on an Idempotency-Key header (still a draft, not an RFC, as of October 2026): a retry with the same key receives a response consistent with the original operation, the same key with different content is an error, and a concurrent request with a key still being processed is a conflict. Store the key and the result in the same database transaction as the posting.

4. The server’s private key never leaves the server

A common mistake we see in designs is a signing key inside the mobile app or in JavaScript. Anything in an app package or in browser code can be extracted, and the OWASP Mobile Application Security Testing Guide (MASTG) has dedicated tests for hard-coded keys. If the client signs at all, it signs only with its own key generated on the device: in Android Keystore, where it can be bound to secure hardware and cannot be exported, or in the Secure Enclave on Apple devices. The Secure Enclave only supports NIST P-256 elliptic curve keys, which has to be considered when choosing the algorithm for a shared contract. A device key proves “this installation”, not the user, so it does not replace authentication.

The server key belongs in a hardware security module (HSM) or a cloud key management service (KMS), where signing happens inside and the application never sees the key material. It does not belong in the repository or in configuration deployed with the application. Use a separate key per environment and purpose. PCI DSS requires service providers to document a cryptographic architecture that prevents the same keys being used in production and test.

Rotation is designed from the start. NIST SP 800-57 distinguishes the period during which a key may be used to sign from the period during which signatures it produced may still be verified. That leads to a zero-downtime procedure: each request carries a key identifier, the new public key is published in advance, verifiers accept both for a while (the overlap), then signing with the old key stops and its public part remains available for verifying the archive. Separately, revocation after a compromise has to be ready: immediate blocking of the key, issuing a new one and a procedure rehearsed in advance.

5. A third party’s result is verified, not accepted

A game result from an external studio moves money on the player’s account, and a confirmation from a payment provider changes the state of a payment. A partner call needs a verified origin (a signature or mTLS) and a timestamp. Then the content is checked: the transaction exists on our side, amount and currency match what we sent, the state transition is permitted (a declined payment does not turn into a successful one) and the game result matches the rules and limits. Duplicate notifications are common, so processing must be idempotent. If the partner offers a status query, fetch the decisive confirmation yourself. A discrepancy is stopped before posting, not after. Trust in a partner is not a security mechanism.

PSD2 applies a similar principle. For strong customer authentication in remote payments, Delegated Regulation (EU) 2018/389 requires dynamic linking, meaning an authentication code tied to the amount and payee and invalidated by any change to either. That obligation falls on payment service providers, not on the merchant. The merchant receives a result from the provider, and it is this result that has to be verified.

6. The audit trail is a product, and sensitive data stays out of it

The regulator wants to know whether you can quickly reconstruct a specific transaction, and logs alone are not enough for that. A record therefore contains the request identifier, the idempotency key, the key identifier, the signature verification result, the server time and the state before and after the operation. Logs should be protected against modification and deletion, with access recorded, as OWASP also recommends. Such a record is also the best support tool for handling complaints.

OWASP lists access tokens, passwords, keys, and payment card and bank account data among the things that should not be logged directly. For card data PCI DSS is binding (currently v4.0.1, after version 4.0 was retired at the end of 2024). A card number is masked on display to at most the BIN and the last four digits, and when stored it must be rendered unreadable by truncation, tokenisation, strong encryption or a hash of the entire number. Since 31 March 2025 that hash must be keyed (for example HMAC), because a plain hash of a low-entropy number can be recovered by brute force. The same logic applies to an IBAN, even though it is outside PCI DSS.

Design checklist

  • We know where TLS is terminated, and payment calls do not accept 0-RTT.
  • A versioned signing contract defines fields, order, encoding and algorithm. Every platform is tested against shared test vectors and the server processes exactly what it verified.
  • Every request has a time and a nonce with an atomic replay check, and every payment operation has an idempotency key stored together with the posting.
  • The server key sits in an HSM or KMS, separated by environment and purpose, and no client contains a server key.
  • Rotation with overlap and revocation after a compromise are documented and rehearsed.
  • For every third-party result we verify origin, time, that the transaction exists, amount, currency and state transition.
  • A transaction can be reconstructed from the audit trail, even though the logs hold no tokens, passwords, keys or readable card and account numbers.

When not to do all this, and what follows

Not every system needs its own signing of client requests. If an e-shop redirects the customer to the payment provider’s hosted page and never handles card data itself, the provider carries most of the burden. You still have to verify its notifications and take care of idempotency and the audit trail. A custom signing layer pays off where the client directly moves money on an account that you keep: gaming and betting accounts, wallets, internal payment orders.

Payment security is part of the architecture, not a module added before launch. The signing contract, idempotency and key rotation are designed together with the data model, and the budget should allow for a reference implementation, test vectors and a key revocation drill. Idempotency and the audit trail also pay off beyond security: fewer duplicate payments and faster complaint handling. The core of these rules comes from online gambling, but it applies just as well to an e-shop or a fintech.