I am using: Ruby 2.4, Rails 5.2.1, Paypal in sandbox mode:
gem 'paypal-sdk-rest', '~> 1.7.3'
Paypal payment as:
items << {
:name => title,
:price => unit_price,
:currency => "USD",
:quantity => quantity,
:description => short_description
}
amount = {
:total => subtotal,
:currency => "USD",
:details => {"subtotal": subtotal, "shipping": "0", "tax": "0"}
}
invoice_number = "invoice-#{rand(1111..9999)}"
#payment = Payment.new({
:intent => "sale",
:payer => {
:payment_method => "paypal"},
:redirect_urls => {
:return_url => "http://localhost:3000/payments/#{order.id}/make_payment",
:cancel_url => "http://localhost:3000/"},
:transactions => [{
:item_list => {
:items => items,
},
:amount => amount,
:description => "Transaction description.",
:invoice_number => invoice_number
}]
})
Payment create as :
if #payment.create
render json: {success: true, paymentID: #payment.id}
else
#payment.error # Error Hash
render json: {success: false}
end
Payment capture:
if payment.execute(payer_id: params[:payerID])
render json: {msg: 'payment_completed', result: 'completed', }
else
render json: {msg: payment.error, result: 'failed'}
end
Refund :
step 1: find paypal payment object from paypal:
payment = PayPal::SDK::REST::Payment.find(order.paypal_id)
step 2: find the transaction to that payment:
transaction = payment.transactions.last
Step2: find related resources to that payment:
related_resource = transaction.related_resources.last
step 3: find the sale object form paypal sdk:
sale = related_resource.sale
sale = Sale.find(sale.id)
Now proceed with refunds:
refund = sale.refund({
:amount => {
:total => "1.31",
:currency => "USD"
}
})
if refund.success?
logger.info "Refund[#{refund.id}] Success"
render json: { result: 'success '}
else
logger.error refund.error.inspect
render json: { result: 'fail'}
end
Someone is having this issue? or now why this is happing on configuring the paypal sdk adaptive?
place the configuration on the yml did not show this message but did not work either, so
i place the sdk configure on: buy action
and on the log shows:
line 58 is PayPal::SDK.configure
NoMethodError (undefined method []' for false:FalseClass):
app/controllers/orders_controller.rb:58:inbuy'
def buy
require 'paypal-sdk-adaptivepayments'
PayPal::SDK.configure(
:mode => "live", # Set "live" for production
:app_id => "APP-xxxxx",
:username => "xxxx.yyyy.com",
:password => "xxxx",
:signature => "xxxxx" )
#api = PayPal::SDK::AdaptivePayments.new
order = Order.find(params[:id])
#pay = #api.build_pay({
:actionType => "PAY",
:cancelUrl => carts_url,
:currencyCode => "US",
:feesPayer => "SENDER",
:ipnNotificationUrl => ipn_notification_order_url(order),
:receiverList => {
:receiver => [{
:email => seller#seller.com,
:amount => 1.0,
:primary => true},
{:email => buyer#buyer.com,
:amount => 1.0,
:primary => false}]},
:returnUrl => carts_url })
#response = #api.pay(#pay)
# Access response
if #response.success? && #response.payment_exec_status != "ERROR"
#response.payKey
redirect_to #api.payment_url(#response) # Url to complete payment
else
#response.error[0].message
redirect_to fail_order_path(order)
end
end
I tried ccavenue but I'am getting this error. paypal is working fine.
undefined methodpayment_service_for'`
This is my controller action
def create
#subscription = Subscription.new(subscription_params)
#programme = Programme.find(subscription_params[:programme_id])
rand_number = rand.to_s[2..11]
#programme.update_attributes(:invoice_id => rand_number)
session[:programme_id]=#programme.id
session[:invoice_id]=#programme.invoice_id
#paypal = PaypalPayment.create(:material_type => 'Programmes',:invoice_id => rand_number,:currency => #programme.currency, :status => 'Processing', :created_at => DateTime.now, :user_id => current_user.specific.id, :email_id => current_user.specific.email, :programme_id => #programme.id,:amount => #programme.price_paisas)
#paypal.save
`session[:paypal_id]=#paypal.id
logger.info #programme.inspect
if subscription_params[:payment_type] == 'paypal'
item_details=[]
if #programme.currency == 'INR'
price = #programme.price.exchange_to('USD')
else
price = #programme.price
end
logger.info price.inspect
item_details << {:name => #programme.title, :quantity => '1', :amount => price.fractional}
response = EXPRESS_GATEWAY.setup_purchase(price.fractional,
:items => item_details,
:currency => "USD",
:order_id => #programme.invoice_id,
:return_url => students_success_url,
:cancel_return_url => students_root_url
)
logger.info response.inspect
session[:programme_price]=price
return redirect_to EXPRESS_GATEWAY.redirect_url_for(response.token)
elsif subscription_params[:payment_type] == 'ccavenue'
payment_service_for #programme.invoice_id, CCAVENUE_ACCOUNT,
:amount => #programme.price.fractional,
:currency => 'INR',
:service => :ccavenue do |service|
service.customer :name => current_user.name,
:email => current_user.email,
:phone => current_user.mobile
service.redirect :return_url => students_success_url
submit_tag 'Proceed to payment'
end
end
end
end`
I referred this link:
https://github.com/meshbrain/active_merchant_ccavenue
The payment_service_for is the view helper of the Active Merchant gem. You should use this method inside views or you should include view helpers inside your controller.
Im trying to remove the signing up / logging in requirement on my rails app for users to make a booking but failed to do so. I'm pretty new and I'm not sure what other changes I'm suppose to make but missed out, any help will be appreciated, thanks!
Working code (with the signing up / logging in requirement)
class CourseController < ApplicationController
before_filter { #account_menu = :courses }
before_filter :authenticate_user!, :except => ['show', 'find', 'messages', 'chat_threads', 'new']
before_filter lambda {
if current_profile.try(:learner?)
flash[:alert] = "You cannot access these pages"
redirect_to '/'
end
}, :only => [:new, :edit, :cancel, :start, :complete]
before_filter lambda {
if current_profile.teacher?
flash[:alert] = "You cannot access these pages"
redirect_to '/'
end
}, :only => [:book, :wish_list, :wish, :unwish, :cancel_booking, :pending]
layout 'account'
def book
#title = "Book a Course"
#course = Course.by_uid(params[:uid])
if current_profile.learner?
if request.post?
price = params[:price].try(:to_f) || 0
current_profile.update_attributes({ :contact_email => params[:contact_email], :contact_phone => params[:contact_phone] })
params['payment-option'] = 'learnlist' if price == 0
case params['payment-option']
when 'learnlist' then
if current_user.balance >= price
current_user.transaction do
br = BookingRequest.create!({
:course_id => #course.id,
:deposited_price => price,
:hourly_price => params[:hourly_price].try(&:to_f),
:lessons => params[:lessons] || [],
:end_of_base_period => params[:end_of_base_period],
:learner_id => current_profile.id,
:source => 'learnlist',
:comments_by_learner => params[:comments],
:place_to_come_by_learner => params[:place_to_come],
:attendance => params[:attendance],
:learner_username => params[:username],
:learner_video_chat_platform => params[:video_chat_platform],
:cancellation_commission => #course.cancellation_commission
})
flash[:notice] = "Your booking request has been successfully sent to the teacher for confirmation"
Notification.add(#course.teacher.user, 'booking_request', br)
redirect_to course_path(#course.uid)
end
else
flash.now[:alert] = "You don't have enough funds in your account to book this course. You'll have to pay with PayPal to book the class"
end
when 'paypal' then
if !current_profile.paypal_set_up? || params[:paypal_email] != current_profile.paypal_email
result = Financials.paypal_preapproval(current_profile, params[:paypal_email], { :course => #course.uid, :price => price, :lessons => params[:lessons] || [], :end_of_base_period => params[:end_of_base_period], :hourly_price => params[:hourly_price].try(&:to_f) })
if result
current_profile.update_attributes!({
:paypal_preapproval_id => result[:preapproval_id],
:paypal_preapproval_confirmed_at => nil,
:paypal_email => params[:paypal_email]
})
redirect_to result[:redirect_url]
else
flash.now[:alert] = "Could not setup PayPal payments. Payments preapproval could not be requested"
end
else
br = BookingRequest.create!({
:course_id => #course.id,
:deposited_price => price,
:hourly_price => params[:hourly_price].try(&:to_f),
:lessons => params[:lessons] || [],
:end_of_base_period => params[:end_of_base_period],
:learner_id => current_profile.id,
:source => 'paypal',
:comments_by_learner => params[:comments],
:place_to_come_by_learner => params[:place_to_come],
:attendance => params[:attendance],
:learner_username => params[:username],
:learner_video_chat_platform => params[:video_chat_platform],
:cancellation_commission => #course.cancellation_commission,
:learnlist_partial_funding => params[:learnlist_partial].try(:to_i) == 1
})
Notification.add(#course.teacher.user, 'booking_request', br)
flash[:notice] = "Booking successfully submitted"
redirect_to course_path(#course.uid)
end
when 'braintree' then
if params[:payment_method_nonce].blank?
flash.now[:alert] = 'You did not configure your payment method. Please click Configure and set it up to proceed'
else
br = BookingRequest.create!({
:course_id => #course.id,
:deposited_price => price,
:hourly_price => params[:hourly_price].try(&:to_f),
:lessons => params[:lessons] || [],
:end_of_base_period => params[:end_of_base_period],
:learner_id => current_profile.id,
:source => 'braintree',
:comments_by_learner => params[:comments],
:place_to_come_by_learner => params[:place_to_come],
:attendance => params[:attendance],
:learner_username => params[:username],
:learner_video_chat_platform => params[:video_chat_platform],
:braintree_payment_method_nonce => params[:payment_method_nonce],
:cancellation_commission => #course.cancellation_commission,
:learnlist_partial_funding => params[:learnlist_partial].try(:to_i) == 1
})
Notification.add(#course.teacher.user, 'booking_request', br)
flash[:notice] = "Booking successfully submitted"
redirect_to course_path(#course.uid)
end
end
end
else
flash[:alert] = "You cannot access this view"
redirect_to '/'
end
end
My attempts to remove the signing up / logging in requirement are as follow. I failed to do it as clicking the book button brings me back to the homepage instead of the book view.
class CourseController < ApplicationController
before_filter { #account_menu = :courses }
before_filter :authenticate_user!, :except => ['show', 'find', 'messages', 'chat_threads', 'new', 'book']
before_filter lambda {
if current_profile.try(:learner?)
flash[:alert] = "You cannot access these pages"
redirect_to '/'
end
}, :only => [:new, :edit, :cancel, :start, :complete]
before_filter lambda {
if current_profile.try(:teacher?)
flash[:alert] = "You cannot access these pages"
redirect_to '/'
end
}, :only => [:book, :wish_list, :wish, :unwish, :cancel_booking, :pending]
layout 'account'
def book
#title = "Book a Course"
#course = Course.by_uid(params[:uid])
if request.post?
price = params[:price].try(:to_f) || 0
current_profile.update_attributes({ :contact_email => params[:contact_email], :contact_phone => params[:contact_phone] })
params['payment-option'] = 'learnlist' if price == 0
case params['payment-option']
when 'learnlist' then
if current_user.balance >= price
current_user.transaction do
br = BookingRequest.create!({
:course_id => #course.id,
:deposited_price => price,
:hourly_price => params[:hourly_price].try(&:to_f),
:lessons => params[:lessons] || [],
:end_of_base_period => params[:end_of_base_period],
:learner_id => current_profile.id,
:source => 'learnlist',
:comments_by_learner => params[:comments],
:place_to_come_by_learner => params[:place_to_come],
:attendance => params[:attendance],
:learner_username => params[:username],
:learner_video_chat_platform => params[:video_chat_platform],
:cancellation_commission => #course.cancellation_commission
})
flash[:notice] = "Your booking request has been successfully sent to the teacher for confirmation"
Notification.add(#course.teacher.user, 'booking_request', br)
redirect_to course_path(#course.uid)
end
else
flash.now[:alert] = "You don't have enough funds in your account to book this course. You'll have to pay with PayPal to book the class"
end
when 'paypal' then
if !current_profile.paypal_set_up? || params[:paypal_email] != current_profile.paypal_email
result = Financials.paypal_preapproval(current_profile, params[:paypal_email], { :course => #course.uid, :price => price, :lessons => params[:lessons] || [], :end_of_base_period => params[:end_of_base_period], :hourly_price => params[:hourly_price].try(&:to_f) })
if result
current_profile.update_attributes!({
:paypal_preapproval_id => result[:preapproval_id],
:paypal_preapproval_confirmed_at => nil,
:paypal_email => params[:paypal_email]
})
redirect_to result[:redirect_url]
else
flash.now[:alert] = "Could not setup PayPal payments. Payments preapproval could not be requested"
end
else
br = BookingRequest.create!({
:course_id => #course.id,
:deposited_price => price,
:hourly_price => params[:hourly_price].try(&:to_f),
:lessons => params[:lessons] || [],
:end_of_base_period => params[:end_of_base_period],
:learner_id => current_profile.id,
:source => 'paypal',
:comments_by_learner => params[:comments],
:place_to_come_by_learner => params[:place_to_come],
:attendance => params[:attendance],
:learner_username => params[:username],
:learner_video_chat_platform => params[:video_chat_platform],
:cancellation_commission => #course.cancellation_commission,
:learnlist_partial_funding => params[:learnlist_partial].try(:to_i) == 1
})
Notification.add(#course.teacher.user, 'booking_request', br)
flash[:notice] = "Booking successfully submitted"
redirect_to course_path(#course.uid)
end
when 'braintree' then
if params[:payment_method_nonce].blank?
flash.now[:alert] = 'You did not configure your payment method. Please click Configure and set it up to proceed'
else
br = BookingRequest.create!({
:course_id => #course.id,
:deposited_price => price,
:hourly_price => params[:hourly_price].try(&:to_f),
:lessons => params[:lessons] || [],
:end_of_base_period => params[:end_of_base_period],
:learner_id => current_profile.id,
:source => 'braintree',
:comments_by_learner => params[:comments],
:place_to_come_by_learner => params[:place_to_come],
:attendance => params[:attendance],
:learner_username => params[:username],
:learner_video_chat_platform => params[:video_chat_platform],
:braintree_payment_method_nonce => params[:payment_method_nonce],
:cancellation_commission => #course.cancellation_commission,
:learnlist_partial_funding => params[:learnlist_partial].try(:to_i) == 1
})
Notification.add(#course.teacher.user, 'booking_request', br)
flash[:notice] = "Booking successfully submitted"
redirect_to course_path(#course.uid)
end
end
end
else
flash[:alert] = "You cannot access this view"
redirect_to '/'
end
Sorry for the long block of code, if there's a need for any more information, I'll be happy to refurnish.
The standard way of dealing with this is to create a user account for the user, without any personal details, but keeping them "logged in" to this user account. This way they can have persistence across pages, can fill their basket, come back later on the same computer etc.
Later, when you actually need their personal details you can say "Before we go to the next step you need to register". Then, you can add the personal details to that account you created for them earlier, and do email verification or whatever you want to do.
With this approach, you will end up with lots of "incomplete" user accounts, where the person never bothered to register, and you could have a scheduled task to delete all the ones that are more than a week old, for example.
If a Stripe charge is successful, and the posted info passes the validations, the user should be redirected to the sold_url.
def create
customer = Stripe::Customer.create(
:email => params[:stripeEmail],
:card => params[:stripeToken],
)
charge = Stripe::Charge.create(
:customer => customer.id,
:amount => #price * 100,
)
if charge["paid"]
#product.update(status: "sold",
email: params[:stripeEmail],
first_name: params[:first_name],
last_name: params[:last_name])
#product.update_attribute(:agreed_to_terms, 1)
redirect_to :back
else
#product.update_attribute(:status, "for_sale")
end
If it fails, it should be redirected :back to the url of the product it failed to buy.
rescue Stripe::CardError => e
flash[:error] = e.message
redirect_to :back
end
I am trying to mock the transaction with stripe-ruby-mock.
RSpec.describe 'checkout page', :type => :request do
let(:stripe_helper) { StripeMock.create_test_helper }
before { StripeMock.start }
after { StripeMock.stop }
it 'redirects back if there is missing info' do
post "/product/1", {:first_name => "john", :last_name => "doe",
:stripeEmail => "johndoe#doe.com"}, {:referer => '/product/1'}
expect(response).to redirect_to '/product/1'
end
it 'successfully processes a charge' do
post "/product/1", {:first_name => "john",
:last_name => "doe",
:stripeEmail => "johndoe#doe.com",
:stripeToken => stripe_helper.generate_card_token,
:agreed_to_terms => 1},
{:referer => '/product/1'}
expect(response).to redirect_to 'sold_url'
end
But it responds that the test with missing info is being redirected to the sold_url.
Failures:
1) checkout page redirects back if there is missing info
Failure/Error: expect(response).to redirect_to '/product/1'
Expected response to be a redirect to <http://www.example.com/product/1> but was a redirect to <http://www.example.com/sold>.
Expected "http://www.example.com/product/1" to be === "http://www.example.com/sold".
Is it my controller or my test that is misbehaving?