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(
&self,
_req: &types::PaymentsCancelRouterData,
req: &types::PaymentsCancelRouterData,
) -> CustomResult<Vec<(String, String)>, errors::ConnectorError> {
Err(errors::ConnectorError::NotImplemented("checkout".to_string()).into())
}
fn get_content_type(&self) -> &'static str {
""
let mut header = vec![
(
headers::CONTENT_TYPE.to_string(),
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(
&self,
_req: &types::PaymentsCancelRouterData,
_connectors: Connectors,
req: &types::PaymentsCancelRouterData,
connectors: Connectors,
) -> 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(
&self,
_req: &types::PaymentsCancelRouterData,
req: &types::PaymentsCancelRouterData,
) -> 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(
&self,
_req: &types::PaymentsCancelRouterData,
_connectors: Connectors,
req: &types::PaymentsCancelRouterData,
connectors: Connectors,
) -> 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(
&self,
_data: &types::PaymentsCancelRouterData,
_res: Response,
data: &types::PaymentsCancelRouterData,
res: Response,
) -> 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(
&self,
_res: Bytes,
res: Bytes,
) -> 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,
#[serde(flatten)]
pub return_url: ReturnUrl,
pub capture: bool,
}
#[derive(Debug, Serialize)]
@ -100,6 +101,11 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for PaymentsRequest {
.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 {
source_type: Some("card".to_owned()),
number: ccard.map(|x| x.card_number.clone()),
@ -116,6 +122,7 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for PaymentsRequest {
processing_channel_id,
three_ds,
return_url,
capture,
})
}
}
@ -131,12 +138,21 @@ pub enum CheckoutPaymentStatus {
Captured,
}
impl From<CheckoutPaymentStatus> for enums::AttemptStatus {
fn from(item: CheckoutPaymentStatus) -> Self {
match item {
CheckoutPaymentStatus::Authorized | CheckoutPaymentStatus::Captured => {
enums::AttemptStatus::Charged
impl From<(CheckoutPaymentStatus, Option<enums::CaptureMethod>)> for enums::AttemptStatus {
fn from(item: (CheckoutPaymentStatus, Option<enums::CaptureMethod>)) -> Self {
let status = item.0;
let capture_method = item.1;
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::Pending => enums::AttemptStatus::Authorizing,
CheckoutPaymentStatus::CardVerified => enums::AttemptStatus::Pending,
@ -161,13 +177,12 @@ pub struct PaymentsResponse {
#[serde(rename = "_links")]
links: Links,
}
impl<F, Req>
TryFrom<types::ResponseRouterData<F, PaymentsResponse, Req, types::PaymentsResponseData>>
for types::RouterData<F, Req, types::PaymentsResponseData>
impl TryFrom<types::PaymentsResponseRouterData<PaymentsResponse>>
for types::PaymentsAuthorizeRouterData
{
type Error = error_stack::Report<errors::ParsingError>;
fn try_from(
item: types::ResponseRouterData<F, PaymentsResponse, Req, types::PaymentsResponseData>,
item: types::PaymentsResponseRouterData<PaymentsResponse>,
) -> Result<Self, Self::Error> {
let redirection_url = item
.response
@ -188,7 +203,10 @@ impl<F, Req>
),
});
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 {
resource_id: types::ResponseId::ConnectorTransactionId(item.response.id),
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)]
pub struct RefundRequest {
amount: Option<i32>,

View File

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

View File

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