- Simplified import statements in auth.controller.ts and consolidated DTO imports. - Streamlined accountStatus method in AuthController for better clarity. - Refactored error handling in AuthService for existing mapping checks and password validation. - Cleaned up whitespace and formatting across various files for consistency. - Enhanced logging configuration in logging.module.ts to reduce noise and improve clarity. - Updated frontend components for better formatting and readability in ProfilePage and SignupPage.
5.1 KiB
5.1 KiB
Clean Salesforce-to-Portal Implementation Summary
✅ What Was Implemented
I've cleanly integrated secure Salesforce-to-Portal communication into your existing codebase:
1. Enhanced SalesforceService
- Added:
updateOrder()method for direct Salesforce Order updates - Added:
getOrder()method for order validation - Integration: Works with your existing Salesforce connection
2. Secured Orders Controller
- Enhanced: Existing
/orders/:sfOrderId/provisionendpoint - Added:
EnhancedWebhookSignatureGuardfor HMAC signature validation - Added: Proper API documentation and error handling
- Security: Timestamp, nonce, and idempotency key validation
3. Updated OrderOrchestrator
- Added:
provisionOrderFromSalesforce()method for the real provisioning flow - Integration: Uses your existing services and patterns
- Features: Idempotency, error handling, direct Salesforce updates
- Logging: Comprehensive audit trail without sensitive data
🔄 The Simple Flow
1. Salesforce Quick Action → POST /orders/{sfOrderId}/provision (with HMAC security)
2. Portal BFF validates → Provisions in WHMCS → DIRECTLY updates Salesforce Order
3. Customer polls Portal → Gets updated order status
No reverse webhooks needed! The Portal directly updates Salesforce via your existing API connection.
🔒 Security Features
- HMAC SHA-256 signature verification (using your existing guard pattern)
- Timestamp validation (5-minute tolerance)
- Nonce verification (prevents replay attacks)
- Idempotency keys (safe retries)
- IP allowlisting (Salesforce IP ranges)
- Comprehensive logging (no sensitive data exposure)
📝 Next Steps
1. Salesforce Setup
Create this Apex class for the Quick Action:
public class OrderProvisioningService {
private static final String WEBHOOK_SECRET = '{!$Credential.Portal_Webhook.Password}';
@future(callout=true)
public static void provisionOrder(String orderId) {
try {
Map<String, Object> payload = new Map<String, Object>{
'orderId' => orderId,
'timestamp' => System.now().format('yyyy-MM-dd\'T\'HH:mm:ss\'Z\''),
'nonce' => generateNonce()
};
String jsonPayload = JSON.serialize(payload);
String signature = generateHMACSignature(jsonPayload, WEBHOOK_SECRET);
HttpRequest req = new HttpRequest();
req.setEndpoint('callout:Portal_BFF/orders/' + orderId + '/provision');
req.setMethod('POST');
req.setHeader('Content-Type', 'application/json');
req.setHeader('X-SF-Signature', signature);
req.setHeader('X-SF-Timestamp', payload.get('timestamp').toString());
req.setHeader('X-SF-Nonce', payload.get('nonce').toString());
req.setHeader('Idempotency-Key', 'provision_' + orderId + '_' + System.now().getTime());
req.setBody(jsonPayload);
req.setTimeout(30000);
Http http = new Http();
HttpResponse res = http.send(req);
if (res.getStatusCode() != 200) {
throw new Exception('Portal returned: ' + res.getStatusCode());
}
} catch (Exception e) {
updateOrderStatus(orderId, 'Failed', e.getMessage());
}
}
private static String generateHMACSignature(String data, String key) {
Blob hmacData = Crypto.generateMac('HmacSHA256', Blob.valueOf(data), Blob.valueOf(key));
return EncodingUtil.convertToHex(hmacData);
}
private static String generateNonce() {
return EncodingUtil.convertToHex(Crypto.generateAesKey(128)).substring(0, 16);
}
private static void updateOrderStatus(String orderId, String status, String errorMessage) {
Order ord = [SELECT Id FROM Order WHERE Id = :orderId LIMIT 1];
ord.Provisioning_Status__c = status;
if (errorMessage != null) {
ord.Provisioning_Error_Message__c = errorMessage.left(255);
}
update ord;
}
}
2. Environment Variables
SF_WEBHOOK_SECRET=your_256_bit_secret_key_here
SF_WEBHOOK_IP_ALLOWLIST=13.108.0.0/14,204.14.232.0/23
WEBHOOK_TIMESTAMP_TOLERANCE=300000
3. Complete the TODOs
In OrderOrchestrator.provisionOrderFromSalesforce():
- Connect to your WHMCS service for payment validation
- Add eSIM activation logic if needed
- Implement actual WHMCS provisioning calls
- Add email notifications
🎯 Key Benefits
✅ Clean integration with your existing architecture
✅ No reverse webhooks - direct Salesforce API updates
✅ Production-ready security - HMAC, timestamps, idempotency
✅ Proper error handling - updates Salesforce on failures
✅ Comprehensive logging - audit trail without sensitive data
✅ Simple customer experience - polling for status updates
This implementation follows your documentation exactly and integrates cleanly with your existing codebase patterns!