Search code examples
ruby-on-railspaypalpaypal-rest-sdk

Unable to create draft PayPal invoice using v2 API version


I am upgrading PayPal Invoicing feature from v1 to v2 (Because v1 is deprecated) in my Ruby on Rails application.

Since there's no official library/gem supporting v2 invoicing, so I decided to build everything as per this official documentation here: https://developer.paypal.com/docs/api/invoicing/v2.

The flow is like this:

  1. System will get an access-token based on ClientID and ClientSecret

  2. From this access-token, I will be generating a new invoice_number by sending curl request to: https://api.sandbox.paypal.com/v2/invoicing/generate-next-invoice-number

  3. Upon receiving the invoice_number, I am sending curl request to create draft invoice endpoint with all the required data

    curl -v -X POST https://api.sandbox.paypal.com/v2/invoicing/invoice
    

The issue I am facing is with the last point. I am getting 201 created response from create draft invoice endpoint but the endpoint is not returning me the complete invoice object along with Invoice ID.

Here's what I am getting:

"201"
{"rel"=>"self", "href"=>"https://api.sandbox.paypal.com/v2/invoicing/invoices/INV2-Z3K7-Y79X-36EM-ZQX8", "method"=>"GET"}

If you try opening this link, you'll see this:

{
  "name":"AUTHENTICATION_FAILURE",
  "message":"Authentication failed due to invalid authentication credentials or a missing Authorization header.",
  "links": [
    {
      "href":"https://developer.paypal.com/docs/api/overview/#error",
      "rel":"information_link"
    }
  ]
}

enter image description here

Not sure what I am missing here!

Below is the code for reference:

require 'net/http'
require 'uri'
require 'json'

class PaypalInvoice
  def initialize order
    @order = order
    @client_id = ENV['PAYPAL_CLIENT_ID']
    @client_secret = ENV['PAYPAL_CLIENT_SECRET']
    @base_url = ENV['AD_PP_ENV'] == 'sandbox' ? 'https://api.sandbox.paypal.com' : 'https://api.paypal.com'
    @paypal_access_token_identifier = 'paypal_access_token'
    @request_id ||= SecureRandom.uuid
  end

  def create_draft_invoice
    raise "Paypal token not found" unless Rails.cache.exist?(@paypal_access_token_identifier)

    invoice_number = "#141"
    sleep 5
    try = 0

    uri = URI.parse(@base_url + "/v2/invoicing/invoices")
    request = Net::HTTP::Post.new(uri)
    request['X-PAYPAL-SANDBOX-EMAIL-ADDRESS'] = ENV['PAYPAL_CLIENT_EMAIL']
    request['Authorization'] = "Bearer #{Rails.cache.fetch(@paypal_access_token_identifier)['access_token']}"
    request['Content-Type'] = 'application/json'
    request['PayPal-Request-Id'] = @request_id.to_s

    request.body = JSON.dump({
      "detail" => get_detail(invoice_number),
      "invoicer" => get_invoicer,
      "primary_recipients" => get_primary_recipients,
      "items" => items_info,
      "configuration" => {
        "partial_payment" => {
          "allow_partial_payment" => false,
        },
        "allow_tip" => false,
        "tax_calculated_after_discount" => true,
        "tax_inclusive" => true
      }
    })

    req_options = {
      use_ssl: uri.scheme == "https",
    }

    response = Net::HTTP.start(uri.host, uri.port, req_options) do |http|
      http.request(request)
    end

    p 'method: create_draft_invoice. response: '
    p response.code
    p JSON.parse(response.body)

    raise "Paypal token expired" if response.code.to_s == '401'

  rescue RuntimeError => error
    p "#{error.to_s}"
    try += 1
    access_token_response_status = get_new_access_token
    retry if access_token_response_status.to_s == '200' and try <= 1
  end
end

Solution

  • This:

    {"rel"=>"self", "href"=>"https://api.sandbox.paypal.com/v2/invoicing/invoices/INV2-Z3K7-Y79X-36EM-ZQX8", "method"=>"GET"}

    Is the endpoint for an API call, specifically 'Show invoice details': https://developer.paypal.com/docs/api/invoicing/v2/#invoices_get

    Loading it in a browser w/o an Authorization: Bearer <Access-Token> header will give an AUTHENTICATION_FAILURE.


    There's currently a bug in Sandbox with unconfirmed emails, so make sure your Sandbox emails are confirmed