Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(router): add support for co-badged cards #5801

Open
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

AkshayaFoiger
Copy link
Contributor

@AkshayaFoiger AkshayaFoiger commented Sep 5, 2024

Type of Change

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

Description

Pass the card network parameter only when it is a cobadged cards.
Extend support for Stripe, Cybersource, Bank of America and Adyen

How did you test it?

  1. Create a payment with a single network card. This card_network will not be stored in customer table. Check this through the list customer call
curl --location 'http://localhost:8080/payments' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key: dev_b75nmOwRvw5gNyjZAWXdUumMZcMxhZ7l1qjFTsXVeSy5vu4XaRWsyRNHYAKytUv0' \
--data-raw '{
    "amount": 6540,
    "currency": "USD",
    "confirm": true,
    "capture_method": "automatic",
    "capture_on": "2022-09-10T10:11:12Z",
    "amount_to_capture": 6540,
     "email": "[email protected]",
     "customer_id":"tester1t23",   
    "setup_future_usage": "on_session",
    "customer_acceptance": {
        "acceptance_type": "offline",
        "accepted_at": "1963-05-03T04:07:52.723Z",
        "online": {
            "ip_address": "13.232.74.226",
            "user_agent": "amet irure esse"
        }
    },   
    "payment_method": "card",
    "payment_method_type": "credit",
    "payment_method_data": {
        "card": {
            "card_number":  "4111111145551142",
            "card_exp_month": "03",
            "card_exp_year": "30",
            "card_holder_name": "joseph Doe",
            "card_cvc": "737"
            ,"card_network":"Visa"
        }
    } 
    ,"billing": {
        "address": {
            "line1": "1467",  
            "city": "San Fransico",
            "state": "California",
            "zip": "94122",
            "country": "AU",
            "first_name": "joseph",
            "last_name": "Doe"
        },

        "email": "[email protected]"
    }
   
}'

Response

{
    "payment_id": "pay_NwW4LYz9iEZkMeuSKXft",
    "merchant_id": "postman_merchant_GHAction_8eb204cb-aa8d-483d-b27b-3ef6fc47ab31",
    "status": "succeeded",
    "amount": 6540,
    "net_amount": 6540,
    "amount_capturable": 0,
    "amount_received": 6540,
    "connector": "cybersource",
    "client_secret": "pay_NwW4LYz9iEZkMeuSKXft_secret_IreftgKvq0jnG4MldOQc",
    "created": "2024-09-05T09:47:49.673Z",
    "currency": "USD",
    "customer_id": "tester1t23",
    "customer": {
        "id": "tester1t23",
        "name": null,
        "email": "[email protected]",
        "phone": null,
        "phone_country_code": null
    },
    "description": null,
    "refunds": null,
    "disputes": null,
    "mandate_id": null,
    "mandate_data": null,
    "setup_future_usage": "on_session",
    "off_session": null,
    "capture_on": null,
    "capture_method": "automatic",
    "payment_method": "card",
    "payment_method_data": {
        "card": {
            "last4": "1142",
            "card_type": null,
            "card_network": null,
            "card_issuer": null,
            "card_issuing_country": null,
            "card_isin": "411111",
            "card_extended_bin": null,
            "card_exp_month": "03",
            "card_exp_year": "30",
            "card_holder_name": null,
            "payment_checks": {
                "avs_response": {
                    "code": "Y",
                    "codeRaw": "Y"
                },
                "card_verification": null
            },
            "authentication_data": null
        },
        "billing": null
    },
    "payment_token": "token_527Zf3zNRip0gxt9QeVf",
    "shipping": null,
    "billing": {
        "address": {
            "city": "San Fransico",
            "country": "AU",
            "line1": "1467",
            "line2": null,
            "line3": null,
            "zip": "94122",
            "state": "California",
            "first_name": "joseph",
            "last_name": "Doe"
        },
        "phone": null,
        "email": "[email protected]"
    },
    "order_details": null,
    "email": "[email protected]",
    "name": null,
    "phone": null,
    "return_url": null,
    "authentication_type": "no_three_ds",
    "statement_descriptor_name": null,
    "statement_descriptor_suffix": null,
    "next_action": null,
    "cancellation_reason": null,
    "error_code": null,
    "error_message": null,
    "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": {
        "customer_id": "tester1t23",
        "created_at": 1725529669,
        "expires": 1725533269,
        "secret": "epk_760f4ce026c24e519cce4d38c35e888a"
    },
    "manual_retry_allowed": false,
    "connector_transaction_id": "7255296708566381703954",
    "frm_message": null,
    "metadata": null,
    "connector_metadata": null,
    "feature_metadata": null,
    "reference_id": "pay_NwW4LYz9iEZkMeuSKXft_1",
    "payment_link": null,
    "profile_id": "pro_CSLvpqjADa20jwma38FN",
    "surcharge_details": null,
    "attempt_count": 1,
    "merchant_decision": null,
    "merchant_connector_id": "mca_NTHtXpk2TQGyNnDOBThk",
    "incremental_authorization_allowed": false,
    "authorization_count": null,
    "incremental_authorizations": null,
    "external_authentication_details": null,
    "external_3ds_authentication_attempted": false,
    "expires_on": "2024-09-05T10:02:49.673Z",
    "fingerprint": null,
    "browser_info": null,
    "payment_method_id": null,
    "payment_method_status": null,
    "updated": "2024-09-05T09:47:51.480Z",
    "charges": null,
    "frm_metadata": null,
    "merchant_order_reference_id": null
}

List customers

curl --location 'http://localhost:8080/customers/tester1t23/payment_methods' \
--header 'Accept: application/json' \
--header 'api-key: dev_b75nmOwRvw5gNyjZAWXdUumMZcMxhZ7l1qjFTsXVeSy5vu4XaRWsyRNHYAKytUv0'

Response

{
    "customer_payment_methods": [
        {
            "payment_token": "token_lrX87aNGoBlxpWc2T7zO",
            "payment_method_id": "pm_ErZgUBSEjuBeubhuv5bY",
            "customer_id": "tester1t23",
            "payment_method": "card",
            "payment_method_type": "credit",
            "payment_method_issuer": null,
            "payment_method_issuer_code": null,
            "recurring_enabled": false,
            "installment_payment_enabled": false,
            "payment_experience": [
                "redirect_to_url"
            ],
            "card": {
                "scheme": null,
                "issuer_country": null,
                "last4_digits": "1142",
                "expiry_month": "03",
                "expiry_year": "30",
                "card_token": null,
                "card_holder_name": "joseph Doe",
                "card_fingerprint": null,
                "nick_name": null,
                "card_network": null,
                "card_isin": "411111",
                "card_issuer": null,
                "card_type": null,
                "saved_to_locker": true
            },
            "metadata": null,
            "created": "2024-09-05T09:47:51.496Z",
            "bank": null,
            "surcharge_details": null,
            "requires_cvv": true,
            "last_used_at": "2024-09-05T09:47:51.496Z",
            "default_payment_method_set": false,
            "billing": {
                "address": {
                    "city": "San Fransico",
                    "country": "AU",
                    "line1": "1467",
                    "line2": null,
                    "line3": null,
                    "zip": "94122",
                    "state": "California",
                    "first_name": "joseph",
                    "last_name": "Doe"
                },
                "phone": null,
                "email": "[email protected]"
            }
        },
        {
            "payment_token": "token_Cd9No4xgXqf1XaGdRntl",
            "payment_method_id": "pm_fJS1pbXWg2n1HpPjZb1V",
            "customer_id": "tester1t23",
            "payment_method": "card",
            "payment_method_type": "credit",
            "payment_method_issuer": null,
            "payment_method_issuer_code": null,
            "recurring_enabled": false,
            "installment_payment_enabled": false,
            "payment_experience": [
                "redirect_to_url"
            ],
            "card": {
                "scheme": null,
                "issuer_country": null,
                "last4_digits": "1142",
                "expiry_month": "03",
                "expiry_year": "30",
                "card_token": null,
                "card_holder_name": "joseph Doe",
                "card_fingerprint": null,
                "nick_name": null,
                "card_network": null,
                "card_isin": "411111",
                "card_issuer": null,
                "card_type": null,
                "saved_to_locker": true
            },
            "metadata": null,
            "created": "2024-09-05T09:29:57.100Z",
            "bank": null,
            "surcharge_details": null,
            "requires_cvv": true,
            "last_used_at": "2024-09-05T09:29:57.100Z",
            "default_payment_method_set": true,
            "billing": {
                "address": {
                    "city": "San Fransico",
                    "country": "AU",
                    "line1": "1467",
                    "line2": null,
                    "line3": null,
                    "zip": "94122",
                    "state": "California",
                    "first_name": "joseph",
                    "last_name": "Doe"
                },
                "phone": null,
                "email": "[email protected]"
            }
        }
    ],
    "is_guest_customer": null
}

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

@AkshayaFoiger AkshayaFoiger requested review from a team as code owners September 5, 2024 06:31
Copy link

semanticdiff-com bot commented Sep 5, 2024

Review changes with SemanticDiff.

Analyzed 9 of 9 files.

Overall, the semantic diff is 9% smaller than the GitHub diff.

Filename Status
✔️ crates/router/src/consts.rs 2.12% smaller
✔️ crates/router/src/core/payments/helpers.rs Analyzed
✔️ crates/router/src/connector/stripe/transformers.rs Analyzed
✔️ crates/router/src/connector/cybersource/transformers.rs 28.69% smaller
✔️ crates/router/src/connector/bankofamerica/transformers.rs 13.6% smaller
✔️ crates/router/src/connector/adyen/transformers.rs 4.68% smaller
✔️ crates/common_utils/src/consts.rs 2.55% smaller
✔️ crates/common_utils/src/types.rs Analyzed
✔️ crates/api_models/src/payments.rs 2.41% smaller

@AkshayaFoiger AkshayaFoiger self-assigned this Sep 5, 2024
@AkshayaFoiger AkshayaFoiger added A-core Area: Core flows C-feature Category: Feature request or enhancement A-connector-integration Area: Connector integration labels Sep 5, 2024
Copy link
Contributor

@sai-harsha-vardhan sai-harsha-vardhan left a comment

Choose a reason for hiding this comment

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

Can you please add detailed description of the approach in PR description?

@@ -1280,6 +1280,16 @@ impl GetAddressFromPaymentMethodData for Card {

impl Card {
fn apply_additional_card_info(&self, additional_card_info: AdditionalCardInfo) -> Self {
let req_card_brand = self
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you please use the same naming convention as card_network here?

.clone()
.or(additional_card_info.card_network);

let card_network = match req_card_brand.clone().map(|_| self.is_cobadged_card()) {
Copy link
Contributor

Choose a reason for hiding this comment

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

why is map required here?

@@ -1303,6 +1310,28 @@ impl Card {
nick_name: self.nick_name.clone(),
}
}
fn is_cobadged_card(&self) -> bool {
let c_card_number_value = self.card_number.get_card_no();
let card_number_length = i32::try_from(c_card_number_value.len()).ok();
Copy link
Contributor

Choose a reason for hiding this comment

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

we can avoid these conversions right?

Lazy::new(|| {
let mut map = HashMap::new();
map.insert(common_enums::CardNetwork::Maestro, CardNetworkPattern {
regex: Regex::new(r"^(5018|5081|5044|504681|504993|5020|502260|5038|603845|603123|6304|6759|676[1-3]|6220|504834|504817|504645|504775|600206|627741)[0-9]{0,15}$").ok(),
Copy link
Contributor

Choose a reason for hiding this comment

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

isn't it unsafe to ignore these errors?

card_network_data.allowed_cvc_length.contains(&len)
})
{
matching_networks += 1;
Copy link
Contributor

Choose a reason for hiding this comment

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

we can break the loop once matching_networks > 1 and return true

@@ -136,3 +140,69 @@ pub const MAX_ALLOWED_AMOUNT: i64 = 999999999;
//payment attempt default unified error code and unified error message
pub const DEFAULT_UNIFIED_ERROR_CODE: &str = "UE_000";
pub const DEFAULT_UNIFIED_ERROR_MESSAGE: &str = "Something went wrong";

/// Regex for Identifying Card Network
pub const CARD_NETWORK_DATA: Lazy<HashMap<common_enums::CardNetwork, CardNetworkPattern>> =
Copy link
Contributor

Choose a reason for hiding this comment

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

we can reuse the CARD_NETWORK_DATA from common_utils consts

@@ -5265,3 +5274,26 @@ pub async fn validate_merchant_connector_ids_in_connector_mandate_details(
}
Ok(())
}

pub fn is_cobadged_card(card_number: CardNumber, card_cvc: masking::Secret<String>) -> bool {
Copy link
Contributor

Choose a reason for hiding this comment

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

can we please move this to impl of Card domain model

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-connector-integration Area: Connector integration A-core Area: Core flows C-feature Category: Feature request or enhancement
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants