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(connector): [Paybox] Add mandates Flow for Paybox #6378

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

Conversation

awasthi21
Copy link
Contributor

Type of Change

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

Description

1)Added Mandate flow for Paybox
2) Added a new field additional_payment_method_data in PaymentsAuthorizeData type
3) Added connector_mandate_request_reference_id in RouterData

Additional Changes

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

Motivation and Context

1)Paybox requires the expiration year, month, and mandate ID in our request, added additional_payment_method to fetch card info.
2)Paybox also needs a unique ID(connector_mandate_request_reference_id) from us during CIT, which they'll return with their own ID for MIT payments.

How did you test it?

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

@awasthi21 awasthi21 added A-connector-integration Area: Connector integration A-core Area: Core flows A-mandates Area: Mandate Flows labels Oct 21, 2024
@awasthi21 awasthi21 self-assigned this Oct 21, 2024
@awasthi21 awasthi21 requested review from a team as code owners October 21, 2024 07:38
Copy link

semanticdiff-com bot commented Oct 21, 2024

Review changes with SemanticDiff.

Analyzed 55 of 55 files.

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

File Information
Filename Status
✔️ crates/router/tests/connectors/aci.rs Analyzed
✔️ crates/router/tests/connectors/utils.rs Analyzed
✔️ crates/router/src/consts.rs Analyzed
✔️ crates/router/src/types.rs Analyzed
✔️ crates/router/src/types/storage/payment_method.rs Analyzed
✔️ crates/router/src/types/api/verify_connector.rs Analyzed
✔️ crates/router/src/services/conversion_impls.rs Analyzed
✔️ crates/router/src/core/payments.rs 72.01% smaller
✔️ crates/router/src/core/utils.rs Analyzed
✔️ crates/router/src/core/webhooks/utils.rs Analyzed
✔️ crates/router/src/core/payments/helpers.rs Analyzed
✔️ crates/router/src/core/payments/tokenization.rs 75.97% smaller
✔️ crates/router/src/core/payments/transformers.rs 21.32% smaller
✔️ crates/router/src/core/payments/operations/payment_confirm.rs 35.62% smaller
✔️ crates/router/src/core/payments/operations/payment_create.rs 32.18% smaller
✔️ crates/router/src/core/payments/operations/payment_response.rs 58.53% smaller
✔️ crates/router/src/core/payments/operations/payment_update.rs 55.17% smaller
✔️ crates/router/src/core/mandate/utils.rs Analyzed
✔️ crates/router/src/core/fraud_check/flows/checkout_flow.rs Analyzed
✔️ crates/router/src/core/fraud_check/flows/fulfillment_flow.rs Analyzed
✔️ crates/router/src/core/fraud_check/flows/record_return.rs Analyzed
✔️ crates/router/src/core/fraud_check/flows/sale_flow.rs Analyzed
✔️ crates/router/src/core/fraud_check/flows/transaction_flow.rs Analyzed
✔️ crates/router/src/core/authentication/transformers.rs Analyzed
✔️ crates/router/src/connector/paybox.rs 2.49% smaller
✔️ crates/router/src/connector/utils.rs 3.66% smaller
✔️ crates/router/src/connector/wellsfargo/transformers.rs Analyzed
✔️ crates/router/src/connector/stripe/transformers.rs 16.44% smaller
✔️ crates/router/src/connector/payme/transformers.rs Analyzed
✔️ crates/router/src/connector/paybox/transformers.rs 11.79% smaller
✔️ crates/router/src/connector/nuvei/transformers.rs Analyzed
✔️ crates/router/src/connector/noon/transformers.rs Analyzed
✔️ crates/router/src/connector/multisafepay/transformers.rs 77.65% smaller
✔️ crates/router/src/connector/gocardless/transformers.rs Analyzed
✔️ crates/router/src/connector/globalpay/transformers.rs 35.21% smaller
✔️ crates/router/src/connector/cybersource/transformers.rs Analyzed
✔️ crates/router/src/connector/braintree/transformers.rs Analyzed
✔️ crates/router/src/connector/bankofamerica/transformers.rs Analyzed
✔️ crates/router/src/connector/bamboraapac/transformers.rs Analyzed
✔️ crates/router/src/connector/authorizedotnet/transformers.rs Analyzed
✔️ crates/router/src/connector/adyen/transformers.rs 62.7% smaller
✔️ crates/router/src/connector/aci/transformers.rs Analyzed
✔️ crates/hyperswitch_domain_models/src/router_data.rs Analyzed
✔️ crates/hyperswitch_domain_models/src/router_request_types.rs 0.93% smaller
✔️ crates/hyperswitch_domain_models/src/router_response_types.rs Analyzed
✔️ crates/hyperswitch_domain_models/src/payments/payment_attempt.rs Analyzed
✔️ crates/hyperswitch_connectors/src/utils.rs 80.05% smaller
✔️ crates/hyperswitch_connectors/src/connectors/payeezy/transformers.rs 36.5% smaller
✔️ crates/hyperswitch_connectors/src/connectors/novalnet/transformers.rs 18.38% smaller
✔️ crates/hyperswitch_connectors/src/connectors/nexinets/transformers.rs Analyzed
✔️ crates/hyperswitch_connectors/src/connectors/fiuu/transformers.rs Analyzed
✔️ crates/hyperswitch_connectors/src/connectors/deutschebank/transformers.rs Analyzed
✔️ crates/diesel_models/src/payment_attempt.rs Analyzed
✔️ crates/common_utils/src/lib.rs Analyzed
✔️ crates/api_models/src/payments.rs 13.83% smaller

Comment on lines 1818 to 2122
// payment_method_data is not required during recurring mandate payment, in such case keep default PaymentMethodData as MandatePayment
let additional_payment_method_data = if payment_data.mandate_id.is_some() {
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this comment applicable for below code too?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not needed

crates/router/src/core/authentication/transformers.rs Outdated Show resolved Hide resolved
@@ -145,6 +146,9 @@ impl
integrity_check: Ok(()),
additional_merchant_data: None,
header_payload,
connector_mandate_request_reference_id: Some(common_utils::generate_id_with_len(
Copy link
Contributor

Choose a reason for hiding this comment

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

Same here

@@ -1557,7 +1561,9 @@ async fn payment_response_update_tracker<F: Clone, T: types::Capturable>(
None
};

if router_data.status == enums::AttemptStatus::Charged {
if router_data.status == enums::AttemptStatus::Charged
Copy link
Contributor

Choose a reason for hiding this comment

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

can use matches! here and please add the relevant code comments

@@ -142,6 +142,8 @@ pub const DEFAULT_UNIFIED_ERROR_MESSAGE: &str = "Something went wrong";
// Recon's feature tag
pub const RECON_FEATURE_TAG: &str = "RECONCILIATION AND SETTLEMENT";

pub const CONNECTOR_MANDATE_REQUEST_REFERENCE_ID_LENGTH: usize = 18;
Copy link
Contributor

Choose a reason for hiding this comment

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

please add a relevant code comment here

Copy link
Contributor Author

Choose a reason for hiding this comment

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

added

Comment on lines 1224 to 1225
card_exp_month: String,
card_exp_year: String,
Copy link
Member

Choose a reason for hiding this comment

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

Can this function accept Secret<String> instead? We can call peek() within the function.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

changed

Comment on lines +63 to +64
pub card_exp_month: Secret<String>,
pub card_exp_year: Secret<String>,
Copy link
Member

Choose a reason for hiding this comment

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

You can also use CardExpirationMonth and CardExpirationMonth from the cards crate here, instead of Secret<String>. They also allow formatting the year with 2/4 digits, as required.

@@ -145,6 +146,9 @@ impl
integrity_check: Ok(()),
additional_merchant_data: None,
header_payload,
connector_mandate_request_reference_id: Some(common_utils::generate_id_with_len(
Copy link
Member

Choose a reason for hiding this comment

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

If we generate a random ID / string but don't store it at all in our database, then it would be of no use to us. Can we please avoid such usages?

@@ -329,7 +329,7 @@ impl<F: Send + Clone> GetTracker<F, PaymentData<F>, api::PaymentsRequest> for Pa
api_models::payments::MandateIds {
mandate_id: Some(mandate_obj.mandate_id),
mandate_reference_id: Some(api_models::payments::MandateReferenceId::ConnectorMandateId(
api_models::payments::ConnectorMandateReferenceId {connector_mandate_id:connector_id.connector_mandate_id,payment_method_id:connector_id.payment_method_id, update_history: None, mandate_metadata:connector_id.mandate_metadata, },
api_models::payments::ConnectorMandateReferenceId {connector_mandate_id:connector_id.connector_mandate_id,payment_method_id:connector_id.payment_method_id, update_history: None, mandate_metadata:connector_id.mandate_metadata,connector_mandate_request_reference_id:connector_id.connector_mandate_request_reference_id},
Copy link
Member

Choose a reason for hiding this comment

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

The formatting seems to be messed up here. 🤔

Comment on lines 140 to 165
connector_mandate_request_reference_id: Some(common_utils::generate_id_with_len(
consts::CONNECTOR_MANDATE_REQUEST_REFERENCE_ID_LENGTH.to_owned(),
)),
Copy link
Member

Choose a reason for hiding this comment

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

Same here, generating a random ID and sharing it with the connector, but not storing it on our database is useless. We won't have a track of the value we'd sent to the connector, and we can't take any further actions where we need the value.

We should ideally avoid such usages in the first place.

Comment on lines 357 to 579
connector_mandate_request_reference_id: Some(common_utils::generate_id_with_len(
consts::CONNECTOR_MANDATE_REQUEST_REFERENCE_ID_LENGTH.to_owned(),
)),
Copy link
Member

Choose a reason for hiding this comment

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

Same here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed

Comment on lines 1825 to 1826
serde_json::Value::Null => None, // Handle null case
_ => Some(data.parse_value("AdditionalPaymentData")),
Copy link
Member

Choose a reason for hiding this comment

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

Do we explicitly need to handle the serde_json::Value::Null case? Won't parse_value() handle that: if the JSON value is not the expected type (say a map in this case), then deserialization should fail?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not really , That was redundant

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes deserialization will fail

@awasthi21 awasthi21 force-pushed the 7040-featconnector-paybox-implement-mandate-flow branch from cb851dc to 8d4780a Compare October 28, 2024 06:07
connector_mandate_ids.get_connector_mandate_id()?,
connector_mandate_ids
.get_connector_mandate_id()
.ok_or_else(missing_field_err("mandate_id"))?,
Copy link
Member

Choose a reason for hiding this comment

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

Optional change:

There also seems to be a errors::ConnectorError::MissingConnectorMandateID enum variant, confirm with the connector team if that enum variant would have to be raised instead.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@deepanshu-iiitu Can you verify this?

Comment on lines 1336 to 1338
pub fn update(&mut self, other: Self) {
*self = other;
}
Copy link
Member

Choose a reason for hiding this comment

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

Do we need this method? Is this being used anywhere?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not used anywhere, WIll remove

Comment on lines 584 to 592
.and_then(|v| match serde_json::from_value::<PaymentMethodsData>(v) {
Ok(data) => Some(data),
Err(err) => {
router_env::logger::info!(
"PaymentMethodsData deserialization failed : {err}"
);
None
}
})
Copy link
Member

Choose a reason for hiding this comment

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

Can use parse_value() here instead, you can then log the error and call .ok().

Also, are we okay with proceeding further if we can't deserialize payment method from the locker?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@Aprabhat19 Can you verify if we can pass None to the additional_payment_method if deserialization fails?

.payment_method_data
.clone()
.map(|x| x.into_inner().expose())
.and_then(|v| match serde_json::from_value::<PaymentMethodsData>(v) {
Copy link
Member

Choose a reason for hiding this comment

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

Same here, can use parse_value() here.

}
.and_then(|v| {
v.parse_value("PaymentMethodsData")
.map_err(|err| {
Copy link
Member

Choose a reason for hiding this comment

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

Can use inspect_err also instead of map_err here (and then can skip the error being returned from the closure).

Optional change, by the way.

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 A-mandates Area: Mandate Flows
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants