feat(connector): [Bluesnap] Add dispute webhooks support (#2053)

This commit is contained in:
SamraatBansal
2023-09-01 14:05:36 +05:30
committed by GitHub
parent 38ab6e54f1
commit f8410b5b2a
4 changed files with 117 additions and 14 deletions

View File

@ -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/

View File

@ -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)
}
}?;

View File

@ -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,

View File

@ -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,