Skip to content

Commit

Permalink
feat(router): add click_to_pay block in payments sessions response …
Browse files Browse the repository at this point in the history
…if enabled (#6829)

Co-authored-by: Sahkal Poddar <[email protected]>
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
  • Loading branch information
3 people authored Dec 13, 2024
1 parent 5a85213 commit 5aa8ea0
Show file tree
Hide file tree
Showing 8 changed files with 261 additions and 0 deletions.
72 changes: 72 additions & 0 deletions api-reference-v2/openapi_spec.json
Original file line number Diff line number Diff line change
Expand Up @@ -6230,6 +6230,57 @@
}
}
},
"ClickToPaySessionResponse": {
"type": "object",
"required": [
"dpaId",
"dpaName",
"locale",
"cardBrands",
"acquirerBin",
"acquirerMerchantId",
"merchantCategoryCode",
"merchantCountryCode",
"transactionAmount",
"transactionCurrencyCode"
],
"properties": {
"dpaId": {
"type": "string"
},
"dpaName": {
"type": "string"
},
"locale": {
"type": "string"
},
"cardBrands": {
"type": "array",
"items": {
"type": "string"
}
},
"acquirerBin": {
"type": "string"
},
"acquirerMerchantId": {
"type": "string"
},
"merchantCategoryCode": {
"type": "string"
},
"merchantCountryCode": {
"type": "string"
},
"transactionAmount": {
"type": "string",
"example": "38.02"
},
"transactionCurrencyCode": {
"$ref": "#/components/schemas/Currency"
}
}
},
"Comparison": {
"type": "object",
"description": "Represents a single comparison condition.",
Expand Down Expand Up @@ -20026,6 +20077,27 @@
}
]
},
{
"allOf": [
{
"$ref": "#/components/schemas/ClickToPaySessionResponse"
},
{
"type": "object",
"required": [
"wallet_name"
],
"properties": {
"wallet_name": {
"type": "string",
"enum": [
"click_to_pay"
]
}
}
}
]
},
{
"type": "object",
"required": [
Expand Down
72 changes: 72 additions & 0 deletions api-reference/openapi_spec.json
Original file line number Diff line number Diff line change
Expand Up @@ -8674,6 +8674,57 @@
}
}
},
"ClickToPaySessionResponse": {
"type": "object",
"required": [
"dpaId",
"dpaName",
"locale",
"cardBrands",
"acquirerBin",
"acquirerMerchantId",
"merchantCategoryCode",
"merchantCountryCode",
"transactionAmount",
"transactionCurrencyCode"
],
"properties": {
"dpaId": {
"type": "string"
},
"dpaName": {
"type": "string"
},
"locale": {
"type": "string"
},
"cardBrands": {
"type": "array",
"items": {
"type": "string"
}
},
"acquirerBin": {
"type": "string"
},
"acquirerMerchantId": {
"type": "string"
},
"merchantCategoryCode": {
"type": "string"
},
"merchantCountryCode": {
"type": "string"
},
"transactionAmount": {
"type": "string",
"example": "38.02"
},
"transactionCurrencyCode": {
"$ref": "#/components/schemas/Currency"
}
}
},
"Comparison": {
"type": "object",
"description": "Represents a single comparison condition.",
Expand Down Expand Up @@ -23944,6 +23995,27 @@
}
]
},
{
"allOf": [
{
"$ref": "#/components/schemas/ClickToPaySessionResponse"
},
{
"type": "object",
"required": [
"wallet_name"
],
"properties": {
"wallet_name": {
"type": "string",
"enum": [
"click_to_pay"
]
}
}
}
]
},
{
"type": "object",
"required": [
Expand Down
19 changes: 19 additions & 0 deletions crates/api_models/src/payments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5943,6 +5943,8 @@ pub enum SessionToken {
OpenBanking(OpenBankingSessionToken),
/// The session response structure for Paze
Paze(Box<PazeSessionTokenResponse>),
/// The sessions response structure for ClickToPay
ClickToPay(Box<ClickToPaySessionResponse>),
/// Whenever there is no session token response or an error in session response
NoSessionTokenReceived,
}
Expand Down Expand Up @@ -6981,6 +6983,23 @@ pub struct ExtendedCardInfoResponse {
pub payload: String,
}

#[derive(Debug, Clone, Eq, PartialEq, serde::Serialize, ToSchema)]
#[serde(rename_all = "camelCase")]
pub struct ClickToPaySessionResponse {
pub dpa_id: String,
pub dpa_name: String,
pub locale: String,
pub card_brands: Vec<String>,
pub acquirer_bin: String,
pub acquirer_merchant_id: String,
pub merchant_category_code: String,
pub merchant_country_code: String,
#[schema(value_type = String, example = "38.02")]
pub transaction_amount: StringMajorUnit,
#[schema(value_type = Currency)]
pub transaction_currency_code: common_enums::Currency,
}

#[cfg(feature = "v1")]
#[cfg(test)]
mod payments_request_api_contract {
Expand Down
13 changes: 13 additions & 0 deletions crates/hyperswitch_domain_models/src/payments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -520,6 +520,19 @@ pub struct HeaderPayload {
pub x_redirect_uri: Option<String>,
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ClickToPayMetaData {
pub dpa_id: String,
pub dpa_name: String,
pub locale: String,
pub card_brands: Vec<String>,
pub acquirer_bin: String,
pub acquirer_merchant_id: String,
pub merchant_category_code: String,
pub merchant_country_code: String,
}

// TODO: uncomment fields as necessary
#[cfg(feature = "v2")]
#[derive(Default, Debug, Clone)]
Expand Down
1 change: 1 addition & 0 deletions crates/openapi/src/openapi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,7 @@ Never share your secret api keys. Keep them guarded and secure.
api_models::payments::ApplePayShippingContactFields,
api_models::payments::ApplePayAddressParameters,
api_models::payments::AmountInfo,
api_models::payments::ClickToPaySessionResponse,
api_models::enums::ProductType,
api_models::payments::GooglePayWalletData,
api_models::payments::PayPalWalletData,
Expand Down
1 change: 1 addition & 0 deletions crates/openapi/src/openapi_v2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,7 @@ Never share your secret api keys. Keep them guarded and secure.
api_models::payments::DeviceChannel,
api_models::payments::ThreeDsCompletionIndicator,
api_models::payments::MifinityData,
api_models::payments::ClickToPaySessionResponse,
api_models::enums::TransactionStatus,
api_models::payments::PaymentCreatePaymentLinkConfig,
api_models::payments::ThreeDsData,
Expand Down
3 changes: 3 additions & 0 deletions crates/router/src/consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -207,3 +207,6 @@ pub const VAULT_GET_FINGERPRINT_FLOW_TYPE: &str = "get_fingerprint_vault";

/// Max volume split for Dynamic routing
pub const DYNAMIC_ROUTING_MAX_VOLUME: u8 = 100;

/// Click To Pay
pub const CLICK_TO_PAY: &str = "CLICK_TO_PAY";
80 changes: 80 additions & 0 deletions crates/router/src/core/payments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3356,6 +3356,21 @@ where
}
}

// If click_to_pay is enabled and authentication_product_ids is configured in profile, we need to attach click_to_pay block in the session response for invoking click_to_pay SDK
if business_profile.is_click_to_pay_enabled {
if let Some(value) = business_profile.authentication_product_ids.clone() {
let session_token = get_session_token_for_click_to_pay(
state,
merchant_account.get_id(),
key_store,
value,
payment_data.get_payment_intent(),
)
.await?;
payment_data.push_sessions_token(session_token);
}
}

let call_connectors_end_time = Instant::now();
let call_connectors_duration =
call_connectors_end_time.saturating_duration_since(call_connectors_start_time);
Expand All @@ -3364,6 +3379,71 @@ where
Ok(payment_data)
}

#[cfg(feature = "v1")]
pub async fn get_session_token_for_click_to_pay(
state: &SessionState,
merchant_id: &id_type::MerchantId,
key_store: &domain::MerchantKeyStore,
authentication_product_ids: serde_json::Value,
payment_intent: &hyperswitch_domain_models::payments::PaymentIntent,
) -> RouterResult<api_models::payments::SessionToken> {
use common_utils::{id_type::MerchantConnectorAccountId, types::AmountConvertor};
use hyperswitch_domain_models::payments::ClickToPayMetaData;

use crate::consts::CLICK_TO_PAY;

let mca_ids: HashMap<String, MerchantConnectorAccountId> = authentication_product_ids
.parse_value("HashMap<String, MerchantConnectorAccountId>")
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Error while parsing authentication product ids")?;
let click_to_pay_mca_id = mca_ids
.get(CLICK_TO_PAY)
.ok_or(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Error while getting click_to_pay mca_id from business profile")?;
let key_manager_state = &(state).into();
let merchant_connector_account = state
.store
.find_by_merchant_connector_account_merchant_id_merchant_connector_id(
key_manager_state,
merchant_id,
click_to_pay_mca_id,
key_store,
)
.await
.to_not_found_response(errors::ApiErrorResponse::MerchantConnectorAccountNotFound {
id: click_to_pay_mca_id.get_string_repr().to_string(),
})?;
let click_to_pay_metadata: ClickToPayMetaData = merchant_connector_account
.metadata
.parse_value("ClickToPayMetaData")
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Error while parsing ClickToPayMetaData")?;
let transaction_currency = payment_intent
.currency
.ok_or(errors::ApiErrorResponse::InternalServerError)
.attach_printable("currency is not present in payment_data.payment_intent")?;
let required_amount_type = common_utils::types::StringMajorUnitForConnector;
let transaction_amount = required_amount_type
.convert(payment_intent.amount, transaction_currency)
.change_context(errors::ApiErrorResponse::PreconditionFailed {
message: "Failed to convert amount to string major unit for clickToPay".to_string(),
})?;
Ok(api_models::payments::SessionToken::ClickToPay(Box::new(
api_models::payments::ClickToPaySessionResponse {
dpa_id: click_to_pay_metadata.dpa_id,
dpa_name: click_to_pay_metadata.dpa_name,
locale: click_to_pay_metadata.locale,
card_brands: click_to_pay_metadata.card_brands,
acquirer_bin: click_to_pay_metadata.acquirer_bin,
acquirer_merchant_id: click_to_pay_metadata.acquirer_merchant_id,
merchant_category_code: click_to_pay_metadata.merchant_category_code,
merchant_country_code: click_to_pay_metadata.merchant_country_code,
transaction_amount,
transaction_currency_code: transaction_currency,
},
)))
}

#[cfg(feature = "v1")]
pub async fn call_create_connector_customer_if_required<F, Req, D>(
state: &SessionState,
Expand Down

0 comments on commit 5aa8ea0

Please sign in to comment.