Issue: Missing Gateway Validation in Webhook Processing
Description
In the payment package, incoming webhook requests are processed via the ProcessWebhook method in payment/service.go. Currently, this method retrieves a transaction using only the parsed reference number from the webhook payload. However, it does not verify that the transaction's stored GatewayID matches the gatewayID parameter provided in the URL route (/api/v1/payments/:gatewayID/webhook).
This omission allows one payment gateway (or any client spoofing that gateway's route) to potentially alter or settle transaction records belonging to another gateway, creating a data integrity risk and security vulnerability.
Similar validation is correctly performed in the ValidateReference flow (used for real-time reference checking).
Impact
- Cross-Gateway Mutation: A webhook routed to
/api/v1/payments/gateway-a/webhook could succeed in marking a transaction as successful even if that transaction was initialized under gateway-b.
- Security & Auditing: Weakens the separation of duties and trust boundaries between different payment providers.
Code References
- Target File: service.go
- Location: Inside the repository transaction block in
ProcessWebhook: service.go#L278-L323
Proposed Solution
Add a validation check inside the RunInTransaction block in ProcessWebhook to ensure the gateway identifiers match before modifying the transaction state.
// Inside ProcessWebhook in service.go
err = s.repo.RunInTransaction(ctx, func(repo PaymentRepository) error {
tx, err := repo.GetByReferenceNumberForUpdate(ctx, gwPayload.ReferenceNumber)
if err != nil {
return fmt.Errorf("failed to retrieve transaction by reference: %w", err)
}
if tx == nil {
return fmt.Errorf("reference %s: %w", gwPayload.ReferenceNumber, ErrTransactionNotFound)
}
// NEW VALIDATION: Ensure the webhook matches the gateway configured for this transaction
if tx.GatewayID != gatewayID {
return fmt.Errorf("gateway mismatch for transaction %s: expected %s, got %s: %w",
tx.ReferenceNumber, tx.GatewayID, gatewayID, ErrTransactionNotFound)
}
// ... existing status and amount checks ...
})
By returning ErrTransactionNotFound (or a similar error), the HTTP handler will return a 404 Not Found (or 422 Unprocessable Entity), signaling to the gateway to stop retrying the mismatched payload.
Issue: Missing Gateway Validation in Webhook Processing
Description
In the
paymentpackage, incoming webhook requests are processed via theProcessWebhookmethod inpayment/service.go. Currently, this method retrieves a transaction using only the parsed reference number from the webhook payload. However, it does not verify that the transaction's storedGatewayIDmatches thegatewayIDparameter provided in the URL route (/api/v1/payments/:gatewayID/webhook).This omission allows one payment gateway (or any client spoofing that gateway's route) to potentially alter or settle transaction records belonging to another gateway, creating a data integrity risk and security vulnerability.
Similar validation is correctly performed in the
ValidateReferenceflow (used for real-time reference checking).Impact
/api/v1/payments/gateway-a/webhookcould succeed in marking a transaction as successful even if that transaction was initialized undergateway-b.Code References
ProcessWebhook: service.go#L278-L323Proposed Solution
Add a validation check inside the
RunInTransactionblock inProcessWebhookto ensure the gateway identifiers match before modifying the transaction state.By returning
ErrTransactionNotFound(or a similar error), the HTTP handler will return a404 Not Found(or422 Unprocessable Entity), signaling to the gateway to stop retrying the mismatched payload.