PaypalAPI
Installation
bundle add paypal-rest-api
Features
- Supported Ruby Versions - (2.6 .. 3.3), head, jruby-9.4, truffleruby-24
- No dependencies;
- Automatic authorization & re-authorization;
- Auto-retries (configured);
- Automatically added Paypal-Request-Id header for idempotent requests if not
provided;
- Webhooks Offline verification (needs to download certificate once)
- Custom callbacks before/after request
- Automatic pagination methods
Usage
There are two options:
- Setting global client
- Setting local client (for possibility to use multiple PayPal environments)
Setting global client
PaypalAPI.client = PaypalAPI::Client.new(
client_id: ENV['PAYPAL_CLIENT_ID'],
client_secret: ENV['PAYPAL_CLIENT_SECRET'],
live: false
)
PaypalAPI.live?
PaypalAPI.api_url
PaypalAPI.web_url
response = PaypalAPI::Orders.show(order_id)
response = PaypalAPI::Orders.create(body: body)
Setting local client
client = PaypalAPI::Client.new(
client_id: ENV['PAYPAL_CLIENT_ID'],
client_secret: ENV['PAYPAL_CLIENT_SECRET'],
live: false
)
client.live?
client.api_url
client.web_url
response = client.orders.show(order_id)
response = client.orders.create(body: body)
Client REST methods
Client can call HTTP methods directly:
response = client.post(path, query: query, body: body, headers: headers)
response = client.get(path, query: query, body: body, headers: headers)
response = client.patch(path, query: query, body: body, headers: headers)
response = client.put(path, query: query, body: body, headers: headers)
response = client.delete(path, query: query, body: body, headers: headers)
response = PaypalAPI.post(path, query: query, body: body, headers: headers)
response = PaypalAPI.get(path, query: query, body: body, headers: headers)
response = PaypalAPI.patch(path, query: query, body: body, headers: headers)
response = PaypalAPI.put(path, query: query, body: body, headers: headers)
response = PaypalAPI.delete(path, query: query, body: body, headers: headers)
Response
Response
object is returned after each API
request.
Original HTTP response data
response.http_status
- response HTTP status as Integerresponse.http_body
- response body as Stringresponse.http_headers
- response headers as Hash with String keysresponse.http_response
- original Net::HTTP::Response objectresponse.request
- Request object that was used to get this response
Parsed JSON body methods
response.body
- parsed JSON body, keys are Symbolsresponse[:field]
- gets :field
attribute from parsed body,
returns nil if response have no such keyresponse.fetch(:field)
- gets :field
attribute from parsed body,
raises KeyError if response has no such key
Error check methods
response.success?
- checks HTTP status code is 2xxresponse.failed?
- checks HTTP status code is not 2xx
Using HATEOAS links
response.follow_up_link('approve', query: nil, body: nil, headers: nil)
-
Finds HATEOAS link is response with rel=approve
and requests it. Returns
nil
if no such link were found.
response.each_page { |response| ... }
- iterates over each page in responseresponse.each_page_item(items_field) { |item| ... }
- iterates over each
page item
Configuration options
PaypalAPI client accepts this additional options:
:live
:retries
,:http_opts
:cache
Option :live
PaypalAPI client can be defined with live
option which is false
by default.
When live
is false
all requests will be send to the sandbox endpoints.
client = PaypalAPI::Client.new(
live: true
)
Option :retries
This is a Hash with retries configuration.
By default retries are enabled, 4 retries with 0, 0.25, 0.75, 1.5 seconds delay.
Default config: {enabled: true, count: 4, sleep: [0, 0.25, 0.75, 1.5]}
.
New options are merged with defaults.
Please keep sleep
array same size as count
.
Retries happen on any network error, on 409, 429, 5xx response status code.
client = PaypalAPI::Client.new(
retries: {enabled: !Rails.env.test?, count: 5, sleep: [0, 0.25, 0.75, 1.5, 2]}
)
Option :http_opts
This are the options that are provided to the Net::HTTP.start
method,
like :read_timeout
, :write_timeout
, etc.
You can find full list of available options here https://docs.ruby-lang.org/en/master/Net/HTTP.html#method-c-start
(Please choose you version of ruby).
By default it is an empty hash.
client = PaypalAPI::Client.new(
http_opts: {read_timeout: 30, write_timeout: 30, open_timeout: 30}
)
Option :cache
This option can be added to save certificates to between redeploys to validate
webhooks offline. By default this gem has only in-memory caching.
Cache object must response to standard caching #fetch(key, &block)
method.
By default it is nil
, so downloaded certificates will be downloaded again after
redeploys.
client = PaypalAPI::Client.new(
cache: Rails.cache
)
PayPal provides HATEOAS links in responses. This links can contain items with
rel=next
attribute. We request next pages using this links.
We have two specific methods:
Response#each_page
- iterates over each page Response
objectResponse#each_page_item(items_field_name)
- iterates over items on each page
Example:
PaypalAPI::WebhookEvents.list(page_size: 25).each_page do |response|
end
PaypalAPI::WebhookEvents.list(page_size: 25).each_page_item(:events) do |hash|
end
Webhoooks verification
Webhooks can be verified offline
or online.
Method PaypalAPI.verify_webhook(webhook_id:, headers:, raw_body:)
verifies webhook. It verifies webhook OFFLINE and fallbacks
to ONLINE if initial verification returns false to be sure you don't miss a
valid webhook.
When some required header is missing the
PaypalAPI::WebhooksVerifier::MissingHeader
error will be raised.
Example of Rails controller with webhook verification:
class Webhooks::PaypalController < ApplicationController
def create
webhook_id = ENV['PAYPAL_WEBHOOK_ID']
headers = request.headers
raw_body = request.raw_post
webhook_is_valid = PaypalAPI.verify_webhook(
webhook_id: webhook_id,
headers: headers,
raw_body: raw_body
)
if webhook_is_valid
handle_valid_webhook_event(body)
else
handle_invalid_webhook_event(webhook_id, headers, body)
end
head :no_content
end
end
Callbacks
Callbacks list:
:before
- Runs before request:after_success
- Runs after getting successful response:after_fail
- Runs after getting failed response (non-2xx) status code:after_network_error
- Runs after getting network error
Callbacks are registered on client
object.
Each callback receive request
and context
variables.
context
can be modified manually to save state between callbacks.
Arguments:
:before
- (request, context):after_success
- (request, context, response):after_fail
- (request, context, response):after_network_error
- (request, context, error)
Context
argument contains retries_enabled
and retries_count
and
retry_number
keys by default.
Examples:
PaypalAPI.client.add_callback(:before) do |request, context|
context[:request_id] = SecureRandom.hex(3)
context[:starts_at] = Process.clock_gettime(Process::CLOCK_MONOTONIC)
end
PaypalAPI.client.add_callback(:after_success) do |request, context, response|
ends_at = Process.clock_gettime(Process::CLOCK_MONOTONIC)
duration = ends_at - context[:starts_at]
SomeLogger.debug(
'PaypalAPI success request',
method: request.method,
uri: request.uri.to_s,
duration: duration
)
end
PaypalAPI.client.add_callback(:after_fail) do |request, context, response|
SomeLogger.error(
'PaypalAPI request failed',
method: request.method,
uri: request.uri.to_s,
response_status: response.http_status,
response_body: response.http_body,
will_retry: context[:will_retry],
retry_number: context[:retry_number],
retry_count: context[:retry_count]
)
end
PaypalAPI.client.add_callback(:after_network_error) do |request, context, error|
SomeLogger.error(
'PaypalAPI network connection error',
method: request.method,
uri: request.uri.to_s,
error: error.message,
paypal_request_id: request.headers['paypal-request-id'],
will_retry: context[:will_retry],
retry_number: context[:retry_number],
retry_count: context[:retry_count]
)
end
Errors
All APIs can raise error in case of network error or non-2xx response status code.
Errors structure:
PaypalAPI::Error
PaypalAPI::Errors::NetworkError
- any network errorPaypalAPI::Errors::FailedRequest
- any non-2xx code error
- 400 -
PaypalAPI::Errors::BadRequest
- 401 -
PaypalAPI::Errors::Unauthorized
- 403 -
PaypalAPI::Errors::Forbidden
- 404 -
PaypalAPI::Errors::NotFound
- 405 -
PaypalAPI::Errors::MethodNotAllowed
- 406 -
PaypalAPI::Errors::NotAcceptable
- 409 -
PaypalAPI::Errors::Conflict
- 415 -
PaypalAPI::Errors::UnsupportedMediaType
- 422 -
PaypalAPI::Errors::UnprocessableEntity
- 429 -
PaypalAPI::Errors::TooManyRequests
- 5xx -
PaypalAPI::Errors::FatalError
- 500 -
PaypalAPI::Errors::InternalServerError
- 503 -
PaypalAPI::Errors::ServiceUnavailable
All errors have additional methods:
#paypal_request_id
- PayPal-Request-Id header sent with request#response
- Original response object, can be nil in case of NetworkError#request
- Original request object#error_name
- Original error name#error_message
- Original PayPal error :message
or error :description
#error_debug_id
- Paypal debug_id found in response#error_details
- Parsed PayPal error details found in parsed response
(with symbolized keys)
begin
response = PaypalAPI.authorized_payments.capture(authorization_id, body: body)
rescue PaypalAPI::Error => error
YourLogger.error(
error,
context: {
paypal_request_id: error.paypal_request_id,
error_name: error.error_name,
error_message: error.error_message,
error_debug_id: error.error_debug_id,
error_details: error.error_details
}
)
end
APIs
All API endpoints accept this parameters:
resource_id
- Resource ID (Unless create
/list
endpoint)query
- Hash with request query paramsbody
- Hash with request body paramsheaders
- Hash with request headers
Orders
PaypalAPI::Orders.create
PaypalAPI::Orders.show
PaypalAPI::Orders.update
PaypalAPI::Orders.confirm
PaypalAPI::Orders.authorize
PaypalAPI::Orders.capture
PaypalAPI::Orders.track
PaypalAPI::Orders.update_tracker
Payment Tokens
PaypalAPI::PaymentTokens.create
PaypalAPI::PaymentTokens.list
PaypalAPI::PaymentTokens.show
PaypalAPI::PaymentTokens.delete
PaypalAPI::SetupTokens.create
PaypalAPI::SetupTokens.show
Payments
PaypalAPI::AuthorizedPayments.show
PaypalAPI::AuthorizedPayments.capture
PaypalAPI::AuthorizedPayments.reauthorize
PaypalAPI::AuthorizedPayments.void
PaypalAPI::CapturedPayments.show
PaypalAPI::CapturedPayments.refund
Webhooks
PaypalAPI::Webhooks.create
PaypalAPI::Webhooks.list
PaypalAPI::Webhooks.show
PaypalAPI::Webhooks.update
PaypalAPI::Webhooks.delete
PaypalAPI::Webhooks.event_types
PaypalAPI::Webhooks.verify
PaypalAPI::WebhookEvents.available
PaypalAPI::WebhookEvents.list
PaypalAPI::WebhookEvents.show
PaypalAPI::WebhookEvents.resend
PaypalAPI::WebhookEvents.simulate
PaypalAPI::WebhookLookups.create
PaypalAPI::WebhookLookups.list
PaypalAPI::WebhookLookups.show
PaypalAPI::WebhookLookups.delete
Subscriptions
PaypalAPI::Subscriptions.create
PaypalAPI::Subscriptions.show
PaypalAPI::Subscriptions.update
PaypalAPI::Subscriptions.revise
PaypalAPI::Subscriptions.suspend
PaypalAPI::Subscriptions.cancel
PaypalAPI::Subscriptions.activate
PaypalAPI::Subscriptions.capture
PaypalAPI::Subscriptions.transactions
PaypalAPI::SubscriptionPlans.create
PaypalAPI::SubscriptionPlans.list
PaypalAPI::SubscriptionPlans.show
PaypalAPI::SubscriptionPlans.update
PaypalAPI::SubscriptionPlans.activate
PaypalAPI::SubscriptionPlans.deactivate
PaypalAPI::SubscriptionPlans.update_pricing
Shipment Tracking
PaypalAPI::ShipmentTracking.add
PaypalAPI::ShipmentTracking.update
PaypalAPI::ShipmentTracking.show
Catalog Products
PaypalAPI::CatalogProducts.create
PaypalAPI::CatalogProducts.list
PaypalAPI::CatalogProducts.show
PaypalAPI::CatalogProducts.update
Disputes
PaypalAPI::Disputes.appeal
PaypalAPI::Disputes.make_offer
PaypalAPI::Disputes.show
PaypalAPI::Disputes.update
PaypalAPI::Disputes.send_message
PaypalAPI::Disputes.provide_supporting_info
PaypalAPI::Disputes.update_status
PaypalAPI::Disputes.deny_offer
PaypalAPI::Disputes.provide_evidence
PaypalAPI::Disputes.settle
PaypalAPI::Disputes.acknowledge_return_item
PaypalAPI::Disputes.accept_claim
PaypalAPI::Disputes.list
PaypalAPI::Disputes.escalate
PaypalAPI::Disputes.accept_offer
UserInfo
Users
PaypalAPI::Users.create
PaypalAPI::Users.list
PaypalAPI::Users.show
PaypalAPI::Users.update
PaypalAPI::Users.delete
Invoices
PaypalAPI::Invoices.create
PaypalAPI::Invoices.list
PaypalAPI::Invoices.show
PaypalAPI::Invoices.update
PaypalAPI::Invoices.delete
PaypalAPI::Invoices.search
PaypalAPI::Invoices.remind
PaypalAPI::Invoices.delete_refund
PaypalAPI::Invoices.delete_payment
PaypalAPI::Invoices.record_refund
PaypalAPI::Invoices.record_payment
PaypalAPI::Invoices.send_invoice
PaypalAPI::Invoices.cancel
PaypalAPI::Invoices.generate_qr_code
PaypalAPI::Invoices.generate_invoice_number
InvoiceTemplates
PaypalAPI::InvoiceTemplates.create
PaypalAPI::InvoiceTemplates.list
PaypalAPI::InvoiceTemplates.show
PaypalAPI::InvoiceTemplates.update
PaypalAPI::InvoiceTemplates.delete
Payouts
PaypalAPI::Payouts.create
PaypalAPI::Payouts.show
PaypalAPI::PayoutItems.show
PaypalAPI::PayoutItems.cancel
ReferencedPayouts
PaypalAPI::ReferencedPayouts.create
PaypalAPI::ReferencedPayouts.show
PaypalAPI::ReferencedPayoutItems.create
PaypalAPI::ReferencedPayoutItems.show
PartnerReferrals
PaypalAPI::PartnerReferrals.create
PaypalAPI::PartnerReferrals.show
PaymentExperienceWebProfiles
PaypalAPI::PaymentExperienceWebProfiles.create
PaypalAPI::PaymentExperienceWebProfiles.list
PaypalAPI::PaymentExperienceWebProfiles.show
PaypalAPI::PaymentExperienceWebProfiles.replace
PaypalAPI::PaymentExperienceWebProfiles.update
PaypalAPI::PaymentExperienceWebProfiles.delete
TransactionSearch
PaypalAPI::TransactionSearch.list_transactions
PaypalAPI::TransactionSearch.list_all_balances
Development
rubocop
rspec
mdl README.md CHANGELOG.md RELEASE.md
Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/aglushkov/paypal-rest-api.
License
The gem is available as open source under the terms of the MIT License.