From 107c66fec331376aa8c9f1e710e1503793fde119 Mon Sep 17 00:00:00 2001 From: chikke srujan <121822803+srujanchikke@users.noreply.github.com> Date: Sun, 17 Dec 2023 13:25:53 +0530 Subject: [PATCH] feat(connector): [PlaceToPay] Implement Cards for PlaceToPay (#3117) Signed-off-by: chikke srujan <121822803+srujanchikke@users.noreply.github.com> Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com> --- config/config.example.toml | 2 +- config/development.toml | 2 +- config/docker_compose.toml | 2 +- crates/router/src/connector/placetopay.rs | 177 ++++++-- .../src/connector/placetopay/transformers.rs | 423 ++++++++++++++---- crates/router/src/connector/utils.rs | 6 + loadtest/config/development.toml | 2 +- 7 files changed, 483 insertions(+), 131 deletions(-) diff --git a/config/config.example.toml b/config/config.example.toml index fb1c12a7d7..7b4380ba2d 100644 --- a/config/config.example.toml +++ b/config/config.example.toml @@ -216,7 +216,7 @@ payeezy.base_url = "https://api-cert.payeezy.com/" payme.base_url = "https://sandbox.payme.io/" paypal.base_url = "https://api-m.sandbox.paypal.com/" payu.base_url = "https://secure.snd.payu.com/" -placetopay.base_url = "https://api-co-dev.placetopay.ws/gateway" +placetopay.base_url = "https://test.placetopay.com/rest/gateway" powertranz.base_url = "https://staging.ptranz.com/api/" prophetpay.base_url = "https://ccm-thirdparty.cps.golf/" rapyd.base_url = "https://sandboxapi.rapyd.net" diff --git a/config/development.toml b/config/development.toml index 9646a0a045..b6a0f9f99c 100644 --- a/config/development.toml +++ b/config/development.toml @@ -203,7 +203,7 @@ payeezy.base_url = "https://api-cert.payeezy.com/" payme.base_url = "https://sandbox.payme.io/" paypal.base_url = "https://api-m.sandbox.paypal.com/" payu.base_url = "https://secure.snd.payu.com/" -placetopay.base_url = "https://api-co-dev.placetopay.ws/gateway" +placetopay.base_url = "https://test.placetopay.com/rest/gateway" powertranz.base_url = "https://staging.ptranz.com/api/" prophetpay.base_url = "https://ccm-thirdparty.cps.golf/" rapyd.base_url = "https://sandboxapi.rapyd.net" diff --git a/config/docker_compose.toml b/config/docker_compose.toml index 13d405131b..eab1ea5408 100644 --- a/config/docker_compose.toml +++ b/config/docker_compose.toml @@ -138,7 +138,7 @@ payeezy.base_url = "https://api-cert.payeezy.com/" payme.base_url = "https://sandbox.payme.io/" paypal.base_url = "https://api-m.sandbox.paypal.com/" payu.base_url = "https://secure.snd.payu.com/" -placetopay.base_url = "https://api-co-dev.placetopay.ws/gateway" +placetopay.base_url = "https://test.placetopay.com/rest/gateway" powertranz.base_url = "https://staging.ptranz.com/api/" prophetpay.base_url = "https://ccm-thirdparty.cps.golf/" rapyd.base_url = "https://sandboxapi.rapyd.net" diff --git a/crates/router/src/connector/placetopay.rs b/crates/router/src/connector/placetopay.rs index 1b80bcf7f7..82998e97a0 100644 --- a/crates/router/src/connector/placetopay.rs +++ b/crates/router/src/connector/placetopay.rs @@ -4,21 +4,21 @@ use std::fmt::Debug; use common_utils::request::RequestContent; use error_stack::{IntoReport, ResultExt}; -use masking::ExposeInterface; use transformers as placetopay; use crate::{ configs::settings, + connector::utils::{self}, core::errors::{self, CustomResult}, headers, services::{ self, - request::{self, Mask}, + request::{self}, ConnectorIntegration, ConnectorValidation, }, types::{ self, - api::{self, ConnectorCommon, ConnectorCommonExt}, + api::{self, enums, ConnectorCommon, ConnectorCommonExt}, ErrorResponse, Response, }, utils::BytesExt, @@ -86,18 +86,6 @@ impl ConnectorCommon for Placetopay { connectors.placetopay.base_url.as_ref() } - fn get_auth_header( - &self, - auth_type: &types::ConnectorAuthType, - ) -> CustomResult)>, errors::ConnectorError> { - let auth = placetopay::PlacetopayAuthType::try_from(auth_type) - .change_context(errors::ConnectorError::FailedToObtainAuthType)?; - Ok(vec![( - headers::AUTHORIZATION.to_string(), - auth.api_key.expose().into_masked(), - )]) - } - fn build_error_response( &self, res: Response, @@ -109,16 +97,32 @@ impl ConnectorCommon for Placetopay { Ok(ErrorResponse { status_code: res.status_code, - code: response.code, - message: response.message, - reason: response.reason, + code: response.status.reason.to_owned(), + message: response.status.message.to_owned(), + reason: Some(response.status.message), attempt_status: None, connector_transaction_id: None, }) } } -impl ConnectorValidation for Placetopay {} +impl ConnectorValidation for Placetopay { + fn validate_capture_method( + &self, + capture_method: Option, + ) -> CustomResult<(), errors::ConnectorError> { + let capture_method = capture_method.unwrap_or_default(); + match capture_method { + enums::CaptureMethod::Manual => Ok(()), + enums::CaptureMethod::Automatic + | enums::CaptureMethod::ManualMultiple + | enums::CaptureMethod::Scheduled => Err(utils::construct_not_supported_error_report( + capture_method, + self.id(), + )), + } + } +} impl ConnectorIntegration for Placetopay @@ -157,9 +161,9 @@ impl ConnectorIntegration CustomResult { - Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into()) + Ok(format!("{}/process", self.base_url(connectors))) } fn get_request_body( @@ -206,7 +210,7 @@ impl ConnectorIntegration CustomResult { let response: placetopay::PlacetopayPaymentsResponse = res .response - .parse_struct("Placetopay PaymentsAuthorizeResponse") + .parse_struct("Placetopay PlacetopayPaymentsResponse") .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; types::RouterData::try_from(types::ResponseRouterData { response, @@ -241,9 +245,18 @@ impl ConnectorIntegration CustomResult { - Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into()) + Ok(format!("{}/query", self.base_url(connectors))) + } + + fn get_request_body( + &self, + req: &types::PaymentsSyncRouterData, + _connectors: &settings::Connectors, + ) -> CustomResult { + let req_obj = placetopay::PlacetopayPsyncRequest::try_from(req)?; + Ok(RequestContent::Json(Box::new(req_obj))) } fn build_request( @@ -253,10 +266,13 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { Ok(Some( services::RequestBuilder::new() - .method(services::Method::Get) + .method(services::Method::Post) .url(&types::PaymentsSyncType::get_url(self, req, connectors)?) .attach_default_headers() .headers(types::PaymentsSyncType::get_headers(self, req, connectors)?) + .set_body(types::PaymentsSyncType::get_request_body( + self, req, connectors, + )?) .build(), )) } @@ -303,17 +319,18 @@ impl ConnectorIntegration CustomResult { - Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into()) + Ok(format!("{}/transaction", self.base_url(connectors))) } fn get_request_body( &self, - _req: &types::PaymentsCaptureRouterData, + req: &types::PaymentsCaptureRouterData, _connectors: &settings::Connectors, ) -> CustomResult { - Err(errors::ConnectorError::NotImplemented("get_request_body method".to_string()).into()) + let req_obj = placetopay::PlacetopayNextActionRequest::try_from(req)?; + Ok(RequestContent::Json(Box::new(req_obj))) } fn build_request( @@ -363,6 +380,75 @@ impl ConnectorIntegration for Placetopay { + fn get_headers( + &self, + req: &types::PaymentsCancelRouterData, + connectors: &settings::Connectors, + ) -> CustomResult)>, errors::ConnectorError> { + self.build_headers(req, connectors) + } + + fn get_content_type(&self) -> &'static str { + self.common_get_content_type() + } + + fn get_url( + &self, + _req: &types::PaymentsCancelRouterData, + connectors: &settings::Connectors, + ) -> CustomResult { + Ok(format!("{}/transaction", self.base_url(connectors))) + } + + fn get_request_body( + &self, + req: &types::PaymentsCancelRouterData, + _connectors: &settings::Connectors, + ) -> CustomResult { + let req_obj = placetopay::PlacetopayNextActionRequest::try_from(req)?; + Ok(RequestContent::Json(Box::new(req_obj))) + } + + fn build_request( + &self, + req: &types::PaymentsCancelRouterData, + connectors: &settings::Connectors, + ) -> CustomResult, errors::ConnectorError> { + Ok(Some( + services::RequestBuilder::new() + .method(services::Method::Post) + .url(&types::PaymentsVoidType::get_url(self, req, connectors)?) + .attach_default_headers() + .headers(types::PaymentsVoidType::get_headers(self, req, connectors)?) + .set_body(types::PaymentsVoidType::get_request_body( + self, req, connectors, + )?) + .build(), + )) + } + + fn handle_response( + &self, + data: &types::PaymentsCancelRouterData, + res: Response, + ) -> CustomResult { + let response: placetopay::PlacetopayPaymentsResponse = res + .response + .parse_struct("Placetopay PaymentCancelResponse") + .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; + types::RouterData::try_from(types::ResponseRouterData { + response, + data: data.clone(), + http_code: res.status_code, + }) + } + + fn get_error_response( + &self, + res: Response, + ) -> CustomResult { + self.build_error_response(res) + } } impl ConnectorIntegration @@ -383,9 +469,9 @@ impl ConnectorIntegration, - _connectors: &settings::Connectors, + connectors: &settings::Connectors, ) -> CustomResult { - Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into()) + Ok(format!("{}/transaction", self.base_url(connectors))) } fn get_request_body( @@ -393,13 +479,7 @@ impl ConnectorIntegration, _connectors: &settings::Connectors, ) -> CustomResult { - let connector_router_data = placetopay::PlacetopayRouterData::try_from(( - &self.get_currency_unit(), - req.request.currency, - req.request.refund_amount, - req, - ))?; - let req_obj = placetopay::PlacetopayRefundRequest::try_from(&connector_router_data)?; + let req_obj = placetopay::PlacetopayRefundRequest::try_from(req)?; Ok(RequestContent::Json(Box::new(req_obj))) } @@ -427,9 +507,9 @@ impl ConnectorIntegration, res: Response, ) -> CustomResult, errors::ConnectorError> { - let response: placetopay::RefundResponse = res + let response: placetopay::PlacetopayRefundResponse = res .response - .parse_struct("placetopay RefundResponse") + .parse_struct("placetopay PlacetopayRefundResponse") .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; types::RouterData::try_from(types::ResponseRouterData { response, @@ -464,9 +544,18 @@ impl ConnectorIntegration CustomResult { - Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into()) + Ok(format!("{}/query", self.base_url(connectors))) + } + + fn get_request_body( + &self, + req: &types::RefundsRouterData, + _connectors: &settings::Connectors, + ) -> CustomResult { + let req_obj = placetopay::PlacetopayRsyncRequest::try_from(req)?; + Ok(RequestContent::Json(Box::new(req_obj))) } fn build_request( @@ -492,9 +581,9 @@ impl ConnectorIntegration CustomResult { - let response: placetopay::RefundResponse = res + let response: placetopay::PlacetopayRefundResponse = res .response - .parse_struct("placetopay RefundSyncResponse") + .parse_struct("placetopay PlacetopayRefundResponse") .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; types::RouterData::try_from(types::ResponseRouterData { response, diff --git a/crates/router/src/connector/placetopay/transformers.rs b/crates/router/src/connector/placetopay/transformers.rs index e947c6830c..7fef8b8954 100644 --- a/crates/router/src/connector/placetopay/transformers.rs +++ b/crates/router/src/connector/placetopay/transformers.rs @@ -1,14 +1,23 @@ -use masking::Secret; +use api_models::payments; +use common_utils::date_time; +use diesel_models::enums; +use error_stack::{IntoReport, ResultExt}; +use masking::{PeekInterface, Secret}; +use ring::digest; use serde::{Deserialize, Serialize}; use crate::{ - connector::utils::PaymentsAuthorizeRequestData, + connector::utils::{ + self, BrowserInformationData, CardData, PaymentsAuthorizeRequestData, + PaymentsSyncRequestData, RouterData, + }, + consts, core::errors, - types::{self, api, storage::enums}, + types::{self, api, storage::enums as storage_enums}, }; pub struct PlacetopayRouterData { - pub amount: i64, // The type of amount that a connector accepts, for example, String, i64, f64, etc. + pub amount: i64, pub router_data: T, } @@ -36,19 +45,65 @@ impl } } -#[derive(Default, Debug, Serialize, Eq, PartialEq)] +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] pub struct PlacetopayPaymentsRequest { - amount: i64, + auth: PlacetopayAuth, + payment: PlacetopayPayment, + instrument: PlacetopayInstrument, + ip_address: Secret, + user_agent: String, +} + +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub enum PlacetopayAuthorizeAction { + Checkin, +} + +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct PlacetopayAuthType { + login: Secret, + tran_key: Secret, +} + +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct PlacetopayAuth { + login: Secret, + tran_key: Secret, + nonce: String, + seed: String, +} + +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct PlacetopayPayment { + reference: String, + description: String, + amount: PlacetopayAmount, +} + +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct PlacetopayAmount { + currency: storage_enums::Currency, + total: i64, +} + +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct PlacetopayInstrument { card: PlacetopayCard, } -#[derive(Default, Debug, Serialize, Eq, PartialEq)] +#[derive(Debug, Serialize, Clone)] +#[serde(rename_all = "camelCase")] pub struct PlacetopayCard { number: cards::CardNumber, - expiry_month: Secret, - expiry_year: Secret, - cvc: Secret, - complete: bool, + expiration: Secret, + cvv: Secret, } impl TryFrom<&PlacetopayRouterData<&types::PaymentsAuthorizeRouterData>> @@ -58,65 +113,132 @@ impl TryFrom<&PlacetopayRouterData<&types::PaymentsAuthorizeRouterData>> fn try_from( item: &PlacetopayRouterData<&types::PaymentsAuthorizeRouterData>, ) -> Result { + let browser_info = item.router_data.request.get_browser_info()?; + let ip_address = browser_info.get_ip_address()?; + let user_agent = browser_info.get_user_agent()?; + let auth = PlacetopayAuth::try_from(&item.router_data.connector_auth_type)?; + let payment = PlacetopayPayment { + reference: item.router_data.connector_request_reference_id.clone(), + description: item.router_data.get_description()?, + amount: PlacetopayAmount { + currency: item.router_data.request.currency, + total: item.amount, + }, + }; match item.router_data.request.payment_method_data.clone() { - api::PaymentMethodData::Card(req_card) => { + payments::PaymentMethodData::Card(req_card) => { let card = PlacetopayCard { - number: req_card.card_number, - expiry_month: req_card.card_exp_month, - expiry_year: req_card.card_exp_year, - cvc: req_card.card_cvc, - complete: item.router_data.request.is_auto_capture()?, + number: req_card.card_number.clone(), + expiration: req_card + .clone() + .get_card_expiry_month_year_2_digit_with_delimiter("/".to_owned()), + cvv: req_card.card_cvc.clone(), }; Ok(Self { - amount: item.amount.to_owned(), - card, + ip_address, + user_agent, + auth, + payment, + instrument: PlacetopayInstrument { + card: card.to_owned(), + }, }) } - _ => Err(errors::ConnectorError::NotImplemented("Payment methods".to_string()).into()), + payments::PaymentMethodData::Wallet(_) + | payments::PaymentMethodData::CardRedirect(_) + | payments::PaymentMethodData::PayLater(_) + | payments::PaymentMethodData::BankRedirect(_) + | payments::PaymentMethodData::BankDebit(_) + | payments::PaymentMethodData::BankTransfer(_) + | payments::PaymentMethodData::Crypto(_) + | payments::PaymentMethodData::MandatePayment + | payments::PaymentMethodData::Reward + | payments::PaymentMethodData::Upi(_) + | payments::PaymentMethodData::Voucher(_) + | payments::PaymentMethodData::GiftCard(_) + | payments::PaymentMethodData::CardToken(_) => { + Err(errors::ConnectorError::NotImplemented( + utils::get_unimplemented_payment_method_error_message("Placetopay"), + ) + .into()) + } } } } -// Auth Struct -pub struct PlacetopayAuthType { - pub(super) api_key: Secret, +impl TryFrom<&types::ConnectorAuthType> for PlacetopayAuth { + type Error = error_stack::Report; + fn try_from(auth_type: &types::ConnectorAuthType) -> Result { + let placetopay_auth = PlacetopayAuthType::try_from(auth_type)?; + let nonce_bytes = utils::generate_random_bytes(16); + let now = error_stack::IntoReport::into_report(date_time::date_as_yyyymmddthhmmssmmmz()) + .change_context(errors::ConnectorError::RequestEncodingFailed)?; + let seed = format!("{}+00:00", now.split_at(now.len() - 5).0); + let mut context = digest::Context::new(&digest::SHA256); + context.update(&nonce_bytes); + context.update(seed.as_bytes()); + context.update(placetopay_auth.tran_key.peek().as_bytes()); + let encoded_digest = base64::Engine::encode(&consts::BASE64_ENGINE, context.finish()); + let nonce = base64::Engine::encode(&consts::BASE64_ENGINE, &nonce_bytes); + Ok(Self { + login: placetopay_auth.login, + tran_key: encoded_digest.into(), + nonce, + seed, + }) + } } impl TryFrom<&types::ConnectorAuthType> for PlacetopayAuthType { type Error = error_stack::Report; + fn try_from(auth_type: &types::ConnectorAuthType) -> Result { - match auth_type { - types::ConnectorAuthType::HeaderKey { api_key } => Ok(Self { - api_key: api_key.to_owned(), - }), - _ => Err(errors::ConnectorError::FailedToObtainAuthType.into()), + if let types::ConnectorAuthType::BodyKey { api_key, key1 } = auth_type { + Ok(Self { + login: api_key.to_owned(), + tran_key: key1.to_owned(), + }) + } else { + Err(errors::ConnectorError::FailedToObtainAuthType)? } } } -// PaymentsResponse -#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)] -#[serde(rename_all = "lowercase")] -pub enum PlacetopayPaymentStatus { - Succeeded, + +#[derive(Debug, Clone, Deserialize)] +#[serde(rename_all = "SCREAMING_SNAKE_CASE")] +pub enum PlacetopayStatus { + Ok, Failed, - #[default] - Processing, + Approved, + Rejected, + Pending, + PendingValidation, + PendingProcess, } -impl From for enums::AttemptStatus { - fn from(item: PlacetopayPaymentStatus) -> Self { +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct PlacetopayStatusResponse { + status: PlacetopayStatus, +} + +impl From for enums::AttemptStatus { + fn from(item: PlacetopayStatus) -> Self { match item { - PlacetopayPaymentStatus::Succeeded => Self::Charged, - PlacetopayPaymentStatus::Failed => Self::Failure, - PlacetopayPaymentStatus::Processing => Self::Authorizing, + PlacetopayStatus::Approved | PlacetopayStatus::Ok => Self::Authorized, + PlacetopayStatus::Failed | PlacetopayStatus::Rejected => Self::Failure, + PlacetopayStatus::Pending + | PlacetopayStatus::PendingValidation + | PlacetopayStatus::PendingProcess => Self::Authorizing, } } } -#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)] +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] pub struct PlacetopayPaymentsResponse { - status: PlacetopayPaymentStatus, - id: String, + status: PlacetopayStatusResponse, + internal_reference: u64, } impl @@ -134,9 +256,11 @@ impl >, ) -> Result { Ok(Self { - status: enums::AttemptStatus::from(item.response.status), + status: enums::AttemptStatus::from(item.response.status.status), response: Ok(types::PaymentsResponseData::TransactionResponse { - resource_id: types::ResponseId::ConnectorTransactionId(item.response.id), + resource_id: types::ResponseId::ConnectorTransactionId( + item.response.internal_reference.to_string(), + ), redirection_data: None, mandate_reference: None, connector_metadata: None, @@ -148,61 +272,76 @@ impl }) } } + // REFUND : // Type definition for RefundRequest -#[derive(Default, Debug, Serialize)] +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] pub struct PlacetopayRefundRequest { - pub amount: i64, + auth: PlacetopayAuth, + internal_reference: u64, + action: PlacetopayNextAction, } -impl TryFrom<&PlacetopayRouterData<&types::RefundsRouterData>> for PlacetopayRefundRequest { +impl TryFrom<&types::RefundsRouterData> for PlacetopayRefundRequest { type Error = error_stack::Report; - fn try_from( - item: &PlacetopayRouterData<&types::RefundsRouterData>, - ) -> Result { + fn try_from(item: &types::RefundsRouterData) -> Result { + let auth = PlacetopayAuth::try_from(&item.connector_auth_type)?; + let internal_reference = item + .request + .connector_transaction_id + .parse::() + .into_report() + .change_context(errors::ConnectorError::RequestEncodingFailed)?; + let action = PlacetopayNextAction::Refund; + Ok(Self { - amount: item.amount.to_owned(), + auth, + internal_reference, + action, }) } } -// Type definition for Refund Response - -#[allow(dead_code)] -#[derive(Debug, Serialize, Default, Deserialize, Clone)] -pub enum RefundStatus { - Succeeded, - Failed, - #[default] - Processing, -} - -impl From for enums::RefundStatus { - fn from(item: RefundStatus) -> Self { +impl From for enums::RefundStatus { + fn from(item: PlacetopayRefundStatus) -> Self { match item { - RefundStatus::Succeeded => Self::Success, - RefundStatus::Failed => Self::Failure, - RefundStatus::Processing => Self::Pending, + PlacetopayRefundStatus::Refunded => Self::Success, + PlacetopayRefundStatus::Failed | PlacetopayRefundStatus::Rejected => Self::Failure, + PlacetopayRefundStatus::Pending | PlacetopayRefundStatus::PendingProcess => { + Self::Pending + } } } } -#[derive(Default, Debug, Clone, Serialize, Deserialize)] -pub struct RefundResponse { - id: String, - status: RefundStatus, +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct PlacetopayRefundResponse { + status: PlacetopayRefundStatus, + internal_reference: u64, } -impl TryFrom> +#[derive(Debug, Deserialize)] +#[serde(rename_all = "SCREAMING_SNAKE_CASE")] +pub enum PlacetopayRefundStatus { + Refunded, + Rejected, + Failed, + Pending, + PendingProcess, +} + +impl TryFrom> for types::RefundsRouterData { type Error = error_stack::Report; fn try_from( - item: types::RefundsResponseRouterData, + item: types::RefundsResponseRouterData, ) -> Result { Ok(Self { response: Ok(types::RefundsResponseData { - connector_refund_id: item.response.id.to_string(), + connector_refund_id: item.response.internal_reference.to_string(), refund_status: enums::RefundStatus::from(item.response.status), }), ..item.data @@ -210,16 +349,40 @@ impl TryFrom> } } -impl TryFrom> +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct PlacetopayRsyncRequest { + auth: PlacetopayAuth, + internal_reference: u64, +} + +impl TryFrom<&types::RefundsRouterData> for PlacetopayRsyncRequest { + type Error = error_stack::Report; + fn try_from(item: &types::RefundsRouterData) -> Result { + let auth = PlacetopayAuth::try_from(&item.connector_auth_type)?; + let internal_reference = item + .request + .connector_transaction_id + .parse::() + .into_report() + .change_context(errors::ConnectorError::RequestEncodingFailed)?; + Ok(Self { + auth, + internal_reference, + }) + } +} + +impl TryFrom> for types::RefundsRouterData { type Error = error_stack::Report; fn try_from( - item: types::RefundsResponseRouterData, + item: types::RefundsResponseRouterData, ) -> Result { Ok(Self { response: Ok(types::RefundsResponseData { - connector_refund_id: item.response.id.to_string(), + connector_refund_id: item.response.internal_reference.to_string(), refund_status: enums::RefundStatus::from(item.response.status), }), ..item.data @@ -227,10 +390,104 @@ impl TryFrom> } } -#[derive(Default, Debug, Serialize, Deserialize, PartialEq)] +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] pub struct PlacetopayErrorResponse { - pub status_code: u16, - pub code: String, - pub message: String, - pub reason: Option, + pub status: PlacetopayError, +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct PlacetopayError { + pub status: PlacetopayErrorStatus, + pub message: String, + pub reason: String, +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "SCREAMING_SNAKE_CASE")] +pub enum PlacetopayErrorStatus { + Failed, +} + +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct PlacetopayPsyncRequest { + auth: PlacetopayAuth, + internal_reference: u64, +} + +impl TryFrom<&types::PaymentsSyncRouterData> for PlacetopayPsyncRequest { + type Error = error_stack::Report; + + fn try_from(item: &types::PaymentsSyncRouterData) -> Result { + let auth = PlacetopayAuth::try_from(&item.connector_auth_type)?; + let internal_reference = item + .request + .get_connector_transaction_id()? + .parse::() + .into_report() + .change_context(errors::ConnectorError::RequestEncodingFailed)?; + Ok(Self { + auth, + internal_reference, + }) + } +} + +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct PlacetopayNextActionRequest { + auth: PlacetopayAuth, + internal_reference: u64, + action: PlacetopayNextAction, +} + +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub enum PlacetopayNextAction { + Refund, + Void, + Process, + Checkout, +} + +impl TryFrom<&types::PaymentsCaptureRouterData> for PlacetopayNextActionRequest { + type Error = error_stack::Report; + + fn try_from(item: &types::PaymentsCaptureRouterData) -> Result { + let auth = PlacetopayAuth::try_from(&item.connector_auth_type)?; + let internal_reference = item + .request + .connector_transaction_id + .parse::() + .into_report() + .change_context(errors::ConnectorError::RequestEncodingFailed)?; + let action = PlacetopayNextAction::Checkout; + Ok(Self { + auth, + internal_reference, + action, + }) + } +} + +impl TryFrom<&types::PaymentsCancelRouterData> for PlacetopayNextActionRequest { + type Error = error_stack::Report; + + fn try_from(item: &types::PaymentsCancelRouterData) -> Result { + let auth = PlacetopayAuth::try_from(&item.connector_auth_type)?; + let internal_reference = item + .request + .connector_transaction_id + .parse::() + .into_report() + .change_context(errors::ConnectorError::RequestEncodingFailed)?; + let action = PlacetopayNextAction::Void; + Ok(Self { + auth, + internal_reference, + action, + }) + } } diff --git a/crates/router/src/connector/utils.rs b/crates/router/src/connector/utils.rs index d06caf3ae2..9a538a7207 100644 --- a/crates/router/src/connector/utils.rs +++ b/crates/router/src/connector/utils.rs @@ -1630,6 +1630,12 @@ pub fn is_manual_capture(capture_method: Option) -> bool { || capture_method == Some(enums::CaptureMethod::ManualMultiple) } +pub fn generate_random_bytes(length: usize) -> Vec { + // returns random bytes of length n + let mut rng = rand::thread_rng(); + (0..length).map(|_| rand::Rng::gen(&mut rng)).collect() +} + pub fn validate_currency( request_currency: types::storage::enums::Currency, merchant_config_currency: Option, diff --git a/loadtest/config/development.toml b/loadtest/config/development.toml index 883c243c6c..43eb06820d 100644 --- a/loadtest/config/development.toml +++ b/loadtest/config/development.toml @@ -111,7 +111,7 @@ payeezy.base_url = "https://api-cert.payeezy.com/" payme.base_url = "https://sandbox.payme.io/" paypal.base_url = "https://api-m.sandbox.paypal.com/" payu.base_url = "https://secure.snd.payu.com/" -placetopay.base_url = "https://api-co-dev.placetopay.ws/gateway" +placetopay.base_url = "https://test.placetopay.com/rest/gateway" powertranz.base_url = "https://staging.ptranz.com/api/" prophetpay.base_url = "https://ccm-thirdparty.cps.golf/" rapyd.base_url = "https://sandboxapi.rapyd.net"