I've been following Railscast 289 tutorial on how to make recurring payments using Paypal.
I got everything working now except that I am now struggling to figure out how am I able to know if the user cancelled their subscription in paypal.
What I've done
I've manage to set the IPN url in the paypal merchant account and everytime someone subscribes it will send me data from paypal to my webhook.
Now I am storing all the parameters that paypal sends me but I am unable to figure out which of the parameters I should be checking in order to know if the user has cancelled their subscription or if it had not enough funds, etc.
At least this is the way I think it works.
I'm using Paypal Recurring Gem
Questions
How do I notice when an user has cancelled their subscription when paypal sends me their IPN to my webhook.
My webhook atm
def create
user_id = Subscription.find_by(paypal_customer_token: params[:payer_id]).user_id
PaymentNotification.create!(params: params, user_id: user_id, user_role_id: params[:item_number], status: params[:payment_status], transaction_id: params[:txn_id])
#user = User.find_by(id: user_id)
if PaymentNotification.last.params[:profile_status] == "Cancelled"
#user.update_attributes(user_role_id: 1)
end
render nothing: true
end
Notice that I don't want to update the users attribute instantly I want to wait until their month subscription has ended.
I'm currently storing their IPN call in PaymentNotification table.
create_table "payment_notifications", force: :cascade do |t|
t.text "params"
t.integer "user_role_id"
t.string "status"
t.string "transaction_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "user_id"
end
Also, what is the best way to check for these parameters in order to take action to users who haven't paid or have cancelled their subscription?
I have another table which stores the subscriptions
create_table "subscriptions", force: :cascade do |t|
t.integer "user_role_id"
t.integer "user_id"
t.string "paypal_customer_token"
t.string "paypal_recurring_profile_token"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
user_role being in this case the plan they are subscribing.
IPN ensures that you're notified when there's any change to your transactions, from a recurring payment perspective, when your customer cancels the profile, an event triggered POST-Back will be sent to your IPN listener.
A sample raw POST-Back message of profile cancelation will be like this,
payment_cycle=Daily &txn_type=recurring_payment_profile_cancel &last_name=US &next_payme
nt_date=N/A &residence_country=US &initial_payment_amount=0.00 ¤cy_code=USD &t
ime_created=19:25:09 Sep 24, 2015 PDT &verify_sign=AgUGxKs4vGiEqeit6IyHkIjDJVpeAqCMRVC4wh9CZYotn
Jqrr-3oWsFe &period_type= Trial &payer_status=verified &test_ipn=1 &tax=0.00 &pa
yer_email=USP#email.com &first_name=Payer &receiver_email=USM#email.com &payer_id=8FMFQ2
KVYYHTY &product_type=1 &shipping=0.00 &amount_per_cycle=2.00 &profile_status=Cancel
led &charset=gb2312 ¬ify_version=3.8 &amount=2.00 &outstanding_balance=0.00 &recurring_payment_id=I-30FKJ55UV1RW &product_name=RP BA Test &ipn_track_id=65583f3a80f10
How would you be able to notice when a cancelation happens?
Read the parameter txn_type in the IPN message, and determine event type with it (in your case, the event type would be recurring_payment_profile_cancel, find more details on txn_type list in the PayPal IPN Variables)
Reconcile the profile/subscription with your database entry and take action
Match the recurring_payment_id=I-30FKJ55UV1RW from the IPN message with your pre-stored database entry paypal_recurring_profile_token, and take action to update the user_role_id field. (Either stop the subscription instantly or update/set a new expiration date based on your business mode)
Related
I have part of a rails application where a user will create a recipe that will be saved in their "cookbook". Other users will be able to take recipes from other users. So there will be an aspect in the application that shows who created the recipe.
Schema for a Recipe
create_table "recipes", force: :cascade do |t|
t.string "recipe_name"
t.string "description"
t.integer "calories"
t.integer "carbs"
t.integer "fats"
t.integer "protein"
t.integer "user_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
Where I am having trouble is displaying the recipe's creator.
def show
#user = current_user
#recipe = Recipe.find_by(params[:id])
creator = User.find_by(params[#recipe.user_id])
#creator = creator.first_name
end
So for right now I have two user's John (Id: 1) and Alex (Id:2). When I have Alex make a recipe and I put a pry under #recipe I get a user_id of 2 when I call #recipe.user_id.
However, when I put the pry under creator and call creator I get the user_id of 1 and I get John. I believe something is wrong with how I am trying to find the user using the user_id in #recipe. I was wondering if anyone know what I am doing wrong or if I need to add more information. Thanks.
This:
User.find_by(params[#recipe.user_id])
Doesn't make sense for a couple of reasons:
find_by expects a hash-like structure. Something like: User.find_by(id: xxx)
params[#recipe.user_id] doesn't make sense because that's going to be something like: params[1] which is not what you want.
This:
#recipe = Recipe.find_by(params[:id])
Also suffers from the malformed find_by.
So, try something like:
def show
#user = current_user
#recipe = Recipe.find(params[:id])
creator = #recipe.user
#creator = creator.first_name
end
This, naturally, assumes you have your association between Receipt and User set up correctly (i.e., using belongs_to).
I'm using Rails to build a simple web app.
I have a form asking users to select a vice category (e.g.: smoking, shopping, drinking coffee, etc.) and how much they spend (value) per day, in euros (unit).
Schema:
create_table "vices", force: :cascade do |t|
t.string "category"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.float "value"
t.string "unit"
t.string "user"
t.string "hobbies"
end
After submitting the form I dynamically show a message:
<%= "Stop #{#vice.category} and you will save #{#vice.value * 365} #{#vice.unit} per year!" %>
Second line:
<%= "Here's what you can buy instead:" %>
After this, I want to show Amazon products that they can buy according to their hobbies.
For example: if you stop smoking you will save 2000 euros a year. Here are drones you can buy instead. And then I show drones from Amazon.
I have been trying to connect to Amazon Product Advertising API using amazon-ecs gem but no luck showing it so far.
I then have a show.json.builder file with this:
json.partial! "vices/vice", vice: #vice
Amazon::Ecs.configure do |options|
options[:AWS_access_key_id] = '[my access key]'
options[:AWS_secret_key] = '[my secret key]'
options[:associate_tag] = '[my associate tag]'
end
res = Amazon::Ecs.item_search('#{#vice.hobbies}', {:response_group => 'Medium', :sort => 'salesrank'})
Is this correct?
How can I show the Amazon results in show.hmtl.erb
This is my first question here on Stack OVerflow. Let me know what else I need to post to expand on the explanation.
Thanks!
I haven't used webhooks before so I wanted to understand how it works before implementing the listener with a rails app.
SendGrid docs say the webhook can notify my URL when an email is delivered.
I assume I don't want to store each notification as a new row in a database, because if we're sending millions of emails, that can quickly build up.
Should I be implementing a listener that increments a counter? For example:
create_table "campaign", force: :cascade do |t|
t.text "name"
t.integer "delivered"
t.integer "failed"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
Whenever a new notification is received via the webhook:
campaign.delivered += 1
campaign.save
Is a counter the correct way to handle webhooks?
Edit: I just read in the docs:
It is possible to see duplicate events in the data posted by the Event
Webhook.
We recommend that you use some form of deduplication when processing
or storing your Event Webhook data using the sg_event_id as a
differentiator, since this ID is unique for every event.
Does this mean I must save each event as a database row with a sg_event_id?
(I replicated an isolated example of my issue on github: https://github.com/diingo/jobs_emps. It contains just the problematic portion - no views, no controllers, just the models I describe below and the breaking test.)
My code is working on development and production but currently a portion is breaking only in the test environment leaving me unable to test it properly.
Here's what's happening:
I have two models, jobs and employees. Each job has employees that oversee and participate in it as a specific role (employee_type) - Manager or Exective. Employees can be promoted - a manager can be promoted to an executive. But their roles for previous jobs they participated in must remain the same.
A join between employees and jobs (JobsEmployee) keeps track of the employee's role through the employee_type attribute. The join will not update a user's position if it was previously set - this is done with a before_save, as seen here:
class JobsEmployee < ActiveRecord::Base
before_save :set_user_type
def set_user_type
self.user_type ||= self.user.type
end
end
This works fine in actual use on development and production. If a job is created, with a manager and exective, job.jobs_employees will show one manager and one executive. If a manager is promoted to executive and that job is then updated for whatever reason, job.jobs_employees will still show one manager and one executive.
However, during testing this changes. Upon updating a job, if an employee was promoted, job.jobs_employees shows two executives.
My test is shown below. You can see I abstracted controller create and update methods into models for convenience. You can reference them on the github link: https://github.com/diingo/jobs_emps/blob/master/app/models/job.rb
RSpec.describe JobsEmployee, :type => :model do
before do
#job_permitted_params = {
city: "Munich",
status: "in_progress"
}
#manager = Employee.create!(name: "Bob Bobber", type: 'Manager')
#executive = Employee.create!(name: "Alice Smith", type: 'Executive')
#job_raw_params = {
job: {
manager_id: #manager.id,
executive_id: #executive.id
}
}
end
it "creates and updates" do
job = Job.create_with_params(#job_permitted_params, #job_raw_params)
# This passes:
expect(job.jobs_employees.map &:employee_type).to include("Manager", "Executive")
#manager.type = 'Executive'
#manager.save!
Job.update_with_params(job, #job_permitted_params, #job_raw_params)
# This breaks in testing but works in production:
expect(job.jobs_employees.map &:employee_type).to include("Manager", "Executive")
end
end
I put break points (pry debugger) in JobsEmployee#set_user_type to see what might be happening. It appears like the record in JobsEmployee are deleted before or during a Job update. So instead of seeing that self.user_type is already set in self.user_type ||= self.user.type, it just runs self.user.type again.
Here is the schema. You can also see it in the github link.
ActiveRecord::Schema.define(version: 20150301232938) do
create_table "employees", force: true do |t|
t.string "name"
t.string "type"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "jobs", force: true do |t|
t.string "city"
t.string "status"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "jobs_employees", force: true do |t|
t.string "employee_type"
t.integer "employee_id"
t.integer "job_id"
t.datetime "created_at"
t.datetime "updated_at"
end
end
I'm not sure why this is happening. Thanks so much for checking out the problem.
The "actual use" you are describing may not correlate with your test. Updating a job and changing an employee's role would not necessarily cause the wholesale reassignment of jobs_employees association that you are doing in your test. I suspect that when you are doing that, Rails is comparing the set of JobsEmployee records represented by the ids you are assigning to the currently associated records. Since the currently associated records have the user_type field set, they aren't equivalent to the records that would be created upon a fresh assignment, so they are deleted and new ones regenerated.
I have a form through which I am uploading images. The workflow is, that I will upload and save an image and then I run a delayed job, that will take this image and will create from it 3 thumbs.
When I refresh the page, I usually see that the thumbs are not created yet, but after a while (10-15 seconds) are the thumbs ready.
But this is not very friendly - I would like to show to a user that his thumb is in progress of creating, but how to do that?
The structure of the delayed_jobs table is like this:
create_table "delayed_jobs", :force => true do |t|
t.integer "priority", :default => 0, :null => false
t.integer "attempts", :default => 0, :null => false
t.text "handler", :null => false
t.text "last_error"
t.datetime "run_at"
t.datetime "locked_at"
t.datetime "failed_at"
t.string "locked_by"
t.string "queue"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
I think the way to go could be to add to this table a column, like photo_id and simply if the respective row would be missing in this table, then I would know that the thumb is ready. Otherwise is in a queue/in progress of creating.
But how to save into this table this extra column? When I call the delay method, it's just like this:
#user.delay.activate!(#device)
How to pass there an ID of a photo?
Thanks
Rather than storing the photo_id in the delayed_jobs table, you can store the job id in the photos (or users) table. For this you'll need to write a Job class like so:
class PhotoThumbnailJob < Struct.new(:user_id)
def perform
# generate the thumbnail
end
end
Then get a reference to the job instance like this:
job = Delayed::Job.enqueue(PhotoThumbnailJob.new(#user.id))
#user.thumbnail_job_id = job.id
Now as long as you have the #user you can get the job. That way you can tell whether it failed or simply hasn't finished yet, and if it failed you can report on the reason, etc.
Note that if you do this, you might want to create a foreign key constraint on thumbnail_job_id and tell it to null that column when the job is deleted, because by default DJ will delete successful jobs from the delayed_jobs table.
I had a need for a similar capability in an application I built. The approach I took was to use WebSockets to send status updates to the user as my DelayedJob jobs progress. I used Pusher because the API is straightforward and it was free for us, but any implementation, including your own, would do.
Otherwise, I would stay very far way from altering the table and instead utilize the various hooks DelayedJob provides to enable a callback capability for you to take whatever action you prefer at various stages of the job.