Why my stripe subscription is not working? I get the below error but i am unable to resolve the issue.
when i created a payment in the stripe admin panel i was given and ID ref. I currently use the ID within my table to match the ID in strip. I am unsure how to correct this.
payment = customer.subscriptions.create(
source: params[:stripeToken],
plan: #subscription
# the subscription.id is the database id
# the plan.id in stripe uses the same id as that of the subscription.id in the database in order to select the right subsciption in stripe
)
error in terminal:
Stripe::InvalidRequestError (This customer has no attached payment source):
the server responded with status 400
2018-06-09T16:27:10.457567+00:00 app[web.1]: Completed 500 Internal Server Error in 987ms
2018-06-09T16:27:10.459118+00:00 app[web.1]:
2018-06-09T16:27:10.459122+00:00 app[web.1]: Stripe::InvalidRequestError (This customer has no attached payment source):
2018-06-09T16:27:10.459124+00:00 app[web.1]: app/controllers/payments_controller.rb:58:in `create'
2018-06-09T16:27:10.459125+00:00 app[web.1]:
2018-06-09T16:27:10.459127+00:00 app[web.1]:
2018-06-09T16:27:10.774887+00:00 heroku[router]: at=info method=GET path="/favicon.ico" host=www.spefz.com request_id=81e62211-2807-4dd3-a9bb-ea260afe0998 fwd="37.152.39.155" dyno=web.1 connect=0ms service=2ms status=200 bytes=202 protocol=https
2018-06-09T16:27:50.921236+00:00 app[api]: Starting process with command `bin/rails console` by user richill
2018-06-09T16:28:01.326416+00:00 heroku[run.7880]: Awaiting client
2018-06-09T16:28:01.346795+00:00 heroku[run.7880]: Starting process with command `bin/rails console`
2018-06-09T16:28:01.714175+00:00 heroku[run.7880]: State changed from starting to up
in my stripe account i have this:
payments_controller.rb [create action in the payments controller]
def create
#payment = Payment.new(payment_params)
#subscription_id = #payment.subscription_id
#event_id = #payment.event_id
# ------- PAYMENT_SUBSCRIPTION -------
if #subscription_id.present?
#user = current_user
#payment = Payment.new(payment_params)
#subscription = #payment.subscription_id
#payment.user_id = current_user.id
if current_user.stripe_id?
customer = Stripe::Customer.retrieve(current_user.stripe_id)
else
customer = Stripe::Customer.create(email: current_user.email)
end
payment = customer.subscriptions.create(
source: params[:stripeToken],
plan: #subscription
# the subscription.id is the database id
# the plan.id in stripe uses the same id as that of the subscription.id in the database in order to select the right subsciption in stripe
)
current_user.update(
stripe_id: customer.id,
stripe_subscription_pymt_id: payment.id,
card_last4: params[:card_last4],
card_exp_month: params[:card_exp_month],
card_exp_year: params[:card_exp_year],
card_type: params[:card_brand],
recent_subscription_pymt_date: DateTime.now,
recent_subscription_cancel_date: nil
)
# if payment is true/successful save the below params under payments table
if payment.present?
#payment.update(
stripe_customer_id: customer.id,
stripe_subscription_id: payment.id,
stripe_payment_id: "none",
subscription_payment_date: DateTime.now,
event_payment_date: "none",
event_payment_date_status: "none",
user_card_type: params[:card_brand],
user_card_last4: params[:card_last4],
user_card_exp_month: params[:card_exp_month],
user_card_exp_year:params[:card_exp_year],
status: "success"
)
else
#payment.update(
stripe_customer_id: customer.id,
stripe_subscription_id: payment.id,
stripe_payment_id: "none",
subscription_payment_date: DateTime.now,
event_payment_date: "none",
user_card_type: params[:card_brand],
user_card_last4: params[:card_last4],
user_card_exp_month: params[:card_exp_month],
user_card_exp_year:params[:card_exp_year],
status: "fail"
)
end
respond_to do |format|
if #payment.save
MailerPaymentuserreceipt.paymentreceipt(#payment).deliver
format.html { redirect_to account_user_path(current_user), notice: 'Your Subscription Payment was successful.' }
format.json { render :show, status: :created, location: #payment }
else
format.html { redirect_to new_payment_path(subscription_id: #subscription.id), alert: 'Ensure all fields are completed'}
format.json { render json: #payment.errors, status: :unprocessable_entity }
end
end
end
end
schema [payment table in the schema]
create_table "payments", force: :cascade do |t|
t.string "email"
t.integer "user_id"
t.integer "subscription_id"
t.string "reference"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "event_id"
t.string "stripe_customer_id"
t.string "stripe_subscription_id"
t.string "stripe_payment_id"
t.datetime "subscription_payment_date"
t.datetime "event_payment_date"
t.string "user_card_type"
t.string "user_card_last4"
t.integer "user_card_exp_month"
t.integer "user_card_exp_year"
t.string "status"
t.string "event_payment_date_status"
t.string "subscription_payment_date_status"
end
To create a subscription on stripe its mandatory to pass customer ID. While source is optional, it is used to add a payment source(card) against that customer. And also try passing items while adding creating subscription.
Example:
Stripe::Subscription.create(
:customer => "cus_D1nB01mz17dzfM",
:items => [
{
:plan => "plan_D1keP6TJp3tjTA",
},
]
)
Try like this
Stripe::Subscription.create(
:customer => customer.id,
:plan => #subscription
)
Related
I am trying to upload photos to my rails backend but I get the error mentioned above.
Here's the controller
class PhotosController < ApplicationController
before_action :set_photo, only: [:show, :update, :destroy]
# GET /photos
def index
#photos = Photo.where(drink_id: params[:drink_id])
render json: #photos
end
# GET /photos/1
def show
render json: #photo
end
# POST /photos
def create
#photo = Photo.new(photo_params)
if #photo.save
render json: #photo, status: :created, location: #photo
else
render json: #photo.errors, status: :unprocessable_entity
end
end
# PATCH/PUT /photos/1
def update
if #photo.update(photo_params)
render json: #photo
else
render json: #photo.errors, status: :unprocessable_entity
end
end
# DELETE /photos/1
def destroy
#photo.destroy
end
private
# Use callbacks to share common setup or constraints between actions.
def set_photo
#photo = Photo.find(params[:id])
end
# Only allow a trusted parameter "white list" through.
def photo_params
params.require(:photo).permit(:name, :src, :drink_id)
end
end
Here's the schema.rb
ActiveRecord::Schema.define(version: 2021_01_12_230812) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
create_table "drinks", force: :cascade do |t|
t.string "name"
t.string "base"
t.string "waves"
t.text "description"
t.string "shop"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
create_table "photos", force: :cascade do |t|
t.string "name"
t.text "src"
t.bigint "drink_id", null: false
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["drink_id"], name: "index_photos_on_drink_id"
end
add_foreign_key "photos", "drinks"
end
This is where I upload the photo
import React, { Component } from 'react'
import {getAllDrinks,uploadDrink, uploadPhoto } from './services/api-helper'
import './App.css';
class App extends Component {
state = {
allDrinks: null,
drinkPhotos: null,
formData: {
}
}
async componentDidMount() {
let allDrinks = await getAllDrinks();
this.setState({
allDrinks
});
}
// lets user select image from their own files
// sets state as the selected file
// turns file into a readable url for src in an img tag
fileChangedHandler = event => {
let reader = new FileReader()
reader.onload = (e) => {
this.setState({ selectedFile: e.target.result})
}
reader.readAsDataURL(event.target.files[0])
console.log(reader)
}
// sets state for formData based off of user input
handleChange = (e) => {
const { name, value } = e.target;
this.setState(prevState => ({
formData: {
...prevState.formData,
[name]: value
}
}));
}
// adds this.state.selectedFile (users selected image) to this.state.formData
// saves formData to the data base
uploadHandler = () => {
uploadDrink(this.state.formData)
window.location.reload()
}
photoUploadHandler = () => {
this.state.formData.src = `${this.state.selectedFile}`
console.log(this.state.formData)
uploadPhoto( this.state.formData.drink_id, this.state.formData)
// window.location.reload()
}
render() {
return (
<div className="App">
<div class='upload-section'>
<input onChange={this.handleChange} placeholder='name' name='name'>
</input>
<input onChange={this.handleChange} placeholder='base' name='base'>
</input>
<input onChange={this.handleChange} placeholder='waves' name='waves'>
</input>
<input onChange={this.handleChange} placeholder='description' name='description'>
</input>
<input onChange={this.handleChange} placeholder='shop' name='shop'>
</input>
<button onClick={this.uploadHandler}>Upload!</button>
</div>
<div class='upload-section'>
<input type="file" onChange={this.fileChangedHandler} name='src'>
</input>
<input onChange={this.handleChange} placeholder='name' name='name'>
</input>
<input onChange={this.handleChange} placeholder='drink reference' name='drink_id'>
</input>
<button onClick={this.photoUploadHandler}>Upload!</button>
</div>
<div id='delete-section'>
{this.state.allDrinks ?
this.state.allDrinks.map(drink=> (
<div className='info-box'>
<p>{drink.name }</p>
</div>
))
:
<></>
}
</div>
</div>
);
}
}
export default App;
I've been racking my brain for days now and can't figure out where the error comes from, any help will be appreciated.
I can't seem to figure out why my PartTransactions test keep failing for updating and creating. All of my other test are successful so, to me I feel like I'm maybe doing something incorrectly in my fixtures - any suggestions would be greatly appreciated!
Here are the failure results when I run my test
Running via Spring preloader in process 62590
Started with run options --seed 23873
FAIL["test_should_create_part_transaction", #<Minitest::Reporters::Suite:0x00007f96615c1710 #name="PartTransactionsControllerTest">, 0.644004000001587]
test_should_create_part_transaction#PartTransactionsControllerTest (0.64s)
"PartTransaction.count" didn't change by 1.
Expected: 3
Actual: 2
test/controllers/part_transactions_controller_test.rb:19:in `block in <class:PartTransactionsControllerTest>'
FAIL["test_should_update_part_transaction", #<Minitest::Reporters::Suite:0x00007f966163a548 #name="PartTransactionsControllerTest">, 0.6869309999747202]
test_should_update_part_transaction#PartTransactionsControllerTest (0.69s)
Expected response to be a <3XX: redirect>, but was a <200: OK>
test/controllers/part_transactions_controller_test.rb:38:in `block in <class:PartTransactionsControllerTest>'
21/21: [===========================================================================================================================================================================================================================================] 100% Time: 00:00:00, Time: 00:00:00
Finished in 0.77788s
21 tests, 26 assertions, 2 failures, 0 errors, 0 skips
Here is my schema.rb
ActiveRecord::Schema.define(version: 2019_01_30_150153) do
create_table "customers", force: :cascade do |t|
t.string "cust_id"
t.string "cust_name"
t.integer "cust_status"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "part_transactions", force: :cascade do |t|
t.integer "trans_id"
t.datetime "trans_date"
t.decimal "trans_qty", precision: 14, scale: 4
t.string "trans_type"
t.string "invoiced"
t.decimal "cs_trans_qty", precision: 6, scale: 2
t.integer "part_id"
t.integer "customer_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["customer_id"], name: "index_csi_part_transactions_on_customer_id"
t.index ["part_id"], name: "index_csi_part_transactions_on_part_id"
end
create_table "parts", force: :cascade do |t|
t.string "part_id"
t.string "description"
t.decimal "qty_on_hand", precision: 14, scale: 4
t.decimal "order_point", precision: 14, scale: 4
t.decimal "reorder_qty", precision: 14, scale: 4
t.string "part_type"
t.string "status"
t.string "inv_cs"
t.decimal "cs_qty", precision: 3
t.string "cs_status"
t.integer "customer_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["customer_id"], name: "index_csi_parts_on_customer_id"
end
end
Here are my models
class Customer < ApplicationRecord
has_many :parts, :dependent => :destroy
has_many :part_transactions, :dependent => :destroy
end
class Part < ApplicationRecord
belongs_to :customer
has_many :part_transactions, :dependent => :destroy
end
class PartTransaction < ApplicationRecord
belongs_to :part
belongs_to :customer
end
Here are my fixtures
#Customer -------------------------------------------------------------
one:
cust_id: one
cust_name: My Company
cust_status: 3
two:
cust_id: two
cust_name: Other Company
cust_status: 1
#Parts-------------------------------------------------------------
one:
part_id: one
description: TestA
qty_on_hand: 2
order_point: 3
reorder_qty: 1
part_type: TestAPartType
status: F
inv_cs: 2
cs_qty: 3
cs_status: F
customer: one
two:
part_id: two
description: TestB
qty_on_hand: 1
order_point: 3
reorder_qty: 6
part_type: TestBPartType
status: F
inv_cs: 2
cs_qty: 3
cs_status: C
customer: two
#PartTransactions------------------------------------------------------
one:
trans_id: one
trans_date: 2019-01-31
trans_qty: 1
trans_qty: 1
trans_type: 1
invoiced: 1
cs_trans_qty: 1
cs_trans_qty: 1
part_id: one
customer: one
two:
trans_id: 2
trans_date: 2019-01-31
trans_qty: 3
trans_qty: 3
trans_type: 3
invoiced: 1
cs_trans_qty: 1
cs_trans_qty: 1
part_id: two
customer: two
Here is my PartTransaction Test
require 'test_helper'
class PartTransactionsControllerTest < ActionDispatch::IntegrationTest
setup do
#part_transaction = part_transactions(:one)
end
test "should get index" do
get part_transactions_url
assert_response :success
end
test "should get new" do
get new_part_transaction_url
assert_response :success
end
test "should create part_transaction" do
assert_difference('PartTransaction.count') do
post part_transactions_url, params: { part_transaction: { cs_trans_qty: #part_transaction.cs_trans_qty, customer_id: #part_transaction.customer_id, invoiced: #part_transaction.invoiced, part_id: #part_transaction.part_id, trans_date: #part_transaction.trans_date, trans_id: #part_transaction.trans_id, trans_qty: #part_transaction.trans_qty, trans_type: #part_transaction.trans_type } }
end
assert_redirected_to part_transaction_url(PartTransaction.last)
end
test "should show part_transaction" do
get part_transaction_url(#part_transaction)
assert_response :success
end
test "should get edit" do
get edit_part_transaction_url(#part_transaction)
assert_response :success
end
test "should update part_transaction" do
patch part_transaction_url(#part_transaction), params: { part_transaction: { cs_trans_qty: #part_transaction.cs_trans_qty, customer_id: #part_transaction.customer_id, invoiced: #part_transaction.invoiced, part_id: #part_transaction.part_id, trans_date: #part_transaction.trans_date, trans_id: #part_transaction.trans_id, trans_qty: #part_transaction.trans_qty, trans_type: #part_transaction.trans_type } }
assert_redirected_to part_transaction_url(#part_transaction)
end
test "should destroy part_transaction" do
assert_difference('PartTransaction.count', -1) do
delete part_transaction_url(#part_transaction)
end
assert_redirected_to part_transactions_url
end
end
Here is my PartTransactionsController
class PartTransactionsController < ApplicationController
before_action :set_part_transaction, only: [:show, :edit, :update, :destroy]
# GET /part_transactions
# GET /part_transactions.json
def index
#part_transactions = PartTransaction.all
end
# GET /part_transactions/1
# GET /part_transactions/1.json
def show
end
# GET /part_transactions/new
def new
#part_transaction = PartTransaction.new
end
# GET /part_transactions/1/edit
def edit
end
# POST /part_transactions
# POST /part_transactions.json
def create
#part_transaction = PartTransaction.new(part_transaction_params)
respond_to do |format|
if #part_transaction.save
format.html { redirect_to #part_transaction, notice: 'Part transaction was successfully created.' }
format.json { render :show, status: :created, location: #part_transaction }
else
format.html { render :new }
format.json { render json: #part_transaction.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /part_transactions/1
# PATCH/PUT /part_transactions/1.json
def update
respond_to do |format|
if #part_transaction.update(part_transaction_params)
format.html { redirect_to #part_transaction, notice: 'Part transaction was successfully updated.' }
format.json { render :show, status: :ok, location: #part_transaction }
else
format.html { render :edit }
format.json { render json: #part_transaction.errors, status: :unprocessable_entity }
end
end
end
# DELETE /part_transactions/1
# DELETE /part_transactions/1.json
def destroy
#part_transaction.destroy
respond_to do |format|
format.html { redirect_to part_transactions_url, notice: 'Part transaction was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_part_transaction
#part_transaction = PartTransaction.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def part_transaction_params
params.require(:part_transaction).permit(:trans_id, :trans_date, :trans_qty, :trans_qty, :trans_type, :invoiced, :cs_trans_qty, :cs_trans_qty, :part_id, :customer_id)
end
end
I don't know how I missed this but, I figured it out and it was caused by my part_transactions fixture - see below
I had part_id in my fixture when it should of been part (*smacks self on the head)
one:
trans_id: one
trans_date: 2019-01-31
trans_qty: 1
trans_qty: 1
trans_type: 1
invoiced: 1
cs_trans_qty: 1
cs_trans_qty: 1
#should be part not part_id
#part_id: one
part: one
customer: one
two:
trans_id: 2
trans_date: 2019-01-31
trans_qty: 3
trans_qty: 3
trans_type: 3
invoiced: 1
cs_trans_qty: 1
cs_trans_qty: 1
#should be part not part_id
#part_id: two
part: two
customer: two
I am using RSpec to test my controller actions and have been successfully tested my index, show, edit actions so far. But for create action it is giving me the following error for valid attributes. I'm using rails 5 and ruby 2.5.3. Can't understand what am I doing wrong.
file /spec/factories/leaves.rb
FactoryBot.define do
factory :leave do
id {Faker::Number.between(from = 1, to = 3)}
user_id {Faker::Number.between(from = 1, to = 3)}
team_lead_id {Faker::Number.between(from = 1, to = 3)}
fiscal_year_id {Faker::Number.between(from = 1, to = 3)}
start_day {Date.today - Faker::Number.number(3).to_i.days}
end_day {Date.today - Faker::Number.number(3).to_i.days}
reason {Faker::Lorem.sentences(sentence_count = 3, supplemental = false)}
status {Faker::Number.between(from = 1, to = 3)}
factory :invalid_leave do
user_id nil
end
end
end
file /spec/controllers/leave_controller_spec.rb
context 'with valid attributes' do
it 'saves the new leave in the database' do
leave_params = FactoryBot.attributes_for(:leave)
expect{ post :create, params: {leave: leave_params}}.to change(Leave,:count).by(1)
end
it 'redirects to leave#index' do
render_template :index
end
end
file /app/controller/leave_controller.rb
def create
#leave = Leave.new(leave_params)
if #leave.save
flash[:notice] = t('leave.leave_create')
redirect_to leave_index_path
else
flash[:notice] = t('leave.leave_create_error')
redirect_to leave_index_path
end
end
The error is:
LeaveController POST#create with valid attributes saves the new leave in the database
Failure/Error: expect{ post :create, params: {leave: leave_params}}.to change(Leave,:count).by(1)
expected `Leave.count` to have changed by 1, but was changed by 0
# ./spec/controllers/leave_controller_spec.rb:64:in `block (4 levels) in <top (required)>'
Update Leave Database
create_table "leaves", force: :cascade do |t|
t.integer "user_id", null: false
t.integer "team_lead_id", null: false
t.integer "fiscal_year_id", null: false
t.date "start_day", null: false
t.date "end_day", null: false
t.text "reason", null: false
t.integer "status", null: false
t.string "comment"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
Leave Model
class Leave < ApplicationRecord
validates :user_id, :team_lead_id, :fiscal_year_id, :start_day, :end_day, :reason, :status, presence: true
end
I think that might be because of id which you set in factory. You shouldn't set id attribute in factory. That's why number of Leave objects didn't change.
Additionally, I assume that you have some relations there - user_id, team_lead_id etc. If these relations are necessarry to create leave object then you have to create factories for these models, too.
In the end your factory should look like this
FactoryBot.define do
factory :leave do
user
team_lead
fiscal_year
start_day {Date.today - Faker::Number.number(3).to_i.days}
end_day {Date.today - Faker::Number.number(3).to_i.days}
reason {Faker::Lorem.sentences(sentence_count = 3, supplemental = false)}
status {Faker::Number.between(from = 1, to = 3)}
factory :invalid_leave do
user nil
end
end
end
Reference: Factory Bot documentation - associations
[For Future Reader] I got it to work by doing the fooling in leave_controller_spec.rb file.
describe 'POST#create' do
context 'with valid attributes' do
let(:valid_attribute) do
attributes_for(:leave,
user_id: 2,
team_lead_id: 3,
fiscal_year_id: 2,
start_day: '2018-10-10'.to_date,
end_day: '2018-10-10'.to_date,
reason: 'Sick',
status: 2)
end
it 'saves the new leave in the database' do
expect do
post :create, params: {leave: valid_attribute}
end.to change(Leave,:count).by(1)
end
it 'redirects to leave#index' do
render_template :index
end
end
I'm building an Events app in Rails and I've hit the error above which relates to this method in my Model -
def validate_availability
errors.add(:base, 'event is fully booked') if booking.count >= event.number_of_spaces
end
The purpose of the method is to avoid over-booking of an event whereby a specific number of spaces are available. In my Controller I have the following code -
Controller#Create
def create
#event = Event.find(params[:event_id])
#booking = #event.bookings.new(booking_params)
#booking.user = current_user
if
#booking.set_booking
flash[:success] = "Your place on our event has been booked"
redirect_to event_booking_path(#event, #booking)
else
flash[:error] = "Booking unsuccessful"
render "new"
end
if #event.is_free?
#booking.save(booking_params)
end
if booking.count >= #event.number_of_spaces
flash[:error] = "Sorry, this event is now fully booked"
render "new"
end
end
I need to define booking.count in my controller but not sure what would work - tried a few things but nothings working. I have the following in my schema -
create_table "bookings", force: :cascade do |t|
t.integer "event_id"
t.integer "user_id"
t.string "stripe_token"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "quantity", default: 1
t.integer "total_amount"
t.string "stripe_charge_id"
t.string "booking_number"
end
The booking.count would rely upon the quantity of spaces/bookings a user wishes to make versus the number of spaces remaining but how do I express this? Do I need a total_bookings column in my table or a separate method?
UPDATE -
Booking.rb
class Booking < ActiveRecord::Base
belongs_to :event
belongs_to :user
before_create :set_booking_number
validates :quantity, presence: true, numericality: { greater_than_or_equal_to: 0 }
validates :total_amount, presence: true, numericality: { greater_than_or_equal_to: 0 }
validate(:validate_booking)
validate(:validate_availability)
def set_booking_number
self.booking_number = "MAMA" + '- ' + SecureRandom.hex(4).upcase
end
def set_booking
if self.event.is_free?
self.total_amount = 0
save!
else
self.total_amount = event.price_pennies * self.quantity
begin
charge = Stripe::Charge.create(
amount: total_amount,
currency: "gbp",
source: stripe_token,
description: "Booking created for amount #{total_amount}")
self.stripe_charge_id = charge.id
save!
rescue Stripe::CardError => e
# if this fails stripe_charge_id will be null, but in case of update we just set it to nil again
self.stripe_charge_id = nil
# we check in validatition if nil
end
end
end
def validate_booking
# stripe_charge_id must be set for not free events
unless self.event.is_free?
return !self.stripe_charge_id.nil?
end
end
private
def validate_availability
errors.add(:base, 'event is fully booked') if event.bookings.count >= event.number_of_spaces
end
end
For the counts of booking table, you should have a booking_count field in events table. Use the counter cache for this. For more details check http://guides.rubyonrails.org/association_basics.html. This is very helpful when records are large.
Your migration for adding column should be similar as below:
def change
add_column :events, :bookings_count, :integer, default: 0
Event.reset_column_information
Event.all.each do |e|
Event.update_counters e.id, :bookings_count => e.bookings.length
end
end
I am following the guide at http://www.tommyblue.it/2013/07/03/paypal-express-checkout-with-ruby-on-rails-and-paypal-sdk-merchant ;and, when I try to pass the order to the paypal_interface, I get a nil object.
def show
#order = Order.find(params[:id])
#paypal = PaypalInterface.new(#order)
#paypal.express_checkout
if #paypal.express_checkout_response.success?
#paypal_url = #paypal.api.express_checkout_url(#paypal.express_checkout_response)
else
# manage error
end
end
edit to include entire file
paypal_interface.rb
require 'paypal-sdk-merchant'
class PaypalInterface
attr_reader :api, :express_checkout_response
PAYPAL_RETURN_URL = Rails.application.routes.url_helpers.paid_orders_url(host: HOST_WO_HTTP)
PAYPAL_CANCEL_URL = Rails.application.routes.url_helpers.revoked_orders_url(host: HOST_WO_HTTP)
PAYPAL_NOTIFY_URL = Rails.application.routes.url_helpers.ipn_orders_url(host: HOST_WO_HTTP)
def initialize(order)
#api = PayPal::SDK::Merchant::API.new
#order = order
end
#set_express_checkout = #api.build_set_express_checkout({
SetExpressCheckoutRequestDetails: {
ReturnURL: PAYPAL_RETURN_URL,
CancelURL: PAYPAL_CANCEL_URL,
PaymentDetails: [{
NotifyURL: PAYPAL_NOTIFY_URL,
OrderTotal: {
currencyID: "EUR",
value: #order.total
},
ItemTotal: {
currencyID: "EUR",
value: #order.total
},
ShippingTotal: {
currencyID: "EUR",
value: "0"
},
TaxTotal: {
currencyID: "EUR",
value: "0"
},
PaymentDetailsItem: [{
Name: #order.code,
Quantity: 1,
Amount: {
currencyID: "EUR",
value: #order.total
},
ItemCategory: "Physical"
}],
PaymentAction: "Sale"
}]
}
})
# Make API call & get response
#express_checkout_response = #api.set_express_checkout(#set_express_checkout)
# Access Response
if #express_checkout_response.success?
#order.set_payment_token(#express_checkout_response.Token)
else
#express_checkout_response.Errors
end
def do_express_checkout
#do_express_checkout_payment = #api.build_do_express_checkout_payment({
DoExpressCheckoutPaymentRequestDetails: {
PaymentAction: "Sale",
Token: #order.payment_token,
PayerID: #order.payerID,
PaymentDetails: [{
OrderTotal: {
currencyID: "EUR",
value: #order.total
},
NotifyURL: PAYPAL_NOTIFY_URL
}]
}
})
# Make API call & get response
#do_express_checkout_payment_response = #api.do_express_checkout_payment(#do_express_checkout_payment)
# Access Response
if #do_express_checkout_payment_response.success?
details = #do_express_checkout_payment_response.DoExpressCheckoutPaymentResponseDetails
#order.set_payment_details(prepare_express_checkout_response(details))
else
errors = #do_express_checkout_payment_response.Errors # => Array
#order.save_payment_errors errors
end
end
end
The class is in /lib/modules and when loaded, the instance variables are nil
Started GET "/orders/1" for 127.0.0.1 at 2014-01-07 01:33:46 -0600
Processing by OrdersController#show as HTML
Parameters: {"id"=>"1"}
Order Load (0.2ms) SELECT "orders".* FROM "orders" WHERE "orders"."id" = ? LIMIT 1 [["id", "1"]]
Completed 500 Internal Server Error in 18ms
NoMethodError (undefined method total' for nil:NilClass):
lib/modules/paypal_interface.rb:25:in'
lib/modules/paypal_interface.rb:3:in <top (required)>'
app/controllers/orders_controller.rb:17:inshow'
schema.rb
ActiveRecord::Schema.define(:version => 20140107060318) do
create_table "orders", :force => true do |t|
t.text "code"
t.text "total"
t.text "payerID"
t.text "payment_token"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
end
I loaded a value of 10 into the form;and, i tried changing the total field to :decimal to no avail, but now I am stuck as the object does not pass to the module from the order controller.
If #question in PaypalInterface is nil, probably Order.find(params[:id]) doesn't find any.
In the show method:
#order = Order.find(params[:id])
#order is nil?
You could try with:
def show
if #order = Order.find(params[:id])
[...]
else
# Can't find order
end
end
I use Pry to debug this kind of errors, just require 'pry' then put binding.pry where you want to stop the code and open a console. An example is:
def show
#order = Order.find(params[:id])
require 'pry'
binding.pry # Go to the console and start debugging #order
[...]
end
ps. What you indicate as paypal_interface.rb is only a part of that file? Because that code seems wrong.