3

I have the smart buttons "working" in sandbox but I can't think of any way to attach the smart buttons success to the order form which creates the order. With Stripe Elements, it's pretty plug and play because it's on the page and a part of the form itself, but with PayPal with the redirects, I can't seem to think of a way.

Does this require javascript or can I do this without it, aside from what's already there?

Form:

<%= form_for(@order, url: listing_orders_path([@listing, @listing_video]), html: {id: "payment_form-4"} ) do |form| %>

   <%= form.label :name, "Your Name", class: "form-label" %>
   <%= form.text_field :name, class: "form-control", required: true, placeholder: "John" %>
#stripe code here (not important)
<%= form.submit %>
  <div id="paypal-button-container"></div>

<!-- Include the PayPal JavaScript SDK -->
<script src="https://www.paypal.com/sdk/js?client-id=sb&currency=USD"></script>

  <script>
    // Render the PayPal button into #paypal-button-container
    paypal.Buttons({

        // Set up the transaction
        createOrder: function(data, actions) {
            return actions.order.create({
                purchase_units: [{
                    amount: {
                        value: <%= @listing.listing_video.price %>
                    }
                }]
            });
        },

        // Finalize the transaction
        onApprove: function(data, actions) {
            return actions.order.capture().then(function(details) {
                // Show a success message to the buyer
                alert('Transaction completed by ' + details.payer.name.given_name + '!');
            });
        }


    }).render('#paypal-button-container');
</script>

Create Method in Controller:

require 'paypal-checkout-sdk'
client_id = Rails.application.credentials[Rails.env.to_sym].dig(:paypal, :client_id)
client_secret = Rails.application.credentials[Rails.env.to_sym].dig(:paypal, :client_secret)
# Creating an environment
environment = PayPal::SandboxEnvironment.new(client_id, client_secret)
client = PayPal::PayPalHttpClient.new(environment)

@amount_paypal = (@listing.listing_video.price || @listing.listing_tweet.price)
request = PayPalCheckoutSdk::Orders::OrdersCreateRequest::new
request.request_body(
  {
    intent: 'AUTHORIZE',
    purchase_units: [
      {
        amount: {
          currency_code: 'USD',
          value: "#{@amount_paypal}"
        }
      }
    ]
  }
)

begin
  # Call API with your client and get a response for your call
  response = client.execute(request)

  # If call returns body in response, you can get the deserialized version from the result attribute of the response
  order = response.result
  puts order
  @order.paypal_authorization_token = response.id
rescue BraintreeHttp::HttpError => ioe
  # Something went wrong server-side
  puts ioe.status_code
  puts ioe.headers['debug_id']
end

How can I tie in the PayPal smart buttons with the form so once the payment is completed, it creates an order if successful?

UPDATE:::::::

Created a PaypalPayments controller and model:

controller:

  def create
    @paypal_payment = PaypalPayment.new
    @listing = Listing.find_by(params[:listing_id])  

    require 'paypal-checkout-sdk'
    client_id = "#{Rails.application.credentials[Rails.env.to_sym].dig(:paypal, :client_id)}"
    client_secret = "#{Rails.application.credentials[Rails.env.to_sym].dig(:paypal, :client_secret)}"
    # Creating an environment
    environment = PayPal::SandboxEnvironment.new(client_id, client_secret)
    client = PayPal::PayPalHttpClient.new(environment)

    @amount_paypal = @listing.listing_video.price
    request = PayPalCheckoutSdk::Orders::OrdersCreateRequest::new
    @paypal_payment = request.request_body({
                            intent: "AUTHORIZE",
                            purchase_units: [
                                {
                                    amount: {
                                        currency_code: "USD",
                                        value: "#{@amount_paypal}"
                                    }
                                }
                            ]
                          })

    begin
        # Call API with your client and get a response for your call
        response = client.execute(request)

        # If call returns body in response, you can get the deserialized version from the result attribute of the response
        order = response.result
        puts order
        # @order.paypal_authorization_token = response.id
    rescue BraintreeHttp::HttpError => ioe
        # Something went wrong server-side
        puts ioe.status_code
        puts ioe.headers["debug_id"]
    end

    # if @paypal_payment.create
    #   render json: {success: true}
    # else
    #   render json: {success: false}
    # end

  end

Javascript in view:

paypal.Buttons({

                                      createOrder: function() {
                                        return fetch('/paypal_payments', {
                                          method: 'post',
                                          headers: {
                                            'content-type': 'application/json'
                                          }
                                        }).then(function(res) {
                                          return res.json();
                                        }).then(function(data) {
                                          return data.orderID;
                                        });
                                      },



                                      onApprove: function(data) {
                                        return fetch('/orders', {
                                          method: 'post',
                                          headers: {
                                            'content-type': 'application/json'
                                          },
                                          body: JSON.stringify({
                                            orderID: data.orderID
                                          })
                                        }).then(function(res) {
                                          return res.json();
                                        }).then(function(details) {
                                          alert('Authorization created for ' + details.payer_given_name);
                                        });
                                      },


                                  }).render('#paypal-button-container');

With this, the paypal box appears but then goes away right after it loads with this in the CMD:

#<OpenStruct id="1Pxxxxxxx394U", links=[#<OpenStruct href="https://api.sandbox.paypal.com/v2/checkout/orders/1P0xxxxxxx394U", rel="self", method="GET">, #<OpenStruct href="https://www.sandbox.paypal.com/checkoutnow?token=1P07xxxxxxx94U", rel="approve", method="GET">, #<OpenStruct href="https://api.sandbox.paypal.com/v2/checkout/orders/1Pxxxxxxx4U", rel="update", method="PATCH">, #<OpenStruct href="https://api.sandbox.paypal.com/v2/checkout/orders/1P07xxxxxxx394U/authorize", rel="authorize", method="POST">], status="CREATED">
No template found for PaypalPaymentsController#create, rendering head :no_content
Completed 204 No Content in 2335ms (ActiveRecord: 15.8ms)
uno
  • 1,421
  • 12
  • 38
  • is this even a method? what controller action is this? – lacostenycoder Apr 13 '19 at 15:26
  • That's in the create method. It has a ton of more code so i just put in only the paypal section. I'm unsure how to tye it all together. Will i need to redirect to the create method on the `onApprove: function(data, actions)` ? – uno Apr 13 '19 at 17:59
  • I'm really not understanding this. I somehow need to remove the pricing information from the `createOrder: function(data, actions) {` in the view point to the controllers create method (or another method with the create code above). Have you integrated the smart buttons to a rails app? – uno Apr 13 '19 at 18:21

2 Answers2

0

I have not used smart buttons. However, you should not have "a ton more code" in a create action. If you are following MVC and rails conventions. It would seem that you need a seperate controller action to handle the payment authorization separately from the create action. But if you can get to this point in your javascript, here is example of how you would send the data from paypal javascript back to your controller, this will need some work but hopefully it points you in the right direction:

// Finalize the transaction
onApprove: function(data, actions) {
  return actions.order.capture().then(function(details) {
    // Show a success message to the buyer
    alert('Transaction completed by ' + details.payer.name.given_name + '!');
   // here is where you should send info to your controller action via ajax.
     $.ajax({
        type: "POST",
        url: "/orders",
        data: data,
        success: function(data) {
          alert(data); // or whatever you wanna do here
          return false;
        },
        error: function(data) {
          alert(data); // or something else
          return false;
        }
     });
  });
}
lacostenycoder
  • 10,623
  • 4
  • 31
  • 48
  • I'll try this. And by a ton more code, I meant logic that has to do with creating the order itself, not the paypal stuff – uno Apr 13 '19 at 22:51
  • So I did something similar from info from the PayPal docs but I still haven't found any ways to tie it in with the form. Because my order create is based on a form submit and not a cart. So far, yeah I can create a payment to paypal, but in order to create the order with a POST, I need to have it submitted from the form....Would you know a way to have it so when onApprove, I can have some sort of Event that it takes the form input? – uno Apr 13 '19 at 23:18
  • THE OP is updated. Getting "closer" here but not working – uno Apr 13 '19 at 23:56
  • @uno did you find your solution? – mystic cola Jul 19 '20 at 19:41
0

This is most likely far too late, but ill add what worked for me. You need to return the response ID to the PayPal script as a json object. All you need to do is update your create function like so :

...
begin
    # Call API with your client and get a response for your call
    response = client.execute(request)

    # If call returns body in response, you can get the deserialized version from the result attribute of the response
    order = response.result
    render json: { orderID: order.id }
    # @order.paypal_authorization_token = response.id
rescue BraintreeHttp::HttpError => ioe
    # Something went wrong server-side
    puts ioe.status_code
    puts ioe.headers["debug_id"]
end
...
gypsyDev
  • 1,232
  • 1
  • 14
  • 22