Security News
Research
Data Theft Repackaged: A Case Study in Malicious Wrapper Packages on npm
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
creative-pp-adaptive
Advanced tools
A Ruby API for PayPal Adaptive Payments
This gem provides access to PayPal's Adaptive Payments API using easy-to-use ruby classes. The internals are largely backed by Virtus and RestClient, and so are easy to work with.
Via rubygems:
gem install pp-adaptive
All API calls are made by calling #execute
on the client, with the relevant
request type. Naming conventions have been ruby-ized, but you should just
follow along with the PayPal documentation to understand the inputs and outputs.
Some aliases have been added, to make things simpler. Some example scenarios are outlined below, but due to the size of the API, this README does not aim in any way to be an exhaustive reproduction of the official Adaptive Payments documentation. Given the declarative, model-style, nature of the classes in this gem, it probably makes sense to just browse the source to know what fields are available.
The following example should give you enough of an idea to be able to follow your senses and use this gem in your own applications.
A typical checkout payment request starts like this.
require "pp-adaptive"
client = AdaptivePayments::Client.new(
:user_id => "your-api-user-id",
:password => "your-api-password",
:signature => "your-api-signature",
:app_id => "your-app-id",
:sandbox => true
)
client.execute(:Pay,
:action_type => "PAY",
:receiver_email => "your@email.com",
:receiver_amount => 50,
:currency_code => "USD",
:cancel_url => "https://your-site.com/cancel",
:return_url => "https://your-site.com/return"
) 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
redirect_to client.payment_url(response)
else
puts "#{response.ack_code}: #{response.error_message}"
end
end
When the customer is sent back from PayPal to your return_url
, you need to
check if the payment was successful or not.
require "pp-adaptive"
client = AdaptivePayments::Client.new(
:user_id => "your-api-user-id",
:password => "your-api-password",
:signature => "your-api-signature",
:app_id => "your-app-id",
:sandbox => true
)
client.execute(:PaymentDetails, :pay_key => "pay-key-from-pay-request") do |response|
if response.success?
puts "Payment status: #{response.payment_exec_status}"
else
puts "#{response.ack_code}: #{response.error_message}"
end
end
If you need to take a cut of a payment and forward the remainder onto one or more other recipients, you use a chained payment. This is just a regular PayRequest, except it includes multiple receivers, one of which is marked as 'primary'.
require "pp-adaptive"
client = AdaptivePayments::Client.new(
:user_id => "your-api-user-id",
:password => "your-api-password",
:signature => "your-api-signature",
:app_id => "your-app-id",
:sandbox => true
)
client.execute(:Pay,
:action_type => "PAY",
:currency_code => "USD",
:cancel_url => "https://your-site.com/cancel",
:return_url => "https://your-site.com/return",
:receivers => [
{ :email => "your@email.com", :amount => 50, :primary => true },
{ :email => "other@site.tld", :amount => 45 }
]
) 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
redirect_to client.payment_url(response)
else
puts "#{response.ack_code}: #{response.error_message}"
end
end
In the above example, you get $5 from the $50 payment, with the remaining $45 going to other@site.tld.
If you need to be able to take payments from a user's account on-demand, you get the user to authorize you for preapproved payments.
require "pp-adaptive"
client = AdaptivePayments::Client.new(
:user_id => "your-api-user-id",
:password => "your-api-password",
:signature => "your-api-signature",
:app_id => "your-app-id",
:sandbox => false
)
client.execute(:Preapproval,
:ending_date => DateTime.now.next_year,
:starting_date => DateTime.now,
:max_total_amount => BigDecimal("950.00"),
:currency_code => "USD",
:cancel_url => "http://site.com/cancelled",
:return_url => "http://site.com/completed"
) do |response|
# you may alternatively pass a PreapprovalRequest instance
if response.success?
puts "Preapproval key: #{response.preapproval_key}"
# send the user to PayPal to give their approval
# e.g. https://www.paypal.com/webscr?cmd=_ap-preapproval&preapprovalkey=abc
redirect_to client.preapproval_url(response)
else
puts "#{response.ack_code}: #{response.error_message}"
end
end
To take a payment from a user who has previously authorized you for preapproved
payments, just pass the pay_key
in the usual PayRequest.
require "pp-adaptive"
client = AdaptivePayments::Client.new(
:user_id => "your-api-user-id",
:password => "your-api-password",
:signature => "your-api-signature",
:app_id => "your-app-id",
:sandbox => false
)
client.execute(:Pay,
:preapproval_key => "existing-preapproval-key",
:action_type => "PAY",
:receiver_email => "your@email.com",
:receiver_amount => 50,
:currency_code => "USD",
:cancel_url => "https://your-site.com/cancel",
:return_url => "https://your-site.com/return"
) do |response|
if response.success?
puts "Pay key: #{response.pay_key}"
puts "Status: #{response.payment_exec_status}"
else
puts "#{response.ack_code}: #{response.error_message}"
end
end
If you have the pay_key from a previously made payment (up to 60 days), you can send a RefundRequest.
require "pp-adaptive"
client = AdaptivePayments::Client.new(
:user_id => "your-api-user-id",
:password => "your-api-password",
:signature => "your-api-signature",
:app_id => "your-app-id",
:sandbox => false
)
client.execute(:Refund, :pay_key => "the-pay-key") do |response|
if response.success?
puts "Refund sent"
else
puts "#{response.ack_code}: #{response.error_message}"
end
end
You can also do partial refunds by passing an amount
field in the request.
Mobile checkouts uses the Mobile Express Checkout setup, an alternative to adaptive API.
The difference is that there is a first POST to paypal to retrieve a token, then a redirect to PayPal, and finally, another POST to paypal after user accepts the payment to finalize the deal.
Please consult client.rb#express_handshake
and client.rb#express_perform
for details on what options to pass and what to expect in response.
A TODO is to wrap the response strings in more functional response objects.
# 1. initialize @client with the optional :checkout_type attribute
@client = AdaptivePayments::Client.new(
:user_id => "your-api-user-id",
:password => "your-api-password",
:signature => "your-api-signature",
:app_id => "your-app-id",
:sandbox => true,
:checkout_type => "mobile" # this does the trick
)
# 2. call the handshake method and obtain the token
@client.express_handshake(options) do |response|
token = response.split("&").select{|param| param["TOKEN="].present?}.first.split("=").last
if token.present?
# do some transaction logging
redirect_to @client.express_checkout_url(token))
else
render json: response
end
end
# User does his thing at PayPal here
# 3. process PayPal return GET parameters at success_path
@client.express_perform(options)) do |response|
success = response["ACK=Success"].present? ? true : false
# use success boolean to process logging, order updates
end
Adaptive Payments is a very extensive API with a lot of endpoints and a lot of fields within each request. It wouldn't be wise to attempt to document them all here, but the official API documentation covers everything in detail.
Just ruby-ize the fields (i.e. underscores, not camel case) and open up the request/response classes in this repository to get a feel for how this all works.
Copyright © Flippa.com Pty Ltd. Licensed under the MIT license. See the LICENSE file for details.
FAQs
Unknown package
We found that creative-pp-adaptive demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 4 open source maintainers 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.
Security News
Research
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
Research
Security News
Attackers used a malicious npm package typosquatting a popular ESLint plugin to steal sensitive data, execute commands, and exploit developer systems.
Security News
The Ultralytics' PyPI Package was compromised four times in one weekend through GitHub Actions cache poisoning and failure to rotate previously compromised API tokens.