mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-30 01:27:31 +08:00
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:
@ -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,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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>,
|
||||
|
||||
@ -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>,
|
||||
|
||||
@ -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>;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user