diff --git a/config/Development.toml b/config/Development.toml index 03577ac557..6a518d7757 100644 --- a/config/Development.toml +++ b/config/Development.toml @@ -61,6 +61,8 @@ base_url = "https://api.stripe.com/" [connectors.braintree] base_url = "https://api.sandbox.braintreegateway.com/" +[connectors.klarna] +base_url = "https://api-na.playground.klarna.com/" [scheduler] stream = "SCHEDULER_STREAM" diff --git a/config/config.example.toml b/config/config.example.toml index 5cccca6fb9..0655d33a8c 100644 --- a/config/config.example.toml +++ b/config/config.example.toml @@ -113,6 +113,15 @@ base_url = "https://api.stripe.com/" [connectors.braintree] base_url = "https://api.sandbox.braintreegateway.com/" +[connectors.klarna] +base_url = "https://api-na.playground.klarna.com/" + +# This data is used to call respective connectors for wallets and cards +[connectors.supported] +wallets = ["klarna","braintree"] +cards = ["stripe","adyen","authorizedotnet","checkout","braintree"] + + # Scheduler settings provides a point to modify the behaviour of scheduler flow. # It defines the the streams/queues name and configuration as well as event selection variables [scheduler] diff --git a/config/docker_compose.toml b/config/docker_compose.toml index 310e166032..0b1c473480 100644 --- a/config/docker_compose.toml +++ b/config/docker_compose.toml @@ -66,4 +66,12 @@ base_url = "https://api.sandbox.checkout.com/" base_url = "https://api.stripe.com/" [connectors.braintree] -base_url = "https://api.sandbox.braintreegateway.com/" \ No newline at end of file +base_url = "https://api.sandbox.braintreegateway.com/" + +[connectors.klarna] +base_url = "https://api-na.playground.klarna.com/" + +[connectors.supported] +wallets = ["klarna","braintree"] +cards = ["stripe","adyen","authorizedotnet","checkout","braintree"] + diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index 2e6991baea..55bb114156 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -532,13 +532,8 @@ impl From for PaymentsResponse { impl From for PaymentsResponse { fn from(item: PaymentsSessionRequest) -> Self { - let payment_id = match item.payment_id { - PaymentIdType::PaymentIntentId(id) => Some(id), - _ => None, - }; - Self { - payment_id, + payment_id: Some(item.payment_id), ..Default::default() } } @@ -664,11 +659,12 @@ pub struct PaymentsRetrieveRequest { pub struct ConnectorSessionToken { pub connector_name: String, pub session_token: String, + pub session_id: Option, } #[derive(Default, Debug, serde::Deserialize, Clone)] pub struct PaymentsSessionRequest { - pub payment_id: PaymentIdType, + pub payment_id: String, pub client_secret: String, } diff --git a/crates/common_utils/src/custom_serde.rs b/crates/common_utils/src/custom_serde.rs index f5b4c95e10..d7bd972150 100644 --- a/crates/common_utils/src/custom_serde.rs +++ b/crates/common_utils/src/custom_serde.rs @@ -85,3 +85,20 @@ pub mod iso8601 { } } } + +/// https://github.com/serde-rs/serde/issues/994#issuecomment-316895860 + +pub mod json_string { + use serde::de::{self, Deserialize, DeserializeOwned, Deserializer}; + use serde_json; + + /// Deserialize a string which is in json format + pub fn deserialize<'de, T, D>(deserializer: D) -> Result + where + T: DeserializeOwned, + D: Deserializer<'de>, + { + let j = String::deserialize(deserializer)?; + serde_json::from_str(&j).map_err(de::Error::custom) + } +} diff --git a/crates/router/src/configs/settings.rs b/crates/router/src/configs/settings.rs index 05ed6247d1..03d67edfa5 100644 --- a/crates/router/src/configs/settings.rs +++ b/crates/router/src/configs/settings.rs @@ -112,6 +112,7 @@ pub struct Connectors { pub checkout: ConnectorParams, pub stripe: ConnectorParams, pub braintree: ConnectorParams, + pub klarna: ConnectorParams, pub supported: SupportedConnectors, } @@ -173,7 +174,8 @@ impl Settings { .try_parsing(true) .separator("__") .list_separator(",") - .with_list_parse_key("redis.cluster_urls"), + .with_list_parse_key("redis.cluster_urls") + .with_list_parse_key("connectors.supported.wallets"), ) .build()?; diff --git a/crates/router/src/connector.rs b/crates/router/src/connector.rs index 995c14bbf9..adb6fa3ce6 100644 --- a/crates/router/src/connector.rs +++ b/crates/router/src/connector.rs @@ -3,9 +3,10 @@ pub mod adyen; pub mod authorizedotnet; pub mod braintree; pub mod checkout; +pub mod klarna; pub mod stripe; pub use self::{ aci::Aci, adyen::Adyen, authorizedotnet::Authorizedotnet, braintree::Braintree, - checkout::Checkout, stripe::Stripe, + checkout::Checkout, klarna::Klarna, stripe::Stripe, }; diff --git a/crates/router/src/connector/braintree.rs b/crates/router/src/connector/braintree.rs index 006c125b57..24bf1ef15a 100644 --- a/crates/router/src/connector/braintree.rs +++ b/crates/router/src/connector/braintree.rs @@ -61,7 +61,99 @@ impl types::PaymentsResponseData, > for Braintree { - //TODO: implement sessions flow + fn get_headers( + &self, + req: &types::PaymentsSessionRouterData, + ) -> CustomResult, errors::ConnectorError> { + let mut headers = vec![ + ( + headers::CONTENT_TYPE.to_string(), + types::PaymentsSessionType::get_content_type(self).to_string(), + ), + (headers::X_ROUTER.to_string(), "test".to_string()), + (headers::X_API_VERSION.to_string(), "6".to_string()), + (headers::ACCEPT.to_string(), "application/json".to_string()), + ]; + let mut api_key = self.get_auth_header(&req.connector_auth_type)?; + headers.append(&mut api_key); + Ok(headers) + } + + fn get_content_type(&self) -> &'static str { + "application/json" + } + + fn get_url( + &self, + req: &types::PaymentsSessionRouterData, + connectors: Connectors, + ) -> CustomResult { + let auth_type = braintree::BraintreeAuthType::try_from(&req.connector_auth_type) + .change_context(errors::ConnectorError::FailedToObtainAuthType)?; + Ok(format!( + "{}/merchants/{}/client_token", + self.base_url(connectors), + auth_type.merchant_account, + )) + } + + fn build_request( + &self, + req: &types::PaymentsSessionRouterData, + connectors: Connectors, + ) -> CustomResult, errors::ConnectorError> { + let request = Some( + services::RequestBuilder::new() + .method(services::Method::Post) + .url(&types::PaymentsSessionType::get_url(self, req, connectors)?) + .headers(types::PaymentsSessionType::get_headers(self, req)?) + .body(types::PaymentsSessionType::get_request_body(self, req)?) + .build(), + ); + + logger::debug!(session_request=?request); + Ok(request) + } + + fn get_error_response( + &self, + res: Bytes, + ) -> CustomResult { + let response: braintree::ErrorResponse = res + .parse_struct("Error Response") + .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; + + Ok(ErrorResponse { + code: consts::NO_ERROR_CODE.to_string(), + message: response.api_error_response.message, + reason: None, + }) + } + + fn get_request_body( + &self, + _req: &types::PaymentsSessionRouterData, + ) -> CustomResult, errors::ConnectorError> { + Ok(None) + } + + fn handle_response( + &self, + data: &types::PaymentsSessionRouterData, + res: Response, + ) -> CustomResult { + logger::debug!(payment_session_response_braintree=?res); + let response: braintree::BraintreeSessionTokenResponse = res + .response + .parse_struct("braintree SessionTokenReponse") + .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; + types::RouterData::try_from(types::ResponseRouterData { + response, + data: data.clone(), + http_code: res.status_code, + }) + .change_context(errors::ConnectorError::ResponseHandlingFailed) + } } impl api::PreVerify for Braintree {} diff --git a/crates/router/src/connector/braintree/transformers.rs b/crates/router/src/connector/braintree/transformers.rs index dc1cc63df0..6ab8aff0b3 100644 --- a/crates/router/src/connector/braintree/transformers.rs +++ b/crates/router/src/connector/braintree/transformers.rs @@ -154,12 +154,54 @@ impl } } -#[derive(Default, Debug, Clone, Deserialize, Eq, PartialEq)] +impl + TryFrom< + types::ResponseRouterData, + > for types::RouterData +{ + type Error = error_stack::Report; + fn try_from( + item: types::ResponseRouterData< + F, + BraintreeSessionTokenResponse, + T, + types::PaymentsResponseData, + >, + ) -> Result { + Ok(types::RouterData { + response: Ok(types::PaymentsResponseData::SessionResponse { + session_token: item.response.client_token.value.authorization_fingerprint, + session_id: None, + }), + ..item.data + }) + } +} + +#[derive(Default, Debug, Clone, Deserialize)] #[serde(rename_all = "camelCase")] pub struct BraintreePaymentsResponse { transaction: TransactionResponse, } +#[derive(Default, Debug, Clone, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct AuthorizationFingerprint { + authorization_fingerprint: String, +} +#[derive(Default, Debug, Clone, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ClientToken { + #[serde(with = "common_utils::custom_serde::json_string")] + pub value: AuthorizationFingerprint, +} + +#[derive(Default, Debug, Clone, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct BraintreeSessionTokenResponse { + pub client_token: ClientToken, +} + #[derive(Default, Debug, Clone, Deserialize, Eq, PartialEq)] #[serde(rename_all = "camelCase")] pub struct TransactionResponse { diff --git a/crates/router/src/connector/klarna.rs b/crates/router/src/connector/klarna.rs new file mode 100644 index 0000000000..7b62997740 --- /dev/null +++ b/crates/router/src/connector/klarna.rs @@ -0,0 +1,241 @@ +#![allow(dead_code)] +mod transformers; +use std::fmt::Debug; + +use bytes::Bytes; +use error_stack::{IntoReport, ResultExt}; +use transformers as klarna; + +use crate::{ + configs::settings::Connectors, + core::errors::{self, CustomResult}, + headers, + services::{self, logger}, + types::{ + self, + api::{self, ConnectorCommon}, + ErrorResponse, Response, + }, + utils::{self, BytesExt}, +}; + +#[derive(Debug, Clone)] +pub struct Klarna; + +impl api::ConnectorCommon for Klarna { + fn id(&self) -> &'static str { + "klarna" + } + + fn common_get_content_type(&self) -> &'static str { + "application/x-www-form-urlencoded" + } + + fn base_url(&self, connectors: Connectors) -> String { + connectors.klarna.base_url + } + + fn get_auth_header( + &self, + auth_type: &types::ConnectorAuthType, + ) -> CustomResult, errors::ConnectorError> { + let auth: klarna::KlarnaAuthType = auth_type + .try_into() + .change_context(errors::ConnectorError::FailedToObtainAuthType)?; + Ok(vec![(headers::AUTHORIZATION.to_string(), auth.basic_token)]) + } +} + +impl api::Payment for Klarna {} + +impl api::PaymentAuthorize for Klarna {} +impl api::PaymentSync for Klarna {} +impl api::PaymentVoid for Klarna {} +impl api::PaymentCapture for Klarna {} +impl api::PaymentSession for Klarna {} + +impl + services::ConnectorIntegration< + api::Session, + types::PaymentsSessionData, + types::PaymentsResponseData, + > for Klarna +{ + fn get_headers( + &self, + req: &types::PaymentsSessionRouterData, + ) -> CustomResult, errors::ConnectorError> { + let mut header = vec![ + ( + headers::CONTENT_TYPE.to_string(), + types::PaymentsAuthorizeType::get_content_type(self).to_string(), + ), + (headers::X_ROUTER.to_string(), "test".to_string()), + ]; + let mut api_key = self.get_auth_header(&req.connector_auth_type)?; + header.append(&mut api_key); + Ok(header) + } + + fn get_content_type(&self) -> &'static str { + self.common_get_content_type() + } + + fn get_url( + &self, + _req: &types::PaymentsSessionRouterData, + connectors: Connectors, + ) -> CustomResult { + Ok(format!( + "{}{}", + self.base_url(connectors), + "payments/v1/sessions" + )) + } + + fn get_request_body( + &self, + req: &types::PaymentsSessionRouterData, + ) -> CustomResult, errors::ConnectorError> { + // encode only for for urlencoded things. + let klarna_req = utils::Encode::::convert_and_encode(req) + .change_context(errors::ConnectorError::RequestEncodingFailed)?; + logger::debug!(klarna_payment_logs=?klarna_req); + Ok(Some(klarna_req)) + } + + fn build_request( + &self, + req: &types::PaymentsSessionRouterData, + connectors: Connectors, + ) -> CustomResult, errors::ConnectorError> { + Ok(Some( + services::RequestBuilder::new() + .method(services::Method::Post) + .url(&types::PaymentsSessionType::get_url(self, req, connectors)?) + .headers(types::PaymentsSessionType::get_headers(self, req)?) + .header(headers::X_ROUTER, "test") + .body(types::PaymentsSessionType::get_request_body(self, req)?) + .build(), + )) + } + + fn handle_response( + &self, + data: &types::PaymentsSessionRouterData, + res: Response, + ) -> CustomResult { + let response: klarna::KlarnaSessionResponse = res + .response + .parse_struct("KlarnaPaymentsResponse") + .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; + types::RouterData::try_from(types::ResponseRouterData { + response, + data: data.clone(), + http_code: res.status_code, + }) + .change_context(errors::ConnectorError::ResponseHandlingFailed) + } + + fn get_error_response( + &self, + res: Bytes, + ) -> CustomResult { + let response: klarna::KlarnaErrorResponse = res + .parse_struct("KlarnaErrorResponse") + .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; + Ok(ErrorResponse { + code: response.error_code, + message: response.error_messages.join(" & "), + reason: None, + }) + } +} + +impl api::PreVerify for Klarna {} + +impl + services::ConnectorIntegration< + api::Verify, + types::VerifyRequestData, + types::PaymentsResponseData, + > for Klarna +{ + // TODO: Critical Implement +} + +impl + services::ConnectorIntegration< + api::Capture, + types::PaymentsCaptureData, + types::PaymentsResponseData, + > for Klarna +{ + // Not Implemented (R) +} + +impl + services::ConnectorIntegration + for Klarna +{ + // Not Implemented (R) +} + +impl + services::ConnectorIntegration< + api::Authorize, + types::PaymentsAuthorizeData, + types::PaymentsResponseData, + > for Klarna +{ + //Not Implemented (R) +} + +impl + services::ConnectorIntegration< + api::Void, + types::PaymentsCancelData, + types::PaymentsResponseData, + > for Klarna +{ +} + +impl api::Refund for Klarna {} +impl api::RefundExecute for Klarna {} +impl api::RefundSync for Klarna {} + +impl services::ConnectorIntegration + for Klarna +{ +} + +impl services::ConnectorIntegration + for Klarna +{ +} + +#[async_trait::async_trait] +impl api::IncomingWebhook for Klarna { + fn get_webhook_object_reference_id( + &self, + _body: &[u8], + ) -> CustomResult { + Err(errors::ConnectorError::WebhooksNotImplemented).into_report() + } + + fn get_webhook_event_type( + &self, + _body: &[u8], + ) -> CustomResult { + Err(errors::ConnectorError::WebhooksNotImplemented).into_report() + } + + fn get_webhook_resource_object( + &self, + _body: &[u8], + ) -> CustomResult { + Err(errors::ConnectorError::WebhooksNotImplemented).into_report() + } +} + +impl services::ConnectorRedirectResponse for Klarna {} diff --git a/crates/router/src/connector/klarna/transformers.rs b/crates/router/src/connector/klarna/transformers.rs new file mode 100644 index 0000000000..18f1236b7e --- /dev/null +++ b/crates/router/src/connector/klarna/transformers.rs @@ -0,0 +1,121 @@ +use serde::{Deserialize, Serialize}; + +use crate::{ + core::errors, + types::{self, storage::enums}, +}; + +#[derive(Default, Debug, Serialize)] +pub struct KlarnaPaymentsRequest {} + +#[derive(Serialize)] +pub struct KlarnaSessionRequest { + intent: KlarnaSessionIntent, + purchase_country: String, + purchase_currency: enums::Currency, + locale: String, + order_amount: i32, + order_lines: Vec, +} + +#[derive(Deserialize)] +pub struct KlarnaSessionResponse { + pub client_token: String, + pub session_id: String, +} + +impl TryFrom<&types::PaymentsSessionRouterData> for KlarnaSessionRequest { + type Error = error_stack::Report; + fn try_from(item: &types::PaymentsSessionRouterData) -> Result { + let request = &item.request; + Ok(Self { + intent: KlarnaSessionIntent::Buy, + purchase_country: "US".to_string(), + purchase_currency: request.currency, + order_amount: request.amount, + locale: "en-US".to_string(), + order_lines: vec![OrderLines { + name: "Battery Power Pack".to_string(), + quantity: 1, + unit_price: request.amount, + total_amount: request.amount, + }], + }) + } +} + +impl TryFrom> + for types::PaymentsSessionRouterData +{ + type Error = error_stack::Report; + fn try_from( + item: types::PaymentsSessionResponseRouterData, + ) -> Result { + let response = &item.response; + Ok(types::RouterData { + response: Ok(types::PaymentsResponseData::SessionResponse { + session_id: Some(response.session_id.clone()), + session_token: response.client_token.clone(), + }), + ..item.data + }) + } +} + +#[derive(Serialize)] +pub struct OrderLines { + name: String, + quantity: u64, + unit_price: i32, + total_amount: i32, +} + +#[derive(Serialize)] +#[serde(rename_all = "snake_case")] +pub enum KlarnaSessionIntent { + Buy, + Tokenize, + BuyAndTokenize, +} + +pub struct KlarnaAuthType { + pub basic_token: String, +} + +impl TryFrom<&types::ConnectorAuthType> for KlarnaAuthType { + type Error = error_stack::Report; + fn try_from(auth_type: &types::ConnectorAuthType) -> Result { + if let types::ConnectorAuthType::HeaderKey { api_key } = auth_type { + Ok(Self { + basic_token: api_key.to_string(), + }) + } else { + Err(errors::ConnectorError::FailedToObtainAuthType.into()) + } + } +} + +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum KlarnaPaymentStatus { + Succeeded, + Failed, + #[default] + Processing, +} + +impl From for enums::AttemptStatus { + fn from(item: KlarnaPaymentStatus) -> Self { + match item { + KlarnaPaymentStatus::Succeeded => enums::AttemptStatus::Charged, + KlarnaPaymentStatus::Failed => enums::AttemptStatus::Failure, + KlarnaPaymentStatus::Processing => enums::AttemptStatus::Authorizing, + } + } +} + +#[derive(Deserialize)] +pub struct KlarnaErrorResponse { + pub error_code: String, + pub error_messages: Vec, +} diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index f51db03acf..46d1fc4f4d 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -398,16 +398,19 @@ where CallConnectorAction::Trigger, merchant_account.storage_scheme, ) - .await?; + .await?; //FIXME: remove this error propogation match res.response { Ok(connector_response) => { - if let types::PaymentsResponseData::SessionResponse { session_token } = - connector_response + if let types::PaymentsResponseData::SessionResponse { + session_token, + session_id, + } = connector_response { payment_data .sessions_token .push(api::ConnectorSessionToken { + session_id, connector_name, session_token, }); @@ -558,6 +561,7 @@ pub fn should_call_connector( enums::IntentStatus::RequiresCapture ) } + "PaymentSession" => true, _ => false, } } diff --git a/crates/router/src/core/payments/operations/payment_session.rs b/crates/router/src/core/payments/operations/payment_session.rs index a629e85a3c..101c924535 100644 --- a/crates/router/src/core/payments/operations/payment_session.rs +++ b/crates/router/src/core/payments/operations/payment_session.rs @@ -115,6 +115,14 @@ impl GetTracker, api::PaymentsSessionRequest> .attach_printable("Database error when finding connector response") })?; + let customer_details = payments::CustomerDetails { + customer_id: payment_intent.customer_id.clone(), + name: None, + email: None, + phone: None, + phone_country_code: None, + }; + Ok(( Box::new(self), PaymentData { @@ -137,7 +145,7 @@ impl GetTracker, api::PaymentsSessionRequest> sessions_token: vec![], connector_response, }, - None, + Some(customer_details), )) } } @@ -174,10 +182,7 @@ impl ValidateRequest for Paymen operations::ValidateResult<'a>, )> { //paymentid is already generated and should be sent in the request - let given_payment_id = request - .payment_id - .get_payment_intent_id() - .change_context(errors::ApiErrorResponse::PaymentNotFound)?; + let given_payment_id = request.payment_id.clone(); Ok(( Box::new(self), diff --git a/crates/router/src/core/payments/transformers.rs b/crates/router/src/core/payments/transformers.rs index 3daf214769..8c5871445a 100644 --- a/crates/router/src/core/payments/transformers.rs +++ b/crates/router/src/core/payments/transformers.rs @@ -459,8 +459,11 @@ impl TryFrom> for types::PaymentsCancelData { impl TryFrom> for types::PaymentsSessionData { type Error = errors::ApiErrorResponse; - fn try_from(_payment_data: PaymentData) -> Result { - Ok(Self {}) + fn try_from(payment_data: PaymentData) -> Result { + Ok(Self { + amount: payment_data.amount.into(), + currency: payment_data.currency, + }) } } diff --git a/crates/router/src/routes/app.rs b/crates/router/src/routes/app.rs index 90f3a71e6e..50161461ab 100644 --- a/crates/router/src/routes/app.rs +++ b/crates/router/src/routes/app.rs @@ -59,6 +59,9 @@ impl Payments { .app_data(web::Data::new(state)) .service(web::resource("").route(web::post().to(payments_create))) .service(web::resource("/list").route(web::get().to(payments_list))) + .service( + web::resource("/session_tokens").route(web::get().to(payments_connector_session)), + ) .service( web::resource("/{payment_id}") .route(web::get().to(payments_retrieve)) @@ -75,9 +78,6 @@ impl Payments { web::resource("/{payment_id}/{merchant_id}/response/{connector}") .route(web::get().to(payments_response)), ) - .service( - web::resource("/session_tokens").route(web::get().to(payments_connector_session)), - ) } } diff --git a/crates/router/src/types.rs b/crates/router/src/types.rs index 571a11b423..58a029b135 100644 --- a/crates/router/src/types.rs +++ b/crates/router/src/types.rs @@ -36,6 +36,9 @@ pub type PaymentsCancelResponseRouterData = ResponseRouterData; pub type PaymentsSyncResponseRouterData = ResponseRouterData; +pub type PaymentsSessionResponseRouterData = + ResponseRouterData; + pub type RefundsResponseRouterData = ResponseRouterData; @@ -51,6 +54,8 @@ pub type RefundExecuteType = dyn services::ConnectorIntegration; pub type RefundSyncType = dyn services::ConnectorIntegration; +pub type PaymentsSessionType = + dyn services::ConnectorIntegration; pub type VerifyRouterData = RouterData; @@ -118,12 +123,15 @@ pub struct PaymentsCancelData { #[derive(Debug, Clone)] pub struct PaymentsSessionData { - //TODO: Add the fields here as required + pub amount: i32, + pub currency: storage_enums::Currency, } -#[derive(serde::Serialize, Debug)] -pub struct PaymentsSessionResponseData { - pub client_token: Option, +#[derive(Debug, Clone)] +pub struct ConnectorSessionToken { + pub connector_name: String, + pub session_id: Option, + pub session_token: String, } #[derive(Debug, Clone)] @@ -137,6 +145,19 @@ pub struct VerifyRequestData { pub setup_mandate_details: Option, } +#[derive(Debug, Clone)] +pub struct PaymentsTransactionResponse { + pub resource_id: ResponseId, + pub redirection_data: Option, + pub redirect: bool, +} + +#[derive(Debug, Clone)] +pub struct PaymentsSessionResponse { + pub session_id: Option, + pub session_token: String, +} + #[derive(Debug, Clone)] pub enum PaymentsResponseData { TransactionResponse { @@ -147,6 +168,7 @@ pub enum PaymentsResponseData { }, SessionResponse { session_token: String, + session_id: Option, }, } diff --git a/crates/router/src/types/api.rs b/crates/router/src/types/api.rs index 4f04fd09cf..a2220dd9f2 100644 --- a/crates/router/src/types/api.rs +++ b/crates/router/src/types/api.rs @@ -83,7 +83,7 @@ impl ConnectorData { let connector_name = types::Connector::from_str(name) .into_report() .change_context(errors::ConnectorError::InvalidConnectorName) - .attach_printable_lazy(|| format!("unable to parse connector name {:?}", connector)) + .attach_printable_lazy(|| format!("unable to parse connector name {connector:?}")) .change_context(errors::ApiErrorResponse::InternalServerError)?; Ok(ConnectorData { connector, @@ -102,6 +102,7 @@ impl ConnectorData { "checkout" => Ok(Box::new(&connector::Checkout)), "authorizedotnet" => Ok(Box::new(&connector::Authorizedotnet)), "braintree" => Ok(Box::new(&connector::Braintree)), + "klarna" => Ok(Box::new(&connector::Klarna)), _ => Err(report!(errors::UnexpectedError) .attach_printable(format!("invalid connector name: {connector_name}"))) .change_context(errors::ConnectorError::InvalidConnectorName) diff --git a/crates/router/src/types/connector.rs b/crates/router/src/types/connector.rs index b2c95dcac3..bfadc5be04 100644 --- a/crates/router/src/types/connector.rs +++ b/crates/router/src/types/connector.rs @@ -7,6 +7,7 @@ pub enum Connector { Aci, Authorizedotnet, Braintree, + Klarna, #[default] Dummy, } diff --git a/loadtest/config/Development.toml b/loadtest/config/Development.toml index c0d779364a..27cdc5b93e 100644 --- a/loadtest/config/Development.toml +++ b/loadtest/config/Development.toml @@ -56,4 +56,12 @@ base_url = "https://api.sandbox.checkout.com/" base_url = "http://stripe-mock:12111/" [connectors.braintree] -base_url = "https://api.sandbox.braintreegateway.com/" \ No newline at end of file +base_url = "https://api.sandbox.braintreegateway.com/" + +[connectors.klarna] +base_url = "https://api-na.playground.klarna.com/" + +[connectors.supported] +wallets = ["klarna","braintree"] +cards = ["stripe","adyen","authorizedotnet","checkout","braintree"] +