creditcard-identifier 2.0.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: 2971d1554919d8eaf0fd916f87d9003cee3aae39d07e0b6f6de388d19eff31f1
4
+ data.tar.gz: c51129f59758f48b10b24f1e7f7f2d6ee336b62aa8e91fceed7ead69d1005fcb
5
+ SHA512:
6
+ metadata.gz: 0b831eb2196ceab513b4b2e77907abd454d14cfb13052dc0e14eea01ad964bfff247f13fdfb382bb0f9bb210c0409721853f02e414a6c05cf6dea8c87c030df0
7
+ data.tar.gz: 10f73f531f751c2f48f347daed4b1d2f726cd19495ae579d4a80b6d40e5b4ea6a2f8b694f17f80d6d46bb8327a1530a936743e86382b5453d92de98ab3559e6e
data/README.md ADDED
@@ -0,0 +1,153 @@
1
+ # Credit Card Identifier - Ruby Library
2
+
3
+ Ruby library for credit card BIN validation using bin-cc data.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'creditcard-identifier'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ ```bash
16
+ bundle install
17
+ ```
18
+
19
+ Or install it yourself as:
20
+
21
+ ```bash
22
+ gem install creditcard-identifier
23
+ ```
24
+
25
+ ## Usage
26
+
27
+ ### Module-level Functions
28
+
29
+ ```ruby
30
+ require 'creditcard_identifier'
31
+
32
+ # Identify card brand
33
+ brand = CreditcardIdentifier.find_brand('4012001037141112')
34
+ puts brand # 'visa'
35
+
36
+ # Check if card is supported
37
+ supported = CreditcardIdentifier.supported?('4012001037141112')
38
+ puts supported # true
39
+ ```
40
+
41
+ ### Using the Validator Class
42
+
43
+ ```ruby
44
+ require 'creditcard_identifier'
45
+
46
+ validator = CreditcardIdentifier::Validator.new
47
+
48
+ # Identify brand
49
+ brand = validator.find_brand('4012001037141112')
50
+ puts brand # 'visa'
51
+
52
+ # Check if supported
53
+ supported = validator.supported?('4012001037141112')
54
+ puts supported # true
55
+
56
+ # Validate CVV
57
+ valid = validator.validate_cvv('123', 'visa')
58
+ puts valid # true
59
+
60
+ # Get brand info
61
+ info = validator.get_brand_info('visa')
62
+ puts info['regexpBin']
63
+
64
+ # List all brands
65
+ brands = validator.list_brands
66
+ puts brands # ['elo', 'diners', 'visa', ...]
67
+ ```
68
+
69
+ ## API
70
+
71
+ ### Module Methods
72
+
73
+ #### `CreditcardIdentifier.find_brand(card_number)`
74
+ Identify the credit card brand.
75
+
76
+ **Parameters:**
77
+ - `card_number` (String): The credit card number
78
+
79
+ **Returns:** (String, nil) Brand name (e.g., 'visa', 'mastercard') or nil if not found
80
+
81
+ #### `CreditcardIdentifier.supported?(card_number)`
82
+ Check if the card number is supported.
83
+
84
+ **Parameters:**
85
+ - `card_number` (String): The credit card number
86
+
87
+ **Returns:** (Boolean) true if supported, false otherwise
88
+
89
+ ### Validator Class
90
+
91
+ #### `initialize(data_path = nil)`
92
+ Initialize validator with brand data.
93
+
94
+ **Parameters:**
95
+ - `data_path` (String, nil): Path to brands.json. If nil, uses bundled data.
96
+
97
+ #### `find_brand(card_number)`
98
+ Identify the credit card brand.
99
+
100
+ **Parameters:**
101
+ - `card_number` (String): The credit card number
102
+
103
+ **Returns:** (String, nil) Brand name or nil if not found
104
+
105
+ #### `supported?(card_number)`
106
+ Check if card number is supported.
107
+
108
+ **Parameters:**
109
+ - `card_number` (String): The credit card number
110
+
111
+ **Returns:** (Boolean) true if supported, false otherwise
112
+
113
+ #### `validate_cvv(cvv, brand_name)`
114
+ Validate CVV for a specific brand.
115
+
116
+ **Parameters:**
117
+ - `cvv` (String): CVV code
118
+ - `brand_name` (String): Brand name (e.g., 'visa', 'mastercard')
119
+
120
+ **Returns:** (Boolean) true if valid, false otherwise
121
+
122
+ #### `get_brand_info(brand_name)`
123
+ Get information about a specific brand.
124
+
125
+ **Parameters:**
126
+ - `brand_name` (String): Brand name
127
+
128
+ **Returns:** (Hash, nil) Brand information or nil if not found
129
+
130
+ #### `list_brands`
131
+ List all supported brands.
132
+
133
+ **Returns:** (Array<String>) List of brand names
134
+
135
+ ## Data Source
136
+
137
+ This library uses the BIN data from the [bin-cc project](https://github.com/renatovico/bin-cc).
138
+
139
+ The data is bundled with the gem, and can be updated by installing a newer version.
140
+
141
+ ## Development
142
+
143
+ After checking out the repo, run `bundle install` to install dependencies.
144
+
145
+ Run tests with:
146
+
147
+ ```bash
148
+ ruby test/test_validator.rb
149
+ ```
150
+
151
+ ## License
152
+
153
+ MIT
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Auto-generated by bin-cc build - DO NOT EDIT
4
+ # Generated: 2026-01-11T20:12:36.612Z
5
+
6
+ module CreditcardIdentifier
7
+ # Credit card brand data
8
+ BRANDS = [
9
+ {
10
+ name: "amex",
11
+ priority_over: [],
12
+ regexp_bin: /\A(3[47])/,
13
+ regexp_full: /\A(?=.{15}$)(?:3[47])[0-9]*\z/,
14
+ regexp_cvv: /\A\d{4}\z/,
15
+ },
16
+ {
17
+ name: "diners",
18
+ priority_over: [],
19
+ regexp_bin: /\A(3(?:0[0-5]|[68][0-9]))/,
20
+ regexp_full: /\A(?=.{14}$)(?:3(?:0[0-5]|[68][0-9]))[0-9]*\z/,
21
+ regexp_cvv: /\A\d{3}\z/,
22
+ },
23
+ {
24
+ name: "elo",
25
+ priority_over: ["visa", "aura", "discover", "banesecard"],
26
+ regexp_bin: /\A(401178|401179|431274|438935|451416|457393|457631|457632|504175|627780|636297|636368|(506699|5067[0-6]\d|50677[0-8])|(50900\d|5090[1-9]\d|509[1-9]\d{2})|65003[1-3]|(65003[5-9]|65004\d|65005[0-1])|(65040[5-9]|6504[1-3]\d)|(65048[5-9]|65049\d|6505[0-2]\d|65053[0-8])|(65054[1-9]|6505[5-8]\d|65059[0-8])|(65070\d|65071[0-8])|6507(2[0-7])|6509[0-9]|(65165[2-9]|6516[6-7]\d)|(65500\d|65501\d)|(65502[1-9]|6550[3-4]\d|65505[0-8]))/,
27
+ regexp_full: /\A(?=.{16}$)(?:401178|401179|431274|438935|451416|457393|457631|457632|504175|627780|636297|636368|(506699|5067[0-6]\d|50677[0-8])|(50900\d|5090[1-9]\d|509[1-9]\d{2})|65003[1-3]|(65003[5-9]|65004\d|65005[0-1])|(65040[5-9]|6504[1-3]\d)|(65048[5-9]|65049\d|6505[0-2]\d|65053[0-8])|(65054[1-9]|6505[5-8]\d|65059[0-8])|(65070\d|65071[0-8])|6507(2[0-7])|6509[0-9]|(65165[2-9]|6516[6-7]\d)|(65500\d|65501\d)|(65502[1-9]|6550[3-4]\d|65505[0-8]))[0-9]*\z/,
28
+ regexp_cvv: /\A\d{3}\z/,
29
+ },
30
+ {
31
+ name: "jcb",
32
+ priority_over: [],
33
+ regexp_bin: /\A((?:2131|1800|35))/,
34
+ regexp_full: /\A(?=.{16}$)(?:(?:2131|1800|35))[0-9]*\z/,
35
+ regexp_cvv: /\A\d{3}\z/,
36
+ },
37
+ {
38
+ name: "mastercard",
39
+ priority_over: [],
40
+ regexp_bin: /\A(5[1-5]|2[2-7][0-2][0-9])/,
41
+ regexp_full: /\A(?=.{16}$)(?:5[1-5]|2[2-7][0-2][0-9])[0-9]*\z/,
42
+ regexp_cvv: /\A\d{3}\z/,
43
+ },
44
+ {
45
+ name: "aura",
46
+ priority_over: [],
47
+ regexp_bin: /\A(50)/,
48
+ regexp_full: /\A(?=.{16,19}$)(?:50)[0-9]*\z/,
49
+ regexp_cvv: /\A\d{3}\z/,
50
+ },
51
+ {
52
+ name: "banesecard",
53
+ priority_over: ["discover"],
54
+ regexp_bin: /\A(636117|637473|637470|637472|650725|650046|650591|651668|651679)/,
55
+ regexp_full: /\A(?=.{16}$)(?:636117|637473|637470|637472|650725|650046|650591|651668|651679)[0-9]*\z/,
56
+ regexp_cvv: /\A\d{3}\z/,
57
+ },
58
+ {
59
+ name: "visa",
60
+ priority_over: [],
61
+ regexp_bin: /\A(4|6367)/,
62
+ regexp_full: /\A(?=.{13,16}$)(?:4|6367)[0-9]*\z/,
63
+ regexp_cvv: /\A\d{3}\z/,
64
+ },
65
+ {
66
+ name: "discover",
67
+ priority_over: ["hipercard"],
68
+ regexp_bin: /\A(6011|622|64|65)/,
69
+ regexp_full: /\A(?=.{16}$)(?:6011|622|64|65)[0-9]*\z/,
70
+ regexp_cvv: /\A\d{4}\z/,
71
+ },
72
+ {
73
+ name: "hipercard",
74
+ priority_over: [],
75
+ regexp_bin: /\A(38|60)/,
76
+ regexp_full: /\A(?=.{16,19}$)(?:38|60)[0-9]*\z/,
77
+ regexp_cvv: /\A\d{3}\z/,
78
+ },
79
+ ].freeze
80
+ end
@@ -0,0 +1,524 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Auto-generated by bin-cc build - DO NOT EDIT
4
+ # Generated: 2026-01-11T20:12:36.612Z
5
+
6
+ module CreditcardIdentifier
7
+ # Credit card brand data (detailed)
8
+ BRANDS_DETAILED = [
9
+ {
10
+ scheme: "amex",
11
+ brand: "American Express",
12
+ type: "credit",
13
+ number: {
14
+ lengths: [15],
15
+ luhn: true
16
+ },
17
+ cvv: {
18
+ length: 4
19
+ },
20
+ patterns: [
21
+ {
22
+ bin: "^3[47]",
23
+ length: [15],
24
+ luhn: true,
25
+ cvv_length: 4
26
+ },
27
+ ],
28
+ countries: ["GLOBAL"],
29
+ metadata: { "sourceFile" => "amex.json" },
30
+ priority_over: [],
31
+ bins: []
32
+ },
33
+ {
34
+ scheme: "diners",
35
+ brand: "Diners Club",
36
+ type: "credit",
37
+ number: {
38
+ lengths: [14],
39
+ luhn: true
40
+ },
41
+ cvv: {
42
+ length: 3
43
+ },
44
+ patterns: [
45
+ {
46
+ bin: "^3(?:0[0-5]|[68][0-9])",
47
+ length: [14],
48
+ luhn: true,
49
+ cvv_length: 3
50
+ },
51
+ ],
52
+ countries: ["GLOBAL"],
53
+ metadata: { "sourceFile" => "diners.json" },
54
+ priority_over: [],
55
+ bins: []
56
+ },
57
+ {
58
+ scheme: "elo",
59
+ brand: "Elo",
60
+ type: "credit",
61
+ number: {
62
+ lengths: [16],
63
+ luhn: true
64
+ },
65
+ cvv: {
66
+ length: 3
67
+ },
68
+ patterns: [
69
+ {
70
+ bin: "^401178|^401179|^431274|^438935|^451416|^457393|^457631|^457632|^504175",
71
+ length: [16],
72
+ luhn: true,
73
+ cvv_length: 3
74
+ },
75
+ {
76
+ bin: "^627780|^636297|^636368",
77
+ length: [16],
78
+ luhn: true,
79
+ cvv_length: 3
80
+ },
81
+ {
82
+ bin: "^(506699|5067[0-6]\\d|50677[0-8])",
83
+ length: [16],
84
+ luhn: true,
85
+ cvv_length: 3
86
+ },
87
+ {
88
+ bin: "^(50900\\d|5090[1-9]\\d|509[1-9]\\d{2})",
89
+ length: [16],
90
+ luhn: true,
91
+ cvv_length: 3
92
+ },
93
+ {
94
+ bin: "^65003[1-3]",
95
+ length: [16],
96
+ luhn: true,
97
+ cvv_length: 3
98
+ },
99
+ {
100
+ bin: "^(65003[5-9]|65004\\d|65005[0-1])",
101
+ length: [16],
102
+ luhn: true,
103
+ cvv_length: 3
104
+ },
105
+ {
106
+ bin: "^(65040[5-9]|6504[1-3]\\d)",
107
+ length: [16],
108
+ luhn: true,
109
+ cvv_length: 3
110
+ },
111
+ {
112
+ bin: "^(65048[5-9]|65049\\d|6505[0-2]\\d|65053[0-8])",
113
+ length: [16],
114
+ luhn: true,
115
+ cvv_length: 3
116
+ },
117
+ {
118
+ bin: "^(65054[1-9]|6505[5-8]\\d|65059[0-8])",
119
+ length: [16],
120
+ luhn: true,
121
+ cvv_length: 3
122
+ },
123
+ {
124
+ bin: "^(65070\\d|65071[0-8])",
125
+ length: [16],
126
+ luhn: true,
127
+ cvv_length: 3
128
+ },
129
+ {
130
+ bin: "^6507(2[0-7])",
131
+ length: [16],
132
+ luhn: true,
133
+ cvv_length: 3
134
+ },
135
+ {
136
+ bin: "^6509[0-9]",
137
+ length: [16],
138
+ luhn: true,
139
+ cvv_length: 3
140
+ },
141
+ {
142
+ bin: "^(65165[2-9]|6516[6-7]\\d)",
143
+ length: [16],
144
+ luhn: true,
145
+ cvv_length: 3
146
+ },
147
+ {
148
+ bin: "^(65500\\d|65501\\d)",
149
+ length: [16],
150
+ luhn: true,
151
+ cvv_length: 3
152
+ },
153
+ {
154
+ bin: "^(65502[1-9]|6550[3-4]\\d|65505[0-8])",
155
+ length: [16],
156
+ luhn: true,
157
+ cvv_length: 3
158
+ },
159
+ ],
160
+ countries: ["BR"],
161
+ metadata: { "sourceFile" => "elo.json" },
162
+ priority_over: ["visa", "aura", "discover", "banesecard"],
163
+ bins: []
164
+ },
165
+ {
166
+ scheme: "jcb",
167
+ brand: "JCB",
168
+ type: "credit",
169
+ number: {
170
+ lengths: [16],
171
+ luhn: true
172
+ },
173
+ cvv: {
174
+ length: 3
175
+ },
176
+ patterns: [
177
+ {
178
+ bin: "^(?:2131|1800|35)",
179
+ length: [16],
180
+ luhn: true,
181
+ cvv_length: 3
182
+ },
183
+ ],
184
+ countries: ["GLOBAL"],
185
+ metadata: { "sourceFile" => "jcb.json" },
186
+ priority_over: [],
187
+ bins: []
188
+ },
189
+ {
190
+ scheme: "mastercard",
191
+ brand: "Mastercard",
192
+ type: "credit",
193
+ number: {
194
+ lengths: [16],
195
+ luhn: true
196
+ },
197
+ cvv: {
198
+ length: 3
199
+ },
200
+ patterns: [
201
+ {
202
+ bin: "^5[1-5]",
203
+ length: [16],
204
+ luhn: true,
205
+ cvv_length: 3
206
+ },
207
+ {
208
+ bin: "^2[2-7][0-2][0-9]",
209
+ length: [16],
210
+ luhn: true,
211
+ cvv_length: 3
212
+ },
213
+ ],
214
+ countries: ["GLOBAL"],
215
+ metadata: { "sourceFile" => "mastercard.json" },
216
+ priority_over: [],
217
+ bins: []
218
+ },
219
+ {
220
+ scheme: "aura",
221
+ brand: "Aura",
222
+ type: "credit",
223
+ number: {
224
+ lengths: [16, 17, 18, 19],
225
+ luhn: true
226
+ },
227
+ cvv: {
228
+ length: 3
229
+ },
230
+ patterns: [
231
+ {
232
+ bin: "^50",
233
+ length: [16, 17, 18, 19],
234
+ luhn: true,
235
+ cvv_length: 3
236
+ },
237
+ ],
238
+ countries: ["BR"],
239
+ metadata: { "sourceFile" => "aura.json" },
240
+ priority_over: [],
241
+ bins: []
242
+ },
243
+ {
244
+ scheme: "banesecard",
245
+ brand: "BaneseCard",
246
+ type: "credit",
247
+ number: {
248
+ lengths: [16],
249
+ luhn: true
250
+ },
251
+ cvv: {
252
+ length: 3
253
+ },
254
+ patterns: [
255
+ {
256
+ bin: "^636117",
257
+ length: [16],
258
+ luhn: true,
259
+ cvv_length: 3
260
+ },
261
+ {
262
+ bin: "^637473",
263
+ length: [16],
264
+ luhn: true,
265
+ cvv_length: 3
266
+ },
267
+ {
268
+ bin: "^637470",
269
+ length: [16],
270
+ luhn: true,
271
+ cvv_length: 3
272
+ },
273
+ {
274
+ bin: "^637472",
275
+ length: [16],
276
+ luhn: true,
277
+ cvv_length: 3
278
+ },
279
+ {
280
+ bin: "^650725",
281
+ length: [16],
282
+ luhn: true,
283
+ cvv_length: 3
284
+ },
285
+ {
286
+ bin: "^650046",
287
+ length: [16],
288
+ luhn: true,
289
+ cvv_length: 3
290
+ },
291
+ {
292
+ bin: "^650591",
293
+ length: [16],
294
+ luhn: true,
295
+ cvv_length: 3
296
+ },
297
+ {
298
+ bin: "^651668",
299
+ length: [16],
300
+ luhn: true,
301
+ cvv_length: 3
302
+ },
303
+ {
304
+ bin: "^651679",
305
+ length: [16],
306
+ luhn: true,
307
+ cvv_length: 3
308
+ },
309
+ ],
310
+ countries: ["BR"],
311
+ metadata: { "sourceFile" => "banesecard.json" },
312
+ priority_over: ["discover"],
313
+ bins: []
314
+ },
315
+ {
316
+ scheme: "visa",
317
+ brand: "Visa",
318
+ type: "credit",
319
+ number: {
320
+ lengths: [13, 16],
321
+ luhn: true
322
+ },
323
+ cvv: {
324
+ length: 3
325
+ },
326
+ patterns: [
327
+ {
328
+ bin: "^4",
329
+ length: [13, 16],
330
+ luhn: true,
331
+ cvv_length: 3
332
+ },
333
+ {
334
+ bin: "^6367",
335
+ length: [16],
336
+ luhn: true,
337
+ cvv_length: 3
338
+ },
339
+ ],
340
+ countries: ["GLOBAL"],
341
+ metadata: { "sourceFile" => ["visa/base.json", "visa/bins-br-1.json", "visa/bins-br-2.json"] },
342
+ priority_over: [],
343
+ bins: [
344
+ {
345
+ bin: "491441",
346
+ type: "CREDIT",
347
+ category: nil,
348
+ issuer: "BANCO PROSPER, S.A.",
349
+ countries: ["BR"]
350
+ },
351
+ {
352
+ bin: "491440",
353
+ type: "CREDIT",
354
+ category: nil,
355
+ issuer: "BANCO PROSPER, S.A.",
356
+ countries: ["BR"]
357
+ },
358
+ {
359
+ bin: "491439",
360
+ type: "CREDIT",
361
+ category: nil,
362
+ issuer: "BANCO PROSPER, S.A.",
363
+ countries: ["BR"]
364
+ },
365
+ {
366
+ bin: "491423",
367
+ type: "CREDIT",
368
+ category: nil,
369
+ issuer: "BANCO DO ESTADO DO RIO GRANDE DO SUL S.A. (BANRISUL S.A.)",
370
+ countries: ["BR"]
371
+ },
372
+ {
373
+ bin: "491416",
374
+ type: "CREDIT",
375
+ category: "CLASSIC",
376
+ issuer: "BANCO DO ESTADO DO PARANA",
377
+ countries: ["BR"]
378
+ },
379
+ {
380
+ bin: "491415",
381
+ type: "CREDIT",
382
+ category: "CLASSIC",
383
+ issuer: "BANCO DO ESTADO DO PARANA",
384
+ countries: ["BR"]
385
+ },
386
+ {
387
+ bin: "491414",
388
+ type: "CREDIT",
389
+ category: "GOLD",
390
+ issuer: "BANCO DO ESTADO DO PARANA",
391
+ countries: ["BR"]
392
+ },
393
+ {
394
+ bin: "491413",
395
+ type: "CREDIT",
396
+ category: "CLASSIC",
397
+ issuer: "BANCO DO ESTADO DO RIO GRANDE DO SUL S/A",
398
+ countries: ["BR"]
399
+ },
400
+ {
401
+ bin: "491412",
402
+ type: "CREDIT",
403
+ category: "CLASSIC",
404
+ issuer: "BANCO DO ESTADO DO RIO GRANDE DO SUL S/A",
405
+ countries: []
406
+ },
407
+ {
408
+ bin: "491411",
409
+ type: "CREDIT",
410
+ category: "GOLD",
411
+ issuer: "BANCO DO ESTADO DO RIO GRANDE DO SUL S/A",
412
+ countries: []
413
+ },
414
+ {
415
+ bin: "491402",
416
+ type: "CREDIT",
417
+ category: nil,
418
+ issuer: "BANCO DO ESTADO DO RIO GRANDE DO SUL S.A. (BANRISUL S.A.)",
419
+ countries: []
420
+ },
421
+ {
422
+ bin: "491316",
423
+ type: "CREDIT",
424
+ category: "CLASSIC",
425
+ issuer: "BANCO SANTANDER BRASIL, S.A.",
426
+ countries: []
427
+ },
428
+ {
429
+ bin: "491315",
430
+ type: "CREDIT",
431
+ category: "CLASSIC",
432
+ issuer: "BANCO SANTANDER, S.A.",
433
+ countries: []
434
+ },
435
+ {
436
+ bin: "491314",
437
+ type: "CREDIT",
438
+ category: "GOLD",
439
+ issuer: "BANCO SANTANDER, S.A.",
440
+ countries: []
441
+ },
442
+ {
443
+ bin: "491256",
444
+ type: "CREDIT",
445
+ category: "PLATINUM",
446
+ issuer: "BANCO PANAMERICANO, S.A.",
447
+ countries: []
448
+ },
449
+ ]
450
+ },
451
+ {
452
+ scheme: "discover",
453
+ brand: "Discover",
454
+ type: "credit",
455
+ number: {
456
+ lengths: [16],
457
+ luhn: true
458
+ },
459
+ cvv: {
460
+ length: 4
461
+ },
462
+ patterns: [
463
+ {
464
+ bin: "^6011",
465
+ length: [16],
466
+ luhn: true,
467
+ cvv_length: 4
468
+ },
469
+ {
470
+ bin: "^622",
471
+ length: [16],
472
+ luhn: true,
473
+ cvv_length: 4
474
+ },
475
+ {
476
+ bin: "^64",
477
+ length: [16],
478
+ luhn: true,
479
+ cvv_length: 4
480
+ },
481
+ {
482
+ bin: "^65",
483
+ length: [16],
484
+ luhn: true,
485
+ cvv_length: 4
486
+ },
487
+ ],
488
+ countries: ["US"],
489
+ metadata: { "sourceFile" => "discover.json" },
490
+ priority_over: ["hipercard"],
491
+ bins: []
492
+ },
493
+ {
494
+ scheme: "hipercard",
495
+ brand: "Hipercard",
496
+ type: "credit",
497
+ number: {
498
+ lengths: [16, 19],
499
+ luhn: true
500
+ },
501
+ cvv: {
502
+ length: 3
503
+ },
504
+ patterns: [
505
+ {
506
+ bin: "^38",
507
+ length: [19],
508
+ luhn: true,
509
+ cvv_length: 3
510
+ },
511
+ {
512
+ bin: "^60",
513
+ length: [16],
514
+ luhn: true,
515
+ cvv_length: 3
516
+ },
517
+ ],
518
+ countries: ["BR"],
519
+ metadata: { "sourceFile" => "hipercard.json" },
520
+ priority_over: [],
521
+ bins: []
522
+ },
523
+ ].freeze
524
+ end
@@ -0,0 +1,192 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'creditcard_identifier/brands'
4
+ require_relative 'creditcard_identifier/brands_detailed'
5
+
6
+ ##
7
+ # Credit Card BIN Validator
8
+ #
9
+ # This module provides credit card validation using bin-cc data.
10
+ module CreditcardIdentifier
11
+ VERSION = '2.0.0'
12
+
13
+ ##
14
+ # Credit card validator using bin-cc data.
15
+ class Validator
16
+ attr_reader :brands, :brands_detailed
17
+
18
+ ##
19
+ # Initialize validator with brand data
20
+ def initialize
21
+ @brands = BRANDS
22
+ @brands_detailed = BRANDS_DETAILED
23
+ end
24
+
25
+ ##
26
+ # Identify the credit card brand
27
+ #
28
+ # @param card_number [String] Credit card number
29
+ # @param detailed [Boolean] If true, returns detailed brand info
30
+ # @return [Hash, nil] Brand hash or nil if not found
31
+ def find_brand(card_number, detailed: false)
32
+ return nil if card_number.nil? || card_number.empty?
33
+
34
+ brand = @brands.find do |b|
35
+ b[:regexp_full].match?(card_number)
36
+ end
37
+
38
+ return nil unless brand
39
+
40
+ if detailed
41
+ detailed_brand = @brands_detailed.find { |b| b[:scheme] == brand[:name] }
42
+ if detailed_brand
43
+ # Find the specific pattern that matched
44
+ matched_pattern = detailed_brand[:patterns]&.find do |p|
45
+ Regexp.new(p[:bin]).match?(card_number)
46
+ end
47
+
48
+ # Find the specific bin that matched (if bins exist)
49
+ bin_prefix = card_number[0, 6]
50
+ matched_bin = detailed_brand[:bins]&.find do |b|
51
+ bin_prefix.start_with?(b[:bin]) || b[:bin] == bin_prefix
52
+ end
53
+
54
+ # Return without the full bins array
55
+ result = detailed_brand.reject { |k, _| k == :bins }
56
+ result[:matched_pattern] = matched_pattern
57
+ result[:matched_bin] = matched_bin
58
+ return result
59
+ end
60
+ end
61
+
62
+ brand
63
+ end
64
+
65
+ ##
66
+ # Check if card number is supported
67
+ #
68
+ # @param card_number [String] Credit card number
69
+ # @return [Boolean] true if supported, false otherwise
70
+ def supported?(card_number)
71
+ !find_brand(card_number).nil?
72
+ end
73
+
74
+ ##
75
+ # Validate CVV for a specific brand
76
+ #
77
+ # @param cvv [String] CVV code
78
+ # @param brand_or_name [String, Hash] Brand name or brand object from find_brand
79
+ # @return [Boolean] true if valid, false otherwise
80
+ def validate_cvv(cvv, brand_or_name)
81
+ return false if cvv.nil? || cvv.empty?
82
+
83
+ # Handle brand object (Hash)
84
+ if brand_or_name.is_a?(Hash)
85
+ # Handle detailed brand object
86
+ if brand_or_name[:cvv] && brand_or_name[:cvv][:length]
87
+ expected_length = brand_or_name[:cvv][:length]
88
+ return cvv.match?(/^\d{#{expected_length}}$/)
89
+ end
90
+ # Handle simplified brand object
91
+ if brand_or_name[:regexp_cvv]
92
+ return brand_or_name[:regexp_cvv].match?(cvv)
93
+ end
94
+ # Handle brand name from object
95
+ brand_name = brand_or_name[:name] || brand_or_name[:scheme]
96
+ if brand_name
97
+ brand = @brands.find { |b| b[:name] == brand_name }
98
+ return brand&.dig(:regexp_cvv)&.match?(cvv) || false
99
+ end
100
+ return false
101
+ end
102
+
103
+ # Handle brand name (String)
104
+ brand = @brands.find { |b| b[:name] == brand_or_name }
105
+ return false if brand.nil?
106
+
107
+ brand[:regexp_cvv].match?(cvv)
108
+ end
109
+
110
+ ##
111
+ # Get information about a specific brand
112
+ #
113
+ # @param brand_name [String] Brand name (e.g., 'visa', 'mastercard')
114
+ # @return [Hash, nil] Brand hash or nil if not found
115
+ def get_brand_info(brand_name)
116
+ @brands.find { |b| b[:name] == brand_name }
117
+ end
118
+
119
+ ##
120
+ # Get detailed information about a specific brand
121
+ #
122
+ # @param scheme [String] Scheme name (e.g., 'visa', 'mastercard')
123
+ # @return [Hash, nil] Detailed brand hash or nil if not found
124
+ def get_brand_info_detailed(scheme)
125
+ @brands_detailed.find { |b| b[:scheme] == scheme }
126
+ end
127
+
128
+ ##
129
+ # List all supported brands
130
+ #
131
+ # @return [Array<String>] List of brand names
132
+ def list_brands
133
+ @brands.map { |b| b[:name] }
134
+ end
135
+ end
136
+
137
+ # Module-level convenience methods
138
+ class << self
139
+ ##
140
+ # Get or create the singleton validator instance
141
+ #
142
+ # @return [Validator] The validator instance
143
+ def validator
144
+ @validator ||= Validator.new
145
+ end
146
+
147
+ ##
148
+ # Get all brand data
149
+ #
150
+ # @return [Array<Hash>] List of brand hashes
151
+ def brands
152
+ BRANDS
153
+ end
154
+
155
+ ##
156
+ # Get all detailed brand data
157
+ #
158
+ # @return [Array<Hash>] List of detailed brand hashes
159
+ def brands_detailed
160
+ BRANDS_DETAILED
161
+ end
162
+
163
+ ##
164
+ # Identify the credit card brand
165
+ #
166
+ # @param card_number [String] Credit card number
167
+ # @param detailed [Boolean] If true, returns detailed brand info
168
+ # @return [Hash, nil] Brand hash or nil if not found
169
+ def find_brand(card_number, detailed: false)
170
+ validator.find_brand(card_number, detailed: detailed)
171
+ end
172
+
173
+ ##
174
+ # Check if card number is supported
175
+ #
176
+ # @param card_number [String] Credit card number
177
+ # @return [Boolean] true if supported, false otherwise
178
+ def supported?(card_number)
179
+ validator.supported?(card_number)
180
+ end
181
+
182
+ ##
183
+ # Validate CVV for a specific brand
184
+ #
185
+ # @param cvv [String] CVV code
186
+ # @param brand_or_name [String, Hash] Brand name or brand object
187
+ # @return [Boolean] true if valid, false otherwise
188
+ def validate_cvv(cvv, brand_or_name)
189
+ validator.validate_cvv(cvv, brand_or_name)
190
+ end
191
+ end
192
+ end
metadata ADDED
@@ -0,0 +1,77 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: creditcard-identifier
3
+ version: !ruby/object:Gem::Version
4
+ version: 2.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Renato Viço
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2026-01-11 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: minitest
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '5.0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '5.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '13.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '13.0'
41
+ description: A Ruby library for credit card BIN validation and brand identification
42
+ email: []
43
+ executables: []
44
+ extensions: []
45
+ extra_rdoc_files: []
46
+ files:
47
+ - README.md
48
+ - lib/creditcard_identifier.rb
49
+ - lib/creditcard_identifier/brands.rb
50
+ - lib/creditcard_identifier/brands_detailed.rb
51
+ homepage: https://github.com/renatovico/bin-cc
52
+ licenses:
53
+ - MIT
54
+ metadata:
55
+ homepage_uri: https://github.com/renatovico/bin-cc
56
+ source_code_uri: https://github.com/renatovico/bin-cc
57
+ documentation_uri: https://github.com/renatovico/bin-cc/tree/master/libs/ruby
58
+ post_install_message:
59
+ rdoc_options: []
60
+ require_paths:
61
+ - lib
62
+ required_ruby_version: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: 2.5.0
67
+ required_rubygems_version: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ version: '0'
72
+ requirements: []
73
+ rubygems_version: 3.5.22
74
+ signing_key:
75
+ specification_version: 4
76
+ summary: Credit card BIN validation using bin-cc data
77
+ test_files: []