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(core): confirm flow and authorization api changes for external authentication #4015

Merged
merged 38 commits into from
Mar 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
109e34a
feat(core): separate authentication related schema changes for existi…
hrithikesh026 Feb 29, 2024
f2034c4
Merge branch 'main' into authn-schema-changes-for-other-tables
hrithikesh026 Mar 4, 2024
51edecc
chore: update Cargo.lock
hyperswitch-bot[bot] Mar 4, 2024
3b699a6
fix: authentication_connector_details to business_profile from mercha…
hrithikesh026 Mar 4, 2024
2ea66d8
Merge branch 'main' into authn-schema-changes-for-other-tables
hrithikesh026 Mar 4, 2024
2e61580
chore: address cargo hack failure
hrithikesh026 Mar 4, 2024
95f5c0f
Merge branch 'main' into authn-schema-changes-for-other-tables
hrithikesh026 Mar 5, 2024
5d6c48c
add other prerequisites for 3ds external authentication
sai-harsha-vardhan Mar 5, 2024
fb9eaf1
resolve conflicts
sai-harsha-vardhan Mar 5, 2024
b7e73cf
feat(core): add core functions for external authentication
hrithikesh026 Mar 5, 2024
1d9d51d
resolve conflicts
sai-harsha-vardhan Mar 6, 2024
915946a
Merge branch 'add-other-prerequisites-for-external-authn' into core-m…
sai-harsha-vardhan Mar 6, 2024
3c8713d
Merge branch 'main' into add-other-prerequisites-for-external-authn
sai-harsha-vardhan Mar 6, 2024
da51b0b
Merge branch 'add-other-prerequisites-for-external-authn' into core-m…
sai-harsha-vardhan Mar 6, 2024
4c3ea4c
feat(core): confirm flow and authorization api changes for external a…
hrithikesh026 Mar 7, 2024
977a6e0
address cargo hack failure
hrithikesh026 Mar 8, 2024
3edf689
address comments
hrithikesh026 Mar 8, 2024
3843560
don't allow request_external_three_ds_authentication update in confir…
hrithikesh026 Mar 8, 2024
a221d15
Merge branch 'main' into confirm-and-authorization-api-changes
hrithikesh026 Mar 8, 2024
cafeb70
Merge branch 'main' into confirm-and-authorization-api-changes
hrithikesh026 Mar 10, 2024
56bf618
match unmatched enum
hrithikesh026 Mar 10, 2024
6e5fed9
chore: update Cargo.lock
hyperswitch-bot[bot] Mar 10, 2024
80a3641
no fallback if preauth call fails
hrithikesh026 Mar 11, 2024
6396aff
return next action object if external authentication
hrithikesh026 Mar 11, 2024
b063eb9
restrict PaymentSource enum types for internal use
hrithikesh026 Mar 11, 2024
ccd6bb0
move is_separate_authentication_supported function to connector enum …
hrithikesh026 Mar 11, 2024
31dee6e
address comments
hrithikesh026 Mar 11, 2024
252770a
Merge branch 'main' into confirm-and-authorization-api-changes
hrithikesh026 Mar 11, 2024
04923e9
chore: update Cargo.lock
hyperswitch-bot[bot] Mar 11, 2024
a4a6583
refactor update_trackers in payment confirm
hrithikesh026 Mar 11, 2024
6b64290
fix compilation failure
hrithikesh026 Mar 11, 2024
a7659e0
Merge branch 'main' into confirm-and-authorization-api-changes
hrithikesh026 Mar 11, 2024
a3ca7b1
address comments
hrithikesh026 Mar 11, 2024
69fad2b
docs(openapi): re-generate OpenAPI specification
hyperswitch-bot[bot] Mar 11, 2024
e4046d3
chore: add threedsecureio base url in deployment files
hrithikesh026 Mar 11, 2024
8887a03
use profile_id from business_profile instead of payment_intent
hrithikesh026 Mar 12, 2024
3013d8e
Merge branch 'main' into confirm-and-authorization-api-changes
hrithikesh026 Mar 12, 2024
6486e00
update production url in deployment config files
hrithikesh026 Mar 12, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion config/deployments/production.toml
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ worldline.base_url = "https://eu.sandbox.api-ingenico.com/"
worldpay.base_url = "https://try.access.worldpay.com/"
zen.base_url = "https://api.zen.com/"
zen.secondary_base_url = "https://secure.zen.com/"
threedsecureio.base_url = "https://service.sandbox.3dsecure.io"
threedsecureio.base_url = "https://service.3dsecure.io"

[delayed_session_response]
connectors_with_delayed_session_response = "trustpay,payme"
Expand Down
121 changes: 121 additions & 0 deletions crates/api_models/src/enums.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,127 @@ impl Connector {
pub fn requires_defend_dispute(&self) -> bool {
matches!(self, Self::Checkout)
}
pub fn is_separate_authentication_supported(&self) -> bool {
#[cfg(feature = "dummy_connector")]
match self {
Self::DummyConnector1
| Self::DummyConnector2
| Self::DummyConnector3
| Self::DummyConnector4
| Self::DummyConnector5
| Self::DummyConnector6
| Self::DummyConnector7 => false,
Self::Aci
| Self::Adyen
| Self::Airwallex
| Self::Authorizedotnet
| Self::Bambora
| Self::Bankofamerica
| Self::Bitpay
| Self::Bluesnap
| Self::Boku
| Self::Braintree
| Self::Cashtocode
| Self::Coinbase
| Self::Cryptopay
| Self::Dlocal
| Self::Fiserv
| Self::Forte
| Self::Globalpay
| Self::Globepay
| Self::Gocardless
| Self::Helcim
| Self::Iatapay
| Self::Klarna
| Self::Mollie
| Self::Multisafepay
| Self::Nexinets
| Self::Nmi
| Self::Nuvei
| Self::Opennode
| Self::Payme
| Self::Paypal
| Self::Payu
| Self::Placetopay
| Self::Powertranz
| Self::Prophetpay
| Self::Rapyd
| Self::Shift4
| Self::Square
| Self::Stax
| Self::Trustpay
| Self::Tsys
| Self::Volt
| Self::Wise
| Self::Worldline
| Self::Worldpay
| Self::Zen
| Self::Signifyd
| Self::Plaid
| Self::Riskified
| Self::Threedsecureio
| Self::Cybersource
| Self::Noon
| Self::Stripe => false,
Self::Checkout => true,
}
#[cfg(not(feature = "dummy_connector"))]
match self {
Self::Aci
| Self::Adyen
| Self::Airwallex
| Self::Authorizedotnet
| Self::Bambora
| Self::Bankofamerica
| Self::Bitpay
| Self::Bluesnap
| Self::Boku
| Self::Braintree
| Self::Cashtocode
| Self::Coinbase
| Self::Cryptopay
| Self::Dlocal
| Self::Fiserv
| Self::Forte
| Self::Globalpay
| Self::Globepay
| Self::Gocardless
| Self::Helcim
| Self::Iatapay
| Self::Klarna
| Self::Mollie
| Self::Multisafepay
| Self::Nexinets
| Self::Nmi
| Self::Nuvei
| Self::Opennode
| Self::Payme
| Self::Paypal
| Self::Payu
| Self::Placetopay
| Self::Powertranz
| Self::Prophetpay
| Self::Rapyd
| Self::Shift4
| Self::Square
| Self::Stax
| Self::Trustpay
| Self::Tsys
| Self::Volt
| Self::Wise
| Self::Worldline
| Self::Worldpay
| Self::Zen
| Self::Signifyd
| Self::Plaid
| Self::Riskified
| Self::Threedsecureio
| Self::Cybersource
| Self::Noon
| Self::Stripe => false,
Self::Checkout => true,
}
}
}

#[derive(
Expand Down
2 changes: 1 addition & 1 deletion crates/api_models/src/payments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2708,7 +2708,7 @@ pub struct PaymentsResponse {
pub external_authentication_details: Option<ExternalAuthenticationDetailsResponse>,

/// Flag indicating if external 3ds authentication is made or not
pub request_external_3ds_authentication: Option<bool>,
pub external_3ds_authentication_attempted: Option<bool>,

/// Date Time expiry of the payment
#[schema(example = "2022-09-10T10:11:12Z")]
Expand Down
9 changes: 9 additions & 0 deletions crates/common_enums/src/enums.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2133,6 +2133,15 @@ pub enum PaymentSource {
ExternalAuthenticator,
}

impl PaymentSource {
pub fn is_for_internal_use_only(&self) -> bool {
match self {
Self::Dashboard | Self::Sdk | Self::MerchantServer | Self::Postman => false,
Self::Webhook | Self::ExternalAuthenticator => true,
}
}
}

#[derive(
Clone,
Copy,
Expand Down
1 change: 1 addition & 0 deletions crates/router/src/core/authentication.rs
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ pub async fn perform_pre_authentication<F: Clone + Send>(
if authentication_data
.as_ref()
.is_some_and(|authentication_data| authentication_data.is_separate_authn_required())
|| authentication.authentication_status.is_failed()
{
*should_continue_confirm_transaction = false;
}
Expand Down
30 changes: 29 additions & 1 deletion crates/router/src/core/authentication/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use crate::{
routes::AppState,
services::{self, execute_connector_processing_step},
types::{
api,
api::{self, ConnectorCallType},
authentication::{AuthNFlowType, AuthenticationResponseData},
storage,
transformers::ForeignFrom,
Expand All @@ -22,6 +22,34 @@ use crate::{
utils::OptionExt,
};

pub fn get_connector_name_if_separate_authn_supported(
connector_call_type: &ConnectorCallType,
) -> Option<String> {
match connector_call_type {
ConnectorCallType::PreDetermined(connector_data) => {
if connector_data
.connector_name
.is_separate_authentication_supported()
{
Some(connector_data.connector_name.to_string())
} else {
None
}
}
ConnectorCallType::Retryable(connectors) => connectors.first().and_then(|connector_data| {
if connector_data
.connector_name
.is_separate_authentication_supported()
{
Some(connector_data.connector_name.to_string())
} else {
None
}
}),
ConnectorCallType::SessionMultiple(_) => None,
}
}

pub async fn update_trackers<F: Clone, Req>(
state: &AppState,
router_data: RouterData<F, Req, AuthenticationResponseData>,
Expand Down
118 changes: 99 additions & 19 deletions crates/router/src/core/payments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ where

let should_add_task_to_process_tracker = should_add_task_to_process_tracker(&payment_data);

payment_data = tokenize_in_router_when_confirm_false(
payment_data = tokenize_in_router_when_confirm_false_or_external_authentication(
state,
&operation,
&mut payment_data,
Expand Down Expand Up @@ -217,6 +217,18 @@ where
should_continue_capture,
);

operation
.to_domain()?
.call_external_three_ds_authentication_if_eligible(
state,
&mut payment_data,
&mut should_continue_transaction,
&connector_details,
&business_profile,
&key_store,
)
.await?;

if should_continue_transaction {
#[cfg(feature = "frm")]
match (
Expand Down Expand Up @@ -1015,6 +1027,70 @@ impl<Ctx: PaymentMethodRetrieve> PaymentRedirectFlow<Ctx> for PaymentRedirectSyn
}
}

#[derive(Clone, Debug)]
pub struct PaymentAuthenticateCompleteAuthorize;

#[async_trait::async_trait]
impl<Ctx: PaymentMethodRetrieve> PaymentRedirectFlow<Ctx> for PaymentAuthenticateCompleteAuthorize {
async fn call_payment_flow(
&self,
state: &AppState,
merchant_account: domain::MerchantAccount,
merchant_key_store: domain::MerchantKeyStore,
req: PaymentsRedirectResponseData,
connector_action: CallConnectorAction,
) -> RouterResponse<api::PaymentsResponse> {
let payment_confirm_req = api::PaymentsRequest {
payment_id: Some(req.resource_id.clone()),
merchant_id: req.merchant_id.clone(),
feature_metadata: Some(api_models::payments::FeatureMetadata {
redirect_response: Some(api_models::payments::RedirectResponse {
param: req.param.map(Secret::new),
json_payload: Some(req.json_payload.unwrap_or(serde_json::json!({})).into()),
}),
}),
..Default::default()
};
Box::pin(payments_core::<
api::Authorize,
api::PaymentsResponse,
_,
_,
_,
Ctx,
>(
state.clone(),
merchant_account,
merchant_key_store,
PaymentConfirm,
payment_confirm_req,
services::api::AuthFlow::Merchant,
connector_action,
None,
HeaderPayload::with_source(enums::PaymentSource::ExternalAuthenticator),
))
.await
}
fn generate_response(
&self,
payments_response: api_models::payments::PaymentsResponse,
business_profile: diesel_models::business_profile::BusinessProfile,
payment_id: String,
connector: String,
) -> RouterResult<api::RedirectionResponse> {
helpers::get_handle_response_url(
payment_id,
&business_profile,
payments_response,
connector,
)
}

fn get_payment_action(&self) -> services::PaymentAction {
services::PaymentAction::CompleteAuthorize
Copy link
Member

Choose a reason for hiding this comment

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

we can maybe add a separate variant for this use case?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This will fixed in the upcoming refactor PR.

}
}

#[allow(clippy::too_many_arguments)]
#[instrument(skip_all)]
pub async fn call_connector_service<F, RouterDReq, ApiRequest, Ctx>(
Expand Down Expand Up @@ -1989,7 +2065,7 @@ where
Ok(payment_data_and_tokenization_action)
}

pub async fn tokenize_in_router_when_confirm_false<F, Req, Ctx>(
pub async fn tokenize_in_router_when_confirm_false_or_external_authentication<F, Req, Ctx>(
state: &AppState,
operation: &BoxedOperation<'_, F, Req, Ctx>,
payment_data: &mut PaymentData<F>,
Expand All @@ -2002,23 +2078,27 @@ where
Ctx: PaymentMethodRetrieve,
{
// On confirm is false and only router related
let payment_data = if !is_operation_confirm(operation) {
let (_operation, payment_method_data, pm_id) = operation
.to_domain()?
.make_pm_data(
state,
payment_data,
validate_result.storage_scheme,
merchant_key_store,
customer,
)
.await?;
payment_data.payment_method_data = payment_method_data;
payment_data.payment_attempt.payment_method_id = pm_id;
payment_data
} else {
payment_data
};
let is_external_authentication_requested = payment_data
.payment_intent
.request_external_three_ds_authentication;
let payment_data =
if !is_operation_confirm(operation) || is_external_authentication_requested == Some(true) {
let (_operation, payment_method_data, pm_id) = operation
.to_domain()?
.make_pm_data(
state,
payment_data,
validate_result.storage_scheme,
merchant_key_store,
customer,
)
.await?;
payment_data.payment_method_data = payment_method_data;
payment_data.payment_attempt.payment_method_id = pm_id;
payment_data
} else {
payment_data
};
Ok(payment_data.to_owned())
}

Expand Down
Loading
Loading