> For the complete documentation index, see [llms.txt](https://dev.indodana.id/indodana-paylater/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://dev.indodana.id/indodana-paylater/go-live-preparation/security-best-practices.md).

# Security Best Practices

How to secure your Indodana integration

These controls apply to every Indodana integration (Direct API, QuickPay, PoS, QRIS). They protect the integration itself — how you exchange requests and callbacks with Indodana and how you store credentials. QuickPay adds a few token-specific controls on top; see [QuickPay Security](/indodana-paylater/api-reference/quickpay-checkout-integration.md#security-best-practices).

## Verify every inbound callback

Indodana calls your endpoints for events such as transaction approval (`approvedNotificationUrl`), refunds, and — for QuickPay — the user token and token revoke callbacks. Always confirm a callback genuinely originates from Indodana before acting on it; otherwise a forged request could trigger a fake "approved" transaction or register a fake token.

Validate the signature in the `Authorization` header of the incoming request by recomputing `HMAC-SHA256({api_key}:{nonce}, {secret})` and comparing it to the signature Indodana sent. See the worked example in [Handling Transaction Confirmation Notification](/indodana-paylater/integration/direct-api.md#handling-transaction-confirmation-notification).

{% hint style="warning" %}
Reject the callback if the signature does not match, if the `nonce` is outside an acceptable freshness window (replay protection), or if the source IP is not on the Indodana allowlist.
{% endhint %}

## Do not use SSL / certificate pinning

{% hint style="danger" %}
Do **not** pin Indodana's SSL/TLS certificates (certificate or public-key pinning) in your servers or mobile apps.
{% endhint %}

Indodana rotates its certificates and serves its endpoints through load balancers / CDN, so the certificate presented for a host can change at any time without notice. If you pin, your integration will suddenly fail to connect the next time a certificate rotates — with no code change on your side.

Instead, rely on standard certificate validation against the public CA trust store (verify the hostname and a valid CA chain). That already provides the transport security you need, and it keeps working across certificate rotations.

## Protect your secret key

* Keep `MERCHANT_SECRET_KEY` server-side only — never in client-side code, mobile apps, source control, or logs.
* Support key rotation so a suspected leak can be remediated without downtime.

## Transport and request hygiene

* Serve all callback endpoints over HTTPS (TLS 1.2 or higher); `http://` callback URLs are rejected.
* Whitelist Indodana's IPs for inbound callbacks — see [Whitelist IP](/indodana-paylater/go-live-preparation/whitelist-ip.md).
* Make callback handling idempotent so duplicate deliveries (for example a repeated approval or revoke) are safe.
