-
-
Notifications
You must be signed in to change notification settings - Fork 60
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
Detect refunds that have already been performed #262
Comments
Stripe sends a `charge.refunded` [1] event when a refund is performed. When that happens, we need to accordingly update our data by creating a {Spree::Refund} for the given payment. Notice that there are two ways of generating Solidus refunds: directly or through an RMA (Return Merchandise Authorization). A refund generated from Stripe can only integrate with the first kind, as there's no reliable way to hook into the second flow. Stripe and Solidus support partial refunds. The event's `refund_amount` field we receive contains the sum of all the performed refunds on the payment, so we need to deduce the previously refunded amount before generating the new one. Stripe refunds are required to be associated with a `Spree::RefundReason`, so we're seeding a record with a generic `Stripe refund` reason name. The name to fetch can be configured through the `#default_refund_reason_name` preference. As noted in #262, we're not checking yet if an incoming refund has already been processed in the past. [1] - https://stripe.com/docs/api/events/types#event_types-charge.refunded
Stripe sends a `charge.refunded` [1] event when a refund is performed. When that happens, we need to accordingly update our data by creating a `Spree::Refund` for the given payment. Notice that there are two ways of generating Solidus refunds: directly or through an RMA (Return Merchandise Authorization). A refund generated from Stripe can only integrate with the first kind, as there's no reliable way to hook into the second flow. Stripe and Solidus support partial refunds. The event's `refund_amount` field we receive contains the sum of all the performed refunds on the payment, so we need to deduce the previously refunded amount before generating the new one. Stripe refunds are required to be associated with a `Spree::RefundReason`, so we're seeding a record with a generic `Stripe refund` reason name. The name to fetch can be configured through the `#default_refund_reason_name` preference. As noted in #262, we're not checking yet if an incoming refund has already been processed in the past. [1] - https://stripe.com/docs/api/events/types#event_types-charge.refunded
Stripe sends a `charge.refunded` [1] event when a refund is performed. When that happens, we need to accordingly update our data by creating a `Spree::Refund` for the given payment. Notice that there are two ways of generating Solidus refunds: directly or through an RMA (Return Merchandise Authorization). A refund generated from Stripe can only integrate with the first kind, as there's no reliable way to hook into the second flow. Stripe and Solidus support partial refunds. The event's `refund_amount` field we receive contains the sum of all the performed refunds on the payment, so we need to deduce the previously refunded amount before generating the new one. Stripe refunds are required to be associated with a `Spree::RefundReason`, so we're seeding a record with a generic `Stripe refund` reason name. The name to fetch can be configured through the `#default_refund_reason_name` preference. As noted in #262, we're not checking yet if an incoming refund has already been processed in the past. [1] - https://stripe.com/docs/api/events/types#event_types-charge.refunded Closes #180
Stripe sends a `charge.refunded` [1] event when a refund is performed. When that happens, we need to accordingly update our data by creating a `Spree::Refund` for the given payment. Notice that there are two ways of generating Solidus refunds: directly or through an RMA (Return Merchandise Authorization). A refund generated from Stripe can only integrate with the first kind, as there's no reliable way to hook into the second flow. Stripe and Solidus support partial refunds. The event's `refund_amount` field we receive contains the sum of all the performed refunds on the payment, so we need to deduce the previously refunded amount before generating the new one. Stripe refunds are required to be associated with a `Spree::RefundReason`, so we're seeding a record with a generic `Stripe refund` reason name. The name to fetch can be configured through the `#default_refund_reason_name` preference. As noted in #262, we're not checking yet if an incoming refund has already been processed in the past. [1] - https://stripe.com/docs/api/events/types#event_types-charge.refunded Closes #180
Stripe sends a `charge.refunded` [1] event when a refund is performed. When that happens, we need to accordingly update our data by creating a `Spree::Refund` for the given payment. Notice that there are two ways of generating Solidus refunds: directly or through an RMA (Return Merchandise Authorization). A refund generated from Stripe can only integrate with the first kind, as there's no reliable way to hook into the second flow. Stripe and Solidus support partial refunds. The event's `refund_amount` field we receive contains the sum of all the performed refunds on the payment, so we need to deduce the previously refunded amount before generating the new one. Stripe refunds are required to be associated with a `Spree::RefundReason`, so we're seeding a record with a generic `Stripe refund` reason name. The name to fetch can be configured through the `#default_refund_reason_name` preference. As noted in #262, we're not checking yet if an incoming refund has already been processed in the past. [1] - https://stripe.com/docs/api/events/types#event_types-charge.refunded Closes #180
Stripe sends a `charge.refunded` [1] event when a refund is performed. When that happens, we need to accordingly update our data by creating a `Spree::Refund` for the given payment. Notice that there are two ways of generating Solidus refunds: directly or through an RMA (Return Merchandise Authorization). A refund generated from Stripe can only integrate with the first kind, as there's no reliable way to hook into the second flow. Stripe and Solidus support partial refunds. The event's `refund_amount` field we receive contains the sum of all the performed refunds on the payment, so we need to deduce the previously refunded amount before generating the new one. Stripe refunds are required to be associated with a `Spree::RefundReason`, so we're seeding a record with a generic `Stripe refund` reason name. The name to fetch can be configured through the `#default_refund_reason_name` preference. As noted in #262, we're not checking yet if an incoming refund has already been processed in the past. [1] - https://stripe.com/docs/api/events/types#event_types-charge.refunded Closes #180
Stripe sends a `charge.refunded` [1] event when a refund is performed. When that happens, we need to accordingly update our data by creating a `Spree::Refund` for the given payment. Notice that there are two ways of generating Solidus refunds: directly or through an RMA (Return Merchandise Authorization). A refund generated from Stripe can only integrate with the first kind, as there's no reliable way to hook into the second flow. Stripe and Solidus support partial refunds. The event's `refund_amount` field we receive contains the sum of all the performed refunds on the payment, so we need to deduce the previously refunded amount before generating the new one. Stripe refunds are required to be associated with a `Spree::RefundReason`, so we're seeding a record with a generic `Stripe refund` reason name. The name to fetch can be configured through the `#default_refund_reason_name` preference. As noted in #262, we're not checking yet if an incoming refund has already been processed in the past. [1] - https://stripe.com/docs/api/events/types#event_types-charge.refunded Closes #180
Stripe sends a `charge.refunded` [1] event when a refund is performed. When that happens, we need to accordingly update our data by creating a `Spree::Refund` for the given payment. Notice that there are two ways of generating Solidus refunds: directly or through an RMA (Return Merchandise Authorization). A refund generated from Stripe can only integrate with the first kind, as there's no reliable way to hook into the second flow. Stripe and Solidus support partial refunds. The event's `refund_amount` field we receive contains the sum of all the performed refunds on the payment, so we need to deduce the previously refunded amount before generating the new one. Stripe refunds are required to be associated with a `Spree::RefundReason`, so we're seeding a record with a generic `Stripe refund` reason name. The name to fetch can be configured through the `#default_refund_reason_name` preference. As noted in #262, we're not checking yet if an incoming refund has already been processed in the past. [1] - https://stripe.com/docs/api/events/types#event_types-charge.refunded Closes #180
Stripe sends a `charge.refunded` [1] event when a refund is performed. When that happens, we need to accordingly update our data by creating a `Spree::Refund` for the given payment. Notice that there are two ways of generating Solidus refunds: directly or through an RMA (Return Merchandise Authorization). A refund generated from Stripe can only integrate with the first kind, as there's no reliable way to hook into the second flow. Stripe and Solidus support partial refunds. The event's `refund_amount` field we receive contains the sum of all the performed refunds on the payment, so we need to deduce the previously refunded amount before generating the new one. Stripe refunds are required to be associated with a `Spree::RefundReason`, so we're seeding a record with a generic `Stripe refund` reason name. The name to fetch can be configured through the `#default_refund_reason_name` preference. As noted in #262, we're not checking yet if an incoming refund has already been processed in the past. [1] - https://stripe.com/docs/api/events/types#event_types-charge.refunded Closes #180
We can probably explore saving the id of the Stripe refund on the Solidus Refund in the |
That could work. We'd need to save it both when we create a refund from Solidus admin and when we create it from an incoming webhook. |
The solution is trickier than expected. The problem is that Stripe doesn't add the created refund id on the event it sends when there's a refund ( The best we can do is use the charge id to fetch all the refunds associated with it ( Still, there's something else to consider. As we said in previous comments, a refund generated from Solidus will call the Stripe API and create a refund there, which in turn will publish the
|
@waiting-for-dev what if we combine the transaction_id with setting a "solidus id" in the stripe refund metadata?
This way if a webhook comes in with an already present metadata solidus id we can immediately match it to its counterpart, and if it's missing we'll know we need to create a Spree::Refund to match it. |
Thanks for your feedback, @elia. That's a great suggestion. For Stripe-generated refunds, there's, in fact, no need to update their metadata fields in any way. Just checking that their id is already in our database as a |
Stripe doesn't guarantee that a webhook event will be delivered only once, so we need to be prepared to handle duplicated incoming refund events. Just creating a Solidus copy whenever we receive a refund event is not enough. On top of that, Stripe won't identify the refund that triggered the event. Both the `charge.refunded` and `payment_intent.succeeded` (for partial captures, which generates a refund for the remaining amount under the hood) webhooks only give us information to retrieve the list of all refunds associated with a charge or payment intent. Because of this, we are now syncing all the refunds associated with a payment intent at every webhook event. We keep track of the refunds already present on Solidus by leveraging the `transaction_id` field on `Spree::Refund`, copying the Stripe refund id as value. On top of that, we need to account for refunds created from Solidus admin panel, which calls the Stripe API. In this case, we need to avoid syncing the refund back to Solidus on the subsequent webhook. We're marking those refunds with a metadata field on Stripe, so we can exclude them from the sync. Closes #262
Stripe doesn't guarantee that a webhook event will be delivered only once, so we need to be prepared to handle duplicated incoming refund events. Just creating a Solidus copy whenever we receive a refund event is not enough. On top of that, Stripe won't identify the refund that triggered the event. Both the `charge.refunded` and `payment_intent.succeeded` (for partial captures, which generates a refund for the remaining amount under the hood) webhooks only give us information to retrieve the list of all refunds associated with a charge or payment intent. Because of this, we are now syncing all the refunds associated with a payment intent at every webhook event. We keep track of the refunds already present on Solidus by leveraging the `transaction_id` field on `Spree::Refund`, copying the Stripe refund id as value. On top of that, we need to account for refunds created from Solidus admin panel, which calls the Stripe API. In this case, we need to avoid syncing the refund back to Solidus on the subsequent webhook. We're marking those refunds with a metadata field on Stripe, so we can exclude them from the sync. Closes #262
Stripe doesn't guarantee that a webhook event will be delivered only once, so we need to be prepared to handle duplicated incoming refund events. Just creating a Solidus copy whenever we receive a refund event is not enough. On top of that, Stripe won't identify the refund that triggered the event. Both the `charge.refunded` and `payment_intent.succeeded` (for partial captures, which generates a refund for the remaining amount under the hood) webhooks only give us information to retrieve the list of all refunds associated with a charge or payment intent. Because of this, we are now syncing all the refunds associated with a payment intent at every webhook event. We keep track of the refunds already present on Solidus by leveraging the `transaction_id` field on `Spree::Refund`, copying the Stripe refund id as value. On top of that, we need to account for refunds created from Solidus admin panel, which calls the Stripe API. In this case, we need to avoid syncing the refund back to Solidus on the subsequent webhook. We're marking those refunds with a metadata field on Stripe, so we can exclude them from the sync. Closes #262
On #180, we're handling the
charge.refunded
webhook event to generate aSpree::Refund
copy. However, we're not checking whether the incoming webhook has already been processed in the past.Although we're skipping the processing when the associated payment has already been fully refunded, it might be the case that we handle twice a partial amount refund. As noted in a comment from the referenced issue, we can't use the transaction ARN as that's something not always available at the moment the event is created.
There're two ways to handle that:
Another scenario for a duplicated refund would be one that has been initiated by Solidus, calling the Stripe API and getting the corresponding webhook event.
The text was updated successfully, but these errors were encountered: