Active Merchant paypal recurring payments - ruby-on-rails

I am using Active Merchant gem to handle payments through the site. But now i want to make these payments recurring, on a monthly basis. Is there a way using active merchant or?
subscription_controller.rb
class SubscriptionsController < ApplicationController
def new
#home_page = true
#white = true
#subscription = Subscription.new(token: params[:token])
if !logged_in?
redirect_to signup_url
end
end
def create
#subscription = Subscription.new(subscription_params)
#subscription.remote_ip = request.remote_ip
#subscription.user_id = current_user.id
if #subscription.save
if #subscription.purchase
#subscription.send_thank_you_email
redirect_to thankyou_path
else
raise ActiveRecord::Rollback
flash[:notice] = "It seems something went wrong with the paypal transaction. Please check that your credit card is valid and has credit in it and try again."
redirect_to :back
end
else
flash[:notice] = "Something went wrong with marking your purchase as complete. Please contact support to check it out."
redirect_to :back
end
end
def purchase
response = GATEWAY.setup_purchase(999,
ip: request.remote_ip,
return_url: new_subscription_url,
cancel_return_url: root_url,
currency: "USD",
items: [{name: "Order", description: "Recurring payment for ******", quantity: "1", amount: 999}]
)
redirect_to GATEWAY.redirect_url_for(response.token)
end
def thank_you
#home_page = true
#white = true
end
private
def subscription_params
params.require(:subscription).permit(:token)
end
end
subscription.rb model
def purchase
response = GATEWAY.purchase(999, express_purchase_options)
response.success?
end
def token=(token)
self[:token] = token
if new_record? && !token.blank?
# you can dump details var if you need more info from buyer
details = GATEWAY.details_for(token)
puts details.params["PayerInfo"]["PayerName"].inspect
self.payer_id = details.payer_id
self.first_name = details.params["PayerInfo"]["PayerName"]["FirstName"]
self.last_name = details.params["PayerInfo"]["PayerName"]["LastName"]
end
end
# send thank you email
def send_thank_you_email
UserMailer.thank_you(self).deliver_now
end
private
def express_purchase_options
{
:ip => remote_ip,
:token => token,
:payer_id => payer_id
}
end
production.rb environment
config.after_initialize do
ActiveMerchant::Billing::Base.mode = :production
::GATEWAY = ActiveMerchant::Billing::PaypalExpressGateway.new(
:login => ENV['PAYPAL_LOGIN'],
:password => ENV['PAYPAL_PASSWORD'],
:signature => ENV['PAYPAL_SIGNATURE']
)
end

I think ActiveMerchant used to have something like this:
subscription = PAYPAL_EXPRESS_GATEWAY.recurring(#subscription.price_in_cents, nil,
:description => 'blah',
:start_date => Date.tomorrow,
:period => 'Year',
:frequency => 1,
:amount => price,
:currency => 'USD'
)
See this answer Does ActiveMerchant support Subscription Based transaction
Also see this: https://github.com/activemerchant/active_merchant/blob/master/lib/active_merchant/billing/gateways/paypal_express.rb
https://github.com/activemerchant/active_merchant/blob/master/lib/active_merchant/billing/gateways/paypal/paypal_recurring_api.rb

Related

I would like to notify users if an order is complete after PayPal IPN and order saved to database

As the title states, I would like to notify buyers after they purchase an item on my site through PayPal if the order was a success or if something went wrong. The flow is a buyer goes to an item listing page. Clicks pay and is brought to the new orders page and click Check out with PayPal.
After they are brought to the PayPal checkout and pay for an order, the PayPal IPN notifies the OrdersControoler Orders#Notify action and if it is verified then it saves the order to the database.
The PayPal Adaptive Payments Pay build_pay method create the parameters to be sent to PayPal including the return and cancel URLs. I am not sure how to go about adding a notify or alert to this.
Anyway here is code for the controller.
OrdersController
class OrdersController < ApplicationController
before_action :set_order, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!, :except => [:notify]
skip_before_action :verify_authenticity_token
protect_from_forgery :except => [:notify]
def sales
#orders = Order.all.where(seller: current_user).order("created_at DESC")
end
def purchases
#orders = Order.all.where(buyer: current_user).order("created_at DESC")
end
# GET /orders/new
def new
#order = Order.new
#item = Item.find(params[:item_id])
end
# POST /orders
# POST /orders.json
def create
##order = Order.new(order_params)
#item = Item.find(params[:item_id])
#seller = #item.user
##order.item_id = #item.id
##order.buyer_id = current_user.id
##order.seller_id = #seller.id
# p = Print.find_by(id: params[:id])
# creator = Creator.find_by(id: p.creator_id)
price = (#item.price + #item.shipping_price.to_i)
commission = 0.06
# Build API call
#api = PayPal::SDK::AdaptivePayments.new
#pay = #api.build_pay({
:actionType => "PAY",
:cancelUrl => "http://f4bd4637.ngrok.io" + new_item_order_path,
:returnUrl => root_url,
:currencyCode => "USD",
:feesPayer => "PRIMARYRECEIVER",
:ipnNotificationUrl => "http://f4bd4637.ngrok.io" + paypal_ipn_notify_path,
:receiverList => {
:receiver => [
{
:amount => price,
:email => ##order.item.paypal_email,
:primary => true
},
{
:amount => price * commission,
:email => "example#gmail.com",
:primary => false
}
]
}})
# Make API call
#pay_response = #api.pay(#pay)
# Check if call was valid, if so, redirect to PayPal payment url
if #pay_response.success?
#pay_response.payKey
redirect_to #api.payment_url(#pay_response)
else
redirect_to root_path, alert: #pay_response.error[0].message
end
end
def notify
response = validate_IPN_notification(request.raw_post)
case response
when "VERIFIED"
##order.save
when "INVALID"
else
end
render :nothing => true
end
private
# Use callbacks to share common setup or constraints between actions.
def set_order
#order = Order.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def order_params
if params[:orders] && params[:orders][:stripe_card_token].present?
params.require(:orders).permit(:stripe_card_token)
end
end
protected
def validate_IPN_notification(raw)
uri = URI.parse('https://ipnpb.sandbox.paypal.com/cgi-bin/webscr?cmd=_notify-validate')
http = Net::HTTP.new(uri.host, uri.port)
http.open_timeout = 60
http.read_timeout = 60
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
http.use_ssl = true
response = http.post(uri.request_uri, raw,
'Content-Length' => "#{raw.size}",
'User-Agent' => "My custom user agent"
).body
end
end
Thanks in advance!

Spree offsite payments Payu.in integration: how to mark an order as paid

I am new to ruby/rails/spree. I am implementing Indian payment gateway with spree-3.0.7.
I am able to process the order but payment status is always at balance_due.
Controller code
def confirm
payment_method = Spree::PaymentMethod.find(payment_method_id)
Spree::LogEntry.create({
source: payment_method,
details: params.to_yaml
})
order = current_order || raise(ActiveRecord::RecordNotFound)
if(address = order.bill_address || order.ship_address)
firstname = address.firstname
end
#confirm for correct hash and order amount requested before marking an payment as 'complete'
checksum_matched = payment_method.checksum_ok?([params[:status], '', '', '', '', '', '', params[:udf4], params[:udf3], params[:udf2], params[:udf1], order.email, firstname, #productinfo, params[:amount], params[:txnid]], params[:hash])
if !checksum_matched
flash.alert = 'Malicious transaction detected.'
redirect_to checkout_state_path(order.state)
return
end
#check for order amount
if !payment_method.amount_ok?(order.total, params[:amount])
flash.alert = 'Malicious transaction detected. Order amount not matched.'
redirect_to checkout_state_path(order.state)
return
end
payment = order.payments.create!({
source_type: 'Spree::Gateway::Payumoney',#could be something generated by system
amount: order.total,
payment_method: payment_method
})
payment.started_processing!
payment.pend!
order.next
order.update_attributes({:state => "complete", :completed_at => Time.now})
if order.complete?
order.update!
flash.notice = Spree.t(:order_processed_successfully)
redirect_to order_path(order)
return
else
redirect_to checkout_state_path(order.state)
return
end
end
Gateway/Model Code
require "offsite_payments"
module Spree
class Gateway::Payumoney < Gateway
preference :merchant_id, :string
preference :secret_key, :string
def provider_class
::OffsitePayments.integration('Payu_In')
end
def provider
#assign payment mode
OffsitePayments.mode = preferred_test_mode == true ? :test : :production
provider_class
end
def checksum(items)
provider_class.checksum(preferred_merchant_id, preferred_secret_key, items)
end
def auto_capture?
true
end
def method_type
"payumoney"
end
def support?(source)
true
end
def authorization
self
end
def purchase(amount, source, gateway_options={})
ActiveMerchant::Billing::Response.new(true, "payumoney success")
end
def success?
true
end
def txnid(order)
order.id.to_s + order.number.to_s
end
def service_provider
"payu_paisa"
end
def checksum_ok?(itms, pg_hash)
Digest::SHA512.hexdigest([preferred_secret_key, *itms, preferred_merchant_id].join("|")) == pg_hash
end
def amount_ok?(order_total, pg_amount)
BigDecimal.new(pg_amount) == order_total
end
end
in spree payment doc https://guides.spreecommerce.com/developer/payments.html they have mentioned if auto_capture? return true then purchase method will be called but purchase method is not getting called.
Can anyone point me to right direction?
You need not call the following commands
payment.started_processing!
payment.pend!
Just leave the payment in its initial state. i.e. checkout state and complete your order.
Because when order is completed process_payments! is called.
This method processes unprocessed payments whose criteria is like below
def unprocessed_payments
payments.select { |payment| payment.checkout? }
end
Hope this solves your case :)
I fixed the issue by marking payment as complete.
remove
payment.started_processing!
payment.pend!
add
payment.complete
above
order.next
I have published my code at github as gem
https://github.com/isantoshsingh/spree_payumoney

How to send email to user after payment is done with paypal

I want to sent email to user after transcation done. now paypal is working fine for me, but user is not getting mail notification after transcation done? how to get email notification for user .
Here is my code
This is my controller code
This is my paypal function
def pay
if #order.update order_params
#order.update_attributes(:invoice_id => rand.to_s[2..11])
if current_user.billing_address.blank?
current_user.create_billing_address(#order.billing_address.dup.attributes)
end
if current_user.shipping_address.blank?
current_user.create_shipping_address(#order.shipping_address.dup.attributes)
end
# #cart.calculate_shipping
if #order.total == 0
return redirect_to checkout_thank_you_path
end
# if !params['payment'].present?
# return redirect_to :back, notice: 'Select Payment Gateway!'
# end
# if params['payment']=='paypal'
#order.order_statuses.create(status_type: 1)
item_details=[]
#order.line_items.each do |item|
item_details << {:name => item.title, :quantity => item.quantity, :amount => item.amount.fractional}
end
logger.info item_details.inspect
response = EXPRESS_GATEWAY.setup_purchase(#cart.total.fractional,
:ip => request.remote_ip,
:currency =>"USD",
:items => item_details,
:order_id => #order.invoice_id,
:return_url => checkout_thank_you_url,
:cancel_return_url => cart_url
)
return redirect_to EXPRESS_GATEWAY.redirect_url_for(response.token)
# else
# return redirect_to 'https://www.payumoney.com/'
# end
else
flash[:alert] = 'Billing and shipping address fields are required!'
render :addresses
end
end
This is my thank function for paypal
def thank_you
#order = Order.find(session[:order_id])
details = EXPRESS_GATEWAY.details_for(params[:token])
response = EXPRESS_GATEWAY.purchase(#cart.total.fractional, {
ip: request.remote_ip,
token: params[:token],
payer_id: details.payer_id,
items: #order.line_items.map{|l| {name: l.title, quantity: l.quantity, amount: l.amount.fractional}}
})
if response.success?
# logger.info payment_params.inspect
payment_params = {gateway: 'PayPal Express Checkout', transaction_id: response.params['token'], ip: request.remote_ip, amount: response.params['gross_amount']}
#cart.order.created_at = DateTime.now
#cart.order.status = 'Paid'
#cart.order.save
session.delete :order_id
# OrderMailer.order_confirmation(#order).deliver
# OrderMailer.admin_receipt(#order).deliver
else
redirect_to :cart_checkout, alert: 'Something went wrong. Please try again. If the problem persists, please contact us.'
end
#cart = Cart.new current_or_null_user.id, session[:order_id], session[:currency] # Start a new cart
end
Any help is appreciatable

Not receiving paypal notifications through active merchant

I was working on an app in which I need to work with ipn, but it seems it does not work well.
I am trying to get the notification in success action and have specified the correct url in paypal sandbox.
def success
topup = current_user.topups.last
logger.debug "topup -------->"
logger.debug topup.amount.to_i
# raise request
details = EXPRESS_GATEWAY.details_for(topup.express_token)
logger.debug "details ------->"
logger.debug details.payer_id
# raise params[:payer_id]
response = EXPRESS_GATEWAY.purchase(topup.price_in_cents,{
:ip => request.remote_ip,
:token => topup.express_token,
:payer_id => details.payer_id
})
logger.debug "Response starts here --------->"
logger.debug response
if response.success?
amount = topup.amount.to_i
current_user.credits = current_user.credits.to_i + amount
current_user.save!
flash[:success] = "Thank you for the top up"
# #account_history = current_user.account_histories.build
# #account_history.update_attribute(:type => "Top Up", :details => "", :amount => amount, :user_id => current_user.id)
redirect_to current_user
notify = Paypal::Notification.new request.raw_post
logger.info "Notifying --------->"
logger.info notify
logger.info notify.status
logger.info "Notifying 2 --------->"
logger.info notify.acknowledge
logger.debug notify.status
if notify.acknowledge
logger.debug "Notifying --------->"
logger.debug notify.mc_gross
logger.debug notify.txn_id
end
else
redirect_to root_url
end
end
notify.acknowledge does not return anything (it is blank)
After banging my head for quite some time I was able to solve the issue. Posting my solution just in case anyone is stuck in the similar situation :-
One important thing is that IPN sends post data to the IPN listener so make sure you have a route defined for this.
I also had a before_filter :authenticate_user! , because of which I was getting html code 401. Had to skip before filter for notify action.
def pay_with_account
topup = Topup.find(params[:id])
response = EXPRESS_GATEWAY.setup_purchase(topup.price_in_cents,{
:ip => request.remote_ip,
:currency_code => 'GBP',
:return_url => topups_success_url(topup),
:notify_url => topups_notify_url(topup),
:cancel_return_url => topups_cancel_url(topup),
# :allow_guest_checkout => true,
:items => [{:name => "Topup", :quantity => 1,:description => "Top up my account", :amount => topup.price_in_cents}]
})
topup.update_attribute :express_token, response.token
redirect_to EXPRESS_GATEWAY.redirect_url_for(response.token)
end
This is the return url :-
def success
topup = current_user.topups.last
details = EXPRESS_GATEWAY.details_for(topup.express_token)
response = EXPRESS_GATEWAY.purchase(topup.price_in_cents,{
:ip => request.remote_ip,
:currency_code => 'GBP',
:token => topup.express_token,
:payer_id => details.payer_id
})
if response.success?
amount = topup.amount.to_i
current_user.credits = current_user.credits.to_i + amount
current_user.save!
redirect_to user_payments_path(current_user)
flash[:success] = "Your transaction was successfully completed"
else
flash[:error] = "Your transaction could not be compelted"
redirect_to user_payments_path(current_user)
end
end
This is the notify url :-
def notify
notify = Paypal::Notification.new(request.raw_post)
if notify.acknowledge
#account_history = topup.user.account_histories.build
#account_history.update_attributes(:payment_type => "Top up",:status => notify.status, :amount => notify.gross)
if params[:payer_status] == "verified"
#account_history.update_attributes(:details => "Pay Pal #{#account_history.id}")
elsif params[:payer_status] == "unverified"
#account_history.update_attributes(:details => "Credit Card #{#account_history.id}")
end
#account_history.save
end
render :nothing => true
end
routes :-
get "topups/pay_with_account"
get "topups/pay_without_account"
get "topups/success"
get "topups/cancel"
get "topups/notify"
post "topups/notify

How to get gateway response from model into controller - Ruby on Rails

My application uses activemerchant to process payments. I'm using Eway as my payment gateway. I'm storing credit card details with Eway to keep them out of my application database.
I'm using a method store which returns a response with a customer billing id that I can use at a later time to process the order.
http://rdoc.info/github/Shopify/active_merchant/master/ActiveMerchant/Billing/EwayManagedGateway
My main issue is how do I get the response value into my controller so I can save it to the member model.
I've created a simple ruby file to test this all works and it does. I just need to convert this code to work inside my rails app.
require "rubygems"
gem 'activemerchant', '1.15.0'
require 'activemerchant'
ActiveMerchant::Billing::Base.mode = :production
gateway = ActiveMerchant::Billing::EwayManagedGateway.new(
:login => '12345678',
:username => 'mylogin#example.com',
:password => 'mypassword'
)
credit_card = ActiveMerchant::Billing::CreditCard.new(
:type => "visa",
:number => "4444333322221111",
:verification_value => "123",
:month => "11",
:year => "2011",
:first_name => "John",
:last_name => "Citizen"
)
options = {
:order_id => '1230123',
:ip => "127.0.0.1",
:email => 'john.citizen#example.com',
:billing_address => { :title => "Mr.",
:address1 => '123 Sample Street',
:city => 'Sampleville',
:state => 'NSW',
:country => 'AU',
:zip => '2000'
},
:description => 'purchased items'
}
if credit_card.valid?
response = gateway.store(credit_card, options)
if response.success?
puts "Credit Card Stored. #{response.message}"
customer = response.params['CreateCustomerResult']
puts "Customer Id: #{customer}"
else
puts "Error: #{response.message}"
end
else
puts "Error, credit card is not valid. #{credit_card.errors.full_messages.join('. ')}"
end
Here is the relevant code in my order model.
serialize :params
cattr_accessor :gateway
def response=(response)
self.success = response.success?
self.message = response.message
self.params = response.params
self.billing_id = response.params['CreateCustomerResult']
rescue ActiveMerchant::ActiveMerchantError => e
self.success = false
self.message = e.message
self.params = {}
self.billing_id = nil
end
def store
response = Order.gateway.store(credit_card, options)
end
Here is my order controller create code.
def create
#member = current_member
#order_deal = Deal.find(params[:deal_id])
#order = #order_deal.orders.build(params[:order])
#order.member_id = current_member.id
#order.ip_address = request.remote_ip
#deal = #order_deal
if #order.save
if #order.store
render :action => "success"
flash[:notice] = "Successfully processed your order."
else
render :action => "new"
end
else
render :action => 'new'
end
end
So essentially I want to get the
response.params['CreateCustomerResult']
and add it to my member model under
member.billing_id = response.params['CreateCustomerResult]
How can I do this?

Resources