sanwo 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: 2372fbf0134b38a2c9714b59a26767a1cae3f2464866cc0a5f5bf5069716285a
4
+ data.tar.gz: df0a25fbfac2b94d6d5db1177c44e6b8e7501c0d71fd3ddf224df38dfc55a1b0
5
+ SHA512:
6
+ metadata.gz: 1f4c77a24307b071a8b1158ece4ded02a608a477f25e25d21b70d9c7167fdb9758255876919729484447e33599d0a0b2df9958379fc2e4631183872dd96f817f
7
+ data.tar.gz: 39cebb9e678e27f15c14884dacd2fa4cf0ae59f6cd8b8056805debe61d46ba1503652117edd2dcdc4b4266dde78283da348dc15901234ffa395612972901dd68
data/README.md ADDED
@@ -0,0 +1,99 @@
1
+ # Sanwo Ruby SDK
2
+
3
+ Add [Sanwo](https://sanwohq.com) payments to your Ruby application — Rails or standalone.
4
+
5
+ > **Full documentation at [docs.sanwo.dev](https://docs.sanwo.dev/server/ruby/)** — guides, examples, and API reference for every SDK and provider.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ gem install sanwo
11
+ ```
12
+
13
+ Or add to your Gemfile:
14
+
15
+ ```ruby
16
+ gem "sanwo"
17
+ ```
18
+
19
+ ## Standalone Usage
20
+
21
+ ```ruby
22
+ require "sanwo"
23
+
24
+ client = Sanwo::Client.new(
25
+ provider: "paystack",
26
+ public_key: "pk_test_xxx"
27
+ )
28
+
29
+ # Include the CDN script tag in your <head>
30
+ client.render_script
31
+ # => '<script src="https://cdn.jsdelivr.net/npm/@sanwohq/embed/dist/sanwo.global.js"></script>'
32
+
33
+ # Render a checkout button (amount in minor units)
34
+ client.render_checkout(
35
+ amount: 50000,
36
+ email: "user@example.com",
37
+ description: "Premium plan",
38
+ button_text: "Subscribe"
39
+ )
40
+ ```
41
+
42
+ ## Rails Setup
43
+
44
+ Add the gem to your Gemfile and configure an initializer:
45
+
46
+ ```ruby
47
+ # config/initializers/sanwo.rb
48
+ Rails.application.config.sanwo_client = Sanwo::Client.new(
49
+ provider: "paystack",
50
+ public_key: ENV["PAYSTACK_PUBLIC_KEY"],
51
+ currency: "NGN"
52
+ )
53
+ ```
54
+
55
+ Then use the view helpers in your templates:
56
+
57
+ ```erb
58
+ <head>
59
+ <%= sanwo_scripts %>
60
+ </head>
61
+ <body>
62
+ <%= sanwo_checkout(amount: 50000, email: current_user.email) %>
63
+ </body>
64
+ ```
65
+
66
+ ### Custom Amount Input
67
+
68
+ ```erb
69
+ <%= sanwo_custom_amount(
70
+ email: current_user.email,
71
+ min_amount: 10000,
72
+ max_amount: 1000000,
73
+ placeholder: "Enter donation amount"
74
+ ) %>
75
+ ```
76
+
77
+ ## Custom Provider Template
78
+
79
+ ```ruby
80
+ client = Sanwo::Client.new(
81
+ provider: "custom",
82
+ public_key: "pk_xxx",
83
+ template_url: "https://example.com/my-template.html"
84
+ )
85
+ ```
86
+
87
+ Or with an inline template:
88
+
89
+ ```ruby
90
+ client = Sanwo::Client.new(
91
+ provider: "custom",
92
+ public_key: "pk_xxx",
93
+ template: "<div>Your custom checkout HTML</div>"
94
+ )
95
+ ```
96
+
97
+ ## License
98
+
99
+ Apache 2.0 — see [LICENSE](LICENSE) for details.
@@ -0,0 +1,315 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "erb"
4
+ require "json"
5
+ require "securerandom"
6
+
7
+ module Sanwo
8
+ # Server-side HTML generator for Sanwo checkout.
9
+ #
10
+ # This class does NOT make any HTTP requests. It produces front-end markup
11
+ # that delegates to the Sanwo embed script loaded from the CDN.
12
+ class Client
13
+ CDN_URL = "https://cdn.jsdelivr.net/npm/@sanwohq/embed/dist/sanwo.global.js"
14
+
15
+ attr_reader :provider, :public_key, :currency, :debug, :template_url, :template
16
+
17
+ # @param provider [String] payment provider identifier (see {Sanwo::Providers})
18
+ # @param public_key [String] your provider's public / publishable key
19
+ # @param currency [String] ISO 4217 currency code (default: "NGN")
20
+ # @param debug [Boolean] enable browser console logging (default: false)
21
+ # @param template_url [String, nil] URL to fetch a custom template from
22
+ # @param template [String, nil] inline HTML template string
23
+ # @raise [ArgumentError] if public_key is blank or custom provider lacks template
24
+ def initialize(provider:, public_key:, currency: "NGN", debug: false, template_url: nil, template: nil)
25
+ raise ArgumentError, "public_key is required" if public_key.nil? || public_key.to_s.strip.empty?
26
+
27
+ @provider = Providers.resolve(provider)
28
+ @public_key = public_key
29
+ @currency = currency
30
+ @debug = debug
31
+ @template_url = template_url
32
+ @template = template
33
+
34
+ if @provider == Providers::CUSTOM && @template.nil? && @template_url.nil?
35
+ raise ArgumentError, 'Custom provider requires a "template" or "template_url"'
36
+ end
37
+ end
38
+
39
+ # Return a <script> tag that loads the Sanwo embed script from the CDN.
40
+ #
41
+ # @return [String] HTML script tag
42
+ def render_script
43
+ html = %(<script src="#{h(CDN_URL)}"></script>)
44
+ mark_safe(html)
45
+ end
46
+
47
+ # Render a complete checkout button with inline JavaScript.
48
+ #
49
+ # The returned HTML contains a <button> element and an inline <script>
50
+ # that uses Sanwo.create() and .checkout() to initiate payment on click.
51
+ #
52
+ # All amounts are in minor units (kobo / cents).
53
+ #
54
+ # @param amount [Integer] amount in minor units
55
+ # @param email [String] customer email address
56
+ # @param description [String, nil] human-readable payment description
57
+ # @param reference [String, nil] unique transaction reference (auto-generated if omitted)
58
+ # @param currency [String, nil] override the default currency
59
+ # @param button_text [String] label displayed on the button
60
+ # @param button_class [String] CSS class(es) applied to the button
61
+ # @param first_name [String, nil] customer first name
62
+ # @param last_name [String, nil] customer last name
63
+ # @param phone [String, nil] customer phone number
64
+ # @param callback [String, nil] name of a global JS function to call on completion
65
+ # @return [String] HTML string
66
+ def render_checkout(amount:, email:, description: nil, reference: nil, currency: nil,
67
+ button_text: "Pay Now", button_class: "sanwo-button",
68
+ first_name: nil, last_name: nil, phone: nil, callback: nil)
69
+ validate_checkout(amount, email)
70
+
71
+ ref = reference || "sanwo_#{SecureRandom.hex(8)}"
72
+ cur = currency || @currency
73
+ btn_id = "sanwo-btn-#{SecureRandom.hex(4)}"
74
+
75
+ customer = { "email" => email }
76
+ customer["firstName"] = first_name if first_name
77
+ customer["lastName"] = last_name if last_name
78
+ customer["phone"] = phone if phone
79
+
80
+ checkout_opts = {
81
+ "amount" => amount,
82
+ "currency" => cur,
83
+ "reference" => ref,
84
+ "customer" => customer,
85
+ }
86
+ checkout_opts["description"] = description if description
87
+
88
+ config_json = JSON.generate(build_create_config)
89
+ opts_json = JSON.generate(checkout_opts)
90
+ debug_flag = @debug ? "true" : "false"
91
+
92
+ callback_js = ""
93
+ if callback
94
+ cb = h(callback)
95
+ callback_js = "if (typeof window['#{cb}'] === 'function') { window['#{cb}'](result); }"
96
+ end
97
+
98
+ html = <<~HTML
99
+ <button type="button" class="#{h(button_class)}" id="#{btn_id}">#{h(button_text)}</button>
100
+ <script>
101
+ (function() {
102
+ var btn = document.getElementById('#{btn_id}');
103
+ btn.addEventListener('click', function() {
104
+ if (btn.disabled) return;
105
+ btn.disabled = true;
106
+ var sanwo = Sanwo.create(#{config_json});
107
+ if (#{debug_flag}) console.log('[Sanwo] Starting checkout', #{opts_json});
108
+ sanwo.checkout(#{opts_json}).then(function(result) {
109
+ btn.disabled = false;
110
+ if (#{debug_flag}) console.log('[Sanwo] Result', result);
111
+ #{callback_js}
112
+ }).catch(function(err) {
113
+ btn.disabled = false;
114
+ console.error('[Sanwo] Checkout error:', err);
115
+ });
116
+ });
117
+ })();
118
+ </script>
119
+ HTML
120
+
121
+ mark_safe(html)
122
+ end
123
+
124
+ # Render a custom-amount checkout widget.
125
+ #
126
+ # Produces an <input> for the amount (displayed in major units) and a pay
127
+ # button. When submitted, the value is converted to minor units automatically.
128
+ #
129
+ # @param email [String, nil] customer email (if omitted, the widget prompts the user)
130
+ # @param currency [String, nil] override the default currency
131
+ # @param button_text [String] label for the pay button
132
+ # @param placeholder [String] placeholder text for the amount input
133
+ # @param min_amount [Integer, nil] minimum amount in minor units
134
+ # @param max_amount [Integer, nil] maximum amount in minor units
135
+ # @param description [String, nil] human-readable payment description
136
+ # @param first_name [String, nil] customer first name
137
+ # @param last_name [String, nil] customer last name
138
+ # @param phone [String, nil] customer phone number
139
+ # @param callback [String, nil] name of a global JS function to call on completion
140
+ # @return [String] HTML string
141
+ def render_custom_amount(email: nil, currency: nil, button_text: "Pay Now",
142
+ placeholder: "Enter amount", min_amount: nil, max_amount: nil,
143
+ description: nil, first_name: nil, last_name: nil,
144
+ phone: nil, callback: nil)
145
+ cur = currency || @currency
146
+ widget_id = "sanwo-custom-#{SecureRandom.hex(4)}"
147
+
148
+ config_json = JSON.generate(build_create_config)
149
+ debug_flag = @debug ? "true" : "false"
150
+
151
+ # Build data attributes
152
+ attrs = [
153
+ %(data-sanwo-provider="#{h(@provider)}"),
154
+ %(data-sanwo-key="#{h(@public_key)}"),
155
+ %(data-sanwo-currency="#{h(cur)}"),
156
+ %(data-sanwo-custom-amount="true"),
157
+ %(data-sanwo-button-text="#{h(button_text)}"),
158
+ %(data-sanwo-placeholder="#{h(placeholder)}"),
159
+ ]
160
+ attrs << %(data-sanwo-debug="true") if @debug
161
+ attrs << %(data-sanwo-template-url="#{h(@template_url)}") if @template_url
162
+ attrs << %(data-sanwo-template="#{h(@template)}") if @template
163
+ attrs << %(data-sanwo-email="#{h(email)}") if email
164
+ attrs << %(data-sanwo-min-amount="#{min_amount}") unless min_amount.nil?
165
+ attrs << %(data-sanwo-max-amount="#{max_amount}") unless max_amount.nil?
166
+ attrs << %(data-sanwo-description="#{h(description)}") if description
167
+ attrs << %(data-sanwo-first-name="#{h(first_name)}") if first_name
168
+ attrs << %(data-sanwo-last-name="#{h(last_name)}") if last_name
169
+ attrs << %(data-sanwo-phone="#{h(phone)}") if phone
170
+ attrs << %(data-sanwo-callback="#{h(callback)}") if callback
171
+
172
+ attrs_str = attrs.join(" ")
173
+
174
+ # Build customer JS object
175
+ customer_fields = []
176
+ customer_fields << "email: #{JSON.generate(email)}" if email
177
+ customer_fields << "firstName: #{JSON.generate(first_name)}" if first_name
178
+ customer_fields << "lastName: #{JSON.generate(last_name)}" if last_name
179
+ customer_fields << "phone: #{JSON.generate(phone)}" if phone
180
+
181
+ customer_js = if customer_fields.any?
182
+ "{ #{customer_fields.join(', ')} }"
183
+ else
184
+ "{ email: emailInput.value }"
185
+ end
186
+
187
+ callback_js = ""
188
+ if callback
189
+ cb = h(callback)
190
+ callback_js = "if (typeof window['#{cb}'] === 'function') { window['#{cb}'](result); }"
191
+ end
192
+
193
+ min_check = ""
194
+ unless min_amount.nil?
195
+ min_major = min_amount / 100.0
196
+ min_check = "if (amountMinor < #{min_amount}) { alert('Minimum amount is #{format('%.2f', min_major)}'); return; }\n "
197
+ end
198
+
199
+ max_check = ""
200
+ unless max_amount.nil?
201
+ max_major = max_amount / 100.0
202
+ max_check = "if (amountMinor > #{max_amount}) { alert('Maximum amount is #{format('%.2f', max_major)}'); return; }\n "
203
+ end
204
+
205
+ email_input_html = ""
206
+ email_input_js = ""
207
+ unless email
208
+ email_input_html = %(\n <input type="email" id="#{widget_id}-email" placeholder="Email address" required style="display:block;margin-bottom:8px;padding:8px;width:100%;box-sizing:border-box;" />)
209
+ email_input_js = "var emailInput = document.getElementById('#{widget_id}-email');\n "
210
+ end
211
+
212
+ description_js = ""
213
+ description_js = " opts.description = #{JSON.generate(description)};\n" if description
214
+
215
+ html = <<~HTML.chomp
216
+ <div id="#{widget_id}" #{attrs_str}>#{email_input_html}
217
+ <input type="number" id="#{widget_id}-amount" placeholder="#{h(placeholder)}" step="0.01" min="0" required style="display:block;margin-bottom:8px;padding:8px;width:100%;box-sizing:border-box;" />
218
+ <button type="button" id="#{widget_id}-btn" class="sanwo-button">#{h(button_text)}</button>
219
+ </div>
220
+ <script>
221
+ (function() {
222
+ var btn = document.getElementById('#{widget_id}-btn');
223
+ var amountInput = document.getElementById('#{widget_id}-amount');
224
+ #{email_input_js} btn.addEventListener('click', function() {
225
+ var amountMajor = parseFloat(amountInput.value);
226
+ if (isNaN(amountMajor) || amountMajor <= 0) { alert('Please enter a valid amount.'); return; }
227
+ var amountMinor = Math.round(amountMajor * 100);
228
+ #{min_check}#{max_check} btn.disabled = true;
229
+ var sanwo = Sanwo.create(#{config_json});
230
+ var opts = {
231
+ amount: amountMinor,
232
+ currency: #{JSON.generate(cur)},
233
+ customer: #{customer_js}
234
+ };
235
+ #{description_js} if (#{debug_flag}) console.log('[Sanwo] Starting checkout', opts);
236
+ sanwo.checkout(opts).then(function(result) {
237
+ btn.disabled = false;
238
+ if (#{debug_flag}) console.log('[Sanwo] Result', result);
239
+ #{callback_js}
240
+ }).catch(function(err) {
241
+ btn.disabled = false;
242
+ console.error('[Sanwo] Checkout error:', err);
243
+ });
244
+ });
245
+ })();
246
+ </script>
247
+ HTML
248
+
249
+ mark_safe(html)
250
+ end
251
+
252
+ # Return the current configuration as a plain hash.
253
+ #
254
+ # @return [Hash]
255
+ def config
256
+ cfg = {
257
+ provider: @provider,
258
+ public_key: @public_key,
259
+ currency: @currency,
260
+ debug: @debug,
261
+ script_url: CDN_URL,
262
+ }
263
+ cfg[:template_url] = @template_url if @template_url
264
+ cfg[:template] = @template if @template
265
+ cfg
266
+ end
267
+
268
+ private
269
+
270
+ # Build the config hash passed to Sanwo.create() in JavaScript.
271
+ #
272
+ # @return [Hash]
273
+ def build_create_config
274
+ cfg = {
275
+ "provider" => @provider,
276
+ "publicKey" => @public_key,
277
+ }
278
+ cfg["templateUrl"] = @template_url if @template_url
279
+ cfg["template"] = @template if @template
280
+ cfg
281
+ end
282
+
283
+ # Validate checkout parameters.
284
+ #
285
+ # @raise [ArgumentError] if amount or email are invalid
286
+ def validate_checkout(amount, email)
287
+ unless amount.is_a?(Integer) && amount.positive?
288
+ raise ArgumentError, "amount must be a positive integer (in minor units)"
289
+ end
290
+ if email.nil? || !email.include?("@")
291
+ raise ArgumentError, "A valid email address is required"
292
+ end
293
+ end
294
+
295
+ # HTML-escape a string.
296
+ #
297
+ # @param value [String]
298
+ # @return [String]
299
+ def h(value)
300
+ ERB::Util.html_escape(value.to_s)
301
+ end
302
+
303
+ # Mark a string as HTML-safe if ActiveSupport is available.
304
+ #
305
+ # @param html [String]
306
+ # @return [String]
307
+ def mark_safe(html)
308
+ if html.respond_to?(:html_safe)
309
+ html.html_safe
310
+ else
311
+ html
312
+ end
313
+ end
314
+ end
315
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sanwo
4
+ module Providers
5
+ PAYSTACK = "paystack"
6
+ FLUTTERWAVE = "flutterwave"
7
+ RAZORPAY = "razorpay"
8
+ MONNIFY = "monnify"
9
+ INTERSWITCH = "interswitch"
10
+ CUSTOM = "custom"
11
+
12
+ VALID_PROVIDERS = [
13
+ PAYSTACK,
14
+ FLUTTERWAVE,
15
+ RAZORPAY,
16
+ MONNIFY,
17
+ INTERSWITCH,
18
+ CUSTOM,
19
+ ].freeze
20
+
21
+ # Resolve and validate a provider string.
22
+ #
23
+ # @param provider [String] the provider identifier
24
+ # @return [String] the downcased, validated provider ID
25
+ # @raise [ArgumentError] if the provider is not recognized
26
+ def self.resolve(provider)
27
+ key = provider.to_s.downcase.strip
28
+ unless VALID_PROVIDERS.include?(key)
29
+ raise ArgumentError, "Unknown provider #{provider.inspect}. Valid providers: #{VALID_PROVIDERS.join(', ')}"
30
+ end
31
+
32
+ key
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "helper"
4
+
5
+ module Sanwo
6
+ module Rails
7
+ class Engine < ::Rails::Engine
8
+ isolate_namespace Sanwo
9
+
10
+ initializer "sanwo.view_helpers" do
11
+ ActiveSupport.on_load(:action_view) do
12
+ include Sanwo::Rails::Helper
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sanwo
4
+ module Rails
5
+ # View helpers for rendering Sanwo checkout components in Rails templates.
6
+ #
7
+ # The helpers delegate to the {Sanwo::Client} instance stored in
8
+ # +Rails.application.config.sanwo_client+.
9
+ #
10
+ # @example In an initializer (config/initializers/sanwo.rb)
11
+ # Rails.application.config.sanwo_client = Sanwo::Client.new(
12
+ # provider: "paystack",
13
+ # public_key: ENV["PAYSTACK_PUBLIC_KEY"],
14
+ # )
15
+ #
16
+ # @example In a view
17
+ # <%= sanwo_scripts %>
18
+ # <%= sanwo_checkout(amount: 50000, email: "user@example.com") %>
19
+ module Helper
20
+ # Render the <script> tag that loads the Sanwo embed script.
21
+ #
22
+ # @return [String] HTML script tag
23
+ def sanwo_scripts
24
+ sanwo_client.render_script
25
+ end
26
+
27
+ # Render a checkout button with inline JavaScript.
28
+ #
29
+ # @param opts [Hash] checkout options (see {Sanwo::Client#render_checkout})
30
+ # @return [String] HTML string
31
+ def sanwo_checkout(**opts)
32
+ sanwo_client.render_checkout(**opts)
33
+ end
34
+
35
+ # Render a custom-amount checkout widget.
36
+ #
37
+ # @param opts [Hash] widget options (see {Sanwo::Client#render_custom_amount})
38
+ # @return [String] HTML string
39
+ def sanwo_custom_amount(**opts)
40
+ sanwo_client.render_custom_amount(**opts)
41
+ end
42
+
43
+ private
44
+
45
+ def sanwo_client
46
+ ::Rails.application.config.sanwo_client
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sanwo
4
+ VERSION = "0.1.0"
5
+ end
data/lib/sanwo.rb ADDED
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "sanwo/version"
4
+ require_relative "sanwo/providers"
5
+ require_relative "sanwo/client"
6
+
7
+ if defined?(Rails)
8
+ require_relative "sanwo/rails/engine"
9
+ end
metadata ADDED
@@ -0,0 +1,110 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sanwo
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - SanwoHQ
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2026-07-30 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: erb
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: rails
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '6.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '6.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '13.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '13.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.0'
69
+ description: Server-side HTML generator for Sanwo checkout — generates HTML/JS that
70
+ loads the Sanwo embed script from CDN and calls Sanwo.create() + .checkout().
71
+ email:
72
+ - hello@sanwohq.com
73
+ executables: []
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - README.md
78
+ - lib/sanwo.rb
79
+ - lib/sanwo/client.rb
80
+ - lib/sanwo/providers.rb
81
+ - lib/sanwo/rails/engine.rb
82
+ - lib/sanwo/rails/helper.rb
83
+ - lib/sanwo/version.rb
84
+ homepage: https://sanwohq.com
85
+ licenses:
86
+ - Apache-2.0
87
+ metadata:
88
+ homepage_uri: https://sanwohq.com
89
+ source_code_uri: https://github.com/Sanwohq/ruby
90
+ changelog_uri: https://github.com/Sanwohq/ruby/blob/main/CHANGELOG.md
91
+ post_install_message:
92
+ rdoc_options: []
93
+ require_paths:
94
+ - lib
95
+ required_ruby_version: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - ">="
98
+ - !ruby/object:Gem::Version
99
+ version: '3.0'
100
+ required_rubygems_version: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ version: '0'
105
+ requirements: []
106
+ rubygems_version: 3.0.3.1
107
+ signing_key:
108
+ specification_version: 4
109
+ summary: Universal payment SDK for Ruby
110
+ test_files: []