feat(connector): add dispute webhooks for Stripe (#918)

This commit is contained in:
Shankar Singh C
2023-05-03 02:34:02 +05:30
committed by GitHub
parent c31b4b41c2
commit 0df2244794
14 changed files with 352 additions and 106 deletions

View File

@ -31,11 +31,14 @@ pub struct DisputeResponse {
/// Reason code of dispute sent by connector
pub connector_reason_code: Option<String>,
/// Evidence deadline of dispute sent by connector
pub challenge_required_by: Option<String>,
#[serde(with = "common_utils::custom_serde::iso8601::option")]
pub challenge_required_by: Option<PrimitiveDateTime>,
/// Dispute created time sent by connector
pub created_at: Option<String>,
#[serde(with = "common_utils::custom_serde::iso8601::option")]
pub created_at: Option<PrimitiveDateTime>,
/// Dispute updated time sent by connector
pub updated_at: Option<String>,
#[serde(with = "common_utils::custom_serde::iso8601::option")]
pub updated_at: Option<PrimitiveDateTime>,
/// Time at which dispute is received
pub received_at: String,
}

View File

@ -880,6 +880,7 @@ impl From<AttemptStatus> for IntentStatus {
frunk::LabelledGeneric,
ToSchema,
)]
#[serde(rename_all = "snake_case")]
pub enum DisputeStage {
PreDispute,
#[default]
@ -901,6 +902,7 @@ pub enum DisputeStage {
frunk::LabelledGeneric,
ToSchema,
)]
#[serde(rename_all = "snake_case")]
pub enum DisputeStatus {
#[default]
DisputeOpened,

View File

@ -86,6 +86,74 @@ pub mod iso8601 {
}
}
/// Use the UNIX timestamp when serializing and deserializing an
/// [`PrimitiveDateTime`][PrimitiveDateTime].
///
/// [PrimitiveDateTime]: ::time::PrimitiveDateTime
pub mod timestamp {
use serde::{Deserializer, Serialize, Serializer};
use time::{serde::timestamp, PrimitiveDateTime, UtcOffset};
/// Serialize a [`PrimitiveDateTime`] using UNIX timestamp.
pub fn serialize<S>(date_time: &PrimitiveDateTime, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
date_time
.assume_utc()
.unix_timestamp()
.serialize(serializer)
}
/// Deserialize an [`PrimitiveDateTime`] from UNIX timestamp.
pub fn deserialize<'a, D>(deserializer: D) -> Result<PrimitiveDateTime, D::Error>
where
D: Deserializer<'a>,
{
timestamp::deserialize(deserializer).map(|offset_date_time| {
let utc_date_time = offset_date_time.to_offset(UtcOffset::UTC);
PrimitiveDateTime::new(utc_date_time.date(), utc_date_time.time())
})
}
/// Use the UNIX timestamp when serializing and deserializing an
/// [`Option<PrimitiveDateTime>`][PrimitiveDateTime].
///
/// [PrimitiveDateTime]: ::time::PrimitiveDateTime
pub mod option {
use serde::Serialize;
use super::*;
/// Serialize an [`Option<PrimitiveDateTime>`] from UNIX timestamp.
pub fn serialize<S>(
date_time: &Option<PrimitiveDateTime>,
serializer: S,
) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
date_time
.map(|date_time| date_time.assume_utc().unix_timestamp())
.serialize(serializer)
}
/// Deserialize an [`Option<PrimitiveDateTime>`] from UNIX timestamp.
pub fn deserialize<'a, D>(deserializer: D) -> Result<Option<PrimitiveDateTime>, D::Error>
where
D: Deserializer<'a>,
{
timestamp::option::deserialize(deserializer).map(|option_offset_date_time| {
option_offset_date_time.map(|offset_date_time| {
let utc_date_time = offset_date_time.to_offset(UtcOffset::UTC);
PrimitiveDateTime::new(utc_date_time.date(), utc_date_time.time())
})
})
}
}
}
/// <https://github.com/serde-rs/serde/issues/994#issuecomment-316895860>
pub mod json_string {

View File

@ -792,7 +792,7 @@ impl api::IncomingWebhook for Adyen {
connector_reason_code: notif.additional_data.chargeback_reason_code,
challenge_required_by: notif.additional_data.defense_period_ends_at,
connector_status: notif.event_code.to_string(),
created_at: notif.event_date.clone(),
created_at: notif.event_date,
updated_at: notif.event_date,
})
}

View File

@ -2,6 +2,7 @@ use api_models::{enums::DisputeStage, webhooks::IncomingWebhookEvent};
use masking::PeekInterface;
use reqwest::Url;
use serde::{Deserialize, Serialize};
use time::PrimitiveDateTime;
use crate::{
connector::utils::PaymentsAuthorizeRequestData,
@ -1553,7 +1554,8 @@ pub struct AdyenAdditionalDataWH {
pub hmac_signature: String,
pub dispute_status: Option<DisputeStatus>,
pub chargeback_reason_code: Option<String>,
pub defense_period_ends_at: Option<String>,
#[serde(default, with = "common_utils::custom_serde::iso8601::option")]
pub defense_period_ends_at: Option<PrimitiveDateTime>,
}
#[derive(Debug, Deserialize)]
@ -1653,7 +1655,8 @@ pub struct AdyenNotificationRequestItemWH {
pub merchant_reference: String,
pub success: String,
pub reason: Option<String>,
pub event_date: Option<String>,
#[serde(default, with = "common_utils::custom_serde::iso8601::option")]
pub event_date: Option<PrimitiveDateTime>,
}
#[derive(Debug, Deserialize)]

View File

@ -1083,7 +1083,7 @@ impl api::IncomingWebhook for Checkout {
.parse_struct("CheckoutWebhookBody")
.change_context(errors::ConnectorError::WebhookReferenceIdNotFound)?;
if checkout::is_chargeback_event(&details.txn_type) {
if checkout::is_chargeback_event(&details.transaction_type) {
return Ok(api_models::webhooks::ObjectReferenceId::PaymentId(
api_models::payments::PaymentIdType::ConnectorTransactionId(
details
@ -1093,7 +1093,7 @@ impl api::IncomingWebhook for Checkout {
),
));
}
if checkout::is_refund_event(&details.txn_type) {
if checkout::is_refund_event(&details.transaction_type) {
return Ok(api_models::webhooks::ObjectReferenceId::RefundId(
api_models::webhooks::RefundIdType::ConnectorRefundId(
details
@ -1117,7 +1117,7 @@ impl api::IncomingWebhook for Checkout {
.parse_struct("CheckoutWebhookBody")
.change_context(errors::ConnectorError::WebhookEventTypeNotFound)?;
Ok(api::IncomingWebhookEvent::from(details.txn_type))
Ok(api::IncomingWebhookEvent::from(details.transaction_type))
}
fn get_webhook_resource_object(
@ -1143,12 +1143,14 @@ impl api::IncomingWebhook for Checkout {
Ok(api::disputes::DisputePayload {
amount: dispute_details.data.amount.to_string(),
currency: dispute_details.data.currency,
dispute_stage: api_models::enums::DisputeStage::from(dispute_details.txn_type.clone()),
dispute_stage: api_models::enums::DisputeStage::from(
dispute_details.transaction_type.clone(),
),
connector_dispute_id: dispute_details.data.id,
connector_reason: None,
connector_reason_code: dispute_details.data.reason_code,
challenge_required_by: dispute_details.data.evidence_required_by,
connector_status: dispute_details.txn_type.to_string(),
connector_status: dispute_details.transaction_type.to_string(),
created_at: dispute_details.created_on,
updated_at: dispute_details.data.date,
})

View File

@ -1,6 +1,7 @@
use common_utils::errors::CustomResult;
use error_stack::{IntoReport, ResultExt};
use serde::{Deserialize, Serialize};
use time::PrimitiveDateTime;
use url::Url;
use crate::{
@ -674,27 +675,27 @@ impl From<CheckoutRedirectResponseStatus> for enums::AttemptStatus {
}
}
pub fn is_refund_event(event_code: &CheckoutTxnType) -> bool {
pub fn is_refund_event(event_code: &CheckoutTransactionType) -> bool {
matches!(
event_code,
CheckoutTxnType::PaymentRefunded | CheckoutTxnType::PaymentRefundDeclined
CheckoutTransactionType::PaymentRefunded | CheckoutTransactionType::PaymentRefundDeclined
)
}
pub fn is_chargeback_event(event_code: &CheckoutTxnType) -> bool {
pub fn is_chargeback_event(event_code: &CheckoutTransactionType) -> bool {
matches!(
event_code,
CheckoutTxnType::DisputeReceived
| CheckoutTxnType::DisputeExpired
| CheckoutTxnType::DisputeAccepted
| CheckoutTxnType::DisputeCanceled
| CheckoutTxnType::DisputeEvidenceSubmitted
| CheckoutTxnType::DisputeEvidenceAcknowledgedByScheme
| CheckoutTxnType::DisputeEvidenceRequired
| CheckoutTxnType::DisputeArbitrationLost
| CheckoutTxnType::DisputeArbitrationWon
| CheckoutTxnType::DisputeWon
| CheckoutTxnType::DisputeLost
CheckoutTransactionType::DisputeReceived
| CheckoutTransactionType::DisputeExpired
| CheckoutTransactionType::DisputeAccepted
| CheckoutTransactionType::DisputeCanceled
| CheckoutTransactionType::DisputeEvidenceSubmitted
| CheckoutTransactionType::DisputeEvidenceAcknowledgedByScheme
| CheckoutTransactionType::DisputeEvidenceRequired
| CheckoutTransactionType::DisputeArbitrationLost
| CheckoutTransactionType::DisputeArbitrationWon
| CheckoutTransactionType::DisputeWon
| CheckoutTransactionType::DisputeLost
)
}
@ -705,20 +706,20 @@ pub struct CheckoutWebhookData {
pub action_id: Option<String>,
pub amount: i32,
pub currency: String,
pub evidence_required_by: Option<String>,
pub evidence_required_by: Option<PrimitiveDateTime>,
pub reason_code: Option<String>,
pub date: Option<String>,
pub date: Option<PrimitiveDateTime>,
}
#[derive(Debug, Deserialize)]
pub struct CheckoutWebhookBody {
#[serde(rename = "type")]
pub txn_type: CheckoutTxnType,
pub transaction_type: CheckoutTransactionType,
pub data: CheckoutWebhookData,
pub created_on: Option<String>,
pub created_on: Option<PrimitiveDateTime>,
}
#[derive(Debug, Deserialize, strum::Display, Clone)]
#[serde(rename_all = "snake_case")]
pub enum CheckoutTxnType {
pub enum CheckoutTransactionType {
PaymentApproved,
PaymentDeclined,
PaymentRefunded,
@ -736,37 +737,35 @@ pub enum CheckoutTxnType {
DisputeLost,
}
impl From<CheckoutTxnType> for api::IncomingWebhookEvent {
fn from(txn_type: CheckoutTxnType) -> Self {
match txn_type {
CheckoutTxnType::PaymentApproved => Self::PaymentIntentSuccess,
CheckoutTxnType::PaymentDeclined => Self::PaymentIntentSuccess,
CheckoutTxnType::PaymentRefunded => Self::RefundSuccess,
CheckoutTxnType::PaymentRefundDeclined => Self::RefundFailure,
CheckoutTxnType::DisputeReceived | CheckoutTxnType::DisputeEvidenceRequired => {
Self::DisputeOpened
}
CheckoutTxnType::DisputeExpired => Self::DisputeExpired,
CheckoutTxnType::DisputeAccepted => Self::DisputeAccepted,
CheckoutTxnType::DisputeCanceled => Self::DisputeCancelled,
CheckoutTxnType::DisputeEvidenceSubmitted
| CheckoutTxnType::DisputeEvidenceAcknowledgedByScheme => Self::DisputeChallenged,
CheckoutTxnType::DisputeWon | CheckoutTxnType::DisputeArbitrationWon => {
Self::DisputeWon
}
CheckoutTxnType::DisputeLost | CheckoutTxnType::DisputeArbitrationLost => {
Self::DisputeLost
impl From<CheckoutTransactionType> for api::IncomingWebhookEvent {
fn from(transaction_type: CheckoutTransactionType) -> Self {
match transaction_type {
CheckoutTransactionType::PaymentApproved => Self::PaymentIntentSuccess,
CheckoutTransactionType::PaymentDeclined => Self::PaymentIntentSuccess,
CheckoutTransactionType::PaymentRefunded => Self::RefundSuccess,
CheckoutTransactionType::PaymentRefundDeclined => Self::RefundFailure,
CheckoutTransactionType::DisputeReceived
| CheckoutTransactionType::DisputeEvidenceRequired => Self::DisputeOpened,
CheckoutTransactionType::DisputeExpired => Self::DisputeExpired,
CheckoutTransactionType::DisputeAccepted => Self::DisputeAccepted,
CheckoutTransactionType::DisputeCanceled => Self::DisputeCancelled,
CheckoutTransactionType::DisputeEvidenceSubmitted
| CheckoutTransactionType::DisputeEvidenceAcknowledgedByScheme => {
Self::DisputeChallenged
}
CheckoutTransactionType::DisputeWon
| CheckoutTransactionType::DisputeArbitrationWon => Self::DisputeWon,
CheckoutTransactionType::DisputeLost
| CheckoutTransactionType::DisputeArbitrationLost => Self::DisputeLost,
}
}
}
impl From<CheckoutTxnType> for api_models::enums::DisputeStage {
fn from(code: CheckoutTxnType) -> Self {
impl From<CheckoutTransactionType> for api_models::enums::DisputeStage {
fn from(code: CheckoutTransactionType) -> Self {
match code {
CheckoutTxnType::DisputeArbitrationLost | CheckoutTxnType::DisputeArbitrationWon => {
Self::PreArbitration
}
CheckoutTransactionType::DisputeArbitrationLost
| CheckoutTransactionType::DisputeArbitrationWon => Self::PreArbitration,
_ => Self::Dispute,
}
}

View File

@ -1284,28 +1284,58 @@ impl api::IncomingWebhook for Stripe {
&self,
request: &api::IncomingWebhookRequestDetails<'_>,
) -> CustomResult<api_models::webhooks::ObjectReferenceId, errors::ConnectorError> {
let details: stripe::StripeWebhookObjectId = request
let details: stripe::WebhookEvent = request
.body
.parse_struct("StripeWebhookObjectId")
.parse_struct("WebhookEvent")
.change_context(errors::ConnectorError::WebhookReferenceIdNotFound)?;
Ok(api_models::webhooks::ObjectReferenceId::PaymentId(
api_models::payments::PaymentIdType::ConnectorTransactionId(details.data.object.id),
))
Ok(match details.event_data.event_object.object {
stripe::WebhookEventObjectType::PaymentIntent => {
api_models::webhooks::ObjectReferenceId::PaymentId(
api_models::payments::PaymentIdType::ConnectorTransactionId(
details.event_data.event_object.id,
),
)
}
stripe::WebhookEventObjectType::Dispute => {
api_models::webhooks::ObjectReferenceId::PaymentId(
api_models::payments::PaymentIdType::ConnectorTransactionId(
details
.event_data
.event_object
.payment_intent
.ok_or(errors::ConnectorError::WebhookReferenceIdNotFound)?,
),
)
}
_ => Err(errors::ConnectorError::WebhookReferenceIdNotFound)?,
})
}
fn get_webhook_event_type(
&self,
request: &api::IncomingWebhookRequestDetails<'_>,
) -> CustomResult<api::IncomingWebhookEvent, errors::ConnectorError> {
let details: stripe::StripeWebhookObjectEventType = request
let details: stripe::WebhookEvent = request
.body
.parse_struct("StripeWebhookObjectEventType")
.change_context(errors::ConnectorError::WebhookEventTypeNotFound)?;
Ok(match details.event_type.as_str() {
"payment_intent.payment_failed" => api::IncomingWebhookEvent::PaymentIntentFailure,
"payment_intent.succeeded" => api::IncomingWebhookEvent::PaymentIntentSuccess,
.parse_struct("WebhookEvent")
.change_context(errors::ConnectorError::WebhookReferenceIdNotFound)?;
Ok(match details.event_type {
stripe::WebhookEventType::PaymentIntentFailed => {
api::IncomingWebhookEvent::PaymentIntentFailure
}
stripe::WebhookEventType::PaymentIntentSucceed => {
api::IncomingWebhookEvent::PaymentIntentSuccess
}
stripe::WebhookEventType::DisputeCreated => api::IncomingWebhookEvent::DisputeOpened,
stripe::WebhookEventType::DisputeClosed => api::IncomingWebhookEvent::DisputeCancelled,
stripe::WebhookEventType::DisputeUpdated => api::IncomingWebhookEvent::try_from(
details
.event_data
.event_object
.status
.ok_or(errors::ConnectorError::WebhookEventTypeNotFound)?,
)?,
_ => Err(errors::ConnectorError::WebhookEventTypeNotFound).into_report()?,
})
}
@ -1314,13 +1344,43 @@ impl api::IncomingWebhook for Stripe {
&self,
request: &api::IncomingWebhookRequestDetails<'_>,
) -> CustomResult<serde_json::Value, errors::ConnectorError> {
let details: stripe::StripeWebhookObjectResource = request
let details: stripe::WebhookEventObjectResource = request
.body
.parse_struct("StripeWebhookObjectResource")
.parse_struct("WebhookEventObjectResource")
.change_context(errors::ConnectorError::WebhookResourceObjectNotFound)?;
Ok(details.data.object)
}
fn get_dispute_details(
&self,
request: &api::IncomingWebhookRequestDetails<'_>,
) -> CustomResult<api::disputes::DisputePayload, errors::ConnectorError> {
let details: stripe::WebhookEvent = request
.body
.parse_struct("WebhookEvent")
.change_context(errors::ConnectorError::WebhookBodyDecodingFailed)?;
Ok(api::disputes::DisputePayload {
amount: details.event_data.event_object.amount.to_string(),
currency: details.event_data.event_object.currency,
dispute_stage: api_models::enums::DisputeStage::Dispute,
connector_dispute_id: details.event_data.event_object.id,
connector_reason: details.event_data.event_object.reason,
connector_reason_code: None,
challenge_required_by: details
.event_data
.event_object
.evidence_details
.map(|payload| payload.due_by),
connector_status: details
.event_data
.event_object
.status
.ok_or(errors::ConnectorError::WebhookResourceObjectNotFound)?
.to_string(),
created_at: Some(details.event_data.event_object.created),
updated_at: None,
})
}
}
impl services::ConnectorRedirectResponse for Stripe {

View File

@ -4,6 +4,7 @@ use common_utils::{errors::CustomResult, pii};
use error_stack::{IntoReport, ResultExt};
use masking::{ExposeInterface, ExposeOptionInterface, Secret};
use serde::{Deserialize, Serialize};
use time::PrimitiveDateTime;
use url::Url;
use uuid::Uuid;
@ -357,6 +358,20 @@ pub enum StripeBankNames {
VanLanschot,
}
impl TryFrom<WebhookEventStatus> for api_models::webhooks::IncomingWebhookEvent {
type Error = errors::ConnectorError;
fn try_from(value: WebhookEventStatus) -> Result<Self, Self::Error> {
Ok(match value {
WebhookEventStatus::WarningNeedsResponse => Self::DisputeOpened,
WebhookEventStatus::WarningClosed => Self::DisputeCancelled,
WebhookEventStatus::WarningUnderReview => Self::DisputeChallenged,
WebhookEventStatus::Won => Self::DisputeWon,
WebhookEventStatus::Lost => Self::DisputeLost,
_ => Err(errors::ConnectorError::WebhookEventTypeNotFound)?,
})
}
}
impl TryFrom<&api_models::enums::BankNames> for StripeBankNames {
type Error = errors::ConnectorError;
fn try_from(bank: &api_models::enums::BankNames) -> Result<Self, Self::Error> {
@ -1566,34 +1581,119 @@ impl<F, T>
// }
#[derive(Debug, Deserialize)]
pub struct StripeWebhookDataObjectId {
pub id: String,
}
#[derive(Debug, Deserialize)]
pub struct StripeWebhookDataId {
pub object: StripeWebhookDataObjectId,
}
#[derive(Debug, Deserialize)]
pub struct StripeWebhookDataResource {
pub struct WebhookEventDataResource {
pub object: serde_json::Value,
}
#[derive(Debug, Deserialize)]
pub struct StripeWebhookObjectResource {
pub data: StripeWebhookDataResource,
pub struct WebhookEventObjectResource {
pub data: WebhookEventDataResource,
}
#[derive(Debug, Deserialize)]
pub struct StripeWebhookObjectEventType {
pub struct WebhookEvent {
#[serde(rename = "type")]
pub event_type: String,
pub event_type: WebhookEventType,
#[serde(rename = "data")]
pub event_data: WebhookEventData,
}
#[derive(Debug, Deserialize)]
pub struct StripeWebhookObjectId {
pub data: StripeWebhookDataId,
pub struct WebhookEventData {
#[serde(rename = "object")]
pub event_object: WebhookEventObjectData,
}
#[derive(Debug, Deserialize)]
pub struct WebhookEventObjectData {
pub id: String,
pub object: WebhookEventObjectType,
pub amount: i32,
pub currency: String,
pub payment_intent: Option<String>,
pub reason: Option<String>,
#[serde(with = "common_utils::custom_serde::timestamp")]
pub created: PrimitiveDateTime,
pub evidence_details: Option<EvidenceDetails>,
pub status: Option<WebhookEventStatus>,
}
#[derive(Debug, Deserialize, strum::Display)]
#[serde(rename_all = "snake_case")]
pub enum WebhookEventObjectType {
PaymentIntent,
Dispute,
Charge,
}
#[derive(Debug, Deserialize)]
pub enum WebhookEventType {
#[serde(rename = "payment_intent.payment_failed")]
PaymentIntentFailed,
#[serde(rename = "payment_intent.succeeded")]
PaymentIntentSucceed,
#[serde(rename = "charge.dispute.captured")]
ChargeDisputeCaptured,
#[serde(rename = "charge.dispute.created")]
DisputeCreated,
#[serde(rename = "charge.dispute.closed")]
DisputeClosed,
#[serde(rename = "charge.dispute.updated")]
DisputeUpdated,
#[serde(rename = "charge.dispute.funds_reinstated")]
ChargeDisputeFundsReinstated,
#[serde(rename = "charge.dispute.funds_withdrawn")]
ChargeDisputeFundsWithdrawn,
#[serde(rename = "charge.expired")]
ChargeExpired,
#[serde(rename = "charge.failed")]
ChargeFailed,
#[serde(rename = "charge.pending")]
ChargePending,
#[serde(rename = "charge.captured")]
ChargeCaptured,
#[serde(rename = "charge.succeeded")]
ChargeSucceeded,
#[serde(rename = "charge.updated")]
ChargeUpdated,
#[serde(rename = "charge.refunded")]
ChanrgeRefunded,
#[serde(rename = "payment_intent.canceled")]
PaymentIntentCanceled,
#[serde(rename = "payment_intent.created")]
PaymentIntentCreated,
#[serde(rename = "payment_intent.processing")]
PaymentIntentProcessing,
#[serde(rename = "payment_intent.requires_action")]
PaymentIntentRequiresAction,
#[serde(rename = "amount_capturable_updated")]
PaymentIntentAmountCapturableUpdated,
}
#[derive(Debug, Serialize, strum::Display, Deserialize, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum WebhookEventStatus {
WarningNeedsResponse,
WarningClosed,
WarningUnderReview,
Won,
Lost,
NeedsResponse,
UnderReview,
ChargeRefunded,
Succeeded,
RequiresPaymentMethod,
RequiresConfirmation,
RequiresAction,
Processing,
RequiresCapture,
Canceled,
}
#[derive(Debug, Deserialize, PartialEq)]
pub struct EvidenceDetails {
#[serde(with = "common_utils::custom_serde::timestamp")]
pub due_by: PrimitiveDateTime,
}
impl

View File

@ -1,4 +1,5 @@
use masking::{Deserialize, Serialize};
use time::PrimitiveDateTime;
use crate::{services, types};
@ -7,7 +8,7 @@ pub struct DisputeId {
pub dispute_id: String,
}
#[derive(Default, Debug, Deserialize)]
#[derive(Default, Debug)]
pub struct DisputePayload {
pub amount: String,
pub currency: String,
@ -16,9 +17,9 @@ pub struct DisputePayload {
pub connector_dispute_id: String,
pub connector_reason: Option<String>,
pub connector_reason_code: Option<String>,
pub challenge_required_by: Option<String>,
pub created_at: Option<String>,
pub updated_at: Option<String>,
pub challenge_required_by: Option<PrimitiveDateTime>,
pub created_at: Option<PrimitiveDateTime>,
pub updated_at: Option<PrimitiveDateTime>,
}
#[derive(Debug, Clone)]

View File

@ -1,11 +1,11 @@
use common_utils::custom_serde;
use diesel::{AsChangeset, Identifiable, Insertable, Queryable};
use serde::{Deserialize, Serialize};
use serde::Serialize;
use time::PrimitiveDateTime;
use crate::{enums as storage_enums, schema::dispute};
#[derive(Clone, Debug, Deserialize, Insertable, Serialize, router_derive::DebugAsDisplay)]
#[derive(Clone, Debug, Insertable, Serialize, router_derive::DebugAsDisplay)]
#[diesel(table_name = dispute)]
#[serde(deny_unknown_fields)]
pub struct DisputeNew {
@ -21,13 +21,13 @@ pub struct DisputeNew {
pub connector_dispute_id: String,
pub connector_reason: Option<String>,
pub connector_reason_code: Option<String>,
pub challenge_required_by: Option<String>,
pub dispute_created_at: Option<String>,
pub updated_at: Option<String>,
pub challenge_required_by: Option<PrimitiveDateTime>,
pub dispute_created_at: Option<PrimitiveDateTime>,
pub updated_at: Option<PrimitiveDateTime>,
pub connector: String,
}
#[derive(Clone, Debug, Deserialize, Serialize, Identifiable, Queryable)]
#[derive(Clone, Debug, Serialize, Identifiable, Queryable)]
#[diesel(table_name = dispute)]
pub struct Dispute {
#[serde(skip_serializing)]
@ -44,9 +44,9 @@ pub struct Dispute {
pub connector_dispute_id: String,
pub connector_reason: Option<String>,
pub connector_reason_code: Option<String>,
pub challenge_required_by: Option<String>,
pub dispute_created_at: Option<String>,
pub updated_at: Option<String>,
pub challenge_required_by: Option<PrimitiveDateTime>,
pub dispute_created_at: Option<PrimitiveDateTime>,
pub updated_at: Option<PrimitiveDateTime>,
#[serde(with = "custom_serde::iso8601")]
pub created_at: PrimitiveDateTime,
#[serde(with = "custom_serde::iso8601")]
@ -62,8 +62,8 @@ pub enum DisputeUpdate {
connector_status: String,
connector_reason: Option<String>,
connector_reason_code: Option<String>,
challenge_required_by: Option<String>,
updated_at: Option<String>,
challenge_required_by: Option<PrimitiveDateTime>,
updated_at: Option<PrimitiveDateTime>,
},
StatusUpdate {
dispute_status: storage_enums::DisputeStatus,
@ -79,8 +79,8 @@ pub struct DisputeUpdateInternal {
connector_status: Option<String>,
connector_reason: Option<String>,
connector_reason_code: Option<String>,
challenge_required_by: Option<String>,
updated_at: Option<String>,
challenge_required_by: Option<PrimitiveDateTime>,
updated_at: Option<PrimitiveDateTime>,
modified_at: Option<PrimitiveDateTime>,
}

View File

@ -128,9 +128,9 @@ diesel::table! {
connector_dispute_id -> Varchar,
connector_reason -> Nullable<Varchar>,
connector_reason_code -> Nullable<Varchar>,
challenge_required_by -> Nullable<Varchar>,
dispute_created_at -> Nullable<Varchar>,
updated_at -> Nullable<Varchar>,
challenge_required_by -> Nullable<Timestamp>,
dispute_created_at -> Nullable<Timestamp>,
updated_at -> Nullable<Timestamp>,
created_at -> Timestamp,
modified_at -> Timestamp,
connector -> Varchar,

View File

@ -0,0 +1,4 @@
ALTER TABLE dispute
ALTER COLUMN challenge_required_by TYPE VARCHAR(255),
ALTER COLUMN dispute_created_at TYPE VARCHAR(255),
ALTER COLUMN updated_at TYPE VARCHAR(255)

View File

@ -0,0 +1,4 @@
ALTER TABLE dispute
ALTER COLUMN challenge_required_by TYPE TIMESTAMP USING dispute_created_at::TIMESTAMP,
ALTER COLUMN dispute_created_at TYPE TIMESTAMP USING dispute_created_at::TIMESTAMP,
ALTER COLUMN updated_at TYPE TIMESTAMP USING dispute_created_at::TIMESTAMP