feat: add mock payment plugin and callback service#253
feat: add mock payment plugin and callback service#253ginaxu1 wants to merge 2 commits intoOpenNSW:mainfrom
Conversation
This comment was marked as outdated.
This comment was marked as outdated.
1789404 to
983797d
Compare
There was a problem hiding this comment.
Code Review
This pull request introduces a new payment processing feature, including a PaymentConfig struct, a payment_transactions database table, and a PaymentHandler for processing payment callbacks and transaction inquiries. The backend now exposes programmatic ExecuteTask functionality and passes database and configuration to payment task plugins. The frontend integrates a mock payment screen and updates the Payment component to handle different payment methods and gateway redirections. Review feedback highlights several areas for improvement: the onTaskUpdated prop is no longer passed to the Payment component, which could lead to less smooth UI refreshes for instant card payments; the API key comparison in HandleTransactionInquiry should use a constant-time comparison to mitigate timing attacks; the "dummy" plugin instantiation for GetTransactionByReference could be refactored into a dedicated service for better decoupling; and the use of TaskID as ExecutionID in the payment_transactions table might not be granular enough to track individual payment attempts, especially in retry scenarios.
807ba0e to
48df837
Compare
48df837 to
5dc677c
Compare
b2ba213 to
7c5080f
Compare
There was a problem hiding this comment.
Code Review
This pull request introduces a robust and extensible payment processing system, which is a significant feature. The use of a gateway interface and registry for different providers is excellent for future maintainability. The database-backed transaction handling with row-level locking in processPayment is well-implemented to ensure atomicity and prevent race conditions.
I've identified a couple of critical bugs related to database schema and state initialization that will cause runtime failures, and a medium-severity issue regarding hardcoded mock secrets. Please see the detailed comments for suggestions on how to address these points.
backend/internal/database/migrations/001_insert_payment_transactions.sql
Show resolved
Hide resolved
7c5080f to
0afe442
Compare
|
nit: keep domain logic out of the HTTP handlers and TM
|
7572ce2 to
2498700
Compare
2498700 to
6147905
Compare
There was a problem hiding this comment.
Code Review
This pull request introduces a new payment processing system, integrating external payment gateways into the existing task management workflow. Key changes include adding a payment_transactions database table, new configuration for payment providers, and a payment.Service to manage callbacks and inquiries. The TaskManager and PaymentTask plugin were refactored to utilize a gateway.Registry and paymentRepository for persistent transaction management, moving away from local store history. The frontend Payment component was updated to support the new flow, including handling redirects to external gateways and passive polling for transaction status. Review comments highlight a critical inconsistency in payment status handling between the FSM and database, a missing validation for mandatory production payment secrets in the configuration, inconsistent environment variable naming for payment settings, and limited extensibility in the payment gateway selection logic.
33f4c30 to
edefe26
Compare
891b3e8 to
648beeb
Compare
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request introduces a well-designed and secure payment architecture. The separation of concerns into a PaymentService, gateway Registry, and task Plugin is excellent. The use of an "Outbound Fetch" pattern, database transactions with row locking, and robust state management in the PaymentTask plugin demonstrates strong attention to security and reliability. The frontend changes are also well-implemented, especially the polling mechanism on the payment return screen.
I have one main point of feedback:
- A critical security stub in the
GovPayProviderthat bypasses payment verification. This must be addressed before production.
Overall, this is a very strong contribution. Addressing this point will make it ready for merging.
| func (p *GovPayProvider) GetPaymentInfo(ctx context.Context, referenceNumber string) (CallbackResult, error) { | ||
| // STUB: In production, this will make an outbound REST call to LankaPay's verification API | ||
| // using p.Secret and p.MerchantID as credentials. | ||
| // For now, we trust the webhook and return SUCCESS to allow development to proceed. | ||
| return CallbackResult{ | ||
| ReferenceNumber: referenceNumber, | ||
| ProviderID: p.ID(), | ||
| Status: "SUCCESS", | ||
| }, nil | ||
| } |
There was a problem hiding this comment.
The GetPaymentInfo function is currently a stub that always returns a success result. This completely bypasses the "Outbound Fetch" security pattern described in the pull request, where the backend should make a secure server-to-server call to the payment gateway to verify the transaction status. Without this verification, the system is vulnerable to fraudulent payment confirmations. While this is acceptable for initial development, it's critical to implement the actual verification logic before this code reaches production. Please ensure a ticket is created to track the implementation of the real verification API call or HMAC signature validation.
…enNSW#257 (OpenNSW#265) * feat: add ActionCard and ActionListView components for task management * refactor: use static Tailwind classes in ActionCard to prevent purging OpenNSW#257 * feat: update ActionCard to use LockClosedIcon for locked state * refactor: use Tailwind @Utility for custom scrollbar in index.css * refactor: remove unused useState import from ActionCard component
648beeb to
be75bf5
Compare
Closes #233
Screen.Recording.2026-03-24.at.13.53.10.mov
Summary
This PR introduces a secure, pluggable payment architecture to process gateway responses. Added the
PaymentGatewayinterface to support multiple vendors (starting with GovPay) and a dedicatedPaymentServiceto act as the domain orchestrator. Secure state transitions are guaranteed using an Outbound Fetch pattern, ensuring that task updates (IDLE → IN_PROGRESS → COMPLETED) only occur after direct server-to-server verification with the gateway provider.Changes Made
backend/internal/task/plugin/gateway/(Pluggable Architecture)PaymentGatewayinterface ininterface.go, separating untrusted data extraction (ExtractReference) from secure verification (GetPaymentInfo).GovPayProviderto encapsulate vendor-specific logic, including URL generation for the hosted checkout page and reference extraction from GovPay webhooks.Registryto manage multiple gateway instances, allowing the system to switch providers dynamically based on the transaction context.backend/internal/payment/(Domain Orchestration)PaymentServiceto house business logic previously residing in API handlers. It orchestrates the Outbound Fetch flow: receiving a raw webhook, extracting a reference, verifying it directly with the gateway's API, and updating the internal databases.db.Transaction) to update statuses and prevent race conditions. Upon successful verification, it callstm.ExecuteTaskto transition the internal FSM state securelybackend/internal/task/api/payment.go(Transport Layer)PaymentHandlerto be transport-only. It now purely extracts the{provider}from the URL path and delegates all processing to thePaymentServiceHandleTransactionInquiryto support synchronous GET requests from the LankaPay network, delegating the final JSON schema formatting to the specific gateway adapterbackend/internal/task/plugin/payment.go(FSM & Plugin)PaymentTaskplugin with a finite state machine:IDLE──(INITIATE_PAYMENT)──►IN_PROGRESS──(PAYMENT_SUCCESS)──►COMPLETED.readSessionandWriteToLocalStoreto persist the gateway URL and transaction metadata in the task's local storeGetRenderInfoto automatically transition tasks fromIN_PROGRESSback toIDLEif a payment session exceeds its TTL, triggering a rotation of the payment sessionbackend/internal/app/bootstrap/(System Wiring)PaymentServiceinto the application container and inject theTaskManagerto enable the service to drive task state changesWireManagersto register an upstream callback. This ensures that when thePaymentServicecompletes a task, theWorkflowManageris automatically notified to progress the overall workflowPortals (Trader App)
Payment.tsxto handle theINITIATE_PAYMENTaction. It now correctly receives and handles thegatewayUrlfor redirection to external payment portals like GovPayuseEffecthook to detect thepayment_errorquery parameter on return redirects, instantly alerting the user if an external transaction failedTaskDetailScreen.tsxto pass anonTaskUpdatedcallback to the plugin renderer, allowing the UI to refresh seamlessly upon mock or local payment completions