I have a ruby on rails app; where I wish to have one user be able to pay another user, less 10% 'commission' to the app; My client wants the fees to come out of the 10% the app keeps, for two reasons 1) not nickel/diming their customer 2) after a certain amount of transactions (per month), the percentage apparently gets lower
So, for example, if User 1 pays User 2 $100, I want it to show up as:
User 1 sends $100 to app -> App receives $97.09 ($100 less the fees) ->
App sends 90.00 (90%) to User 2 -> User 2 receives the full $90 (no fees on his side)
However, despite setting the app as the primary receiver, it posted the fees on the secondary receiver, making User 2 pay the fees. I also attempted to set User 2 as the primary, and only send 10% forward to the app afterwards, but then it moved the fees to the primary receiver. The only thing I changed in the code was the percentage being charged, and the primary/secondary emails. My code looks like this:
<!-- app/lib/pay_pal_api.rb -->
require "pp-adaptive"
class PayPalAPI
def self.payment_url(submission)
amount = submission.amount_in_cents.to_f / 100.0
recipient_cut = amount * 0.9
recipient_email = submission.submitter.paypal_email
client.execute(:Pay,
:action_type => "PAY",
:currency_code => "USD",
:cancel_url => "http://localhost:3000/my-studio",
:return_url => "http://localhost:3000/submissions/#{submission.id}",
:receivers => [
{ :email => recipient_email, :amount => recipient_cut, :primary => false },
{ :email => "TestApp#gmail.com", :amount => amount, :primary => true }
]
) do |response|
if response.success?
puts "Pay key: #{response.pay_key}"
# send the user to PayPal to make the payment
# e.g. https://www.sandbox.paypal.com/webscr?cmd=_ap-payment&paykey=abc
return client.payment_url(response)
else
puts "#{response.ack_code}: #{response.error_message}"
end
end
return nil
end
I figured out that in the pp-adaptive gem, it requires this syntax:
:fees_payer => "PRIMARYRECEIVER",
Now it is working perfectly.
Use the feesPayer field and set it to either PRIMARYRECEIVER or SECONDARYONLY depending on who is receiving the payment first. The ruby SDK version of this is fees_payer -- from the API Reference:
feesPayer xs:string (Optional) The payer of PayPal fees. Allowable values are:
SENDER – Sender pays all fees (for personal, implicit simple/parallel payments; do not use for chained or unilateral payments)
PRIMARYRECEIVER – Primary receiver pays all fees (chained payments only)
EACHRECEIVER – Each receiver pays their own fee (default, personal and unilateral payments)
SECONDARYONLY – Secondary receivers pay all fees (use only for chained payments with one secondary receiver)
EG:
client.execute(:Pay,
:action_type => "PAY",
:currency_code => "USD",
:cancel_url => "http://localhost:3000/my-studio",
:return_url => "http://localhost:3000/submissions/#{submission.id}",
:fees_payer => "SECONDARYONLY",
:receivers => [
{ :email => recipient_email, :amount => recipient_cut, :primary => false },
{ :email => "TestApp#gmail.com", :amount => amount, :primary => true }
]
)
Related
I'm building an application (based in online resource). You can sign up or login with devise. Then, you can buy a product. Or make your own list and sell your products.
I'm integrating Stripe. When I create the Charge, I get this error in the console: Stripe::InvalidRequestError (Must provide source or customer.).
Here is the code of the orders_controller.rb for the charge action:
Stripe.api_key = ENV["STRIPE_API_KEY"]
token = params[:stripeToken]
begin
charge = Stripe::Charge.create(
:amount => (#listing.price * 100).floor,
:currency => "usd",
:card => token
)
flash[:notice] = "Thanks for ordering!"
rescue Stripe::CardError => e
flash[:danger] = e.message
end
Of course I'm taking a look in the Stripe API Documentation here: Charge documentation example and here: Charge full API Reference
I'm not sure how to handle the :resource or customer. I saw in other materials in the web that some people create a customer. In other sites it says that the :card is deprecated, so I'm a little confused.
I will leave the github repository of my project, and feel free to take a look. I'm trying to deal with Transfers and Recipients too. Project Repository
Thanks.
As mentioned in the docs, stripe expects either customer or source to be mentioned while creating a charge. So, you either need to
Create a customer on stripe(if you want to charge that customer in future too) from the token you received, and mention that customer while creating a charge,
customer = Stripe::Customer.create(source: params[:stripeToken])
charge = Stripe::Charge.create({
:amount => 400,
:currency => "usd",
:customer => customer.id,
:description => "Charge for test#example.com"
})
Or, if you don't want to create a customer, then directly mention received token as a source,
Stripe::Charge.create({
:amount => 400,
:currency => "usd",
:source => params[:stripeToken],
:description => "Charge for test#example.com"
})
For anyone going through the Stripe docs, you may have something like this (using an existing account)
account_links = Stripe::AccountLink.create({
account: 'acct_1032D82eZvKYlo2C',
refresh_url: 'https://example.com/reauth',
return_url: 'https://example.com/return',
type: 'account_onboarding',
})
if so, just change it to look like this (i.e. include the actual account.id you created in the previous step):
account_links = Stripe::AccountLink.create({
account: account.id,
refresh_url: 'https://example.com/reauth',
return_url: 'https://example.com/return',
type: 'account_onboarding',
})
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.
I have a rails 4 app that I'm using Stripe checkout. I've followed their tutorial, and my controller looks like:
def create
s = Subscription.new
s.user_id = current_user.id
s.plan_id = params[:plan_id]
s.stripe_token = params[:stripeToken]
s.save
# Amount in cents
#amount = 699
customer = Stripe::Customer.create(
:email => current_user.email,
:card => params[:stripeToken]
)
charge = Stripe::Charge.create(
:customer => customer.id,
:amount => #amount,
:description => 'Sitekite Pro',
:currency => 'usd'
)
rescue Stripe::CardError => e
flash[:error] = e.message
redirect_to charges_path
end
I looked at a couple other tutorials, looking for help with creating a subscription with Stripe checkout. Some of them dont have the Stripe::Charge part. Is the Stripe::Charge part only for single charges? How do I sign the user up for a monthly subscription with the same #amount?
The Stripe::Charge is indeed for single charges. The Customer create is what you need, but when you create the customer you specify a plan (plans are defined in your Stripe dashboard). The plan will specify the amount to charge and how often to charge it.
When the charge is actually made, which might be the same day or might be several days later depending on whether your plan provides, say, some free access time... the Stripe service can send the charge to a webhook... which is to say a route in your project for the dedicated use of the Stripe service.
Stripe will post charges (and failures) to your webhook, and you can handle them appropriately (logging the payments and maybe restricting the user if his card becomes expired or a regular payment fails for some other reason)
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
I'm trying to add a discount object to a subscription with the braintree-rails gem, but it is not applied. I'm guessing my code must be wrong, but I can't find a working example.
discount = BraintreeRails::Discount.find(params[:subscription_promo])
subscription = #plan.subscriptions.build permitted_params[:subscription]
subscription.discounts << discount
# ...
subscription.save
When I dump discount, it is loaded properly. The subscription is created just fine, but at full price. The discount is not there. How can I add the discount to the subscription?
Update: I tried modifying the direct query, but that has not helped.
#subscription.raw_object.discounts = {add:[{inherited_from_id: discount.id}]}
Update 2: I also ran a direct Braintree request against the API with the request expected of the above code, and it worked. There's something wrong happening between setting it and saving.
Update 3: A workaround is possible by extracting the attributes of the BraintreeRails::Subscription object, using Braintree::Subscription to call the API, and using BraintreeRails::Subscription.find to load it back into the object. This is definitely not optimal, though, since it's not very clean, and requires an extra API call.
gem author here.
Unfortunately neither BraintreeRails nor the Braintree ruby gem supports the subscription.discounts << discount style of adding discounts to subscriptions at the moment.
As you can see in braintree ruby doc, the adding/updating/overriding addon/discounts API is a little too flexible to be wrapped in a single subscription.discounts << discount line.
If your setup of addon/discounts for subscription is simple and doesn't vary much, you can try create one plan for each desired combination, and then use the right plan to create the subscription.
If your setup is quite dynamic(in terms of price, billing cycle, quantity etc), use the Braintree API directly is probably your best option. E.g.:
result = Braintree::Subscription.create(
:payment_method_token => "the_payment_method_token",
:plan_id => "the_plan_id",
:add_ons => {
:add => [
{
:inherited_from_id => "add_on_id_1",
:amount => BigDecimal.new("20.00")
}
],
:update => [
{
:existing_id => "add_on_id_2",
:quantity => 2
}
],
:remove => ["add_on_id_3"]
},
:discounts => {
:add => [
{
:inherited_from_id => "discount_id_1",
:amount => BigDecimal.new("15.00")
}
],
:update => [
{
:existing_id => "discount_id_2",
:quantity => 3
}
],
:remove => ["discount_id_3"]
}
)