-
Notifications
You must be signed in to change notification settings - Fork 9
feat(connector): [TSYS] Connector Integration #347
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
📝 WalkthroughWalkthroughAdds a new TSYS payment connector: module and re-export, full connector implementation and transformers with flow mappings (Authorize, PSync, Capture, Void, Refund, RSync), trait implementations and stubs, domain/type registration, factory wiring, and config entries for TSYS base URLs. Changes
Pre-merge checks❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
📜 Recent review detailsConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro 📒 Files selected for processing (1)
🧰 Additional context used🧠 Learnings (23)📓 Common learnings📚 Learning: 2025-12-09T11:54:49.256ZApplied to files:
📚 Learning: 2025-11-25T05:32:43.442ZApplied to files:
📚 Learning: 2025-11-25T05:32:43.442ZApplied to files:
📚 Learning: 2025-11-25T05:32:43.442ZApplied to files:
📚 Learning: 2025-11-25T05:32:43.442ZApplied to files:
📚 Learning: 2025-11-25T05:32:43.442ZApplied to files:
📚 Learning: 2025-11-25T05:32:43.442ZApplied to files:
📚 Learning: 2025-09-24T08:45:31.195ZApplied to files:
📚 Learning: 2025-11-25T05:32:43.442ZApplied to files:
📚 Learning: 2025-11-25T05:32:43.442ZApplied to files:
📚 Learning: 2025-09-26T08:08:52.624ZApplied to files:
📚 Learning: 2025-09-24T10:18:33.115ZApplied to files:
📚 Learning: 2025-11-25T05:32:43.442ZApplied to files:
📚 Learning: 2025-09-15T06:56:22.535ZApplied to files:
📚 Learning: 2025-11-25T05:32:43.442ZApplied to files:
📚 Learning: 2025-11-25T05:32:43.442ZApplied to files:
📚 Learning: 2025-11-25T05:32:43.442ZApplied to files:
📚 Learning: 2025-11-25T05:32:43.442ZApplied to files:
📚 Learning: 2025-09-22T12:10:19.352ZApplied to files:
📚 Learning: 2025-11-25T05:32:43.442ZApplied to files:
📚 Learning: 2025-09-15T04:33:18.323ZApplied to files:
📚 Learning: 2025-11-25T05:32:43.442ZApplied to files:
🧬 Code graph analysis (1)backend/connector-integration/src/connectors/tsys/transformers.rs (2)
🔇 Additional comments (9)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🧹 Nitpick comments (4)
backend/connector-integration/src/connectors.rs (1)
157-159: TSYS module wiring matches existing connector pattern
pub mod tsys;andpub use self::tsys::Tsys;are consistent with how other connectors are exposed in this file, so TSYS will be available to the factory and callers without extra changes. As per coding guidelines, registering the new connector module here is the right place to wire it in.backend/connector-integration/src/connectors/tsys.rs (1)
40-42: Unused header constant.The
CONTENT_TYPEconstant is defined but never used. Thebuild_headersfunction (lines 274-283) usesself.common_get_content_type()directly with the header key as a string literal. Consider removing this unused constant or using it consistently.pub(crate) mod headers { - pub(crate) const CONTENT_TYPE: &str = "Content-Type"; + // Removed: unused constant, content-type is set via common_get_content_type() }Or use the constant:
Ok(vec![( - headers::CONTENT_TYPE.to_string(), + "Content-Type".to_string(), self.common_get_content_type().to_string().into(), )])backend/connector-integration/src/connectors/tsys/transformers.rs (2)
438-452: String matching on transaction_type may be fragile.The status derivation uses
item.transaction_type.contains("Auth-Only")which is case-sensitive and could break if TSYS changes the response format. Consider:
- Documenting the expected values from TSYS
- Using case-insensitive comparison if appropriate
- Adding a fallback or logging for unexpected values
impl From<TsysTransactionDetails> for AttemptStatus { fn from(item: TsysTransactionDetails) -> Self { match item.transaction_status { TsysTransactionStatus::Approved => { - if item.transaction_type.contains("Auth-Only") { + // TSYS returns "Auth-Only" for authorization-only transactions + if item.transaction_type.to_uppercase().contains("AUTH-ONLY") { Self::Authorized } else { Self::Charged } } TsysTransactionStatus::Void => Self::Voided, TsysTransactionStatus::Declined => Self::Failure, } } }
807-823: Error details from TSYS response are discarded.The
get_error_responsehelper returns generic hardcoded messages ("Payment failed", "Transaction declined") regardless of the actual TSYS error. WhenTsysResponseTypes::ErrorResponseis matched throughout the file, the actualresponse_codeandresponse_messageare not passed to this helper.Consider propagating the actual TSYS error details for better debugging and error handling:
-fn get_error_response(status_code: u16) -> domain_types::router_data::ErrorResponse { +fn get_error_response( + status_code: u16, + code: Option<String>, + message: Option<String>, +) -> domain_types::router_data::ErrorResponse { domain_types::router_data::ErrorResponse { - code: status_code.to_string(), - message: "Payment failed".to_string(), - reason: Some("Transaction declined".to_string()), + code: code.unwrap_or_else(|| status_code.to_string()), + message: message.clone().unwrap_or_else(|| "Payment failed".to_string()), + reason: message.or_else(|| Some("Transaction declined".to_string())), status_code, attempt_status: None, connector_transaction_id: None, network_decline_code: None, network_advice_code: None, network_error_message: None, } }Then update callers to pass the error details:
TsysResponseTypes::ErrorResponse(err) => ( Err(get_error_response(item.http_code, Some(err.response_code), Some(err.response_message))), AttemptStatus::Failure, ),
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (9)
backend/connector-integration/src/connectors.rs(1 hunks)backend/connector-integration/src/connectors/tsys.rs(1 hunks)backend/connector-integration/src/connectors/tsys/transformers.rs(1 hunks)backend/connector-integration/src/types.rs(1 hunks)backend/domain_types/src/connector_types.rs(2 hunks)backend/domain_types/src/types.rs(1 hunks)config/development.toml(1 hunks)config/production.toml(1 hunks)config/sandbox.toml(1 hunks)
🧰 Additional context used
🧠 Learnings (23)
📓 Common learnings
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/{my_connector,*_connector}.rs : Define marker trait implementations for each supported flow (e.g., `PaymentAuthorizeV2`, `PaymentSyncV2`, `PaymentCapture`) in the connector file
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/tests/**/*_payment_flows_test.rs : Write thorough end-to-end tests in `backend/grpc-server/tests/my_connector_payment_flows_test.rs` to cover all supported payment flows, including success and failure scenarios
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/{*_connector.rs,transformers.rs} : Ensure all matching arms have proper 'not implemented' error handling for connectors that don't support specific payment methods or payment method types
Learnt from: Anurag-05-prog
Repo: juspay/connector-service PR: 188
File: backend/connector-integration/src/connectors/peachpayments.rs:306-315
Timestamp: 2025-09-24T08:45:31.195Z
Learning: In Peachpayments connector (backend/connector-integration/src/connectors/peachpayments.rs), the connector_transaction_id field has different types across flows: PaymentVoidData uses String directly, while PaymentsCaptureData uses ResponseId enum that requires pattern matching or get_connector_transaction_id() method.
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/{my_connector,*_connector}.rs : Implement the `ConnectorCommon` trait for basic connector information including `id()`, `get_currency_unit()`, `get_auth_header()`, `base_url()`, and `build_error_response()` methods
📚 Learning: 2025-11-25T05:32:43.442Z
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Register new connectors in the `ConnectorEnum` in `backend/domain_types/src/connector_types.rs` and in the `convert_connector` function in `backend/connector-integration/src/types.rs`
Applied to files:
backend/connector-integration/src/connectors.rsbackend/domain_types/src/types.rsbackend/domain_types/src/connector_types.rsbackend/connector-integration/src/types.rsbackend/connector-integration/src/connectors/tsys.rs
📚 Learning: 2025-11-25T05:32:43.442Z
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/{my_connector,*_connector}.rs : Each new connector should follow the directory structure: `backend/connector-integration/src/connectors/my_connector.rs` for main connector logic, `backend/connector-integration/src/connectors/my_connector/transformers.rs` for request/response structs and TryFrom implementations, and `backend/grpc-server/tests/my_connector_payment_flows_test.rs` for end-to-end tests
Applied to files:
backend/connector-integration/src/connectors.rsbackend/domain_types/src/types.rsbackend/domain_types/src/connector_types.rsbackend/connector-integration/src/types.rsbackend/connector-integration/src/connectors/tsys.rsbackend/connector-integration/src/connectors/tsys/transformers.rs
📚 Learning: 2025-11-25T05:32:43.442Z
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/{my_connector,*_connector}.rs : Implement the `ConnectorCommon` trait for basic connector information including `id()`, `get_currency_unit()`, `get_auth_header()`, `base_url()`, and `build_error_response()` methods
Applied to files:
backend/connector-integration/src/connectors.rsbackend/domain_types/src/types.rsbackend/domain_types/src/connector_types.rsbackend/connector-integration/src/types.rsconfig/production.tomlbackend/connector-integration/src/connectors/tsys.rs
📚 Learning: 2025-11-25T05:32:43.442Z
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/transformers.rs : All new connectors must implement proper status mapping according to Hyperswitch standards, with default status always being `pending`
Applied to files:
backend/domain_types/src/types.rsbackend/domain_types/src/connector_types.rsbackend/connector-integration/src/types.rsbackend/connector-integration/src/connectors/tsys.rs
📚 Learning: 2025-11-25T05:32:43.442Z
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/{my_connector,*_connector}.rs : Define marker trait implementations for each supported flow (e.g., `PaymentAuthorizeV2`, `PaymentSyncV2`, `PaymentCapture`) in the connector file
Applied to files:
backend/domain_types/src/types.rsbackend/domain_types/src/connector_types.rsbackend/connector-integration/src/types.rsbackend/connector-integration/src/connectors/tsys.rsbackend/connector-integration/src/connectors/tsys/transformers.rs
📚 Learning: 2025-11-25T05:32:43.442Z
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/{*_connector.rs,transformers.rs} : Ensure all matching arms have proper 'not implemented' error handling for connectors that don't support specific payment methods or payment method types
Applied to files:
backend/domain_types/src/types.rsbackend/domain_types/src/connector_types.rsbackend/connector-integration/src/types.rsbackend/connector-integration/src/connectors/tsys.rsbackend/connector-integration/src/connectors/tsys/transformers.rs
📚 Learning: 2025-11-25T05:32:43.442Z
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/{*_connector.rs,transformers.rs} : Never hardcode values in connector code; always retrieve values from the request or available resources and implement proper error handling when values are not found
Applied to files:
backend/domain_types/src/types.rsbackend/domain_types/src/connector_types.rsbackend/connector-integration/src/types.rs
📚 Learning: 2025-11-25T05:32:43.442Z
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/transformers.rs : Handle all types of errors properly using Enums in connector error responses, supporting different error structures, string errors, and empty body responses
Applied to files:
backend/domain_types/src/connector_types.rsbackend/connector-integration/src/types.rsbackend/connector-integration/src/connectors/tsys/transformers.rs
📚 Learning: 2025-11-25T05:32:43.442Z
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/transformers.rs : Use `MinorUnit` type for amount fields in connector request/response structs instead of primitive types like `i64`
Applied to files:
backend/domain_types/src/connector_types.rsbackend/connector-integration/src/types.rs
📚 Learning: 2025-11-25T05:32:43.442Z
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/transformers.rs : Implement `TryFrom` trait to convert between `RouterDataV2` and connector-specific request/response structs in `transformers.rs`
Applied to files:
backend/domain_types/src/connector_types.rsbackend/connector-integration/src/types.rsbackend/connector-integration/src/connectors/tsys.rsbackend/connector-integration/src/connectors/tsys/transformers.rs
📚 Learning: 2025-09-24T08:45:31.195Z
Learnt from: Anurag-05-prog
Repo: juspay/connector-service PR: 188
File: backend/connector-integration/src/connectors/peachpayments.rs:306-315
Timestamp: 2025-09-24T08:45:31.195Z
Learning: In Peachpayments connector (backend/connector-integration/src/connectors/peachpayments.rs), the connector_transaction_id field has different types across flows: PaymentVoidData uses String directly, while PaymentsCaptureData uses ResponseId enum that requires pattern matching or get_connector_transaction_id() method.
Applied to files:
backend/domain_types/src/connector_types.rsbackend/connector-integration/src/connectors/tsys.rsbackend/connector-integration/src/connectors/tsys/transformers.rs
📚 Learning: 2025-11-25T05:32:43.442Z
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/transformers.rs : In `transformers.rs`, define Rust structs that exactly match the JSON (or other format) structure of the connector's API for requests, responses, and error responses
Applied to files:
backend/connector-integration/src/types.rsbackend/connector-integration/src/connectors/tsys.rsbackend/connector-integration/src/connectors/tsys/transformers.rs
📚 Learning: 2025-11-25T05:32:43.442Z
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/{*_connector.rs,transformers.rs} : Maintain proper option chaining and error handling instead of using random default values or unwrap operations
Applied to files:
backend/connector-integration/src/types.rs
📚 Learning: 2025-10-13T08:02:24.195Z
Learnt from: iemyashasvi
Repo: juspay/connector-service PR: 194
File: config/sandbox.toml:54-55
Timestamp: 2025-10-13T08:02:24.195Z
Learning: For Worldpay Vantiv sandbox environment in config/sandbox.toml, the correct URLs are:
- base_url: "https://www.testvantivcnp.com/sandbox/new/sandbox/communicator/online"
- secondary_base_url: "https://www.testvantivcnp.com/sandbox"
Applied to files:
config/sandbox.tomlconfig/development.tomlconfig/production.toml
📚 Learning: 2025-10-13T07:57:48.659Z
Learnt from: iemyashasvi
Repo: juspay/connector-service PR: 194
File: config/development.toml:62-63
Timestamp: 2025-10-13T07:57:48.659Z
Learning: For Worldpay Vantiv development environment in config/development.toml, the correct URLs are:
- base_url: "https://transact.vantivprelive.com/vap/communicator/online"
- secondary_base_url: "https://onlinessr.vantivprelive.com"
Applied to files:
config/sandbox.tomlconfig/development.tomlconfig/production.toml
📚 Learning: 2025-11-25T05:32:43.442Z
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/{my_connector,*_connector}.rs : The main connector file should use the macro framework to define the connector's structure, supported flows, and flow-specific logic through `create_all_prerequisites!` and `macro_connector_implementation!` macros
Applied to files:
backend/connector-integration/src/connectors/tsys.rs
📚 Learning: 2025-11-25T05:32:43.442Z
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/tests/**/*_payment_flows_test.rs : Write thorough end-to-end tests in `backend/grpc-server/tests/my_connector_payment_flows_test.rs` to cover all supported payment flows, including success and failure scenarios
Applied to files:
backend/connector-integration/src/connectors/tsys.rs
📚 Learning: 2025-11-25T05:32:43.442Z
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/transformers.rs : Store critical identifiers (`connector_transaction_id`, `reference_id`, `mandate_id`) in the `handle_response` method for all flows
Applied to files:
backend/connector-integration/src/connectors/tsys.rsbackend/connector-integration/src/connectors/tsys/transformers.rs
📚 Learning: 2025-11-25T05:32:43.442Z
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/transformers.rs : Create utility functions in transformers for common operations including email handling, address processing, return URL construction, and connector transaction ID extraction
Applied to files:
backend/connector-integration/src/connectors/tsys/transformers.rs
📚 Learning: 2025-11-25T05:32:43.442Z
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/transformers.rs : Ensure correct mapping of `response_transaction_id` and `redirect_urls` with respective fields in router data within response handlers
Applied to files:
backend/connector-integration/src/connectors/tsys/transformers.rs
📚 Learning: 2025-09-15T06:56:22.535Z
Learnt from: Shubhodip900
Repo: juspay/connector-service PR: 172
File: backend/connector-integration/src/connectors/trustpay.rs:747-762
Timestamp: 2025-09-15T06:56:22.535Z
Learning: In Trustpay connector (backend/connector-integration/src/connectors/trustpay.rs), the refund_connector_metadata field has different data formats in different contexts: RSync expects it to be already parsed as JSON Value (accessing directly with .get()), while other refund flows expect it to be a JSON string that needs serde_json::from_str() parsing. The extract_payment_method_from_refund_metadata helper is designed for the latter case and should not be used in RSync.
Applied to files:
backend/connector-integration/src/connectors/tsys/transformers.rs
📚 Learning: 2025-09-26T08:08:52.624Z
Learnt from: Shubhodip900
Repo: juspay/connector-service PR: 193
File: backend/connector-integration/src/connectors/rapyd/transformers.rs:505-524
Timestamp: 2025-09-26T08:08:52.624Z
Learning: In Rapyd connector (backend/connector-integration/src/connectors/rapyd/transformers.rs), the next_action field in ResponseData struct is required (not Optional) as per Rapyd API documentation. This field always appears in payment responses with values like "3d_verification", "pending_capture", "pending_confirmation", or "not_applicable", consistent with hyperswitch connector patterns.
Applied to files:
backend/connector-integration/src/connectors/tsys/transformers.rs
🧬 Code graph analysis (1)
backend/connector-integration/src/connectors/tsys.rs (4)
backend/domain_types/src/connector_types.rs (1)
std(734-734)backend/connector-integration/src/connectors/macros.rs (2)
router_data(136-147)request_body(108-117)backend/domain_types/src/types.rs (4)
connectors(198-198)connectors(202-204)connectors(208-210)connectors(214-216)backend/common_utils/src/request.rs (1)
headers(165-168)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Run Tests
- GitHub Check: Analyze (rust)
🔇 Additional comments (16)
config/development.toml (1)
46-46: Confirm TSYS stage URL for development configuration
tsys.base_url = "https://stagegw.transnox.com/"looks like a staging endpoint, which is reasonable for development, but it’s worth double‑checking with TSYS docs that:
- this is the correct test/stage URL you intend to use, and
- production config (in
config/production.toml) points to the live TSYS endpoint rather than this stage host.backend/connector-integration/src/types.rs (1)
25-81: Factory registration forConnectorEnum::Tsysis consistentThe new arm
ConnectorEnum::Tsys => Box::new(connectors::Tsys::new()),follows the same pattern as other connectors and correctly wires TSYS intoget_connector_by_name. This aligns with the guideline to register new connectors insideconvert_connector.config/sandbox.toml (1)
24-24: Validate TSYS stage URL choice for sandboxSandbox is also pointed at
https://stagegw.transnox.com/. If TSYS distinguishes between “stage”, “sandbox”, and “cert/live” endpoints, please confirm this host is the correct non‑prod target for the sandbox environment and that production uses the appropriate live URL.backend/domain_types/src/connector_types.rs (1)
46-101: TSYS connector registration inConnectorEnumand gRPC mapping looks correctAdding
TsystoConnectorEnum(withserialize_all = "snake_case") and mappinggrpc_api_types::payments::Connector::Tsys => Ok(Self::Tsys)keeps naming consistent with thetsysconfig keys and follows the established pattern used by other connectors. This is exactly where new connectors are expected to be registered.Also applies to: 103-178
backend/domain_types/src/types.rs (1)
105-162: TSYS connector params field looks consistent; just ensure config is fully wiredAdding
pub tsys: ConnectorParamstoConnectorsis consistent with existing connector fields and works withDefault/serde. Please just confirm:
tsysentries exist in all active env configs (dev/sandbox/prod and any others you deploy), and- TSYS is also registered in
ConnectorEnumandconvert_connector(per guidelines), so this field is actually used.If those are in place—as suggested by the PR summary—this change is good to go.
backend/connector-integration/src/connectors/tsys.rs (4)
44-96: LGTM!The
ConnectorCommonimplementation correctly provides connector metadata, currency handling (minor units), and error response building. Body-based authentication is appropriately handled with an empty auth header. Based on learnings, this follows the expected pattern forid(),get_currency_unit(),get_auth_header(),base_url(), andbuild_error_response()methods.
98-228: LGTM!Empty implementations for authentication flows (PreAuthenticate, Authenticate, PostAuthenticate, SdkSessionToken) and their corresponding SourceVerification traits are appropriate since TSYS doesn't support 3DS authentication. This follows the pattern of providing proper "not implemented" handling for unsupported flows as per coding guidelines.
229-315: LGTM!The macro-based connector setup correctly defines all supported payment flows (Authorize, PSync, Capture, Void, Refund, RSync) with appropriate request/response type mappings. The helper functions for building headers and URLs are well-structured, and the use of a single API endpoint (
servlets/transnox_api_server) is consistent across all flows.
457-712: LGTM!Marker trait implementations for supported flows (PaymentAuthorizeV2, PaymentSyncV2, PaymentCapture, PaymentVoidV2, RefundV2, RefundSyncV2) and empty implementations for unsupported flows correctly satisfy the ConnectorServiceTrait requirements. This follows the expected pattern for new connector integrations as per coding guidelines.
backend/connector-integration/src/connectors/tsys/transformers.rs (7)
26-52: LGTM!The
TsysAuthTypecorrectly extracts authentication credentials fromSignatureKeywith properSecret<String>wrapping for sensitive fields. The error handling for unsupported auth types follows the expected pattern.
147-281: LGTM!Response types are well-structured with proper serde attributes. Status mappings correctly distinguish between Auth (→ Authorized) and Sale (→ Charged) responses. The
get_payments_responsehelper appropriately populatesresource_idandconnector_response_reference_idwith the transaction ID. As per guidelines, critical identifiers are correctly stored in the response handler.
283-369: LGTM!Capture and Void response handlers correctly map success to
Charged/Voidedand errors toCaptureFailed/VoidFailed. The pattern is consistent with the Authorize flow.
536-591: LGTM!The Capture request correctly extracts the connector transaction ID using
get_connector_transaction_id()method, which is the proper pattern forPaymentsCaptureDatathat uses theResponseIdenum. Based on learnings, this follows the correct approach.
593-635: LGTM!The Void request correctly accesses
connector_transaction_iddirectly as aString, which matches thePaymentVoidDatatype behavior noted in learnings.
692-700: Verify TSYS refund status semantics.The comment states "TSYS marks successful refunds as VOID" which maps
TsysTransactionStatus::VoidtoRefundStatus::Success. This is counterintuitive but may be correct for TSYS. Additionally,Approvedmaps toPendingwhich suggests the refund is in progress.Ensure this behavior is documented and verified against TSYS API documentation, as it differs from typical payment processor semantics.
739-805: LGTM!The RSync flow correctly reuses the sync request/response types and uses
connector_refund_idfor querying refund status. The response handling properly converts transaction details to refund status.
| let auth_data = TsysPaymentAuthSaleRequest { | ||
| device_id: auth.device_id, | ||
| transaction_key: auth.transaction_key, | ||
| card_data_source: "INTERNET".to_string(), | ||
| transaction_amount: item.request.minor_amount.get_amount_as_i64(), | ||
| currency_code: item.request.currency, | ||
| card_number: card_data.card_number.clone(), | ||
| expiration_date: card_data.get_card_expiry_month_year_2_digit_with_delimiter("/".to_owned())?, | ||
| cvv2: card_data.card_cvc.clone(), | ||
| order_number: item.resource_common_data.connector_request_reference_id.clone(), | ||
| terminal_capability: "ICC_CHIP_READ_ONLY".to_string(), | ||
| terminal_operating_environment: "ON_MERCHANT_PREMISES_ATTENDED".to_string(), | ||
| cardholder_authentication_method: "NOT_AUTHENTICATED".to_string(), | ||
| developer_id: auth.developer_id, | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🌐 Web query:
TSYS TransNox API terminal capability values card not present CNP e-commerce
💡 Result:
Use the posTerminal fields in the TransNox/tnspayments API:
- posTerminal.cardPresenceCapability = "CARD_NOT_PRESENT" (or "CARD_PRESENT_AND_CARD_NOT_PRESENT" if supporting both). [1]
- For e‑commerce, set posTerminal.cardholderActivated = "ELECTRONIC_COMMERCE" (CAT level 6) or set posTerminal.inputCapability to the appropriate e‑commerce value if required by your integration. [1][2]
References: TSYS/tnspayments API docs. [1] secure.na.tnspayments.com API Pay (posTerminal.cardPresenceCapability). [2] secure.na.tnspayments.com API Pay (posTerminal.cardholderActivated / inputCapability).
Update terminal capability values for card-not-present e-commerce transactions.
The hardcoded terminal capability values are inconsistent with TSYS API specifications for e-commerce/CNP transactions. The current values:
card_data_source: "INTERNET"terminal_capability: "ICC_CHIP_READ_ONLY"terminal_operating_environment: "ON_MERCHANT_PREMISES_ATTENDED"
are designed for physical chip card readers in attended locations, not card-not-present transactions.
For e-commerce CNP transactions, TSYS TransNox API requires:
posTerminal.cardPresenceCapability = "CARD_NOT_PRESENT"posTerminal.cardholderActivated = "ELECTRONIC_COMMERCE"(CAT level 6)
Update TsysPaymentAuthSaleRequest field mappings to align with these documented TSYS API requirements for online transactions.
🤖 Prompt for AI Agents
In backend/connector-integration/src/connectors/tsys/transformers.rs around
lines 117-131 the code uses terminal values intended for attended chip reads;
change the mappings to TSYS CNP/e-commerce values: keep card_data_source as
"INTERNET", set terminal_capability to "CARD_NOT_PRESENT", and set the
cardholder activation/authentication field to "ELECTRONIC_COMMERCE" (CAT level
6) instead of "ON_MERCHANT_PREMISES_ATTENDED" or "ICC_CHIP_READ_ONLY"; update
the corresponding struct field assignments so the request sent to TSYS reflects
posTerminal.cardPresenceCapability="CARD_NOT_PRESENT" and
posTerminal.cardholderActivated="ELECTRONIC_COMMERCE".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
♻️ Duplicate comments (1)
backend/connector-integration/src/connectors/tsys/transformers.rs (1)
138-140: Terminal capability values are incorrect for e-commerce transactions.This issue was previously identified. The current values (
ICC_CHIP_READ_ONLY,ON_MERCHANT_PREMISES_ATTENDED) are designed for physical chip card readers in attended locations, not card-not-present e-commerce transactions.
🧹 Nitpick comments (2)
backend/connector-integration/src/connectors/tsys/transformers.rs (2)
765-773: Clarify the unusual refund status mapping.The comment on line 769 explains that TSYS marks successful refunds as VOID, but the
Approved -> Pendingmapping (line 768) might be confusing for future maintainers. Consider adding more context:impl From<TsysTransactionDetails> for common_enums::enums::RefundStatus { fn from(item: TsysTransactionDetails) -> Self { match item.transaction_status { - TsysTransactionStatus::Approved => Self::Pending, - TsysTransactionStatus::Void => Self::Success, // TSYS marks successful refunds as VOID + // TSYS refund lifecycle: initially returns Approved (in-progress), + // then transitions to Void when the refund is fully processed/settled + TsysTransactionStatus::Approved => Self::Pending, // Refund submitted, awaiting settlement + TsysTransactionStatus::Void => Self::Success, // Refund settled/completed TsysTransactionStatus::Declined => Self::Failure, } } }
898-910: Generic error response loses connector-specific error details.The
get_error_responsehelper only uses the HTTP status code and returns hardcoded messages. WhenTsysResponseTypes::ErrorResponseis received, the actualTsysErrorResponsecontainingresponse_codeandresponse_messageis discarded.Consider passing the error response details to preserve debugging context:
-fn get_error_response(status_code: u16) -> domain_types::router_data::ErrorResponse { +fn get_error_response( + status_code: u16, + error: Option<&TsysErrorResponse>, +) -> domain_types::router_data::ErrorResponse { + let (code, message, reason) = match error { + Some(e) => ( + e.response_code.clone(), + e.response_message.clone(), + Some(e.response_message.clone()), + ), + None => ( + status_code.to_string(), + "Payment failed".to_string(), + Some("Transaction declined".to_string()), + ), + }; domain_types::router_data::ErrorResponse { - code: status_code.to_string(), - message: "Payment failed".to_string(), - reason: Some("Transaction declined".to_string()), + code, + message, + reason, status_code, attempt_status: None, connector_transaction_id: None, network_decline_code: None, network_advice_code: None, network_error_message: None, } }
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
backend/connector-integration/src/connectors/tsys.rs(1 hunks)backend/connector-integration/src/connectors/tsys/transformers.rs(1 hunks)
🧰 Additional context used
🧠 Learnings (19)
📓 Common learnings
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/{my_connector,*_connector}.rs : Define marker trait implementations for each supported flow (e.g., `PaymentAuthorizeV2`, `PaymentSyncV2`, `PaymentCapture`) in the connector file
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/{my_connector,*_connector}.rs : Implement the `ConnectorCommon` trait for basic connector information including `id()`, `get_currency_unit()`, `get_auth_header()`, `base_url()`, and `build_error_response()` methods
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Register new connectors in the `ConnectorEnum` in `backend/domain_types/src/connector_types.rs` and in the `convert_connector` function in `backend/connector-integration/src/types.rs`
📚 Learning: 2025-11-25T05:32:43.442Z
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/{my_connector,*_connector}.rs : Define marker trait implementations for each supported flow (e.g., `PaymentAuthorizeV2`, `PaymentSyncV2`, `PaymentCapture`) in the connector file
Applied to files:
backend/connector-integration/src/connectors/tsys.rsbackend/connector-integration/src/connectors/tsys/transformers.rs
📚 Learning: 2025-11-25T05:32:43.442Z
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/{my_connector,*_connector}.rs : Each new connector should follow the directory structure: `backend/connector-integration/src/connectors/my_connector.rs` for main connector logic, `backend/connector-integration/src/connectors/my_connector/transformers.rs` for request/response structs and TryFrom implementations, and `backend/grpc-server/tests/my_connector_payment_flows_test.rs` for end-to-end tests
Applied to files:
backend/connector-integration/src/connectors/tsys.rsbackend/connector-integration/src/connectors/tsys/transformers.rs
📚 Learning: 2025-11-25T05:32:43.442Z
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/{*_connector.rs,transformers.rs} : Ensure all matching arms have proper 'not implemented' error handling for connectors that don't support specific payment methods or payment method types
Applied to files:
backend/connector-integration/src/connectors/tsys.rsbackend/connector-integration/src/connectors/tsys/transformers.rs
📚 Learning: 2025-11-25T05:32:43.442Z
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/{my_connector,*_connector}.rs : Implement the `ConnectorCommon` trait for basic connector information including `id()`, `get_currency_unit()`, `get_auth_header()`, `base_url()`, and `build_error_response()` methods
Applied to files:
backend/connector-integration/src/connectors/tsys.rs
📚 Learning: 2025-11-25T05:32:43.442Z
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/{my_connector,*_connector}.rs : The main connector file should use the macro framework to define the connector's structure, supported flows, and flow-specific logic through `create_all_prerequisites!` and `macro_connector_implementation!` macros
Applied to files:
backend/connector-integration/src/connectors/tsys.rs
📚 Learning: 2025-11-25T05:32:43.442Z
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Register new connectors in the `ConnectorEnum` in `backend/domain_types/src/connector_types.rs` and in the `convert_connector` function in `backend/connector-integration/src/types.rs`
Applied to files:
backend/connector-integration/src/connectors/tsys.rs
📚 Learning: 2025-11-25T05:32:43.442Z
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/tests/**/*_payment_flows_test.rs : Write thorough end-to-end tests in `backend/grpc-server/tests/my_connector_payment_flows_test.rs` to cover all supported payment flows, including success and failure scenarios
Applied to files:
backend/connector-integration/src/connectors/tsys.rs
📚 Learning: 2025-11-25T05:32:43.442Z
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/transformers.rs : In `transformers.rs`, define Rust structs that exactly match the JSON (or other format) structure of the connector's API for requests, responses, and error responses
Applied to files:
backend/connector-integration/src/connectors/tsys.rsbackend/connector-integration/src/connectors/tsys/transformers.rs
📚 Learning: 2025-09-24T08:45:31.195Z
Learnt from: Anurag-05-prog
Repo: juspay/connector-service PR: 188
File: backend/connector-integration/src/connectors/peachpayments.rs:306-315
Timestamp: 2025-09-24T08:45:31.195Z
Learning: In Peachpayments connector (backend/connector-integration/src/connectors/peachpayments.rs), the connector_transaction_id field has different types across flows: PaymentVoidData uses String directly, while PaymentsCaptureData uses ResponseId enum that requires pattern matching or get_connector_transaction_id() method.
Applied to files:
backend/connector-integration/src/connectors/tsys.rsbackend/connector-integration/src/connectors/tsys/transformers.rs
📚 Learning: 2025-11-25T05:32:43.442Z
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/transformers.rs : All new connectors must implement proper status mapping according to Hyperswitch standards, with default status always being `pending`
Applied to files:
backend/connector-integration/src/connectors/tsys.rs
📚 Learning: 2025-11-25T05:32:43.442Z
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/transformers.rs : Store critical identifiers (`connector_transaction_id`, `reference_id`, `mandate_id`) in the `handle_response` method for all flows
Applied to files:
backend/connector-integration/src/connectors/tsys.rsbackend/connector-integration/src/connectors/tsys/transformers.rs
📚 Learning: 2025-09-15T06:56:22.535Z
Learnt from: Shubhodip900
Repo: juspay/connector-service PR: 172
File: backend/connector-integration/src/connectors/trustpay.rs:747-762
Timestamp: 2025-09-15T06:56:22.535Z
Learning: In Trustpay connector (backend/connector-integration/src/connectors/trustpay.rs), the refund_connector_metadata field has different data formats in different contexts: RSync expects it to be already parsed as JSON Value (accessing directly with .get()), while other refund flows expect it to be a JSON string that needs serde_json::from_str() parsing. The extract_payment_method_from_refund_metadata helper is designed for the latter case and should not be used in RSync.
Applied to files:
backend/connector-integration/src/connectors/tsys.rsbackend/connector-integration/src/connectors/tsys/transformers.rs
📚 Learning: 2025-11-25T05:32:43.442Z
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/transformers.rs : Implement `TryFrom` trait to convert between `RouterDataV2` and connector-specific request/response structs in `transformers.rs`
Applied to files:
backend/connector-integration/src/connectors/tsys.rsbackend/connector-integration/src/connectors/tsys/transformers.rs
📚 Learning: 2025-11-25T05:32:43.442Z
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/transformers.rs : Handle all types of errors properly using Enums in connector error responses, supporting different error structures, string errors, and empty body responses
Applied to files:
backend/connector-integration/src/connectors/tsys/transformers.rs
📚 Learning: 2025-11-25T05:32:43.442Z
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/transformers.rs : Create utility functions in transformers for common operations including email handling, address processing, return URL construction, and connector transaction ID extraction
Applied to files:
backend/connector-integration/src/connectors/tsys/transformers.rs
📚 Learning: 2025-11-25T05:32:43.442Z
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/transformers.rs : Ensure correct mapping of `response_transaction_id` and `redirect_urls` with respective fields in router data within response handlers
Applied to files:
backend/connector-integration/src/connectors/tsys/transformers.rs
📚 Learning: 2025-09-26T08:08:52.624Z
Learnt from: Shubhodip900
Repo: juspay/connector-service PR: 193
File: backend/connector-integration/src/connectors/rapyd/transformers.rs:505-524
Timestamp: 2025-09-26T08:08:52.624Z
Learning: In Rapyd connector (backend/connector-integration/src/connectors/rapyd/transformers.rs), the next_action field in ResponseData struct is required (not Optional) as per Rapyd API documentation. This field always appears in payment responses with values like "3d_verification", "pending_capture", "pending_confirmation", or "not_applicable", consistent with hyperswitch connector patterns.
Applied to files:
backend/connector-integration/src/connectors/tsys/transformers.rs
📚 Learning: 2025-09-24T10:18:33.115Z
Learnt from: Anurag-05-prog
Repo: juspay/connector-service PR: 188
File: backend/connector-integration/src/connectors/peachpayments/transformers.rs:50-56
Timestamp: 2025-09-24T10:18:33.115Z
Learning: In Peach Payments connector API (backend/connector-integration/src/connectors/peachpayments/transformers.rs), the authorize request struct uses the field "charge_method" as expected by the Peach Payments API, not "paymentType" or other variations.
Applied to files:
backend/connector-integration/src/connectors/tsys/transformers.rs
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: Run Tests
- GitHub Check: Compilation Check
- GitHub Check: Analyze (rust)
🔇 Additional comments (14)
backend/connector-integration/src/connectors/tsys.rs (8)
1-41: LGTM! Module structure and imports are well-organized.The file properly declares the
transformersmodule, imports necessary types fromdomain_types,interfaces, and local crates, and defines theheadersmodule with theCONTENT_TYPEconstant. This aligns with the connector directory structure guidelines.
44-95: LGTM! ConnectorCommon implementation is complete and follows conventions.The implementation correctly:
- Returns
CurrencyUnit::Minorfor amounts in cents- Uses body-based authentication (empty auth headers)
- Properly deserializes error responses and emits events
Based on learnings, this aligns with the
ConnectorCommontrait requirements forid(),get_currency_unit(),get_auth_header(),base_url(), andbuild_error_response()methods.
97-157: Empty authentication stubs are appropriate for TSYS.TSYS doesn't support 3DS authentication flows, and these empty implementations satisfy the trait requirements. The comment on line 98 clearly documents this design decision.
159-266: Validation and SourceVerification implementations are complete.All required
SourceVerificationtrait implementations are provided for the supported flows (Authorize, PSync, Capture, Void, Refund, RSync) and the unsupported authentication flows.
356-494: LGTM! Flow implementations follow the macro framework correctly.All six flows (Authorize, PSync, Capture, Void, Refund, RSync) are properly implemented using
macro_connector_implementation!with:
- JSON request body encoding
- POST HTTP method
- Appropriate URL helpers (payments vs refunds)
- Standard default implementations for headers, content type, and error responses
Based on learnings, this aligns with the expected connector file structure for defining flow-specific logic through macros.
496-526: LGTM! Marker trait implementations are complete for all supported flows.Based on learnings, marker trait implementations (
PaymentAuthorizeV2,PaymentSyncV2,PaymentCapture,PaymentVoidV2,RefundV2,RefundSyncV2) are correctly defined for the Tsys connector.
528-815: LGTM! Unsupported flow stubs are complete.All unsupported flows (CreateOrder, CreateAccessToken, SetupMandate, PaymentMethodToken, RepeatPayment, VoidPC, disputes, webhooks, etc.) have the required empty implementations to satisfy
ConnectorServiceTrait. This follows the connector integration pattern correctly.
817-822: LGTM! ConnectorServiceTrait implementation completes the connector.The final
ConnectorServiceTraitimplementation correctly wraps all the preceding trait implementations, making the TSYS connector fully functional within the framework.backend/connector-integration/src/connectors/tsys/transformers.rs (6)
26-52: LGTM! Authentication type mapping is correct.The
TsysAuthTypestruct correctly mapsConnectorAuthType::SignatureKeyfields to TSYS credentials (device_id,transaction_key,developer_id). The error handling for unsupported auth types follows the pattern from learnings.
158-296: LGTM! Authorize response handling is well-structured.The response type hierarchy properly handles:
- Success vs error responses via
TsysResponseTypes- Auth vs Sale differentiation via
TsysPaymentsResponse- Correct status mapping:
AuthResponsesuccess →Authorized,SaleResponsesuccess →Charged- Critical identifiers stored:
connector_transaction_id,connector_response_reference_idBased on learnings, this correctly implements the response handling pattern with proper status mapping and identifier storage.
298-386: LGTM! Capture and Void response handling is consistent.Both
TryFromimplementations correctly map success responses to terminal statuses (Charged,Voided) and error responses to failure statuses (CaptureFailed,VoidFailed). The pattern is consistent with the Authorize flow.
572-637: LGTM! Capture request construction is correct.The
TryFromimplementation properly:
- Extracts
connector_transaction_idusingget_connector_transaction_id()with appropriate error handling- Uses
minor_amount_to_capturefor the capture amount- Includes all required authentication fields
639-696: LGTM! Void request construction handles transaction ID correctly.Based on learnings,
PaymentVoidDatausesconnector_transaction_idas aStringdirectly (unlikePaymentsCaptureDatawhich usesResponseId), so the direct.clone()access on line 690 is correct.
813-892: LGTM! RSync flow properly reuses sync types.The RSync implementation correctly:
- Reuses
TsysSyncRequest/TsysSyncResponsetypes via transparent wrappers- Uses
connector_refund_idto look up refund status- Maps transaction details to refund status using the previously defined
Fromimpl
| pub fn get_url_payments<F, Req, Res>( | ||
| &self, | ||
| req: &RouterDataV2<F, PaymentFlowData, Req, Res>, | ||
| ) -> CustomResult<String, errors::ConnectorError> { | ||
| let base_url = self.connector_base_url_payments(req); | ||
| Ok(format!("{}servlets/transnox_api_server", base_url)) | ||
| } | ||
|
|
||
| pub fn get_url_refunds<F, Req, Res>( | ||
| &self, | ||
| req: &RouterDataV2<F, RefundFlowData, Req, Res>, | ||
| ) -> CustomResult<String, errors::ConnectorError> { | ||
| let base_url = self.connector_base_url_refunds(req); | ||
| Ok(format!("{}servlets/transnox_api_server", base_url)) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
# Check the implementation of connector_base_url_payments and connector_base_url_refunds in tsys.rs
rg -n "connector_base_url" backend/connector-integration/src/connectors/tsys.rs -A 3Repository: juspay/connector-service
Length of output: 768
🏁 Script executed:
# Check how other connectors handle URL construction with base_url
rg -n "format!.*servlets" backend/connector-integration/src/connectors/ -B 2 -A 1Repository: juspay/connector-service
Length of output: 933
🏁 Script executed:
# Look at the create_all_prerequisites macro to understand base_url behavior
rg -n "connector_base_url_payments\|connector_base_url_refunds" backend/connector-integration/src/ --type=rs -B 2 -A 5 | head -100Repository: juspay/connector-service
Length of output: 94
🏁 Script executed:
# Check if base_url in other connectors ends with / or how they handle concatenation
rg -n "format!.*http" backend/connector-integration/src/connectors/ -B 1 -A 1 | grep -E "(format!|https)" | head -40Repository: juspay/connector-service
Length of output: 50
🏁 Script executed:
# Read the full implementation of base_url methods and surrounding code
sed -n '320,355p' backend/connector-integration/src/connectors/tsys.rsRepository: juspay/connector-service
Length of output: 1313
🏁 Script executed:
# Check how other connectors construct URLs with base_url
rg -n "connector_base_url.*\)" backend/connector-integration/src/connectors/ -A 6 | head -80Repository: juspay/connector-service
Length of output: 6315
🏁 Script executed:
# Look for URL patterns in multiple connectors
fd "\.rs$" backend/connector-integration/src/connectors/ --exec grep -l "get_url_payments" {} \; | head -5 | xargs -I {} sh -c 'echo "=== {} ===" && grep -A 2 "get_url_payments" {}'Repository: juspay/connector-service
Length of output: 487
🏁 Script executed:
# Check if base URLs are defined with trailing slashes in config or defaults
rg -n "https://.*api" backend/ --type rust -B 2 -A 2 | head -60Repository: juspay/connector-service
Length of output: 5248
🏁 Script executed:
# Check if there's a pattern - do most connectors use explicit "/" in format strings for URLs?
rg -n 'format!\(".*\{.*\/' backend/connector-integration/src/connectors/ | head -20Repository: juspay/connector-service
Length of output: 168
🏁 Script executed:
# Verify if base_url values are consistently defined with trailing slash
rg -n '"base_url".*:' backend/ --type rust | head -15Repository: juspay/connector-service
Length of output: 194
🏁 Script executed:
# Compare TSYS URL construction with xendit side by side
echo "=== TSYS ===" && sed -n '342,344p' backend/connector-integration/src/connectors/tsys.rs && echo "=== XENDIT ===" && sed -n '438,440p' backend/connector-integration/src/connectors/xendit.rsRepository: juspay/connector-service
Length of output: 341
Add explicit forward slash in URL construction to match codebase patterns.
The URL construction uses format!("{}servlets/transnox_api_server", base_url) without an explicit / between the base URL and path. Other connectors (e.g., Xendit) use format!("{}/payment_requests", ...) with the explicit separator. While base URLs are typically configured with trailing slashes, maintaining consistency with the established pattern prevents potential malformed URLs if configuration assumptions change.
Update both get_url_payments and get_url_refunds:
pub fn get_url_payments<F, Req, Res>(
&self,
req: &RouterDataV2<F, PaymentFlowData, Req, Res>,
) -> CustomResult<String, errors::ConnectorError> {
let base_url = self.connector_base_url_payments(req);
- Ok(format!("{}servlets/transnox_api_server", base_url))
+ Ok(format!("{}/servlets/transnox_api_server", base_url))
}Apply the same fix to get_url_refunds.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| pub fn get_url_payments<F, Req, Res>( | |
| &self, | |
| req: &RouterDataV2<F, PaymentFlowData, Req, Res>, | |
| ) -> CustomResult<String, errors::ConnectorError> { | |
| let base_url = self.connector_base_url_payments(req); | |
| Ok(format!("{}servlets/transnox_api_server", base_url)) | |
| } | |
| pub fn get_url_refunds<F, Req, Res>( | |
| &self, | |
| req: &RouterDataV2<F, RefundFlowData, Req, Res>, | |
| ) -> CustomResult<String, errors::ConnectorError> { | |
| let base_url = self.connector_base_url_refunds(req); | |
| Ok(format!("{}servlets/transnox_api_server", base_url)) | |
| } | |
| pub fn get_url_payments<F, Req, Res>( | |
| &self, | |
| req: &RouterDataV2<F, PaymentFlowData, Req, Res>, | |
| ) -> CustomResult<String, errors::ConnectorError> { | |
| let base_url = self.connector_base_url_payments(req); | |
| Ok(format!("{}/servlets/transnox_api_server", base_url)) | |
| } | |
| pub fn get_url_refunds<F, Req, Res>( | |
| &self, | |
| req: &RouterDataV2<F, RefundFlowData, Req, Res>, | |
| ) -> CustomResult<String, errors::ConnectorError> { | |
| let base_url = self.connector_base_url_refunds(req); | |
| Ok(format!("{}/servlets/transnox_api_server", base_url)) | |
| } |
🤖 Prompt for AI Agents
In backend/connector-integration/src/connectors/tsys.rs around lines 338 to 352,
the constructed URLs omit an explicit '/' between base_url and the path which
can lead to malformed URLs if base_url lacks a trailing slash; update both
get_url_payments and get_url_refunds to insert a forward slash when formatting
the path (e.g., format!("{}/servlets/transnox_api_server", base_url)) so both
functions consistently produce a correctly separated URL.
| impl From<TsysTransactionDetails> for AttemptStatus { | ||
| fn from(item: TsysTransactionDetails) -> Self { | ||
| match item.transaction_status { | ||
| TsysTransactionStatus::Approved => { | ||
| if item.transaction_type.contains("Auth-Only") { | ||
| Self::Authorized | ||
| } else { | ||
| Self::Charged | ||
| } | ||
| } | ||
| TsysTransactionStatus::Void => Self::Voided, | ||
| TsysTransactionStatus::Declined => Self::Failure, | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🌐 Web query:
TSYS TransNox API transactionType field values documentation
💡 Result:
The TransNox / TSYS (tnspayments) API documents the transactionType (shown as transaction.type or transaction.source in their docs). Common documented values:
-
transaction.type (action on order): AUTHENTICATION, AUTHORIZATION, AUTHORIZATION_UPDATE, CAPTURE, CHARGEBACK, FUNDING, PAYMENT, REFUND, REFUND_REQUEST, VERIFICATION, VOID_AUTHORIZATION, VOID_CAPTURE, VOID_PAYMENT, VOID_REFUND. [1]
-
transaction.source / transaction.sourceOfFunds (channel / origin): CALL_CENTRE, CARD_PRESENT, INTERNET, MAIL_ORDER, MERCHANT, MOTO, PAYER_PRESENT, SERVICE_PROVIDER, TELEPHONE_ORDER, VOICE_RESPONSE (case‑sensitive). [1][2]
If you need the exact field name and schema for your TransNox endpoint (NVP vs REST) or a direct link to the API page you use (region/version), tell me which endpoint/version and I’ll fetch the precise doc page.
Sources:
[1] TSYS / tnspayments API docs — Transaction.type and related fields. (secure.ap.tnspayments.com).
Citations:
🏁 Script executed:
# Find the TsysTransactionDetails struct definition
fd -t f "*.rs" -path "*/tsys/*" | xargs grep -l "TsysTransactionDetails"Repository: juspay/connector-service
Length of output: 238
🏁 Script executed:
# Examine the TsysTransactionDetails struct and transaction_type field
rg "struct TsysTransactionDetails" -A 20 backend/connector-integration/src/connectors/tsys/Repository: juspay/connector-service
Length of output: 2159
🏁 Script executed:
# Check how transaction_type is defined (String vs enum)
rg "transaction_type" backend/connector-integration/src/connectors/tsys/ -B 2 -A 2Repository: juspay/connector-service
Length of output: 1092
🏁 Script executed:
# Look for similar string-based matching patterns in other connectors
rg "contains\(" backend/connector-integration/src/connectors/ -t rs | grep -E "transaction|status|type" | head -20Repository: juspay/connector-service
Length of output: 94
🏁 Script executed:
# Look for test data or sample TSYS responses
find backend/connector-integration -path "*tsys*" -type f -name "*.json" -o -name "*test*" -o -name "*sample*" | head -20Repository: juspay/connector-service
Length of output: 376
🏁 Script executed:
# Search for where transaction_type gets populated/mapped in TSYS connector
rg "transaction_type" backend/connector-integration/src/connectors/tsys/ -B 5 -A 5Repository: juspay/connector-service
Length of output: 1975
🏁 Script executed:
# Look at test fixtures or responses related to TSYS
find backend/connector-integration -path "*tsys*" -name "*.rs" -type f | xargs grep -l "Auth-Only\|Approved\|transaction_type" | head -10Repository: juspay/connector-service
Length of output: 133
🏁 Script executed:
# Check TsysTransactionStatus and related types
rg "enum TsysTransactionStatus|enum.*Status" backend/connector-integration/src/connectors/tsys/ -A 8Repository: juspay/connector-service
Length of output: 6144
🏁 Script executed:
# Look for any documentation or comments about transaction type values
rg "transaction.*type|Auth-Only|Approved" backend/connector-integration/src/connectors/tsys/ -B 2 | head -50Repository: juspay/connector-service
Length of output: 1473
🏁 Script executed:
# Look for test files or examples in tsys connector
find backend/connector-integration -path "*tsys*" -name "*.rs" | xargs ls -lahRepository: juspay/connector-service
Length of output: 282
🏁 Script executed:
# Search for any constants or known values of transaction_type
rg "Auth-Only|Sale|Capture|Authorization" backend/connector-integration/src/connectors/tsys/ -iRepository: juspay/connector-service
Length of output: 5031
🏁 Script executed:
# Check the full tsys transformers.rs file structure to understand context
wc -l backend/connector-integration/src/connectors/tsys/transformers.rsRepository: juspay/connector-service
Length of output: 137
🏁 Script executed:
# Look at the full file to understand response parsing
head -100 backend/connector-integration/src/connectors/tsys/transformers.rsRepository: juspay/connector-service
Length of output: 3304
🏁 Script executed:
# Search for where transaction_type originates or any mapping logic
rg "transactionType\|transaction_type" backend/connector-integration/src/connectors/tsys/ -B 3 -A 3Repository: juspay/connector-service
Length of output: 50
🏁 Script executed:
# Search for any tests or fixtures that show actual transaction_type values
find backend/connector-integration -path "*tsys*" -type f | xargs grep -l "transaction_type\|Auth-Only\|transactionType" 2>/dev/nullRepository: juspay/connector-service
Length of output: 133
🏁 Script executed:
# Look at the main tsys.rs file for more context about API integration
head -200 backend/connector-integration/src/connectors/tsys.rs | tail -150Repository: juspay/connector-service
Length of output: 4453
🏁 Script executed:
# Check if there's any documentation or comments about transaction types in TSYS
grep -r "Auth-Only\|transaction.*type" backend/connector-integration/src/connectors/tsys/ -C 2Repository: juspay/connector-service
Length of output: 1092
String-based transaction type matching is loose but case conversion isn't the solution.
The code uses contains("Auth-Only") which is fragile, but not because of case sensitivity. Official TSYS API documentation shows transaction types use consistent uppercase values. The real issue is that the matching logic is too loose—using contains() could match partial strings, and "Auth-Only" itself isn't documented in official TSYS API specifications.
Consider using exact matching instead and document the known TSYS transaction type values, or validate against an enum if the complete set is known. Adding .to_lowercase() would mask the underlying loose matching problem without actually solving it.
🤖 Prompt for AI Agents
In backend/connector-integration/src/connectors/tsys/transformers.rs around
lines 470 to 484, the code uses item.transaction_type.contains("Auth-Only")
which is too loose and can match partial or undocumented values; replace the
contains-based check with an exact match against the documented TSYS transaction
type(s) (e.g., == "AUTH-ONLY" or the exact documented casing) or, better,
parse/validate transaction_type into a dedicated enum of known TSYS types and
match on that enum; update the comment to list the known TSYS transaction type
values you support and handle unknown types explicitly (e.g., map to Failure or
a new Unknown variant) instead of using to_lowercase() or other
case-normalization to mask the issue.
| #[derive(Debug, Serialize)] | ||
| #[serde(rename_all = "camelCase")] | ||
| pub struct TsysReturnRequest { | ||
| #[serde(rename = "deviceID")] | ||
| device_id: Secret<String>, | ||
| transaction_key: Secret<String>, | ||
| transaction_amount: i64, | ||
| #[serde(rename = "transactionID")] | ||
| transaction_id: String, | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🌐 Web query:
TSYS TransNox API Return refund request required fields developerID documentation
💡 Result:
Do you want the official TransNox/TSYS (TNSPay) Refund/Return API documentation link, or a short list of the exact required request fields (XML/JSON) including whether developerID is required?
🏁 Script executed:
# First, let's examine the TSYS connector file to see all request structs
cd backend/connector-integration/src/connectors
find . -name "*tsys*" -type fRepository: juspay/connector-service
Length of output: 77
🌐 Web query:
TSYS TransNox Return refund API request fields specification XML
💡 Result:
I can help — but I couldn't find a public TSYS/TransNox "Return" (refund) XML field specification online. Do you already have a sample request or a merchant/integration account (gateway) that I can look at, or do you want me to try to retrieve official TSYS/TransNox developer documentation (may require login/partner access)?
🏁 Script executed:
# Examine the tsys.rs file to see all request structs and their fields
wc -l backend/connector-integration/src/connectors/tsys.rsRepository: juspay/connector-service
Length of output: 124
🏁 Script executed:
# Let's examine the tsys.rs file, focusing on the request structs around line 702
sed -n '600,750p' backend/connector-integration/src/connectors/tsys.rs | cat -nRepository: juspay/connector-service
Length of output: 5197
🏁 Script executed:
# Search for struct definitions in the file, particularly request structs
grep -n "^pub struct Tsys.*Request" backend/connector-integration/src/connectors/tsys.rsRepository: juspay/connector-service
Length of output: 50
🏁 Script executed:
# Let's check the directory structure and find the correct file
find backend/connector-integration/src/connectors -name "*tsys*" -type d
ls -la backend/connector-integration/src/connectors/tsys/ 2>/dev/null || echo "No tsys directory found"
# Also check if transformers.rs exists
find backend/connector-integration/src/connectors -name "transformers.rs" | grep -i tsysRepository: juspay/connector-service
Length of output: 366
🏁 Script executed:
# Now let's examine the transformers.rs file around line 702-711
sed -n '650,800p' backend/connector-integration/src/connectors/tsys/transformers.rs | cat -nRepository: juspay/connector-service
Length of output: 5879
🏁 Script executed:
# Let's check the other request structs mentioned in the review to verify they have developer_id
grep -n "struct Tsys.*Request" backend/connector-integration/src/connectors/tsys/transformers.rs | head -20Repository: juspay/connector-service
Length of output: 544
🏁 Script executed:
# Let's check each of these request structs to see which ones have developer_id
for line in 66 394 578 645 704; do
echo "=== Struct at line $line ==="
sed -n "${line},$((line+15))p" backend/connector-integration/src/connectors/tsys/transformers.rs
doneRepository: juspay/connector-service
Length of output: 2485
Missing developer_id field in TsysReturnRequest struct.
TsysReturnRequest lacks the developer_id field that is present in all other TSYS request structs (TsysPaymentAuthSaleRequest, TsysSearchTransactionRequest, TsysCaptureRequest, TsysCancelRequest). This inconsistency should be addressed to maintain uniformity in API request structure.
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct TsysReturnRequest {
#[serde(rename = "deviceID")]
device_id: Secret<String>,
transaction_key: Secret<String>,
transaction_amount: i64,
#[serde(rename = "transactionID")]
transaction_id: String,
+ #[serde(rename = "developerID")]
+ developer_id: Secret<String>,
}Update the TryFrom implementation to include developer_id:
let return_request = TsysReturnRequest {
device_id: auth.device_id,
transaction_key: auth.transaction_key,
transaction_amount: item.request.refund_amount,
transaction_id: item.request.connector_transaction_id.clone(),
+ developer_id: auth.developer_id,
};Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In backend/connector-integration/src/connectors/tsys/transformers.rs around
lines 702 to 711, the TsysReturnRequest struct is missing the developer_id field
that other TSYS request structs include; add a developer_id: Secret<String>
field with the same serde rename used elsewhere (e.g., #[serde(rename =
"developerID")]) and then update the TryFrom<TsysConnectorRequest> (or the
relevant TryFrom impl) to populate developer_id from the source request (clone
or convert as done for other Secret<String> fields); also ensure any necessary
imports/types are present and run cargo check to validate serialization names
and types match the other TSYS request structs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
.nextest.toml(1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/{my_connector,*_connector}.rs : Implement the `ConnectorCommon` trait for basic connector information including `id()`, `get_currency_unit()`, `get_auth_header()`, `base_url()`, and `build_error_response()` methods
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Register new connectors in the `ConnectorEnum` in `backend/domain_types/src/connector_types.rs` and in the `convert_connector` function in `backend/connector-integration/src/types.rs`
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: Run Tests
- GitHub Check: Compilation Check
- GitHub Check: Analyze (rust)
🔇 Additional comments (2)
.nextest.toml (2)
1-10: Inconsistency: AI summary references backend TSYS connector implementation not present in provided files.The AI summary mentions comprehensive TSYS connector additions (
tsys.rs, transformers, ConnectorEnum updates), along with learnings referencing theConnectorCommontrait and connector registration. However, only.nextest.tomlis provided for review. This suggests either the backend implementation files are out of scope or the summary is misaligned with the code under review.Can you confirm whether the backend TSYS connector files are part of this PR review, or is this configuration-only change?
8-8: Verify status-level change semantics.Line 8 is marked as changed but the value
"all"appears to be the expected setting for CI runs. Confirm whether this is a deliberate change or an artifact of the diff annotation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
.github/workflows/ci.yml(1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/tests/**/*_payment_flows_test.rs : Write thorough end-to-end tests in `backend/grpc-server/tests/my_connector_payment_flows_test.rs` to cover all supported payment flows, including success and failure scenarios
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/{my_connector,*_connector}.rs : Define marker trait implementations for each supported flow (e.g., `PaymentAuthorizeV2`, `PaymentSyncV2`, `PaymentCapture`) in the connector file
Learnt from: Anurag-05-prog
Repo: juspay/connector-service PR: 188
File: backend/connector-integration/src/connectors/peachpayments.rs:306-315
Timestamp: 2025-09-24T08:45:31.195Z
Learning: In Peachpayments connector (backend/connector-integration/src/connectors/peachpayments.rs), the connector_transaction_id field has different types across flows: PaymentVoidData uses String directly, while PaymentsCaptureData uses ResponseId enum that requires pattern matching or get_connector_transaction_id() method.
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/{*_connector.rs,transformers.rs} : Ensure all matching arms have proper 'not implemented' error handling for connectors that don't support specific payment methods or payment method types
📚 Learning: 2025-11-25T05:32:43.442Z
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/tests/**/*_payment_flows_test.rs : Write thorough end-to-end tests in `backend/grpc-server/tests/my_connector_payment_flows_test.rs` to cover all supported payment flows, including success and failure scenarios
Applied to files:
.github/workflows/ci.yml
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: Run Tests
- GitHub Check: Compilation Check
- GitHub Check: Analyze (rust)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (1)
config/production.toml (1)
24-24:⚠️ Staging URL remains in production configuration (unresolved from prior review).This is a duplicate of a previous review comment: Line 24 still contains
https://stagegw.transnox.com/, which is a staging gateway endpoint. Production configurations must use the actual production TSYS gateway URL. The "stagegw" prefix explicitly indicates a test/staging environment, making this unsuitable for production deployments.Based on TSYS documentation, update this to a production endpoint such as
https://gateway.transit-pass.com/servlets/TransNox_API_Serveror the appropriate TSYS SSL gateway endpoint (e.g.,ssl3.vitalps.net:5003for Vital/TSYS SSL gateways).Apply this diff to correct the production endpoint:
- tsys.base_url = "https://stagegw.transnox.com/" + tsys.base_url = "https://gateway.transit-pass.com/servlets/TransNox_API_Server"
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (8)
backend/connector-integration/src/connectors.rs(1 hunks)backend/connector-integration/src/types.rs(1 hunks)backend/domain_types/src/connector_types.rs(2 hunks)backend/domain_types/src/types.rs(1 hunks)backend/grpc-server/tests/beta_tests/bluesnap_payment_flows_test.rs(7 hunks)config/development.toml(1 hunks)config/production.toml(1 hunks)config/sandbox.toml(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
- backend/domain_types/src/connector_types.rs
- config/sandbox.toml
- backend/connector-integration/src/types.rs
🧰 Additional context used
🧠 Learnings (13)
📓 Common learnings
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/tests/**/*_payment_flows_test.rs : Write thorough end-to-end tests in `backend/grpc-server/tests/my_connector_payment_flows_test.rs` to cover all supported payment flows, including success and failure scenarios
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/{my_connector,*_connector}.rs : Define marker trait implementations for each supported flow (e.g., `PaymentAuthorizeV2`, `PaymentSyncV2`, `PaymentCapture`) in the connector file
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/{*_connector.rs,transformers.rs} : Ensure all matching arms have proper 'not implemented' error handling for connectors that don't support specific payment methods or payment method types
📚 Learning: 2025-11-25T05:32:43.442Z
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Register new connectors in the `ConnectorEnum` in `backend/domain_types/src/connector_types.rs` and in the `convert_connector` function in `backend/connector-integration/src/types.rs`
Applied to files:
backend/connector-integration/src/connectors.rsbackend/domain_types/src/types.rs
📚 Learning: 2025-11-25T05:32:43.442Z
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/{my_connector,*_connector}.rs : Each new connector should follow the directory structure: `backend/connector-integration/src/connectors/my_connector.rs` for main connector logic, `backend/connector-integration/src/connectors/my_connector/transformers.rs` for request/response structs and TryFrom implementations, and `backend/grpc-server/tests/my_connector_payment_flows_test.rs` for end-to-end tests
Applied to files:
backend/connector-integration/src/connectors.rsbackend/domain_types/src/types.rs
📚 Learning: 2025-11-25T05:32:43.442Z
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/{my_connector,*_connector}.rs : Implement the `ConnectorCommon` trait for basic connector information including `id()`, `get_currency_unit()`, `get_auth_header()`, `base_url()`, and `build_error_response()` methods
Applied to files:
backend/connector-integration/src/connectors.rsbackend/domain_types/src/types.rsconfig/production.toml
📚 Learning: 2025-11-25T05:32:43.442Z
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/tests/**/*_payment_flows_test.rs : Write thorough end-to-end tests in `backend/grpc-server/tests/my_connector_payment_flows_test.rs` to cover all supported payment flows, including success and failure scenarios
Applied to files:
backend/grpc-server/tests/beta_tests/bluesnap_payment_flows_test.rs
📚 Learning: 2025-09-15T04:33:18.323Z
Learnt from: Shubhodip900
Repo: juspay/connector-service PR: 172
File: backend/grpc-server/tests/trustpay_payment_flows_test.rs:1-4
Timestamp: 2025-09-15T04:33:18.323Z
Learning: In test files for the juspay/connector-service repository, broad Clippy suppressions (#![allow(clippy::expect_used)], #![allow(clippy::unwrap_used)], #![allow(clippy::panic)]) are used as an established coding convention, as confirmed by the maintainer.
Applied to files:
backend/grpc-server/tests/beta_tests/bluesnap_payment_flows_test.rs
📚 Learning: 2025-11-25T05:32:43.442Z
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/{*_connector.rs,transformers.rs} : Ensure all matching arms have proper 'not implemented' error handling for connectors that don't support specific payment methods or payment method types
Applied to files:
backend/grpc-server/tests/beta_tests/bluesnap_payment_flows_test.rsbackend/domain_types/src/types.rs
📚 Learning: 2025-09-15T06:56:22.535Z
Learnt from: Shubhodip900
Repo: juspay/connector-service PR: 172
File: backend/connector-integration/src/connectors/trustpay.rs:747-762
Timestamp: 2025-09-15T06:56:22.535Z
Learning: In Trustpay connector (backend/connector-integration/src/connectors/trustpay.rs), the refund_connector_metadata field has different data formats in different contexts: RSync expects it to be already parsed as JSON Value (accessing directly with .get()), while other refund flows expect it to be a JSON string that needs serde_json::from_str() parsing. The extract_payment_method_from_refund_metadata helper is designed for the latter case and should not be used in RSync.
Applied to files:
backend/grpc-server/tests/beta_tests/bluesnap_payment_flows_test.rs
📚 Learning: 2025-11-25T05:32:43.442Z
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/transformers.rs : All new connectors must implement proper status mapping according to Hyperswitch standards, with default status always being `pending`
Applied to files:
backend/domain_types/src/types.rs
📚 Learning: 2025-11-25T05:32:43.442Z
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/{my_connector,*_connector}.rs : Define marker trait implementations for each supported flow (e.g., `PaymentAuthorizeV2`, `PaymentSyncV2`, `PaymentCapture`) in the connector file
Applied to files:
backend/domain_types/src/types.rs
📚 Learning: 2025-11-25T05:32:43.442Z
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/{*_connector.rs,transformers.rs} : Never hardcode values in connector code; always retrieve values from the request or available resources and implement proper error handling when values are not found
Applied to files:
backend/domain_types/src/types.rs
📚 Learning: 2025-10-13T07:57:48.659Z
Learnt from: iemyashasvi
Repo: juspay/connector-service PR: 194
File: config/development.toml:62-63
Timestamp: 2025-10-13T07:57:48.659Z
Learning: For Worldpay Vantiv development environment in config/development.toml, the correct URLs are:
- base_url: "https://transact.vantivprelive.com/vap/communicator/online"
- secondary_base_url: "https://onlinessr.vantivprelive.com"
Applied to files:
config/production.tomlconfig/development.toml
📚 Learning: 2025-10-13T08:02:24.195Z
Learnt from: iemyashasvi
Repo: juspay/connector-service PR: 194
File: config/sandbox.toml:54-55
Timestamp: 2025-10-13T08:02:24.195Z
Learning: For Worldpay Vantiv sandbox environment in config/sandbox.toml, the correct URLs are:
- base_url: "https://www.testvantivcnp.com/sandbox/new/sandbox/communicator/online"
- secondary_base_url: "https://www.testvantivcnp.com/sandbox"
Applied to files:
config/production.tomlconfig/development.toml
🔇 Additional comments (3)
backend/connector-integration/src/connectors.rs (1)
161-162: LGTM! Standard connector module exposure.The TSYS connector module exposure follows the established pattern used by all other connectors in this file. This correctly makes the
Tsystype publicly available for use throughout the codebase.Based on learnings, ensure that:
- TSYS is registered in
ConnectorEnuminbackend/domain_types/src/connector_types.rs- The
convert_connectorfunction inbackend/connector-integration/src/types.rsincludes the TSYS mappingbackend/grpc-server/tests/beta_tests/bluesnap_payment_flows_test.rs (1)
250-250: These Bluesnap tests were already disabled before this PR—this change did not introduce the#[ignore]markers.The git history shows this TSYS integration PR only merged changes without modifying
bluesnap_payment_flows_test.rs. The#[ignore]attributes were pre-existing, and the tests reside in thebeta_tests/directory, indicating these are intentionally in a beta/experimental state as part of the original Bluesnap connector integration. The review comment incorrectly attributes the test disabling to this PR when it predates it.Likely an incorrect or invalid review comment.
backend/domain_types/src/types.rs (1)
105-163: TSYS connector params field is consistent; just verify wiring and config coverageThe added
pub tsys: ConnectorParamsfollows the existing pattern onConnectorsand looks fine structurally. The main things to double‑check outside this file:
- TSYS is registered everywhere required for a new connector (e.g.,
ConnectorEnuminconnector_types.rs,convert_connectorinconnector-integration/src/types.rs, marker traits, transformers, etc.).- All relevant config environments (development/sandbox/production and any test/local configs) define a
tsysentry under connectors soConnectorsdeserialization doesn’t fail at startup.Based on learnings, new connectors should be consistently registered and wired across enums, types, and config.
backend/connector-integration/src/connectors/tsys/transformers.rs
Outdated
Show resolved
Hide resolved
backend/connector-integration/src/connectors/tsys/transformers.rs
Outdated
Show resolved
Hide resolved
backend/connector-integration/src/connectors/tsys/transformers.rs
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
♻️ Duplicate comments (4)
backend/connector-integration/src/connectors/tsys.rs (1)
348-362: Add explicit forward slash in URL construction for robustness.This was flagged in a previous review. The URL construction relies on the base URL having a trailing slash. Add an explicit separator to prevent malformed URLs if configuration assumptions change:
- Ok(format!("{}servlets/transnox_api_server", base_url)) + Ok(format!("{}/servlets/transnox_api_server", base_url.trim_end_matches('/')))Apply the same fix to both
get_url_paymentsandget_url_refunds.backend/connector-integration/src/connectors/tsys/transformers.rs (3)
475-489: String-based transaction type matching is fragile.This was flagged in a previous review. The
contains("Auth-Only")check is loose and could match unintended values. Consider:
- Using exact string comparison:
item.transaction_type == "Auth-Only"- If TSYS has a defined set of transaction types, create an enum and parse into it
TsysTransactionStatus::Approved => { - if item.transaction_type.contains("Auth-Only") { + if item.transaction_type == "Auth-Only" { Self::Authorized } else { Self::Charged } }
143-145: Update terminal capability values for e-commerce/CNP transactions.This was flagged in a previous review. The hardcoded terminal values are designed for physical chip card readers, not card-not-present e-commerce transactions:
ICC_CHIP_READ_ONLYimplies physical chip reading capabilityON_MERCHANT_PREMISES_ATTENDEDimplies in-person attended transactionFor CNP e-commerce, TSYS API typically expects values indicating card-not-present capability. Please verify the correct values from TSYS TransNox API documentation for online transactions.
711-720: Missingdeveloper_idfield inTsysReturnRequest.This was flagged in a previous review.
TsysReturnRequestlacks thedeveloper_idfield that is present in all other TSYS request structs (TsysPaymentAuthSaleRequest,TsysSearchTransactionRequest,TsysCaptureRequest,TsysCancelRequest).pub struct TsysReturnRequest { #[serde(rename = "deviceID")] device_id: Secret<String>, transaction_key: Secret<String>, transaction_amount: StringMinorUnit, #[serde(rename = "transactionID")] transaction_id: String, + #[serde(rename = "developerID")] + developer_id: Secret<String>, }Also update the
TryFromimplementation at lines 753-762 to includedeveloper_id: auth.developer_id.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (9)
backend/connector-integration/src/connectors.rs(1 hunks)backend/connector-integration/src/connectors/tsys.rs(1 hunks)backend/connector-integration/src/connectors/tsys/transformers.rs(1 hunks)backend/connector-integration/src/types.rs(1 hunks)backend/domain_types/src/connector_types.rs(2 hunks)backend/domain_types/src/types.rs(1 hunks)config/development.toml(1 hunks)config/production.toml(1 hunks)config/sandbox.toml(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (6)
- backend/domain_types/src/connector_types.rs
- backend/connector-integration/src/connectors.rs
- backend/connector-integration/src/types.rs
- backend/domain_types/src/types.rs
- config/development.toml
- config/production.toml
🧰 Additional context used
🧠 Learnings (26)
📓 Common learnings
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/tests/**/*_payment_flows_test.rs : Write thorough end-to-end tests in `backend/grpc-server/tests/my_connector_payment_flows_test.rs` to cover all supported payment flows, including success and failure scenarios
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/{my_connector,*_connector}.rs : Define marker trait implementations for each supported flow (e.g., `PaymentAuthorizeV2`, `PaymentSyncV2`, `PaymentCapture`) in the connector file
Learnt from: Anurag-05-prog
Repo: juspay/connector-service PR: 188
File: backend/connector-integration/src/connectors/peachpayments.rs:306-315
Timestamp: 2025-09-24T08:45:31.195Z
Learning: In Peachpayments connector (backend/connector-integration/src/connectors/peachpayments.rs), the connector_transaction_id field has different types across flows: PaymentVoidData uses String directly, while PaymentsCaptureData uses ResponseId enum that requires pattern matching or get_connector_transaction_id() method.
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/{*_connector.rs,transformers.rs} : Ensure all matching arms have proper 'not implemented' error handling for connectors that don't support specific payment methods or payment method types
📚 Learning: 2025-10-13T08:02:24.195Z
Learnt from: iemyashasvi
Repo: juspay/connector-service PR: 194
File: config/sandbox.toml:54-55
Timestamp: 2025-10-13T08:02:24.195Z
Learning: For Worldpay Vantiv sandbox environment in config/sandbox.toml, the correct URLs are:
- base_url: "https://www.testvantivcnp.com/sandbox/new/sandbox/communicator/online"
- secondary_base_url: "https://www.testvantivcnp.com/sandbox"
Applied to files:
config/sandbox.toml
📚 Learning: 2025-10-13T07:57:48.659Z
Learnt from: iemyashasvi
Repo: juspay/connector-service PR: 194
File: config/development.toml:62-63
Timestamp: 2025-10-13T07:57:48.659Z
Learning: For Worldpay Vantiv development environment in config/development.toml, the correct URLs are:
- base_url: "https://transact.vantivprelive.com/vap/communicator/online"
- secondary_base_url: "https://onlinessr.vantivprelive.com"
Applied to files:
config/sandbox.toml
📚 Learning: 2025-11-25T05:32:43.442Z
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/{my_connector,*_connector}.rs : Define marker trait implementations for each supported flow (e.g., `PaymentAuthorizeV2`, `PaymentSyncV2`, `PaymentCapture`) in the connector file
Applied to files:
backend/connector-integration/src/connectors/tsys.rsbackend/connector-integration/src/connectors/tsys/transformers.rs
📚 Learning: 2025-11-25T05:32:43.442Z
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/{my_connector,*_connector}.rs : Each new connector should follow the directory structure: `backend/connector-integration/src/connectors/my_connector.rs` for main connector logic, `backend/connector-integration/src/connectors/my_connector/transformers.rs` for request/response structs and TryFrom implementations, and `backend/grpc-server/tests/my_connector_payment_flows_test.rs` for end-to-end tests
Applied to files:
backend/connector-integration/src/connectors/tsys.rs
📚 Learning: 2025-11-25T05:32:43.442Z
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/{*_connector.rs,transformers.rs} : Ensure all matching arms have proper 'not implemented' error handling for connectors that don't support specific payment methods or payment method types
Applied to files:
backend/connector-integration/src/connectors/tsys.rsbackend/connector-integration/src/connectors/tsys/transformers.rs
📚 Learning: 2025-11-25T05:32:43.442Z
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/{my_connector,*_connector}.rs : Implement the `ConnectorCommon` trait for basic connector information including `id()`, `get_currency_unit()`, `get_auth_header()`, `base_url()`, and `build_error_response()` methods
Applied to files:
backend/connector-integration/src/connectors/tsys.rs
📚 Learning: 2025-11-25T05:32:43.442Z
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/transformers.rs : In `transformers.rs`, define Rust structs that exactly match the JSON (or other format) structure of the connector's API for requests, responses, and error responses
Applied to files:
backend/connector-integration/src/connectors/tsys.rsbackend/connector-integration/src/connectors/tsys/transformers.rs
📚 Learning: 2025-11-25T05:32:43.442Z
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Register new connectors in the `ConnectorEnum` in `backend/domain_types/src/connector_types.rs` and in the `convert_connector` function in `backend/connector-integration/src/types.rs`
Applied to files:
backend/connector-integration/src/connectors/tsys.rs
📚 Learning: 2025-11-25T05:32:43.442Z
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/{my_connector,*_connector}.rs : The main connector file should use the macro framework to define the connector's structure, supported flows, and flow-specific logic through `create_all_prerequisites!` and `macro_connector_implementation!` macros
Applied to files:
backend/connector-integration/src/connectors/tsys.rs
📚 Learning: 2025-11-25T05:32:43.442Z
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/tests/**/*_payment_flows_test.rs : Write thorough end-to-end tests in `backend/grpc-server/tests/my_connector_payment_flows_test.rs` to cover all supported payment flows, including success and failure scenarios
Applied to files:
backend/connector-integration/src/connectors/tsys.rsbackend/connector-integration/src/connectors/tsys/transformers.rs
📚 Learning: 2025-11-25T05:32:43.442Z
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/transformers.rs : All new connectors must implement proper status mapping according to Hyperswitch standards, with default status always being `pending`
Applied to files:
backend/connector-integration/src/connectors/tsys.rsbackend/connector-integration/src/connectors/tsys/transformers.rs
📚 Learning: 2025-09-24T08:45:31.195Z
Learnt from: Anurag-05-prog
Repo: juspay/connector-service PR: 188
File: backend/connector-integration/src/connectors/peachpayments.rs:306-315
Timestamp: 2025-09-24T08:45:31.195Z
Learning: In Peachpayments connector (backend/connector-integration/src/connectors/peachpayments.rs), the connector_transaction_id field has different types across flows: PaymentVoidData uses String directly, while PaymentsCaptureData uses ResponseId enum that requires pattern matching or get_connector_transaction_id() method.
Applied to files:
backend/connector-integration/src/connectors/tsys.rsbackend/connector-integration/src/connectors/tsys/transformers.rs
📚 Learning: 2025-09-15T06:56:22.535Z
Learnt from: Shubhodip900
Repo: juspay/connector-service PR: 172
File: backend/connector-integration/src/connectors/trustpay.rs:747-762
Timestamp: 2025-09-15T06:56:22.535Z
Learning: In Trustpay connector (backend/connector-integration/src/connectors/trustpay.rs), the refund_connector_metadata field has different data formats in different contexts: RSync expects it to be already parsed as JSON Value (accessing directly with .get()), while other refund flows expect it to be a JSON string that needs serde_json::from_str() parsing. The extract_payment_method_from_refund_metadata helper is designed for the latter case and should not be used in RSync.
Applied to files:
backend/connector-integration/src/connectors/tsys.rsbackend/connector-integration/src/connectors/tsys/transformers.rs
📚 Learning: 2025-11-25T05:32:43.442Z
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/{*_connector.rs,transformers.rs} : Never hardcode values in connector code; always retrieve values from the request or available resources and implement proper error handling when values are not found
Applied to files:
backend/connector-integration/src/connectors/tsys.rsbackend/connector-integration/src/connectors/tsys/transformers.rs
📚 Learning: 2025-11-25T05:32:43.442Z
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/transformers.rs : Ensure correct mapping of `response_transaction_id` and `redirect_urls` with respective fields in router data within response handlers
Applied to files:
backend/connector-integration/src/connectors/tsys.rsbackend/connector-integration/src/connectors/tsys/transformers.rs
📚 Learning: 2025-11-25T05:32:43.442Z
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/transformers.rs : Create utility functions in transformers for common operations including email handling, address processing, return URL construction, and connector transaction ID extraction
Applied to files:
backend/connector-integration/src/connectors/tsys.rsbackend/connector-integration/src/connectors/tsys/transformers.rs
📚 Learning: 2025-09-15T04:33:18.323Z
Learnt from: Shubhodip900
Repo: juspay/connector-service PR: 172
File: backend/grpc-server/tests/trustpay_payment_flows_test.rs:1-4
Timestamp: 2025-09-15T04:33:18.323Z
Learning: In test files for the juspay/connector-service repository, broad Clippy suppressions (#![allow(clippy::expect_used)], #![allow(clippy::unwrap_used)], #![allow(clippy::panic)]) are used as an established coding convention, as confirmed by the maintainer.
Applied to files:
backend/connector-integration/src/connectors/tsys.rs
📚 Learning: 2025-11-25T05:32:43.442Z
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/transformers.rs : Store critical identifiers (`connector_transaction_id`, `reference_id`, `mandate_id`) in the `handle_response` method for all flows
Applied to files:
backend/connector-integration/src/connectors/tsys.rsbackend/connector-integration/src/connectors/tsys/transformers.rs
📚 Learning: 2025-09-26T08:08:52.624Z
Learnt from: Shubhodip900
Repo: juspay/connector-service PR: 193
File: backend/connector-integration/src/connectors/rapyd/transformers.rs:505-524
Timestamp: 2025-09-26T08:08:52.624Z
Learning: In Rapyd connector (backend/connector-integration/src/connectors/rapyd/transformers.rs), the next_action field in ResponseData struct is required (not Optional) as per Rapyd API documentation. This field always appears in payment responses with values like "3d_verification", "pending_capture", "pending_confirmation", or "not_applicable", consistent with hyperswitch connector patterns.
Applied to files:
backend/connector-integration/src/connectors/tsys.rsbackend/connector-integration/src/connectors/tsys/transformers.rs
📚 Learning: 2025-11-25T05:32:43.442Z
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/transformers.rs : Implement `TryFrom` trait to convert between `RouterDataV2` and connector-specific request/response structs in `transformers.rs`
Applied to files:
backend/connector-integration/src/connectors/tsys.rsbackend/connector-integration/src/connectors/tsys/transformers.rs
📚 Learning: 2025-11-25T05:32:43.442Z
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/transformers.rs : Handle all types of errors properly using Enums in connector error responses, supporting different error structures, string errors, and empty body responses
Applied to files:
backend/connector-integration/src/connectors/tsys/transformers.rs
📚 Learning: 2025-09-24T10:18:33.115Z
Learnt from: Anurag-05-prog
Repo: juspay/connector-service PR: 188
File: backend/connector-integration/src/connectors/peachpayments/transformers.rs:50-56
Timestamp: 2025-09-24T10:18:33.115Z
Learning: In Peach Payments connector API (backend/connector-integration/src/connectors/peachpayments/transformers.rs), the authorize request struct uses the field "charge_method" as expected by the Peach Payments API, not "paymentType" or other variations.
Applied to files:
backend/connector-integration/src/connectors/tsys/transformers.rs
📚 Learning: 2025-11-25T05:32:43.442Z
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/transformers.rs : Fields that have a limited set of possible values should be defined as enum types rather than as `String` or other generic types
Applied to files:
backend/connector-integration/src/connectors/tsys/transformers.rs
📚 Learning: 2025-11-25T05:32:43.442Z
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/transformers.rs : Use `MinorUnit` type for amount fields in connector request/response structs instead of primitive types like `i64`
Applied to files:
backend/connector-integration/src/connectors/tsys/transformers.rs
📚 Learning: 2025-11-25T05:32:43.442Z
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/transformers.rs : When a field is required by a connector (e.g., email, name, address, state, country), validate its presence and return an error if missing, rather than relying on the connector to handle it
Applied to files:
backend/connector-integration/src/connectors/tsys/transformers.rs
🧬 Code graph analysis (1)
backend/connector-integration/src/connectors/tsys/transformers.rs (2)
backend/domain_types/src/types.rs (6)
RawCardNumber(1012-1012)from(6519-6533)from(7276-7282)from(7395-7400)from(7464-7500)from(7504-7509)backend/domain_types/src/connector_types.rs (1)
from(2362-2558)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: Compilation Check
- GitHub Check: Run Tests
- GitHub Check: Analyze (rust)
🔇 Additional comments (12)
config/sandbox.toml (1)
86-86: LGTM!The TSYS sandbox base URL is correctly configured with a trailing slash, consistent with other connector URLs in this file.
backend/connector-integration/src/connectors/tsys.rs (5)
51-102: LGTM!The
ConnectorCommonimplementation follows established patterns with proper error response handling and appropriate body-based authentication approach for TSYS.
278-364: LGTM!The macro-based flow definitions correctly wire up all six supported flows (Authorize, PSync, Capture, Void, Refund, RSync) with appropriate request/response types and the
StringMinorUnitamount converter. Based on learnings, marker trait implementations are properly defined for each supported flow.
369-504: LGTM!The flow implementations correctly use the macro framework with consistent configuration across all six flows. Payment flows delegate to
get_url_paymentsand refund flows toget_url_refundsas expected.
540-812: LGTM!The stub implementations for unsupported flows (CreateOrder, CreateAccessToken, SetupMandate, etc.) correctly satisfy the
ConnectorServiceTraitrequirements without providing actual implementations. Based on learnings, this follows the established pattern for connectors that don't support specific flows.
506-536: LGTM!Marker trait implementations correctly declare support for all six flows: Authorize, PSync, Capture, Void, Refund, and RSync. Based on learnings, this follows the guideline to define marker trait implementations for each supported flow.
backend/connector-integration/src/connectors/tsys/transformers.rs (6)
27-53: LGTM!The
TsysAuthTypecorrectly extracts authentication credentials fromSignatureKeywith proper error handling for unsupported auth types.
226-237: LGTM!The
get_payments_responsehelper correctly constructsPaymentsResponseDatawith theconnector_transaction_idproperly stored in bothresource_idandconnector_response_reference_id. Based on learnings, this follows the guideline to store critical identifiers in response handlers.
539-575: LGTM!The PSync response handling correctly preserves the existing status on error responses and derives the new status from
TsysTransactionDetailson success. This is appropriate behavior for a sync flow.
581-646: LGTM!The Capture flow request correctly extracts the connector transaction ID using
get_connector_transaction_id()and includes all required fields includingdeveloper_id. Based on learnings aboutPaymentsCaptureDatausingResponseIdenum, this implementation properly handles the type difference.
652-705: LGTM!The Void flow correctly uses direct string clone for
connector_transaction_id. Based on learnings,PaymentVoidDatausesStringdirectly (unlikePaymentsCaptureDatawhich usesResponseIdenum), so this implementation is correct.
873-905: LGTM!The RSync flow correctly uses
connector_refund_idto search for refund status and reuses the sync request/response structures appropriately.
| #[derive(Debug, Serialize)] | ||
| pub enum TsysPaymentsRequest<T: PaymentMethodDataTypes> { | ||
| Auth(TsysPaymentAuthSaleRequest<T>), | ||
| Sale(TsysPaymentAuthSaleRequest<T>), | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
# Find the TsysPaymentAuthSaleRequest struct definition
rg -A 10 "struct TsysPaymentAuthSaleRequest" backend/connector-integration/src/connectors/tsys/Repository: juspay/connector-service
Length of output: 1207
🏁 Script executed:
# Find how TsysPaymentsRequest is used
rg -B 3 -A 3 "TsysPaymentsRequest::" backend/connector-integration/src/connectors/tsys/Repository: juspay/connector-service
Length of output: 50
🏁 Script executed:
# Check for any existing serde attributes on the enum or related structs
rg -B 5 "enum TsysPaymentsRequest" backend/connector-integration/src/connectors/tsys/Repository: juspay/connector-service
Length of output: 747
🏁 Script executed:
# Read the full transformers.rs file to understand the context
wc -l backend/connector-integration/src/connectors/tsys/transformers.rsRepository: juspay/connector-service
Length of output: 137
🏁 Script executed:
# Look for where TsysPaymentsRequest might be used (search in the entire tsys module)
fd . backend/connector-integration/src/connectors/tsys/ -type fRepository: juspay/connector-service
Length of output: 238
🏁 Script executed:
# Search more broadly for how this request is built
rg "TsysPaymentAuthSaleRequest" backend/connector-integration/src/connectors/tsys/ -A 2Repository: juspay/connector-service
Length of output: 1117
🏁 Script executed:
# Check the create_request method or similar transformers
rg -B 5 -A 15 "fn.*create.*request" backend/connector-integration/src/connectors/tsys/transformers.rs | head -100Repository: juspay/connector-service
Length of output: 50
🏁 Script executed:
# Read the enum and surrounding context
sed -n '50,80p' backend/connector-integration/src/connectors/tsys/transformers.rsRepository: juspay/connector-service
Length of output: 1080
🏁 Script executed:
# Look for where the enum is serialized to JSON or sent in requests
rg "serde_json\|to_string\|json!" backend/connector-integration/src/connectors/tsys/ -B 2 -A 2Repository: juspay/connector-service
Length of output: 50
🏁 Script executed:
# Check if there's a mod.rs or lib.rs that shows the connector structure
cat backend/connector-integration/src/connectors/tsys/mod.rs 2>/dev/null || echo "File not found"Repository: juspay/connector-service
Length of output: 82
🏁 Script executed:
# Search for test files related to TSYS
fd "test" backend/connector-integration/src/connectors/tsys/ 2>/dev/null || rg "mod test" backend/connector-integration/src/connectors/tsys/Repository: juspay/connector-service
Length of output: 50
🏁 Script executed:
# Search for where Auth and Sale variants are constructed
rg "TsysPaymentsRequest::" backend/connector-integration/src/connectors/tsys/ -B 5 -A 5Repository: juspay/connector-service
Length of output: 50
🏁 Script executed:
# Look for the PaymentsConnectorIntegrationImpl trait or connector implementation
rg -B 3 -A 10 "is_auto_capture\|Auth\|Sale" backend/connector-integration/src/connectors/tsys/transformers.rs | head -80Repository: juspay/connector-service
Length of output: 50
🏁 Script executed:
# Check for any mock or test files that show expected request format
find backend/connector-integration -path "*tsys*test*" -o -path "*tsys*mock*" 2>/dev/nullRepository: juspay/connector-service
Length of output: 50
🏁 Script executed:
# Search for merchant account or auth connector trait implementation
rg "impl.*Connector.*for.*Tsys" backend/connector-integration/src/connectors/tsys/ -A 20Repository: juspay/connector-service
Length of output: 2127
🏁 Script executed:
# Look for ForeignFrom or ForeignTryFrom implementations that might use TsysPaymentsRequest
rg "ForeignFrom|ForeignTryFrom" backend/connector-integration/src/connectors/tsys/ -B 2 -A 10Repository: juspay/connector-service
Length of output: 50
🏁 Script executed:
# Search for where Auth or Sale decisions are made
rg -B 10 -A 10 "Auth\(" backend/connector-integration/src/connectors/tsys/transformers.rs | head -80Repository: juspay/connector-service
Length of output: 1511
🏁 Script executed:
# Look at the PaymentAuthorizeRequestData to TsysPaymentsRequest conversion
rg -B 5 -A 20 "impl.*From.*PaymentAuthorizeRequestData\|impl.*TryFrom.*PaymentAuthorizeRequestData" backend/connector-integration/src/connectors/tsys/transformers.rsRepository: juspay/connector-service
Length of output: 50
🏁 Script executed:
# Search more broadly for where the enum variants are matched/used
sed -n '140,200p' backend/connector-integration/src/connectors/tsys/transformers.rsRepository: juspay/connector-service
Length of output: 1908
🏁 Script executed:
# Find the main connector file
ls -la backend/connector-integration/src/connectors/tsys/Repository: juspay/connector-service
Length of output: 250
🏁 Script executed:
# Look for where the request is actually sent
rg "post\|POST\|request" backend/connector-integration/src/connectors/tsys/ -i | head -30Repository: juspay/connector-service
Length of output: 50
🏁 Script executed:
# Check the connector trait implementation
rg "impl.*Connector" backend/connector-integration/src/connectors/tsys/ -A 5Repository: juspay/connector-service
Length of output: 728
🏁 Script executed:
# Look at the entire authorization request building logic
sed -n '100,160p' backend/connector-integration/src/connectors/tsys/transformers.rsRepository: juspay/connector-service
Length of output: 2522
🏁 Script executed:
# Search for PaymentRequest or similar that might use TsysPaymentsRequest
rg "PaymentRequest\|RequestContainer" backend/connector-integration/src/connectors/tsys/ -B 3 -A 3Repository: juspay/connector-service
Length of output: 50
🏁 Script executed:
# Look for ForeignFrom or ForeignTryFrom that creates a request from TsysPaymentsRequest
rg "impl.*Foreign" backend/connector-integration/src/connectors/tsys/transformers.rs -A 15 | head -80Repository: juspay/connector-service
Length of output: 50
🏁 Script executed:
# Search for where requests are serialized in the broader codebase for similar patterns
rg "enum.*Request.*\{" backend/connector-integration/src/connectors/ | grep -i "auth\|sale\|Auth\|Sale" | head -10Repository: juspay/connector-service
Length of output: 494
🏁 Script executed:
# Look at how other connectors handle Auth/Sale or similar variants
rg "#\[derive.*Serialize\]" backend/connector-integration/src/connectors/ -A 3 | grep -B 3 "enum.*{" | head -50Repository: juspay/connector-service
Length of output: 50
🏁 Script executed:
# Search for all references to TsysPaymentsRequest in transformers.rs
rg "TsysPaymentsRequest" backend/connector-integration/src/connectors/tsys/transformers.rs -nRepository: juspay/connector-service
Length of output: 165
🏁 Script executed:
# Look at the end of the file to see if there's integration code
tail -100 backend/connector-integration/src/connectors/tsys/transformers.rsRepository: juspay/connector-service
Length of output: 3476
🏁 Script executed:
# Search for how Paytm (which also has AuthorizeRequest enum) handles it
rg -B 3 -A 10 "enum PaytmAuthorizeRequest" backend/connector-integration/src/connectors/paytm/ -A 10Repository: juspay/connector-service
Length of output: 1302
🏁 Script executed:
# Check if there's any mention of "untagged" or custom serde in similar connectors
rg "serde\(.*tag\|serde\(.*untagged" backend/connector-integration/src/connectors/stripe/transformers.rs | head -20Repository: juspay/connector-service
Length of output: 50
🏁 Script executed:
# Search for any documentation or comments about TSYS API format
rg "TSYS\|tsys" backend/connector-integration/src/connectors/tsys/transformers.rs -i -B 2 -A 2 | head -50Repository: juspay/connector-service
Length of output: 50
🏁 Script executed:
# Look for any test fixtures or documentation about expected TSYS request format
find backend -type f -name "*.md" -o -name "*.txt" | xargs grep -l "TSYS\|tsys" 2>/dev/null | head -5Repository: juspay/connector-service
Length of output: 50
🏁 Script executed:
# Check for any example payloads or documentation in comments
grep -r "TSYS\|transactionID\|deviceID" backend/connector-integration/src/connectors/tsys/ -B 3 -A 3 | head -60Repository: juspay/connector-service
Length of output: 5475
🏁 Script executed:
# Look for how the request is actually sent - search for HTTP client usage patterns
rg "build_request\|make_request\|post_request" backend/connector-integration/src/connectors/tsys/ -A 5Repository: juspay/connector-service
Length of output: 50
🏁 Script executed:
# Search for where TsysPaymentsRequest is actually serialized or used in requests
rg -B 10 -A 10 "TryFrom.*PaymentFlowData.*for TsysPaymentsRequest" backend/connector-integration/src/connectors/tsys/transformers.rs | tail -30Repository: juspay/connector-service
Length of output: 50
🏁 Script executed:
# Check if there's any custom serialization code for the enum
rg "Serialize\|serialize" backend/connector-integration/src/connectors/tsys/transformers.rs | grep -v "derive\|Deserialize"Repository: juspay/connector-service
Length of output: 50
🏁 Script executed:
# Look at how Paytm actually uses their untagged request enum
rg -B 5 -A 15 "PaytmAuthorizeRequest" backend/connector-integration/src/connectors/paytm/ | head -80Repository: juspay/connector-service
Length of output: 8504
🏁 Script executed:
# Check if there's any request building pattern that might handle enum serialization
rg "impl.*Serialize\|fn serialize" backend/connector-integration/src/connectors/tsys/ -B 2 -A 10Repository: juspay/connector-service
Length of output: 50
🏁 Script executed:
# Verify Paytm's #[serde(untagged)] is actually in the definition
sed -n '1,50p' backend/connector-integration/src/connectors/paytm/request.rs | grep -B 5 -A 5 "PaytmAuthorizeRequest"Repository: juspay/connector-service
Length of output: 50
Add #[serde(untagged)] attribute to serialize Auth/Sale variants without wrapper keys.
The TsysPaymentsRequest enum will serialize with variant names as keys (e.g., {"Auth": {...}}), but TSYS API expects a flat request structure matching the inner TsysPaymentAuthSaleRequest fields. Similar enums in this codebase (e.g., Paytm's PaytmAuthorizeRequest) use #[serde(untagged)] to flatten enum variants. Add this attribute to match the expected TSYS API format.
🤖 Prompt for AI Agents
In backend/connector-integration/src/connectors/tsys/transformers.rs around
lines 59-63, the TsysPaymentsRequest enum is missing the serde attribute so it
currently serializes with wrapper keys like {"Auth": {...}}, but TSYS expects
the inner TsysPaymentAuthSaleRequest fields at the top level; add
#[serde(untagged)] above the enum definition to serialize variants without
wrapper keys, then run tests/serialization checks to verify the produced JSON
matches the flat TSYS request format.
backend/connector-integration/src/connectors/tsys/transformers.rs
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (4)
backend/connector-integration/src/connectors/tsys/transformers.rs (4)
59-63: Add#[serde(untagged)]to serialize enum variants without wrapper keys.The
TsysPaymentsRequestenum lacks the#[serde(untagged)]attribute, causing it to serialize with variant names as keys (e.g.,{"Auth": {...}}). TSYS API expects a flat structure matching the innerTsysPaymentAuthSaleRequestfields directly.Apply this diff:
+#[serde(untagged)] #[derive(Debug, Serialize)] pub enum TsysPaymentsRequest<T: PaymentMethodDataTypes> { Auth(TsysPaymentAuthSaleRequest<T>), Sale(TsysPaymentAuthSaleRequest<T>), }
125-147: Terminal capability values inappropriate for card-not-present e-commerce transactions.The hardcoded values are designed for physical chip card readers, not CNP transactions:
terminal_capability: "ICC_CHIP_READ_ONLY"implies a chip readerterminal_operating_environment: "ON_MERCHANT_PREMISES_ATTENDED"implies physical presenceFor e-commerce CNP transactions, TSYS TransNox API requires CNP-specific values.
TSYS TransNox API card not present CNP e-commerce terminal capability field values
496-510: String-based transaction type matching uses loose substring check.Line 500 uses
contains("Auth-Only")which can match partial strings. Thetransaction_typefield should either:
- Be defined as an enum with known TSYS transaction type values, or
- Use exact matching (
==) instead of substring matchingPer coding guidelines, fields with limited possible values should be enum types rather than
String.Consider defining an enum for known transaction types:
+#[derive(Debug, Clone, Deserialize, Serialize)] +#[serde(rename_all = "kebab-case")] +pub enum TsysTransactionType { + AuthOnly, + Sale, + Capture, + Void, + Return, + #[serde(other)] + Unknown, +} + #[derive(Debug, Clone, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct TsysTransactionDetails { #[serde(rename = "transactionID")] transaction_id: String, - transaction_type: String, + transaction_type: TsysTransactionType, transaction_status: TsysTransactionStatus, } impl From<TsysTransactionDetails> for AttemptStatus { fn from(item: TsysTransactionDetails) -> Self { match item.transaction_status { TsysTransactionStatus::Approved => { - if item.transaction_type.contains("Auth-Only") { + match item.transaction_type { + TsysTransactionType::AuthOnly => Self::Authorized, + _ => Self::Charged, - Self::Authorized - } else { - Self::Charged } } TsysTransactionStatus::Void => Self::Voided, TsysTransactionStatus::Declined => Self::Failure, } } }
732-741: Missingdeveloper_idfield in refund request structure.
TsysReturnRequestlacks thedeveloper_idfield present in other TSYS request structs (TsysCaptureRequestline 612,TsysCancelRequestline 682,TsysSearchTransactionRequestline 427). This inconsistency should be resolved.Apply this diff:
#[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct TsysReturnRequest { #[serde(rename = "deviceID")] device_id: Secret<String>, transaction_key: Secret<String>, transaction_amount: StringMinorUnit, #[serde(rename = "transactionID")] transaction_id: String, + #[serde(rename = "developerID")] + developer_id: Secret<String>, }Then update the TryFrom implementation around line 774:
let return_request = TsysReturnRequest { device_id: auth.device_id, transaction_key: auth.transaction_key, transaction_amount: TsysAmountConvertor::convert( MinorUnit(item.request.refund_amount), item.request.currency, ) .change_context(errors::ConnectorError::RequestEncodingFailed)?, transaction_id: item.request.connector_transaction_id.clone(), + developer_id: auth.developer_id, };
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (8)
backend/connector-integration/src/connectors.rs(1 hunks)backend/connector-integration/src/connectors/tsys/transformers.rs(1 hunks)backend/connector-integration/src/types.rs(1 hunks)backend/domain_types/src/connector_types.rs(2 hunks)backend/domain_types/src/types.rs(1 hunks)config/development.toml(1 hunks)config/production.toml(1 hunks)config/sandbox.toml(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (6)
- backend/connector-integration/src/types.rs
- backend/domain_types/src/connector_types.rs
- config/development.toml
- backend/domain_types/src/types.rs
- config/production.toml
- config/sandbox.toml
🧰 Additional context used
🧠 Learnings (24)
📓 Common learnings
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/tests/**/*_payment_flows_test.rs : Write thorough end-to-end tests in `backend/grpc-server/tests/my_connector_payment_flows_test.rs` to cover all supported payment flows, including success and failure scenarios
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/{my_connector,*_connector}.rs : Define marker trait implementations for each supported flow (e.g., `PaymentAuthorizeV2`, `PaymentSyncV2`, `PaymentCapture`) in the connector file
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/{my_connector,*_connector}.rs : Each new connector should follow the directory structure: `backend/connector-integration/src/connectors/my_connector.rs` for main connector logic, `backend/connector-integration/src/connectors/my_connector/transformers.rs` for request/response structs and TryFrom implementations, and `backend/grpc-server/tests/my_connector_payment_flows_test.rs` for end-to-end tests
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Register new connectors in the `ConnectorEnum` in `backend/domain_types/src/connector_types.rs` and in the `convert_connector` function in `backend/connector-integration/src/types.rs`
📚 Learning: 2025-11-25T05:32:43.442Z
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Register new connectors in the `ConnectorEnum` in `backend/domain_types/src/connector_types.rs` and in the `convert_connector` function in `backend/connector-integration/src/types.rs`
Applied to files:
backend/connector-integration/src/connectors.rs
📚 Learning: 2025-11-25T05:32:43.442Z
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/{my_connector,*_connector}.rs : Each new connector should follow the directory structure: `backend/connector-integration/src/connectors/my_connector.rs` for main connector logic, `backend/connector-integration/src/connectors/my_connector/transformers.rs` for request/response structs and TryFrom implementations, and `backend/grpc-server/tests/my_connector_payment_flows_test.rs` for end-to-end tests
Applied to files:
backend/connector-integration/src/connectors.rsbackend/connector-integration/src/connectors/tsys/transformers.rs
📚 Learning: 2025-12-09T11:54:49.256Z
Learnt from: siaass842
Repo: juspay/connector-service PR: 347
File: backend/connector-integration/src/connectors/tsys/transformers.rs:778-786
Timestamp: 2025-12-09T11:54:49.256Z
Learning: In the TSYS connector (backend/connector-integration/src/connectors/tsys/transformers.rs), the TSYS/TransNox API returns TsysTransactionStatus::Void to indicate successfully completed refunds in RefundSync flow. Therefore, TsysTransactionStatus::Void should be mapped to RefundStatus::Success for refund operations, which differs from typical payment processor semantics where VOID indicates pre-settlement cancellation.
Applied to files:
backend/connector-integration/src/connectors.rsbackend/connector-integration/src/connectors/tsys/transformers.rs
📚 Learning: 2025-11-25T05:32:43.442Z
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/{my_connector,*_connector}.rs : Implement the `ConnectorCommon` trait for basic connector information including `id()`, `get_currency_unit()`, `get_auth_header()`, `base_url()`, and `build_error_response()` methods
Applied to files:
backend/connector-integration/src/connectors.rs
📚 Learning: 2025-11-25T05:32:43.442Z
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/{my_connector,*_connector}.rs : Define marker trait implementations for each supported flow (e.g., `PaymentAuthorizeV2`, `PaymentSyncV2`, `PaymentCapture`) in the connector file
Applied to files:
backend/connector-integration/src/connectors.rsbackend/connector-integration/src/connectors/tsys/transformers.rs
📚 Learning: 2025-11-25T05:32:43.442Z
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/transformers.rs : In `transformers.rs`, define Rust structs that exactly match the JSON (or other format) structure of the connector's API for requests, responses, and error responses
Applied to files:
backend/connector-integration/src/connectors/tsys/transformers.rs
📚 Learning: 2025-11-25T05:32:43.442Z
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/{*_connector.rs,transformers.rs} : Ensure all matching arms have proper 'not implemented' error handling for connectors that don't support specific payment methods or payment method types
Applied to files:
backend/connector-integration/src/connectors/tsys/transformers.rs
📚 Learning: 2025-11-25T05:32:43.442Z
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/transformers.rs : Store critical identifiers (`connector_transaction_id`, `reference_id`, `mandate_id`) in the `handle_response` method for all flows
Applied to files:
backend/connector-integration/src/connectors/tsys/transformers.rs
📚 Learning: 2025-11-25T05:32:43.442Z
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/transformers.rs : Implement `TryFrom` trait to convert between `RouterDataV2` and connector-specific request/response structs in `transformers.rs`
Applied to files:
backend/connector-integration/src/connectors/tsys/transformers.rs
📚 Learning: 2025-11-25T05:32:43.442Z
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/transformers.rs : Handle all types of errors properly using Enums in connector error responses, supporting different error structures, string errors, and empty body responses
Applied to files:
backend/connector-integration/src/connectors/tsys/transformers.rs
📚 Learning: 2025-11-25T05:32:43.442Z
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/transformers.rs : Create utility functions in transformers for common operations including email handling, address processing, return URL construction, and connector transaction ID extraction
Applied to files:
backend/connector-integration/src/connectors/tsys/transformers.rs
📚 Learning: 2025-09-24T08:45:31.195Z
Learnt from: Anurag-05-prog
Repo: juspay/connector-service PR: 188
File: backend/connector-integration/src/connectors/peachpayments.rs:306-315
Timestamp: 2025-09-24T08:45:31.195Z
Learning: In Peachpayments connector (backend/connector-integration/src/connectors/peachpayments.rs), the connector_transaction_id field has different types across flows: PaymentVoidData uses String directly, while PaymentsCaptureData uses ResponseId enum that requires pattern matching or get_connector_transaction_id() method.
Applied to files:
backend/connector-integration/src/connectors/tsys/transformers.rs
📚 Learning: 2025-09-26T08:08:52.624Z
Learnt from: Shubhodip900
Repo: juspay/connector-service PR: 193
File: backend/connector-integration/src/connectors/rapyd/transformers.rs:505-524
Timestamp: 2025-09-26T08:08:52.624Z
Learning: In Rapyd connector (backend/connector-integration/src/connectors/rapyd/transformers.rs), the next_action field in ResponseData struct is required (not Optional) as per Rapyd API documentation. This field always appears in payment responses with values like "3d_verification", "pending_capture", "pending_confirmation", or "not_applicable", consistent with hyperswitch connector patterns.
Applied to files:
backend/connector-integration/src/connectors/tsys/transformers.rs
📚 Learning: 2025-09-24T10:18:33.115Z
Learnt from: Anurag-05-prog
Repo: juspay/connector-service PR: 188
File: backend/connector-integration/src/connectors/peachpayments/transformers.rs:50-56
Timestamp: 2025-09-24T10:18:33.115Z
Learning: In Peach Payments connector API (backend/connector-integration/src/connectors/peachpayments/transformers.rs), the authorize request struct uses the field "charge_method" as expected by the Peach Payments API, not "paymentType" or other variations.
Applied to files:
backend/connector-integration/src/connectors/tsys/transformers.rs
📚 Learning: 2025-11-25T05:32:43.442Z
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/transformers.rs : Fields that have a limited set of possible values should be defined as enum types rather than as `String` or other generic types
Applied to files:
backend/connector-integration/src/connectors/tsys/transformers.rs
📚 Learning: 2025-09-15T06:56:22.535Z
Learnt from: Shubhodip900
Repo: juspay/connector-service PR: 172
File: backend/connector-integration/src/connectors/trustpay.rs:747-762
Timestamp: 2025-09-15T06:56:22.535Z
Learning: In Trustpay connector (backend/connector-integration/src/connectors/trustpay.rs), the refund_connector_metadata field has different data formats in different contexts: RSync expects it to be already parsed as JSON Value (accessing directly with .get()), while other refund flows expect it to be a JSON string that needs serde_json::from_str() parsing. The extract_payment_method_from_refund_metadata helper is designed for the latter case and should not be used in RSync.
Applied to files:
backend/connector-integration/src/connectors/tsys/transformers.rs
📚 Learning: 2025-11-25T05:32:43.442Z
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/{*_connector.rs,transformers.rs} : Never hardcode values in connector code; always retrieve values from the request or available resources and implement proper error handling when values are not found
Applied to files:
backend/connector-integration/src/connectors/tsys/transformers.rs
📚 Learning: 2025-11-25T05:32:43.442Z
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/transformers.rs : Ensure correct mapping of `response_transaction_id` and `redirect_urls` with respective fields in router data within response handlers
Applied to files:
backend/connector-integration/src/connectors/tsys/transformers.rs
📚 Learning: 2025-11-25T05:32:43.442Z
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/transformers.rs : When a field is required by a connector (e.g., email, name, address, state, country), validate its presence and return an error if missing, rather than relying on the connector to handle it
Applied to files:
backend/connector-integration/src/connectors/tsys/transformers.rs
📚 Learning: 2025-11-25T05:32:43.442Z
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/transformers.rs : Use `MinorUnit` type for amount fields in connector request/response structs instead of primitive types like `i64`
Applied to files:
backend/connector-integration/src/connectors/tsys/transformers.rs
📚 Learning: 2025-09-22T12:10:19.352Z
Learnt from: chaitak-gorai
Repo: juspay/connector-service PR: 173
File: backend/connector-integration/src/connectors/helcim/transformers.rs:304-325
Timestamp: 2025-09-22T12:10:19.352Z
Learning: In HyperSwitch (HS), verification transactions (type "verify") are mapped to the same AttemptStatus as purchase transactions - Approved verify transactions map to AttemptStatus::Charged rather than AttemptStatus::Authorized. This follows HS system conventions rather than typical payment processor conventions.
Applied to files:
backend/connector-integration/src/connectors/tsys/transformers.rs
📚 Learning: 2025-11-25T05:32:43.442Z
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/tests/**/*_payment_flows_test.rs : Write thorough end-to-end tests in `backend/grpc-server/tests/my_connector_payment_flows_test.rs` to cover all supported payment flows, including success and failure scenarios
Applied to files:
backend/connector-integration/src/connectors/tsys/transformers.rs
📚 Learning: 2025-11-25T05:32:43.442Z
Learnt from: CR
Repo: juspay/connector-service PR: 0
File: memory-bank/coderabbit/coderabbit_guidelines.md:0-0
Timestamp: 2025-11-25T05:32:43.442Z
Learning: Applies to memory-bank/coderabbit/**/transformers.rs : All new connectors must implement proper status mapping according to Hyperswitch standards, with default status always being `pending`
Applied to files:
backend/connector-integration/src/connectors/tsys/transformers.rs
🧬 Code graph analysis (1)
backend/connector-integration/src/connectors/tsys/transformers.rs (2)
backend/domain_types/src/types.rs (6)
RawCardNumber(1014-1014)from(6648-6662)from(7405-7411)from(7524-7529)from(7593-7629)from(7633-7638)backend/domain_types/src/connector_types.rs (1)
from(2367-2563)
🔇 Additional comments (7)
backend/connector-integration/src/connectors.rs (1)
167-168: LGTM! TSYS module integration follows established pattern.The module declaration and public export are consistent with other connectors in this file.
backend/connector-integration/src/connectors/tsys/transformers.rs (6)
27-53: LGTM! Authentication type implementation is well-structured.Proper use of
Secret<String>for sensitive fields and correct error handling for unsupported auth types.
163-412: LGTM! Response handling implementation is comprehensive.Proper use of untagged enums for response variants, correct status mappings, and well-structured error handling for Authorize, Capture, and Void flows.
602-667: LGTM! Capture flow implementation is correct.Request structure includes all necessary fields including
developer_id, and properly extracts the connector transaction ID with error handling.
673-726: LGTM! Void flow implementation is correct.Request structure properly includes all required fields and correctly extracts the transaction ID.
851-928: LGTM! RSync flow implementation correctly reuses sync structures.Proper use of transparent wrappers and correct status mapping for refund synchronization.
934-949: LGTM! Error response handling now properly uses connector error details.The function correctly extracts error code and message from the TSYS error response instead of using hardcoded values. This resolves the previous issue.
| use common_enums::AttemptStatus; | ||
| use common_utils::types::{MinorUnit, StringMinorUnit}; | ||
| use domain_types::{ | ||
| connector_flow::{Authorize, Capture, PSync, RSync, Refund, Void}, | ||
| connector_types::{ | ||
| PaymentFlowData, PaymentVoidData, PaymentsAuthorizeData, PaymentsCaptureData, | ||
| PaymentsResponseData, PaymentsSyncData, RefundFlowData, RefundSyncData, RefundsData, | ||
| RefundsResponseData, ResponseId, | ||
| }, | ||
| errors, | ||
| payment_method_data::{PaymentMethodData, PaymentMethodDataTypes, RawCardNumber}, | ||
| router_data::ConnectorAuthType, | ||
| router_data_v2::RouterDataV2, | ||
| }; | ||
| use error_stack::ResultExt; | ||
| use hyperswitch_masking::Secret; | ||
| use serde::{Deserialize, Serialize}; | ||
|
|
||
| use crate::types::ResponseRouterData; | ||
|
|
||
| use super::{TsysAmountConvertor, TsysRouterData}; | ||
|
|
||
| // ============================================================================ | ||
| // Authentication Type | ||
| // ============================================================================ | ||
|
|
||
| #[derive(Debug, Clone)] | ||
| pub struct TsysAuthType { | ||
| pub device_id: Secret<String>, | ||
| pub transaction_key: Secret<String>, | ||
| pub developer_id: Secret<String>, | ||
| } | ||
|
|
||
| impl TryFrom<&ConnectorAuthType> for TsysAuthType { | ||
| type Error = error_stack::Report<errors::ConnectorError>; | ||
|
|
||
| fn try_from(auth_type: &ConnectorAuthType) -> Result<Self, Self::Error> { | ||
| match auth_type { | ||
| ConnectorAuthType::SignatureKey { | ||
| api_key, | ||
| key1, | ||
| api_secret, | ||
| } => Ok(Self { | ||
| device_id: api_key.to_owned(), | ||
| transaction_key: key1.to_owned(), | ||
| developer_id: api_secret.to_owned(), | ||
| }), | ||
| _ => Err(error_stack::report!( | ||
| errors::ConnectorError::FailedToObtainAuthType | ||
| )), | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // ============================================================================ | ||
| // AUTHORIZE FLOW - Request/Response | ||
| // ============================================================================ | ||
|
|
||
| #[derive(Debug, Serialize)] | ||
| pub enum TsysPaymentsRequest<T: PaymentMethodDataTypes> { | ||
| Auth(TsysPaymentAuthSaleRequest<T>), | ||
| Sale(TsysPaymentAuthSaleRequest<T>), | ||
| } | ||
|
|
||
| #[derive(Default, Debug, Serialize)] | ||
| #[serde(rename_all = "camelCase")] | ||
| pub struct TsysPaymentAuthSaleRequest<T: PaymentMethodDataTypes> { | ||
| #[serde(rename = "deviceID")] | ||
| device_id: Secret<String>, | ||
| transaction_key: Secret<String>, | ||
| card_data_source: String, | ||
| transaction_amount: StringMinorUnit, | ||
| currency_code: common_enums::enums::Currency, | ||
| card_number: RawCardNumber<T>, | ||
| expiration_date: Secret<String>, | ||
| cvv2: Secret<String>, | ||
| order_number: String, | ||
| terminal_capability: String, | ||
| terminal_operating_environment: String, | ||
| cardholder_authentication_method: String, | ||
| #[serde(rename = "developerID")] | ||
| developer_id: Secret<String>, | ||
| } | ||
|
|
||
| // TryFrom for macro compatibility - owned TsysRouterData | ||
| impl< | ||
| T: PaymentMethodDataTypes | ||
| + std::fmt::Debug | ||
| + std::marker::Sync | ||
| + std::marker::Send | ||
| + 'static | ||
| + Serialize, | ||
| > | ||
| TryFrom< | ||
| TsysRouterData< | ||
| RouterDataV2< | ||
| Authorize, | ||
| PaymentFlowData, | ||
| PaymentsAuthorizeData<T>, | ||
| PaymentsResponseData, | ||
| >, | ||
| T, | ||
| >, | ||
| > for TsysPaymentsRequest<T> | ||
| { | ||
| type Error = error_stack::Report<errors::ConnectorError>; | ||
|
|
||
| fn try_from( | ||
| item_data: TsysRouterData< | ||
| RouterDataV2< | ||
| Authorize, | ||
| PaymentFlowData, | ||
| PaymentsAuthorizeData<T>, | ||
| PaymentsResponseData, | ||
| >, | ||
| T, | ||
| >, | ||
| ) -> Result<Self, Self::Error> { | ||
| let item = &item_data.router_data; | ||
|
|
||
| match &item.request.payment_method_data { | ||
| PaymentMethodData::Card(card_data) => { | ||
| let auth: TsysAuthType = TsysAuthType::try_from(&item.connector_auth_type)?; | ||
|
|
||
| let auth_data = TsysPaymentAuthSaleRequest { | ||
| device_id: auth.device_id, | ||
| transaction_key: auth.transaction_key, | ||
| card_data_source: "INTERNET".to_string(), | ||
| transaction_amount: TsysAmountConvertor::convert( | ||
| item.request.minor_amount, | ||
| item.request.currency, | ||
| ) | ||
| .change_context(errors::ConnectorError::RequestEncodingFailed)?, | ||
| currency_code: item.request.currency, | ||
| card_number: card_data.card_number.clone(), | ||
| expiration_date: card_data | ||
| .get_card_expiry_month_year_2_digit_with_delimiter("/".to_owned())?, | ||
| cvv2: card_data.card_cvc.clone(), | ||
| order_number: item | ||
| .resource_common_data | ||
| .connector_request_reference_id | ||
| .clone(), | ||
| terminal_capability: "ICC_CHIP_READ_ONLY".to_string(), | ||
| terminal_operating_environment: "ON_MERCHANT_PREMISES_ATTENDED".to_string(), | ||
| cardholder_authentication_method: "NOT_AUTHENTICATED".to_string(), | ||
| developer_id: auth.developer_id, | ||
| }; | ||
|
|
||
| // Check if auto-capture or manual capture | ||
| if item.request.is_auto_capture()? { | ||
| Ok(Self::Sale(auth_data)) | ||
| } else { | ||
| Ok(Self::Auth(auth_data)) | ||
| } | ||
| } | ||
| _ => Err(errors::ConnectorError::NotImplemented( | ||
| "Payment method not supported".to_string(), | ||
| ))?, | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // Response types for Authorize | ||
| #[derive(Debug, Clone, Deserialize, Serialize)] | ||
| #[serde(rename_all = "UPPERCASE")] | ||
| pub enum TsysPaymentStatus { | ||
| Pass, | ||
| Fail, | ||
| } | ||
|
|
||
| #[derive(Debug, Clone, Deserialize, Serialize)] | ||
| #[serde(rename_all = "UPPERCASE")] | ||
| pub enum TsysTransactionStatus { | ||
| Approved, | ||
| Declined, | ||
| Void, | ||
| } | ||
|
|
||
| #[derive(Debug, Clone, Deserialize, Serialize)] | ||
| #[serde(rename_all = "camelCase")] | ||
| pub struct TsysResponse { | ||
| pub status: TsysPaymentStatus, | ||
| pub response_code: String, | ||
| pub response_message: String, | ||
| #[serde(rename = "transactionID")] | ||
| pub transaction_id: String, | ||
| } | ||
|
|
||
| #[derive(Debug, Clone, Deserialize, Serialize)] | ||
| #[serde(rename_all = "camelCase")] | ||
| pub struct TsysErrorResponse { | ||
| pub status: TsysPaymentStatus, | ||
| pub response_code: String, | ||
| pub response_message: String, | ||
| } | ||
|
|
||
| #[derive(Debug, Clone, Deserialize, Serialize)] | ||
| #[serde(untagged)] | ||
| pub enum TsysResponseTypes { | ||
| SuccessResponse(TsysResponse), | ||
| ErrorResponse(TsysErrorResponse), | ||
| } | ||
|
|
||
| #[derive(Debug, Clone, Deserialize, Serialize)] | ||
| #[allow(clippy::enum_variant_names)] | ||
| pub enum TsysPaymentsResponse { | ||
| AuthResponse(TsysResponseTypes), | ||
| SaleResponse(TsysResponseTypes), | ||
| CaptureResponse(TsysResponseTypes), | ||
| VoidResponse(TsysResponseTypes), | ||
| } | ||
|
|
||
| // Separate wrapper types for each flow to avoid macro conflicts | ||
| #[derive(Debug, Clone, Deserialize, Serialize)] | ||
| #[serde(transparent)] | ||
| pub struct TsysAuthorizeResponse(pub TsysPaymentsResponse); | ||
|
|
||
| #[derive(Debug, Clone, Deserialize, Serialize)] | ||
| #[serde(transparent)] | ||
| pub struct TsysCaptureResponse(pub TsysPaymentsResponse); | ||
|
|
||
| #[derive(Debug, Clone, Deserialize, Serialize)] | ||
| #[serde(transparent)] | ||
| pub struct TsysVoidResponse(pub TsysPaymentsResponse); | ||
|
|
||
| fn get_payments_response(connector_response: TsysResponse, http_code: u16) -> PaymentsResponseData { | ||
| PaymentsResponseData::TransactionResponse { | ||
| resource_id: ResponseId::ConnectorTransactionId(connector_response.transaction_id.clone()), | ||
| redirection_data: None, | ||
| mandate_reference: None, | ||
| connector_metadata: None, | ||
| network_txn_id: None, | ||
| connector_response_reference_id: Some(connector_response.transaction_id), | ||
| incremental_authorization_allowed: None, | ||
| status_code: http_code, | ||
| } | ||
| } | ||
|
|
||
| impl<T: PaymentMethodDataTypes> | ||
| TryFrom< | ||
| ResponseRouterData< | ||
| TsysAuthorizeResponse, | ||
| RouterDataV2< | ||
| Authorize, | ||
| PaymentFlowData, | ||
| PaymentsAuthorizeData<T>, | ||
| PaymentsResponseData, | ||
| >, | ||
| >, | ||
| > for RouterDataV2<Authorize, PaymentFlowData, PaymentsAuthorizeData<T>, PaymentsResponseData> | ||
| { | ||
| type Error = error_stack::Report<errors::ConnectorError>; | ||
|
|
||
| fn try_from( | ||
| item: ResponseRouterData< | ||
| TsysAuthorizeResponse, | ||
| RouterDataV2< | ||
| Authorize, | ||
| PaymentFlowData, | ||
| PaymentsAuthorizeData<T>, | ||
| PaymentsResponseData, | ||
| >, | ||
| >, | ||
| ) -> Result<Self, Self::Error> { | ||
| let (response, status) = match item.response.0 { | ||
| TsysPaymentsResponse::AuthResponse(resp) => match resp { | ||
| TsysResponseTypes::SuccessResponse(auth_response) => ( | ||
| Ok(get_payments_response(auth_response, item.http_code)), | ||
| AttemptStatus::Authorized, | ||
| ), | ||
| TsysResponseTypes::ErrorResponse(error_response) => ( | ||
| Err(get_error_response(&error_response, item.http_code)), | ||
| AttemptStatus::AuthorizationFailed, | ||
| ), | ||
| }, | ||
| TsysPaymentsResponse::SaleResponse(resp) => match resp { | ||
| TsysResponseTypes::SuccessResponse(sale_response) => ( | ||
| Ok(get_payments_response(sale_response, item.http_code)), | ||
| AttemptStatus::Charged, | ||
| ), | ||
| TsysResponseTypes::ErrorResponse(error_response) => ( | ||
| Err(get_error_response(&error_response, item.http_code)), | ||
| AttemptStatus::Failure, | ||
| ), | ||
| }, | ||
| _ => { | ||
| let generic_error = TsysErrorResponse { | ||
| status: TsysPaymentStatus::Fail, | ||
| response_code: item.http_code.to_string(), | ||
| response_message: item.http_code.to_string(), | ||
| }; | ||
| ( | ||
| Err(get_error_response(&generic_error, item.http_code)), | ||
| AttemptStatus::Failure, | ||
| ) | ||
| }, | ||
| }; | ||
|
|
||
| Ok(Self { | ||
| response, | ||
| resource_common_data: PaymentFlowData { | ||
| status, | ||
| ..item.router_data.resource_common_data | ||
| }, | ||
| ..item.router_data | ||
| }) | ||
| } | ||
| } | ||
|
|
||
| // TryFrom for Capture flow | ||
| impl | ||
| TryFrom< | ||
| ResponseRouterData< | ||
| TsysCaptureResponse, | ||
| RouterDataV2<Capture, PaymentFlowData, PaymentsCaptureData, PaymentsResponseData>, | ||
| >, | ||
| > for RouterDataV2<Capture, PaymentFlowData, PaymentsCaptureData, PaymentsResponseData> | ||
| { | ||
| type Error = error_stack::Report<errors::ConnectorError>; | ||
|
|
||
| fn try_from( | ||
| item: ResponseRouterData< | ||
| TsysCaptureResponse, | ||
| RouterDataV2<Capture, PaymentFlowData, PaymentsCaptureData, PaymentsResponseData>, | ||
| >, | ||
| ) -> Result<Self, Self::Error> { | ||
| let (response, status) = match item.response.0 { | ||
| TsysPaymentsResponse::CaptureResponse(resp) => match resp { | ||
| TsysResponseTypes::SuccessResponse(capture_response) => ( | ||
| Ok(get_payments_response(capture_response, item.http_code)), | ||
| AttemptStatus::Charged, | ||
| ), | ||
| TsysResponseTypes::ErrorResponse(error_response) => ( | ||
| Err(get_error_response(&error_response, item.http_code)), | ||
| AttemptStatus::CaptureFailed, | ||
| ), | ||
| }, | ||
| _ => { | ||
| let generic_error = TsysErrorResponse { | ||
| status: TsysPaymentStatus::Fail, | ||
| response_code: item.http_code.to_string(), | ||
| response_message: item.http_code.to_string(), | ||
| }; | ||
| ( | ||
| Err(get_error_response(&generic_error, item.http_code)), | ||
| AttemptStatus::CaptureFailed, | ||
| ) | ||
| }, | ||
| }; | ||
|
|
||
| Ok(Self { | ||
| response, | ||
| resource_common_data: PaymentFlowData { | ||
| status, | ||
| ..item.router_data.resource_common_data | ||
| }, | ||
| ..item.router_data | ||
| }) | ||
| } | ||
| } | ||
|
|
||
| // TryFrom for Void flow | ||
| impl | ||
| TryFrom< | ||
| ResponseRouterData< | ||
| TsysVoidResponse, | ||
| RouterDataV2<Void, PaymentFlowData, PaymentVoidData, PaymentsResponseData>, | ||
| >, | ||
| > for RouterDataV2<Void, PaymentFlowData, PaymentVoidData, PaymentsResponseData> | ||
| { | ||
| type Error = error_stack::Report<errors::ConnectorError>; | ||
|
|
||
| fn try_from( | ||
| item: ResponseRouterData< | ||
| TsysVoidResponse, | ||
| RouterDataV2<Void, PaymentFlowData, PaymentVoidData, PaymentsResponseData>, | ||
| >, | ||
| ) -> Result<Self, Self::Error> { | ||
| let (response, status) = match item.response.0 { | ||
| TsysPaymentsResponse::VoidResponse(resp) => match resp { | ||
| TsysResponseTypes::SuccessResponse(void_response) => ( | ||
| Ok(get_payments_response(void_response, item.http_code)), | ||
| AttemptStatus::Voided, | ||
| ), | ||
| TsysResponseTypes::ErrorResponse(error_response) => ( | ||
| Err(get_error_response(&error_response, item.http_code)), | ||
| AttemptStatus::VoidFailed, | ||
| ), | ||
| }, | ||
| _ => { | ||
| let generic_error = TsysErrorResponse { | ||
| status: TsysPaymentStatus::Fail, | ||
| response_code: item.http_code.to_string(), | ||
| response_message: item.http_code.to_string(), | ||
| }; | ||
| ( | ||
| Err(get_error_response(&generic_error, item.http_code)), | ||
| AttemptStatus::VoidFailed, | ||
| ) | ||
| }, | ||
| }; | ||
|
|
||
| Ok(Self { | ||
| response, | ||
| resource_common_data: PaymentFlowData { | ||
| status, | ||
| ..item.router_data.resource_common_data | ||
| }, | ||
| ..item.router_data | ||
| }) | ||
| } | ||
| } | ||
|
|
||
| // ============================================================================ | ||
| // PSYNC FLOW - Request/Response | ||
| // ============================================================================ | ||
|
|
||
| #[derive(Debug, Serialize)] | ||
| #[serde(rename_all = "camelCase")] | ||
| pub struct TsysSearchTransactionRequest { | ||
| #[serde(rename = "deviceID")] | ||
| device_id: Secret<String>, | ||
| transaction_key: Secret<String>, | ||
| #[serde(rename = "transactionID")] | ||
| transaction_id: String, | ||
| #[serde(rename = "developerID")] | ||
| developer_id: Secret<String>, | ||
| } | ||
|
|
||
| #[derive(Debug, Serialize)] | ||
| #[serde(rename_all = "PascalCase")] | ||
| pub struct TsysSyncRequest { | ||
| search_transaction: TsysSearchTransactionRequest, | ||
| } | ||
|
|
||
| // Wrapper struct for PSync to avoid macro conflicts | ||
| #[derive(Debug, Serialize)] | ||
| #[serde(transparent)] | ||
| pub struct TsysPSyncRequest(TsysSyncRequest); | ||
|
|
||
| #[derive(Debug, Deserialize, Serialize)] | ||
| #[serde(transparent)] | ||
| pub struct TsysPSyncResponse(TsysSyncResponse); | ||
|
|
||
| impl< | ||
| T: PaymentMethodDataTypes | ||
| + std::fmt::Debug | ||
| + std::marker::Sync | ||
| + std::marker::Send | ||
| + 'static | ||
| + Serialize, | ||
| > | ||
| TryFrom< | ||
| TsysRouterData< | ||
| RouterDataV2<PSync, PaymentFlowData, PaymentsSyncData, PaymentsResponseData>, | ||
| T, | ||
| >, | ||
| > for TsysPSyncRequest | ||
| { | ||
| type Error = error_stack::Report<errors::ConnectorError>; | ||
|
|
||
| fn try_from( | ||
| item_data: TsysRouterData< | ||
| RouterDataV2<PSync, PaymentFlowData, PaymentsSyncData, PaymentsResponseData>, | ||
| T, | ||
| >, | ||
| ) -> Result<Self, Self::Error> { | ||
| let item = &item_data.router_data; | ||
| let auth: TsysAuthType = TsysAuthType::try_from(&item.connector_auth_type)?; | ||
|
|
||
| let search_transaction = TsysSearchTransactionRequest { | ||
| device_id: auth.device_id, | ||
| transaction_key: auth.transaction_key, | ||
| transaction_id: item | ||
| .request | ||
| .connector_transaction_id | ||
| .get_connector_transaction_id() | ||
| .change_context(errors::ConnectorError::MissingConnectorTransactionID)?, | ||
| developer_id: auth.developer_id, | ||
| }; | ||
|
|
||
| Ok(Self(TsysSyncRequest { search_transaction })) | ||
| } | ||
| } | ||
|
|
||
| // PSync Response | ||
| #[derive(Debug, Clone, Deserialize, Serialize)] | ||
| #[serde(rename_all = "camelCase")] | ||
| pub struct TsysTransactionDetails { | ||
| #[serde(rename = "transactionID")] | ||
| transaction_id: String, | ||
| transaction_type: String, | ||
| transaction_status: TsysTransactionStatus, | ||
| } | ||
|
|
||
| impl From<TsysTransactionDetails> for AttemptStatus { | ||
| fn from(item: TsysTransactionDetails) -> Self { | ||
| match item.transaction_status { | ||
| TsysTransactionStatus::Approved => { | ||
| if item.transaction_type.contains("Auth-Only") { | ||
| Self::Authorized | ||
| } else { | ||
| Self::Charged | ||
| } | ||
| } | ||
| TsysTransactionStatus::Void => Self::Voided, | ||
| TsysTransactionStatus::Declined => Self::Failure, | ||
| } | ||
| } | ||
| } | ||
|
|
||
| #[derive(Debug, Clone, Deserialize, Serialize)] | ||
| #[serde(rename_all = "camelCase")] | ||
| pub struct TsysPaymentsSyncResponse { | ||
| pub status: TsysPaymentStatus, | ||
| pub response_code: String, | ||
| pub response_message: String, | ||
| pub transaction_details: TsysTransactionDetails, | ||
| } | ||
|
|
||
| #[derive(Debug, Deserialize, Serialize)] | ||
| #[serde(untagged)] | ||
| pub enum SearchResponseTypes { | ||
| SuccessResponse(TsysPaymentsSyncResponse), | ||
| ErrorResponse(TsysErrorResponse), | ||
| } | ||
|
|
||
| #[derive(Debug, Deserialize, Serialize)] | ||
| #[serde(rename_all = "PascalCase")] | ||
| pub struct TsysSyncResponse { | ||
| search_transaction_response: SearchResponseTypes, | ||
| } | ||
|
|
||
| fn get_payments_sync_response( | ||
| connector_response: &TsysPaymentsSyncResponse, | ||
| http_code: u16, | ||
| ) -> PaymentsResponseData { | ||
| PaymentsResponseData::TransactionResponse { | ||
| resource_id: ResponseId::ConnectorTransactionId( | ||
| connector_response | ||
| .transaction_details | ||
| .transaction_id | ||
| .clone(), | ||
| ), | ||
| redirection_data: None, | ||
| mandate_reference: None, | ||
| connector_metadata: None, | ||
| network_txn_id: None, | ||
| connector_response_reference_id: Some( | ||
| connector_response | ||
| .transaction_details | ||
| .transaction_id | ||
| .clone(), | ||
| ), | ||
| incremental_authorization_allowed: None, | ||
| status_code: http_code, | ||
| } | ||
| } | ||
|
|
||
| impl | ||
| TryFrom< | ||
| ResponseRouterData< | ||
| TsysPSyncResponse, | ||
| RouterDataV2<PSync, PaymentFlowData, PaymentsSyncData, PaymentsResponseData>, | ||
| >, | ||
| > for RouterDataV2<PSync, PaymentFlowData, PaymentsSyncData, PaymentsResponseData> | ||
| { | ||
| type Error = error_stack::Report<errors::ConnectorError>; | ||
|
|
||
| fn try_from( | ||
| item: ResponseRouterData< | ||
| TsysPSyncResponse, | ||
| RouterDataV2<PSync, PaymentFlowData, PaymentsSyncData, PaymentsResponseData>, | ||
| >, | ||
| ) -> Result<Self, Self::Error> { | ||
| let (response, status) = match item.response.0.search_transaction_response { | ||
| SearchResponseTypes::SuccessResponse(search_response) => ( | ||
| Ok(get_payments_sync_response(&search_response, item.http_code)), | ||
| AttemptStatus::from(search_response.transaction_details), | ||
| ), | ||
| SearchResponseTypes::ErrorResponse(error_response) => ( | ||
| Err(get_error_response(&error_response, item.http_code)), | ||
| item.router_data.resource_common_data.status, | ||
| ), | ||
| }; | ||
|
|
||
| Ok(Self { | ||
| response, | ||
| resource_common_data: PaymentFlowData { | ||
| status, | ||
| ..item.router_data.resource_common_data | ||
| }, | ||
| ..item.router_data | ||
| }) | ||
| } | ||
| } | ||
|
|
||
| // ============================================================================ | ||
| // CAPTURE FLOW - Request/Response | ||
| // ============================================================================ | ||
|
|
||
| #[derive(Debug, Serialize)] | ||
| #[serde(rename_all = "camelCase")] | ||
| pub struct TsysCaptureRequest { | ||
| #[serde(rename = "deviceID")] | ||
| device_id: Secret<String>, | ||
| transaction_key: Secret<String>, | ||
| transaction_amount: StringMinorUnit, | ||
| #[serde(rename = "transactionID")] | ||
| transaction_id: String, | ||
| #[serde(rename = "developerID")] | ||
| developer_id: Secret<String>, | ||
| } | ||
|
|
||
| #[derive(Debug, Serialize)] | ||
| #[serde(rename_all = "PascalCase")] | ||
| pub struct TsysPaymentsCaptureRequest { | ||
| capture: TsysCaptureRequest, | ||
| } | ||
|
|
||
| // TryFrom for macro compatibility - owned TsysRouterData | ||
| impl< | ||
| T: PaymentMethodDataTypes | ||
| + std::fmt::Debug | ||
| + std::marker::Sync | ||
| + std::marker::Send | ||
| + 'static | ||
| + Serialize, | ||
| > | ||
| TryFrom< | ||
| TsysRouterData< | ||
| RouterDataV2<Capture, PaymentFlowData, PaymentsCaptureData, PaymentsResponseData>, | ||
| T, | ||
| >, | ||
| > for TsysPaymentsCaptureRequest | ||
| { | ||
| type Error = error_stack::Report<errors::ConnectorError>; | ||
|
|
||
| fn try_from( | ||
| item_data: TsysRouterData< | ||
| RouterDataV2<Capture, PaymentFlowData, PaymentsCaptureData, PaymentsResponseData>, | ||
| T, | ||
| >, | ||
| ) -> Result<Self, Self::Error> { | ||
| let item = &item_data.router_data; | ||
| let auth: TsysAuthType = TsysAuthType::try_from(&item.connector_auth_type)?; | ||
|
|
||
| let capture = TsysCaptureRequest { | ||
| device_id: auth.device_id, | ||
| transaction_key: auth.transaction_key, | ||
| transaction_id: item | ||
| .request | ||
| .connector_transaction_id | ||
| .clone() | ||
| .get_connector_transaction_id() | ||
| .change_context(errors::ConnectorError::MissingConnectorTransactionID)?, | ||
| developer_id: auth.developer_id, | ||
| transaction_amount: TsysAmountConvertor::convert( | ||
| item.request.minor_amount_to_capture, | ||
| item.request.currency, | ||
| ) | ||
| .change_context(errors::ConnectorError::RequestEncodingFailed)?, | ||
| }; | ||
|
|
||
| Ok(Self { capture }) | ||
| } | ||
| } | ||
|
|
||
| // ============================================================================ | ||
| // VOID FLOW - Request/Response | ||
| // ============================================================================ | ||
|
|
||
| #[derive(Debug, Serialize)] | ||
| #[serde(rename_all = "camelCase")] | ||
| pub struct TsysCancelRequest { | ||
| #[serde(rename = "deviceID")] | ||
| device_id: Secret<String>, | ||
| transaction_key: Secret<String>, | ||
| #[serde(rename = "transactionID")] | ||
| transaction_id: String, | ||
| #[serde(rename = "developerID")] | ||
| developer_id: Secret<String>, | ||
| } | ||
|
|
||
| #[derive(Debug, Serialize)] | ||
| #[serde(rename_all = "PascalCase")] | ||
| pub struct TsysPaymentsCancelRequest { | ||
| void: TsysCancelRequest, | ||
| } | ||
|
|
||
| impl< | ||
| T: PaymentMethodDataTypes | ||
| + std::fmt::Debug | ||
| + std::marker::Sync | ||
| + std::marker::Send | ||
| + 'static | ||
| + Serialize, | ||
| > | ||
| TryFrom< | ||
| TsysRouterData< | ||
| RouterDataV2<Void, PaymentFlowData, PaymentVoidData, PaymentsResponseData>, | ||
| T, | ||
| >, | ||
| > for TsysPaymentsCancelRequest | ||
| { | ||
| type Error = error_stack::Report<errors::ConnectorError>; | ||
|
|
||
| fn try_from( | ||
| item_data: TsysRouterData< | ||
| RouterDataV2<Void, PaymentFlowData, PaymentVoidData, PaymentsResponseData>, | ||
| T, | ||
| >, | ||
| ) -> Result<Self, Self::Error> { | ||
| let item = &item_data.router_data; | ||
| let auth: TsysAuthType = TsysAuthType::try_from(&item.connector_auth_type)?; | ||
|
|
||
| let void = TsysCancelRequest { | ||
| device_id: auth.device_id, | ||
| transaction_key: auth.transaction_key, | ||
| transaction_id: item.request.connector_transaction_id.clone(), | ||
| developer_id: auth.developer_id, | ||
| }; | ||
|
|
||
| Ok(Self { void }) | ||
| } | ||
| } | ||
|
|
||
| // ============================================================================ | ||
| // REFUND FLOW - Request/Response | ||
| // ============================================================================ | ||
|
|
||
| #[derive(Debug, Serialize)] | ||
| #[serde(rename_all = "camelCase")] | ||
| pub struct TsysReturnRequest { | ||
| #[serde(rename = "deviceID")] | ||
| device_id: Secret<String>, | ||
| transaction_key: Secret<String>, | ||
| transaction_amount: StringMinorUnit, | ||
| #[serde(rename = "transactionID")] | ||
| transaction_id: String, | ||
| } | ||
|
|
||
| #[derive(Debug, Serialize)] | ||
| #[serde(rename_all = "PascalCase")] | ||
| pub struct TsysRefundRequest { | ||
| #[serde(rename = "Return")] | ||
| return_request: TsysReturnRequest, | ||
| } | ||
|
|
||
| // TryFrom for macro compatibility - owned TsysRouterData | ||
| impl< | ||
| T: PaymentMethodDataTypes | ||
| + std::fmt::Debug | ||
| + std::marker::Sync | ||
| + std::marker::Send | ||
| + 'static | ||
| + Serialize, | ||
| > | ||
| TryFrom< | ||
| TsysRouterData<RouterDataV2<Refund, RefundFlowData, RefundsData, RefundsResponseData>, T>, | ||
| > for TsysRefundRequest | ||
| { | ||
| type Error = error_stack::Report<errors::ConnectorError>; | ||
|
|
||
| fn try_from( | ||
| item_data: TsysRouterData< | ||
| RouterDataV2<Refund, RefundFlowData, RefundsData, RefundsResponseData>, | ||
| T, | ||
| >, | ||
| ) -> Result<Self, Self::Error> { | ||
| let item = &item_data.router_data; | ||
| let auth: TsysAuthType = TsysAuthType::try_from(&item.connector_auth_type)?; | ||
|
|
||
| let return_request = TsysReturnRequest { | ||
| device_id: auth.device_id, | ||
| transaction_key: auth.transaction_key, | ||
| transaction_amount: TsysAmountConvertor::convert( | ||
| MinorUnit(item.request.refund_amount), | ||
| item.request.currency, | ||
| ) | ||
| .change_context(errors::ConnectorError::RequestEncodingFailed)?, | ||
| transaction_id: item.request.connector_transaction_id.clone(), | ||
| }; | ||
|
|
||
| Ok(Self { return_request }) | ||
| } | ||
| } | ||
|
|
||
| // Refund Response | ||
| impl From<TsysPaymentStatus> for common_enums::enums::RefundStatus { | ||
| fn from(item: TsysPaymentStatus) -> Self { | ||
| match item { | ||
| TsysPaymentStatus::Pass => Self::Success, | ||
| TsysPaymentStatus::Fail => Self::Failure, | ||
| } | ||
| } | ||
| } | ||
|
|
||
| impl From<TsysTransactionDetails> for common_enums::enums::RefundStatus { | ||
| fn from(item: TsysTransactionDetails) -> Self { | ||
| match item.transaction_status { | ||
| TsysTransactionStatus::Approved => Self::Pending, | ||
| TsysTransactionStatus::Void => Self::Success, // TSYS marks successful refunds as VOID | ||
| TsysTransactionStatus::Declined => Self::Failure, | ||
| } | ||
| } | ||
| } | ||
|
|
||
| #[derive(Debug, Deserialize, Serialize)] | ||
| #[serde(rename_all = "PascalCase")] | ||
| pub struct RefundResponse { | ||
| return_response: TsysResponseTypes, | ||
| } | ||
|
|
||
| impl | ||
| TryFrom< | ||
| ResponseRouterData< | ||
| RefundResponse, | ||
| RouterDataV2<Refund, RefundFlowData, RefundsData, RefundsResponseData>, | ||
| >, | ||
| > for RouterDataV2<Refund, RefundFlowData, RefundsData, RefundsResponseData> | ||
| { | ||
| type Error = error_stack::Report<errors::ConnectorError>; | ||
|
|
||
| fn try_from( | ||
| item: ResponseRouterData< | ||
| RefundResponse, | ||
| RouterDataV2<Refund, RefundFlowData, RefundsData, RefundsResponseData>, | ||
| >, | ||
| ) -> Result<Self, Self::Error> { | ||
| let response = match item.response.return_response { | ||
| TsysResponseTypes::SuccessResponse(return_response) => Ok(RefundsResponseData { | ||
| connector_refund_id: return_response.transaction_id, | ||
| refund_status: common_enums::enums::RefundStatus::from(return_response.status), | ||
| status_code: item.http_code, | ||
| }), | ||
| TsysResponseTypes::ErrorResponse(error_response) => Err(get_error_response(&error_response, item.http_code)), | ||
| }; | ||
|
|
||
| Ok(Self { | ||
| response, | ||
| ..item.router_data | ||
| }) | ||
| } | ||
| } | ||
|
|
||
| // ============================================================================ | ||
| // RSYNC FLOW - Request/Response | ||
| // ============================================================================ | ||
|
|
||
| // Wrapper struct for RSync to avoid macro conflicts | ||
| #[derive(Debug, Serialize)] | ||
| #[serde(transparent)] | ||
| pub struct TsysRSyncRequest(TsysSyncRequest); | ||
|
|
||
| #[derive(Debug, Deserialize, Serialize)] | ||
| #[serde(transparent)] | ||
| pub struct TsysRSyncResponse(TsysSyncResponse); | ||
|
|
||
| impl< | ||
| T: PaymentMethodDataTypes | ||
| + std::fmt::Debug | ||
| + std::marker::Sync | ||
| + std::marker::Send | ||
| + 'static | ||
| + Serialize, | ||
| > | ||
| TryFrom< | ||
| TsysRouterData<RouterDataV2<RSync, RefundFlowData, RefundSyncData, RefundsResponseData>, T>, | ||
| > for TsysRSyncRequest | ||
| { | ||
| type Error = error_stack::Report<errors::ConnectorError>; | ||
|
|
||
| fn try_from( | ||
| item_data: TsysRouterData< | ||
| RouterDataV2<RSync, RefundFlowData, RefundSyncData, RefundsResponseData>, | ||
| T, | ||
| >, | ||
| ) -> Result<Self, Self::Error> { | ||
| let item = &item_data.router_data; | ||
| let auth: TsysAuthType = TsysAuthType::try_from(&item.connector_auth_type)?; | ||
|
|
||
| let search_transaction = TsysSearchTransactionRequest { | ||
| device_id: auth.device_id, | ||
| transaction_key: auth.transaction_key, | ||
| transaction_id: item.request.connector_refund_id.clone(), | ||
| developer_id: auth.developer_id, | ||
| }; | ||
|
|
||
| Ok(Self(TsysSyncRequest { search_transaction })) | ||
| } | ||
| } | ||
|
|
||
| impl | ||
| TryFrom< | ||
| ResponseRouterData< | ||
| TsysRSyncResponse, | ||
| RouterDataV2<RSync, RefundFlowData, RefundSyncData, RefundsResponseData>, | ||
| >, | ||
| > for RouterDataV2<RSync, RefundFlowData, RefundSyncData, RefundsResponseData> | ||
| { | ||
| type Error = error_stack::Report<errors::ConnectorError>; | ||
|
|
||
| fn try_from( | ||
| item: ResponseRouterData< | ||
| TsysRSyncResponse, | ||
| RouterDataV2<RSync, RefundFlowData, RefundSyncData, RefundsResponseData>, | ||
| >, | ||
| ) -> Result<Self, Self::Error> { | ||
| let response = match item.response.0.search_transaction_response { | ||
| SearchResponseTypes::SuccessResponse(search_response) => Ok(RefundsResponseData { | ||
| connector_refund_id: search_response.transaction_details.transaction_id.clone(), | ||
| refund_status: common_enums::enums::RefundStatus::from( | ||
| search_response.transaction_details, | ||
| ), | ||
| status_code: item.http_code, | ||
| }), | ||
| SearchResponseTypes::ErrorResponse(error_response) => { | ||
| Err(get_error_response(&error_response, item.http_code)) | ||
| }, | ||
| }; | ||
|
|
||
| Ok(Self { | ||
| response, | ||
| ..item.router_data | ||
| }) | ||
| } | ||
| } | ||
|
|
||
| // ============================================================================ | ||
| // ERROR RESPONSE HELPER | ||
| // ============================================================================ | ||
|
|
||
| fn get_error_response( | ||
| connector_response: &TsysErrorResponse, | ||
| status_code: u16, | ||
| ) -> domain_types::router_data::ErrorResponse { | ||
| domain_types::router_data::ErrorResponse { | ||
| code: connector_response.response_code.clone(), | ||
| message: connector_response.response_message.clone(), | ||
| reason: Some(connector_response.response_message.clone()), | ||
| status_code, | ||
| attempt_status: None, | ||
| connector_transaction_id: None, | ||
| network_decline_code: None, | ||
| network_advice_code: None, | ||
| network_error_message: None, | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Search for TSYS test files in multiple locations
echo "=== Searching for TSYS test files ==="
find . -type f -name "*tsys*test*" 2>/dev/null | head -20
echo ""
echo "=== Searching for test directory structure ==="
ls -la backend/grpc-server/tests/ 2>/dev/null | grep -i tsys || echo "No TSYS files in backend/grpc-server/tests/"
echo ""
echo "=== Checking for any test files in connector-integration/tests ==="
find backend/connector-integration -type d -name tests 2>/dev/null
echo ""
echo "=== Searching for payment_flows_test files ==="
find . -type f -name "*payment_flows_test*" 2>/dev/null | head -20Repository: juspay/connector-service
Length of output: 1617
Add end-to-end test file for TSYS connector payment flows.
The TSYS connector implementation lacks test coverage. Per project guidelines, create backend/grpc-server/tests/tsys_payment_flows_test.rs with comprehensive end-to-end tests covering:
- Authorize (Auth and Sale variants) — success and failure
- Capture — success and failure
- Void — success and failure
- Refund — success and failure
- Payment Sync (PSync) — success and failure
- Refund Sync (RSync) — success and failure
🤖 Prompt for AI Agents
In backend/connector-integration/src/connectors/tsys/transformers.rs lines
1-949, add an end-to-end test file at
backend/grpc-server/tests/tsys_payment_flows_test.rs that implements
comprehensive integration tests for TSYS covering: Authorize (Auth and Sale)
success and failure, Capture success and failure, Void success and failure,
Refund success and failure, PSync success and failure, and RSync success and
failure; the tests should initialize any required test fixtures/config
(credentials or test doubles), send realistic requests exercising the TryFrom
conversions and HTTP serializers/deserializers, assert expected
PaymentsResponseData/RefundsResponseData and ErrorResponse shapes and status
codes, and clean up test state. Ensure tests run under the project test harness
(use #[tokio::test] or #[test] as appropriate), avoid leaking secrets, and mock
or stub external HTTP interactions when running in CI so tests are
deterministic.
Description
Motivation and Context
Flows implemented ,tested and compared using Diff Checker:
Additional Changes
How did you test it?
ucs validation
https://github.com/juspay/hyperswitch-cloud/issues/11912?issue=juspay%7Chyperswitch-cloud%7C12928