tion path. This allows the main payment service to fail fast while the recovery module manages the out-of-band process.
Implementation Strategy
The following TypeScript implementation demonstrates a robust approach to handling BNI Direct authentication failures and preparing for recovery. This code assumes a wrapper architecture around the BNI Direct SDK or HTTP client.
import { SecureVault } from './security/vault';
import { RecoveryNotifier } from './ops/recovery-notifier';
import { BNIErrorCodes } from './bni/constants';
interface BNIRecoveryArtifacts {
companyId: string;
userId: string;
accountNumber: string;
}
interface BNIClientConfig {
recoveryChannels: {
whatsappNumbers: string[];
branchContact: string;
};
}
export class BniDirectRecoveryManager {
private vault: SecureVault;
private notifier: RecoveryNotifier;
private config: BNIClientConfig;
constructor(vault: SecureVault, notifier: RecoveryNotifier, config: BNIClientConfig) {
this.vault = vault;
this.notifier = notifier;
this.config = config;
}
/**
* Analyzes authentication errors to determine if a lockout has occurred.
* Returns a recovery payload if lockout is detected, otherwise null.
*/
public async analyzeAuthError(error: Error): Promise<BNIRecoveryArtifacts | null> {
// Lockout errors in BNI Direct typically carry specific codes or messages.
// We check against known lockout indicators.
const isLockout = this.isLockoutError(error);
if (!isLockout) {
return null;
}
// Aggressively stop retries. Lockouts require manual intervention.
console.error('BNI Direct lockout detected. Halting transaction retries.');
// Retrieve verification artifacts securely.
// Never log sensitive IDs in plain text during recovery prep.
const artifacts = await this.vault.getRecoveryArtifacts();
if (!this.validateArtifacts(artifacts)) {
throw new Error('Recovery artifacts incomplete. Cannot initiate unblock.');
}
return artifacts;
}
/**
* Initiates the recovery workflow by notifying operations with a structured payload.
* This does not contact the bank directly; it triggers the human process.
*/
public async triggerRecoveryWorkflow(artifacts: BNIRecoveryArtifacts): Promise<void> {
const recoveryPayload = {
type: 'BNI_DIRECT_LOCKOUT',
severity: 'HIGH',
artifacts: {
companyId: artifacts.companyId,
userId: artifacts.userId,
accountNumber: artifacts.accountNumber,
},
recommendedAction: 'CONTACT_WHATSAPP',
contactDetails: this.config.recoveryChannels.whatsappNumbers,
timestamp: new Date().toISOString(),
};
// Send to ops channel (e.g., Slack, PagerDuty, Email)
await this.notifier.sendAlert(recoveryPayload);
// Audit trail for compliance
await this.vault.logRecoveryAttempt(recoveryPayload);
}
private isLockoutError(error: Error): boolean {
// Implementation depends on specific BNI Direct error responses.
// Common patterns include HTTP 403 with specific sub-codes or
// messages indicating "Account Blocked" or "Token Locked".
const lockoutIndicators = [
BNIErrorCodes.ACCOUNT_LOCKED,
BNIErrorCodes.TOKEN_BLOCKED,
'Access Denied: Account Blocked',
];
return lockoutIndicators.some(indicator =>
error.message.includes(indicator) ||
(error as any).code === indicator
);
}
private validateArtifacts(artifacts: Partial<BNIRecoveryArtifacts>): boolean {
return !!(
artifacts.companyId &&
artifacts.userId &&
artifacts.accountNumber
);
}
}
Rationale
analyzeAuthError: This method centralizes error classification. By checking against BNIErrorCodes, you avoid parsing brittle string messages. If a lockout is detected, the method returns the artifacts needed for recovery, signaling to the caller that retries are futile.
triggerRecoveryWorkflow: This method decouples the recovery action from the code. It generates a structured payload containing the exact data required by BNI. This payload is sent to operations, ensuring the human responder has all necessary information (Company ID, User ID, Account Number) without back-and-forth communication.
- Secure Vault Usage: Verification data is retrieved from a vault. This prevents credential leakage in logs and ensures that the recovery process uses the most current, authorized identifiers.
- Audit Logging:
logRecoveryAttempt ensures compliance. Banking integrations require audit trails for all access restoration events.
Pitfall Guide
Production experience with BNI Direct integrations reveals recurring mistakes that increase downtime and security risk. Avoid these pitfalls to maintain system resilience.
-
Pitfall: Automated Retry on Lockout Errors
- Explanation: Developers often configure exponential backoff for all authentication errors. When a lockout occurs, retries are ignored by the bank and may extend the lockout duration or trigger additional security alerts.
- Fix: Implement error classification. Immediately halt retries for lockout codes and switch to the recovery workflow.
-
Pitfall: Hardcoding Recovery Contact Numbers
- Explanation: Embedding WhatsApp numbers or branch details directly in source code makes updates difficult and risks exposing contact info in version control.
- Fix: Store recovery channels in configuration management or a secure vault. Update them via deployment pipelines, not code commits.
-
Pitfall: Incomplete Verification Artifacts
- Explanation: Initiating recovery without all three required artifacts (Company ID, User ID, Account Number) leads to rejection by the bank operator. This adds significant latency as the ops team must hunt for missing data.
- Fix: Validate artifacts before triggering recovery. The system should refuse to send a recovery alert if any artifact is missing, forcing a fix in the vault configuration.
-
Pitfall: Confusing User ID with Company ID
- Explanation: BNI Direct distinguishes between the corporate entity (Company ID) and the individual user (User ID). Providing the wrong ID type during verification can delay the process or cause the bank to unblock the wrong entity.
- Fix: Use distinct fields in your data models and verification payloads. Ensure the ops runbook clearly defines the difference.
-
Pitfall: Treating WhatsApp as an API Endpoint
- Explanation: Some teams attempt to send recovery requests via WhatsApp Business API or automated bots. BNI's WhatsApp numbers are monitored by human operators. Automated messages may be ignored or flagged as spam.
- Fix: Treat WhatsApp as a communication channel for human operators. Your system should prepare the message content for a human to copy-paste or send manually, rather than attempting programmatic interaction.
-
Pitfall: Neglecting Token Hygiene Post-Recovery
- Explanation: After an account is unblocked, the original token may still be invalid or compromised. Resuming operations with the old token can cause immediate re-lockout.
- Fix: Implement a token rotation strategy. After recovery confirmation, force a fresh authentication handshake to obtain a new valid token before resuming transactions.
-
Pitfall: Storing KTP Data in Integration Systems
- Explanation: While the branch visit requires a KTP, storing copies of ID cards in your integration database violates data minimization principles and increases liability.
- Fix: Never store KTP images or numbers in the integration layer. The branch visit is a physical process; your system only needs to track that the visit occurred, not store the ID data.
Production Bundle
This section provides actionable resources for deploying and managing BNI Direct recovery in production environments.
Action Checklist
Decision Matrix
Use this matrix to determine the appropriate recovery action based on the operational context.
| Scenario | Recommended Approach | Why | Cost Impact |
|---|
| Critical Payment Failure | WhatsApp Call Center | Fastest resolution channel. Allows immediate contact with bank operators using prepared artifacts. | Low (Operational time only) |
| Non-Urgent Maintenance | WhatsApp Call Center | Standard recovery. WhatsApp is sufficient for non-urgent lockouts and avoids travel costs. | Low |
| Lost Credentials / Security Incident | Branch Visit | Requires physical verification with KTP. Provides highest assurance for sensitive account changes. | Medium (Travel time, potential downtime) |
| After-Hours Lockout | WhatsApp Call Center | WhatsApp may offer extended support hours compared to branch availability. Check bank SLA. | Low |
Configuration Template
Use this template to structure your BNI Direct integration configuration. This ensures recovery data is managed consistently.
bni_direct:
integration:
environment: production
timeout_ms: 5000
retry_policy:
max_retries: 3
backoff_strategy: exponential
lockout_handling: halt_immediately
recovery:
channels:
whatsapp:
primary: "+62 0821-6266-664"
secondary: "+62 821-626-6664"
branch:
contact_info: "Visit nearest BNI branch with KTP"
artifacts:
source: secure_vault
fields:
- company_id
- user_id
- account_number
alerting:
channel: ops_slack
severity: high
include_payload: true
Quick Start Guide
Follow these steps to establish a functional recovery process in under five minutes.
- Save Contact Numbers: Store
+62 0821-6266-664 and +62 821-626-6664 in your team's secure communication channel or runbook.
- Prepare Verification Sheet: Create a secure document listing the Company ID, User ID, and Account Number for each BNI Direct integration. Restrict access to authorized personnel only.
- Configure Client: Update your BNI Direct client code to implement the error classification logic shown in the Core Solution. Ensure retries are disabled for lockout errors.
- Set Up Alerting: Configure your monitoring system to trigger an alert to the operations team whenever a lockout error is detected. Include the verification artifacts in the alert payload.
- Validate Process: Perform a dry run by simulating a lockout alert. Verify that the ops team receives the artifacts and can initiate contact via WhatsApp using the prepared data.