Rails - Create separate table or bypass all model validation?
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 开发者_JAVA技巧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.
精彩评论