feat(connector): [TSYS] Added Integrity Check Support for Authorize, PSync, Refund and RSync Flows
Type of Change
- [ ] New feature
- [ ] Bugfix
- [x] Enhancement
- [ ] Refactoring
- [ ] Dependency updates
- [ ] Documentation
- [ ] CI/CD
Issue No.
Fixes #9230
Description
In this PR, we have added integrity checks for Authorize, PSync, Capture, Refund, and RSync flows for the TSYS Connector.
What is an integrity check?
A scenario where there is a discrepancy between the amount sent in the request and the amount received from the connector, which is checked during response handling.
- Added integrity object creation for all five flows in TSYS connector
- Used helper functions from
utils.rsto generate integrity objects - Added proper error handling with
.change_context()for clearer error messages - Added info logs to track integrity object attachment
- Used proper amount field for each flow:
amountfor PSync,minor_amountfor Authorize,minor_amount_to_capturefor Capture, andminor_refund_amountfor Refunds
Additional Changes
- [ ] This PR modifies the API contract
- [ ] This PR modifies the database schema
- [ ] This PR modifies application configuration/environment variables
Motivation and Context
This PR holds the payment in a non-terminal state in case there is any data discrepancy in the above flows, improving reliability and security. It also resolves a startup issue caused by missing configuration.
How did you test it?
Case 1: AUTHORIZE Flow
- Used the TSYS connector's Authorize flow and tested with:
- Hardcoded different amount in the mock to simulate connector discrepancy
- Verified integrity check failure when amounts don't match
- Confirmed successful integrity check when amounts match
Case 2: PAYMENT SYNC Flow
- Used the TSYS connector's PSync flow and tested with:
- Forced payment sync with mismatched amount values
- Verified integrity check failure message and non-terminal status
- Confirmed success case when amounts match
Case 3: CAPTURE Flow (Manual Capture)
- Used the TSYS connector with manual capture method and tested with:
- Created payment with
capture_method: "manual" - Attempted capture with different amount than original authorization
- Verified integrity check error for mismatched amounts
- Confirmed successful capture with matching amounts
- Created payment with
Case 4: REFUND Flow
- Used the TSYS connector's refund flow and tested with:
- Created payment and successful capture
- Initiated refund with mismatched amounts
- Verified integrity check failure and payment held in review status
- Confirmed successful refund with matching amounts
Case 5: REFUND SYNC Flow
- Used the TSYS connector's refund sync flow and tested with:
- Created refund and forced sync
- Simulated response with mismatched amounts
- Verified integrity check failure
- Confirmed successful handling with matching amounts
Checklist
- [x] I formatted the code using
cargo +nightly fmt --all - [x] I addressed lints thrown by
cargo clippy - [x] I reviewed the submitted code
- [x] I added unit tests for my changes where possible
Feature Description/Summary
Integrity check is a scenario where there is a discrepancy between the amount sent in the request and the amount received from the connector, which is checked during response handling.
Implementation Details
- Added integrity object creation for all five flows in TSYS connector
- Used helper functions from
utils.rsto generate integrity objects - Added proper error handling with
.change_context()for clearer error messages - Added info logs to track integrity object attachment
- Used proper amount field for each flow:
amountfor PSync,minor_amountfor Authorize,minor_amount_to_capturefor Capture, andminor_refund_amountfor Refunds
Test Cases
Authorize
Request :
{
"amount": 2500,
"currency": "AED",
"connector": ["tsys"],
"confirm": true,
"capture_method": "automatic",
"capture_on": "2022-09-10T10:11:12Z",
"amount_to_capture": 2500,
"customer_id": "abcdef",
"email": "[email protected]",
"name": "John Doe",
"phone": "999999999",
"phone_country_code": "+65",
"description": "Its my first payment request",
"authentication_type": "no_three_ds",
"return_url": "https://duck.com",
"payment_method": "card",
"payment_method_type": "credit",
"payment_method_data": {
"card": {
"card_number": "4002400000000002",
"card_exp_month": "12",
"card_exp_year": "2028",
"card_holder_name": "Max Mustermann",
"card_cvc": "999"
}
},
"billing": {
"address": {
"line1": "1467",
"line2": "Harrison Street",
"line3": "Harrison Street",
"city": "San Fransico",
"state": "California",
"zip": "94122",
"first_name": "joseph",
"last_name": "Doe"
},
"phone": {
"number": "9123456789",
"country_code": "+91"
}
},
"shipping": {
"address": {
"line1": "1467",
"line2": "Harrison Street",
"line3": "Harrison Street",
"city": "San Fransico",
"state": "California",
"zip": "94122",
"first_name": "joseph",
"last_name": "Doe"
},
"phone": {
"number": "9123456789",
"country_code": "+91"
}
},
"statement_descriptor_name": "joseph",
"statement_descriptor_suffix": "JS",
"browser_info": {
"user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36",
"accept_header": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
"language": "nl-NL",
"color_depth": 24,
"screen_height": 723,
"screen_width": 1536,
"time_zone": 0,
"java_enabled": true,
"java_script_enabled": true,
"ip_address": "109.71.40.0"
}
}
Response:
{
"error": {
"type": "api",
"message": "Integrity Check Failed! as data mismatched for amount expected 2500 but found 10",
"code": "IE_00",
"connector_transaction_id": "67102339"
}
}
PSync
{
"payment_id": "pay_sOm1KUFScqQtqoHU2cau",
"merchant_id": "merchant_1760530422",
"status": "conflicted",
"amount": 2500,
"net_amount": 2500,
"shipping_cost": null,
"amount_capturable": 0,
"amount_received": 2500,
"connector": "tsys",
"client_secret": "pay_sOm1KUFScqQtqoHU2cau_secret_11lpvi2WNBZdZQRKvwBY",
"created": "2025-10-15T12:15:42.959Z",
"currency": "AED",
"customer_id": "abcdef",
"customer": {
"id": "abcdef",
"name": "John Doe",
"email": "[email protected]",
"phone": "999999999",
"phone_country_code": "+65"
},
"description": "Its my first payment request",
"refunds": null,
"disputes": null,
"mandate_id": null,
"mandate_data": null,
"setup_future_usage": null,
"off_session": null,
"capture_on": null,
"capture_method": "automatic",
"payment_method": "card",
"payment_method_data": {
"card": {
"last4": "0002",
"card_type": null,
"card_network": null,
"card_issuer": null,
"card_issuing_country": null,
"card_isin": "400240",
"card_extended_bin": null,
"card_exp_month": "12",
"card_exp_year": "2028",
"card_holder_name": "Max Mustermann",
"payment_checks": null,
"authentication_data": null
},
"billing": null
},
"payment_token": "token_gizQEEPj4a3Z0tR68dSF",
"shipping": {
"address": {
"city": "San Fransico",
"country": null,
"line1": "1467",
"line2": "Harrison Street",
"line3": "Harrison Street",
"zip": "94122",
"state": "California",
"first_name": "joseph",
"last_name": "Doe",
"origin_zip": null
},
"phone": {
"number": "9123456789",
"country_code": "+91"
},
"email": null
},
"billing": {
"address": {
"city": "San Fransico",
"country": null,
"line1": "1467",
"line2": "Harrison Street",
"line3": "Harrison Street",
"zip": "94122",
"state": "California",
"first_name": "joseph",
"last_name": "Doe",
"origin_zip": null
},
"phone": {
"number": "9123456789",
"country_code": "+91"
},
"email": null
},
"order_details": null,
"email": "[email protected]",
"name": "John Doe",
"phone": "999999999",
"return_url": "https://duck.com/",
"authentication_type": "no_three_ds",
"statement_descriptor_name": "joseph",
"statement_descriptor_suffix": "JS",
"next_action": null,
"cancellation_reason": null,
"error_code": "IE",
"error_message": "Integrity Check Failed! Value mismatched for fields amount expected 2500 but found 10",
"unified_code": null,
"unified_message": null,
"payment_experience": null,
"payment_method_type": "credit",
"connector_label": null,
"business_country": null,
"business_label": "default",
"business_sub_label": null,
"allowed_payment_method_types": null,
"ephemeral_key": null,
"manual_retry_allowed": null,
"connector_transaction_id": "67101609",
"frm_message": null,
"metadata": null,
"connector_metadata": null,
"feature_metadata": {
"redirect_response": null,
"search_tags": null,
"apple_pay_recurring_details": null,
"gateway_system": "direct"
},
"reference_id": null,
"payment_link": null,
"profile_id": "pro_WuHKzhKnO83bOhPlQXSL",
"surcharge_details": null,
"attempt_count": 1,
"merchant_decision": null,
"merchant_connector_id": "mca_camUR87QSGIErnkIEFY3",
"incremental_authorization_allowed": null,
"authorization_count": null,
"incremental_authorizations": null,
"external_authentication_details": null,
"external_3ds_authentication_attempted": false,
"expires_on": "2025-10-15T12:30:42.959Z",
"fingerprint": null,
"browser_info": {
"os_type": null,
"referer": null,
"language": "nl-NL",
"time_zone": 0,
"ip_address": "109.71.40.0",
"os_version": null,
"user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36",
"color_depth": 24,
"device_model": null,
"java_enabled": true,
"screen_width": 1536,
"accept_header": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
"screen_height": 723,
"accept_language": "en",
"java_script_enabled": true
},
"payment_channel": null,
"payment_method_id": null,
"network_transaction_id": null,
"payment_method_status": null,
"updated": "2025-10-15T12:16:06.791Z",
"split_payments": null,
"frm_metadata": null,
"extended_authorization_applied": null,
"request_extended_authorization": null,
"capture_before": null,
"merchant_order_reference_id": null,
"order_tax_amount": null,
"connector_mandate_id": null,
"card_discovery": "manual",
"force_3ds_challenge": false,
"force_3ds_challenge_trigger": false,
"issuer_error_code": null,
"issuer_error_message": null,
"is_iframe_redirection_enabled": null,
"whole_connector_response": null,
"enable_partial_authorization": null,
"enable_overcapture": null,
"is_overcapture_enabled": null,
"network_details": null,
"is_stored_credential": null,
"mit_category": null
}
Capture
{
"error": {
"type": "api",
"message": "Integrity Check Failed! as data mismatched for capture_amount expected 2500 but found 10",
"code": "IE_00",
"connector_transaction_id": "67102683"
}
}
Refund
Request:
{
"payment_id": "{{payment_id}}",
"amount": 2500,
"reason": "Customer returned product",
"refund_type": "instant"
}
Response:
{
"refund_id": "ref_KzuLnsdCWbJA6buDsf5S",
"payment_id": "pay_LdonKASrJYn4fyQXUvCO",
"amount": 2500,
"currency": "AED",
"status": "review",
"reason": "Customer returned product",
"metadata": null,
"error_message": "Integrity Check Failed! as data mismatched for fields refund_amount expected 2500 but found 10",
"error_code": "IE",
"unified_code": null,
"unified_message": null,
"created_at": "2025-10-15T12:27:09.057Z",
"updated_at": "2025-10-15T12:27:10.230Z",
"connector": "tsys",
"profile_id": "pro_WuHKzhKnO83bOhPlQXSL",
"merchant_connector_id": "mca_camUR87QSGIErnkIEFY3",
"split_refunds": null,
"issuer_error_code": null,
"issuer_error_message": null
}
RSync
{
"refund_id": "ref_KzuLnsdCWbJA6buDsf5S",
"payment_id": "pay_LdonKASrJYn4fyQXUvCO",
"amount": 2500,
"currency": "AED",
"status": "review",
"reason": "Customer returned product",
"metadata": null,
"error_message": "Integrity Check Failed! as data mismatched for fields refund_amount expected 2500 but found 10",
"error_code": "IE",
"unified_code": null,
"unified_message": null,
"created_at": "2025-10-15T12:27:09.057Z",
"updated_at": "2025-10-15T12:29:00.039Z",
"connector": "tsys",
"profile_id": "pro_WuHKzhKnO83bOhPlQXSL",
"merchant_connector_id": "mca_camUR87QSGIErnkIEFY3",
"split_refunds": null,
"issuer_error_code": null,
"issuer_error_message": null
}
@Vani-1107 Hello ma'am, I have removed the logger from all flows. Pl let me know if there are any further changes required!!
@deepanshu-iiitu Hello Sir, Pl provide your review for this PR or any further changes to be made !!
@Vani-1107 @deepanshu-iiitu Hello there, Kindly provide me with the next review. I can't be assigned next issue until this one gets merged. Thats why I request to make it done asap, so that I can properly work on next issues.