State_machine prevents ActiveRecord from being persisted

Hello list,

I have a subscription model that has a upgrade event. This changes the
state
from free->paying and triggers a charge method in the subscription
model.
This charge method then creates a new instance of the payment model
(just in
memory, using .new), and triggers the pay event on it. The pay event
tries
to transition the payment from pending to paid and calls the do_pay
method
before the transition (before_transition).

Everything would be 100% if it weren’t for a small details. The payment
should be persisted even if the subscription transition from
free->paying
has failed. In the do_pay method, if the payment failed, I do
self.decline
(a FSM event that simply changes from pending->declined) and I have an
after_transition callback setup like: “after_transition any => any, do
{|p|
p.save }” which gets triggered. However, since I return false in the
do_pay
method to prevent the pendign->paid transition to complete (the payment
failed, remember?), state_machine then rollbacks the payment’s save.

What I did was to call:

self.class.connection.commit_db_transaction

after self.decline and before return false in the payment’s do_pay
method.
This works, but is kind of ugly.

If anyone out there has any suggestions on how I could improve this
design
or maybe just tell me that this is ok to do, alleviating my worries
about
what seems to be a hack, I would be grateful!

Thanks,

Marcelo.

What type of payment system are you using? IPN?

If so, then once the payment reaches the payment gateway and the
customer
finishes paying, you check the status of the payment received and if
successful, you change the subscription based off the amount(s)
received.
You are supplying information but you aren’t supplying any actual
methods so
that we can review what you are doing. I use an IPN system for one of
my
sites and do the above. I’d love to help you out but without seeing
code
and how things are actually working with your controllers/models, I
can’t.

Sincerely,

Joel D.
Website Bio: http://jdezenzio.com/
Rails Production Sites: http://ncaastatpages.com

Hi Joel, thanks for the reply.

The system is working well. My problem is with the state_machine gem. If
the
payment fails, I can’t really get it saved without doing the hack I
mentioned, becase the state_machine implementation rollback any db
transaction when you return false from an event, however, I have to
return
false to tell the subscription object that the payment has failed,
hence,
the subscription upgrade fails, and state_machine rollback.

It has been solved (with that hack) but I just wanted to know if there
was
a more elegant non-hackish way to do that with state_machine.

Regards,

Marcelo.

It sounds like you’re in a tangle between FSM states and ActiveRecord
states. You might need to add a payment_queued state that forces an AR
save before transitioning to do_pay. You could then add some logic that
fires a loop back to through do_pay (retry n times after increasing
timeout) before failing.

Hello,

I also has this problem, I need to create an AuditTrail instance in db
even
if the transition is failed, but if the transition failed, the
transaction
is rollback, and the failed AuditTrail instance is not created in
database.