Rails - Create separate table or bypass all model validation? - ruby-on-rails

I am currently using wepay with rails. Don't worry this post is nothing about wepay.
So when a customer wants to buy something from my site, he/she will be redirected to wepay.
Then after paying on wepay, wepay will redirect the user to /purchases/received
After X amount of time, Wepay will also do a post call to /purchases/callback to tell me that the payment has been captured (credit card processing is slow)
So my original plan is as follows:
For the Purchase model, have a field, wepay_id and wepay_confirmed.
When the user place an order on wepay, the redirection to /puchases/received will create a purchase instance and save in my db
When the callback is called look up by wepay_id and then set wepay_confirmed to true.
However, as I discovered that the X amount of time could be so fast that /purchases/callback is called before /purchases/received could create the object.
So now I have two options:
Allow /purchases/callback to create an empty Purchase instance with just the id and confirmed = true. As I was doing this, I realized that I no longer can validate my model in the traditional manner. This really bugs me.
Create a separate table called Wepay_Confirmed. Whenever callback is called, create an entry in wepay_confirmed. Map the presence of an (checkout_id) in this table to Purchase.confirmed attribute.
I am thinking of doing 2. How can I do this? Do I have to generate a scaffold for a specific model to map to Wepay_Confirmed?
If you have any other suggestions, please reply

I would try to keep your application the way it is because it does make sense however you should look into returning an error code to wepay and have them submit the request later after the record is created.
Just emailed the developers over at WePay and got this response:
Hi Devin,
We do have automatic IPN retries. Retries happen 5 minutes after the
initial try, if the retry doesn't work, we try 15 minutes later, and
then an hour later. However, right now they are only on empty 404
responses.
The best solution is to actually just ignore the IPN if he does not
have the record in his database. Our IPNs only tell an application to
look up the checkout details with the /checkout call. They do not have
any details of the checkout. Since he should be looking up the
/checkout status anyway when he creates the checkout object on his
end, he doesn't need the IPN to tell him to look up the status in this
case.
If that doesn't work for him he can also email me at api#wepay.com and
we may be able to work out a solution.
Andrew
So it looks like you can modify the flow of you application to ignore the IPN's without a record and check manually or you can respond with a 404 and they will retry at the above intervals.

As I mentioned in my comment, I would personally prefer to create the purchase record upon purchase, then send the user to the WePay site, then handle the return trip and callback as actions to be completed against that original purchase site.
For one, that matches the reality of the transaction more accurately. When a user makes a purchase from your site, it makes sense to me that it's something you should persist at that point.
The two elements of the WePay transaction (the return trip to your site and the charge confirmation callback) would all act on that original purchase record. This will also allow you to see how many people abandon the purchase process when they hit WePay, which could reveal issues in your user experience that might help to maximize conversions.

I created a gem called wepay-rails which handles all of this for you. Under the hood it creates the entry (WepayCheckoutRecord) before sending the payer off to wepay. It has an IPN listener built in that handles the updating of that record. In my personal rails app, I am using state machine on the WepayCheckoutRecord model to track the changes to the state and doing 'things' as the state changes on that record.
I hope that helps.
Adam -

If you take the 2nd approach, you dont need to scaffold it. You can just create a migration and use it inside one of your other 'scaffolds'. Scaffolds are really just a way to get started with a resource. I dont think your intent here is to have a fully-fledged resource. Unless it is then you can use it as a scaffold.

Related

Admin user approving SQL changes from a user

I've read into Cancan and Pundit (also Devise) for managing users in a Rails App. But I wanted to know if something was possible.
Basically, I want to have users change/add lines in a table (using SQLite at the moment, but will be moving to SQL in the future - call them entries). But before it gets added to the actual table, it gets sent to the admin for approval. Then the admin can just hit 'approve' and the statement gets run.
I'm just confused about how to hold the statement and then when approved, the statement runs. Any information would be appreciated.
By statement, do you mean that your users are actually writing the SQL themselves?
If not, I'd setup a second model identical to your first that acts like a queue of some sort that holds the proposed changes/additions. This way, you'll be able to compare the old and new statements if necessary, and, when approved, you'll be able to perform the create/update magic on your original model.
Hope this gives you some ideas!

Paypal Payments Standard not returning payment variables on iPad

I have just finished developing a website using Paypal Payments Standard, and all is working just fine in most computers, but paypal does not return any payment variables on iPad (and maybe other devices).
I created my own cart, and use the Buy Now functionality to pay for the entire order.
The Buy Now form sets the RETURN variable to the cart page, and the RM variable is set to 2, which should post the variables back to the cart page.
When the payment is complete, the cart page checks for posted payment variables and logs them into the database.
As said, this works perfectly on most computers.
On iPad, though, the user returns to the cart page, but no payment variables are posted. This is just the same when changing the RM variable to 1, which should send the variables as GET parameters.
You can see the code and working website at: http://unameit.ch/
This isn't exactly an answer to your problem, but would solve the issue and would be better for you anyway.
Using your return URL is not a good way to get data into your database because even with Auto-Return enabled there is no guarantee the user would make it back to your return URL. In such cases, that code would never run and the data would never make it into your database.
Instead, what you should use is IPN. IPN will be triggered regardless of whether or not the user makes it back to your site. It's very similar to PDT except that instead of POSTing data back to the return URL it POSTs it to a separate listener script apart from your checkout pages. It happens in real-time so the result would be the same as what you're trying to do now, but it would always work regardless of whether or not the user made it back to your site, and you wouldn't have to worry about issues like you're running into here with the iPad transactions.
I highly recommend you do it this way or you'll find that you're missing order data in your database even if you end up getting this particular problem resolved.
I have had the same issue with the manual callback and have been speaking to PayPal tech support. They have agreed that there is a bug with this working on a mobile/tablet devices. Basically, if you go to the mobile PayPal site to make the payment, you won't get any data POSTed back to your return URL:
They have told us to use the ExpressCheckoutAPI instead:
"Yes the ExpressCheckout works without any issues on all platforms.
As a mater of fact I found that the mobileWPS checkout is Wraped around the
ExpressCheckout and this is the reason why your data is chopped off.
Some of the data is lost in translation from WPS to EC."
Sorry that this isn't a answer but at least we know that PayPal know it's a bug.

Get Attendee Data w/ Website Workflow's 3rd Part Next Steps

I am working w/ the Event Brite API and I have a need that I am trying to figure out the best approach for. Right now, I have an event that people will be registering for. At the final step of the registration process, I need to ask them some questions that are specific to my event. Sadly, these questions are data-driven from my website, so I am unable to use the packaged surveys w/ Event Bright.
In a perfect world, I would use the basic flow detailed in the Website Workflow of the EB documentation, ending upon the "3rd Party Next Steps" step (redirect method).
http://developer.eventbrite.com/doc/workflows/
Upon landing on that page, I would like to be able to access the order data that we just created in order to update my database and to send emails to each person who purchased a seat. This email would contain the information needed to kick off the survey portion of my registration process.
Is this possible in the current API? Does the redirect post any data back to the 3rd party site? I saw a few SO posts that gave a few keywords that could be included in the redirect URL (is there a comprehensive list?). If so, is there a way to use that data to look up order information for that order only?
Right now, my only other alternative is to set up a polling service that would pull EB API data, check for new values, and then kick off the process on intervals. This would be pretty noisy for all parties involved, create delay for my attendees, and I would like to avoid it if possible. Thoughts?
Thanks!
Here are the full set of parameters which we support after an attendee places an order:
http://yoursite.com/?eid=$event_id&attid=$attendee_id&oid=$order_id
It's possible that order_id and attendee_id would not be a numeric value, in which case it would return a value of "unknown." You'll always have the event_id though.
If you want to get order-specific data after redirecting an attendee to your site, you can using the event_list_attendees method, along with the modified_after parameter. You'll still have to look through the result set for the new order_id, but the result set will be much smaller and easier to navigate. You can get more information here: http://developer.eventbrite.com/doc/events/event_list_attendees/
You can pass the order_id in your redirect URL in order to solve this.
When you define a redirect URL, Evenbrite will automatically swap in the order_id value in place of the string "$order_id".
http://your3rdpartywebsite.com/welcome_back/?order_id=$order_id
or:
http://your3rdpartywebsite.com/welcome_back/$order_id/
When the user completes their transaction, they will be redirected to your external site, as shown here: /http://developer.eventbrite.com/doc/workflows/
When your post-transaction landing page is loaded, grab the order_id from the request URL, and call the event_list_attendees API method to find the order information in the response.

Keeping POST data during sign up with Devise

I might be approaching this problem the wrong way ... so if you have a more elegant solution I'm all ears.
Imagine I'm making a system like Kickstarter. I want my users to be able to specify how much they want to pledge before I ask them to sign up.
Then, if they're not registered I need them to sign up before putting them back in the flow that they would have been on had they just signed in. Devise makes this easy by redirecting a user back to the after_sign_up_path_for which ends up being after_sign_in_path_for by default.
So this will always issue a GET request. But if I have data that I received from the POST with the amount they wanted to pledge, but that's lost.
Is the only way to do this to store that posted data in the session? Or is there a clever way to start creating the pledge record without the user (without needing to run jobs to destroy orphaned pledge records)?
I found the approach described in this blog post over at highgroove.com quite interesting in this regard: 
http://highgroove.com/articles/2012/10/09/lazy-user-registration-for-rails-apps.html
The basic idea is to always have an anonymous user at hand, even if the current vistor is not registered. Like this you can create e.g. associations as usual and — once the visitor actually does sign–up — you edit the user rather than all associated objects.
If the user does not ever register, you can simply look for abandoned user accounts and delete them including their associations, rather than look for all kind of abandoned models.

Implementing Reservation System Using Ruby on Rails

Build a restaurant reservation system with the following function:
Here's the list of prioritized functions:
Restaurant Owner
can set number of tables (assuming all 4 seating per table)
can review current reservation,
reservation older than 2 hours are automatically cleared
can add reservation done over phone for given day/time
can remove reservation
can update reservation done over phone
Customer
can review number of available table for day / time
can add reservation for day / time, get confirmation number
can cancel reservation with confirmation number
can update reservation with confirmation number
I am completely new to ruby on rails, I just need a simple hint on how to get started and what should be my approach for this problem?
Start by defining the models (entities), their properties, and how they relate to each other. Next, figure out what functionality needs to be exposed to the front end.
(Those steps can occur in either order, or, more realistically, each will affect your thoughts about the other, so it bounces back and forth as you iterate over the various things the system must handle.)
Expand the user stories you have above with conditions you'll encounter and how you'll know it's done. Rails makes it easy to get starting building up preliminary functionality--don't get hung up with how it looks at first, just make sure you can actually do what you need to.
You'll also need a user authentication/authorization system; I recommend using an existing one like authlogic or devise. Whether or not you need something like cancan for authorization I don't know; but you'll need some way of making sure people can only see what they're supposed to be able to.
You'll also need something like eventmachine for sweeping away old reservations (man, in NYC if you're like 10 minutes late, you're outta there!) but take things a step at a time--first just implement the sweeper as a manual process to get the logic worked out.
Good luck!

Resources