Skip to content

Commit

Permalink
feat(router): add start_redirection api for three_ds flow in v2 (#6470
Browse files Browse the repository at this point in the history
)

Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
  • Loading branch information
1 parent 0389ae7 commit 6f24bb4
Show file tree
Hide file tree
Showing 19 changed files with 316 additions and 28 deletions.
8 changes: 8 additions & 0 deletions api-reference-v2/openapi_spec.json
Original file line number Diff line number Diff line change
Expand Up @@ -13383,6 +13383,14 @@
"payment_method_subtype": {
"$ref": "#/components/schemas/PaymentMethodType"
},
"next_action": {
"allOf": [
{
"$ref": "#/components/schemas/NextActionData"
}
],
"nullable": true
},
"connector_transaction_id": {
"type": "string",
"description": "A unique identifier for a payment provided by the connector",
Expand Down
13 changes: 11 additions & 2 deletions crates/api_models/src/events/payment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ use common_utils::events::{ApiEventMetric, ApiEventsType};

#[cfg(feature = "v2")]
use super::{
PaymentsConfirmIntentResponse, PaymentsCreateIntentRequest, PaymentsGetIntentRequest,
PaymentsIntentResponse,
PaymentStartRedirectionRequest, PaymentsConfirmIntentResponse, PaymentsCreateIntentRequest,
PaymentsGetIntentRequest, PaymentsIntentResponse,
};
#[cfg(all(
any(feature = "v2", feature = "v1"),
Expand Down Expand Up @@ -365,3 +365,12 @@ impl ApiEventMetric for PaymentsSessionResponse {
})
}
}

#[cfg(feature = "v2")]
impl ApiEventMetric for PaymentStartRedirectionRequest {
fn get_api_event_type(&self) -> Option<ApiEventsType> {
Some(ApiEventsType::Payment {
payment_id: self.id.clone(),
})
}
}
26 changes: 26 additions & 0 deletions crates/api_models/src/payments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3876,9 +3876,16 @@ pub enum NextActionType {
#[serde(tag = "type", rename_all = "snake_case")]
pub enum NextActionData {
/// Contains the url for redirection flow
#[cfg(feature = "v1")]
RedirectToUrl {
redirect_to_url: String,
},
/// Contains the url for redirection flow
#[cfg(feature = "v2")]
RedirectToUrl {
#[schema(value_type = String)]
redirect_to_url: Url,
},
/// Informs the next steps for bank transfer and also contains the charges details (ex: amount received, amount charged etc)
DisplayBankTransferInformation {
bank_transfer_steps_and_charges_details: BankTransferNextStepsData,
Expand Down Expand Up @@ -4538,6 +4545,9 @@ pub struct PaymentsConfirmIntentResponse {
#[schema(value_type = PaymentMethodType, example = "apple_pay")]
pub payment_method_subtype: api_enums::PaymentMethodType,

/// Additional information required for redirection
pub next_action: Option<NextActionData>,

/// A unique identifier for a payment provided by the connector
#[schema(value_type = Option<String>, example = "993672945374576J")]
pub connector_transaction_id: Option<String>,
Expand All @@ -4558,6 +4568,22 @@ pub struct PaymentsConfirmIntentResponse {
pub error: Option<ErrorDetails>,
}

#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
#[cfg(feature = "v2")]
pub struct PaymentStartRedirectionRequest {
/// Global Payment ID
pub id: id_type::GlobalPaymentId,
}

#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
#[cfg(feature = "v2")]
pub struct PaymentStartRedirectionParams {
/// The identifier for the Merchant Account.
pub publishable_key: String,
/// The identifier for business profile
pub profile_id: id_type::ProfileId,
}

/// Fee information to be charged on the payment being collected
#[derive(Setter, Clone, Default, Debug, PartialEq, serde::Serialize, ToSchema)]
pub struct PaymentChargeResponse {
Expand Down
2 changes: 1 addition & 1 deletion crates/diesel_models/src/payment_attempt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -770,7 +770,7 @@ pub struct PaymentAttemptUpdateInternal {
pub updated_by: String,
pub merchant_connector_id: Option<id_type::MerchantConnectorAccountId>,
pub connector: Option<String>,
// authentication_data: Option<serde_json::Value>,
pub authentication_data: Option<pii::SecretSerdeValue>,
// encoded_data: Option<String>,
pub unified_code: Option<Option<String>>,
pub unified_message: Option<Option<String>>,
Expand Down
19 changes: 13 additions & 6 deletions crates/diesel_models/src/payment_intent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ pub struct PaymentIntent {
pub last_synced: Option<PrimitiveDateTime>,
pub setup_future_usage: Option<storage_enums::FutureUsage>,
pub client_secret: common_utils::types::ClientSecret,
pub active_attempt_id: Option<String>,
pub active_attempt_id: Option<common_utils::id_type::GlobalAttemptId>,
#[diesel(deserialize_as = super::OptionalDieselArray<masking::Secret<OrderDetailsWithAmount>>)]
pub order_details: Option<Vec<masking::Secret<OrderDetailsWithAmount>>>,
pub allowed_payment_method_types: Option<pii::SecretSerdeValue>,
Expand Down Expand Up @@ -250,7 +250,7 @@ pub struct PaymentIntentNew {
pub last_synced: Option<PrimitiveDateTime>,
pub setup_future_usage: Option<storage_enums::FutureUsage>,
pub client_secret: common_utils::types::ClientSecret,
pub active_attempt_id: Option<String>,
pub active_attempt_id: Option<common_utils::id_type::GlobalAttemptId>,
#[diesel(deserialize_as = super::OptionalDieselArray<masking::Secret<OrderDetailsWithAmount>>)]
pub order_details: Option<Vec<masking::Secret<OrderDetailsWithAmount>>>,
pub allowed_payment_method_types: Option<pii::SecretSerdeValue>,
Expand Down Expand Up @@ -359,6 +359,7 @@ pub enum PaymentIntentUpdate {
ConfirmIntent {
status: storage_enums::IntentStatus,
updated_by: String,
active_attempt_id: common_utils::id_type::GlobalAttemptId,
},
/// Update the payment intent details on payment intent confirmation, after calling the connector
ConfirmIntentPostUpdate {
Expand Down Expand Up @@ -520,7 +521,7 @@ pub struct PaymentIntentUpdateInternal {
// pub setup_future_usage: Option<storage_enums::FutureUsage>,
// pub metadata: Option<pii::SecretSerdeValue>,
pub modified_at: PrimitiveDateTime,
// pub active_attempt_id: Option<String>,
pub active_attempt_id: Option<common_utils::id_type::GlobalAttemptId>,
// pub description: Option<String>,
// pub statement_descriptor: Option<String>,
// #[diesel(deserialize_as = super::OptionalDieselArray<pii::SecretSerdeValue>)]
Expand Down Expand Up @@ -594,7 +595,7 @@ impl PaymentIntentUpdate {
// setup_future_usage,
// metadata,
modified_at: _,
// active_attempt_id,
active_attempt_id,
// description,
// statement_descriptor,
// order_details,
Expand All @@ -620,7 +621,7 @@ impl PaymentIntentUpdate {
// setup_future_usage: setup_future_usage.or(source.setup_future_usage),
// metadata: metadata.or(source.metadata),
modified_at: common_utils::date_time::now(),
// active_attempt_id: active_attempt_id.unwrap_or(source.active_attempt_id),
active_attempt_id: active_attempt_id.or(source.active_attempt_id),
// description: description.or(source.description),
// statement_descriptor: statement_descriptor.or(source.statement_descriptor),
// order_details: order_details.or(source.order_details),
Expand Down Expand Up @@ -735,15 +736,21 @@ impl PaymentIntentUpdate {
impl From<PaymentIntentUpdate> for PaymentIntentUpdateInternal {
fn from(payment_intent_update: PaymentIntentUpdate) -> Self {
match payment_intent_update {
PaymentIntentUpdate::ConfirmIntent { status, updated_by } => Self {
PaymentIntentUpdate::ConfirmIntent {
status,
updated_by,
active_attempt_id,
} => Self {
status: Some(status),
modified_at: common_utils::date_time::now(),
updated_by,
active_attempt_id: Some(active_attempt_id),
},
PaymentIntentUpdate::ConfirmIntentPostUpdate { status, updated_by } => Self {
status: Some(status),
modified_at: common_utils::date_time::now(),
updated_by,
active_attempt_id: None,
},
}
}
Expand Down
24 changes: 21 additions & 3 deletions crates/hyperswitch_domain_models/src/payments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,24 @@ impl PaymentIntent {
pub fn get_id(&self) -> &id_type::GlobalPaymentId {
&self.id
}

#[cfg(feature = "v2")]
pub fn create_start_redirection_url(
&self,
base_url: &str,
publishable_key: String,
) -> CustomResult<url::Url, errors::api_error_response::ApiErrorResponse> {
let start_redirection_url = &format!(
"{}/v2/payments/{}/start_redirection?publishable_key={}&profile_id={}",
base_url,
self.get_id().get_string_repr(),
publishable_key,
self.profile_id.get_string_repr()
);
url::Url::parse(start_redirection_url)
.change_context(errors::api_error_response::ApiErrorResponse::InternalServerError)
.attach_printable("Error creating start redirection url")
}
}

#[cfg(feature = "v2")]
Expand Down Expand Up @@ -272,8 +290,8 @@ pub struct PaymentIntent {
pub setup_future_usage: storage_enums::FutureUsage,
/// The client secret that is generated for the payment. This is used to authenticate the payment from client facing apis.
pub client_secret: common_utils::types::ClientSecret,
/// The active attempt for the payment intent. This is the payment attempt that is currently active for the payment intent.
pub active_attempt: Option<RemoteStorageObject<PaymentAttempt>>,
/// The active attempt id for the payment intent. This is the payment attempt that is currently active for the payment intent.
pub active_attempt_id: Option<id_type::GlobalAttemptId>,
/// The order details for the payment.
pub order_details: Option<Vec<Secret<OrderDetailsWithAmount>>>,
/// This is the list of payment method types that are allowed for the payment intent.
Expand Down Expand Up @@ -421,7 +439,7 @@ impl PaymentIntent {
last_synced: None,
setup_future_usage: request.setup_future_usage.unwrap_or_default(),
client_secret,
active_attempt: None,
active_attempt_id: None,
order_details,
allowed_payment_method_types,
connector_metadata,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1296,6 +1296,7 @@ pub enum PaymentAttemptUpdate {
status: storage_enums::AttemptStatus,
connector_payment_id: Option<String>,
updated_by: String,
authentication_data: Option<pii::SecretSerdeValue>,
},
/// Update the payment attempt on confirming the intent, after calling the connector on error response
ConfirmIntentError {
Expand Down Expand Up @@ -1923,6 +1924,7 @@ impl From<PaymentAttemptUpdate> for diesel_models::PaymentAttemptUpdateInternal
unified_message: None,
connector_payment_id: None,
connector: Some(connector),
authentication_data: None,
},
PaymentAttemptUpdate::ConfirmIntentError {
status,
Expand All @@ -1941,11 +1943,13 @@ impl From<PaymentAttemptUpdate> for diesel_models::PaymentAttemptUpdateInternal
unified_message: None,
connector_payment_id: None,
connector: None,
authentication_data: None,
},
PaymentAttemptUpdate::ConfirmIntentResponse {
status,
connector_payment_id,
updated_by,
authentication_data,
} => Self {
status: Some(status),
error_message: None,
Expand All @@ -1959,6 +1963,7 @@ impl From<PaymentAttemptUpdate> for diesel_models::PaymentAttemptUpdateInternal
unified_message: None,
connector_payment_id,
connector: None,
authentication_data,
},
}
}
Expand Down
30 changes: 20 additions & 10 deletions crates/hyperswitch_domain_models/src/payments/payment_intent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,7 @@ pub enum PaymentIntentUpdate {
ConfirmIntent {
status: storage_enums::IntentStatus,
updated_by: String,
active_attempt_id: id_type::GlobalAttemptId,
},
ConfirmIntentPostUpdate {
status: storage_enums::IntentStatus,
Expand Down Expand Up @@ -363,9 +364,14 @@ pub struct PaymentIntentUpdateInternal {
impl From<PaymentIntentUpdate> for PaymentIntentUpdateInternal {
fn from(payment_intent_update: PaymentIntentUpdate) -> Self {
match payment_intent_update {
PaymentIntentUpdate::ConfirmIntent { status, updated_by } => Self {
PaymentIntentUpdate::ConfirmIntent {
status,
updated_by,
active_attempt_id,
} => Self {
status: Some(status),
updated_by,
active_attempt_id: Some(active_attempt_id),
..Default::default()
},
PaymentIntentUpdate::ConfirmIntentPostUpdate { status, updated_by } => Self {
Expand Down Expand Up @@ -582,9 +588,15 @@ use diesel_models::{
impl From<PaymentIntentUpdate> for DieselPaymentIntentUpdate {
fn from(value: PaymentIntentUpdate) -> Self {
match value {
PaymentIntentUpdate::ConfirmIntent { status, updated_by } => {
Self::ConfirmIntent { status, updated_by }
}
PaymentIntentUpdate::ConfirmIntent {
status,
updated_by,
active_attempt_id,
} => Self::ConfirmIntent {
status,
updated_by,
active_attempt_id,
},
PaymentIntentUpdate::ConfirmIntentPostUpdate { status, updated_by } => {
Self::ConfirmIntentPostUpdate { status, updated_by }
}
Expand Down Expand Up @@ -1134,7 +1146,7 @@ impl behaviour::Conversion for PaymentIntent {
last_synced,
setup_future_usage,
client_secret,
active_attempt,
active_attempt_id,
order_details,
allowed_payment_method_types,
connector_metadata,
Expand Down Expand Up @@ -1182,7 +1194,7 @@ impl behaviour::Conversion for PaymentIntent {
last_synced,
setup_future_usage: Some(setup_future_usage),
client_secret,
active_attempt_id: active_attempt.map(|attempt| attempt.get_id()),
active_attempt_id,
order_details: order_details.map(|order_details| {
order_details
.into_iter()
Expand Down Expand Up @@ -1319,9 +1331,7 @@ impl behaviour::Conversion for PaymentIntent {
last_synced: storage_model.last_synced,
setup_future_usage: storage_model.setup_future_usage.unwrap_or_default(),
client_secret: storage_model.client_secret,
active_attempt: storage_model
.active_attempt_id
.map(RemoteStorageObject::ForeignID),
active_attempt_id: storage_model.active_attempt_id,
order_details: storage_model.order_details.map(|order_details| {
order_details
.into_iter()
Expand Down Expand Up @@ -1395,7 +1405,7 @@ impl behaviour::Conversion for PaymentIntent {
last_synced: self.last_synced,
setup_future_usage: Some(self.setup_future_usage),
client_secret: self.client_secret,
active_attempt_id: self.active_attempt.map(|attempt| attempt.get_id()),
active_attempt_id: self.active_attempt_id,
order_details: self.order_details,
allowed_payment_method_types: self
.allowed_payment_method_types
Expand Down
Loading

0 comments on commit 6f24bb4

Please sign in to comment.