feat(checkout): cancel impl for checkout (#54)

Co-authored-by: Abhishek Marrivagu <abhishek.marrivagu@juspay.in>
Co-authored-by: Sanchith Hegde <22217505+SanchithHegde@users.noreply.github.com>
Co-authored-by: Sangamesh Kulkarni <59434228+Sangamesh26@users.noreply.github.com>
Co-authored-by: Manoj Ghorela <118727120+manoj-juspay@users.noreply.github.com>
Co-authored-by: Manoj Ghorela <manoj.ghorela@manoj.ghorela-MacBookPro>
Co-authored-by: Narayan Bhat <48803246+Narayanbhat166@users.noreply.github.com>
This commit is contained in:
Kartikeya Hegde
2022-12-02 12:27:24 +05:30
committed by GitHub
parent bad0d7eeab
commit 35289df715
4 changed files with 162 additions and 30 deletions

View File

@ -271,50 +271,92 @@ impl
{ {
fn get_headers( fn get_headers(
&self, &self,
_req: &types::PaymentsCancelRouterData, req: &types::PaymentsCancelRouterData,
) -> CustomResult<Vec<(String, String)>, errors::ConnectorError> { ) -> CustomResult<Vec<(String, String)>, errors::ConnectorError> {
Err(errors::ConnectorError::NotImplemented("checkout".to_string()).into()) let mut header = vec![
} (
headers::CONTENT_TYPE.to_string(),
fn get_content_type(&self) -> &'static str { types::PaymentsVoidType::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_url( fn get_url(
&self, &self,
_req: &types::PaymentsCancelRouterData, req: &types::PaymentsCancelRouterData,
_connectors: Connectors, connectors: Connectors,
) -> CustomResult<String, errors::ConnectorError> { ) -> CustomResult<String, errors::ConnectorError> {
Err(errors::ConnectorError::NotImplemented("checkout".to_string()).into()) Ok(format!(
"{}payments/{}/voids",
self.base_url(connectors),
&req.request.connector_transaction_id
))
} }
fn get_request_body( fn get_request_body(
&self, &self,
_req: &types::PaymentsCancelRouterData, req: &types::PaymentsCancelRouterData,
) -> CustomResult<Option<String>, errors::ConnectorError> { ) -> CustomResult<Option<String>, errors::ConnectorError> {
Err(errors::ConnectorError::NotImplemented("checkout".to_string()).into()) let checkout_req = utils::Encode::<checkout::PaymentVoidRequest>::convert_and_encode(req)
.change_context(errors::ConnectorError::RequestEncodingFailed)?;
Ok(Some(checkout_req))
} }
fn build_request( fn build_request(
&self, &self,
_req: &types::PaymentsCancelRouterData, req: &types::PaymentsCancelRouterData,
_connectors: Connectors, connectors: Connectors,
) -> CustomResult<Option<services::Request>, errors::ConnectorError> { ) -> CustomResult<Option<services::Request>, errors::ConnectorError> {
Err(errors::ConnectorError::NotImplemented("checkout".to_string()).into()) Ok(Some(
services::RequestBuilder::new()
.method(services::Method::Post)
.url(&types::PaymentsVoidType::get_url(self, req, connectors)?)
.headers(types::PaymentsVoidType::get_headers(self, req)?)
.body(types::PaymentsVoidType::get_request_body(self, req)?)
.build(),
))
} }
fn handle_response( fn handle_response(
&self, &self,
_data: &types::PaymentsCancelRouterData, data: &types::PaymentsCancelRouterData,
_res: Response, res: Response,
) -> CustomResult<types::PaymentsCancelRouterData, errors::ConnectorError> { ) -> CustomResult<types::PaymentsCancelRouterData, errors::ConnectorError> {
Err(errors::ConnectorError::NotImplemented("checkout".to_string()).into()) let mut response: checkout::PaymentVoidResponse = res
.response
.parse_struct("PaymentVoidResponse")
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
response.status = res.status_code;
logger::debug!(payments_create_response=?response);
types::RouterData::try_from(types::ResponseRouterData {
response,
data: data.clone(),
http_code: res.status_code,
})
.change_context(errors::ConnectorError::ResponseHandlingFailed)
} }
fn get_error_response( fn get_error_response(
&self, &self,
_res: Bytes, res: Bytes,
) -> CustomResult<ErrorResponse, errors::ConnectorError> { ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
Err(errors::ConnectorError::NotImplemented("checkout".to_string()).into()) let response: checkout::ErrorResponse = res
.parse_struct("ErrorResponse")
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
Ok(ErrorResponse {
code: response
.error_codes
.unwrap_or_else(|| vec![consts::NO_ERROR_CODE.to_string()])
//Considered all the codes here but have to look into the exact no.of codes
.join(" & "),
message: response
.error_type
.unwrap_or_else(|| consts::NO_ERROR_MESSAGE.to_string()),
reason: None,
})
} }
} }

View File

@ -46,6 +46,7 @@ pub struct PaymentsRequest {
pub three_ds: CheckoutThreeDS, pub three_ds: CheckoutThreeDS,
#[serde(flatten)] #[serde(flatten)]
pub return_url: ReturnUrl, pub return_url: ReturnUrl,
pub capture: bool,
} }
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
@ -100,6 +101,11 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for PaymentsRequest {
.map(|return_url| format!("{return_url}?status=failure")), .map(|return_url| format!("{return_url}?status=failure")),
}; };
let capture = matches!(
item.request.capture_method,
Some(enums::CaptureMethod::Automatic)
);
let source_var = Source::Card(CardSource { let source_var = Source::Card(CardSource {
source_type: Some("card".to_owned()), source_type: Some("card".to_owned()),
number: ccard.map(|x| x.card_number.clone()), number: ccard.map(|x| x.card_number.clone()),
@ -116,6 +122,7 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for PaymentsRequest {
processing_channel_id, processing_channel_id,
three_ds, three_ds,
return_url, return_url,
capture,
}) })
} }
} }
@ -131,12 +138,21 @@ pub enum CheckoutPaymentStatus {
Captured, Captured,
} }
impl From<CheckoutPaymentStatus> for enums::AttemptStatus { impl From<(CheckoutPaymentStatus, Option<enums::CaptureMethod>)> for enums::AttemptStatus {
fn from(item: CheckoutPaymentStatus) -> Self { fn from(item: (CheckoutPaymentStatus, Option<enums::CaptureMethod>)) -> Self {
match item { let status = item.0;
CheckoutPaymentStatus::Authorized | CheckoutPaymentStatus::Captured => { let capture_method = item.1;
enums::AttemptStatus::Charged match status {
CheckoutPaymentStatus::Authorized => {
if capture_method == Some(enums::CaptureMethod::Automatic)
|| capture_method.is_none()
{
enums::AttemptStatus::Charged
} else {
enums::AttemptStatus::Authorized
}
} }
CheckoutPaymentStatus::Captured => enums::AttemptStatus::Charged,
CheckoutPaymentStatus::Declined => enums::AttemptStatus::Failure, CheckoutPaymentStatus::Declined => enums::AttemptStatus::Failure,
CheckoutPaymentStatus::Pending => enums::AttemptStatus::Authorizing, CheckoutPaymentStatus::Pending => enums::AttemptStatus::Authorizing,
CheckoutPaymentStatus::CardVerified => enums::AttemptStatus::Pending, CheckoutPaymentStatus::CardVerified => enums::AttemptStatus::Pending,
@ -161,13 +177,12 @@ pub struct PaymentsResponse {
#[serde(rename = "_links")] #[serde(rename = "_links")]
links: Links, links: Links,
} }
impl<F, Req> impl TryFrom<types::PaymentsResponseRouterData<PaymentsResponse>>
TryFrom<types::ResponseRouterData<F, PaymentsResponse, Req, types::PaymentsResponseData>> for types::PaymentsAuthorizeRouterData
for types::RouterData<F, Req, types::PaymentsResponseData>
{ {
type Error = error_stack::Report<errors::ParsingError>; type Error = error_stack::Report<errors::ParsingError>;
fn try_from( fn try_from(
item: types::ResponseRouterData<F, PaymentsResponse, Req, types::PaymentsResponseData>, item: types::PaymentsResponseRouterData<PaymentsResponse>,
) -> Result<Self, Self::Error> { ) -> Result<Self, Self::Error> {
let redirection_url = item let redirection_url = item
.response .response
@ -188,7 +203,10 @@ impl<F, Req>
), ),
}); });
Ok(types::RouterData { Ok(types::RouterData {
status: enums::AttemptStatus::from(item.response.status), status: enums::AttemptStatus::from((
item.response.status,
item.data.request.capture_method,
)),
response: Ok(types::PaymentsResponseData { response: Ok(types::PaymentsResponseData {
resource_id: types::ResponseId::ConnectorTransactionId(item.response.id), resource_id: types::ResponseId::ConnectorTransactionId(item.response.id),
redirect: redirection_data.is_some(), redirect: redirection_data.is_some(),
@ -199,6 +217,76 @@ impl<F, Req>
} }
} }
impl TryFrom<types::PaymentsSyncResponseRouterData<PaymentsResponse>>
for types::PaymentsSyncRouterData
{
type Error = error_stack::Report<errors::ParsingError>;
fn try_from(
item: types::PaymentsSyncResponseRouterData<PaymentsResponse>,
) -> Result<Self, Self::Error> {
Ok(types::RouterData {
status: enums::AttemptStatus::from((item.response.status, None)),
response: Ok(types::PaymentsResponseData {
resource_id: types::ResponseId::ConnectorTransactionId(item.response.id),
//TODO: Add redirection details here
redirection_data: None,
redirect: false,
}),
..item.data
})
}
}
#[derive(Clone, Default, Debug, Eq, PartialEq, Serialize)]
pub struct PaymentVoidRequest {
reference: String,
}
#[derive(Clone, Default, Debug, Eq, PartialEq, Deserialize)]
pub struct PaymentVoidResponse {
#[serde(skip)]
pub(super) status: u16,
action_id: String,
reference: String,
}
impl From<&PaymentVoidResponse> for enums::AttemptStatus {
fn from(item: &PaymentVoidResponse) -> enums::AttemptStatus {
if item.status == 202 {
Self::Voided
} else {
Self::VoidFailed
}
}
}
impl TryFrom<types::PaymentsCancelResponseRouterData<PaymentVoidResponse>>
for types::PaymentsCancelRouterData
{
type Error = error_stack::Report<errors::ValidateError>;
fn try_from(
item: types::PaymentsCancelResponseRouterData<PaymentVoidResponse>,
) -> Result<Self, Self::Error> {
let response = &item.response;
Ok(types::RouterData {
response: Ok(types::PaymentsResponseData {
resource_id: types::ResponseId::ConnectorTransactionId(response.action_id.clone()),
redirect: false,
redirection_data: None,
}),
status: response.into(),
..item.data
})
}
}
impl TryFrom<&types::PaymentsCancelRouterData> for PaymentVoidRequest {
type Error = error_stack::Report<errors::ParsingError>;
fn try_from(item: &types::PaymentsCancelRouterData) -> Result<Self, Self::Error> {
Ok(Self {
reference: item.request.connector_transaction_id.clone(),
})
}
}
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct RefundRequest { pub struct RefundRequest {
amount: Option<i32>, amount: Option<i32>,

View File

@ -216,7 +216,7 @@ where
_txn_id: &str, _txn_id: &str,
_payment_attempt: &storage::PaymentAttempt, _payment_attempt: &storage::PaymentAttempt,
_request: &Option<api::PaymentMethod>, _request: &Option<api::PaymentMethod>,
_token: Option<i32>, _token: &Option<String>,
) -> RouterResult<( ) -> RouterResult<(
BoxedOperation<'a, F, api::PaymentsSessionRequest>, BoxedOperation<'a, F, api::PaymentsSessionRequest>,
Option<api::PaymentMethod>, Option<api::PaymentMethod>,

View File

@ -31,6 +31,8 @@ pub type PaymentsResponseRouterData<R> =
ResponseRouterData<api::Authorize, R, PaymentsAuthorizeData, PaymentsResponseData>; ResponseRouterData<api::Authorize, R, PaymentsAuthorizeData, PaymentsResponseData>;
pub type PaymentsCancelResponseRouterData<R> = pub type PaymentsCancelResponseRouterData<R> =
ResponseRouterData<api::Void, R, PaymentsCancelData, PaymentsResponseData>; ResponseRouterData<api::Void, R, PaymentsCancelData, PaymentsResponseData>;
pub type PaymentsSyncResponseRouterData<R> =
ResponseRouterData<api::PSync, R, PaymentsSyncData, PaymentsResponseData>;
pub type RefundsResponseRouterData<F, R> = pub type RefundsResponseRouterData<F, R> =
ResponseRouterData<F, R, RefundsData, RefundsResponseData>; ResponseRouterData<F, R, RefundsData, RefundsResponseData>;