
Company News
Socket Has Acquired Secure Annex
Socket has acquired Secure Annex to expand extension security across browsers, IDEs, and AI tools.
paypal-rest-api
Advanced tools
bundle add paypal-rest-api
# in config/initializers/paypal_rest_api.rb
PaypalAPI.client = PaypalAPI::Client.new(
client_id: ENV['PAYPAL_CLIENT_ID'],
client_secret: ENV['PAYPAL_CLIENT_SECRET'],
live: ENV['PAYPAL_LIVE'] == 'true'
)
# Use this API to create order
PaypalAPI::Orders.create(body: body, headers: {prefer: 'return=representation'})
# After customer approves order, we can tell PayPal to authorize it
PaypalAPI::Orders.authorize(order_id)
# After PayPal authorizes order, we can capture it
PaypalAPI::AuthorizedPayments.capture(authorization_id)
# After payment was captured, we can refund it
PaypalAPI::CapturedPayments.refund(capture_id, body: payload, headers: headers)
# Use this APIs to register/actualize PayPal Webhooks
PaypalAPI::Webhooks.list
PaypalAPI::Webhooks.create(body: body)
PaypalAPI::Webhooks.update(webhook_id, body: body)
PaypalAPI::Webhooks.delete(webhook_id)
# Anywhere in your business logic
client = PaypalAPI::Client.new(
client_id: ENV['PAYPAL_CLIENT_ID'],
client_secret: ENV['PAYPAL_CLIENT_SECRET'],
live: ENV['PAYPAL_LIVE'] == 'true'
)
# Show order
client.orders.show(order_id)
# Create order
client.orders.create(body: body)
If you want to request some undocumented APIs (or some forgotten API):
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)
# Or, using per-request client:
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)
PaypalAPI.client = PaypalAPI::Client.new(
live: ENV['PAYPAL_LIVE'] == 'true',
client_id: ENV['PAYPAL_CLIENT_ID'],
client_secret: ENV['PAYPAL_CLIENT_SECRET']
)
PaypalAPI.live? # => false
PaypalAPI.sandbox? # => true
PaypalAPI.api_url # => "https://api-m.sandbox.paypal.com"
PaypalAPI.web_url # => "https://sandbox.paypal.com"
Response object is returned after each API request.
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 responseresponse.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 keyresponse.success? - checks HTTP status code is 2xxresponse.failed? - checks HTTP status code is not 2xxresponse.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 itemPaypalAPI client accepts this additional options:
:live:retries:http_opts:cache:livePaypalAPI 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: ENV['PAYPAL_LIVE'] == 'true'
# ...
)
:retriesThis 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]}
# ...
)
:http_optsThis 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}
# ...
)
:cacheThis option can be added to save webhook-validating certificates between
redeploys to validate webhooks offline. By default this gem has only in-memory
caching. The cache object must respond to standard for caching #fetch 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 pageExample:
PaypalAPI::WebhookEvents.list(page_size: 25).each_page do |response|
# ...
end
PaypalAPI::WebhookEvents.list(page_size: 25).each_page_item(:events) do |hash|
# ...
end
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 a Rails controller with a webhook verification:
class Webhooks::PaypalController < ApplicationController
def create
# PayPal registered webhook ID for current URL
webhook_id = ENV['PAYPAL_WEBHOOK_ID']
headers = request.headers # must be a Hash
raw_body = request.raw_post # must be a raw String body
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
Paypal::API client allows to subscribe to this callbacks:
: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 errorEach 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, retries_count and
retry_number options by default.
On :after_fail and :after_network_error there are also the
:will_retry boolean option.
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
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
PaypalAPI::Errors::BadRequestPaypalAPI::Errors::UnauthorizedPaypalAPI::Errors::ForbiddenPaypalAPI::Errors::NotFoundPaypalAPI::Errors::MethodNotAllowedPaypalAPI::Errors::NotAcceptablePaypalAPI::Errors::ConflictPaypalAPI::Errors::UnsupportedMediaTypePaypalAPI::Errors::UnprocessableEntityPaypalAPI::Errors::TooManyRequestsPaypalAPI::Errors::FatalError
PaypalAPI::Errors::InternalServerErrorPaypalAPI::Errors::ServiceUnavailableAll 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
}
)
# `error.request` and `error.response` methods can be used also
end
All API endpoints accept query:, body: and headers: optional keyword
Hash parameters. So this parameters can be omitted in next docs.
query - Hash with request query paramsbody - Hash with request body paramsheaders - Hash with request headersPaypalAPI::ShipmentTracking.add(body: body)PaypalAPI::ShipmentTracking.update(id, body: body)PaypalAPI::ShipmentTracking.show(id)PaypalAPI::CatalogProducts.create(body: body)PaypalAPI::CatalogProducts.listPaypalAPI::CatalogProducts.show(product_id)PaypalAPI::CatalogProducts.update(product_id, body: body)PaypalAPI::Disputes.settle(id)PaypalAPI::Disputes.update_status(id)PaypalAPI::Disputes.escalate(id)PaypalAPI::Disputes.accept_offer(id)PaypalAPI::Disputes.listPaypalAPI::Disputes.provide_supporting_info(id)PaypalAPI::Disputes.show(id)PaypalAPI::Disputes.update(id)PaypalAPI::Disputes.deny_offer(id)PaypalAPI::Disputes.make_offer(id)PaypalAPI::Disputes.appeal(id)PaypalAPI::Disputes.provide_evidence(id)PaypalAPI::Disputes.acknowledge_return_itemPaypalAPI::Disputes.send_message(id)PaypalAPI::Disputes.accept_claim(id)PaypalAPI::UserInfo.showPaypalAPI::Users.createPaypalAPI::Users.listPaypalAPI::Users.update(id)PaypalAPI::Users.show(id)PaypalAPI::Users.delete(id)PaypalAPI::Invoices.createPaypalAPI::Invoices.listPaypalAPI::Invoices.send_invoice(invoice_id)PaypalAPI::Invoices.remind(invoice_id)PaypalAPI::Invoices.cancel(invoice_id)PaypalAPI::Invoices.record_payment(invoice_id)PaypalAPI::Invoices.delete_payment(invoice_id)PaypalAPI::Invoices.record_refund(invoice_id)PaypalAPI::Invoices.delete_refund(invoice_id)PaypalAPI::Invoices.generate_qr_code(invoice_id)PaypalAPI::Invoices.generate_invoice_numberPaypalAPI::Invoices.show(invoice_id)PaypalAPI::Invoices.update(invoice_id)PaypalAPI::Invoices.delete(invoice_id)PaypalAPI::Invoices.searchPaypalAPI::InvoiceTemplates.createPaypalAPI::InvoiceTemplates.listPaypalAPI::InvoiceTemplates.show(template_id)PaypalAPI::InvoiceTemplates.update(template_id)PaypalAPI::InvoiceTemplates.delete(template_id)PaypalAPI::Orders.createPaypalAPI::Orders.show(order_id)PaypalAPI::Orders.update(order_id)PaypalAPI::Orders.confirm(order_id)PaypalAPI::Orders.authorize(order_id)PaypalAPI::Orders.capture(order_id)PaypalAPI::Orders.track(order_id)PaypalAPI::Orders.update_tracker(order_id, tracker_id)PaypalAPI::OrdersV1.createPaypalAPI::OrdersV1.show(order_id)PaypalAPI::OrdersV1.cancel(order_id)PaypalAPI::OrdersV1.pay(order_id)PaypalAPI::PartnerReferrals.createPaypalAPI::PartnerReferrals.show(partner_referral_id)PaypalAPI::PaymentExperienceWebProfiles.createPaypalAPI::PaymentExperienceWebProfiles.listPaypalAPI::PaymentExperienceWebProfiles.showPaypalAPI::PaymentExperienceWebProfiles.replacePaypalAPI::PaymentExperienceWebProfiles.updatePaypalAPI::PaymentExperienceWebProfiles.deletePaypalAPI::PaymentTokens.createPaypalAPI::PaymentTokens.listPaypalAPI::PaymentTokens.show(id)PaypalAPI::PaymentTokens.delete(id)PaypalAPI::SetupTokens.createPaypalAPI::SetupTokens.show(setup_token_id)PaypalAPI::AuthorizedPayments.show(authorization_id)PaypalAPI::AuthorizedPayments.capture(authorization_id)PaypalAPI::AuthorizedPayments.reauthorize(authorization_id)PaypalAPI::AuthorizedPayments.void(authorization_id)PaypalAPI::CapturedPayments.show(capture_id)PaypalAPI::CapturedPayments.refund(capture_id)PaypalAPI::Refunds.show(refund_id)PaypalAPI::Payouts.createPaypalAPI::Payouts.show(payout_id)PaypalAPI::PayoutItems.show(payout_item_id)PaypalAPI::PayoutItems.cancel(payout_item_id)PaypalAPI::ReferencedPayouts.createPaypalAPI::ReferencedPayouts.show(payouts_batch_id)PaypalAPI::ReferencedPayoutItems.createPaypalAPI::ReferencedPayoutItems.show(payouts_item_id)PaypalAPI::Subscriptions.createPaypalAPI::Subscriptions.show(id)PaypalAPI::Subscriptions.update(id)PaypalAPI::Subscriptions.revise(id)PaypalAPI::Subscriptions.suspend(id)PaypalAPI::Subscriptions.cancel(id)PaypalAPI::Subscriptions.activate(id)PaypalAPI::Subscriptions.capture(id)PaypalAPI::Subscriptions.transactions(id)PaypalAPI::SubscriptionPlans.createPaypalAPI::SubscriptionPlans.listPaypalAPI::SubscriptionPlans.show(plan_id)PaypalAPI::SubscriptionPlans.update(plan_id)PaypalAPI::SubscriptionPlans.activate(plan_id)PaypalAPI::SubscriptionPlans.deactivate(plan_id)PaypalAPI::SubscriptionPlans.update_pricing(plan_id)PaypalAPI::TransactionSearch.list_transactionsPaypalAPI::TransactionSearch.list_all_balancesPaypalAPI::Webhooks.createPaypalAPI::Webhooks.listPaypalAPI::Webhooks.show(webhook_id)PaypalAPI::Webhooks.update(webhook_id)PaypalAPI::Webhooks.delete(webhook_id)PaypalAPI::Webhooks.event_types(webhook_id)PaypalAPI::Webhooks.verifyPaypalAPI::WebhookEvents.availablePaypalAPI::WebhookEvents.listPaypalAPI::WebhookEvents.show(event_id)PaypalAPI::WebhookEvents.resend(event_id)PaypalAPI::WebhookEvents.simulatePaypalAPI::WebhookLookups.createPaypalAPI::WebhookLookups.listPaypalAPI::WebhookLookups.show(webhook_lookup_id)PaypalAPI::WebhookLookups.delete(webhook_lookup_id) rubocop
rspec
mdl README.md CHANGELOG.md RELEASE.md
Bug reports and pull requests are welcome on GitHub at https://github.com/aglushkov/paypal-rest-api.
The gem is available as open source under the terms of the MIT License.
FAQs
Unknown package
We found that paypal-rest-api demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Company News
Socket has acquired Secure Annex to expand extension security across browsers, IDEs, and AI tools.

Research
/Security News
Socket is tracking cloned Open VSX extensions tied to GlassWorm, with several updated from benign-looking sleepers into malware delivery vehicles.

Product
Reachability analysis for PHP is now available in experimental, helping teams identify which vulnerabilities are actually exploitable.