Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(core): add new payments webhook events #3212

Merged
merged 8 commits into from
Jan 10, 2024
14 changes: 13 additions & 1 deletion crates/api_models/src/webhooks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,18 @@ use crate::{disputes, enums as api_enums, mandates, payments, refunds};
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, Copy)]
#[serde(rename_all = "snake_case")]
pub enum IncomingWebhookEvent {
/// Authorization + Capture success
PaymentIntentFailure,
/// Authorization + Capture failure
PaymentIntentSuccess,
PaymentIntentProcessing,
PaymentIntentPartiallyFunded,
PaymentIntentCancelled,
PaymentIntentCancelFailure,
PaymentIntentAuthorizationSuccess,
PaymentIntentAuthorizationFailure,
PaymentIntentCaptureSuccess,
PaymentIntentCaptureFailure,
PaymentActionRequired,
EventNotSupported,
SourceChargeable,
Expand Down Expand Up @@ -86,7 +93,12 @@ impl From<IncomingWebhookEvent> for WebhookFlow {
| IncomingWebhookEvent::PaymentIntentProcessing
| IncomingWebhookEvent::PaymentActionRequired
| IncomingWebhookEvent::PaymentIntentPartiallyFunded
| IncomingWebhookEvent::PaymentIntentCancelled => Self::Payment,
| IncomingWebhookEvent::PaymentIntentCancelled
| IncomingWebhookEvent::PaymentIntentCancelFailure
| IncomingWebhookEvent::PaymentIntentAuthorizationSuccess
| IncomingWebhookEvent::PaymentIntentAuthorizationFailure
| IncomingWebhookEvent::PaymentIntentCaptureSuccess
| IncomingWebhookEvent::PaymentIntentCaptureFailure => Self::Payment,
IncomingWebhookEvent::EventNotSupported => Self::ReturnResponse,
IncomingWebhookEvent::RefundSuccess | IncomingWebhookEvent::RefundFailure => {
Self::Refund
Expand Down
4 changes: 4 additions & 0 deletions crates/common_enums/src/enums.rs
Original file line number Diff line number Diff line change
Expand Up @@ -921,10 +921,14 @@ impl Currency {
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum EventType {
/// Authorize + Capture success
PaymentSucceeded,
/// Authorize + Capture failed
PaymentFailed,
PaymentProcessing,
PaymentCancelled,
PaymentAuthorized,
PaymentCaptured,
ActionRequired,
RefundSucceeded,
RefundFailed,
Expand Down
7 changes: 7 additions & 0 deletions crates/router/src/compatibility/stripe/webhooks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,13 @@ fn get_stripe_event_type(event_type: api_models::enums::EventType) -> &'static s
api_models::enums::EventType::DisputeLost => "dispute.lost",
api_models::enums::EventType::MandateActive => "mandate.active",
api_models::enums::EventType::MandateRevoked => "mandate.revoked",

// as per this doc https://stripe.com/docs/api/events/types#event_types-payment_intent.amount_capturable_updated
api_models::enums::EventType::PaymentAuthorized => {
"payment_intent.amount_capturable_updated"
}
// stripe treats partially captured payments as succeeded.
api_models::enums::EventType::PaymentCaptured => "payment_intent.succeeded",
}
}

Expand Down
15 changes: 15 additions & 0 deletions crates/router/src/connector/nmi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -873,6 +873,21 @@ impl api::IncomingWebhook for Nmi {
reference_body.event_body.order_id,
),
),
nmi::NmiActionType::Auth => api_models::webhooks::ObjectReferenceId::PaymentId(
api_models::payments::PaymentIdType::PaymentAttemptId(
reference_body.event_body.order_id,
),
),
nmi::NmiActionType::Capture => api_models::webhooks::ObjectReferenceId::PaymentId(
api_models::payments::PaymentIdType::PaymentAttemptId(
reference_body.event_body.order_id,
),
),
nmi::NmiActionType::Void => api_models::webhooks::ObjectReferenceId::PaymentId(
api_models::payments::PaymentIdType::PaymentAttemptId(
reference_body.event_body.order_id,
),
),
nmi::NmiActionType::Refund => api_models::webhooks::ObjectReferenceId::RefundId(
api_models::webhooks::RefundIdType::RefundId(reference_body.event_body.order_id),
),
Expand Down
16 changes: 7 additions & 9 deletions crates/router/src/connector/nmi/transformers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1166,21 +1166,19 @@ pub enum NmiWebhookEventType {
impl ForeignFrom<NmiWebhookEventType> for webhooks::IncomingWebhookEvent {
fn foreign_from(status: NmiWebhookEventType) -> Self {
match status {
NmiWebhookEventType::SaleSuccess | NmiWebhookEventType::CaptureSuccess => {
Self::PaymentIntentSuccess
}
NmiWebhookEventType::SaleFailure | NmiWebhookEventType::CaptureFailure => {
Self::PaymentIntentFailure
}
NmiWebhookEventType::SaleSuccess => Self::PaymentIntentSuccess,
NmiWebhookEventType::SaleFailure => Self::PaymentIntentFailure,
NmiWebhookEventType::RefundSuccess => Self::RefundSuccess,
NmiWebhookEventType::RefundFailure => Self::RefundFailure,
NmiWebhookEventType::VoidSuccess => Self::PaymentIntentCancelled,
NmiWebhookEventType::AuthSuccess => Self::PaymentIntentAuthorizationSuccess,
NmiWebhookEventType::CaptureSuccess => Self::PaymentIntentCaptureSuccess,
NmiWebhookEventType::AuthFailure => Self::PaymentIntentAuthorizationFailure,
NmiWebhookEventType::CaptureFailure => Self::PaymentIntentCaptureFailure,
NmiWebhookEventType::VoidFailure => Self::PaymentIntentCancelFailure,
NmiWebhookEventType::SaleUnknown
| NmiWebhookEventType::RefundUnknown
| NmiWebhookEventType::AuthSuccess
| NmiWebhookEventType::AuthFailure
| NmiWebhookEventType::AuthUnknown
| NmiWebhookEventType::VoidFailure
| NmiWebhookEventType::VoidUnknown
| NmiWebhookEventType::CaptureUnknown => Self::EventNotSupported,
}
Expand Down
4 changes: 3 additions & 1 deletion crates/router/src/connector/stripe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1977,6 +1977,9 @@ impl api::IncomingWebhook for Stripe {
stripe::WebhookEventType::PaymentIntentCanceled => {
api::IncomingWebhookEvent::PaymentIntentCancelled
}
stripe::WebhookEventType::PaymentIntentAmountCapturableUpdated => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't PaymentIntentAuthorizationSuccess meant for Authorized payments?

But as per stripe docs PaymentIntentAmountCapturableUpdated "Occurs when a PaymentIntent has funds to be captured. Check the amount_capturable property on the PaymentIntent to determine the amount that can be captured. You may capture the PaymentIntent with an amount_to_capture value up to the specified amount"

Ref - https://stripe.com/docs/api/events/types

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

amount_capturable ideally will be updated only when authorization succeeds.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

api::IncomingWebhookEvent::PaymentIntentAuthorizationSuccess
}
stripe::WebhookEventType::ChargeSucceeded => {
if let Some(stripe::WebhookPaymentMethodDetails {
payment_method:
Expand Down Expand Up @@ -2033,7 +2036,6 @@ impl api::IncomingWebhook for Stripe {
| stripe::WebhookEventType::ChargeRefunded
| stripe::WebhookEventType::PaymentIntentCreated
| stripe::WebhookEventType::PaymentIntentProcessing
| stripe::WebhookEventType::PaymentIntentAmountCapturableUpdated
| stripe::WebhookEventType::SourceTransactionCreated => {
api::IncomingWebhookEvent::EventNotSupported
}
Expand Down
2 changes: 1 addition & 1 deletion crates/router/src/connector/stripe/transformers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3315,7 +3315,7 @@ pub enum WebhookEventType {
PaymentIntentProcessing,
#[serde(rename = "payment_intent.requires_action")]
PaymentIntentRequiresAction,
#[serde(rename = "amount_capturable_updated")]
#[serde(rename = "payment_intent.amount_capturable_updated")]
PaymentIntentAmountCapturableUpdated,
#[serde(rename = "source.chargeable")]
SourceChargeable,
Expand Down
12 changes: 8 additions & 4 deletions crates/router/src/types/transformers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -352,11 +352,15 @@ impl ForeignFrom<api_enums::IntentStatus> for Option<storage_enums::EventType> {
Some(storage_enums::EventType::ActionRequired)
}
api_enums::IntentStatus::Cancelled => Some(storage_enums::EventType::PaymentCancelled),
api_enums::IntentStatus::PartiallyCaptured
| api_enums::IntentStatus::PartiallyCapturedAndCapturable => {
Some(storage_enums::EventType::PaymentCaptured)
}
api_enums::IntentStatus::RequiresCapture => {
Some(storage_enums::EventType::PaymentAuthorized)
}
api_enums::IntentStatus::RequiresPaymentMethod
| api_enums::IntentStatus::RequiresConfirmation
| api_enums::IntentStatus::RequiresCapture
| api_enums::IntentStatus::PartiallyCaptured
| api_enums::IntentStatus::PartiallyCapturedAndCapturable => None,
| api_enums::IntentStatus::RequiresConfirmation => None,
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- This file should undo anything in `up.sql`
SELECT 1;
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
-- Your SQL goes here
ALTER TYPE "EventType" ADD VALUE IF NOT EXISTS 'payment_authorized';
ALTER TYPE "EventType" ADD VALUE IF NOT EXISTS 'payment_captured';
2 changes: 2 additions & 0 deletions openapi/openapi_spec.json
Original file line number Diff line number Diff line change
Expand Up @@ -5556,6 +5556,8 @@
"payment_failed",
"payment_processing",
"payment_cancelled",
"payment_authorized",
"payment_captured",
"action_required",
"refund_succeeded",
"refund_failed",
Expand Down
Loading