Skip to content

Commit

Permalink
refactor(connector): [Iatapay] refactor authorize flow and fix paymen…
Browse files Browse the repository at this point in the history
…t status mapping (#2409)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
  • Loading branch information
prasunna09 and github-actions[bot] authored Jan 25, 2024
1 parent b45e4ca commit f0c7bb9
Show file tree
Hide file tree
Showing 2 changed files with 203 additions and 71 deletions.
95 changes: 45 additions & 50 deletions crates/router/src/connector/iatapay.rs
Original file line number Diff line number Diff line change
Expand Up @@ -369,15 +369,13 @@ impl ConnectorIntegration<api::PSync, types::PaymentsSyncData, types::PaymentsRe
req: &types::PaymentsSyncRouterData,
connectors: &settings::Connectors,
) -> CustomResult<String, errors::ConnectorError> {
let connector_id = req
.request
.connector_transaction_id
.get_connector_transaction_id()
.change_context(errors::ConnectorError::MissingConnectorTransactionID)?;
let auth: iatapay::IatapayAuthType =
iatapay::IatapayAuthType::try_from(&req.connector_auth_type)?;
let merchant_id = auth.merchant_id.peek();
Ok(format!(
"{}/payments/{}",
"{}/merchants/{merchant_id}/payments/{}",
self.base_url(connectors),
connector_id
req.connector_request_reference_id.clone()
))
}

Expand Down Expand Up @@ -634,56 +632,53 @@ impl api::IncomingWebhook for Iatapay {
&self,
request: &api::IncomingWebhookRequestDetails<'_>,
) -> CustomResult<api_models::webhooks::ObjectReferenceId, errors::ConnectorError> {
let notif: IatapayPaymentsResponse =
request
.body
.parse_struct("IatapayPaymentsResponse")
.change_context(errors::ConnectorError::WebhookReferenceIdNotFound)?;
if notif.iata_payment_id.is_some() {
Ok(api_models::webhooks::ObjectReferenceId::PaymentId(
api_models::payments::PaymentIdType::ConnectorTransactionId(
notif.iata_payment_id.unwrap_or_default(),
),
))
} else {
Ok(api_models::webhooks::ObjectReferenceId::RefundId(
api_models::webhooks::RefundIdType::ConnectorRefundId(
notif.iata_refund_id.unwrap_or_default(),
),
))
let notif: iatapay::IatapayWebhookResponse = request
.body
.parse_struct("IatapayWebhookResponse")
.change_context(errors::ConnectorError::WebhookReferenceIdNotFound)?;
match notif {
iatapay::IatapayWebhookResponse::IatapayPaymentWebhookBody(wh_body) => {
match wh_body.merchant_payment_id {
Some(merchant_payment_id) => {
Ok(api_models::webhooks::ObjectReferenceId::PaymentId(
api_models::payments::PaymentIdType::PaymentAttemptId(
merchant_payment_id,
),
))
}
None => Ok(api_models::webhooks::ObjectReferenceId::PaymentId(
api_models::payments::PaymentIdType::ConnectorTransactionId(
wh_body.iata_payment_id,
),
)),
}
}
iatapay::IatapayWebhookResponse::IatapayRefundWebhookBody(wh_body) => {
match wh_body.merchant_refund_id {
Some(merchant_refund_id) => {
Ok(api_models::webhooks::ObjectReferenceId::RefundId(
api_models::webhooks::RefundIdType::RefundId(merchant_refund_id),
))
}
None => Ok(api_models::webhooks::ObjectReferenceId::RefundId(
api_models::webhooks::RefundIdType::ConnectorRefundId(
wh_body.iata_refund_id,
),
)),
}
}
}
}

fn get_webhook_event_type(
&self,
request: &api::IncomingWebhookRequestDetails<'_>,
) -> CustomResult<api::IncomingWebhookEvent, errors::ConnectorError> {
let notif: IatapayPaymentsResponse =
request
.body
.parse_struct("IatapayPaymentsResponse")
.change_context(errors::ConnectorError::WebhookEventTypeNotFound)?;
match notif.status {
iatapay::IatapayPaymentStatus::Authorized => match notif.iata_payment_id.is_some() {
true => Ok(api::IncomingWebhookEvent::PaymentIntentSuccess),
false => Ok(api::IncomingWebhookEvent::RefundSuccess),
},
iatapay::IatapayPaymentStatus::Failed => match notif.iata_payment_id.is_some() {
true => Ok(api::IncomingWebhookEvent::PaymentIntentFailure),
false => Ok(api::IncomingWebhookEvent::RefundFailure),
},
iatapay::IatapayPaymentStatus::Unknown
| iatapay::IatapayPaymentStatus::Created
| iatapay::IatapayPaymentStatus::Initiated
| iatapay::IatapayPaymentStatus::Cleared
| iatapay::IatapayPaymentStatus::Settled
| iatapay::IatapayPaymentStatus::Tobeinvestigated
| iatapay::IatapayPaymentStatus::Blocked
| iatapay::IatapayPaymentStatus::Locked
| iatapay::IatapayPaymentStatus::UnexpectedSettled => {
Ok(api::IncomingWebhookEvent::EventNotSupported)
}
}
let notif: iatapay::IatapayWebhookResponse = request
.body
.parse_struct("IatapayWebhookResponse")
.change_context(errors::ConnectorError::WebhookEventTypeNotFound)?;
api::IncomingWebhookEvent::try_from(notif)
}

fn get_webhook_resource_object(
Expand Down
179 changes: 158 additions & 21 deletions crates/router/src/connector/iatapay/transformers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ use masking::{Secret, SwitchStrategy};
use serde::{Deserialize, Serialize};

use crate::{
connector::utils::{self, PaymentsAuthorizeRequestData, RefundsRequestData, RouterData},
connector::utils::{
self as connector_util, PaymentsAuthorizeRequestData, RefundsRequestData, RouterData,
},
consts,
core::errors,
services,
Expand Down Expand Up @@ -45,15 +47,15 @@ impl<T>
{
type Error = error_stack::Report<errors::ConnectorError>;
fn try_from(
(_currency_unit, _currency, _amount, item): (
(currency_unit, currency, amount, item): (
&types::api::CurrencyUnit,
types::storage::enums::Currency,
i64,
T,
),
) -> Result<Self, Self::Error> {
Ok(Self {
amount: utils::to_currency_base_unit_asf64(_amount, _currency)?,
amount: connector_util::get_amount_as_f64(currency_unit, amount, currency)?,
router_data: item,
})
}
Expand Down Expand Up @@ -136,7 +138,6 @@ impl
let payment_method = item.router_data.payment_method;
let country = match payment_method {
PaymentMethod::Upi => "IN".to_string(),

PaymentMethod::Card
| PaymentMethod::CardRedirect
| PaymentMethod::PayLater
Expand All @@ -154,7 +155,19 @@ impl
api::PaymentMethodData::Upi(upi_data) => upi_data.vpa_id.map(|id| PayerInfo {
token_id: id.switch_strategy(),
}),
_ => None,
api::PaymentMethodData::Card(_)
| api::PaymentMethodData::CardRedirect(_)
| api::PaymentMethodData::Wallet(_)
| api::PaymentMethodData::PayLater(_)
| api::PaymentMethodData::BankRedirect(_)
| api::PaymentMethodData::BankDebit(_)
| api::PaymentMethodData::BankTransfer(_)
| api::PaymentMethodData::Crypto(_)
| api::PaymentMethodData::MandatePayment
| api::PaymentMethodData::Reward
| api::PaymentMethodData::Voucher(_)
| api::PaymentMethodData::GiftCard(_)
| api::PaymentMethodData::CardToken(_) => None,
};
let payload = Self {
merchant_id: IatapayAuthType::try_from(&item.router_data.connector_auth_type)?
Expand Down Expand Up @@ -212,25 +225,21 @@ pub enum IatapayPaymentStatus {
Initiated,
Authorized,
Settled,
Tobeinvestigated,
Blocked,
Cleared,
Failed,
Locked,
#[serde(rename = "UNEXPECTED SETTLED")]
UnexpectedSettled,
#[serde(other)]
Unknown,
}

impl From<IatapayPaymentStatus> for enums::AttemptStatus {
fn from(item: IatapayPaymentStatus) -> Self {
match item {
IatapayPaymentStatus::Authorized | IatapayPaymentStatus::Settled => Self::Charged,
IatapayPaymentStatus::Authorized
| IatapayPaymentStatus::Settled
| IatapayPaymentStatus::Cleared => Self::Charged,
IatapayPaymentStatus::Failed | IatapayPaymentStatus::UnexpectedSettled => Self::Failure,
IatapayPaymentStatus::Created => Self::AuthenticationPending,
IatapayPaymentStatus::Initiated => Self::Pending,
_ => Self::Voided,
}
}
}
Expand Down Expand Up @@ -276,7 +285,7 @@ fn get_iatpay_response(
errors::ConnectorError,
> {
let status = enums::AttemptStatus::from(response.status);
let error = if status == enums::AttemptStatus::Failure {
let error = if connector_util::is_payment_failure(status) {
Some(types::ErrorResponse {
code: response
.failure_code
Expand Down Expand Up @@ -433,11 +442,32 @@ impl TryFrom<types::RefundsResponseRouterData<api::Execute, RefundResponse>>
fn try_from(
item: types::RefundsResponseRouterData<api::Execute, RefundResponse>,
) -> Result<Self, Self::Error> {
Ok(Self {
response: Ok(types::RefundsResponseData {
let refund_status = enums::RefundStatus::from(item.response.status);
let response = if connector_util::is_refund_failure(refund_status) {
Err(types::ErrorResponse {
code: item
.response
.failure_code
.unwrap_or_else(|| consts::NO_ERROR_CODE.to_string()),
message: item
.response
.failure_details
.clone()
.unwrap_or_else(|| consts::NO_ERROR_MESSAGE.to_string()),
reason: item.response.failure_details,
status_code: item.http_code,
attempt_status: None,
connector_transaction_id: Some(item.response.iata_refund_id.clone()),
})
} else {
Ok(types::RefundsResponseData {
connector_refund_id: item.response.iata_refund_id.to_string(),
refund_status: enums::RefundStatus::from(item.response.status),
}),
refund_status,
})
};

Ok(Self {
response,
..item.data
})
}
Expand All @@ -450,11 +480,31 @@ impl TryFrom<types::RefundsResponseRouterData<api::RSync, RefundResponse>>
fn try_from(
item: types::RefundsResponseRouterData<api::RSync, RefundResponse>,
) -> Result<Self, Self::Error> {
Ok(Self {
response: Ok(types::RefundsResponseData {
let refund_status = enums::RefundStatus::from(item.response.status);
let response = if connector_util::is_refund_failure(refund_status) {
Err(types::ErrorResponse {
code: item
.response
.failure_code
.unwrap_or_else(|| consts::NO_ERROR_CODE.to_string()),
message: item
.response
.failure_details
.clone()
.unwrap_or_else(|| consts::NO_ERROR_MESSAGE.to_string()),
reason: item.response.failure_details,
status_code: item.http_code,
attempt_status: None,
connector_transaction_id: Some(item.response.iata_refund_id.clone()),
})
} else {
Ok(types::RefundsResponseData {
connector_refund_id: item.response.iata_refund_id.to_string(),
refund_status: enums::RefundStatus::from(item.response.status),
}),
refund_status,
})
};
Ok(Self {
response,
..item.data
})
}
Expand All @@ -473,3 +523,90 @@ pub struct IatapayAccessTokenErrorResponse {
pub error: String,
pub path: String,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct IatapayPaymentWebhookBody {
pub status: IatapayWebhookStatus,
pub iata_payment_id: String,
pub merchant_payment_id: Option<String>,
pub failure_code: Option<String>,
pub failure_details: Option<String>,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct IatapayRefundWebhookBody {
pub status: IatapayRefundWebhookStatus,
pub iata_refund_id: String,
pub merchant_refund_id: Option<String>,
pub failure_code: Option<String>,
pub failure_details: Option<String>,
}

#[derive(Debug, Serialize, Deserialize)]
pub enum IatapayWebhookResponse {
IatapayPaymentWebhookBody(IatapayPaymentWebhookBody),
IatapayRefundWebhookBody(IatapayRefundWebhookBody),
}

impl TryFrom<IatapayWebhookResponse> for api::IncomingWebhookEvent {
type Error = error_stack::Report<errors::ConnectorError>;
fn try_from(payload: IatapayWebhookResponse) -> CustomResult<Self, errors::ConnectorError> {
match payload {
IatapayWebhookResponse::IatapayPaymentWebhookBody(wh_body) => match wh_body.status {
IatapayWebhookStatus::Authorized | IatapayWebhookStatus::Settled => {
Ok(Self::PaymentIntentSuccess)
}
IatapayWebhookStatus::Initiated => Ok(Self::PaymentIntentProcessing),
IatapayWebhookStatus::Failed => Ok(Self::PaymentIntentFailure),
IatapayWebhookStatus::Created
| IatapayWebhookStatus::Cleared
| IatapayWebhookStatus::Tobeinvestigated
| IatapayWebhookStatus::Blocked
| IatapayWebhookStatus::UnexpectedSettled
| IatapayWebhookStatus::Unknown => Ok(Self::EventNotSupported),
},
IatapayWebhookResponse::IatapayRefundWebhookBody(wh_body) => match wh_body.status {
IatapayRefundWebhookStatus::Authorized | IatapayRefundWebhookStatus::Settled => {
Ok(Self::RefundSuccess)
}
IatapayRefundWebhookStatus::Failed => Ok(Self::RefundFailure),
IatapayRefundWebhookStatus::Created
| IatapayRefundWebhookStatus::Locked
| IatapayRefundWebhookStatus::Initiated
| IatapayRefundWebhookStatus::Unknown => Ok(Self::EventNotSupported),
},
}
}
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "UPPERCASE")]
pub enum IatapayWebhookStatus {
Created,
Initiated,
Authorized,
Settled,
Cleared,
Failed,
Tobeinvestigated,
Blocked,
#[serde(rename = "UNEXPECTED SETTLED")]
UnexpectedSettled,
#[serde(other)]
Unknown,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "UPPERCASE")]
pub enum IatapayRefundWebhookStatus {
Created,
Initiated,
Authorized,
Settled,
Failed,
Locked,
#[serde(other)]
Unknown,
}

0 comments on commit f0c7bb9

Please sign in to comment.