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 router_derive;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@ -342,6 +342,19 @@ impl Currency {
|
|||||||
Ok(amount)
|
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
|
/// 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 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/
|
/// 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 =
|
let webhook_body: bluesnap::BluesnapWebhookBody =
|
||||||
serde_urlencoded::from_bytes(request.body)
|
serde_urlencoded::from_bytes(request.body)
|
||||||
.into_report()
|
.into_report()
|
||||||
.change_context(errors::ConnectorError::WebhookSignatureNotFound)?;
|
.change_context(errors::ConnectorError::WebhookReferenceIdNotFound)?;
|
||||||
Ok(api_models::webhooks::ObjectReferenceId::PaymentId(
|
Ok(api_models::webhooks::ObjectReferenceId::PaymentId(
|
||||||
api_models::payments::PaymentIdType::PaymentAttemptId(
|
api_models::payments::PaymentIdType::PaymentAttemptId(
|
||||||
webhook_body.merchant_transaction_id,
|
webhook_body.merchant_transaction_id,
|
||||||
@ -1058,18 +1058,31 @@ impl api::IncomingWebhook for Bluesnap {
|
|||||||
serde_urlencoded::from_bytes(request.body)
|
serde_urlencoded::from_bytes(request.body)
|
||||||
.into_report()
|
.into_report()
|
||||||
.change_context(errors::ConnectorError::WebhookEventTypeNotFound)?;
|
.change_context(errors::ConnectorError::WebhookEventTypeNotFound)?;
|
||||||
|
api::IncomingWebhookEvent::try_from(details)
|
||||||
|
}
|
||||||
|
|
||||||
Ok(match details.transaction_type {
|
fn get_dispute_details(
|
||||||
bluesnap::BluesnapWebhookEvents::Decline
|
&self,
|
||||||
| bluesnap::BluesnapWebhookEvents::CcChargeFailed => {
|
request: &api::IncomingWebhookRequestDetails<'_>,
|
||||||
api::IncomingWebhookEvent::PaymentIntentFailure
|
) -> CustomResult<api::disputes::DisputePayload, errors::ConnectorError> {
|
||||||
}
|
let dispute_details: bluesnap::BluesnapDisputeWebhookBody =
|
||||||
bluesnap::BluesnapWebhookEvents::Charge => {
|
serde_urlencoded::from_bytes(request.body)
|
||||||
api::IncomingWebhookEvent::PaymentIntentSuccess
|
.into_report()
|
||||||
}
|
.change_context(errors::ConnectorError::WebhookBodyDecodingFailed)?;
|
||||||
bluesnap::BluesnapWebhookEvents::Unknown => {
|
Ok(api::disputes::DisputePayload {
|
||||||
api::IncomingWebhookEvent::EventNotSupported
|
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::BluesnapTxnType::Capture,
|
||||||
bluesnap::BluesnapProcessingStatus::Success,
|
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 => {
|
bluesnap::BluesnapWebhookEvents::Unknown => {
|
||||||
Err(errors::ConnectorError::WebhookEventTypeNotFound)
|
Err(errors::ConnectorError::WebhookResourceObjectNotFound)
|
||||||
}
|
}
|
||||||
}?;
|
}?;
|
||||||
|
|
||||||
|
|||||||
@ -818,19 +818,76 @@ pub struct BluesnapWebhookBody {
|
|||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct BluesnapWebhookObjectEventType {
|
pub struct BluesnapWebhookObjectEventType {
|
||||||
pub transaction_type: BluesnapWebhookEvents,
|
pub transaction_type: BluesnapWebhookEvents,
|
||||||
|
pub cb_status: Option<BluesnapChargebackStatus>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
#[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 {
|
pub enum BluesnapWebhookEvents {
|
||||||
Decline,
|
Decline,
|
||||||
CcChargeFailed,
|
CcChargeFailed,
|
||||||
Charge,
|
Charge,
|
||||||
|
Chargeback,
|
||||||
|
ChargebackStatusChanged,
|
||||||
#[serde(other)]
|
#[serde(other)]
|
||||||
Unknown,
|
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)]
|
#[derive(Debug, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[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 struct BluesnapWebhookObjectResource {
|
||||||
pub reference_number: String,
|
pub reference_number: String,
|
||||||
pub transaction_type: BluesnapWebhookEvents,
|
pub transaction_type: BluesnapWebhookEvents,
|
||||||
|
|||||||
@ -1000,6 +1000,16 @@ pub fn to_currency_base_unit(
|
|||||||
.change_context(errors::ConnectorError::RequestEncodingFailed)
|
.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(
|
pub fn construct_not_implemented_error_report(
|
||||||
capture_method: enums::CaptureMethod,
|
capture_method: enums::CaptureMethod,
|
||||||
connector_name: &str,
|
connector_name: &str,
|
||||||
|
|||||||
Reference in New Issue
Block a user