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 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
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ # Specify your gem's dependencies in solidgate.gemspec
6
+ gemspec
7
+
8
+ gem "rake", "~> 13.0"
9
+
10
+ group :development, :test do
11
+ gem "pry"
12
+ end
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,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rake"
4
+ # require "rspec/core/rake_task"
5
+
6
+ # RSpec::Core::RakeTask.new(:spec)
7
+
8
+ require "bundler/gem_tasks"
9
+
10
+ task default: :spec
@@ -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,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solidgate
4
+ VERSION = "0.1.0"
5
+ 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: []