I am a bit of a new rails developer and I am not making sense of Paypal's documentation to create a web experience profile before processing a payment with the REST API:
I can make payments ok with code below as long as I do not try to use the experience profile.
When debugging, I get a value for #webprofile similar to:
#<PayPal::SDK::REST::DataTypes::WebProfile:0x007fe0f9344e50 #error=nil,
#name="YeowZa! T-Shirt Shop", #presentation=#<PayPal::SDK::REST::DataTypes::Presentation:0x007fe0f8ec51b8 #error=nil, #brand_name="YeowZa! Paypa
l", #logo_image="http://www.yeowza.com", #locale_code="US">, #input_fields=#<PayPal::SDK::REST::DataTypes::InputFields:0x007fe0f927b0f0 #error=nil, #allow_note=true, #no_shipping=0, #address_override=1>, #flow_config
=#<PayPal::SDK::REST::DataTypes::FlowConfig:0x007fe0f90808e0 #error=nil, #landing_page_type="billing", #bank_txn_pending_url="http://www.yeowza.com">, #request_id="bcc4bc41-b61c-4d28-94f1-a0912121d8e8", #header={}>
On my console I see:
Request[post]: https://api.sandbox.paypal.com/v1/payment-experience/web-profiles/
Response[400]: Bad Request,
My code so far is:
class PaypalController < ApplicationController
require 'paypal-sdk-rest'
include PayPal::SDK::REST
include PayPal::SDK::Core::Logging
def create
PayPal::SDK::REST.set_config(
:mode => "sandbox", # "sandbox" or "live"
:client_id => "my-id",
:client_secret => "my-secret")
# Build Payment object
#payment = Payment.new({
:intent => "sale",
:payer => {
:payment_method => "paypal"},
:experience_profile_id => self.web_experience,
:redirect_urls => {
:return_url => "http://me.com/paypal_complete",
:cancel_url => "http://me.com/paypal_cancel"},
:transactions => [{
:item_list => {
:items => [{
:name => "me.com Thing",
:sku => "the-specific-horse",
:price => "2",
:currency => "USD",
:quantity => "1" }]},
:amount => {
:total => "2.00",
:currency => "USD" },
:description => "Payment for the-specific-thing" }]
})
# Create Payment and return the status(true or false)
if #payment.create
# Redirect the user to given approval url
#redirect_url = #payment.links.find{|v| v.method == "REDIRECT" }.href
logger.info "Payment[#{#payment.id}]"
logger.info "Redirect: #{#redirect_url}"
redirect_to #redirect_url
else
logger.error #payment.error.inspect
end
end
def paypal_complete
begin
# paymentId: PAY-8L3183743T450642VKWDPH7I
# token: EC-57E34614K6825515M
# PayerID: RBWLMFNFF4ZUC
payment_id = params[:paymentId]
# Retrieve the payment object by calling the
# `find` method
# on the Payment class by passing Payment ID
#payment = Payment.find(payment_id)
logger.info "Got Payment Details for Payment[#{#payment.id}]"
rescue ResourceNotFound => err
# It will throw ResourceNotFound exception if the payment not found
logger.error "Payment Not Found"
end
end
def web_experience
#this is not used right now...don't know how
#webprofile = WebProfile.new(
{
:name => "YeowZa! T-Shirt Shop",
:presentation => {
:brand_name => "YeowZa! Paypal",
:logo_image => "http://www.yeowza.com",
:locale_code => "US"
},
:input_fields => {
:allow_note => true,
:no_shipping => 0,
:address_override => 1
},
:flow_config => {
:landing_page_type => "billing",
:bank_txn_pending_url => "http://www.yeowza.com"
}
})
if #webprofile.create
# Redirect the user to given approval url
logger.info "#webprofile[#{#webprofile}]"
debugger
else
logger.error #payment.error.inspect
debugger
end
end
end
This is old but I thought others might end up here like I did.
I had a similar issue with the .NET SDK. Resolved by removing fields from the request object that I shouldn't have been using i.e. the field bank_txn_pending_url I had set to an empty string, when I didn't define this field at all (which would equate to null), the request returned 200. It clearly states in the documentation that this field should only be used in Germany in certain circumstances.
Once created though, subsequent requests fail, because you cannot have two profiles with the same name. You need to get the list of existing profiles and use the existing ID if found. No need to create over and over.
Bummer the dashboard doesn't capture all requests in the transaction section, would make life much easier if it did.
Related
with Stripe on Rails 4, I want to stock the shipping address of my customer in a variable #testvar, in my ChargesController, so I can :
- Display it in the charges#create page, as a "Here is where we will deliver"
- Send an email to the admin with this info.
Here is my ChargesController : (UPDATED 3rd April)
class ChargesController < ApplicationController
def new
end
def create
admin = Admin.last
customer = Stripe::Customer.create(
email: params[:stripeEmail],
card: params[:stripeToken],
)
charge = Stripe::Charge.create(
:customer => customer.id,
:amount => #amount,
:currency => 'eur',
:receipt_email => params[:stripeEmail],
)
customer_name = charge["name"]
AdminMailer.outfit_ordered(admin, customer_name).deliver_now
rescue Stripe::CardError => e
flash[:error] = e.message
redirect_to :back
end
end
UPDATE 3rd April
I've added a mailer, hence the two following line
customer_name = charge["name"]
AdminMailer.outfit_ordered(admin, customer_name).deliver_now
I've found the following part in another aswer, but I can't get it to work.
stripe_customer_params = JSON.parse customer.to_s
#testvar = stripe_customer_params['cards']['data'].first['shipping_address']
Furthermore, when I assign a random value to my #testvar (like #testvar = "test") and display it in my charges#create view, it works... But #testvar keeps this value with the other tests I run after that.
I am stuck with this problem and can't find anything that helps me in the Stripe's API documentation.
Thanks for your help.
I am using Starburst gem to show announcements upon user login. There is an option to limit the users
Starburst::Announcement.create(
:body => 'Upgrade to platinum and save 10% with coupon code XYZ!',
:limit_to_users =>
[
{
:field => "subscription",
:value => "gold"
}
]
)
Is there any way to pass multiple values for a field like this:
{
:field => "subscription",
:value => ["gold","silver"]
}
Thanks
Currently Starburst doesn't support 'or' in queries. Your best bet is to create a method on your user class and use that for targeting, which will achieve the same result.
Add to the User class:
class User < ActiveRecord::Base
def gold_or_silver
subscription == "gold" || subscription == "silver"
end
end
Then enable the method in config/initializers/starburst.rb (create it if it does not already exist):
Starburst.configuration do |config|
config.user_instance_methods = ["gold_or_silver"] '
end
Then in filtering the message:
Starburst::Announcement.create(
:body => 'Upgrade to platinum and save 10% with coupon code XYZ!',
:limit_to_users =>
[
{
:field => "silver_or_gold",
:value => true
}
]
)
I have a weird issue with the PayPal express checkout. When I pass in an options[:item][:quantity], I get an error code from PayPal that the transaction in invalid.
#controllers/orders_controller.rb
def express
options = {
:ip => request.remote_ip,
:return_url => new_order_url,
:cancel_return_url => products_url,
:items => current_cart.line_items_hash
}
response = EXPRESS_GATEWAY.setup_purchase(current_cart.build_order.price_in_cents,
options)
redirect_to EXPRESS_GATEWAY.redirect_url_for(response.token)
end
# models/cart.rb
def line_items_hash
self.line_items.map do |line_item|
{
:name => Product.find(line_item.product_id).name
# :quantity => line_item.quantity,
:description => line_item.product.description,
:amount => line_item.gross_price_in_cents}
end
end
So this works fine. The problem is that the correct quantity is not shown on the PayPal order confirmation page. However, if I uncomment the quantity variable in the line_items_hash function the whole thing breaks and I get "invalid transaction from paypal". Any ideas?
Silly old me. Paypal keeps invalidating my transaction because I'm passing bad information. I was setting the amount to line_item total which is already the total of quantity * product.price. So the information that Paypal was receiving is quantity * (quantity * product.price).
Silly little mistake but I managed to catch it in the end.
# models/cart.rb
def line_items_hash
self.line_items.map do |line_item|
{
:name => Product.find(line_item.product_id).name
:quantity => line_item.quantity,
:description => line_item.product.description,
:amount => line_item.price.in_cents}
end
end
When I use this code:
#access_token = Doorkeeper::AccessToken.create!({
:application_id => grant.application_id,
:resource_owner_id => grant.resource_owner_id,
:scopes => grant.scopes_string,
:expires_in => server.access_token_expires_in,
:use_refresh_token => server.refresh_token_enabled?,
:meta => grant.meta
})
The :meta parameter is not saved. When I use this code:
#access_token = Doorkeeper::AccessToken.new({
:application_id => grant.application_id,
:resource_owner_id => grant.resource_owner_id,
:scopes => grant.scopes_string,
:expires_in => server.access_token_expires_in,
:use_refresh_token => server.refresh_token_enabled?
})
#access_token.meta = grant.meta
#access_token.save!
The :meta parameter is saved as I expected. What is the difference between these two code snippets?
Edit: the Doorkeeper:AccessToken class is defined in 2 files, here and here. I don't see anything that would impact the above code though.
You need to allow meta attribute for mass assignment:
if ::Rails.version.to_i < 4 || defined?(ProtectedAttributes)
attr_accessible :resource_owner_id,
:application_id,
:expires_in,
:redirect_uri,
:scopes,
:meta # Add this
end
I am trying out seeds.rb for the first time, and one of my data models uses encapsulation provided by the money gem.
Relevant gems:
money (3.6.1)
rails (3.0.5)
My model thus far:
app/models/list.rb
class List < ActiveRecord::Base
attr_accessible :alias, :unit, :participating_manufacturer, :quantity
:latest_price_cents, :latest_price_currency, :url
belongs_to :user
composed_of :latest_price,
:class_name => "Money",
:mapping => [%w(latest_price_cents latest_price_cents), %w(latest_price_currency currency_as_string)],
:constructor => Proc.new {
|latest_price_cents, latest_price_currency| Money.new(latest_price_cents ||
0, latest_price_currency || Money.default_currency)
},
:converter => Proc.new {
|value| value.respond_to?(:to_money) ? value.to_money : raise(ArgumentError,
"Can't convert #{value.class} to Money")
}
end
1) (Addressed successfully)
2) When I get to writing validations, would it be best to write them for the :latest_price attribute or for the :latest_price_cents & :latest_price_currency attributes seperately?
/db/seeds.rb
users = User.create([{ :name => "Foo", :email => "foo#gmail.com",
:password => "foobar", :password_confirmation => "foobar" }])
# etc, will add more users to the array
list = List.create(:user_id => users.first.id, :alias => "Januvia 100mg",
:unit => "tablet", :participating_manufacturer => "Merck",
:quantity => 30, :latest_price_cents => 7500,
:latest_price_currency => "USD", :url =>
"http://www.foobar.com/januvia/100mg-tablets/")
3) Perhaps it is minutiae, but in the seed, should I be assigning values to the virtual :latest_price attribute or to the latest_price_cents and latest_price_currency attributes directly? Is there any way to use faker rather than /db/seeds.rb to perform this task?
I am new to rails and web development.
I can't see your latest_price attribute anywhere, so I'm not sure how to answer your question. Generally, you should validate the attributes entered in the user form. So if a user enters latest_price_cents and latest_price_currency in a form, then they're the ones which need validating.
There's a bug in your seed file. You want to pass in a hash, not an array, when creating a new user; and users should be an array.
users = []
users << User.create!(:name => "Foo",
:email => "foo#gmail.com",
:password => "foobar",)
:password_confirmation => "foobar")
However, if you're considering faker because you want to create some dummy data, take a look at Machinist or Factory Girl. They're designed for creating dummy data, normally for automated tests.
Once you've set up some blueprints, if you want to create dummy data in your seeds file, you can do something like this in seeds.rb:
20.times { List.make } unless Rails.env.production?