tracking_number 2.4.0 → 3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7c0ef2fa722ce7a2d9cc6cfaac9c5510ef7ac79074ba4e227f9554b8157da2ee
4
- data.tar.gz: 3a5f46d4dc5027e8bf68c4f7cf0756851e284d29b499c38d4c264b57ebc61fc4
3
+ metadata.gz: 71f01e80c118f12c210f3c9a5195edf0c9d257c37ff4ce36529962f4131a57ed
4
+ data.tar.gz: 79519ddef14f7728eb6de46437360a9040368e261ce3dba4cdadf9a9b382891e
5
5
  SHA512:
6
- metadata.gz: 404a19a4f9847ade5581c5ff7c0cf950fde208d773f81f6882715d44b4395f94fc505538d46b718ad0c7fbfcdf152e512619ca86372dc1021a524ab2614cd898
7
- data.tar.gz: c4fbe1cf3637fea233106008bc31c1b555a63ea7e04588f08446d00a5d6dd3948daf744e078a74c6e96d7f257c51986fa8712db5f3b9e1e742e0ea9d61a6c63c
6
+ metadata.gz: 86ef4bdd51d50a76f26b0bc53f17db5518c48973ecc715b90058dc446c1a7ac2558db35a75f9a572837e4aacbf9e6fa33b2c5870dfc21076a50a8c538e156313
7
+ data.tar.gz: f78a9a8b106ce172f87f231c9d59aa6c52a1f76b8a6b471aaa5a17ac92ee605d71cab5d216b3032d3eba8fd0244a406f6358f6be465e1332c1edce32d07c9ad8
@@ -0,0 +1,2 @@
1
+ github: [jkeen]
2
+ buy_me_a_coffee: jeffkeen
data/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  tracking_number changelog
2
2
 
3
+ # [3.0.0](https://github.com/jkeen/tracking_number/compare/v2.5.0...v3.0.0) (2026-08-15)
4
+
5
+
6
+ * feat!: Update to tracking_number_data 2.0, which rebuilds the IMpb USPS definitions from Publication 199 and absorbs the DHL and Fedex definitions that described the same tracking number. ([448e953](https://github.com/jkeen/tracking_number/commit/448e9534b0be8d2f205de5712e3215281922de99))
7
+
8
+
9
+ ### BREAKING CHANGES
10
+
11
+ * `fedex_smartpost`, `dhl_ecommerce_30` and `usps_32V2` are absorbed into `usps_impb_c`, which is the definition they were always describing. `usps_91` and `usps_22` become `usps_impb_c` and `usps_impb_n`, and Application Identifier 91 splits off to `usps_legacy`. Every number these matched still decodes, now as USPS.
12
+
13
+ No active definitions declare `partners` anymore, but it remains valid in our data spec for potential future use.
14
+
15
+ # [2.5.0](https://github.com/jkeen/tracking_number/compare/v2.4.0...v2.5.0) (2026-08-13)
16
+
17
+
18
+ ### Features
19
+
20
+ * update to tracking_number_data 1.12, which includes support for Canpar, Purolator, Spee-Dee, YunExpress, and an updated FedEx matcher ([ca67edf](https://github.com/jkeen/tracking_number/commit/ca67edf0eaeab33171008bf115e96bdcf55c0d4b))
21
+
3
22
  # [2.4.0](https://github.com/jkeen/tracking_number/compare/v2.3.0...v2.4.0) (2025-08-21)
4
23
 
5
24
 
data/README.md CHANGED
@@ -2,21 +2,15 @@
2
2
  [![Gem Version](https://badge.fury.io/rb/tracking_number.svg)](https://badge.fury.io/rb/tracking_number)
3
3
  [![Gem](https://img.shields.io/gem/dt/tracking_number.svg)]()
4
4
 
5
- > Hey there tracking number enthusiast! I don't use this project in any production capacity, and really never have. I am not a tracking number expert, and I don't have inside connections to a shipping company—I'm just a guy that once tried to make a package tracking app and this gem is all that survived. When I have absolutely nothing to do it's kinda fun to tinker with, but time has become more and more of a precious resource. Anyway, maintaining this is thankless work, and if this project has been useful for you I sure would appreciate a cup or two of coffee slid my way as a token of appreciation. A PR would also be nice.
6
- >
7
- > <a href="https://www.buymeacoffee.com/jeffkeen" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" style="height: auto !important;width: 150px !important;" ></a>
8
-
9
- ## Tracking Number (v1.x)
5
+ ## Tracking Number
10
6
 
11
7
  This gem identifies valid tracking numbers and can tell you a little bit about the shipment just from the number—there's quite a bit of info tucked away into those numbers, it turns out.
12
8
 
13
- It detects tracking numbers from UPS, FedEx, DHL, USPS, OnTrac, Amazon Logistics, and 160+ countries national postal services (S10 standard).
9
+ It detects tracking numbers from USPS, UPS, FedEx, Amazon, DHL, OnTrac, LaserShip, Canada Post, DPD, Purolator, Old Dominion Freight Line, Spee-Dee Delivery, Canpar, YunExpress, Landmark Global, GOFO Express, and any carrier using the S10 international standard.
14
10
 
15
11
  This gem does not do tracking. That is left up to you.
16
12
 
17
- #### New in 1.0
18
-
19
- Starting with the 1.0 release the specifications for detecting tracking numbers have been moved into a separate repository ([tracking_number_data](http://github.com/jkeen/tracking_number_data)) that this gem relies on. I did this so a) we can have a single place to document all tracking number types and it can be more of a crowdsourced effort, and b) so clients can be written in other languages easier.
13
+ All the matching data is kept in [tracking_number_data](https://github.com/jkeen/tracking_number_data) and displayed in full on [trackingnumber.fyi](https://trackingnumber.fyi).
20
14
 
21
15
  ## Usage
22
16
 
@@ -134,32 +128,6 @@ Most tracking numbers have a format where each part of the number has meaning. `
134
128
  # }
135
129
  ```
136
130
 
137
- #### Multiple shippers / Partnerships
138
- Some tracking numbers match multiple carriers, because they belong to multiple carriers. Some shipments like Fedex Smartpost contract the "last mile" out to USPS.
139
-
140
- ```ruby
141
- # Search defaults to only showing numbers that fulfill the carrier side of the relationship
142
- # (if a partnership exists at all), as this is the end a consumer would most likely be interested in.
143
-
144
- results = TrackingNumber.search('420112139261290983497923666238')
145
- => [#<TrackingNumber::USPS91:0x26ac0 420112139261290983497923666238>]
146
-
147
- all_results = TrackingNumber.search('420112139261290983497923666238', match: :all)
148
- => [#<TrackingNumber::FedExSmartPost:0x30624 420112139261290983497923666238>, #<TrackingNumber::USPS91:0x26ac0 420112139261290983497923666238>]
149
-
150
- tn = results.first
151
- tn.shipper? #=> false
152
- tn.carrier? #=> true
153
- tn.partnership? #=> true
154
- tn.partners
155
- #=> <struct TrackingNumber::Base::PartnerStruct
156
- # shipper=#<TrackingNumber::FedExSmartPost:0x30624 420112139261290983497923666238>,
157
- # carrier=#<TrackingNumber::USPS91:0x2f1fc 420112139261290983497923666238>>
158
-
159
- tn.partners.shipper #=> #<TrackingNumber::FedExSmartPost:0x30624 420112139261290983497923666238>
160
- tn.partners.carrier == tn #=> true
161
- ```
162
-
163
131
  ## ActiveModel validation
164
132
 
165
133
  For Rails 3 (or any ActiveModel client), validate your fields as a tracking number:
@@ -9,8 +9,10 @@
9
9
  "validation": {
10
10
  "checksum": {
11
11
  "name": "mod10",
12
+ "modulo": 10,
12
13
  "evens_multiplier": 3,
13
- "odds_multiplier": 1
14
+ "odds_multiplier": 1,
15
+ "reverse": false
14
16
  }
15
17
  },
16
18
  "tracking_url": "https://www.canadapost-postescanada.ca/track-reperage/en#/search?searchFor=%s",
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "Canpar",
3
+ "courier_code": "canpar",
4
+ "tracking_numbers": [
5
+ {
6
+ "name": "Canpar (22)",
7
+ "id": "canpar_22",
8
+ "regex": "\\s*(?<SerialNumber>[CDKLSUXZ]\\s*([0-9]\\s*){21})\\s*",
9
+ "validation": {},
10
+ "tracking_url": "https://www.canpar.com/en/track/tracking.jsp?reference=%s&locale=en",
11
+ "test_numbers": {
12
+ "valid": [
13
+ "D576002440000001718010",
14
+ "D576002440000001428004",
15
+ "D576002440000002496001",
16
+ "L576002440000000001004",
17
+ "L576002440000000004001",
18
+ "S576002440000000004001",
19
+ "S576002440000000025001"
20
+ ],
21
+ "invalid": [
22
+ "1576002440000002510003",
23
+ "D57600244000000251000",
24
+ "M576002440000002510003"
25
+ ]
26
+ }
27
+ }
28
+ ]
29
+ }
@@ -8,7 +8,8 @@
8
8
  "regex": "\\s*(?<SerialNumber>([0-9]\\s*){9,10})(?<CheckDigit>([0-9]\\s*))",
9
9
  "validation": {
10
10
  "checksum": {
11
- "name": "mod7"
11
+ "name": "mod7",
12
+ "modulo": 7
12
13
  }
13
14
  },
14
15
  "tracking_url": "http://www.dhl.com/en/express/tracking.html?brand=DHL&AWB=%s",
@@ -89,80 +90,8 @@
89
90
  "GR295117494011169045"
90
91
  ]
91
92
  }
92
- },
93
- {
94
- "name": "DHL E-Commerce (30)",
95
- "id": "dhl_ecommerce_30",
96
- "regex": [
97
- "\\s*(?<RoutingApplicationId>4\\s*2\\s*0\\s*)(?<DestinationZip>([0-9]\\s*){5})",
98
- "(?!\\s*9\\s*[1-5]\\b)",
99
- "(?<SerialNumber>",
100
- "(?<ServiceType>(9[2-5]\\s*))",
101
- "(?<ShipperId>([0-9]\\s*){9})",
102
- "(?<PackageId>([0-9]\\s*){10})",
103
- ")",
104
- "(?<CheckDigit>[0-9]\\s*)"
105
- ],
106
- "validation": {
107
- "checksum": {
108
- "name": "mod10",
109
- "evens_multiplier": 3,
110
- "odds_multiplier": 1,
111
- "reverse": true
112
- }
113
- },
114
- "tracking_url": "https://ecommerceportal.dhl.com/track?trackingnumber=%s",
115
- "partners": [{
116
- "description": "DHL eCommerce hands off to USPS for the last mile",
117
- "partner_type": "carrier",
118
- "partner_id": "usps_22",
119
- "validation": {
120
- "matches_all": [
121
- { "regex_group_name": "RoutingApplicationId", "matches": "420" },
122
- { "regex_group_name": "ServiceType", "matches_regex": "9[2345]" }
123
- ]
124
- }
125
- },
126
- {
127
- "description": "DHL eCommerce hands off to USPS for the last mile",
128
- "partner_type": "carrier",
129
- "partner_id": "usps_91",
130
- "validation": {
131
- "matches_all": [
132
- { "regex_group_name": "RoutingApplicationId", "matches": "420" },
133
- { "regex_group_name": "ServiceType", "matches_regex": "9[2345]" }
134
- ]
135
- }
136
- }],
137
- "additional": [
138
- {
139
- "name": "Service Type",
140
- "regex_group_name": "ServiceType",
141
- "lookup": [
142
- { "matches": "92", "name": "USPS Tracking (Commercial)" },
143
- { "matches": "93", "name": "Collect on Delivery (COD)" },
144
- { "matches": "94", "name": "USPS Tracking" },
145
- { "matches": "95", "name": "Certified Mail" }
146
- ]
147
- }
148
- ],
149
- "test_numbers": {
150
- "valid": [
151
- "420902459261290336128704042634",
152
- "420941179261290336128704062441",
153
- "420926029261290336128704067248",
154
- "420902729261290336128704124163",
155
- "420950549261290336128704133837",
156
- "420193809261290336128704280838",
157
- "420900469261290336128704216936"
158
- ],
159
- "invalid": [
160
- "420941179261290336128704062442",
161
- "420941179261290336128704062400",
162
- "420941179261290336128704062422"
163
- ]
164
- }
165
93
  }
166
94
 
95
+
167
96
  ]
168
97
  }
@@ -17,7 +17,9 @@
17
17
  ],
18
18
  "validation": {
19
19
  "checksum": {
20
- "name": "mod_37_36"
20
+ "name": "mod_37_36",
21
+ "modulo": 36,
22
+ "alphabet": "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
21
23
  }
22
24
  },
23
25
  "tracking_url": "https://www.dpdgroup.com/nl/mydpd/my-parcels/track?lang=en&parcelNumber=%s",
@@ -323,7 +325,9 @@
323
325
  ],
324
326
  "validation": {
325
327
  "checksum": {
326
- "name": "mod_37_36"
328
+ "name": "mod_37_36",
329
+ "modulo": 36,
330
+ "alphabet": "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
327
331
  }
328
332
  },
329
333
  "tracking_url": "https://www.dpdgroup.com/nl/mydpd/my-parcels/track?lang=en&parcelNumber=%s",
@@ -45,8 +45,7 @@
45
45
  "name": "FedEx Express (34)",
46
46
  "id": "fedex_34",
47
47
  "regex": [
48
- "\\s*1\\s*0\\s*[0-9]\\s*[0-9]\\s*[0-9]\\s*",
49
- "([0-9]\\s*){10}",
48
+ "\\s*[0-8]\\s*([0-9]\\s*){14}",
50
49
  "(?<DestinationZip>([0-9]\\s*){5})",
51
50
  "(?<SerialNumber>([0-9]\\s*){13})",
52
51
  "(?<CheckDigit>[0-9]\\s*)"
@@ -81,7 +80,11 @@
81
80
  "1001901781990001000300617767839437",
82
81
  " 1 0 0 1 9 0 1 7 8 1 9 9 0 0 0 1 0 0 0 3 0 0 6 1 7 7 6 7 8 3 9 4 3 7 ",
83
82
  "1002297871540001000300790695517286",
84
- "1027590111820004833500785458233610"
83
+ "1027590111820004833500785458233610",
84
+ "1195266473930007521100871079919940",
85
+ "1 1 9 52 6 64739 3000 75211 00 8710 799 199 40",
86
+ "1195271874060007521100871140316925",
87
+ "1195583874210007521100871140316936"
85
88
  ],
86
89
  "invalid": [
87
90
  "1001901781990001000300617767839438"
@@ -89,66 +92,37 @@
89
92
  }
90
93
  },
91
94
  {
92
- "name": "FedEx SmartPost",
93
- "id": "fedex_smartpost",
94
- "description": "Shipped by FedEx, Delivered by USPS",
95
+ "name": "FedEx ASTRA (32)",
96
+ "id": "fedex_astra_32",
97
+ "description": "The legacy ASTRA barcode. Positions 17 through 28 hold a FedEx Express tracking number, and its check digit is the only one the barcode carries. What the other fields mean is not yet known.",
95
98
  "regex": [
96
- "\\s*(?:",
97
- "(?:(?<RoutingApplicationId>4\\s*2\\s*0\\s*)(?<DestinationZip>([0-9]\\s*){5}))?",
98
- "(?<ApplicationIdentifier>9\\s*2\\s*)",
99
- ")?",
100
- "(?<SerialNumber>",
101
- "(?<SCNC>6\\s*1\\s*)",
102
- "(?<ServiceType>2\\s*9\\s*)",
103
- "(?<ShipperId>([0-9]\\s*){8})",
104
- "(?<PackageId>([0-9]\\s*){11}|([0-9]\\s*){7})",
105
- ")",
106
- "(?<CheckDigit>([0-9]\\s*))"
99
+ "\\s*3\\s*",
100
+ "(?:[0-9]\\s*){5}",
101
+ "(?:[0-9]\\s*){3}",
102
+ "(?:[0-9]\\s*){7}",
103
+ "(?<SerialNumber>(?:[0-9]\\s*){11})",
104
+ "(?<CheckDigit>[0-9]\\s*)",
105
+ "(?:[0-9]\\s*){4}"
107
106
  ],
108
- "additional": [
109
- {
110
- "name": "Service Type",
111
- "regex_group_name": "ServiceType",
112
- "lookup": [
113
- {
114
- "matches_regex": ".",
115
- "name": "Delivered by USPS"
116
- }
117
- ]
118
- }
119
- ],
120
- "partners": [{
121
- "partner_id": "usps_91",
122
- "partner_type": "carrier",
123
- "description": "FedEx SmartPost is a shipping service that utilizes FedEx for the initial transport and the United States Postal Service for final delivery."
124
- }],
125
107
  "validation": {
126
108
  "checksum": {
127
- "name": "mod10",
128
- "evens_multiplier": 3,
129
- "odds_multiplier": 1
130
- },
131
- "serial_number_format": {
132
- "prepend_if": {
133
- "matches_regex": "^(?!92).+",
134
- "content": "92"
135
- }
109
+ "name": "sum_product_with_weightings_and_modulo",
110
+ "weightings": [3, 1, 7, 3, 1, 7, 3, 1, 7, 3, 1],
111
+ "modulo1": 11,
112
+ "modulo2": 10
136
113
  }
137
114
  },
138
- "tracking_url": "https://www.fedex.com/apps/fedextrack/?tracknumbers=%s",
139
115
  "test_numbers": {
140
116
  "valid": [
141
- "9261292700768711948021",
142
- "420 11213 92 6129098349792366623 8",
143
- "92 6129098349792366623 8"
117
+ "32971514560102447849175802862014",
118
+ "32971510360102447848540980802018",
119
+ "32971508360102447847941133172013",
120
+ "33810799560000004380417923803212",
121
+ "3 29715 145 6010244 784917580286 2014"
144
122
  ],
145
123
  "invalid": [
146
- "9261292700768711948020",
147
- "92001903060085300042901077",
148
- "420 11213 61290983497923666231",
149
- "92 6129098349792366623 5",
150
- "9400 1112 0108 0805 4830 16",
151
- "9361 2898 7870 0317 6337 95"
124
+ "32971514560102447849175802852014",
125
+ "3297151456010244784917580286201"
152
126
  ]
153
127
  }
154
128
  },
@@ -159,8 +133,10 @@
159
133
  "validation": {
160
134
  "checksum": {
161
135
  "name": "mod10",
136
+ "modulo": 10,
162
137
  "evens_multiplier": 1,
163
- "odds_multiplier": 3
138
+ "odds_multiplier": 3,
139
+ "reverse": false
164
140
  }
165
141
  },
166
142
  "tracking_url": "https://www.fedex.com/apps/fedextrack/?tracknumbers=%s",
@@ -184,8 +160,10 @@
184
160
  "validation": {
185
161
  "checksum": {
186
162
  "name": "mod10",
163
+ "modulo": 10,
187
164
  "evens_multiplier": 3,
188
- "odds_multiplier": 1
165
+ "odds_multiplier": 1,
166
+ "reverse": false
189
167
  }
190
168
  },
191
169
  "test_numbers": {
@@ -225,6 +203,9 @@
225
203
  {
226
204
  "name": "FedEx Ground 96 (22)",
227
205
  "id": "fedex_ground_96",
206
+ "glossary": {
207
+ "ApplicationIdentifier": { "description": "Always 96, marking a ground shipping label." }
208
+ },
228
209
  "regex": [
229
210
  "\\s*(?<ApplicationIdentifier>9\\s*6\\s*)",
230
211
  "(?<SCNC>([0-9]\\s*){2})",
@@ -235,8 +216,10 @@
235
216
  "validation": {
236
217
  "checksum": {
237
218
  "name": "mod10",
219
+ "modulo": 10,
238
220
  "evens_multiplier": 1,
239
- "odds_multiplier": 3
221
+ "odds_multiplier": 3,
222
+ "reverse": false
240
223
  }
241
224
  },
242
225
  "tracking_url": "https://www.fedex.com/apps/fedextrack/?tracknumbers=%s",
@@ -253,6 +236,11 @@
253
236
  {
254
237
  "name": "FedEx Ground GSN",
255
238
  "id": "fedex_ground_gsn",
239
+ "description": "Ground's 34 digit barcode, carrying the Ground shipper number and the tracking number in the last twelve digits",
240
+ "glossary": {
241
+ "ApplicationIdentifier": { "description": "Always 96, marking a ground shipping label." },
242
+ "SCNC": { "description": "22, or 32 where the shipper number is nine digits." }
243
+ },
256
244
  "regex": [
257
245
  "\\s*(?<ApplicationIdentifier>9\\s*6\\s*)",
258
246
  "(?<SCNC>([0-9]\\s*){2})",
@@ -0,0 +1,24 @@
1
+ {
2
+ "name": "GOFO Express",
3
+ "courier_code": "gofo",
4
+ "tracking_numbers": [
5
+ {
6
+ "name": "GOFO Express (US)",
7
+ "id": "gofo_us",
8
+ "regex": [
9
+ "\\s*G\\s*F\\s*U\\s*S\\s*(?<SerialNumber>([0-9]\\s*){14})\\s*"
10
+ ],
11
+ "tracking_url": "https://www.gofoexpress.com/tracking.html?searchID=%s",
12
+ "validation": {},
13
+ "test_numbers": {
14
+ "valid": [
15
+ "GFUS01011884214464",
16
+ "GFUS01011884214272"
17
+ ],
18
+ "invalid": [
19
+ "GFUS0101188421446"
20
+ ]
21
+ }
22
+ }
23
+ ]
24
+ }
@@ -79,6 +79,27 @@
79
79
  "1LS7119013618127-2"
80
80
  ]
81
81
  }
82
+ },
83
+ {
84
+ "name": "LaserShip 1LSCX (15)",
85
+ "id": "lasership_1lscx",
86
+ "regex": [
87
+ "\\s*1\\s*L\\s*S\\s*",
88
+ "\\s*C\\s*X\\s*",
89
+ "(?<SerialNumber>",
90
+ "([0-9A-Z]\\s*){10}",
91
+ ")"
92
+ ],
93
+ "validation": {},
94
+ "test_numbers": {
95
+ "valid": [
96
+ "1LS CX VE00 58631Y",
97
+ "1LSCXVE005BUEFX"
98
+ ],
99
+ "invalid": [
100
+ "1LSCZVE0058631Y"
101
+ ]
102
+ }
82
103
  }
83
104
  ]
84
105
  }
@@ -13,7 +13,8 @@
13
13
  ],
14
14
  "validation": {
15
15
  "checksum": {
16
- "name": "luhn"
16
+ "name": "luhn",
17
+ "modulo": 10
17
18
  }
18
19
  },
19
20
  "tracking_url": "https://www.odfl.com/us/en/tools/trace-track-ltl-freight/trace.html?proNumbers=%s",
@@ -43,7 +44,8 @@
43
44
  ],
44
45
  "validation": {
45
46
  "checksum": {
46
- "name": "luhn"
47
+ "name": "luhn",
48
+ "modulo": 10
47
49
  }
48
50
  },
49
51
  "tracking_url": "https://www.odfl.com/us/en/tools/trace-track-ltl-freight/trace.html?proNumbers=%s",
@@ -9,8 +9,10 @@
9
9
  "validation": {
10
10
  "checksum": {
11
11
  "name": "mod10",
12
+ "modulo": 10,
12
13
  "evens_multiplier": 1,
13
- "odds_multiplier": 2
14
+ "odds_multiplier": 2,
15
+ "reverse": false
14
16
  },
15
17
  "serial_number_format": {
16
18
  "prepend_if": {
@@ -42,8 +44,10 @@
42
44
  "validation": {
43
45
  "checksum": {
44
46
  "name": "mod10",
47
+ "modulo": 10,
45
48
  "evens_multiplier": 1,
46
- "odds_multiplier": 2
49
+ "odds_multiplier": 2,
50
+ "reverse": false
47
51
  },
48
52
  "serial_number_format": {
49
53
  "prepend_if": {
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "Purolator",
3
+ "courier_code": "purolator",
4
+ "tracking_numbers": [
5
+ {
6
+ "name": "Purolator (12)",
7
+ "id": "purolator_numeric",
8
+ "regex": "\\s*(?<SerialNumber>[0-5]\\s*([0-9]\\s*){10})(?<CheckDigit>[0-9])\\s*",
9
+ "validation": {
10
+ "checksum": {
11
+ "name": "luhn"
12
+ }
13
+ },
14
+ "tracking_url": "https://www.purolator.com/en/shipping/tracker?searchValue=%s",
15
+ "test_numbers": {
16
+ "valid": [
17
+ "320595463938",
18
+ "320606146993",
19
+ "287809468872",
20
+ "320610804900",
21
+ "331426749957",
22
+ "331434972567"
23
+ ],
24
+ "invalid": [
25
+ "320595463939",
26
+ "287809468875"
27
+ ]
28
+ }
29
+ },
30
+ {
31
+ "name": "Purolator (alpha + 9)",
32
+ "id": "purolator_alpha",
33
+ "regex": "\\s*(?<SerialNumber>([A-Z]\\s*){3}([0-9]\\s*){9})\\s*",
34
+ "validation": {},
35
+ "tracking_url": "https://www.purolator.com/en/shipping/tracker?searchValue=%s",
36
+ "test_numbers": {
37
+ "valid": [
38
+ "KYV009956937",
39
+ "CGK002986959",
40
+ "JFV247545960",
41
+ "TLR000083964"
42
+ ],
43
+ "invalid": [
44
+ "KY009956937",
45
+ "331426749957"
46
+ ]
47
+ }
48
+ }
49
+ ]
50
+ }
@@ -4,10 +4,13 @@
4
4
  "tracking_numbers": [
5
5
  {
6
6
  "id": "s10",
7
+ "description": "The UPU format every postal service uses for international items",
7
8
  "name": "S10",
8
9
  "validation": {
9
10
  "checksum": {
10
- "name": "s10"
11
+ "name": "s10",
12
+ "weightings": [8, 6, 4, 2, 3, 5, 9, 7],
13
+ "modulo": 11
11
14
  },
12
15
  "additional": {
13
16
  "exists": [
@@ -17,6 +20,11 @@
17
20
  },
18
21
  "regex": "\\s*(?<ServiceType>([A-Z]\\s*){2})(?<SerialNumber>([0-9]\\s*){8})(?<CheckDigit>([0-9]\\s*))(?<CountryCode>([A-Z]\\s*){2})",
19
22
  "tracking_url": null,
23
+ "glossary": {
24
+ "ServiceType": { "description": "Two letters for the class of postal service, the first of which identifies the service and the second the variant." },
25
+ "SerialNumber": { "description": "Eight digits assigned by the issuing postal service." },
26
+ "CountryCode": { "description": "Two letters for the country whose postal service issued the number, following ISO 3166." }
27
+ },
20
28
  "test_numbers": {
21
29
  "valid": [
22
30
  "RB123456785GB",
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "Spee-Dee Delivery",
3
+ "courier_code": "speedee",
4
+ "tracking_numbers": [
5
+ {
6
+ "name": "Spee-Dee (20)",
7
+ "id": "speedee",
8
+ "regex": "\\s*S\\s*P\\s*(?<SerialNumber>([0-9]\\s*){18})\\s*",
9
+ "validation": {},
10
+ "tracking_url": "https://www.speedeedelivery.com/track/?tracking=%s",
11
+ "test_numbers": {
12
+ "valid": [
13
+ "SP029692510000920746",
14
+ "SP029692510000901479",
15
+ "SP029692510000688332",
16
+ "SP029692510000279301",
17
+ "SP029692450001607639"
18
+ ],
19
+ "invalid": [
20
+ "029692510000920746",
21
+ "SX029692510000920746"
22
+ ]
23
+ }
24
+ }
25
+ ]
26
+ }
@@ -15,8 +15,10 @@
15
15
  "validation": {
16
16
  "checksum": {
17
17
  "name": "mod10",
18
+ "modulo": 10,
18
19
  "evens_multiplier": 1,
19
- "odds_multiplier": 2
20
+ "odds_multiplier": 2,
21
+ "reverse": false
20
22
  }
21
23
  },
22
24
  "tracking_url": "https://wwwapps.ups.com/WebTracking/track?track=yes&trackNums=%s",
@@ -146,8 +148,10 @@
146
148
  "validation": {
147
149
  "checksum": {
148
150
  "name": "mod10",
151
+ "modulo": 10,
149
152
  "evens_multiplier": 1,
150
- "odds_multiplier": 2
153
+ "odds_multiplier": 2,
154
+ "reverse": false
151
155
  }
152
156
  },
153
157
  "additional": [