Integration security
Collects the practices for protecting the token, the logs, callback delivery, and key validation in DICT.
The Pix integration handles real money. Every layer must have its own line of defense.
Token storage
Bearer token is PayZu's only credential. Whoever has it can move the balance.
| Where to store | OK? |
|---|---|
| Google Secret Manager | Yes |
| AWS Secrets Manager | Yes |
| HashiCorp Vault | Yes |
| Environment variable in CI | Yes |
.env in production | No |
| Hardcoded in code | No |
localStorage / sessionStorage | No |
| Front-end (Web, Mobile) | Never |
Never expose the token on the front-end. Every PayZu call must go through your backend, which injects the Authorization on the server.
Log masking
Configure your logger to mask the Authorization header and sensitive payload fields (payerDocument, pixKey, etc).
import pino from 'pino';
const logger = pino({
redact: {
paths: [
'req.headers.authorization',
'res.headers.authorization',
'*.payerDocument',
'*.pixKey',
],
censor: '[REDACTED]',
},
});import logging, re
class RedactFilter(logging.Filter):
def filter(self, record):
if isinstance(record.msg, str):
record.msg = re.sub(r'Bearer\s+[A-Za-z0-9._\-]+', 'Bearer [REDACTED]', record.msg)
return True
logging.getLogger().addFilter(RedactFilter())logger, _ := zap.NewProduction()
defer logger.Sync()
logger.Info("payzu call",
zap.String("url", url),
zap.String("authorization", "[REDACTED]"),
)Token rotation
| When to rotate | Action |
|---|---|
| Someone with access leaves the company | Immediate |
| Suspected leak (accidental commit, public log) | Immediate + audit |
| Preventive rotation | Quarterly or semi-annually |
| After pen-test | Immediate if exposed during the test |
Request the rotation from PayZu support. Have a planned rollover (two active tokens for a window) so you don't bring production down.
Webhook endpoint protection
Only accept callbacks from PayZu's official IP. Request the current IP from support.
location /webhooks/payzu {
allow 35.199.0.0/16;
deny all;
proxy_pass http://backend;
}Create a WAF rule: (http.request.uri.path eq "/webhooks/payzu" and ip.src ne <IP_PAYZU>) → Block.
const PAYZU_IPS = (process.env.PAYZU_WEBHOOK_IPS ?? '').split(',');
app.post('/webhooks/payzu', (req, res, next) => {
const ip = req.ip;
if (!PAYZU_IPS.includes(ip)) return res.status(403).end();
next();
});DICT validation before paying
On Pix payments by key, validate the holder via DICT before transferring. It detects bank/CPF changes and prevents paying the wrong recipient.
async function safeWithdraw(pixKey: string, pixType: string, expectedName: string, amount: number) {
const url = new URL('https://api.payzu.processamento.com/v1/pix/key');
url.searchParams.set('pixKey', pixKey);
const dict = await fetch(url, {
headers: {
Authorization: `Bearer ${process.env.PAYZU_TOKEN}`,
'Content-Type': 'application/json',
},
}).then(r => r.json());
if (dict.name?.toLowerCase().trim() !== expectedName.toLowerCase().trim()) {
throw new Error(`Holder mismatch: expected ${expectedName}, found ${dict.name}`);
}
return createWithdraw({ pixKey, pixType, amount });
}Details at DICT Lookup.
2FA on sensitive operations
Consider additional 2FA in your application (not at PayZu) before:
- Pix payment above a high limit.
- Creating/changing a payment Pix key.
- Admin login with permission to move balance.
PayZu already validates on the backend, but application-level 2FA reduces the blast radius of a compromised session. Details at 2FA.
Principle of least privilege
| Setup | Recommendation |
|---|---|
| Single token for prod + dev | Separate: sandbox token vs prod token. |
| Same token shared across services | One token per service/team (to audit usage). |
| Devs with access to the prod token | Only infra/SRE should have it; devs use sandbox. |
Common pitfalls
| Pitfall | Symptom |
|---|---|
Token in a versioned .env | Leaks on the first wrong git push --force |
Logger prints the Authorization header | Token leaks in any log capture |
| Webhook open to any IP | Forged payloads may be accepted |
| Paying without DICT on Pix payment by key | May pay the wrong recipient |
| Same token in sandbox and production | An environment mistake becomes a prod incident |