Skip to content

Conversation

@siaass842
Copy link
Contributor

@siaass842 siaass842 commented Dec 5, 2025

Description

Motivation and Context

Flows implemented ,tested and compared using Diff Checker:

  • Auth
  • Capture
  • Void
  • Refund
  • Rsync
  • Psync

Additional Changes

  • This PR modifies the API contract
  • This PR modifies application configuration/environment variables

How did you test it?

ucs validation
https://github.com/juspay/hyperswitch-cloud/issues/11912?issue=juspay%7Chyperswitch-cloud%7C12928

@siaass842 siaass842 requested review from a team as code owners December 5, 2025 05:32
@siaass842 siaass842 self-assigned this Dec 5, 2025
@siaass842 siaass842 added GRACE coderabbit-ai-review Trigger CodeRabbit review labels Dec 5, 2025
@siaass842 siaass842 changed the title Tsys connector integration feat(connector): [TSYS] Connector Integration Dec 5, 2025
@coderabbitai
Copy link

coderabbitai bot commented Dec 5, 2025

📝 Walkthrough

Walkthrough

Adds 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

Cohort / File(s) Summary
Connector registration
backend/connector-integration/src/connectors.rs
Declared pub mod tsys; and re-exported Tsys (pub use self::tsys::Tsys;).
TSYS connector surface
backend/connector-integration/src/connectors/tsys.rs
Added TSYS connector implementation: ConnectorCommon impl, connector wiring, flow trait implementations (Authorize, PSync, Capture, Void, Refund, RSync), ValidationTrait, SourceVerification, header helpers, URL builders, macro-driven flow wiring, and stubs for unsupported flows; exposes transformers module and crate-private headers.
TSYS transformers
backend/connector-integration/src/connectors/tsys/transformers.rs
Added request/response types, auth mapping (TsysAuthType), TryFrom/From conversions, status translation, error mapping, and helpers for Authorize, PSync, Capture, Void, Refund, RSync flows.
Connector enum
backend/domain_types/src/connector_types.rs
Added Tsys variant to ConnectorEnum and updated ForeignTryFrom<grpc_api_types::payments::Connector> mapping to include Tsys.
Connectors config type
backend/domain_types/src/types.rs
Added pub tsys: ConnectorParams field to Connectors struct.
Connector factory
backend/connector-integration/src/types.rs
Added match arm for ConnectorEnum::Tsys in convert_connector, returning connectors::Tsys::new().
Environment configuration
config/development.toml, config/sandbox.toml, config/production.toml
Added [connectors].tsys.base_url entries: https://stagegw.transnox.com/ (development, sandbox) and https://gateway.transit-pass.com/ (production).

Pre-merge checks

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 40.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: adding TSYS connector integration with appropriate feature prefix and connector name.
Description check ✅ Passed The description is directly related to the changeset, detailing the implemented flows (Auth, Capture, Void, Refund, Rsync, Psync) and testing methodology.

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c7a6aff and c2e21a5.

📒 Files selected for processing (1)
  • backend/connector-integration/src/connectors/tsys/transformers.rs (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/**/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: 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.
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: 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: 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-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/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/**/{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/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 : 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 : 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-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-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/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
📚 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-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/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
🧬 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 (9)
backend/connector-integration/src/connectors/tsys/transformers.rs (9)

27-53: LGTM!

Authentication type properly implements TryFrom with appropriate error handling and uses Secret<String> for all sensitive fields.


163-308: LGTM!

Authorize response handling properly maps TSYS response types to domain statuses, stores transaction IDs correctly, and handles both success and error cases appropriately.


310-412: LGTM!

Capture and Void response handlers properly map TSYS responses to domain statuses with appropriate error handling.


418-484: LGTM!

PSync request construction properly uses get_connector_transaction_id() helper and handles missing transaction ID with appropriate error.


512-596: LGTM!

PSync response handling properly extracts transaction details, maps statuses, and handles both success and error cases.


598-726: LGTM!

Capture and Void request builders properly construct TSYS-specific payloads with correct field mappings, amount conversions, and transaction ID handling.


809-847: LGTM!

Refund response handling properly extracts connector refund ID, maps statuses, and handles errors appropriately.


849-930: LGTM!

RSync flow properly reuses sync request/response structures with correct field mappings for refund sync operations.


936-951: LGTM!

Error response helper properly maps TSYS error details to domain error response, addressing the previously flagged hardcoding issue.


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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a 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; and pub 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_TYPE constant is defined but never used. The build_headers function (lines 274-283) uses self.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:

  1. Documenting the expected values from TSYS
  2. Using case-insensitive comparison if appropriate
  3. 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_response helper returns generic hardcoded messages ("Payment failed", "Transaction declined") regardless of the actual TSYS error. When TsysResponseTypes::ErrorResponse is matched throughout the file, the actual response_code and response_message are 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

📥 Commits

Reviewing files that changed from the base of the PR and between c1f21bd and 62f91c4.

📒 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.rs
  • backend/domain_types/src/types.rs
  • backend/domain_types/src/connector_types.rs
  • backend/connector-integration/src/types.rs
  • 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 : 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.rs
  • backend/domain_types/src/types.rs
  • backend/domain_types/src/connector_types.rs
  • backend/connector-integration/src/types.rs
  • backend/connector-integration/src/connectors/tsys.rs
  • 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/**/{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
  • backend/domain_types/src/types.rs
  • backend/domain_types/src/connector_types.rs
  • backend/connector-integration/src/types.rs
  • config/production.toml
  • 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 : 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
  • backend/domain_types/src/connector_types.rs
  • backend/connector-integration/src/types.rs
  • 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 : 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
  • backend/domain_types/src/connector_types.rs
  • backend/connector-integration/src/types.rs
  • backend/connector-integration/src/connectors/tsys.rs
  • 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/domain_types/src/types.rs
  • backend/domain_types/src/connector_types.rs
  • backend/connector-integration/src/types.rs
  • backend/connector-integration/src/connectors/tsys.rs
  • 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/domain_types/src/types.rs
  • backend/domain_types/src/connector_types.rs
  • 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/**/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.rs
  • backend/connector-integration/src/types.rs
  • 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/domain_types/src/connector_types.rs
  • 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/**/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.rs
  • backend/connector-integration/src/types.rs
  • backend/connector-integration/src/connectors/tsys.rs
  • 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/domain_types/src/connector_types.rs
  • backend/connector-integration/src/connectors/tsys.rs
  • 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 : 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.rs
  • backend/connector-integration/src/connectors/tsys.rs
  • 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} : 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.toml
  • config/development.toml
  • config/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.toml
  • config/development.toml
  • config/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.rs
  • 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-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 for ConnectorEnum::Tsys is consistent

The new arm ConnectorEnum::Tsys => Box::new(connectors::Tsys::new()), follows the same pattern as other connectors and correctly wires TSYS into get_connector_by_name. This aligns with the guideline to register new connectors inside convert_connector.

config/sandbox.toml (1)

24-24: Validate TSYS stage URL choice for sandbox

Sandbox 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 in ConnectorEnum and gRPC mapping looks correct

Adding Tsys to ConnectorEnum (with serialize_all = "snake_case") and mapping grpc_api_types::payments::Connector::Tsys => Ok(Self::Tsys) keeps naming consistent with the tsys config 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 wired

Adding pub tsys: ConnectorParams to Connectors is consistent with existing connector fields and works with Default/serde. Please just confirm:

  • tsys entries exist in all active env configs (dev/sandbox/prod and any others you deploy), and
  • TSYS is also registered in ConnectorEnum and convert_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 ConnectorCommon implementation 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 for id(), get_currency_unit(), get_auth_header(), base_url(), and build_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 TsysAuthType correctly extracts authentication credentials from SignatureKey with proper Secret<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_response helper appropriately populates resource_id and connector_response_reference_id with 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/Voided and errors to CaptureFailed/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 for PaymentsCaptureData that uses the ResponseId enum. Based on learnings, this follows the correct approach.


593-635: LGTM!

The Void request correctly accesses connector_transaction_id directly as a String, which matches the PaymentVoidData type behavior noted in learnings.


692-700: Verify TSYS refund status semantics.

The comment states "TSYS marks successful refunds as VOID" which maps TsysTransactionStatus::Void to RefundStatus::Success. This is counterintuitive but may be correct for TSYS. Additionally, Approved maps to Pending which 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_id for querying refund status. The response handling properly converts transaction details to refund status.

Comment on lines 117 to 131
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,
};
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 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".

Copy link

@coderabbitai coderabbitai bot left a 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 -> Pending mapping (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_response helper only uses the HTTP status code and returns hardcoded messages. When TsysResponseTypes::ErrorResponse is received, the actual TsysErrorResponse containing response_code and response_message is 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

📥 Commits

Reviewing files that changed from the base of the PR and between 62f91c4 and 432e30d.

📒 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.rs
  • 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/**/{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
  • 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.rs
  • 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/**/{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.rs
  • 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.rs
  • 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.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.rs
  • 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.rs
  • 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.rs
  • 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-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 transformers module, imports necessary types from domain_types, interfaces, and local crates, and defines the headers module with the CONTENT_TYPE constant. This aligns with the connector directory structure guidelines.


44-95: LGTM! ConnectorCommon implementation is complete and follows conventions.

The implementation correctly:

  • Returns CurrencyUnit::Minor for amounts in cents
  • Uses body-based authentication (empty auth headers)
  • Properly deserializes error responses and emits events

Based on learnings, this aligns with the ConnectorCommon trait requirements for id(), get_currency_unit(), get_auth_header(), base_url(), and build_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 SourceVerification trait 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 ConnectorServiceTrait implementation 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 TsysAuthType struct correctly maps ConnectorAuthType::SignatureKey fields 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: AuthResponse success → Authorized, SaleResponse success → Charged
  • Critical identifiers stored: connector_transaction_id, connector_response_reference_id

Based 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 TryFrom implementations 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 TryFrom implementation properly:

  • Extracts connector_transaction_id using get_connector_transaction_id() with appropriate error handling
  • Uses minor_amount_to_capture for the capture amount
  • Includes all required authentication fields

639-696: LGTM! Void request construction handles transaction ID correctly.

Based on learnings, PaymentVoidData uses connector_transaction_id as a String directly (unlike PaymentsCaptureData which uses ResponseId), 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/TsysSyncResponse types via transparent wrappers
  • Uses connector_refund_id to look up refund status
  • Maps transaction details to refund status using the previously defined From impl

Comment on lines +338 to +352
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))
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 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 3

Repository: 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 1

Repository: 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 -100

Repository: 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 -40

Repository: 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.rs

Repository: 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 -80

Repository: 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 -60

Repository: 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 -20

Repository: 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 -15

Repository: 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.rs

Repository: 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.

Suggested change
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.

Comment on lines +470 to +484
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,
}
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 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 2

Repository: 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 -20

Repository: 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 -20

Repository: 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 5

Repository: 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 -10

Repository: 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 8

Repository: 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 -50

Repository: 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 -lah

Repository: 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/ -i

Repository: 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.rs

Repository: 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.rs

Repository: 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 3

Repository: 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/null

Repository: 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 -150

Repository: 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 2

Repository: 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.

Comment on lines 702 to 711
#[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,
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 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 f

Repository: 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.rs

Repository: 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 -n

Repository: 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.rs

Repository: 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 tsys

Repository: 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 -n

Repository: 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 -20

Repository: 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
done

Repository: 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.

@siaass842 siaass842 requested a review from a team as a code owner December 5, 2025 06:38
Copy link

@coderabbitai coderabbitai bot left a 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

📥 Commits

Reviewing files that changed from the base of the PR and between 432e30d and d953cbe.

📒 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 the ConnectorCommon trait and connector registration. However, only .nextest.toml is 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.

Copy link

@coderabbitai coderabbitai bot left a 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

📥 Commits

Reviewing files that changed from the base of the PR and between 8743f1f and c67c427.

📒 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)

Copy link

@coderabbitai coderabbitai bot left a 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_Server or the appropriate TSYS SSL gateway endpoint (e.g., ssl3.vitalps.net:5003 for 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

📥 Commits

Reviewing files that changed from the base of the PR and between e906cd2 and 82c6835.

📒 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.rs
  • 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 : 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.rs
  • 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 : 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
  • backend/domain_types/src/types.rs
  • config/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.rs
  • backend/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.toml
  • config/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.toml
  • config/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 Tsys type publicly available for use throughout the codebase.

Based on learnings, ensure that:

  • TSYS is registered in ConnectorEnum in backend/domain_types/src/connector_types.rs
  • The convert_connector function in backend/connector-integration/src/types.rs includes the TSYS mapping
backend/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 the beta_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 coverage

The added pub tsys: ConnectorParams follows the existing pattern on Connectors and looks fine structurally. The main things to double‑check outside this file:

  • TSYS is registered everywhere required for a new connector (e.g., ConnectorEnum in connector_types.rs, convert_connector in connector-integration/src/types.rs, marker traits, transformers, etc.).
  • All relevant config environments (development/sandbox/production and any test/local configs) define a tsys entry under connectors so Connectors deserialization doesn’t fail at startup.

Based on learnings, new connectors should be consistently registered and wired across enums, types, and config.

Copy link

@coderabbitai coderabbitai bot left a 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_payments and get_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:

  1. Using exact string comparison: item.transaction_type == "Auth-Only"
  2. 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_ONLY implies physical chip reading capability
  • ON_MERCHANT_PREMISES_ATTENDED implies in-person attended transaction

For 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: Missing developer_id field in TsysReturnRequest.

This was flagged in a previous review. TsysReturnRequest lacks the developer_id field 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 TryFrom implementation at lines 753-762 to include developer_id: auth.developer_id.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 82c6835 and 69a82ad.

📒 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.rs
  • 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/**/{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.rs
  • 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/**/{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.rs
  • 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: 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.rs
  • 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.rs
  • 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.rs
  • 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.rs
  • 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.rs
  • 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.rs
  • 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.rs
  • backend/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.rs
  • 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.rs
  • 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.rs
  • 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-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 ConnectorCommon implementation 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 StringMinorUnit amount 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_payments and refund flows to get_url_refunds as expected.


540-812: LGTM!

The stub implementations for unsupported flows (CreateOrder, CreateAccessToken, SetupMandate, etc.) correctly satisfy the ConnectorServiceTrait requirements 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 TsysAuthType correctly extracts authentication credentials from SignatureKey with proper error handling for unsupported auth types.


226-237: LGTM!

The get_payments_response helper correctly constructs PaymentsResponseData with the connector_transaction_id properly stored in both resource_id and connector_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 TsysTransactionDetails on 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 including developer_id. Based on learnings about PaymentsCaptureData using ResponseId enum, 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, PaymentVoidData uses String directly (unlike PaymentsCaptureData which uses ResponseId enum), so this implementation is correct.


873-905: LGTM!

The RSync flow correctly uses connector_refund_id to search for refund status and reuses the sync request/response structures appropriately.

Comment on lines +59 to +63
#[derive(Debug, Serialize)]
pub enum TsysPaymentsRequest<T: PaymentMethodDataTypes> {
Auth(TsysPaymentAuthSaleRequest<T>),
Sale(TsysPaymentAuthSaleRequest<T>),
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 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.rs

Repository: 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 f

Repository: 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 2

Repository: 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 -100

Repository: 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.rs

Repository: 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 2

Repository: 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 5

Repository: 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 -80

Repository: 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/null

Repository: 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 20

Repository: 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 10

Repository: 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 -80

Repository: 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.rs

Repository: 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.rs

Repository: 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 -30

Repository: juspay/connector-service

Length of output: 50


🏁 Script executed:

# Check the connector trait implementation
rg "impl.*Connector" backend/connector-integration/src/connectors/tsys/ -A 5

Repository: 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.rs

Repository: 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 3

Repository: 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 -80

Repository: 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 -10

Repository: 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 -50

Repository: 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 -n

Repository: 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.rs

Repository: 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 10

Repository: 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 -20

Repository: 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 -50

Repository: 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 -5

Repository: 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 -60

Repository: 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 5

Repository: 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 -30

Repository: 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 -80

Repository: 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 10

Repository: 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.

@seekshiva seekshiva linked an issue Dec 8, 2025 that may be closed by this pull request
shuklatushar226
shuklatushar226 previously approved these changes Dec 9, 2025
Copy link

@coderabbitai coderabbitai bot left a 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 TsysPaymentsRequest enum 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 inner TsysPaymentAuthSaleRequest fields 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 reader
  • terminal_operating_environment: "ON_MERCHANT_PREMISES_ATTENDED" implies physical presence

For 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. The transaction_type field should either:

  1. Be defined as an enum with known TSYS transaction type values, or
  2. Use exact matching (==) instead of substring matching

Per 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: Missing developer_id field in refund request structure.

TsysReturnRequest lacks the developer_id field present in other TSYS request structs (TsysCaptureRequest line 612, TsysCancelRequest line 682, TsysSearchTransactionRequest line 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

📥 Commits

Reviewing files that changed from the base of the PR and between 5a5316f and c7a6aff.

📒 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.rs
  • backend/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.rs
  • 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/**/{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.rs
  • 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 : 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.

Comment on lines 1 to 949
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,
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 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 -20

Repository: 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

coderabbit-ai-review Trigger CodeRabbit review GRACE

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Dec 5 - TSYS connector

3 participants