mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-11-01 11:06:50 +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(
|
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,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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>,
|
||||||
|
|||||||
@ -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>,
|
||||||
|
|||||||
@ -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>;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user