Skip to content

Conversation

Debarshi-Gupta
Copy link
Contributor

@Debarshi-Gupta Debarshi-Gupta commented Jan 27, 2025

Type of Change

  • Bugfix
  • New feature
  • Enhancement
  • Refactoring
  • Dependency updates
  • Documentation
  • CI/CD

Description

Problem Statement:

Card testing attacks involve fraudsters using stolen credit card details to make small transactions on e-commerce sites using different combinations of CVC to verify if the CVC is valid. Successful transactions indicate the card is active and the CVC is valid, while failed ones are discarded. This is done to avoid detection before committing larger fraudulent purchases.

Solution:

We have implemented three rules for detecting and preventing this card testing attacks:

I) Card <> IP Blocking for Merchant : If there are X number of unsuccessful payment attempts from a single IP Address for a specific merchant, then that combination of Card <> IP will be blocked for that merchant for a particular duration.

II) Card Blocking for Guest Users for Merchant: If there are X number of unsuccessful payment attempts for a single card, then guest user payments will be blocked for that card and that merchant for a particular duration. Logged in customers will still be able to use that card for payment.

III) Customer ID Blocking for Merchant: If there are X number of unsuccessful payment attempts from a single Customer ID, then that customer ID will be blocked from making any payments for a particular duration.

These unsuccessful payment thresholds and duration for blocking is configurable and stored in database.
Whether the merchants want these rules to be enabled/disabled, that is also configurable.

Implementation:

The attacker gets the client_secret from the payment intent create call which happens through our SDK, and uses the client_secret to hit the /confirm API repeatedly with different sets of card numbers and CVC.

A method validate_request_with_state has been created in the GetTracker trait which enforces the three above mentioned rules depending on the business_profile config. The validate_request_with_state method internally calls validate_card_ip_blocking_for_business_profile method, validate_guest_user_card_blocking_for_business_profile method and validate_customer_id_blocking_for_business_profile method for performing the below validations:

I) Card<>IP Blocking: A fingerprint is generated for the card which is unique to the business_profile (by using a hash key which is unique to the profile and stored as card_testing_secret_key in business_profile). This is done so that the cards are blocked at a profile level. The IP Address is fetched from the payment intent create request, and then for each unsuccessful payment attempt, the value of the redis key CardFingerprint<>IP is increased by 1, and after it reaches a certain threshold, it is blocked.

II) Guest User Card Blocking: A fingerprint is generated for the card which is unique to the business_profile (by using a hash key which is unique to the profile and stored as card_testing_secret_key in business_profile). This is done so that the cards are not blocked at a profile level. For each unsuccessful payment attempt, the value of the redis key CardFingerprint is increased by 1, and after it reaches a certain threshold, it is blocked, but only for guest users. Logged in customers can still make payments with that card.

III) Customer ID Blocking: The customer_id is fetched from the payment intent create call request, and then for each unsuccessful payment attempt, the value of redis key CardFingerprint<>Profile is increased by 1, and after it reaches a certain threshold, it is blocked.

Additional Changes

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

Motivation and Context

How did you test it?

Postman Tests

Step By Step Guide to Test:

  1. Use Adyen Connector to test the curls (because for Adyen, only a specific CVC works for test cards)
  2. Update Business Profile to set card_ip_blocking_status as enabled, guest_user_card_blocking_status as disabled and customer_id_blocking_status as disabled
  3. Now that card_ip_blocking is enabled, start hitting the endpoints repeatedly with incorrect CVC to make the payments fail (Use curl provided below). After X attempts (determined by card_ip_blocking_threshold), the payment attempt will be blocked.
  4. Update Business Profile to set card_ip_blocking_status as disabled, guest_user_card_blocking_status as enabled and customer_id_blocking_status as disabled
  5. Now that guest_user_card_blocking is enabled, start hitting the endpoints repeatedly with incorrect CVC to make the payments fail (Use curl provided below). After X attempts (determined by guest_user_card_blocking_threshold), the payment attempt will be blocked.
  6. Update Business Profile to set card_ip_blocking_status as disabled, guest_user_card_blocking_status as disabled and customer_id_blocking_status as enabled
  7. Now that customer_id_blocking is enabled, start hitting the endpoints repeatedly with incorrect CVC to make the payments fail (Use curl provided below). After X attempts (determined by customer_id_blocking_threshold), the payment attempt will be blocked.

Update Business Profile (For enabling card_ip_blocking)

-Request

curl --location 'http://localhost:8080/account/postman_merchant_GHAction_20cfc2e0-4fb3-4fde-a322-a5d4412b1596/business_profile/pro_VvEqQhEZBLj6WGrH4efC' \
--header 'Content-Type: application/json' \
--header 'api-key: test_admin' \
--data-raw '{
    "profile_name": "Update5",
    "return_url": "https://google.com/success",
    "enable_payment_response_hash": true,
    "redirect_to_merchant_with_http_post": false,
    "webhook_details": {
        "webhook_version": "1.0.1",
        "webhook_username": "ekart_retail",
        "webhook_password": "password_ekart@123",
        "webhook_url": "https://webhook.site",
        "payment_created_enabled": true,
        "payment_succeeded_enabled": true,
        "payment_failed_enabled": true
    },
    "metadata": null,
    
    
    
    
    "intent_fulfillment_time": 900,
    "frm_routing_algorithm": null,
    "payout_routing_algorithm": null,
    "applepay_verified_domains": null,
    "session_expiry": 900,
    "payment_link_config": {
        "display_billing_details": true,
        "hide_card_nickname_field": true
    },
    "authentication_connector_details": null,
    "use_billing_as_payment_method_billing": true,
    "collect_shipping_details_from_wallet_connector": false,
    "collect_billing_details_from_wallet_connector": false,
    "is_connector_agnostic_mit_enabled": false,
    "payout_link_config": null,
    "outgoing_webhook_custom_http_headers": null,
    "card_testing_guard_config": {
        "card_ip_blocking_status": "enabled",
        "card_ip_blocking_threshold": 3,
        "guest_user_card_blocking_status": "disabled",
        "guest_user_card_blocking_threshold": 10,
        "customer_id_blocking_status": "disabled",
        "customer_id_blocking_threshold": 5,
        "card_testing_guard_expiry": 3600
    }
}'

-Response

{
    "merchant_id": "postman_merchant_GHAction_20cfc2e0-4fb3-4fde-a322-a5d4412b1596",
    "profile_id": "pro_VvEqQhEZBLj6WGrH4efC",
    "profile_name": "Update5",
    "return_url": "https://google.com/success",
    "enable_payment_response_hash": true,
    "payment_response_hash_key": "5C72AqxtJV2P2YfLQoIF7L72uuz9srr3H290cKiA8pEaR0H7kPWqmC8fB6Y8Xe4Y",
    "redirect_to_merchant_with_http_post": false,
    "webhook_details": {
        "webhook_version": "1.0.1",
        "webhook_username": "ekart_retail",
        "webhook_password": "password_ekart@123",
        "webhook_url": "https://webhook.site",
        "payment_created_enabled": true,
        "payment_succeeded_enabled": true,
        "payment_failed_enabled": true
    },
    "metadata": null,
    "routing_algorithm": null,
    "intent_fulfillment_time": 900,
    "frm_routing_algorithm": null,
    "payout_routing_algorithm": null,
    "applepay_verified_domains": null,
    "session_expiry": 900,
    "payment_link_config": {
        "domain_name": null,
        "theme": null,
        "logo": null,
        "seller_name": null,
        "sdk_layout": null,
        "display_sdk_only": null,
        "enabled_saved_payment_method": null,
        "hide_card_nickname_field": true,
        "show_card_form_by_default": null,
        "transaction_details": null,
        "background_image": null,
        "details_layout": null,
        "payment_button_text": null,
        "business_specific_configs": null,
        "allowed_domains": null,
        "branding_visibility": null
    },
    "authentication_connector_details": null,
    "use_billing_as_payment_method_billing": true,
    "extended_card_info_config": null,
    "collect_shipping_details_from_wallet_connector": false,
    "collect_billing_details_from_wallet_connector": false,
    "always_collect_shipping_details_from_wallet_connector": false,
    "always_collect_billing_details_from_wallet_connector": false,
    "is_connector_agnostic_mit_enabled": false,
    "payout_link_config": null,
    "outgoing_webhook_custom_http_headers": null,
    "tax_connector_id": null,
    "is_tax_connector_enabled": false,
    "is_network_tokenization_enabled": false,
    "is_auto_retries_enabled": false,
    "max_auto_retries_enabled": null,
    "is_click_to_pay_enabled": false,
    "authentication_product_ids": null,
    "card_testing_guard_config": {
        "card_ip_blocking_status": "enabled",
        "card_ip_blocking_threshold": 3,
        "guest_user_card_blocking_status": "disabled",
        "guest_user_card_blocking_threshold": 10,
        "customer_id_blocking_status": "disabled",
        "customer_id_blocking_threshold": 5,
        "card_testing_guard_expiry": 3600
    }
}

1. Card <> IP Blocking (Below Threshold)

-Request

curl --location 'http://localhost:8080/payments' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key: dev_DXWmMAIEepKhfTAZd13oawyvbN7eNbPHY16nLqlZynQOAOcqdKRWBstt5cQSS7LO' \
--data-raw '{
    "amount": 12345,
    "currency": "USD",
    "confirm": true,
    "capture_method": "automatic",
    "capture_on": "2022-09-10T10:11:12Z",
    "amount_to_capture": 12345,
    "customer_id": "abcd",
    "email": "[email protected]",
    "name": "John Doe",
    "phone": "999999999",
    "phone_country_code": "+1",
    "description": "Its my first payment request",
    "authentication_type": "no_three_ds",
    "return_url": "https://duck.com",
    "payment_method": "card",
    "payment_method_data": {
        "card": {
            "card_number": "4212345678901237",
            "card_exp_month": "03",
            "card_exp_year": "30",
            "card_holder_name": "John Smith",
            "card_cvc": "739"
        }
    },
    "billing": {
        "address": {
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "city": "San Fransico",
            "state": "California",
            "zip": "94122",
            "country": "US",
            "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",
            "country": "US",
            "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": "125.0.0.6"
    }
}'

-Response

{
    "payment_id": "pay_tVK43iCoFLj3giMpq1aR",
    "merchant_id": "postman_merchant_GHAction_7522c600-57f2-4a82-b4f4-9df401d240ab",
    "status": "failed",
    "amount": 12345,
    "net_amount": 12345,
    "shipping_cost": null,
    "amount_capturable": 0,
    "amount_received": null,
    "connector": "adyen",
    "client_secret": "pay_tVK43iCoFLj3giMpq1aR_secret_slmMPbx0VVLowhjqrwF9",
    "created": "2025-01-27T07:16:16.548Z",
    "currency": "USD",
    "customer_id": "abcd",
    "customer": {
        "id": "abcd",
        "name": "John Doe",
        "email": "[email protected]",
        "phone": "999999999",
        "phone_country_code": "+1"
    },
    "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": "1237",
            "card_type": null,
            "card_network": null,
            "card_issuer": null,
            "card_issuing_country": null,
            "card_isin": "421234",
            "card_extended_bin": null,
            "card_exp_month": "03",
            "card_exp_year": "30",
            "card_holder_name": "John Smith",
            "payment_checks": null,
            "authentication_data": null
        },
        "billing": null
    },
    "payment_token": null,
    "shipping": {
        "address": {
            "city": "San Fransico",
            "country": "US",
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "zip": "94122",
            "state": "California",
            "first_name": "joseph",
            "last_name": "Doe"
        },
        "phone": {
            "number": "9123456789",
            "country_code": "+91"
        },
        "email": null
    },
    "billing": {
        "address": {
            "city": "San Fransico",
            "country": "US",
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "zip": "94122",
            "state": "California",
            "first_name": "joseph",
            "last_name": "Doe"
        },
        "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": "24",
    "error_message": "CVC Declined",
    "unified_code": "UE_9000",
    "unified_message": "Something went wrong",
    "payment_experience": null,
    "payment_method_type": null,
    "connector_label": null,
    "business_country": null,
    "business_label": "default",
    "business_sub_label": null,
    "allowed_payment_method_types": null,
    "ephemeral_key": {
        "customer_id": "abcd",
        "created_at": 1737962176,
        "expires": 1737965776,
        "secret": "epk_76f0a10b19ad4a91bd597b75680b4afb"
    },
    "manual_retry_allowed": true,
    "connector_transaction_id": "D3W4SJB5J4P8CX65",
    "frm_message": null,
    "metadata": null,
    "connector_metadata": null,
    "feature_metadata": null,
    "reference_id": null,
    "payment_link": null,
    "profile_id": "pro_6nc1JRTymtljmHmhnTEt",
    "surcharge_details": null,
    "attempt_count": 1,
    "merchant_decision": null,
    "merchant_connector_id": "mca_wpJDRDrWhfV2oZR77bFX",
    "incremental_authorization_allowed": false,
    "authorization_count": null,
    "incremental_authorizations": null,
    "external_authentication_details": null,
    "external_3ds_authentication_attempted": false,
    "expires_on": "2025-01-27T07:31:16.548Z",
    "fingerprint": null,
    "browser_info": {
        "language": "nl-NL",
        "time_zone": 0,
        "ip_address": "125.0.0.6",
        "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,
        "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,
        "java_script_enabled": true
    },
    "payment_method_id": null,
    "payment_method_status": null,
    "updated": "2025-01-27T07:16:19.648Z",
    "split_payments": null,
    "frm_metadata": null,
    "merchant_order_reference_id": null,
    "order_tax_amount": null,
    "connector_mandate_id": null
}

2. Card <> IP Blocking (After threshold)

-Request

curl --location 'http://localhost:8080/payments' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key: dev_DXWmMAIEepKhfTAZd13oawyvbN7eNbPHY16nLqlZynQOAOcqdKRWBstt5cQSS7LO' \
--data-raw '{
    "amount": 12345,
    "currency": "USD",
    "confirm": true,
    "capture_method": "automatic",
    "capture_on": "2022-09-10T10:11:12Z",
    "amount_to_capture": 12345,
    "customer_id": "abcd",
    "email": "[email protected]",
    "name": "John Doe",
    "phone": "999999999",
    "phone_country_code": "+1",
    "description": "Its my first payment request",
    "authentication_type": "no_three_ds",
    "return_url": "https://duck.com",
    "payment_method": "card",
    "payment_method_data": {
        "card": {
            "card_number": "4212345678901237",
            "card_exp_month": "03",
            "card_exp_year": "30",
            "card_holder_name": "John Smith",
            "card_cvc": "739"
        }
    },
    "billing": {
        "address": {
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "city": "San Fransico",
            "state": "California",
            "zip": "94122",
            "country": "US",
            "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",
            "country": "US",
            "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": "125.0.0.6"
    }
}'

-Response

{
    "error": {
        "type": "invalid_request",
        "message": "Blocked due to suspicious activity",
        "code": "IR_16"
    }
}

3. Guest User Card Blocking (Below Threshold)

-Request

curl --location 'http://localhost:8080/payments' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key: dev_DXWmMAIEepKhfTAZd13oawyvbN7eNbPHY16nLqlZynQOAOcqdKRWBstt5cQSS7LO' \
--data-raw '{
    "amount": 12345,
    "currency": "USD",
    "confirm": true,
    "capture_method": "automatic",
    "capture_on": "2022-09-10T10:11:12Z",
    "amount_to_capture": 12345,
    
    "email": "[email protected]",
    "name": "John Doe",
    "phone": "999999999",
    "phone_country_code": "+1",
    "description": "Its my first payment request",
    "authentication_type": "no_three_ds",
    "return_url": "https://duck.com",
    "payment_method": "card",
    "payment_method_data": {
        "card": {
            "card_number": "4212345678901237",
            "card_exp_month": "03",
            "card_exp_year": "30",
            "card_holder_name": "John Smith",
            "card_cvc": "739"
        }
    },
    "billing": {
        "address": {
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "city": "San Fransico",
            "state": "California",
            "zip": "94122",
            "country": "US",
            "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",
            "country": "US",
            "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": "125.0.0.6"
    }
}'

-Response

{
    "payment_id": "pay_6FU271N4pLzdJAxJEr49",
    "merchant_id": "postman_merchant_GHAction_7522c600-57f2-4a82-b4f4-9df401d240ab",
    "status": "failed",
    "amount": 12345,
    "net_amount": 12345,
    "shipping_cost": null,
    "amount_capturable": 0,
    "amount_received": null,
    "connector": "adyen",
    "client_secret": "pay_6FU271N4pLzdJAxJEr49_secret_wvcjDfb9IucJ4G2m9P0O",
    "created": "2025-01-27T07:20:24.599Z",
    "currency": "USD",
    "customer_id": null,
    "customer": {
        "id": null,
        "name": "John Doe",
        "email": "[email protected]",
        "phone": "999999999",
        "phone_country_code": "+1"
    },
    "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": "1237",
            "card_type": null,
            "card_network": null,
            "card_issuer": null,
            "card_issuing_country": null,
            "card_isin": "421234",
            "card_extended_bin": null,
            "card_exp_month": "03",
            "card_exp_year": "30",
            "card_holder_name": "John Smith",
            "payment_checks": null,
            "authentication_data": null
        },
        "billing": null
    },
    "payment_token": null,
    "shipping": {
        "address": {
            "city": "San Fransico",
            "country": "US",
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "zip": "94122",
            "state": "California",
            "first_name": "joseph",
            "last_name": "Doe"
        },
        "phone": {
            "number": "9123456789",
            "country_code": "+91"
        },
        "email": null
    },
    "billing": {
        "address": {
            "city": "San Fransico",
            "country": "US",
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "zip": "94122",
            "state": "California",
            "first_name": "joseph",
            "last_name": "Doe"
        },
        "phone": {
            "number": "9123456789",
            "country_code": "+91"
        },
        "email": null
    },
    "order_details": null,
    "email": null,
    "name": null,
    "phone": null,
    "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": "24",
    "error_message": "CVC Declined",
    "unified_code": "UE_9000",
    "unified_message": "Something went wrong",
    "payment_experience": null,
    "payment_method_type": null,
    "connector_label": null,
    "business_country": null,
    "business_label": "default",
    "business_sub_label": null,
    "allowed_payment_method_types": null,
    "ephemeral_key": null,
    "manual_retry_allowed": true,
    "connector_transaction_id": "NTQV5DFG3XPPT9V5",
    "frm_message": null,
    "metadata": null,
    "connector_metadata": null,
    "feature_metadata": null,
    "reference_id": null,
    "payment_link": null,
    "profile_id": "pro_6nc1JRTymtljmHmhnTEt",
    "surcharge_details": null,
    "attempt_count": 1,
    "merchant_decision": null,
    "merchant_connector_id": "mca_wpJDRDrWhfV2oZR77bFX",
    "incremental_authorization_allowed": false,
    "authorization_count": null,
    "incremental_authorizations": null,
    "external_authentication_details": null,
    "external_3ds_authentication_attempted": false,
    "expires_on": "2025-01-27T07:35:24.599Z",
    "fingerprint": null,
    "browser_info": {
        "language": "nl-NL",
        "time_zone": 0,
        "ip_address": "125.0.0.6",
        "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,
        "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,
        "java_script_enabled": true
    },
    "payment_method_id": null,
    "payment_method_status": null,
    "updated": "2025-01-27T07:20:26.282Z",
    "split_payments": null,
    "frm_metadata": null,
    "merchant_order_reference_id": null,
    "order_tax_amount": null,
    "connector_mandate_id": null
}

4. Guest User Card Blocking (After Threshold & Guest User)

-Request

curl --location 'http://localhost:8080/payments' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key: dev_DXWmMAIEepKhfTAZd13oawyvbN7eNbPHY16nLqlZynQOAOcqdKRWBstt5cQSS7LO' \
--data-raw '{
    "amount": 12345,
    "currency": "USD",
    "confirm": true,
    "capture_method": "automatic",
    "capture_on": "2022-09-10T10:11:12Z",
    "amount_to_capture": 12345,
    
    "email": "[email protected]",
    "name": "John Doe",
    "phone": "999999999",
    "phone_country_code": "+1",
    "description": "Its my first payment request",
    "authentication_type": "no_three_ds",
    "return_url": "https://duck.com",
    "payment_method": "card",
    "payment_method_data": {
        "card": {
            "card_number": "4212345678901237",
            "card_exp_month": "03",
            "card_exp_year": "30",
            "card_holder_name": "John Smith",
            "card_cvc": "739"
        }
    },
    "billing": {
        "address": {
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "city": "San Fransico",
            "state": "California",
            "zip": "94122",
            "country": "US",
            "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",
            "country": "US",
            "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": "125.0.0.6"
    }
}'

-Response

{
    "error": {
        "type": "invalid_request",
        "message": "Blocked due to suspicious activity",
        "code": "IR_16"
    }
}

5. Customer ID Blocking (Below Threshold)

-Request

curl --location 'http://localhost:8080/payments' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key: dev_DXWmMAIEepKhfTAZd13oawyvbN7eNbPHY16nLqlZynQOAOcqdKRWBstt5cQSS7LO' \
--data-raw '{
    "amount": 12345,
    "currency": "USD",
    "confirm": true,
    "capture_method": "automatic",
    "capture_on": "2022-09-10T10:11:12Z",
    "amount_to_capture": 12345,
    "customer_id": "abcd",
    "email": "[email protected]",
    "name": "John Doe",
    "phone": "999999999",
    "phone_country_code": "+1",
    "description": "Its my first payment request",
    "authentication_type": "no_three_ds",
    "return_url": "https://duck.com",
    "payment_method": "card",
    "payment_method_data": {
        "card": {
            "card_number": "4212345678901237",
            "card_exp_month": "03",
            "card_exp_year": "30",
            "card_holder_name": "John Smith",
            "card_cvc": "739"
        }
    },
    "billing": {
        "address": {
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "city": "San Fransico",
            "state": "California",
            "zip": "94122",
            "country": "US",
            "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",
            "country": "US",
            "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": "125.0.0.6"
    }
}'

-Response

{
    "payment_id": "pay_k631dMCrATBssb71COuI",
    "merchant_id": "postman_merchant_GHAction_7522c600-57f2-4a82-b4f4-9df401d240ab",
    "status": "failed",
    "amount": 12345,
    "net_amount": 12345,
    "shipping_cost": null,
    "amount_capturable": 0,
    "amount_received": null,
    "connector": "adyen",
    "client_secret": "pay_k631dMCrATBssb71COuI_secret_P5VvWWn8owaGR9cONsTA",
    "created": "2025-01-27T07:23:44.184Z",
    "currency": "USD",
    "customer_id": "abcd",
    "customer": {
        "id": "abcd",
        "name": "John Doe",
        "email": "[email protected]",
        "phone": "999999999",
        "phone_country_code": "+1"
    },
    "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": "1237",
            "card_type": null,
            "card_network": null,
            "card_issuer": null,
            "card_issuing_country": null,
            "card_isin": "421234",
            "card_extended_bin": null,
            "card_exp_month": "03",
            "card_exp_year": "30",
            "card_holder_name": "John Smith",
            "payment_checks": null,
            "authentication_data": null
        },
        "billing": null
    },
    "payment_token": null,
    "shipping": {
        "address": {
            "city": "San Fransico",
            "country": "US",
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "zip": "94122",
            "state": "California",
            "first_name": "joseph",
            "last_name": "Doe"
        },
        "phone": {
            "number": "9123456789",
            "country_code": "+91"
        },
        "email": null
    },
    "billing": {
        "address": {
            "city": "San Fransico",
            "country": "US",
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "zip": "94122",
            "state": "California",
            "first_name": "joseph",
            "last_name": "Doe"
        },
        "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": "24",
    "error_message": "CVC Declined",
    "unified_code": "UE_9000",
    "unified_message": "Something went wrong",
    "payment_experience": null,
    "payment_method_type": null,
    "connector_label": null,
    "business_country": null,
    "business_label": "default",
    "business_sub_label": null,
    "allowed_payment_method_types": null,
    "ephemeral_key": {
        "customer_id": "abcd",
        "created_at": 1737962624,
        "expires": 1737966224,
        "secret": "epk_365d150a51ac498a8be408a0e048fe9a"
    },
    "manual_retry_allowed": true,
    "connector_transaction_id": "BMF7X5SNLCWNM9V5",
    "frm_message": null,
    "metadata": null,
    "connector_metadata": null,
    "feature_metadata": null,
    "reference_id": null,
    "payment_link": null,
    "profile_id": "pro_6nc1JRTymtljmHmhnTEt",
    "surcharge_details": null,
    "attempt_count": 1,
    "merchant_decision": null,
    "merchant_connector_id": "mca_wpJDRDrWhfV2oZR77bFX",
    "incremental_authorization_allowed": false,
    "authorization_count": null,
    "incremental_authorizations": null,
    "external_authentication_details": null,
    "external_3ds_authentication_attempted": false,
    "expires_on": "2025-01-27T07:38:44.184Z",
    "fingerprint": null,
    "browser_info": {
        "language": "nl-NL",
        "time_zone": 0,
        "ip_address": "125.0.0.6",
        "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,
        "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,
        "java_script_enabled": true
    },
    "payment_method_id": null,
    "payment_method_status": null,
    "updated": "2025-01-27T07:23:45.746Z",
    "split_payments": null,
    "frm_metadata": null,
    "merchant_order_reference_id": null,
    "order_tax_amount": null,
    "connector_mandate_id": null
}

6. Customer ID Blocking (After Threshold)

-Request

curl --location 'http://localhost:8080/payments' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key: dev_DXWmMAIEepKhfTAZd13oawyvbN7eNbPHY16nLqlZynQOAOcqdKRWBstt5cQSS7LO' \
--data-raw '{
    "amount": 12345,
    "currency": "USD",
    "confirm": true,
    "capture_method": "automatic",
    "capture_on": "2022-09-10T10:11:12Z",
    "amount_to_capture": 12345,
    "customer_id": "abcd",
    "email": "[email protected]",
    "name": "John Doe",
    "phone": "999999999",
    "phone_country_code": "+1",
    "description": "Its my first payment request",
    "authentication_type": "no_three_ds",
    "return_url": "https://duck.com",
    "payment_method": "card",
    "payment_method_data": {
        "card": {
            "card_number": "4212345678901237",
            "card_exp_month": "03",
            "card_exp_year": "30",
            "card_holder_name": "John Smith",
            "card_cvc": "739"
        }
    },
    "billing": {
        "address": {
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "city": "San Fransico",
            "state": "California",
            "zip": "94122",
            "country": "US",
            "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",
            "country": "US",
            "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": "125.0.0.6"
    }
}'

-Response

{
    "error": {
        "type": "invalid_request",
        "message": "Blocked due to suspicious activity",
        "code": "IR_16"
    }
}

Cypress Tests

image

Checklist

  • I formatted the code cargo +nightly fmt --all
  • I addressed lints thrown by cargo clippy
  • I reviewed the submitted code
  • I added unit tests for my changes where possible

@Debarshi-Gupta Debarshi-Gupta requested review from a team as code owners January 27, 2025 04:37
Copy link

semanticdiff-com bot commented Jan 27, 2025

Review changes with  SemanticDiff

Changed Files
File Status
  crates/openapi/src/openapi_v2.rs  9% smaller
  crates/openapi/src/openapi.rs  8% smaller
  crates/router/src/types/api/admin.rs  5% smaller
  crates/router/src/core/payments/helpers.rs  1% smaller
  crates/hyperswitch_domain_models/src/business_profile.rs  1% smaller
  crates/router/src/core/admin.rs  1% smaller
  api-reference-v2/openapi_spec.json  0% smaller
  api-reference/openapi_spec.json  0% smaller
  crates/api_models/src/admin.rs  0% smaller
  crates/common_utils/src/consts.rs  0% smaller
  crates/diesel_models/src/business_profile.rs  0% smaller
  crates/diesel_models/src/schema.rs  0% smaller
  crates/diesel_models/src/schema_v2.rs  0% smaller
  crates/hyperswitch_domain_models/src/card_testing_guard_data.rs  0% smaller
  crates/hyperswitch_domain_models/src/lib.rs  0% smaller
  crates/router/src/consts.rs  0% smaller
  crates/router/src/core.rs  0% smaller
  crates/router/src/core/card_testing_guard.rs  0% smaller
  crates/router/src/core/card_testing_guard/utils.rs  0% smaller
  crates/router/src/core/payments.rs  0% smaller
  crates/router/src/core/payments/operations.rs  0% smaller
  crates/router/src/core/payments/operations/payment_approve.rs  0% smaller
  crates/router/src/core/payments/operations/payment_cancel.rs  0% smaller
  crates/router/src/core/payments/operations/payment_capture.rs  0% smaller
  crates/router/src/core/payments/operations/payment_complete_authorize.rs  0% smaller
  crates/router/src/core/payments/operations/payment_confirm.rs  0% smaller
  crates/router/src/core/payments/operations/payment_create.rs  0% smaller
  crates/router/src/core/payments/operations/payment_post_session_tokens.rs  0% smaller
  crates/router/src/core/payments/operations/payment_reject.rs  0% smaller
  crates/router/src/core/payments/operations/payment_response.rs  0% smaller
  crates/router/src/core/payments/operations/payment_session.rs  0% smaller
  crates/router/src/core/payments/operations/payment_start.rs  0% smaller
  crates/router/src/core/payments/operations/payment_status.rs  0% smaller
  crates/router/src/core/payments/operations/payment_update.rs  0% smaller
  crates/router/src/core/payments/operations/payments_incremental_authorization.rs  0% smaller
  crates/router/src/core/payments/operations/tax_calculation.rs  0% smaller
  crates/router/src/services.rs  0% smaller
  crates/router/src/services/card_testing_guard.rs  0% smaller
  crates/router/src/types/transformers.rs  0% smaller
  migrations/2025-01-27-113914_add_card_testing_guard_config_column_to_business_profile/down.sql Unsupported file format
  migrations/2025-01-27-113914_add_card_testing_guard_config_column_to_business_profile/up.sql Unsupported file format

@hyperswitch-bot hyperswitch-bot bot added the M-database-changes Metadata: This PR involves database schema changes label Jan 27, 2025
@Debarshi-Gupta Debarshi-Gupta self-assigned this Jan 27, 2025
@Debarshi-Gupta Debarshi-Gupta linked an issue Jan 27, 2025 that may be closed by this pull request
2 tasks
@hyperswitch-bot hyperswitch-bot bot added the M-api-contract-changes Metadata: This PR involves API contract changes label Jan 27, 2025
pub const DEFAULT_CUSTOMER_ID_BLOCKING_THRESHOLD: i32 = 5;

/// Default Card Testing Guard Redis Expiry
pub const DEFAULT_CARD_TESTING_GUARD_EXPIRY: i32 = 3600;
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
pub const DEFAULT_CARD_TESTING_GUARD_EXPIRY: i32 = 3600;
pub const DEFAULT_CARD_TESTING_GUARD_EXPIRY_IN_SECS: i32 = 3600;

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have resolved it, thanks

@hyperswitch-bot hyperswitch-bot bot removed the M-api-contract-changes Metadata: This PR involves API contract changes label Jan 29, 2025
@Debarshi-Gupta Debarshi-Gupta changed the title feat(core) [Card Testing Guard] : Implement Card Testing Guard feat(core): [Card Testing Guard] Implement Card Testing Guard Jan 29, 2025
@Debarshi-Gupta Debarshi-Gupta added the M-api-contract-changes Metadata: This PR involves API contract changes label Jan 29, 2025
@Debarshi-Gupta Debarshi-Gupta requested review from a team as code owners January 29, 2025 06:08
@Debarshi-Gupta Debarshi-Gupta force-pushed the card-testing-solution branch 2 times, most recently from 1ff0d20 to b871ec9 Compare January 29, 2025 13:52
Comment on lines 239 to 254
pub struct CardTestingGuardConfig {
/// Determines if Card IP Blocking is enabled for profile
pub is_card_ip_blocking_enabled: bool,
/// Determines the unsuccessful payment threshold for Card IP Blocking for profile
pub card_ip_blocking_threshold: i32,
/// Determines if Guest User Card Blocking is enabled for profile
pub is_guest_user_card_blocking_enabled: bool,
/// Determines the unsuccessful payment threshold for Guest User Card Blocking for profile
pub guest_user_card_blocking_threshold: i32,
/// Determines if Customer Id Blocking is enabled for profile
pub is_customer_id_blocking_enabled: bool,
/// Determines the unsuccessful payment threshold for Customer Id Blocking for profile
pub customer_id_blocking_threshold: i32,
/// Determines Redis Expiry for Card Testing Guard for profile
pub card_testing_guard_expiry: i32,
}
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
pub struct CardTestingGuardConfig {
/// Determines if Card IP Blocking is enabled for profile
pub is_card_ip_blocking_enabled: bool,
/// Determines the unsuccessful payment threshold for Card IP Blocking for profile
pub card_ip_blocking_threshold: i32,
/// Determines if Guest User Card Blocking is enabled for profile
pub is_guest_user_card_blocking_enabled: bool,
/// Determines the unsuccessful payment threshold for Guest User Card Blocking for profile
pub guest_user_card_blocking_threshold: i32,
/// Determines if Customer Id Blocking is enabled for profile
pub is_customer_id_blocking_enabled: bool,
/// Determines the unsuccessful payment threshold for Customer Id Blocking for profile
pub customer_id_blocking_threshold: i32,
/// Determines Redis Expiry for Card Testing Guard for profile
pub card_testing_guard_expiry: i32,
}
pub struct CardTestingGuardConfig {
/// Determines if Card IP Blocking is enabled for profile
pub card_ip_blocking: Enabled/Disabled,
/// Determines the unsuccessful payment threshold for Card IP Blocking for profile
pub card_ip_blocking_threshold: i32,
/// Determines if Guest User Card Blocking is enabled for profile
pub guest_user_card_blocking: Enabled/Disable,
/// Determines the unsuccessful payment threshold for Guest User Card Blocking for profile
pub guest_user_card_blocking_threshold: i32,
/// Determines if Customer Id Blocking is enabled for profile
pub customer_id_blocking: Enabled/Disabled,
/// Determines the unsuccessful payment threshold for Customer Id Blocking for profile
pub customer_id_blocking_threshold: i32,
/// Determines Redis Expiry for Card Testing Guard for profile
pub card_testing_guard_expiry_in_secs: i32,
}
Avoid using boolean instead use enum, and add unit when it comes to SI units.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have resolved it, thanks

/// * The Fingerprint encryption operation fails
pub async fn get_merchant_profile_fingerprint_secret(
state: &SessionState,
merchant_id: &common_utils::id_type::MerchantId,
Copy link
Member

Choose a reason for hiding this comment

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

Pass merchant_key_store as a argument to this function, utils function shouldn't read data from database and already merchant key store is populated in the handler after authentication

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Since we are no longer generating cardtesting_secret_key in oltp flow, this function has been removed.


match merchant_card_testing_secret_key {
Some(card_testing_secret_key) => Ok(card_testing_secret_key.get_inner().clone().expose()),
None => {
Copy link
Member

Choose a reason for hiding this comment

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

If there secret is not there we should throw error, we can't randomly create a key and insert that to profiles in OLTP flow. Profiles will get updated only in admin flows.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have resolved it, thanks. The card_testing_secret_key will now only be updated in profile create and update flows.

pub card_testing_guard_data: Option<CardTestingGuardData>,
}

#[derive(Clone, serde::Serialize, Debug)]
Copy link
Member

Choose a reason for hiding this comment

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

Move this to domain_models, avoid writing types in payments.rs file.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have resolved it, thanks

}
}
Ok(None) => Ok(cache_key),
Err(_) => Err(errors::ApiErrorResponse::InternalServerError)?,
Copy link
Member

Choose a reason for hiding this comment

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

Avoid discarding the error, the error should be lifted into internalservererror.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have resolved it, thanks

}
}
Ok(None) => Ok(cache_key),
Err(_) => Err(errors::ApiErrorResponse::InternalServerError)?,
Copy link
Member

Choose a reason for hiding this comment

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

Avoid discarding the error, the error should be lifted into internalservererror.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have resolved it, thanks.

}
}
Ok(None) => Ok(cache_key),
Err(_) => Err(errors::ApiErrorResponse::InternalServerError)?,
Copy link
Member

Choose a reason for hiding this comment

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

Avoid discarding the error, the error should be lifted into internalservererror.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have resolved it, thanks

let unsuccessful_payment_threshold = card_testing_guard_config.card_ip_blocking_threshold;

match services::card_testing_guard::get_blocked_count_from_cache(state, &cache_key).await {
Ok(Some(unsuccessful_payment_count)) => {
Copy link
Member

Choose a reason for hiding this comment

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

Handling response is same for all validate functions means, refactor the code and avoid repetition.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have resolved it, thanks

jarnura
jarnura previously approved these changes Feb 5, 2025
state: &SessionState,
request: &api::PaymentsRequest,
payment_method_data: Option<&api_models::payments::PaymentMethodData>,
payment_data: &mut PaymentData<F>,
Copy link
Member

Choose a reason for hiding this comment

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

Don't mutate paymentData in this function, instead pass the output, in the caller update PaymentData

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have resolved it, thanks.

jarnura
jarnura previously approved these changes Feb 6, 2025
@ThisIsMani ThisIsMani removed the request for review from a team February 6, 2025 11:22
jarnura
jarnura previously approved these changes Feb 10, 2025
Sarthak1799
Sarthak1799 previously approved these changes Feb 10, 2025
@Gnanasundari24 Gnanasundari24 added this pull request to the merge queue Feb 18, 2025
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to no response for status checks Feb 18, 2025
@Gnanasundari24 Gnanasundari24 added this pull request to the merge queue Feb 20, 2025
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to failed status checks Feb 20, 2025
@Debarshi-Gupta Debarshi-Gupta dismissed stale reviews from Sarthak1799 and jarnura via 554629d February 20, 2025 12:58
jarnura
jarnura previously approved these changes Feb 21, 2025
@Gnanasundari24 Gnanasundari24 added this pull request to the merge queue Feb 24, 2025
Merged via the queue into main with commit 2451e9b Feb 25, 2025
17 of 20 checks passed
@Gnanasundari24 Gnanasundari24 deleted the card-testing-solution branch February 25, 2025 01:14
pixincreate added a commit to pixincreate/hyperswitch that referenced this pull request Feb 25, 2025
…tion-fork

* 'main' of github.com:juspay/hyperswitch:
  feat(core): [Card Testing Guard] Implement Card Testing Guard  (juspay#7108)
  chore(version): 2025.02.25.0
  ci: minor refactors and improvements (juspay#7308)
  fix(connector): [NMI] Added enum for Void Reason (juspay#7221)
  ci(CI-pr): disable `cargo-hack` step for hotfix PRs (juspay#7334)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
M-api-contract-changes Metadata: This PR involves API contract changes M-database-changes Metadata: This PR involves database schema changes
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[FEATURE] Card Testing Guard
4 participants