Simple and flexible CloudPayments API client. (https://developers.cloudpayments.ru/, https://developers.cloudpayments.ru/en/)
It's a kind of fork of the cloud_payments
gem. The main idea to build a simple and flexible client.
So what is the difference:
CloudPay
- has fewer dependencies. It depends on only the
faraday
gem. - doesn't contain value objects for responses. Now, it is the responsibility of an app to provide value objects for all responses.
- has a flexible system of configuration.
- provides an ability to send idempotent requests.
- allows using of namespaces as standalone classes.
- provides a unified interface for API methods.
- has two ways to handle errors: with exceptions and result object.
Add this line to your application's Gemfile:
gem 'cloud_pay'
And then execute:
$ bundle install
Or install it yourself as:
$ gem install cloud_pay
We can have a few configuration presets. It's easy to define using the CloudPay.configure
method. Example:
CloudPay.configure do |c|
# default configuration preset
end
CloudPay.confugure(:preset_name) do |c|
# named configuration preset
end
Then, we can select the needed config when we are creating a client:
config = CloudPay::Config.new(host: '...')
CloudPay.client # Client with default config
CloudPay.client(:preset_name) # Client with predefined config
CloudPay::Client.new
CloudPay::Client.new(:preset_name)
Also, we can configure the client using an instance of the CloudPay::Config
class or hash.
config = CloudPay::Config.new(host: '...')
CloudPay.client(host: '...')
CloudPay.client(config)
CloudPay::Client.new(host: '...')
CloudPay::Client.new(config)
This way to resolve config is also applied to all API namespaces and webhooks handler.
# Namespaces
CloudPay::Payments::Tokens.new
CloudPay::Payments::Tokens.new(:preset_name)
CloudPay::Payments::Tokens.new(host: '...')
CloudPay::Payments::Tokens.new(config)
# Webhooks
CloudPay::Webhooks.new
CloudPay::Webhooks.new(:preset_name)
CloudPay::Webhooks.new(host: '...')
CloudPay::Webhooks.new(config)
It's also possible to switch the default config for some block of code. It can be used in middlewares, for example.
CloudPay.with_config(Rails.env.to_sym) do
CloudPay.client # Client with `:development`, `:test` or `:production` config
end
class CloudPayMiddleware
def call(env)
config = config_name(env)
CloudPay.with_config(config) do
@app.call(env)
end
end
private
def config_name(env)
# choose predefined config name according to some conditions
end
end
The example above allows switching configs according to some conditions: domain name, headers, or some other. So when we use CloudPay::Client.new
in the app, it will use config defined in the middleware.
It's also possible to use a simple hash to initialize the client (or any namespace). Such a solution allows implementing multitenancy systems when the config is a part of application data and cannot be predefined in the config stage.
CloudPay::Client.new(public_key: '...', secret_key: '...')
Note: All undefined options will be inherit from default config when we use hash as a config. That means we can override some options ad-hoc:
CloudPay::Client.new(
public_key: '...',
secret_key: '...'
).config
# #<CloudPay::Config:0x00007f8fd1af04e8
# @connection_block=nil,
# @connection_options={},
# @host="https://api.cloudpayments.ru",
# @log=false,
# @public_key="...",
# @secret_key="...">
CloudPay.with_config(:test) do
CloudPay::Client.new(
public_key: '...',
secret_key: '...'
).config
# #<CloudPay::Config:0x00007f8fd1af04e8
# @connection_block=nil,
# @connection_options={},
# @host="http://localhost:9292",
# @log=false,
# @public_key="...",
# @secret_key="...">
end
There are two ways to handle errors in the gem: using the result object and using exceptions. We use exceptions when we call a method with an exclamation mark (!
), and we use the result object otherwise.
The result object has a simple interface that consists of only three meaningful methods: success?
, model
, and error_message
. Every API method returns this result object so that we can check the result of the request. Look at the example below:
result = CloudPay.client.payments.cards.charge(params)
if result.success?
# do something with data using the `model` method.
# pp result.model
else
# Handle an error.
# You can use the `error_message` method to get a real error message or
# You can use the `model` method to get response data.
# pp result.model
# pp result.error_message
end
Using an exclamation mark forces the client to raise an exception in case of an unsuccessful response. The specific exception depends on the type of response.
-
It can be a
CloulPay::ValidationError
exception if the request has missing attributes. -
It can be an exception inherited from
CloudPay::ServerError
in theCloudPay::HttpErrors
namespace if an HTTP request is ended with any status code more than or equal to 300. -
It can be an exception inherited from
CloudPay::ReasonedGatewayError
inCloudPay::GatewayErrors
namespace ifReasonCode
key in theModel
object has some meaningful code. -
it can be a
CloudPay::GatewayError
exception if the gem cannot determine a specific reason for an error.
It can also raise other exceptions that depend on the HTTP transport, such as faraday's errors or network errors.
begin
model = CloudPay.client.payments.cards.charge!(params, idempotency_key: idempotency_key)
# do something with data
rescue CloudPay::ValidationError => e
# handle validation errors
rescue *CloudPay.retryable_errors => e
# put in queue to retry later
rescue CloudPay::Error => e
# handle the rest of errors
end
The gem provides a list of retryable errors. This list is needed to catch exceptions that can be the reason for retrying the request. We can redefine this list using the CloudPay.set_retryable_errors
method.
CloudPay.set_retryable_errors([
CloudPay::GatewayErrors::FormatError,
CloudPay::GatewayErrors::InsufficientFunds,
CloudPay::GatewayErrors::Timeout,
CloudPay::GatewayErrors::CannotReachNetwork,
CloudPay::GatewayErrors::SystemError,
Faraday::ConnectionFailed,
Faraday::TimeoutError
])
Note: Use the idempotency_key
option if you intend to retry requests. It can lead to multiple write-offs of funds if you retry your request without the idempotency key.
CloudPay.client.payments.cards.charge(params, idempotency_key: 'some-unique-id')
It should be an unique value for every separate payment.
This gem supports the following API methods:
-
Test method - The method to test the interaction with the API and returns
true
orfalse
.CloudPay.client.test
-
Payment by a Cryptogram - The method to make a payment by a cryptogram generated by the Checkout script, Apple Pay, or Google Pay.
CloudPay.client.payments.cards.charge(params) CloudPay.client.payments.cards.auth(params) # or ns = CloudPay::Payments::Cards.new ns.charge(params) ns.auth(params)
-
3-D Secure Processing - The method to complete the payment when 3-D Secure authentication is used.
CloudPay.client.payments.cards.post3ds(params) # or ns = CloudPay::Payments::Cards.new ns.post3ds(id, params)
-
Payment by a Token (Recurring) - The method to make a payment by a token received either with payment by cryptogram or via Pay notification.
CloudPay.client.payments.tokens.charge(params) CloudPay.client.payments.tokens.auth(params) # or ns = CloudPay::Payments::Tokens.new ns.charge(params) ns.auth(params)
-
Payment Confirmation - For payments made by the DMS scheme, you need to confirm a transaction. Confirmation can be done through the Back office or via calling this API method.
CloudPay.client.payments.confirm(params) # or ns = CloudPay::Payments.new ns.confirm(params)
-
Payment Cancellation - Cancellation of payment can be executed through your Back Office or by calling the API method.
CloudPay.client.payments.void(id) # or ns = CloudPay::Payments.new ns.void(id)
It has a
cancel
alias.CloudPay.client.payments.cancel(id) # or ns = CloudPay::Payments.new ns.cancel(id)
-
Refund - Refund can be executed through your Back Office or by calling the API method.
CloudPay.client.payments.refund(id, params) # or ns = CloudPay::Payments.new ns.refund(id, params)
-
Payout by a Cryptogram - Payment by a cryptogram can be executed through the calling of this API method.
CloudPay.client.payments.cards.topup(params) # or ns = CloudPay::Payments::Cards.new ns.topup(params)
-
Payout by a Token - Payout by a token can be executed through the calling of the following API method.
CloudPay.client.payments.tokens.topup(params) # or ns = CloudPay::Payments::Tokens.new ns.topup(params)
-
Transaction Details - The method returns a transaction details.
CloudPay.client.payments.get(id) # or ns = CloudPay::Payments.new ns.get(id)
-
Payment Status Check - The method for payment searching, which returns its status.
CloudPay.client.payments.find(invoice_id) # or ns = CloudPay::Payments.new ns.find(invoice_id)
This method has two versions. It performs first version by default. However, you can specify the version as an
:version
option in the last argument:CloudPay.client.payments.find(invoice_id, version: 2) # or ns = CloudPay::Payments.new ns.find(invoice_id, version: 2)
-
Transaction List - The method to get a list of transactions for a day.
CloudPay.client.payments.list(params) # or ns = CloudPay::Payments.new ns.list(params)
-
Token List - The method to get a list of all payment tokens of CloudPayments.
CloudPay.client.payments.tokens.list # or ns = CloudPay::Payments::Tokens.new ns.list
-
Creation of Subscriptions on Recurrent Payments - The method to create subscriptions on recurrent payments.
CloudPay.client.subscriptions.create(params) # or ns = CloudPay::Subscriptions.new ns.create(params)
-
Subscription Details - The method to get an information about subscription status.
CloudPay.client.subscriptions.get(id) # or ns = CloudPay::Subscriptions.new ns.get(id)
-
Subscriptions Search - The method to get a list of subscriptions for a particular account.
CloudPay.client.subscriptions.find(account_id) # or ns = CloudPay::Subscriptions.new ns.find(account_id)
-
Recurrent Payments Subscription Change - The method to change a subscription on recurrent payments.
CloudPay.client.subscriptions.update(params) # or ns = CloudPay::Subscriptions.new ns.update(params)
-
Subscription on Recurrent Payments Cancellation - The method to cancel subscription on recurrent payments.
CloudPay.client.subscriptions.cancel(id) # or ns = CloudPay::Subscriptions.new ns.cancel(id)
-
Invoice Creation on Email - The method to generate a payment link and sending it to a payer's email.
CloudPay.client.orders.create(params) # or ns = CloudPay::Orders.new ns.create(params)
-
Created Invoice Cancellation - The method to create invoice cancellation.
CloudPay.client.orders.cancel(id) # or ns = CloudPay::Orders.new ns.cancel(id)
-
View of Notification Settings - The method to view notification settings.
CloudPay.client.notifications.get(type) # or ns = CloudPay::Notifications.new ns.get(type)
-
Change of Notification Settings - The method to change notification settings.
CloudPay.client.notifications.update(type, params) # or ns = CloudPay::Notifications.new ns.update(type, params)
-
Start of Apple Pay Session - Start of a session is required to take payments via Apple Pay on Web.
CloudPay.client.apple_pay.start_session(params) # or ns = CloudPay::ApplePay.new ns.start_session(params)
-
CloudKassir: Register Fiscalization - The method of launching a cash register to the fiscal operation mode.
CloudPay.client.kassir.fiscalize(params) # or ns = CloudPay::Kassir.new ns.fiscalize(params)
-
CloudKassir: Online Receipt Generation - Method of an online receipt generation.
CloudPay.client.kassir.receipt.create(params) # or ns = CloudPay::Kassir::Receipt.new ns.create(params)
-
CloudKassir: Receipt Status Request - Method of getting the receipt status.
CloudPay.client.kassir.receipt.status(id) # or ns = CloudPay::Kassir::Receipt.new ns.status(id)
-
CloudKassir: Receipt Details Request - Method of getting the receipt details.
CloudPay.client.kassir.receipt.get(id) # or ns = CloudPay::Kassir::Receipt.new ns.get(id)
-
CloudKassir: Cash Register State Change - The method of manual control of the state of the cash register. The cash register can be turned off (for maintenance) and activated (put into operation).
CloudPay.client.kassir.state.update(params) # or ns = CloudPay::Kassir::State.new ns.update(params)
-
CloudKassir: Receiving Cash Register Data - Method of receiving cash register data.
CloudPay.client.kassir.state.get(params) # or ns = CloudPay::Kassir::State.new ns.get(params)
- Coming soon -
Bug reports and pull requests are welcome on GitHub at https://github.com/undr/cloud_pay.