mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-11-01 11:06:50 +08:00
feat(connector): [Bluesnap] Add dispute webhooks support (#2053)
This commit is contained in:
@ -1,4 +1,4 @@
|
||||
use std::num::TryFromIntError;
|
||||
use std::num::{ParseFloatError, TryFromIntError};
|
||||
|
||||
use router_derive;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@ -342,6 +342,19 @@ impl Currency {
|
||||
Ok(amount)
|
||||
}
|
||||
|
||||
///Convert the higher decimal amount to its base absolute units
|
||||
pub fn to_currency_lower_unit(&self, amount: String) -> Result<String, ParseFloatError> {
|
||||
let amount_f64 = amount.parse::<f64>()?;
|
||||
let amount_string = if self.is_zero_decimal_currency() {
|
||||
amount_f64
|
||||
} else if self.is_three_decimal_currency() {
|
||||
amount_f64 * 1000.00
|
||||
} else {
|
||||
amount_f64 * 100.00
|
||||
};
|
||||
Ok(amount_string.to_string())
|
||||
}
|
||||
|
||||
/// Convert the amount to its base denomination based on Currency and check for zero decimal currency and return String
|
||||
/// Paypal Connector accepts Zero and Two decimal currency but not three decimal and it should be updated as required for 3 decimal currencies.
|
||||
/// Paypal Ref - https://developer.paypal.com/docs/reports/reference/paypal-supported-currencies/
|
||||
|
||||
@ -1042,7 +1042,7 @@ impl api::IncomingWebhook for Bluesnap {
|
||||
let webhook_body: bluesnap::BluesnapWebhookBody =
|
||||
serde_urlencoded::from_bytes(request.body)
|
||||
.into_report()
|
||||
.change_context(errors::ConnectorError::WebhookSignatureNotFound)?;
|
||||
.change_context(errors::ConnectorError::WebhookReferenceIdNotFound)?;
|
||||
Ok(api_models::webhooks::ObjectReferenceId::PaymentId(
|
||||
api_models::payments::PaymentIdType::PaymentAttemptId(
|
||||
webhook_body.merchant_transaction_id,
|
||||
@ -1058,18 +1058,31 @@ impl api::IncomingWebhook for Bluesnap {
|
||||
serde_urlencoded::from_bytes(request.body)
|
||||
.into_report()
|
||||
.change_context(errors::ConnectorError::WebhookEventTypeNotFound)?;
|
||||
api::IncomingWebhookEvent::try_from(details)
|
||||
}
|
||||
|
||||
Ok(match details.transaction_type {
|
||||
bluesnap::BluesnapWebhookEvents::Decline
|
||||
| bluesnap::BluesnapWebhookEvents::CcChargeFailed => {
|
||||
api::IncomingWebhookEvent::PaymentIntentFailure
|
||||
}
|
||||
bluesnap::BluesnapWebhookEvents::Charge => {
|
||||
api::IncomingWebhookEvent::PaymentIntentSuccess
|
||||
}
|
||||
bluesnap::BluesnapWebhookEvents::Unknown => {
|
||||
api::IncomingWebhookEvent::EventNotSupported
|
||||
}
|
||||
fn get_dispute_details(
|
||||
&self,
|
||||
request: &api::IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<api::disputes::DisputePayload, errors::ConnectorError> {
|
||||
let dispute_details: bluesnap::BluesnapDisputeWebhookBody =
|
||||
serde_urlencoded::from_bytes(request.body)
|
||||
.into_report()
|
||||
.change_context(errors::ConnectorError::WebhookBodyDecodingFailed)?;
|
||||
Ok(api::disputes::DisputePayload {
|
||||
amount: connector_utils::to_currency_lower_unit(
|
||||
dispute_details.invoice_charge_amount.abs().to_string(),
|
||||
dispute_details.currency,
|
||||
)?,
|
||||
currency: dispute_details.currency.to_string(),
|
||||
dispute_stage: api_models::enums::DisputeStage::Dispute,
|
||||
connector_dispute_id: dispute_details.reversal_ref_num,
|
||||
connector_reason: dispute_details.reversal_reason,
|
||||
connector_reason_code: None,
|
||||
challenge_required_by: None,
|
||||
connector_status: dispute_details.cb_status,
|
||||
created_at: None,
|
||||
updated_at: None,
|
||||
})
|
||||
}
|
||||
|
||||
@ -1092,8 +1105,18 @@ impl api::IncomingWebhook for Bluesnap {
|
||||
bluesnap::BluesnapTxnType::Capture,
|
||||
bluesnap::BluesnapProcessingStatus::Success,
|
||||
)),
|
||||
bluesnap::BluesnapWebhookEvents::Chargeback
|
||||
| bluesnap::BluesnapWebhookEvents::ChargebackStatusChanged => {
|
||||
// returning the complete incoming webhook body, It won't be consumed in dispute flow, so currently does not hold any significance
|
||||
let res_json =
|
||||
utils::Encode::<bluesnap::BluesnapWebhookObjectResource>::encode_to_value(
|
||||
&details,
|
||||
)
|
||||
.change_context(errors::ConnectorError::WebhookResourceObjectNotFound)?;
|
||||
return Ok(res_json);
|
||||
}
|
||||
bluesnap::BluesnapWebhookEvents::Unknown => {
|
||||
Err(errors::ConnectorError::WebhookEventTypeNotFound)
|
||||
Err(errors::ConnectorError::WebhookResourceObjectNotFound)
|
||||
}
|
||||
}?;
|
||||
|
||||
|
||||
@ -818,19 +818,76 @@ pub struct BluesnapWebhookBody {
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct BluesnapWebhookObjectEventType {
|
||||
pub transaction_type: BluesnapWebhookEvents,
|
||||
pub cb_status: Option<BluesnapChargebackStatus>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||
pub enum BluesnapChargebackStatus {
|
||||
#[serde(alias = "New")]
|
||||
New,
|
||||
#[serde(alias = "Working")]
|
||||
Working,
|
||||
#[serde(alias = "Closed")]
|
||||
Closed,
|
||||
#[serde(alias = "Completed_Lost")]
|
||||
CompletedLost,
|
||||
#[serde(alias = "Completed_Pending")]
|
||||
CompletedPending,
|
||||
#[serde(alias = "Completed_Won")]
|
||||
CompletedWon,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||
pub enum BluesnapWebhookEvents {
|
||||
Decline,
|
||||
CcChargeFailed,
|
||||
Charge,
|
||||
Chargeback,
|
||||
ChargebackStatusChanged,
|
||||
#[serde(other)]
|
||||
Unknown,
|
||||
}
|
||||
|
||||
impl TryFrom<BluesnapWebhookObjectEventType> for api::IncomingWebhookEvent {
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(details: BluesnapWebhookObjectEventType) -> Result<Self, Self::Error> {
|
||||
match details.transaction_type {
|
||||
BluesnapWebhookEvents::Decline | BluesnapWebhookEvents::CcChargeFailed => {
|
||||
Ok(Self::PaymentIntentFailure)
|
||||
}
|
||||
BluesnapWebhookEvents::Charge => Ok(Self::PaymentIntentSuccess),
|
||||
BluesnapWebhookEvents::Chargeback | BluesnapWebhookEvents::ChargebackStatusChanged => {
|
||||
match details
|
||||
.cb_status
|
||||
.ok_or(errors::ConnectorError::WebhookEventTypeNotFound)?
|
||||
{
|
||||
BluesnapChargebackStatus::New | BluesnapChargebackStatus::Working => {
|
||||
Ok(Self::DisputeOpened)
|
||||
}
|
||||
BluesnapChargebackStatus::Closed => Ok(Self::DisputeExpired),
|
||||
BluesnapChargebackStatus::CompletedLost => Ok(Self::DisputeLost),
|
||||
BluesnapChargebackStatus::CompletedPending => Ok(Self::DisputeChallenged),
|
||||
BluesnapChargebackStatus::CompletedWon => Ok(Self::DisputeWon),
|
||||
}
|
||||
}
|
||||
BluesnapWebhookEvents::Unknown => Ok(Self::EventNotSupported),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct BluesnapDisputeWebhookBody {
|
||||
pub invoice_charge_amount: f64,
|
||||
pub currency: diesel_models::enums::Currency,
|
||||
pub reversal_reason: Option<String>,
|
||||
pub reversal_ref_num: String,
|
||||
pub cb_status: String,
|
||||
}
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct BluesnapWebhookObjectResource {
|
||||
pub reference_number: String,
|
||||
pub transaction_type: BluesnapWebhookEvents,
|
||||
|
||||
@ -1000,6 +1000,16 @@ pub fn to_currency_base_unit(
|
||||
.change_context(errors::ConnectorError::RequestEncodingFailed)
|
||||
}
|
||||
|
||||
pub fn to_currency_lower_unit(
|
||||
amount: String,
|
||||
currency: diesel_models::enums::Currency,
|
||||
) -> Result<String, error_stack::Report<errors::ConnectorError>> {
|
||||
currency
|
||||
.to_currency_lower_unit(amount)
|
||||
.into_report()
|
||||
.change_context(errors::ConnectorError::ResponseHandlingFailed)
|
||||
}
|
||||
|
||||
pub fn construct_not_implemented_error_report(
|
||||
capture_method: enums::CaptureMethod,
|
||||
connector_name: &str,
|
||||
|
||||
Reference in New Issue
Block a user