Resubscribe an email address in Campaign Monitor - ruby-on-rails

Has anyone had success in resubscribing an email address after being unsubscribed via the Campaign Monitor API.
I ask as i want to keep a list of Active User's email addresses in my CM Active List. When they are suspended they get removed, when they join or pay their fees before getting deleted they are (re)subscribed.
Looking at the Rails API docs:
# File lib/campaign_monitor.rb, line 241
def remove_subscriber(email)
response = #cm_client.Subscriber_Unsubscribe("ListID" => #id, "Email" => email)
Result.new(response["Message"], response["Code"].to_i)
end
# File lib/campaign_monitor.rb, line 445
def unsubscribe(list_id)
response = #cm_client.Subscriber_Unsubscribe("ListID" => list_id, "Email" => #email_address)
Result.new(response["Message"], response["Code"].to_i)
end
On the CM website to move an email in the subscriber list to the active list you need to confirm you have permission to resubscribe them, can anyone say for sure that this applies to the API too?

I've just found the Subscriber.AddAndResubscribe method, undocumented in http://campaignmonitor.rubyforge.org/

Related

Can't use upsert on existing member

I'm trying to subscribe users to Mailchimp with Gibbon 2.2.4 with a generic subscribe method I've been using, and then shortly after I want to add in some extra fields to track the results of a quiz they took.
I want to store this data on Mailchimp because I'd like to manage the emails I send off directly from Mailchimp's dashboard.
The service I created to handle my subscriptions:
class MailchimpService
def subscribe(list_id,email,first_name)
GIBBON.lists(list_id).members.create({
body: {
email_address: email,
status: 'subscribed',
merge_fields: {
FNAME: first_name,
},
double_optin: false,
update_existing: true
}
})
end
def subscribe_to_quiz(first_name, email, user_id, quiz_id)
list_id = ENV['QUIZ_MAILCHIMP_LIST_ID']
if subscribe(list_id,email,first_name)
attempt = QuizAttempt.where("user_id = ? AND quiz_id = ?", user_id, quiz_id).last
correct = attempt.correct_answer_count
total = attempt.questions_answered
successful = attempt.successful?
send_quiz_results(list_id, email, correct, total, successful)
end
end
def send_quiz_results(list_id, email, correct, total, successful)
GIBBON.lists(list_id).members(email).upsert(
body: {
email_address: email,
status: 'subscribed',
merge_fields: {
correct_answers: correct,
total_answers: total,
successful: successful
},
update_existing: true
})
end
end
In subscribe_to_quiz, I'm subscribing the user to my quiz_list in Mailchimp. The values of the fields I'm updating here are irrelevant, but I think they're quite explanatory. When I try to run my upsert statement in send_quiz_results, I get the following error:
the server responded with status 400
#title="Member Exists",
#detail="foo#bar.baz is already a list member. Use PUT to insert or update list members.",
#body={"type"=>"http://developer.mailchimp.com/documentation/mailchimp/guides/error-glossary/", "title"=>"Member Exists", "status"=>400, "detail"=>"foo#bar.baz is already a list member. Use PUT to insert or update list members.", "instance"=>""},
#raw_body="{\"type\":\"http://developer.mailchimp.com/documentation/mailchimp/guides/error-glossary/\",\"title\":\"Member Exists\",\"status\":400,\"detail\":\"foo#bar.baz is already a list member. Use PUT to insert or update list members.\",\"instance\":\"\"}",
#status_code=400
I have no clue why it won't let me do this... It seems like it's referencing a create statement, but the extracted source for the error references my upsert statement.
I know I'm using the corresponding PUT verb for Gibbon, since the following is taken straight from the documentation:
Of course, body is only supported on create, update, and upsert calls. Those map to HTTP POST, PATCH, and PUT verbs respectively.
I have no clue why this isn't working... I've tried taking out the other fields and just simply throwing in the ones I'm updating. I've also tried running it straight from the terminal to make sure nothing's overlapping.
The MailChimp API docs show that when updating a member you must provide the member's subscriber_hash, which the MD5 hash of the lowercase version of the members email address.
Use Digest::MD5.hexdigest to hash the email address with MD5:
GIBBON.lists(list_id).members(Digest::MD5.hexdigest(email.downcase)).upsert

Access subscription details in Stripe payment

I have two subscription plans in my Ruby on Rails application. I use stripe webhook to email to customer when subscription has been created. In the email I want to store data about subscription (and plan) details e.g. when trial_end and plan name or price.
def webhook
stripe_event = Stripe::Event.retrieve(params[:id]) #retrieving Event ID
if stripe_event.type == "customer.subscription.created" #checks if retrieved Event type subscription is created
stripe_customer_token = stripe_event.data.object.customer # Get Customer ID
customer = Stripe::Customer.retrieve(stripe_customer_token) #here I'm able to retrieve Customer data e.g. customer.email
subscription = customer.subscriptions.first.id #according to documentation I need to retrieve Subscription by supplying its ID. I can retrieve Subscription, but don't understand how to retrieve its data, like: subscription.trial_end
UserMailer.customer_subscription_created(customer.email).deliver #this works well
UserMailer.customer_subscription_created(subscription.trial_end).deliver #this does not work
end
end
I have retrieved Subscription of my Customer. When I retrieve customer I can access my customer data like: customer.email I assumed I would be able to do the same when I retrieve Subscription: subscription.trial_end, but this gives me an error. How can I access Subscription data?
Besides when I change plan of a Subscription I do it like so and it works:
def change_user_plan(customer_id, subscription_id)
customer = Stripe::Customer.retrieve("#{customer_id}")
subscription = customer.subscriptions.retrieve("#{subscription_id}")
subscription.plan = 2
subscription.save
end
Here is link to Stripe API to retrieve Subscription
You are correct, you are able to do what you are trying to do. Once you have your subscription, subscription.trial_end works. I just tested it:
2.1.6 :013 > customer = Stripe::Customer.retrieve("#{customer_id}")
=> #<Stripe::Customer:0x3fcd1ed0a630 id=...> JSON: { ... }
2.1.6 :014 > subscription = customer.subscriptions.retrieve("#{subscription_id}")
=> #<Stripe::Subscription:0x3fcd1ecae574 id=...> JSON: { ... }
2.1.6 :015 > subscription.trial_end
=> 1438387199
The problem is this line:
subscription = customer.subscriptions.first.id
You are saving the subscription id itself. You need to do:
subscription = customer.subscriptions.first
to save the whole subscription. Also, you can use subscriptions.retrieve to supply the id for retrieval (as you are doing in your second code example).

Rails 3: loops and plucking items out best practices

I am working on a small app that allows for users to add a product (or subscription) to their cart. Upon creating their account, the new user is sent to a "bundle" page where it asks if they would like to add a different subscription to a different product altogether for a bundled price.
Here is where I am stuck: Upon submitting the user's credit card info I get slightly "lost in translation" when trying to setup the bundle pricing to submit to Authorize.net (I understand how to authnet, not the question here).
Here is what I have so far:
current_order.products.includes(:client).each do |product|
transaction = current_order.submit_order_to_authnet(product)
if transaction.result_code == 'Ok'
new_group = Group.create!(:name => "#{current_user.full_name} #{product.title}", :type => 'school', :start_date => Time.now, :status => 'active', :site_id => 1)
primary = session[:primary_product_id].eql?(product.id) ? true : false
# Add subscription to Group
new_group.add_subscription(product, current_order, transaction.subscription_id, 'active', primary)
# Add Subscription to CurrentOrder
current_order.subscriptions << new_group.subscriptions.last
# Add user to NewGroup
current_user.groups << new_group
# Create New Group Admin
new_group.group_admins.create(:user_id => current_user.id)
# Send success email
OrderMailer.checkout_confirmation(current_user).deliver
else
errors << transaction.result_code
end
end
I am trying to figure out the best solution when it comes to looping through each product in the users current_order because the second subscription in the users cart is the subscription that gets the discount applied too. I know I can write something like this:
current_order.products.includes(:client).each do |product|
if current_order.products.many? and product == current_order.products.last
# run discount logic
else
# continue with authnet for single subscription
end
end
But I am just not sure if that is a best practice or not. Thoughts?
So the only subscription that doesn't get discounted is the first one? Why not write it like this:
current_order.products.includes(:client).each do |product|
if product == current_order.products.first
# continue with authnet for single subscription
else
# run discount logic
end
end

confused and disoriented with paypal ipn

I am using this gem for payments in paypal https://github.com/tc/paypal_adaptive
I am very confused and disoriented with this gem. It has a poorly documented and is difficult for me to understand how to get the data from paypal on ipn response.
I hope this question will help more people having the same problem.
My steps are:
1º I send request to paypal from my orders_controller.rb with method preaproval_payment.
def preapproval_payment
preapproval_request = PaypalAdaptive::Request.new
data = {
"returnUrl" => response_paypal_user_orders_url(current_user),
"cancelUrl"=> cancel_payment_gift_url(#gift),
"requestEnvelope" => {"errorLanguage" => "en_US"},
"senderEmail" => "gift_1342711309_per#gmail.com",
"startingDate" => Time.now,
"endingDate" => Time.now + (60*60*24) * 30,
"currencyCode"=>"USD",
"maxAmountPerPayment" => "#gift.price",
"ipnNotificationUrl" => ipn_notification_url,
"ip" => request.remote_ip
}
preapproval_response = preapproval_request.preapproval(data)
puts data
if preapproval_response.success?
redirect_to preapproval_response.preapproval_paypal_payment_url
else
redirect_to gift_url(#gift), alert: t(".something_was_wrong")
end
end
2º These are the data of my request in my log console from command puts data :
{"returnUrl"=>"http://localhost:3000/en/u/maserranocaceres/orders/response_paypal", "cancelUrl"=>"http://localhost:3000/en/gifts/gift-1/cancel_payment", "requestEnvelope"=>{"errorLanguage"=>"en_US"}, "senderEmail"=>"gift_1342711309_per#gmail.com", "startingDate"=>2012-07-29 13:05:49 +0200, "endingDate"=>2012-08-28 13:05:49 +0200, "currencyCode"=>"USD", "maxAmountPerPayment"=>9, "ipnNotificationUrl"=>"http://localhost:3000/ipn_notification?locale=en", "ip"=>"127.0.0.1"}
3º I redirect to paypal page, and I make the payment on paypal successfully :D.
4º When payment is completed successfully, I am directed to:
http://localhost:3000/en/u/maserranocaceres/orders/response_paypal
I have response_paypal action in orders_controller.rb. It is GET action and my code for this action is:
def response_paypal
respond_to do |format|
format.html { redirect_to user_orders_url(current_user), :alert => "works fine return url"}
end
end
Up to this point everything works fine.
Now what I need is to get the data I received from paypal and save my database a new order if payment is successfully processed.
5º For this purpose I make a file in lib/paypal_ipn.rb and I add to this file the content from https://github.com/tc/paypal_adaptive/blob/master/templates/paypal_ipn.rb
# Allow the metal piece to run in isolation
require(File.dirname(__FILE__) + "/../../config/environment") unless defined?(Rails)
class PaypalIpn
def self.call(env)
if env["PATH_INFO"] =~ /^\/paypal_ipn/
request = Rack::Request.new(env)
params = request.params
ipn = PaypalAdaptive::IpnNotification.new
ipn.send_back(env['rack.request.form_vars'])
if ipn.verified?
#mark transaction as completed in your DB
output = "Verified."
else
output = "Not Verified."
end
[200, {"Content-Type" => "text/html"}, [output]]
else
[404, {"Content-Type" => "text/html"}, ["Not Found"]]
end
end
end
In my routes.rb I add:
match "/ipn_notification" => PaypalIpn
My 2 problems are:
a) I do not see that after making the payment this file to be fired and I can not see in my console data I get from paypal.
b) I want to send to paypal in my request, the id of object #gift for being able to recover later in paypal_ipn.rb and to save my database.
What am I doing wrong and how I can solve these problems?
Thank you
I haven't used that gem, but I've used PayPal IPN before. Here are some things you should check:
Do you have your PayPal account set up to use IPN? You must enable this setting on the account for this to work.
Have you verified that when you pass ipn_notification_url during the payment process, that it matches your "/ipn_notification" route?
For this to work, PayPal must be able to communicate directly with the server that is running this app. This means that typically, unless you have a custom setup on your local machine with dynamic DNS or something, that you will need to actually deploy this code to a server in order for PayPal to be able to communicate with your app. In other words, if this is running on http://localhost:3000, this will not work.
To answer your second question, how to recover #gift in order to record the fact it was paid in your database, I'm not entirely sure how to do it with this gem, but I'll tell you how I do it using ActiveMerchant - it is probably quite similar.
In your payment request to PayPal, you can pass in an invoice number. I believe the field is just called "invoice". Here you would pass the ID of the gift.
When PayPal notifies your app via IPN that the order was paid for, it will pass the invoice number back to you. Retrieve the #gift using this invoice number and then you can do what you need with it.
Here are the relevant parts of my working PayPal code, using the ActiveMerchant gem: https://gist.github.com/3198178
Good luck!

What's the most efficient way to keep a user database in sync with an external mailing list service?

I'd like some advice on how I should synchronize a list of email addresses on 11k users against an external mailing list program, in this case Mailchimp.
Normally the way I'd do this is simply to have an :after_save callback, to send a single update to the external api.
But already each hour, a rake task is run to update a property on every user in the database. If I simply did that, every hour, the the poor mailchimp API would get be hit 11,000 times.
What's the most efficient, simple way to do this, to check only if a single attribute you're watching has changed from what it was before the save?
If there's a variable that persists across the transaction lifecycle I would simply do something like this, where I check if the value has changed, and if it's different execute come other code.
class User
:before_save :store_old_email
:after_save :sync_with_chimp
def store_old_email
$ugly_of_global_variable_to_store_email = user.email
end
:sync_with_chimp
if $ugly_of_global_variable_to_store_email != user.email
//update_mail_chimp_api
end
end
end
I've checked the rails api here, and I'm still slightly unclear on how I should be doing this.
Would you use the dirty? class here to do this?
This is the way I went with in the end.
It turns out Rails gives you loads of handy callbacks in the dirty to do this.
Any suggestions on how to make this code less repetitive wold be gratefully received.
def update_mailchimp(optin)
# Create a Hominid object (A wrapper to the mailchimp api), and pass in a hash from the yaml file
# telling which mailing list id to update with subscribe/unsubscribe notifications)
#hominid = Hominid.new
client_site_list_id = YAML.load(File.read(RAILS_ROOT + "/config/mailchimp.yml"))
case optin
when 'subscribe_newsletter'
logger.debug("subscribing to newsletter...")
"success!" if #hominid.subscribe(client_site_list_id['client_site_to_mailchimp_API_link'], email, {:FNAME => first_name, :LNAME => last_name}, 'html')
when 'unsubscribe_newsletter'
logger.debug("unsubscribing from newsletter...")
"success!" if #hominid.subscribe(client_site_list_id['client_site_to_mailchimp_API_link'], email, {:FNAME => first_name, :LNAME => last_name}, 'html')
when 'subscribe_monthly_update'
logger.debug("subscribing to monthly update...")
"success!" if #hominid.subscribe(client_site_list_id['monthly_update'], email, {:FNAME => first_name, :LNAME => last_name}, 'html')
when 'unsubscribe_monthly_update'
logger.debug("unsubscribing from monthly update...")
"success!" if #hominid.unsubscribe(client_site_list_id['monthly_update'], email, {:FNAME => first_name, :LNAME => last_name}, 'html')
end
end
# Keep the users in sync with mailchimp's own records - by only firing requests to the API if details on a user have changed after saving.
def check_against_mailchimp
logger.info("Checking if changes need to be sent to mailchimp...")
if newsletter_changed?
logger.info("Newsletter changed...")
newsletter ? update_mailchimp('subscribe_newsletter') : update_mailchimp('unsubscribe_newsletter')
end
if monthly_update_changed?
logger.info("update preferences changed...")
monthly_update ? update_mailchimp('subscribe_monthly_update') : update_mailchimp('unsubscribe_monthly_update')
end
end
you could change your users model to an active resource instead of active record and just use mailchimps api as your db for users
this is an older post about active resource but might get you started down the right path
http://www.therailsway.com/2007/9/3/using-activeresource-to-consume-web-services

Resources