solidgate-ruby-sdk 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.rspec +1 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +31 -0
- data/Gemfile +12 -0
- data/Gemfile.lock +37 -0
- data/LICENSE.txt +21 -0
- data/README.md +96 -0
- data/Rakefile +10 -0
- data/examples/basic_usage.rb +88 -0
- data/lib/solidgate/client.rb +247 -0
- data/lib/solidgate/configuration.rb +77 -0
- data/lib/solidgate/errors.rb +52 -0
- data/lib/solidgate/payment.rb +99 -0
- data/lib/solidgate/version.rb +5 -0
- data/lib/solidgate/webhook.rb +20 -0
- data/lib/solidgate.rb +40 -0
- data/solidgate.gemspec +43 -0
- metadata +92 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: ea4cb6a76307e222aa86375dba8378cf9591d8d66e1512805f5cab04466ab8d2
|
|
4
|
+
data.tar.gz: 8ee67abaf5dbb9fb2ad4fe00824d8da9f2ba776673322c26de61a1cd5f44c248
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: db9bd5aeb1d91aad532f50f327059c00251fb9cb6bf8d68c373281dec6633800e33393a0ac1b757858b42604800df23dddd58e41feea498fa135ffaeee3207cc
|
|
7
|
+
data.tar.gz: a82dc9a15b3997cc5fdc34fc587e8ebb3b2eba3887984117a4ce7c8d72af020b86177b7ffc5662d0ac42afb77d948f1c843aeae334de7b87e9809ad343047ad5
|
data/.rspec
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
--require spec_helper
|
data/.ruby-version
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
2.7.6
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [0.1.0] - 2025-12-18
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- Initial release of the Solidgate Ruby SDK
|
|
14
|
+
- Payment creation, retrieval, capture, void, and refund functionality
|
|
15
|
+
- Subscription management (basic structure)
|
|
16
|
+
- Comprehensive error handling with specific error classes
|
|
17
|
+
- Configuration management for API credentials and environment settings
|
|
18
|
+
- HTTP client with proper authentication and signature generation
|
|
19
|
+
- Support for both sandbox and production environments
|
|
20
|
+
- RSpec test suite foundation
|
|
21
|
+
|
|
22
|
+
### Features
|
|
23
|
+
- Support for all major payment operations
|
|
24
|
+
- Automatic request signing using SHA-1 + Base64
|
|
25
|
+
- Configurable timeouts and connection settings
|
|
26
|
+
- Detailed error messages and codes
|
|
27
|
+
- Thread-safe configuration
|
|
28
|
+
- Comprehensive documentation and examples
|
|
29
|
+
|
|
30
|
+
[Unreleased]: https://github.com/cgtrader/solidgate-ruby-sdk/compare/v0.1.0...HEAD
|
|
31
|
+
[0.1.0]: https://github.com/cgtrader/solidgate-ruby-sdk/releases/tag/v0.1.0
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
PATH
|
|
2
|
+
remote: .
|
|
3
|
+
specs:
|
|
4
|
+
solidgate-ruby-sdk (0.1.0)
|
|
5
|
+
faraday
|
|
6
|
+
faraday-multipart
|
|
7
|
+
|
|
8
|
+
GEM
|
|
9
|
+
remote: https://rubygems.org/
|
|
10
|
+
specs:
|
|
11
|
+
base64 (0.3.0)
|
|
12
|
+
coderay (1.1.3)
|
|
13
|
+
faraday (2.8.1)
|
|
14
|
+
base64
|
|
15
|
+
faraday-net_http (>= 2.0, < 3.1)
|
|
16
|
+
ruby2_keywords (>= 0.0.4)
|
|
17
|
+
faraday-multipart (1.1.1)
|
|
18
|
+
multipart-post (~> 2.0)
|
|
19
|
+
faraday-net_http (3.0.2)
|
|
20
|
+
method_source (1.1.0)
|
|
21
|
+
multipart-post (2.4.1)
|
|
22
|
+
pry (0.15.2)
|
|
23
|
+
coderay (~> 1.1)
|
|
24
|
+
method_source (~> 1.0)
|
|
25
|
+
rake (13.3.1)
|
|
26
|
+
ruby2_keywords (0.0.5)
|
|
27
|
+
|
|
28
|
+
PLATFORMS
|
|
29
|
+
ruby
|
|
30
|
+
|
|
31
|
+
DEPENDENCIES
|
|
32
|
+
pry
|
|
33
|
+
rake (~> 13.0)
|
|
34
|
+
solidgate-ruby-sdk!
|
|
35
|
+
|
|
36
|
+
BUNDLED WITH
|
|
37
|
+
1.17.2
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 CGTrader
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# Solidgate Ruby SDK
|
|
2
|
+
|
|
3
|
+
A Ruby (unofficial) SDK for integrating with the Solidgate payment gateway API.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
Add this line to your application's Gemfile:
|
|
8
|
+
|
|
9
|
+
```ruby
|
|
10
|
+
gem 'solidgate'
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
And then execute:
|
|
14
|
+
|
|
15
|
+
$ bundle install
|
|
16
|
+
|
|
17
|
+
Or install it yourself as:
|
|
18
|
+
|
|
19
|
+
$ gem install solidgate
|
|
20
|
+
|
|
21
|
+
## Configuration
|
|
22
|
+
|
|
23
|
+
Configure the SDK with your Solidgate credentials in an initializer file:
|
|
24
|
+
|
|
25
|
+
```ruby
|
|
26
|
+
Solidgate.configure do |config|
|
|
27
|
+
config.public_key = 'your_public_key'
|
|
28
|
+
config.private_key = 'your_private_key'
|
|
29
|
+
config.webhook_public_key = 'your_webhook_public_key'
|
|
30
|
+
config.webhook_private_key = 'your_webhook_private_key'
|
|
31
|
+
config.sandbox = true
|
|
32
|
+
end
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Usage
|
|
36
|
+
|
|
37
|
+
### Creating a Payment
|
|
38
|
+
|
|
39
|
+
```ruby
|
|
40
|
+
client = Solidgate::Client.new
|
|
41
|
+
|
|
42
|
+
payment_intent = {
|
|
43
|
+
order_id: 'order_id_123', # Unique order identifier provided by the merchant
|
|
44
|
+
product_id: 'product_id_456', # Product identifier generated in Solidgate Dashboard
|
|
45
|
+
customer_account_id: 'customer_789', # Unique customer identifier provided by the merchant
|
|
46
|
+
order_description: 'Premium package',
|
|
47
|
+
type: 'auth',
|
|
48
|
+
settle_interval: 0, # delay in hours for automatic settlement, 0 means immediate settlement
|
|
49
|
+
retry_attempt: 3,
|
|
50
|
+
language: I18n.locale # language to render the payment form
|
|
51
|
+
}.to_json
|
|
52
|
+
|
|
53
|
+
encrypted_payment_intent = client.generate_intent(payment_intent)
|
|
54
|
+
|
|
55
|
+
# use the payment_data in your FE to render the payment form
|
|
56
|
+
|
|
57
|
+
payment_data = {
|
|
58
|
+
merchant: Solidgate.configuration.public_key,
|
|
59
|
+
signature: SolidgateClient.generate_signature(encrypted_payment_intent),
|
|
60
|
+
paymentIntent: encrypted_payment_intent
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Handling Webhooks
|
|
65
|
+
|
|
66
|
+
```ruby
|
|
67
|
+
payload = request.body.read
|
|
68
|
+
Solidgate::Webhook.new.validate_signature(payload, request.headers['Signature']) # returns true/false to verify the webhook
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
## Available Error Classes
|
|
73
|
+
|
|
74
|
+
- `Solidgate::Error` - Base error class
|
|
75
|
+
- `Solidgate::ConfigurationError` - Configuration issues
|
|
76
|
+
- `Solidgate::ValidationError` - Parameter validation errors
|
|
77
|
+
- `Solidgate::AuthenticationError` - Authentication failures
|
|
78
|
+
- `Solidgate::InvalidRequestError` - Invalid request parameters
|
|
79
|
+
- `Solidgate::APIError` - General API errors
|
|
80
|
+
- `Solidgate::ConnectionError` - Network connectivity issues
|
|
81
|
+
- `Solidgate::TimeoutError` - Request timeout
|
|
82
|
+
- `Solidgate::RateLimitError` - Rate limit exceeded
|
|
83
|
+
|
|
84
|
+
## Development
|
|
85
|
+
|
|
86
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
|
87
|
+
|
|
88
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
|
89
|
+
|
|
90
|
+
## Contributing
|
|
91
|
+
|
|
92
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/cgtrader/solidgate-ruby-sdk.
|
|
93
|
+
|
|
94
|
+
## License
|
|
95
|
+
|
|
96
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Example usage of the Solidgate Ruby SDK
|
|
5
|
+
|
|
6
|
+
require_relative "../lib/solidgate"
|
|
7
|
+
|
|
8
|
+
# Configure the SDK
|
|
9
|
+
Solidgate.configure do |config|
|
|
10
|
+
config.public_key = ENV["SOLIDGATE_PUBLIC_KEY"] || "your_public_key_here"
|
|
11
|
+
config.private_key = ENV["SOLIDGATE_PRIVATE_KEY"] || "your_private_key_here"
|
|
12
|
+
config.sandbox = true # Use sandbox for testing
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
begin
|
|
16
|
+
# Create a payment instance
|
|
17
|
+
payment = Solidgate::Payment.new
|
|
18
|
+
|
|
19
|
+
puts "=== Creating a Payment ==="
|
|
20
|
+
|
|
21
|
+
# Create a new payment
|
|
22
|
+
payment_params = {
|
|
23
|
+
order_id: "example_order_#{Time.now.to_i}",
|
|
24
|
+
amount: 1000, # $10.00 in cents
|
|
25
|
+
currency: "USD",
|
|
26
|
+
card_data: {
|
|
27
|
+
number: "4111111111111111", # Test card number
|
|
28
|
+
exp_month: "12",
|
|
29
|
+
exp_year: "2025",
|
|
30
|
+
cvv: "123"
|
|
31
|
+
},
|
|
32
|
+
customer: {
|
|
33
|
+
email: "customer@example.com",
|
|
34
|
+
first_name: "John",
|
|
35
|
+
last_name: "Doe"
|
|
36
|
+
},
|
|
37
|
+
description: "Test payment from Ruby SDK example"
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
payment_response = payment.create(payment_params)
|
|
41
|
+
puts "Payment created successfully!"
|
|
42
|
+
puts "Payment ID: #{payment_response['payment_id']}"
|
|
43
|
+
puts "Status: #{payment_response['status']}"
|
|
44
|
+
|
|
45
|
+
payment_id = payment_response["payment_id"]
|
|
46
|
+
|
|
47
|
+
# Get payment details
|
|
48
|
+
puts "\n=== Getting Payment Details ==="
|
|
49
|
+
payment_details = payment.get(payment_id)
|
|
50
|
+
puts "Payment Amount: #{payment_details['amount']}"
|
|
51
|
+
puts "Payment Status: #{payment_details['status']}"
|
|
52
|
+
|
|
53
|
+
# Capture the payment (if it was authorized)
|
|
54
|
+
if payment_details["status"] == "authorized"
|
|
55
|
+
puts "\n=== Capturing Payment ==="
|
|
56
|
+
capture_response = payment.capture(payment_id)
|
|
57
|
+
puts "Payment captured successfully!"
|
|
58
|
+
puts "Capture Status: #{capture_response['status']}"
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Example of partial refund
|
|
62
|
+
if payment_details["status"] == "captured" || payment_details["status"] == "succeeded"
|
|
63
|
+
puts "\n=== Refunding Payment (Partial) ==="
|
|
64
|
+
refund_response = payment.refund(payment_id, amount: 500, reason: "Partial refund example")
|
|
65
|
+
puts "Refund processed successfully!"
|
|
66
|
+
puts "Refund Amount: #{refund_response['amount']}"
|
|
67
|
+
puts "Refund Status: #{refund_response['status']}"
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
rescue Solidgate::ValidationError => e
|
|
71
|
+
puts "❌ Validation Error: #{e.message}"
|
|
72
|
+
puts "Errors: #{e.errors}"
|
|
73
|
+
rescue Solidgate::AuthenticationError => e
|
|
74
|
+
puts "❌ Authentication Error: #{e.message}"
|
|
75
|
+
puts "Please check your API credentials"
|
|
76
|
+
rescue Solidgate::APIError => e
|
|
77
|
+
puts "❌ API Error: #{e.message}"
|
|
78
|
+
puts "Error Code: #{e.code}" if e.code
|
|
79
|
+
puts "HTTP Status: #{e.http_status}" if e.http_status
|
|
80
|
+
rescue Solidgate::ConnectionError => e
|
|
81
|
+
puts "❌ Connection Error: #{e.message}"
|
|
82
|
+
puts "Please check your internet connection"
|
|
83
|
+
rescue Solidgate::Error => e
|
|
84
|
+
puts "❌ Solidgate Error: #{e.message}"
|
|
85
|
+
rescue => e
|
|
86
|
+
puts "❌ Unexpected Error: #{e.message}"
|
|
87
|
+
puts e.backtrace if ENV["DEBUG"]
|
|
88
|
+
end
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "faraday"
|
|
4
|
+
require "faraday/multipart"
|
|
5
|
+
require "json"
|
|
6
|
+
require "digest"
|
|
7
|
+
require "base64"
|
|
8
|
+
require "openssl"
|
|
9
|
+
require 'pry'
|
|
10
|
+
|
|
11
|
+
module Solidgate
|
|
12
|
+
# HTTP client for interacting with the Solidgate API
|
|
13
|
+
class Client
|
|
14
|
+
attr_reader :config
|
|
15
|
+
|
|
16
|
+
IV_LENGTH = 16
|
|
17
|
+
KEY_LENGTH = 32
|
|
18
|
+
|
|
19
|
+
def initialize(options = Solidgate.configuration)
|
|
20
|
+
@config = build_config(options)
|
|
21
|
+
@config.validate!
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Create a new payment
|
|
25
|
+
#
|
|
26
|
+
# @param params [Hash] payment parameters
|
|
27
|
+
# @return [Hash] payment response
|
|
28
|
+
def create_payment(params)
|
|
29
|
+
post("/v1/charge", params)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Get payment status
|
|
33
|
+
#
|
|
34
|
+
# @param payment_id [String] payment ID
|
|
35
|
+
# @return [Hash] payment details
|
|
36
|
+
def get_payment(payment_id)
|
|
37
|
+
get("/v1/charge/#{payment_id}")
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Capture a payment
|
|
41
|
+
#
|
|
42
|
+
# @param payment_id [String] payment ID
|
|
43
|
+
# @param params [Hash] capture parameters (optional)
|
|
44
|
+
# @return [Hash] capture response
|
|
45
|
+
def capture_payment(payment_id, params = {})
|
|
46
|
+
post("/v1/charge/#{payment_id}/capture", params)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Void a payment
|
|
50
|
+
#
|
|
51
|
+
# @param payment_id [String] payment ID
|
|
52
|
+
# @return [Hash] void response
|
|
53
|
+
def void_payment(payment_id)
|
|
54
|
+
post("/v1/charge/#{payment_id}/void")
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Refund a payment
|
|
58
|
+
#
|
|
59
|
+
# @param payment_id [String] payment ID
|
|
60
|
+
# @param params [Hash] refund parameters
|
|
61
|
+
# @return [Hash] refund response
|
|
62
|
+
def refund_payment(payment_id, params = {})
|
|
63
|
+
post("/v1/charge/#{payment_id}/refund", params)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Settle a payment
|
|
67
|
+
#
|
|
68
|
+
# @param params [Hash] settlement parameters
|
|
69
|
+
# @return [Hash] settlement response
|
|
70
|
+
def settle_payment(params = {})
|
|
71
|
+
conifg.api_url
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Create a subscription
|
|
75
|
+
#
|
|
76
|
+
# @param params [Hash] subscription parameters
|
|
77
|
+
# @return [Hash] subscription response
|
|
78
|
+
def create_subscription(params)
|
|
79
|
+
post("/v1/subscription", params)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Get subscription details
|
|
83
|
+
#
|
|
84
|
+
# @param subscription_id [String] subscription ID
|
|
85
|
+
# @return [Hash] subscription details
|
|
86
|
+
def subscription_status(subscription_id)
|
|
87
|
+
post("api/v1/subscription/status", { subscription_id: subscription_id })
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def create_product(params)
|
|
91
|
+
post("/api/v1/products", params)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def products
|
|
95
|
+
get("/api/v1/products")
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def product_prices(product_id)
|
|
99
|
+
get("/api/v1/products/#{product_id}/prices")
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def generate_intent(params)
|
|
103
|
+
encrypt_payload(params)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def generate_signature(json_string, public_key: config.public_key, private_key: config.private_key)
|
|
107
|
+
digest = OpenSSL::Digest.new('sha512')
|
|
108
|
+
instance = OpenSSL::HMAC.new(private_key, digest)
|
|
109
|
+
instance.update(public_key + json_string + public_key)
|
|
110
|
+
Base64.strict_encode64(instance.hexdigest)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
private
|
|
114
|
+
|
|
115
|
+
def build_config(options)
|
|
116
|
+
if options.is_a?(Configuration)
|
|
117
|
+
options
|
|
118
|
+
else
|
|
119
|
+
config = Configuration.new
|
|
120
|
+
options.each { |key, value| config.send("#{key}=", value) if config.respond_to?("#{key}=") }
|
|
121
|
+
config
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def connection
|
|
126
|
+
@connection ||= Faraday.new(
|
|
127
|
+
url: config.api_url,
|
|
128
|
+
headers: default_headers
|
|
129
|
+
) do |conn|
|
|
130
|
+
conn.request :multipart
|
|
131
|
+
conn.request :url_encoded
|
|
132
|
+
conn.response :json, content_type: /\bjson$/
|
|
133
|
+
conn.adapter Faraday.default_adapter
|
|
134
|
+
|
|
135
|
+
conn.options.timeout = config.timeout
|
|
136
|
+
conn.options.open_timeout = config.open_timeout
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def default_headers
|
|
141
|
+
{
|
|
142
|
+
"Accept" => "application/json",
|
|
143
|
+
"Content-Type" => "application/json",
|
|
144
|
+
"User-Agent" => config.user_agent
|
|
145
|
+
}
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def get(path)
|
|
149
|
+
request(:get, path)
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def post(path, body = {})
|
|
153
|
+
request(:post, path, body)
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def request(method, path, body = nil)
|
|
157
|
+
body_json = body ? JSON.generate(body) : ''
|
|
158
|
+
signature = generate_signature(body_json)
|
|
159
|
+
|
|
160
|
+
response = connection.send(method) do |req|
|
|
161
|
+
req.url path
|
|
162
|
+
req.headers["Merchant"] = config.public_key
|
|
163
|
+
req.headers["Signature"] = signature
|
|
164
|
+
req.body = body_json if body_json
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
handle_response(response)
|
|
168
|
+
rescue Faraday::TimeoutError
|
|
169
|
+
raise TimeoutError, "Request timed out"
|
|
170
|
+
rescue Faraday::ConnectionFailed => e
|
|
171
|
+
raise ConnectionError, "Connection failed: #{e.message}"
|
|
172
|
+
rescue => e
|
|
173
|
+
raise Error, "Unexpected error: #{e.message}"
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def handle_response(response)
|
|
177
|
+
case response.status
|
|
178
|
+
when 200..299
|
|
179
|
+
response.body
|
|
180
|
+
when 400
|
|
181
|
+
raise InvalidRequestError.new(
|
|
182
|
+
extract_error_message(response),
|
|
183
|
+
extract_error_code(response),
|
|
184
|
+
response.body,
|
|
185
|
+
response.status
|
|
186
|
+
)
|
|
187
|
+
when 401
|
|
188
|
+
raise AuthenticationError.new(
|
|
189
|
+
"Authentication failed",
|
|
190
|
+
"authentication_failed",
|
|
191
|
+
response.body,
|
|
192
|
+
response.status
|
|
193
|
+
)
|
|
194
|
+
when 429
|
|
195
|
+
raise RateLimitError.new(
|
|
196
|
+
"Rate limit exceeded",
|
|
197
|
+
"rate_limit_exceeded",
|
|
198
|
+
response.body,
|
|
199
|
+
response.status
|
|
200
|
+
)
|
|
201
|
+
when 500..599
|
|
202
|
+
raise APIError.new(
|
|
203
|
+
"Server error",
|
|
204
|
+
"server_error",
|
|
205
|
+
response.body,
|
|
206
|
+
response.status
|
|
207
|
+
)
|
|
208
|
+
else
|
|
209
|
+
raise APIError.new(
|
|
210
|
+
"Unknown error",
|
|
211
|
+
"unknown_error",
|
|
212
|
+
response.body,
|
|
213
|
+
response.status
|
|
214
|
+
)
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
def extract_error_message(response)
|
|
219
|
+
return "Unknown error" unless response.body.is_a?(Hash)
|
|
220
|
+
|
|
221
|
+
response.body["error"]["message"] || "API error"
|
|
222
|
+
rescue
|
|
223
|
+
"Unknown error"
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
def extract_error_code(response)
|
|
227
|
+
return nil unless response.body.is_a?(Hash)
|
|
228
|
+
|
|
229
|
+
response.body["error"]["code"]
|
|
230
|
+
rescue
|
|
231
|
+
nil
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
def encrypt_payload(attributes)
|
|
235
|
+
key = config.private_key[0, KEY_LENGTH]
|
|
236
|
+
iv = OpenSSL::Random.random_bytes(IV_LENGTH)
|
|
237
|
+
cipher = OpenSSL::Cipher.new('aes-256-cbc')
|
|
238
|
+
cipher.encrypt
|
|
239
|
+
cipher.key = key
|
|
240
|
+
cipher.iv = iv
|
|
241
|
+
encrypted = cipher.update(attributes) + cipher.final
|
|
242
|
+
|
|
243
|
+
base64_encoded = Base64.urlsafe_encode64(iv + encrypted)
|
|
244
|
+
base64_encoded.gsub('+', '-').gsub('/', '_')
|
|
245
|
+
end
|
|
246
|
+
end
|
|
247
|
+
end
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Solidgate
|
|
4
|
+
# Configuration class for the Solidgate SDK
|
|
5
|
+
class Configuration
|
|
6
|
+
# @!attribute [rw] public_key
|
|
7
|
+
# @return [String] Solidgate public key
|
|
8
|
+
attr_accessor :public_key
|
|
9
|
+
|
|
10
|
+
# @!attribute [rw] private_key
|
|
11
|
+
# @return [String] Solidgate private key
|
|
12
|
+
attr_accessor :private_key
|
|
13
|
+
|
|
14
|
+
# @!attribute [rw] sandbox
|
|
15
|
+
# @return [Boolean] whether to use sandbox environment
|
|
16
|
+
attr_accessor :sandbox
|
|
17
|
+
|
|
18
|
+
# @!attribute [rw] api_url
|
|
19
|
+
# @return [String] API base URL
|
|
20
|
+
attr_accessor :api_url
|
|
21
|
+
|
|
22
|
+
# @!attribute [rw] timeout
|
|
23
|
+
# @return [Integer] request timeout in seconds
|
|
24
|
+
attr_accessor :timeout
|
|
25
|
+
|
|
26
|
+
# @!attribute [rw] open_timeout
|
|
27
|
+
# @return [Integer] connection timeout in seconds
|
|
28
|
+
attr_accessor :open_timeout
|
|
29
|
+
|
|
30
|
+
# @!attribute [rw] user_agent
|
|
31
|
+
# @return [String] custom user agent
|
|
32
|
+
attr_accessor :user_agent
|
|
33
|
+
|
|
34
|
+
# @!attribute [rw] webhook_public_key
|
|
35
|
+
# @return [String] Webhook public key
|
|
36
|
+
attr_accessor :webhook_public_key
|
|
37
|
+
|
|
38
|
+
# @!attribute [rw] webhook_private_key
|
|
39
|
+
# @return [String] Webhook private key
|
|
40
|
+
attr_accessor :webhook_private_key
|
|
41
|
+
|
|
42
|
+
# Production API URL
|
|
43
|
+
PRODUCTION_URL = "https://subscriptions.solidgate.com"
|
|
44
|
+
|
|
45
|
+
# Sandbox API URL
|
|
46
|
+
SANDBOX_URL = "https://subscriptions.solidgate.com"
|
|
47
|
+
|
|
48
|
+
def initialize
|
|
49
|
+
@sandbox = false
|
|
50
|
+
@timeout = 30
|
|
51
|
+
@open_timeout = 10
|
|
52
|
+
@user_agent = "Solidgate Ruby SDK/#{VERSION}"
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Get the appropriate API URL based on sandbox setting
|
|
56
|
+
#
|
|
57
|
+
# @return [String] API base URL
|
|
58
|
+
def api_url
|
|
59
|
+
@api_url || (sandbox? ? SANDBOX_URL : PRODUCTION_URL)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Check if sandbox mode is enabled
|
|
63
|
+
#
|
|
64
|
+
# @return [Boolean] true if sandbox mode is enabled
|
|
65
|
+
def sandbox?
|
|
66
|
+
@sandbox
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Validate that required configuration is present
|
|
70
|
+
#
|
|
71
|
+
# @raise [ConfigurationError] if required configuration is missing
|
|
72
|
+
def validate!
|
|
73
|
+
raise ConfigurationError, "public_key is required" unless public_key
|
|
74
|
+
raise ConfigurationError, "private_key is required" unless private_key
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Solidgate
|
|
4
|
+
# Base error class for all Solidgate errors
|
|
5
|
+
class Error < StandardError
|
|
6
|
+
attr_reader :code, :details
|
|
7
|
+
|
|
8
|
+
def initialize(message = nil, code = nil, details = nil)
|
|
9
|
+
super(message)
|
|
10
|
+
@code = code
|
|
11
|
+
@details = details
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Configuration error
|
|
16
|
+
class ConfigurationError < Error; end
|
|
17
|
+
|
|
18
|
+
# API error
|
|
19
|
+
class APIError < Error
|
|
20
|
+
def initialize(message, code, details = nil, http_status = nil)
|
|
21
|
+
super(message, code, details)
|
|
22
|
+
@http_status = http_status
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
attr_reader :http_status
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Authentication error
|
|
29
|
+
class AuthenticationError < APIError; end
|
|
30
|
+
|
|
31
|
+
# Invalid request error
|
|
32
|
+
class InvalidRequestError < APIError; end
|
|
33
|
+
|
|
34
|
+
# Connection error
|
|
35
|
+
class ConnectionError < Error; end
|
|
36
|
+
|
|
37
|
+
# Timeout error
|
|
38
|
+
class TimeoutError < Error; end
|
|
39
|
+
|
|
40
|
+
# Rate limit error
|
|
41
|
+
class RateLimitError < APIError; end
|
|
42
|
+
|
|
43
|
+
# Validation error
|
|
44
|
+
class ValidationError < Error
|
|
45
|
+
attr_reader :errors
|
|
46
|
+
|
|
47
|
+
def initialize(message, errors = {})
|
|
48
|
+
super(message)
|
|
49
|
+
@errors = errors
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Solidgate
|
|
4
|
+
# Payment resource for handling payment-related operations
|
|
5
|
+
class Payment
|
|
6
|
+
attr_reader :client
|
|
7
|
+
|
|
8
|
+
def initialize(client = nil)
|
|
9
|
+
@client = client || Solidgate.client
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# Create a new payment charge
|
|
13
|
+
#
|
|
14
|
+
# @param params [Hash] payment parameters
|
|
15
|
+
# @option params [String] :order_id unique order identifier
|
|
16
|
+
# @option params [Integer] :amount amount in cents
|
|
17
|
+
# @option params [String] :currency currency code (e.g., "USD")
|
|
18
|
+
# @option params [Hash] :card_data card information
|
|
19
|
+
# @option params [Hash] :customer customer information
|
|
20
|
+
# @option params [String] :description payment description
|
|
21
|
+
# @return [Hash] payment response
|
|
22
|
+
def create(params)
|
|
23
|
+
validate_create_params(params)
|
|
24
|
+
client.create_payment(params)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Retrieve payment information
|
|
28
|
+
#
|
|
29
|
+
# @param payment_id [String] payment ID
|
|
30
|
+
# @return [Hash] payment details
|
|
31
|
+
def get(payment_id)
|
|
32
|
+
raise ArgumentError, "payment_id is required" if payment_id.nil? || payment_id.empty?
|
|
33
|
+
|
|
34
|
+
client.get_payment(payment_id)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Capture a previously authorized payment
|
|
38
|
+
#
|
|
39
|
+
# @param payment_id [String] payment ID
|
|
40
|
+
# @param amount [Integer] amount to capture in cents (optional, defaults to full amount)
|
|
41
|
+
# @return [Hash] capture response
|
|
42
|
+
def capture(payment_id, amount: nil)
|
|
43
|
+
raise ArgumentError, "payment_id is required" if payment_id.nil? || payment_id.empty?
|
|
44
|
+
|
|
45
|
+
params = {}
|
|
46
|
+
params[:amount] = amount if amount
|
|
47
|
+
|
|
48
|
+
client.capture_payment(payment_id, params)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Void a payment (cancel an authorization)
|
|
52
|
+
#
|
|
53
|
+
# @param payment_id [String] payment ID
|
|
54
|
+
# @return [Hash] void response
|
|
55
|
+
def void(payment_id)
|
|
56
|
+
raise ArgumentError, "payment_id is required" if payment_id.nil? || payment_id.empty?
|
|
57
|
+
|
|
58
|
+
client.void_payment(payment_id)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Refund a payment (full or partial)
|
|
62
|
+
#
|
|
63
|
+
# @param payment_id [String] payment ID
|
|
64
|
+
# @param amount [Integer] amount to refund in cents (optional, defaults to full amount)
|
|
65
|
+
# @param reason [String] refund reason (optional)
|
|
66
|
+
# @return [Hash] refund response
|
|
67
|
+
def refund(payment_id, amount: nil, reason: nil)
|
|
68
|
+
raise ArgumentError, "payment_id is required" if payment_id.nil? || payment_id.empty?
|
|
69
|
+
|
|
70
|
+
params = {}
|
|
71
|
+
params[:amount] = amount if amount
|
|
72
|
+
params[:reason] = reason if reason
|
|
73
|
+
|
|
74
|
+
client.refund_payment(payment_id, params)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
private
|
|
78
|
+
|
|
79
|
+
def validate_create_params(params)
|
|
80
|
+
errors = {}
|
|
81
|
+
|
|
82
|
+
errors[:order_id] = "is required" unless params[:order_id]
|
|
83
|
+
errors[:amount] = "is required" unless params[:amount]
|
|
84
|
+
errors[:currency] = "is required" unless params[:currency]
|
|
85
|
+
|
|
86
|
+
if params[:amount] && params[:amount] <= 0
|
|
87
|
+
errors[:amount] = "must be greater than 0"
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
if params[:currency] && params[:currency].length != 3
|
|
91
|
+
errors[:currency] = "must be a 3-letter currency code"
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
unless errors.empty?
|
|
95
|
+
raise ValidationError.new("Invalid payment parameters", errors)
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module Solidgate
|
|
2
|
+
class Webhook
|
|
3
|
+
# Webhook resource for handling webhook-related operations
|
|
4
|
+
attr_reader :client
|
|
5
|
+
|
|
6
|
+
def initialize(client = nil)
|
|
7
|
+
@client = client || Solidgate.client
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
# Validate webhook signature
|
|
11
|
+
# @param payload [String] webhook payload
|
|
12
|
+
# @param signature [String] webhook signature
|
|
13
|
+
# @return [Boolean] whether the signature is valid
|
|
14
|
+
def validate_signature(payload, signature)
|
|
15
|
+
client.generate_signature(payload,
|
|
16
|
+
public_key: Solidgate.configuration.webhook_public_key,
|
|
17
|
+
private_key: Solidgate.configuration.webhook_private_key) == signature
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
data/lib/solidgate.rb
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "solidgate/version"
|
|
4
|
+
require_relative "solidgate/configuration"
|
|
5
|
+
require_relative "solidgate/errors"
|
|
6
|
+
require_relative "solidgate/client"
|
|
7
|
+
require_relative "solidgate/payment"
|
|
8
|
+
require_relative "solidgate/webhook"
|
|
9
|
+
|
|
10
|
+
module Solidgate
|
|
11
|
+
class << self
|
|
12
|
+
# Configure the Solidgate client
|
|
13
|
+
#
|
|
14
|
+
# @yield [Configuration] configuration object
|
|
15
|
+
# @example
|
|
16
|
+
# Solidgate.configure do |config|
|
|
17
|
+
# config.public_key = "your_public_key"
|
|
18
|
+
# config.private_key = "your_private_key"
|
|
19
|
+
# config.sandbox = true
|
|
20
|
+
# end
|
|
21
|
+
def configure
|
|
22
|
+
yield(configuration)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Current configuration
|
|
26
|
+
#
|
|
27
|
+
# @return [Configuration] current configuration
|
|
28
|
+
def configuration
|
|
29
|
+
@configuration ||= Configuration.new
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Create a new client instance
|
|
33
|
+
#
|
|
34
|
+
# @param options [Hash] optional configuration overrides
|
|
35
|
+
# @return [Client] new client instance
|
|
36
|
+
def client
|
|
37
|
+
Client.new
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
data/solidgate.gemspec
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "lib/solidgate/version"
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |spec|
|
|
6
|
+
spec.name = "solidgate-ruby-sdk"
|
|
7
|
+
spec.version = Solidgate::VERSION
|
|
8
|
+
spec.authors = ["Hector Carrillo"]
|
|
9
|
+
spec.email = ["carrfane@gmail.com"]
|
|
10
|
+
|
|
11
|
+
spec.summary = "Ruby SDK for Solidgate payment processing"
|
|
12
|
+
spec.description = "A Ruby SDK for integrating with the Solidgate payment gateway API"
|
|
13
|
+
spec.homepage = "https://github.com/carrfane/solidgate-ruby-sdk"
|
|
14
|
+
spec.license = "MIT"
|
|
15
|
+
spec.required_ruby_version = ">= 2.6.0"
|
|
16
|
+
|
|
17
|
+
spec.metadata["allowed_push_host"] = "https://rubygems.org"
|
|
18
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
|
19
|
+
spec.metadata["source_code_uri"] = "https://github.com/carrfane/solidgate-ruby-sdk"
|
|
20
|
+
spec.metadata["changelog_uri"] = "https://github.com/carrfane/solidgate-ruby-sdk/blob/master/CHANGELOG.md"
|
|
21
|
+
|
|
22
|
+
# Specify which files should be added to the gem when it is released.
|
|
23
|
+
spec.files = Dir.chdir(__dir__) do
|
|
24
|
+
`git ls-files -z`.split("\x0").reject do |f|
|
|
25
|
+
(f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|circleci)|appveyor)})
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
spec.bindir = "exe"
|
|
30
|
+
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
|
31
|
+
spec.require_paths = ["lib"]
|
|
32
|
+
|
|
33
|
+
# Runtime dependencies
|
|
34
|
+
spec.add_dependency "faraday"
|
|
35
|
+
spec.add_dependency "faraday-multipart"
|
|
36
|
+
|
|
37
|
+
# Development dependencies
|
|
38
|
+
# spec.add_development_dependency "rspec", "~> 3.0"
|
|
39
|
+
# spec.add_development_dependency "webmock", "~> 3.0"
|
|
40
|
+
# spec.add_development_dependency "vcr", "~> 6.0"
|
|
41
|
+
# spec.add_development_dependency "rubocop", "~> 1.0"
|
|
42
|
+
# spec.add_development_dependency "pry", "~> 0.14"
|
|
43
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: solidgate-ruby-sdk
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Hector Carrillo
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: exe
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2026-01-05 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: faraday
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ">="
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '0'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - ">="
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '0'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: faraday-multipart
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - ">="
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '0'
|
|
34
|
+
type: :runtime
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - ">="
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '0'
|
|
41
|
+
description: A Ruby SDK for integrating with the Solidgate payment gateway API
|
|
42
|
+
email:
|
|
43
|
+
- carrfane@gmail.com
|
|
44
|
+
executables: []
|
|
45
|
+
extensions: []
|
|
46
|
+
extra_rdoc_files: []
|
|
47
|
+
files:
|
|
48
|
+
- ".rspec"
|
|
49
|
+
- ".ruby-version"
|
|
50
|
+
- CHANGELOG.md
|
|
51
|
+
- Gemfile
|
|
52
|
+
- Gemfile.lock
|
|
53
|
+
- LICENSE.txt
|
|
54
|
+
- README.md
|
|
55
|
+
- Rakefile
|
|
56
|
+
- examples/basic_usage.rb
|
|
57
|
+
- lib/solidgate.rb
|
|
58
|
+
- lib/solidgate/client.rb
|
|
59
|
+
- lib/solidgate/configuration.rb
|
|
60
|
+
- lib/solidgate/errors.rb
|
|
61
|
+
- lib/solidgate/payment.rb
|
|
62
|
+
- lib/solidgate/version.rb
|
|
63
|
+
- lib/solidgate/webhook.rb
|
|
64
|
+
- solidgate.gemspec
|
|
65
|
+
homepage: https://github.com/carrfane/solidgate-ruby-sdk
|
|
66
|
+
licenses:
|
|
67
|
+
- MIT
|
|
68
|
+
metadata:
|
|
69
|
+
allowed_push_host: https://rubygems.org
|
|
70
|
+
homepage_uri: https://github.com/carrfane/solidgate-ruby-sdk
|
|
71
|
+
source_code_uri: https://github.com/carrfane/solidgate-ruby-sdk
|
|
72
|
+
changelog_uri: https://github.com/carrfane/solidgate-ruby-sdk/blob/master/CHANGELOG.md
|
|
73
|
+
post_install_message:
|
|
74
|
+
rdoc_options: []
|
|
75
|
+
require_paths:
|
|
76
|
+
- lib
|
|
77
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
78
|
+
requirements:
|
|
79
|
+
- - ">="
|
|
80
|
+
- !ruby/object:Gem::Version
|
|
81
|
+
version: 2.6.0
|
|
82
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
83
|
+
requirements:
|
|
84
|
+
- - ">="
|
|
85
|
+
- !ruby/object:Gem::Version
|
|
86
|
+
version: '0'
|
|
87
|
+
requirements: []
|
|
88
|
+
rubygems_version: 3.1.6
|
|
89
|
+
signing_key:
|
|
90
|
+
specification_version: 4
|
|
91
|
+
summary: Ruby SDK for Solidgate payment processing
|
|
92
|
+
test_files: []
|