Question for folks here. I notice that failed paym...
# support
t
Question for folks here. I notice that failed payments don't seem to store the actual error code from the gateway anywhere on them. In our system, we want some of the callsites to behave differently based on why the payment failed - declined card vs insufficient funds, etc. Is there any reason that
Spree::Payment::Processing#handle_response
doesn't store the
response.error_code
on the payment as
self.response_code
? Is doing so in a monkey patch going to set us up for some sort of nasty failure? (I could easily just add a new column, but I'm curious about the design thought behind the existing system)
w
I think that also can be handled by the specific gateway, which integration are you using?
t
We've got kind of a handrolled version of the Stripe gateway, for a lot of various reasons.
But I'm not sure I see how the gateway can handle it - the gateway seems to control whether or not the payment will end up being completed or not based on whether it returns a successful response. But from what I can see, it seems like the Payment object never stores any information about the failure cases except for in the log entries.
And for context, what we're trying to do is report a different error status to the API caller when
order.complete
fails with a "hard" error vs a "soft" error. With what I can see currently, there's no good information in the Payment object we can use to make that determination -
order.errors
contains relevant messages, but those aren't really suitable for status codes because they could change/etc.
t
So it does - but it puts that information in the log_entries, which sort of feels weird to me. It feels a little odd to reach into
payment.log_entries.last
to drive conditional logic. That's why I'm asking about
Payment.response_code
- it feels better to look at
order.payments.last.response_code
in order to determine how to respond to the client.
Does that all make sense? A log entry doesn't necessarily feel like an appropriate place to have this kind of data to me, which is what is giving me hesitation.
w
Oh, I think you're right and the info is only stored in the log entries.
If you own the gateway, could you perform your logic in the methods there?
t
I'm not quite sure I understand what you're asking - the problem isn't the gateway, the problem is higher up. We have code that presents an API to clients to complete checkout which calls
order.complete
. Based on what happens at that moment, we want to change the return value. Solidus seems to expect the gateway to return an
ActiveMerchant::Billing::Response
, which then is processed in
Payment::Processing
. If the response is not successful, processing raises a
Core::GatewayError
which is caught during
process_payments_with
.
So I suppose we could try to raise an error rather than returning a response, but it feels like that is fighting against Solidus - nothing will mark the payment as
failed
in that scenario, I think, for example.
w
Yeah, that looks like going against Solidus, although maybe you don't have other options. Also hacky, but maybe you could add another callback to the payments state machine and raise from there.
t
What I've done so far that seems to work pretty well is to add a monkey-patch to
Spree::Payment
to actually store the error code as the response code:
Copy code
def handle_response(response)
  super
rescue Spree::Core::GatewayError
  update!(response_code: response.error_code)
  raise
end
That lets us end up with a Payment which has a response that looks something like
state: failed, response_code: card_declined
in this particular case
w
I'm not familiar enough with the payment system, but that looks like the least possible obstrusive solution
t
That's what led me down this line of questioning, though - all of these solutions feel like I'm possibly fighting the framework, which sort of raised my spidey sense. 🙂 I really appreciate you working through some of these thoughts with me, @waiting_for_dev! It seems like I'm at least not wildly out of line here, in a space where none of the solutions really feel particularly great.
w
yeah, all that ActiveMerchant stuff is quite legacy and it'd be great to revisit it at some point
💯 1