payify 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/.env.dist +2 -0
- data/.rubocop.yml +66 -0
- data/Gemfile +28 -0
- data/Gemfile.lock +249 -0
- data/LICENSE +21 -0
- data/README.md +164 -0
- data/Rakefile +14 -0
- data/app/assets/config/payify_manifest.js +1 -0
- data/app/assets/images/payify/.keep +0 -0
- data/app/assets/images/payify/screenshot.png +0 -0
- data/app/assets/images/payify/vat.png +0 -0
- data/app/assets/stylesheets/payify/application.css +15 -0
- data/app/controllers/concerns/.keep +0 -0
- data/app/controllers/payify/payments_controller.rb +29 -0
- data/app/helpers/payify/application_helper.rb +4 -0
- data/app/jobs/payify/application_job.rb +4 -0
- data/app/mailers/payify/application_mailer.rb +6 -0
- data/app/models/concerns/payify/has_payment_concern.rb +28 -0
- data/app/models/concerns/payify/stripe_payment_concern.rb +33 -0
- data/app/models/payify/application_record.rb +5 -0
- data/app/models/payify/payment.rb +35 -0
- data/app/serializers/payify/payment_serializer.rb +10 -0
- data/app/views/payify/application.html.erb +15 -0
- data/app/views/payify/payments/_form.html.erb +46 -0
- data/app/views/payify/payments/complete.html.erb +26 -0
- data/app/views/payify/payments/new.html.erb +24 -0
- data/config/locales/payify.en.yml +7 -0
- data/config/routes.rb +8 -0
- data/db/migrate/20230611124944_create_payment.rb +15 -0
- data/db/migrate/20230617052430_add_stripe_payment_intent_id.rb +6 -0
- data/lib/payify/engine.rb +5 -0
- data/lib/payify/version.rb +5 -0
- data/lib/payify.rb +25 -0
- data/lib/stripe/client.rb +31 -0
- data/lib/tasks/payify_tasks.rake +4 -0
- data/payify.gemspec +27 -0
- data/sig/payify.rbs +4 -0
- metadata +95 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 65e38363d021bd6956ef7a915c235ecca2f48c3d8175895a671d3a801f323408
|
4
|
+
data.tar.gz: 6123dd61b176a86860396f93e3f50605169551056a278928d333dc6571c46990
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 572d1098b2a7305ac4bad08eec7560422ec9a9661d352bf78536ac9a3e8315e36984b1ab8d362a28bab8a1db18731c924d1a068f83aa81130600044404dd2da9
|
7
|
+
data.tar.gz: 2b7f1129bf709e830c305b43c7ca03f80ebf48c04a676f1eeab765ecfbf4450f7057dc9ce22a26e212fd2ed65d256ecaa45d738b412a09daa5145b5dcc7a078c
|
data/.env.dist
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
AllCops:
|
2
|
+
Exclude:
|
3
|
+
- '**/bin/**/*'
|
4
|
+
- '**/test/**/*'
|
5
|
+
- '**/db/**/*'
|
6
|
+
- '**/config/**/*'
|
7
|
+
Security/YAMLLoad:
|
8
|
+
Enabled: false
|
9
|
+
Style/EmptyMethod:
|
10
|
+
EnforcedStyle: expanded
|
11
|
+
Metrics/AbcSize:
|
12
|
+
Max: 26
|
13
|
+
Metrics/CyclomaticComplexity:
|
14
|
+
Max: 12
|
15
|
+
Metrics/PerceivedComplexity:
|
16
|
+
Max: 12
|
17
|
+
Layout/LineLength:
|
18
|
+
Max: 160
|
19
|
+
Layout/FirstHashElementIndentation:
|
20
|
+
Enabled: false
|
21
|
+
Layout/ArgumentAlignment:
|
22
|
+
Enabled: false
|
23
|
+
Layout/HashAlignment:
|
24
|
+
Enabled: false
|
25
|
+
Metrics/MethodLength:
|
26
|
+
Max: 20
|
27
|
+
Metrics/ClassLength:
|
28
|
+
Max: 300
|
29
|
+
Metrics/BlockLength:
|
30
|
+
Max: 2000
|
31
|
+
Metrics/ModuleLength:
|
32
|
+
Max: 300
|
33
|
+
Style/Documentation:
|
34
|
+
Enabled: false
|
35
|
+
Style/FrozenStringLiteralComment:
|
36
|
+
Enabled: false
|
37
|
+
Style/OptionalBooleanParameter:
|
38
|
+
Enabled: false
|
39
|
+
Style/MethodDefParentheses:
|
40
|
+
Enabled: true
|
41
|
+
Style/StringLiterals:
|
42
|
+
EnforcedStyle: double_quotes
|
43
|
+
Style/RescueStandardError:
|
44
|
+
EnforcedStyle: implicit
|
45
|
+
Naming/VariableNumber:
|
46
|
+
CheckSymbols: false
|
47
|
+
Style/VariableNumber:
|
48
|
+
Enabled: false
|
49
|
+
Style/HashSyntax:
|
50
|
+
EnforcedShorthandSyntax: never
|
51
|
+
Gemspec/RequiredRubyVersion:
|
52
|
+
Enabled: false
|
53
|
+
Style/WordArray:
|
54
|
+
Enabled: false
|
55
|
+
Style/SymbolArray:
|
56
|
+
Enabled: false
|
57
|
+
Lint/Debugger:
|
58
|
+
Enabled: false
|
59
|
+
Layout/MultilineMethodCallIndentation:
|
60
|
+
Enabled: false
|
61
|
+
Style/TrailingCommaInArguments:
|
62
|
+
EnforcedStyleForMultiline: comma
|
63
|
+
Layout/ArrayAlignment:
|
64
|
+
EnforcedStyle: with_fixed_indentation
|
65
|
+
Style/ClassVars:
|
66
|
+
Enabled: false
|
data/Gemfile
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
source "https://rubygems.org"
|
4
|
+
|
5
|
+
# Specify your gem's dependencies in payify.gemspec
|
6
|
+
gemspec
|
7
|
+
|
8
|
+
gem "puma"
|
9
|
+
gem "rails", "~> 7.0.4"
|
10
|
+
gem "sprockets-rails"
|
11
|
+
gem "sqlite3"
|
12
|
+
|
13
|
+
group :development, :test do
|
14
|
+
gem "rails-controller-testing"
|
15
|
+
gem "rspec-rails"
|
16
|
+
gem "rubocop", "~> 1.21"
|
17
|
+
|
18
|
+
gem "factory_bot_rails"
|
19
|
+
gem "faker"
|
20
|
+
end
|
21
|
+
|
22
|
+
gem "rake", "~> 13.0.6"
|
23
|
+
|
24
|
+
gem "active_model_serializers"
|
25
|
+
gem "dotenv-rails", "~> 2.8.1"
|
26
|
+
gem "money", "~> 6.16"
|
27
|
+
gem "stripe", "~> 8.5.0"
|
28
|
+
gem "timecop", "~> 0.9.6"
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,249 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
payify (0.1.0)
|
5
|
+
rails (>= 7.0)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: https://rubygems.org/
|
9
|
+
specs:
|
10
|
+
actioncable (7.0.5)
|
11
|
+
actionpack (= 7.0.5)
|
12
|
+
activesupport (= 7.0.5)
|
13
|
+
nio4r (~> 2.0)
|
14
|
+
websocket-driver (>= 0.6.1)
|
15
|
+
actionmailbox (7.0.5)
|
16
|
+
actionpack (= 7.0.5)
|
17
|
+
activejob (= 7.0.5)
|
18
|
+
activerecord (= 7.0.5)
|
19
|
+
activestorage (= 7.0.5)
|
20
|
+
activesupport (= 7.0.5)
|
21
|
+
mail (>= 2.7.1)
|
22
|
+
net-imap
|
23
|
+
net-pop
|
24
|
+
net-smtp
|
25
|
+
actionmailer (7.0.5)
|
26
|
+
actionpack (= 7.0.5)
|
27
|
+
actionview (= 7.0.5)
|
28
|
+
activejob (= 7.0.5)
|
29
|
+
activesupport (= 7.0.5)
|
30
|
+
mail (~> 2.5, >= 2.5.4)
|
31
|
+
net-imap
|
32
|
+
net-pop
|
33
|
+
net-smtp
|
34
|
+
rails-dom-testing (~> 2.0)
|
35
|
+
actionpack (7.0.5)
|
36
|
+
actionview (= 7.0.5)
|
37
|
+
activesupport (= 7.0.5)
|
38
|
+
rack (~> 2.0, >= 2.2.4)
|
39
|
+
rack-test (>= 0.6.3)
|
40
|
+
rails-dom-testing (~> 2.0)
|
41
|
+
rails-html-sanitizer (~> 1.0, >= 1.2.0)
|
42
|
+
actiontext (7.0.5)
|
43
|
+
actionpack (= 7.0.5)
|
44
|
+
activerecord (= 7.0.5)
|
45
|
+
activestorage (= 7.0.5)
|
46
|
+
activesupport (= 7.0.5)
|
47
|
+
globalid (>= 0.6.0)
|
48
|
+
nokogiri (>= 1.8.5)
|
49
|
+
actionview (7.0.5)
|
50
|
+
activesupport (= 7.0.5)
|
51
|
+
builder (~> 3.1)
|
52
|
+
erubi (~> 1.4)
|
53
|
+
rails-dom-testing (~> 2.0)
|
54
|
+
rails-html-sanitizer (~> 1.1, >= 1.2.0)
|
55
|
+
active_model_serializers (0.10.13)
|
56
|
+
actionpack (>= 4.1, < 7.1)
|
57
|
+
activemodel (>= 4.1, < 7.1)
|
58
|
+
case_transform (>= 0.2)
|
59
|
+
jsonapi-renderer (>= 0.1.1.beta1, < 0.3)
|
60
|
+
activejob (7.0.5)
|
61
|
+
activesupport (= 7.0.5)
|
62
|
+
globalid (>= 0.3.6)
|
63
|
+
activemodel (7.0.5)
|
64
|
+
activesupport (= 7.0.5)
|
65
|
+
activerecord (7.0.5)
|
66
|
+
activemodel (= 7.0.5)
|
67
|
+
activesupport (= 7.0.5)
|
68
|
+
activestorage (7.0.5)
|
69
|
+
actionpack (= 7.0.5)
|
70
|
+
activejob (= 7.0.5)
|
71
|
+
activerecord (= 7.0.5)
|
72
|
+
activesupport (= 7.0.5)
|
73
|
+
marcel (~> 1.0)
|
74
|
+
mini_mime (>= 1.1.0)
|
75
|
+
activesupport (7.0.5)
|
76
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
77
|
+
i18n (>= 1.6, < 2)
|
78
|
+
minitest (>= 5.1)
|
79
|
+
tzinfo (~> 2.0)
|
80
|
+
ast (2.4.2)
|
81
|
+
builder (3.2.4)
|
82
|
+
case_transform (0.2)
|
83
|
+
activesupport
|
84
|
+
concurrent-ruby (1.2.2)
|
85
|
+
crass (1.0.6)
|
86
|
+
date (3.3.3)
|
87
|
+
diff-lcs (1.5.0)
|
88
|
+
dotenv (2.8.1)
|
89
|
+
dotenv-rails (2.8.1)
|
90
|
+
dotenv (= 2.8.1)
|
91
|
+
railties (>= 3.2)
|
92
|
+
erubi (1.12.0)
|
93
|
+
factory_bot (6.2.1)
|
94
|
+
activesupport (>= 5.0.0)
|
95
|
+
factory_bot_rails (6.2.0)
|
96
|
+
factory_bot (~> 6.2.0)
|
97
|
+
railties (>= 5.0.0)
|
98
|
+
faker (3.2.0)
|
99
|
+
i18n (>= 1.8.11, < 2)
|
100
|
+
globalid (1.1.0)
|
101
|
+
activesupport (>= 5.0)
|
102
|
+
i18n (1.14.1)
|
103
|
+
concurrent-ruby (~> 1.0)
|
104
|
+
json (2.6.3)
|
105
|
+
jsonapi-renderer (0.2.2)
|
106
|
+
loofah (2.21.3)
|
107
|
+
crass (~> 1.0.2)
|
108
|
+
nokogiri (>= 1.12.0)
|
109
|
+
mail (2.8.1)
|
110
|
+
mini_mime (>= 0.1.1)
|
111
|
+
net-imap
|
112
|
+
net-pop
|
113
|
+
net-smtp
|
114
|
+
marcel (1.0.2)
|
115
|
+
method_source (1.0.0)
|
116
|
+
mini_mime (1.1.2)
|
117
|
+
minitest (5.18.0)
|
118
|
+
money (6.16.0)
|
119
|
+
i18n (>= 0.6.4, <= 2)
|
120
|
+
net-imap (0.3.4)
|
121
|
+
date
|
122
|
+
net-protocol
|
123
|
+
net-pop (0.1.2)
|
124
|
+
net-protocol
|
125
|
+
net-protocol (0.2.1)
|
126
|
+
timeout
|
127
|
+
net-smtp (0.3.3)
|
128
|
+
net-protocol
|
129
|
+
nio4r (2.5.9)
|
130
|
+
nokogiri (1.15.2-x86_64-linux)
|
131
|
+
racc (~> 1.4)
|
132
|
+
parallel (1.23.0)
|
133
|
+
parser (3.2.2.3)
|
134
|
+
ast (~> 2.4.1)
|
135
|
+
racc
|
136
|
+
puma (6.3.0)
|
137
|
+
nio4r (~> 2.0)
|
138
|
+
racc (1.7.0)
|
139
|
+
rack (2.2.7)
|
140
|
+
rack-test (2.1.0)
|
141
|
+
rack (>= 1.3)
|
142
|
+
rails (7.0.5)
|
143
|
+
actioncable (= 7.0.5)
|
144
|
+
actionmailbox (= 7.0.5)
|
145
|
+
actionmailer (= 7.0.5)
|
146
|
+
actionpack (= 7.0.5)
|
147
|
+
actiontext (= 7.0.5)
|
148
|
+
actionview (= 7.0.5)
|
149
|
+
activejob (= 7.0.5)
|
150
|
+
activemodel (= 7.0.5)
|
151
|
+
activerecord (= 7.0.5)
|
152
|
+
activestorage (= 7.0.5)
|
153
|
+
activesupport (= 7.0.5)
|
154
|
+
bundler (>= 1.15.0)
|
155
|
+
railties (= 7.0.5)
|
156
|
+
rails-controller-testing (1.0.5)
|
157
|
+
actionpack (>= 5.0.1.rc1)
|
158
|
+
actionview (>= 5.0.1.rc1)
|
159
|
+
activesupport (>= 5.0.1.rc1)
|
160
|
+
rails-dom-testing (2.0.3)
|
161
|
+
activesupport (>= 4.2.0)
|
162
|
+
nokogiri (>= 1.6)
|
163
|
+
rails-html-sanitizer (1.6.0)
|
164
|
+
loofah (~> 2.21)
|
165
|
+
nokogiri (~> 1.14)
|
166
|
+
railties (7.0.5)
|
167
|
+
actionpack (= 7.0.5)
|
168
|
+
activesupport (= 7.0.5)
|
169
|
+
method_source
|
170
|
+
rake (>= 12.2)
|
171
|
+
thor (~> 1.0)
|
172
|
+
zeitwerk (~> 2.5)
|
173
|
+
rainbow (3.1.1)
|
174
|
+
rake (13.0.6)
|
175
|
+
regexp_parser (2.8.1)
|
176
|
+
rexml (3.2.5)
|
177
|
+
rspec-core (3.12.2)
|
178
|
+
rspec-support (~> 3.12.0)
|
179
|
+
rspec-expectations (3.12.3)
|
180
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
181
|
+
rspec-support (~> 3.12.0)
|
182
|
+
rspec-mocks (3.12.5)
|
183
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
184
|
+
rspec-support (~> 3.12.0)
|
185
|
+
rspec-rails (6.0.3)
|
186
|
+
actionpack (>= 6.1)
|
187
|
+
activesupport (>= 6.1)
|
188
|
+
railties (>= 6.1)
|
189
|
+
rspec-core (~> 3.12)
|
190
|
+
rspec-expectations (~> 3.12)
|
191
|
+
rspec-mocks (~> 3.12)
|
192
|
+
rspec-support (~> 3.12)
|
193
|
+
rspec-support (3.12.0)
|
194
|
+
rubocop (1.52.0)
|
195
|
+
json (~> 2.3)
|
196
|
+
parallel (~> 1.10)
|
197
|
+
parser (>= 3.2.0.0)
|
198
|
+
rainbow (>= 2.2.2, < 4.0)
|
199
|
+
regexp_parser (>= 1.8, < 3.0)
|
200
|
+
rexml (>= 3.2.5, < 4.0)
|
201
|
+
rubocop-ast (>= 1.28.0, < 2.0)
|
202
|
+
ruby-progressbar (~> 1.7)
|
203
|
+
unicode-display_width (>= 2.4.0, < 3.0)
|
204
|
+
rubocop-ast (1.29.0)
|
205
|
+
parser (>= 3.2.1.0)
|
206
|
+
ruby-progressbar (1.13.0)
|
207
|
+
sprockets (4.2.0)
|
208
|
+
concurrent-ruby (~> 1.0)
|
209
|
+
rack (>= 2.2.4, < 4)
|
210
|
+
sprockets-rails (3.4.2)
|
211
|
+
actionpack (>= 5.2)
|
212
|
+
activesupport (>= 5.2)
|
213
|
+
sprockets (>= 3.0.0)
|
214
|
+
sqlite3 (1.6.3-x86_64-linux)
|
215
|
+
stripe (8.5.0)
|
216
|
+
thor (1.2.2)
|
217
|
+
timecop (0.9.6)
|
218
|
+
timeout (0.3.2)
|
219
|
+
tzinfo (2.0.6)
|
220
|
+
concurrent-ruby (~> 1.0)
|
221
|
+
unicode-display_width (2.4.2)
|
222
|
+
websocket-driver (0.7.5)
|
223
|
+
websocket-extensions (>= 0.1.0)
|
224
|
+
websocket-extensions (0.1.5)
|
225
|
+
zeitwerk (2.6.8)
|
226
|
+
|
227
|
+
PLATFORMS
|
228
|
+
x86_64-linux
|
229
|
+
|
230
|
+
DEPENDENCIES
|
231
|
+
active_model_serializers
|
232
|
+
dotenv-rails (~> 2.8.1)
|
233
|
+
factory_bot_rails
|
234
|
+
faker
|
235
|
+
money (~> 6.16)
|
236
|
+
payify!
|
237
|
+
puma
|
238
|
+
rails (~> 7.0.4)
|
239
|
+
rails-controller-testing
|
240
|
+
rake (~> 13.0.6)
|
241
|
+
rspec-rails
|
242
|
+
rubocop (~> 1.21)
|
243
|
+
sprockets-rails
|
244
|
+
sqlite3
|
245
|
+
stripe (~> 8.5.0)
|
246
|
+
timecop (~> 0.9.6)
|
247
|
+
|
248
|
+
BUNDLED WITH
|
249
|
+
2.4.12
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2023 Nathan Lopez
|
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,164 @@
|
|
1
|
+
# Payify
|
2
|
+
|
3
|
+
PayifyRails is a Ruby gem that simplifies payment integration into Ruby on Rails projects. It allows to easily add payment functionality to your models. For example, by applying the HasPaymentConcern to a reservation model, you can manage the payment process for reservations seamlessly.
|
4
|
+
|
5
|
+

|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
To install the gem add it into a Gemfile (Bundler):
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
gem 'payify', git: 'https://github.com/andrewdsilva/payify'
|
13
|
+
```
|
14
|
+
|
15
|
+
And then execute:
|
16
|
+
|
17
|
+
```
|
18
|
+
bundle install
|
19
|
+
```
|
20
|
+
|
21
|
+
## Features ✅
|
22
|
+
|
23
|
+
- Includes a Payment model
|
24
|
+
- Provides concerns to easily add a payment system to your model
|
25
|
+
- Enables payment with Stripe
|
26
|
+
- Offers a payment form to integrate into your application
|
27
|
+
- Allows payment status to be checked via an api route
|
28
|
+
- Allows configuration of payment (currency, VAT)
|
29
|
+
- Provides a user interface for payment processing
|
30
|
+
- Enables management of payment status (pending, paid)
|
31
|
+
|
32
|
+
## Configuration
|
33
|
+
|
34
|
+
For now, Payify uses Stripe as the payment gateway. You can configure the currency using an initializer.
|
35
|
+
|
36
|
+
```ruby
|
37
|
+
# initializers/payify.rb
|
38
|
+
Payify.setup do |config|
|
39
|
+
config.currency = "usd"
|
40
|
+
config.default_tax_rates_id = "eur"
|
41
|
+
end
|
42
|
+
```
|
43
|
+
|
44
|
+
To handle VAT or different tax rates on your payments, you need to create tax rates on Stripe and define the default_tax_rates_id or, alternatively, define the tax_rates_id method on your payment-related models. Leave it empty if you don't manage any taxes.
|
45
|
+
|
46
|
+
You can set your Stripe API credentials using environment variables. (Secret key, Publishable key)
|
47
|
+
|
48
|
+
```ruby
|
49
|
+
# .env
|
50
|
+
STRIPE_API_KEY="..."
|
51
|
+
STRIPE_PUBLISHABLE_KEY="..."
|
52
|
+
```
|
53
|
+
|
54
|
+
## Usage
|
55
|
+
|
56
|
+
To enable payment functionality for a model, simply include the HasPayment concern:
|
57
|
+
|
58
|
+
```ruby
|
59
|
+
class Reservation < ApplicationRecord
|
60
|
+
include Payify::HasPaymentConcern
|
61
|
+
|
62
|
+
def ammount_to_pay
|
63
|
+
self.price
|
64
|
+
end
|
65
|
+
|
66
|
+
# Optional : Override the default tax rates id
|
67
|
+
def tax_rates_id
|
68
|
+
'txr_1234567890'
|
69
|
+
end
|
70
|
+
end
|
71
|
+
```
|
72
|
+
|
73
|
+
When you want to request a payment for a model on which you added the concern, you just need to call the create_payment method.
|
74
|
+
|
75
|
+
```ruby
|
76
|
+
reservation_1.create_payment
|
77
|
+
```
|
78
|
+
|
79
|
+
Then you can find the id of the new pending payment with payment.id.
|
80
|
+
|
81
|
+
```ruby
|
82
|
+
reservation_1.payment.id
|
83
|
+
```
|
84
|
+
|
85
|
+
Now you just have to redirect the user to `/payments/:id/new` or include the payment form in your page.
|
86
|
+
|
87
|
+
```ruby
|
88
|
+
# reservation/show.html.erb
|
89
|
+
<%= render "payify/payments/form", payment: @payment %>
|
90
|
+
```
|
91
|
+
|
92
|
+
After completing the payment process, the user will be redirected to:
|
93
|
+
|
94
|
+
```
|
95
|
+
/payments/:id/complete
|
96
|
+
```
|
97
|
+
|
98
|
+
The application will then verify the payment status with Stripe. You can do it manually calling the following method:
|
99
|
+
|
100
|
+
```ruby
|
101
|
+
@payment.stripe_confirm_payment
|
102
|
+
```
|
103
|
+
|
104
|
+
If the payment has been successfully processed, a confirmation message will be displayed to the user. The payment method `paid?` will return `true`.
|
105
|
+
|
106
|
+
To customize the page that displays the payment status, you can create the following file:
|
107
|
+
|
108
|
+
```ruby
|
109
|
+
# views/payify/payments/complete.html.erb
|
110
|
+
|
111
|
+
<% if @payment.paid? %>
|
112
|
+
<div class="alert alert-success" role="alert">
|
113
|
+
<%= I18n.t("payments.complete.paid") %>
|
114
|
+
</div>
|
115
|
+
<% else %>
|
116
|
+
<div class="alert alert-danger" role="alert">
|
117
|
+
<%= I18n.t("payments.complete.pending") %>
|
118
|
+
</div>
|
119
|
+
<% end %>
|
120
|
+
```
|
121
|
+
|
122
|
+
## API
|
123
|
+
|
124
|
+
If you prefer using the Payify API, after creating the payment object, you can initialize a new Stripe payment by making a request to: `/payments/:id/new.json`
|
125
|
+
|
126
|
+
This request will return a JSON response containing the `stripe_payment_intent_id` and `stripe_client_secret` required to process a payment using Stripe.
|
127
|
+
|
128
|
+
After making the payment, you can make a request to the following endpoint to update the payment status and retrieve its current state:
|
129
|
+
|
130
|
+
```
|
131
|
+
/payments/:id/complete.json
|
132
|
+
```
|
133
|
+
|
134
|
+
## Status
|
135
|
+
|
136
|
+
You can access the payment status using `@payment.status`. The possible statuses are:
|
137
|
+
|
138
|
+
- `pending`: The payment is still being processed.
|
139
|
+
- `paid`: The payment has been successfully completed.
|
140
|
+
- `failed`: The payment has failed.
|
141
|
+
|
142
|
+
## Tests
|
143
|
+
|
144
|
+
To run the tests, execute `rspec` command.
|
145
|
+
|
146
|
+
You can test the payment process in your web browser by following these steps:
|
147
|
+
|
148
|
+
1. Navigate to the `test/dummy` directory.
|
149
|
+
2. Run the following commands:
|
150
|
+
- `rails db:create`
|
151
|
+
- `rails db:migrate`
|
152
|
+
- `rails db:seed`
|
153
|
+
- `rails s`
|
154
|
+
3. Open your browser and go to: [http://localhost:3000/](http://localhost:3000/).
|
155
|
+
|
156
|
+
Each time you run the seed command, your test database will be reset with a reservation that has a payment with an ID of 1. This allows you to test the payment process at the following address: [http://localhost:3000/payments/1/new](http://localhost:3000/payments/1/new).
|
157
|
+
|
158
|
+
## Contributing
|
159
|
+
|
160
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/andrewdsilva/payify.
|
161
|
+
|
162
|
+
## License
|
163
|
+
|
164
|
+
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,14 @@
|
|
1
|
+
require "bundler/setup"
|
2
|
+
|
3
|
+
APP_RAKEFILE = File.expand_path("test/dummy/Rakefile", __dir__)
|
4
|
+
load "rails/tasks/engine.rake"
|
5
|
+
|
6
|
+
load "rails/tasks/statistics.rake"
|
7
|
+
|
8
|
+
require "bundler/gem_tasks"
|
9
|
+
|
10
|
+
require "rubocop/rake_task"
|
11
|
+
|
12
|
+
RuboCop::RakeTask.new
|
13
|
+
|
14
|
+
task default: :rubocop
|
@@ -0,0 +1 @@
|
|
1
|
+
//= link_directory ../stylesheets/payify .css
|
File without changes
|
Binary file
|
Binary file
|
@@ -0,0 +1,15 @@
|
|
1
|
+
/*
|
2
|
+
* This is a manifest file that'll be compiled into application.css, which will include all the files
|
3
|
+
* listed below.
|
4
|
+
*
|
5
|
+
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
|
6
|
+
* or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
|
7
|
+
*
|
8
|
+
* You're free to add application-wide styles to this file and they'll appear at the bottom of the
|
9
|
+
* compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
|
10
|
+
* files in this directory. Styles in this file should be added after the last require_* statement.
|
11
|
+
* It is generally better to create a new file per style scope.
|
12
|
+
*
|
13
|
+
*= require_tree .
|
14
|
+
*= require_self
|
15
|
+
*/
|
File without changes
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Payify
|
2
|
+
class PaymentsController < ActionController::Base
|
3
|
+
before_action :set_object, only: %i[new complete]
|
4
|
+
|
5
|
+
def new
|
6
|
+
@payment.stripe_init_intent
|
7
|
+
|
8
|
+
respond_to do |format|
|
9
|
+
format.html
|
10
|
+
format.json { render json: @payment }
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def complete
|
15
|
+
@payment.stripe_confirm_payment unless @payment.paid?
|
16
|
+
|
17
|
+
respond_to do |format|
|
18
|
+
format.html
|
19
|
+
format.json { render json: @payment }
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def set_object
|
26
|
+
@payment = Payment.find(params[:id])
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Payify
|
2
|
+
module HasPaymentConcern
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
has_one :payment, as: :model, dependent: :destroy, class_name: "Payify::Payment"
|
7
|
+
end
|
8
|
+
|
9
|
+
def create_payment
|
10
|
+
build_payment(amount: amount_to_pay)
|
11
|
+
|
12
|
+
payment.save
|
13
|
+
payment
|
14
|
+
end
|
15
|
+
|
16
|
+
def cancel_payment
|
17
|
+
payment.destroy if payment.present?
|
18
|
+
end
|
19
|
+
|
20
|
+
def amount_to_pay
|
21
|
+
raise NotImplementedError, "The 'amount_to_pay' method must be implemented in the including model."
|
22
|
+
end
|
23
|
+
|
24
|
+
def tax_rates_id
|
25
|
+
nil
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require "stripe/client"
|
2
|
+
|
3
|
+
module Payify
|
4
|
+
module StripePaymentConcern
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
def stripe_client
|
8
|
+
@stripe_client ||= Stripe::Client.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def stripe_init_intent
|
12
|
+
return unless stripe_payment_inent_id.nil? && !paid?
|
13
|
+
|
14
|
+
intent = stripe_client.create_payment_intent(amount, tax_id)
|
15
|
+
|
16
|
+
update_attribute(:stripe_payment_inent_id, intent.id)
|
17
|
+
update_attribute(:stripe_client_secret, intent.client_secret)
|
18
|
+
end
|
19
|
+
|
20
|
+
def stripe_confirm_payment
|
21
|
+
intent = stripe_client.find_intent(stripe_payment_inent_id)
|
22
|
+
|
23
|
+
return unless intent["status"] == "succeeded"
|
24
|
+
|
25
|
+
update_attribute(:paid_at, Time.now)
|
26
|
+
update_attribute(:status, Payify::Payment.statuses[:paid])
|
27
|
+
end
|
28
|
+
|
29
|
+
def tax_id
|
30
|
+
model&.tax_rates_id || Payify.default_tax_rates_id
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Payify
|
2
|
+
class Payment < ApplicationRecord
|
3
|
+
include ::Payify::StripePaymentConcern
|
4
|
+
|
5
|
+
enum status: { pending: 0, paid: 1, failed: 2 }
|
6
|
+
enum payment_method: { stripe: 0 }
|
7
|
+
|
8
|
+
belongs_to :model, polymorphic: true, optional: true
|
9
|
+
|
10
|
+
validates :amount, presence: true, numericality: { greater_than_or_equal_to: 0 }
|
11
|
+
validates :status, presence: true
|
12
|
+
|
13
|
+
before_destroy :cannot_destroy_if_paid
|
14
|
+
|
15
|
+
def make_payment(transaction_id)
|
16
|
+
self.transaction_id = transaction_id
|
17
|
+
self.paid_at = Time.current
|
18
|
+
self.status = :paid
|
19
|
+
end
|
20
|
+
|
21
|
+
def can_destroy?
|
22
|
+
!paid?
|
23
|
+
end
|
24
|
+
|
25
|
+
def paid?
|
26
|
+
paid_at.present?
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def cannot_destroy_if_paid
|
32
|
+
raise ActiveRecord::RecordNotDestroyed if paid?
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
module Payify
|
2
|
+
class PaymentSerializer < ActiveModel::Serializer
|
3
|
+
attributes :id, :amount, :status, :payment_method, :transaction_id, :paid_at
|
4
|
+
|
5
|
+
attributes :stripe_payment_inent_id, :stripe_client_secret
|
6
|
+
|
7
|
+
attribute(:paid) { object.paid? }
|
8
|
+
attribute(:can_destroy) { object.can_destroy? }
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
<script src="https://js.stripe.com/v3/"></script>
|
2
|
+
|
3
|
+
<%= form_with(id: "payment-form", url: complete_payment_path, method: "post") do |form| %>
|
4
|
+
<div id="payment-element">
|
5
|
+
</div>
|
6
|
+
|
7
|
+
<div id="error-message">
|
8
|
+
</div>
|
9
|
+
|
10
|
+
<%= form.submit I18n.t("payments.submit"), id: "submit", class: "btn btn-primary w-100 mt-4" %>
|
11
|
+
<% end %>
|
12
|
+
|
13
|
+
<script>
|
14
|
+
const stripe = Stripe("<%= Payify.stripe_publishable_key %>");
|
15
|
+
|
16
|
+
const options = {
|
17
|
+
clientSecret: "<%= payment.stripe_client_secret %>",
|
18
|
+
};
|
19
|
+
|
20
|
+
const elements = stripe.elements(options);
|
21
|
+
|
22
|
+
const paymentElement = elements.create("payment");
|
23
|
+
paymentElement.mount("#payment-element");
|
24
|
+
|
25
|
+
const form = document.getElementById("payment-form");
|
26
|
+
|
27
|
+
form.addEventListener("submit", async (event) => {
|
28
|
+
event.preventDefault();
|
29
|
+
|
30
|
+
const {error} = await stripe.confirmPayment({
|
31
|
+
elements,
|
32
|
+
confirmParams: {
|
33
|
+
return_url: "<%= request.base_url %>/payments/<%= payment.id %>/complete",
|
34
|
+
},
|
35
|
+
});
|
36
|
+
|
37
|
+
if (error) {
|
38
|
+
const messageContainer = document.querySelector("#error-message");
|
39
|
+
messageContainer.textContent = error.message;
|
40
|
+
} else {
|
41
|
+
// Your customer will be redirected to your `return_url`. For some payment
|
42
|
+
// methods like iDEAL, your customer will be redirected to an intermediate
|
43
|
+
// site first to authorize the payment, then redirected to the `return_url`.
|
44
|
+
}
|
45
|
+
});
|
46
|
+
</script>
|
@@ -0,0 +1,26 @@
|
|
1
|
+
<!doctype html>
|
2
|
+
<html lang="en">
|
3
|
+
<head>
|
4
|
+
<meta charset="utf-8">
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
6
|
+
|
7
|
+
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
|
8
|
+
|
9
|
+
<title>Payment</title>
|
10
|
+
</head>
|
11
|
+
<body>
|
12
|
+
|
13
|
+
<div class="container py-5 my-5" style="max-width: 500px;">
|
14
|
+
<% if @payment.paid? %>
|
15
|
+
<div class="alert alert-success" role="alert">
|
16
|
+
<%= I18n.t("payments.complete.paid") %>
|
17
|
+
</div>
|
18
|
+
<% else %>
|
19
|
+
<div class="alert alert-danger" role="alert">
|
20
|
+
<%= I18n.t("payments.complete.pending") %>
|
21
|
+
</div>
|
22
|
+
<% end %>
|
23
|
+
</div>
|
24
|
+
|
25
|
+
</body>
|
26
|
+
</html>
|
@@ -0,0 +1,24 @@
|
|
1
|
+
<!doctype html>
|
2
|
+
<html lang="en">
|
3
|
+
<head>
|
4
|
+
<meta charset="utf-8">
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
6
|
+
|
7
|
+
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
|
8
|
+
|
9
|
+
<title>Payment</title>
|
10
|
+
</head>
|
11
|
+
<body>
|
12
|
+
|
13
|
+
<div class="container py-5 my-5" style="max-width: 500px;">
|
14
|
+
<% if @payment.paid? %>
|
15
|
+
<div class="alert alert-info" role="alert">
|
16
|
+
<%= I18n.t("payments.already_paid") %>
|
17
|
+
</div>
|
18
|
+
<% else %>
|
19
|
+
<%= render "form", payment: @payment %>
|
20
|
+
<% end %>
|
21
|
+
</div>
|
22
|
+
|
23
|
+
</body>
|
24
|
+
</html>
|
data/config/routes.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
class CreatePayment < ActiveRecord::Migration[7.0]
|
2
|
+
def change
|
3
|
+
create_table :payify_payments do |t|
|
4
|
+
t.decimal :amount, precision: 10, scale: 2, null: false
|
5
|
+
t.integer :status, default: 0, null: false
|
6
|
+
t.integer :payment_method
|
7
|
+
t.string :transaction_id
|
8
|
+
t.datetime :paid_at
|
9
|
+
t.string :model_type
|
10
|
+
t.integer :model_id
|
11
|
+
|
12
|
+
t.timestamps
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
data/lib/payify.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "payify/version"
|
4
|
+
require "payify/engine"
|
5
|
+
require "dotenv/load"
|
6
|
+
|
7
|
+
module Payify
|
8
|
+
class Error < StandardError; end
|
9
|
+
|
10
|
+
mattr_accessor :currency
|
11
|
+
@@currency = "usd"
|
12
|
+
|
13
|
+
mattr_accessor :default_tax_rates_id
|
14
|
+
@@default_tax_rates_id = nil
|
15
|
+
|
16
|
+
mattr_accessor :stripe_api_key
|
17
|
+
@@stripe_api_key = ENV["STRIPE_API_KEY"]
|
18
|
+
|
19
|
+
mattr_accessor :stripe_publishable_key
|
20
|
+
@@stripe_publishable_key = ENV["STRIPE_PUBLISHABLE_KEY"]
|
21
|
+
|
22
|
+
def self.setup
|
23
|
+
yield self
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Stripe
|
2
|
+
class Client
|
3
|
+
def initialize
|
4
|
+
stripe
|
5
|
+
|
6
|
+
Stripe.api_key = Payify.stripe_api_key
|
7
|
+
end
|
8
|
+
|
9
|
+
def stripe
|
10
|
+
@stripe ||= Stripe::StripeClient.new
|
11
|
+
end
|
12
|
+
|
13
|
+
def create_payment_intent(amount, tax_id = nil, object_invoice = "")
|
14
|
+
infos = {
|
15
|
+
amount: (amount * 100).to_i,
|
16
|
+
currency: Payify.currency,
|
17
|
+
description: object_invoice,
|
18
|
+
setup_future_usage: "off_session",
|
19
|
+
metadata: {
|
20
|
+
"tax_id": tax_id
|
21
|
+
}
|
22
|
+
}
|
23
|
+
|
24
|
+
Stripe::PaymentIntent.create(infos)
|
25
|
+
end
|
26
|
+
|
27
|
+
def find_intent(payment_intent_id)
|
28
|
+
Stripe::PaymentIntent.retrieve(payment_intent_id)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/payify.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "lib/payify/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "payify"
|
7
|
+
spec.version = Payify::VERSION
|
8
|
+
spec.authors = ["Nathan Lopez"]
|
9
|
+
spec.email = ["nathan.lopez042@gmail.com"]
|
10
|
+
|
11
|
+
spec.summary = "Payment integration for Ruby on Rails projects."
|
12
|
+
spec.description = "Payify is a Ruby gem that simplifies payment integration into Ruby on Rails projects."
|
13
|
+
spec.homepage = "https://github.com/andrewdsilva/payify"
|
14
|
+
spec.license = "MIT"
|
15
|
+
spec.required_ruby_version = ">= 2.6.0"
|
16
|
+
|
17
|
+
spec.files = Dir.chdir(__dir__) do
|
18
|
+
`git ls-files -z`.split("\x0").reject do |f|
|
19
|
+
(File.expand_path(f) == __FILE__) || f.start_with?(*%w[bin/ test/ spec/ features/ .git .circleci appveyor])
|
20
|
+
end
|
21
|
+
end
|
22
|
+
spec.bindir = "exe"
|
23
|
+
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
24
|
+
spec.require_paths = ["lib"]
|
25
|
+
|
26
|
+
spec.add_dependency "rails", "~> 7.0"
|
27
|
+
end
|
data/sig/payify.rbs
ADDED
metadata
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: payify
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Nathan Lopez
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2023-06-18 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rails
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '7.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '7.0'
|
27
|
+
description: Payify is a Ruby gem that simplifies payment integration into Ruby on
|
28
|
+
Rails projects.
|
29
|
+
email:
|
30
|
+
- nathan.lopez042@gmail.com
|
31
|
+
executables: []
|
32
|
+
extensions: []
|
33
|
+
extra_rdoc_files: []
|
34
|
+
files:
|
35
|
+
- ".env.dist"
|
36
|
+
- ".rubocop.yml"
|
37
|
+
- Gemfile
|
38
|
+
- Gemfile.lock
|
39
|
+
- LICENSE
|
40
|
+
- README.md
|
41
|
+
- Rakefile
|
42
|
+
- app/assets/config/payify_manifest.js
|
43
|
+
- app/assets/images/payify/.keep
|
44
|
+
- app/assets/images/payify/screenshot.png
|
45
|
+
- app/assets/images/payify/vat.png
|
46
|
+
- app/assets/stylesheets/payify/application.css
|
47
|
+
- app/controllers/concerns/.keep
|
48
|
+
- app/controllers/payify/payments_controller.rb
|
49
|
+
- app/helpers/payify/application_helper.rb
|
50
|
+
- app/jobs/payify/application_job.rb
|
51
|
+
- app/mailers/payify/application_mailer.rb
|
52
|
+
- app/models/concerns/payify/has_payment_concern.rb
|
53
|
+
- app/models/concerns/payify/stripe_payment_concern.rb
|
54
|
+
- app/models/payify/application_record.rb
|
55
|
+
- app/models/payify/payment.rb
|
56
|
+
- app/serializers/payify/payment_serializer.rb
|
57
|
+
- app/views/payify/application.html.erb
|
58
|
+
- app/views/payify/payments/_form.html.erb
|
59
|
+
- app/views/payify/payments/complete.html.erb
|
60
|
+
- app/views/payify/payments/new.html.erb
|
61
|
+
- config/locales/payify.en.yml
|
62
|
+
- config/routes.rb
|
63
|
+
- db/migrate/20230611124944_create_payment.rb
|
64
|
+
- db/migrate/20230617052430_add_stripe_payment_intent_id.rb
|
65
|
+
- lib/payify.rb
|
66
|
+
- lib/payify/engine.rb
|
67
|
+
- lib/payify/version.rb
|
68
|
+
- lib/stripe/client.rb
|
69
|
+
- lib/tasks/payify_tasks.rake
|
70
|
+
- payify.gemspec
|
71
|
+
- sig/payify.rbs
|
72
|
+
homepage: https://github.com/andrewdsilva/payify
|
73
|
+
licenses:
|
74
|
+
- MIT
|
75
|
+
metadata: {}
|
76
|
+
post_install_message:
|
77
|
+
rdoc_options: []
|
78
|
+
require_paths:
|
79
|
+
- lib
|
80
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
81
|
+
requirements:
|
82
|
+
- - ">="
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
version: 2.6.0
|
85
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
requirements: []
|
91
|
+
rubygems_version: 3.4.14
|
92
|
+
signing_key:
|
93
|
+
specification_version: 4
|
94
|
+
summary: Payment integration for Ruby on Rails projects.
|
95
|
+
test_files: []
|